DebianDocker安装及使用

Docker 安装

注意!, Docker默认使用iptables来管理分配容器网络部分, 其他防火墙可能不管用(群晖就没用iptables, 有关防火墙的部分得走系统防火墙进行配置)


按照官方教程安装

下载Docker和Docker-compose的可执行文件: Docker & Docker-compose

# 下载二进制文件压缩包
wget https://download.docker.com/linux/static/stable/x86_64/docker-27.5.0.tgz

# 解压压缩包
tar -zxvf docker-27.5.0.tgz

# 将解压后的二进制文件移至/usr/bin/
sudo cp docker/* /usr/bin/

# 编辑配置文件
sudo vim /etc/systemd/system/docker.service

docker.service

[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target
  
[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock --selinux-enabled=false --default-ulimit nofile=65536:65536
ExecReload=/bin/kill -s HUP $MAINPID
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
# Uncomment TasksMax if your systemd version supports it.
# Only systemd 226 and above support this version.
#TasksMax=infinity
TimeoutStartSec=0
# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes
# kill only the docker process, not all processes in the cgroup
KillMode=process
# restart the docker process if it exits prematurely
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s
  
[Install]
WantedBy=multi-user.target

# 给予运行权限
sudo chmod +x /etc/systemd/system/docker.service

# 载入docker.service
sudo systemctl daemon-reload

# 启动docker服务
sudo systemctl enable docker
sudo systemctl start docker

# 下载docker-compose二进制文件
wget https://github.com/docker/compose/releases/download/v2.32.4/docker-compose-linux-x86_64
# 修改名称
mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose
# 给予执行权限
chmod +x /usr/local/bin/docker-compose

Docker基础命令

docker pull

pull

# 拉取镜像
docker pull docker.io/library/nginx:latest
## docker.io 镜像服务器地址 官方地址
## library   用户名        library是专门给上有软件提供商官方使用的命名空间
## nginx     仓库名
## latest    选择版本
docker pull nginx # 与上述等效, 只有官方提供最新版本才能这么用

# 可选项
-a # 获取仓库中的所有镜像
--disable-content-trust # 取消镜像内容校验
--registry-mirror=proxy_URL  # 指定镜像代理服务地址

docker image

image

images/ls

# 查看已下载镜像
docker image ls
docker images # 与上等价
## REPOSITORY 仓库
## TAG        标签
## IMAGE ID   ID
## CREATED    时间
## SIZE       大小

# 可选项
-a # 列出所有镜像文件, 包括临时文件
--digests=true # 列出镜像数字摘要值
-f # 过滤列出的镜像, 不会用!!!
--no-trunctrue=true # 对输出结果中太长部分进行截断
-q # 仅输出ID信息

inspect

# 查看镜像详细信息
docker [image] inspect ubuntu:20.04
## 返回JSON格式的消息
## 可使用-f {{}}进行筛选

rmi/rm

# 删除镜像
docker image rm ubuntu:20.04
docker rmi ubuntu:20.04 # 与上等价
# 可选项
-f # 强制删除镜像, 即使有容器依赖
-no-prune # 不要清理未带标签的父镜像

prune

# 清理无用/临时镜像
docker image prune
# 选项
-a # 清理所有无用镜像
-filter filter # 清理符合给定过滤器镜像
-f # 强制删除镜像

build

# 基于Dockerfile创建镜像
docker [image] build -t Dockerfile .
# 可选项
--target set_name # 构建到set_name阶段停止
--progress=plain  # 查看标准输出

save

# 导出镜像文件
docker [iamge] save -o docker_ubuntu_20.04.tar ubuntu:20.04
# -o 导出到指定文件中
docker save ubuntu:20.04 > docker_ubuntu_20.04.tar # 与上等价

load

# 从文件导入镜像
docker [image] load -i docker_ubuntu_20.04.tar 
# -i 指明文件
docker load < docker_ubuntu_20.04.tar

push

# 注册dockerhub账号
https://hub.docker.com

# 登录dockerhub
docker login  -u dfwhy
dckr_pat_suyaZ_4ArOClsaMREX_m7RqCKS0

# 命名为docker.io/user/image:tag格式
docker tag myubuntu:20.04 test_user/myubuntu:20.04

# 上传镜像
docker [image] push test_user/myubuntu:20.04

# 退出登录
docker logout

docker container

container

commit

# 基于已有容器创建镜像
docker [container] commit ubuntu_test ubuntu:20.04_test
# 选项
-a/--author "" # 作者信息
-c  command # 提交时执行Dockerfile指令
-m/--message "" # 提交信息
-p # 提交时暂停容器运行

import

# 导入镜像
docker [container] import file|URL

create

# 创建容器
docker [container] create -itd ubtuntu:20.04
# 可选项

start

# 启动容器
docker [container] start ubuntu_test

run

# 创建并启动容器
docker [container] run -itd --name ubuntu_test ubuntu:20.04
# 等价于
docker create --name ubuntu_test && docker start ubuntu_test
# 可选项
-mount type=binf,source=/webapp,destination=/opt/webapp
-v /webapp:/opt/webapp # 与上等价
## 支持三种类型数据卷
### volume 普通数据卷, 映射至主机路径下
### bind   绑定数据卷, 映射到主机指定路径下
### tmpfs  临时数据卷, 只存在内存中
-t \ # docker分配一个伪终端并绑定至容器标准输入上
-i \ # 保持容器的标准输入打开
-d \ # 后台运行容器
--mount source=volume_name,target=mount_path \ # 加载数据卷到指定位置
-mount type=bind,source=目标文件夹,target=挂载文件夹,readonly \ #挂载主机目录至容器目录
--network networkname \ # 指定网络
--dns dns_ip \ # 指定dns服务器
--bridge bridge_name \ # 指定挂载网桥名称
--bip CIDR \ # 指定docker0的掩码???
--host SOCKET \ # Docker服务端接收命令的通道???
--ice true|false \ # 是否支持容器间通信
--ip-forward true|false \ # 容器间通信???
--iptables true|false \ # 是否允许Docker添加iptables规则
--mtu BYTES \ # 容器中的MTU?
--cap-add NET_ADMIN \ # 容器添加创建网卡的权限(wireguard)会用到

logs

# 查看容器输出
docker [container] logs ubuntu_test
# 可选项
-details      # 打印详细信息
-f            # 持续保持输出
-since string # 输出从某个时间开始的日志
-tail  string # 输出最近的日志
-t            # 显示时间戳信息
-until string # 输出某个时间前的日志

pause

# 暂停容器
docker [container] pause ubuntu_test

unpause

# 恢复暂停容器
docker [container] unpause ubuntu_test

stop

# 终止容器
docker [container] stop ubuntu_test
# 较温和停止, 若超过10s仍未停止则默认执行kill

kill

# 强行终止容器
docker [container] kill ubuntu_test

prune

# 清除所有停止的容器
docker container prune

restart

# 先终止容器再启动容器
docker [container] restart ubuntu_test

attach

# 进入容器进行操作
docker [container] attach ubuntu_test
# 可选项
--no-stdin=true|false # 是否关闭标准输入, 默认打开

exec

# 创建一个bash进程, 并进入容器
docker -it [container] exec ubuntu_test bash

rm

# 删除容器
docker [container] rm ubuntu_test
# 可选项 未测试过!!!
-f # 强制删除运行中容器
-l # 删除容器的连接, 但保留容器
-v # 删除容器挂载的数据卷

export

# 导出容器
docker [container] export -o ubuntu_docker.tar ubuntu_test

import

# 导入容器
docker [container] import ubuntu_docker.tar ubuntu_test_20.04
# import与load区别在于 前者导出完整数据的镜像 后者导出当前容器快照

inspect

# 查看容器详情
docker container inspect ubuntu_test

top

# 查看容器内部进程
docker [container] top ubuntu_test

stats

# 查看容器状态
docker [container] stats ubuntu_test
# 可选项
-a # 输出所有容器统计信息
-format string # 格式化输出信息
-no-stream # 不持续输出
-no-trunc # 不截断信息

cp

# 复制文件
docker [container] cp filepath container:file_path

diff

# 查看容器内文件系统变更
docker [container] dirr ubuntu_test

port

# 查看端口映射
docker container port ubuntu_test

update

# 更新运行时配置
docker [container] update
# 可选项

docker volume

volume

ls

# 查看数据卷
docker volume ls

create

# 创建数据卷
docker volume create volume_name

inspect

# 查看卷详细信息
docker volume inspect volume_name

rm

# 删除数据卷
docker volume rm volume_name

prune

# 清理所有无主数据卷
docker volume prune

docker tag

tag

# 为本地镜像添加新的标签
docker tag ubuntu:20.04 myubuntu:20.04

docker history

history

# 查看镜像历史
docker history ubuntu:20.04

docker search

search

# 搜索镜像
docker search [option] nginx # 搜索包含nginx关键词的仓库
## 可选项
-f # 过滤输出
--format string # 格式化输出
--limit int # 限制输出结果个数
--no-trunc # 不截断输出

docker system

system

# 查看镜像、容器、数据卷所占用的空间
docker system df
# TYPE          TOTAL      ACTIVE          SIZE       RECLAIMABLE
# Images        总镜像数量   正在运行镜像数量  总镜像大小   可回收空间
# Containers    总容器数量   正在运行容器数量  总容器大小   可回收空间
# Local Volumes 总数据卷数量 正在运行数据卷数量 总数据卷大小 可回收空间
# Build Cache   总缓存数量   0              总缓存大小   可回收空间

docker manifest

manifest

inspect

# 查看容器manifest表
docker manifest inspect image

create

# 将镜像推送至dockerhub后, 创建manifest列表
docker manifest create username/test \
                       username/test-x86-64 \
                       username/test-arm64

annotate

# 设置manifest列表
docker manifest annotate username/test \
                         username/test-x86-64 \
                         --os linux --arch x86_64

docker manifest annotate username/test \
                         username/arm64v8-test \
                         --os linux --arch arm64 --variant v8

push

# 推送manifest列表
docker manifest push username/test

docker network

network

ls

# 查看docker网络
docker network ls

create

# 新建docker网络, 可指定网络类型为bridge或overlay
docker network create -d bridge/overlay network_name

docker compose

compose

# compose格式
docker compose \
       -f filepath \ # 指定compose模板文件, 默认docker-compose.yml
       -p project_name \ # 指定项目名称, 默认以目录所在名作为项目名称
       --verbose \ # 输出更多调试信息

version

# 查看docker compose版本
docker compose version

build

# docker compose build构建服务
docker compose build \
               --force-rm \ # 删除构建过程中的临时容器
               --no-cache \ # 构建镜像过程不使用cache
               --pull \ # 始终通过pull获取最新镜像

config

# 检查配置是否正确
docker compose config

down

# 停止up启动容器, 并移除网络
docker compose down

exec

# 进入指定容器
docker compose exec

help

# 获取帮助
docker compse help

images

# 列出所有包含镜像
docker compose images

kill

# 发出信号停止服务容器
docker compose kill -s SIGINT

logs

# 查看容器输出
docker compose logs

pause

# 暂停容器
docker compose pause

port

# 打印容器端口映射的公共端口
docker compose port --protocol=tcp|udp \ # 指定端口协议
                    --ndex=index \ # 指定命令对象容器序号

ps

# 列出项目中所有容器
docker compose ps

pull

# 拉取服务依赖镜像
docker compose pull

push

# 推送服务依赖镜像到Docker仓库
docker compose push

restart

# 重启项目中的服务
docker compose restart

rm

# 删除所有停止状态的服务容器
docker compose rm

DockerHub

# 注册dockerhub账号
https://hub.docker.com

# 登录dockerhub
docker login -u dfwhy
dckr_pat_suyaZ_4ArOClsaMREX_m7RqCKS0

# 上传镜像
docker [image] push test_user/myubuntu:20.04

# 退出登录
docker logout

Dockerfile

Dockerfile 编写规则

# 以某个镜像为基础进行修改, scratch为空白不存在镜像, 可使用as为某一阶段命名
FROM scratch as set_name
# 执行命令, 每次执行完指令后自动提交为一个commit, 所以最好删除所有无用文件
RUN  shell_command
# 复制文件, 修改文件所属者
COPY --chown=user:group 上下文路径 目标路径
# 复制文件, 从其他镜像负责文件
COPY --from=image_name:tag 镜像中文件路径 目标路径
# 与COPY同, 貌似能自动解压, 懒得用
ADD  与COPY同
# 容器启动默认运行的进程, 该进程执行结束容器也会自动关闭
CMD  [ "sh", "-c", "echo $HOME" ]
# 使用ENTRYPOINT后会将CMD的内容作为参数传给ENTRYPOINT, 可用来追加参数
ENTRYPOINT 与CMD同
# 定义环境变量, 容器运行时任有该环境变量
ENV  $SHELL=/bin/bash
# 定义环境变量, 但容器运行时消失
ARG     与ENV同
# 挂载目录, 指定容器的/data自动转化为匿名卷
VOLUME /data
# 声明容器运行时提供服务端口
EXPOSE port1 port2
# 指定工作目录
WORKDIR 路径
# 指定当前用户
USER user:group
# 健康检查
HEALTHCHECK --interval=健康检查间隔 \
            --timeout=健康检查运行超时时间 \
            --retries=连续失败指定次数 \
# 当前镜像不调用接的命令, 继承镜像才调用
ONBUILD Docker Command
# 键值对形式添加元数据
LABEL <key>=<value>

Docker yml编写规则

version: "3" # 指定版本

services: # 所有服务
  webapp: # 服务名称
    user: nginx # 容器运行应用的用户名
    working_dir: /code # 工作目录
    container_name: web-container # 指定服务名称
    image: examples/web # 选择镜像
    build: 
      context: ./webapp # 指定dockerfile所在文件夹路径
      dockerfile: Dockerdile # 指定dockerfile文件名
      args:
        buildno: 1 # 构建变量过程中使用的参数
      cache_from: # 指定构建镜像的缓存
        - alpine:latest
        - corp/web_app:3.14
    command: echo "hello world" # 容器启动后默认执行命令, 覆盖RUN
    devices: # 设备间映射关系
      - "/dev/ttyUSB1:/dev/ttyUSB0"
    ports: # 端口映射
      - "80:80"
    volumes: # 数据卷挂载路径
      - "/data"
    depends_on: # 容器间依赖关系, 会先等待redis和db启动后再启动, 但不会等待完全启动成功
      - db
      - redis
    dns: 8.8.8.8 # 指定DNS服务器
    tmpfs: # 挂载tmpfs文件系统到容器
      - /run
      - /tmp
    environment: # 设置环境变量
      - RACK_ENV=development
      - SESSION_SECRET
    extra_hosts: # 添加映射信息
        - "googledns:8.8.8.8"
        - "dockerhub:52.1.157.61"
    healthcheck: # 进行健康检查
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 1m30s
      timeout: 10s
      retries: 3
    labels: # 添加元数据
      com.startupteam.description: "webapp for a startup team"
      com.startupteam.department: "devops department"
      com.startupteam.release: "rc3 for v1.0"
    network_mode: "bridge" # 设置网络模式
    network_mode: "host"
    network_mode: "none"
    network_mode: "service:[service name]"
    network_mode: "container:[container name/id]"
    pid: "host" # 与主机系统共享进程命名空间
    
    sysctls: # 配置容器内核参数
      net.core.somaxconn: 1024
      net.ipv4.tcp_syncookies: 0
    ulimits: # 指定容器限制值
      nproc: 65535
      nofile:
        soft: 20000
        hard: 40000
    
networks: # 配置容器连接网络
  some-network:
  other-network:

Docker技术细节

Docker镜像层

Docker在容器中修改文件时, 实际上时将镜像层中的文件复制至容器层, 然后修改容器层中副本, 该方法称为写时复制, COW(copy-on-write)

# 查看docker存储驱动
docker info | grep -i "storage"
# Storage Driver: overlay2 Linux内核大于等于4.0时一般用overlayfs2

overlay2 文件系统分层结构

![]()

  • 最下层为"lower层", 其中数据为只读, "lower层"数量可以是多个
  • 中间层为"upper层", 其中数据可读可写
  • 最上层为"merged层", 即用户看到的叠加混合视图
# 测试overlay2
# 创建3个lower层, upper层和merged层, work为文件系统使用的临时工作目录, 在挂载中使用
mkdir -p overlay2_test/lower{1,2,3} overlay2_test/{work,upper,merged}
# 创建测试文件
echo "file1 -lower1" > overlay2_test/lower1/file1
echo "file2 -lower1" > overlay2_test/lower1/file2
echo "file2 -lower2" > overlay2_test/lower2/file2
echo "file3 -lower3" > overlay2_test/lower3/file3

mkdir -p overlay2_test/lower2/tdir
mkdir -p overlay2_test/lower3/tdir
echo "file1 - tdir -lower2" > overlay2_test/lower2/tdir/file1
echo "file2 - tdir -lower3" > overlay2_test/lower3/tdir/file2
# overlay2_test/
# ├── lower1
# │   ├── file1
# │   └── file2
# ├── lower2
# │   ├── file2
# │   └── tdir
# │       └── file1
# ├── lower3
# │   ├── file3
# │   └── tdir
# │       └── file2
# ├── merged
# ├── upper
# └── work

# 挂载文件系统
cd overlay2_test/
mount -t overlay -o lowerdir=lower1:lower2:lower3,upperdir=upper,workdir=work overlay merged
# 文件系统结构为
# merged
# overlay
# lower1
# lower2
# lower3

# 测试是否挂载成功
mount | grep overlay | grep overlay2_test
# overlay on /root/overlay2_test/merged type overlay (rw,relatime,lowerdir=lower1:lower2:lower3,upperdir=upper,workdir=work,uuid=on,nouserxattr)
# overlay2_test/
# ├── lower1
# │   ├── file1
# │   └── file2
# ├── lower2
# │   ├── file2
# │   └── tdir
# │       └── file1
# ├── lower3
# │   ├── file3
# │   └── tdir
# │       └── file2
# ├── merged
# │   ├── file1
# │   ├── file2
# │   ├── file3
# │   └── tdir
# │       ├── file1
# │       └── file2
# ├── upper
# └── work
#     └── work

# 进行测试
cat overlay2_test/merged/file1
# file1 -lower1
cat overlay2_test/merged/file2
# file2 -lower1
cat overlay2_test/merged/file3
# file3 -lower3
cat overlay2_test/merged/tdir/file1
# file1 - tdir -lower2
cat overlay2_test/merged/tdir/file2
# file2 - tdir -lower3

## 修改测试
echo "file1 - merged" > overlay2_test/merged/file1
cat overlay2_test/merged/file1
# file1 - merged
cat overlay2_test/lower1/file1
# file1 -lower1
# overlay2_test/
# ├── lower1
# │   ├── file1
# │   └── file2
# ├── lower2
# │   ├── file2
# │   └── tdir
# │       └── file1
# ├── lower3
# │   ├── file3
# │   └── tdir
# │       └── file2
# ├── merged
# │   ├── file1
# │   ├── file2
# │   ├── file3
# │   └── tdir
# │       ├── file1
# │       └── file2
# ├── upper
# │   └── file1
# └── work
#     └── work

## 删除测试
rm overlay2_test/merged/file3
# overlay2_test/
# ├── lower1
# │   ├── file1
# │   └── file2
# ├── lower2
# │   ├── file2
# │   └── tdir
# │       └── file1
# ├── lower3
# │   ├── file3
# │   └── tdir
# │       └── file2
# ├── merged
# │   ├── file1
# │   ├── file2
# │   └── tdir
# │       ├── file1     -rw-r--r-- 1 root root   15 Oct  3 16:00 file1
# │       └── file2     c--------- 2 root root 0, 0 Oct  3 16:03 file3
# ├── upper
# │   ├── file1
# │   └── file3 
# └── work
#     └── work
#         └── #11
# 删除操作实际上实在upper层创建一个without文件, 主次设备号均为0

## 手动删除测试
mknod overlay2_test/upper/tdir c 0 0
# overlay2_test/
# ├── lower1
# │   ├── file1
# │   └── file2
# ├── lower2
# │   ├── file2
# │   └── tdir
# │       └── file1
# ├── lower3
# │   ├── file3
# │   └── tdir
# │       └── file2
# ├── merged
# │   ├── file1
# │   └── file2
# ├── upper
# │   ├── file1
# │   ├── file3
# │   └── tdir
# └── work
#     └── work
#         └── #11

Docker镜像的层存放在/var/lib/docker/overlay2/哈希值/diff

Docker在镜像层最上还增加了init层, /etc/hosts/etc/hostname/etc/resolv.conf文件均由Docker生成

Docker 踩坑

Docker动态修改端口号

# 查看该容器开放端口
docker port 容器ID 
# 获取容器Id
docker inspect 容器ID | grep Id 
# 停止容器
docker stop 容器ID
# 停止docker服务
systemctl stop docker

vim /var/lib/docker/containers/容器Id/hostconfig.json
    # 在$PortBindings$中添加端口
    "映射端口/tcp":[{"HostIp":"","HostPort":"暴露端口"}

vim /var/lib/docker/containers/容器Id/config.v2.json
    # 在$ExposedPorts$中添加端口
    “映射端口/tcp”:{}

# 启动docker服务
systemctl start docker 
参考:
Docker — 从入门到实践
最后修改:2025 年 04 月 14 日
赛博讨口子