☰
Docker安装本质是运行时契约:守护进程、containerd与runc的协同机制
2026/10/13 18:13:04 网站建设 项目流程

1. 为什么“Docker安装”这件事,90%的人第一步就卡在了认知盲区

很多人点开教程,第一眼看到“下载Docker Desktop”或“sudo apt install docker.io”,就立刻复制粘贴敲回车——结果要么报错“command not found”,要么装完发现docker --version能跑,但docker run hello-world死活拉不下来镜像,最后在终端里反复输入systemctl status docker,盯着那一行红色的inactive (dead)发呆。

这不是你手速慢,也不是网络差,而是从一开始就没搞清一个根本问题:Docker不是单个可执行文件,而是一套分层运行时体系。它由三个核心组件构成——dockerd(守护进程,负责容器生命周期管理)、containerd(容器运行时,真正创建和隔离进程的底层引擎)、runc(符合OCI标准的容器运行时实现,直接调用Linux内核的namespaces和cgroups)。这三者之间有严格的版本兼容关系,就像汽车的发动机、变速箱和驱动轴——你不能给一台2023款发动机硬配2018年的变速箱,否则轻则顿挫,重则锁死。

我见过太多真实案例:某高校实验室的A同学,在Ubuntu 20.04上用apt install docker.io装了个老版本docker.io(实际是Moby项目的一个旧分支),结果docker build时遇到failed to solve with frontend dockerfile.v0错误;B同学在Mac上直接双击Docker Desktop最新版安装包,却因系统启用了SIP(System Integrity Protection)导致com.docker.vmnetd服务无法注册,所有端口映射全部失效;还有C同学在CentOS 7上用yum install docker,装完发现dockerd默认没启用cgroup v2支持,一跑Kubernetes集群就报cgroup driver: cgroupfs is not supported。

这些都不是“命令写错了”,而是对Docker的部署模型缺乏基本理解。Docker官方明确将安装方式分为三类:

  • 桌面环境(Desktop):面向Mac/Windows用户,打包了完整的虚拟机(Hyper-V/WSL2或Hypervisor.framework)、Kubernetes控制面、GUI面板,本质是“开箱即用的开发沙盒”;
  • 服务器环境(Engine):面向Linux服务器,只提供dockerd+containerd+runc最小运行时,要求管理员自行配置存储驱动、网络插件、安全策略;
  • 云原生环境(Rootless):面向无root权限场景(如共享主机、CI流水线),通过rootlesskit在用户空间模拟容器命名空间,牺牲部分性能换取安全性。

所以,“安装Docker”这个动作,本质上是在做一次运行时契约的签署:你承诺操作系统满足哪些内核特性(如CONFIG_NAMESPACES、CONFIG_CGROUPS),你接受哪种资源隔离粒度(cgroup v1 vs v2),你愿意为便利性让渡多少控制权(Desktop的黑盒封装 vs Engine的手动调优)。跳过这个认知环节,后面所有命令都像在没有地图的情况下开车——路标看得见,但永远不知道下一个路口该左转还是右转。

提示:判断你的系统是否具备基础条件,只需执行三条命令:
uname -r—— 确认内核版本 ≥3.10(CentOS 7最低要求);
lsmod | grep overlay—— 检查overlay2存储驱动模块是否加载(现代Linux发行版默认启用);
cat /proc/sys/user/max_user_namespaces—— 若值为0,需手动开启sysctl -w user.max_user_namespaces=10000(rootless模式必需)。

2. Linux服务器安装实录:从裸机到可运行容器的七步闭环

假设你有一台刚重装系统的Ubuntu 22.04服务器,目标是部署一个稳定、可升级、符合生产规范的Docker Engine。这里不推荐apt install docker.io,因为Ubuntu源里的docker.io包由社区维护,版本滞后且与上游Docker CE不完全兼容(比如22.04默认源中仍是20.10.x,而Docker CE已迭代至24.0.x)。我们必须走官方仓库路径,但每一步都有隐藏陷阱。

2.1 卸载残留:清理历史安装的“幽灵进程”

很多服务器之前装过Docker,但卸载不彻底。apt remove docker docker-engine docker.io containerd runc只是删了包,没杀进程、没清配置、没删数据目录。必须手动执行:

# 停止所有Docker相关服务 sudo systemctl stop docker docker.socket docker-storage-setup docker-cleanup.timer # 彻底删除二进制、配置、数据 sudo apt purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo rm -rf /var/lib/docker /var/lib/containerd /etc/docker # 清理systemd残留单元文件 sudo rm -f /etc/systemd/system/docker.service.d sudo systemctl daemon-reload

这一步的关键在于/var/lib/docker目录——它不仅是镜像存储位置,还包含network(网络配置)、volumes(卷数据)、plugins(插件状态)等关键元数据。如果残留旧版本的overlay2子目录结构,新版本dockerd启动时会因元数据格式不兼容直接崩溃,日志里只显示failed to start daemon: error initializing graphdriver: driver not supported,根本看不出是残留数据惹的祸。

2.2 依赖预检:内核模块与用户权限的双重校验

Docker依赖两个核心内核特性:overlayfs(存储驱动)和cgroups(资源限制)。Ubuntu 22.04默认启用,但某些精简版镜像(如cloud-init最小化安装)可能禁用。执行:

# 检查overlay模块是否可用 modprobe overlay echo $? # 返回0表示成功 # 检查cgroup v2是否启用(推荐,但非强制) grep -q "cgroup_enable=cpuset" /proc/cmdline && echo "cgroup v1 enabled" || echo "cgroup v2 enabled" # 验证当前用户是否在docker组(避免每次sudo) sudo groupadd docker 2>/dev/null sudo usermod -aG docker $USER # 注意:此操作后需重新登录或执行 newgrp docker 才生效

这里有个极易被忽略的细节:usermod -aG docker $USER添加用户组后,当前shell会话不会自动继承新组权限。很多新手执行完就立刻试docker run hello-world,报错permission denied while trying to connect to the Docker daemon socket。正确做法是退出当前SSH会话,重新登录;或者在当前会话执行exec su -l $USER刷新组信息。这是Linux用户组机制的固有行为,不是Docker的bug。

2.3 官方仓库配置:HTTPS证书与APT密钥的精准绑定

Docker官方仓库使用https://download.docker.com/linux/ubuntu,但它的SSL证书由Docker Inc.签发,而非系统信任库中的根CA。如果服务器时间不准(误差>5分钟),或系统缺少ca-certificates包,apt update会报The following signatures couldn't be verified because the public key is not available。必须分步处理:

# 更新系统时间(NTP同步) sudo timedatectl set-ntp true # 安装基础证书包 sudo apt update && sudo apt install -y ca-certificates curl gnupg lsb-release # 添加Docker官方GPG密钥(注意:必须用curl -fsSL,不能用wget) curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg # 创建源列表(关键:[arch=amd64] 和 [signed-by] 必须严格匹配) echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

重点看最后一行:signed-by=/usr/share/keyrings/docker-archive-keyring.gpg指定了密钥路径,$(dpkg --print-architecture)动态获取架构(amd64/arm64),$(lsb_release -cs)获取发行版代号(jammy/focal)。任何一处手误(比如把jammy写成ubuntu22.04),都会导致apt update时出现NO_PUBKEY错误,后续安装必然失败。

2.4 安装与验证:守护进程启动的三重检查法

执行安装命令后,不要急着跑容器,先做三层验证:

sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 第一层:检查二进制版本一致性 docker --version # 应显示 Docker version 24.0.x, build ... dockerd --version # 必须与docker --version主版本号一致 containerd --version # 输出应含"v1.7.x"(Docker CE 24.0默认捆绑) # 第二层:检查守护进程状态 sudo systemctl status docker # 正常状态:Active: active (running) with green dot,且Loaded行显示"enabled; vendor preset: enabled" # 第三层:检查socket通信(绕过systemd,直连Unix socket) sudo docker -H unix:///var/run/docker.sock info | grep "Server Version" # 若返回版本号,证明dockerd监听正常;若报"connection refused",说明dockerd未启动或socket路径错误

这里有个实战技巧:docker -H unix:///var/run/docker.sock是调试神器。当docker info报错时,用这个命令直连socket,能排除docker客户端代理、环境变量DOCKER_HOST污染等问题。我曾帮某公司运维排查一个诡异问题:docker ps返回空,但sudo docker ps正常——最终发现是普通用户家目录下.bashrc里误写了export DOCKER_HOST=tcp://localhost:2375,而该端口根本没开,导致客户端试图连接TCP而非Unix socket。

2.5 存储驱动调优:overlay2不是万能解药

Docker默认使用overlay2存储驱动,但它在某些场景下会出问题。比如在XFS文件系统上,如果挂载时没加ftype=1选项,overlay2无法创建多层目录,docker build时会报failed to chown /var/lib/docker/overlay2/xxx/merged: invalid argument。解决方案是:

# 检查XFS挂载参数 xfs_info /var/lib/docker | grep ftype # 若输出不含"ftype=1",需重新挂载(需root权限且影响服务) sudo umount /var/lib/docker sudo mount -t xfs -o rw,relatime,attr2,inode64,largeio,swalloc,ftype=1 /dev/sdb1 /var/lib/docker

更稳妥的做法是修改Docker守护进程配置,强制使用vfs驱动(纯拷贝,性能差但100%兼容):

sudo mkdir -p /etc/docker echo '{"storage-driver": "vfs"}' | sudo tee /etc/docker/daemon.json sudo systemctl restart docker

但vfs会极大降低镜像拉取和构建速度——实测在SSD上,docker pull ubuntu:22.04耗时从8秒增至42秒。所以除非业务场景极度特殊(如只读容器、临时测试),否则优先修复overlay2依赖,而非降级驱动。

2.6 镜像加速:国内源配置的“三明治”结构

国内访问Docker Hub极慢,必须配置镜像加速器。但很多人只改/etc/docker/daemon.json,却忘了两件事:一是加速器域名本身需要DNS解析,二是HTTP代理可能干扰HTTPS连接。完整配置如下:

# 创建daemon.json(注意:JSON语法必须严格,末尾不能有逗号) sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn", "https://hub-mirror.c.163.com"], "insecure-registries": [], "live-restore": true, "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } } EOF # 重启前,先检查DNS是否能解析镜像站 nslookup docker.mirrors.ustc.edu.cn # 若超时,需配置DNS(临时) echo 'nameserver 223.5.5.5' | sudo tee /etc/resolv.conf # 重启并验证 sudo systemctl restart docker sudo docker info | grep "Registry Mirrors" -A 3

这里的live-restore: true是生产环境必备项:它允许dockerd进程重启时,正在运行的容器不中断。log-opts则防止日志文件无限增长——某次线上事故中,一个未配置日志轮转的容器占满200GB磁盘,就是因为/var/lib/docker/containers/xxx/json.log持续写入。

2.7 权限加固:从“免sudo”到“最小权限”的演进

让普通用户免sudo运行Docker,不等于放任不管。docker组成员拥有等同于root的权限——因为docker.sock是root-owned,任何能写该socket的用户,都能通过docker run -v /:/host -it ubuntu chroot /host逃逸到宿主机。必须实施纵深防御:

# 方案1:使用user namespace映射(推荐) echo '{"userns-remap": "default"}' | sudo tee -a /etc/docker/daemon.json sudo systemctl restart docker # 方案2:限制容器能力(Capability) sudo docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE -p 8080:80 nginx # 方案3:启用SELinux/AppArmor(Ubuntu默认用AppArmor) sudo aa-status | grep docker # 若未启用,需安装apparmor-utils并加载docker profile

userns-remap是最有效的方案:它为每个容器分配独立的UID/GID范围(如容器内uid 0映射到宿主机uid 100000),即使容器被攻破,攻击者也无法直接操作宿主机root文件。但启用后,/var/lib/docker目录所有权会变为100000:100000,需手动chown -R 100000:100000 /var/lib/docker。

3. Docker常用命令的“语义解码”:每个flag背后的操作系统真相

docker run -it --rm -v /host:/container alpine sh这条命令,表面看是“启动一个交互式Alpine容器并挂载目录”,但每个参数都在调用Linux内核的不同子系统。理解这些,才能真正掌控容器行为。

3.1-it:伪终端(PTY)与进程组的底层握手

-i(interactive)和-t(tty)看似简单,实则触发了复杂的内核交互:

  • -i:保持STDIN打开,并设置docker run的--attach=stdin标志,让dockerd在创建容器时,将宿主机的/dev/pts/N与容器内/dev/tty建立双向管道;
  • -t:请求分配一个伪终端(PTY),dockerd会调用posix_openpt()创建新的PTY对(master/slave),并将slave端绑定到容器init进程的/dev/tty。

如果只加-i不加-t,容器内sh会报错sh: can't access tty; job control turned off,因为shell需要TTY来实现作业控制(Ctrl+Z挂起、fg/bg切换)。而只加-t不加-i,则STDIN关闭,你无法输入命令。两者必须共存,这是POSIX终端规范的硬性要求。

实战经验:在CI流水线中运行docker run -t会失败,因为CI环境无TTY。正确写法是docker run -i(或直接省略,-i是默认行为),并确保命令不依赖交互式特性。

3.2--rm:容器生命周期与文件系统卸载的原子性保证

--rm参数让容器退出后自动删除,但它的实现远比“删个目录”复杂。Docker在dockerd中维护了一个containerd的Task对象,当容器进程退出时:

  1. containerd向runc发送kill信号,等待进程终止;
  2. runc调用unshare(CLONE_NEWNS)解除挂载命名空间,触发内核卸载所有/var/lib/docker/overlay2/xxx/merged下的挂载点;
  3. containerd确认挂载点已卸载后,才通知dockerd删除容器元数据(/var/lib/docker/containers/xxx/config.v2.json);
  4. 最后,dockerd异步清理overlay2层数据(diff,merged,work目录)。

如果第2步失败(如某个进程还在merged目录下打开文件),--rm会卡住,docker ps -a显示容器状态为Exited (0) 2 minutes ago但一直不消失。此时需用lsof +D /var/lib/docker/overlay2/xxx/merged找出占用进程,或强制umount -l懒卸载。

3.3-v /host:/container:bind mount的四种挂载传播模式

-v参数本质是Linux的bind mount,但它有四种传播模式(shared,slave,private,unbindable),默认是rprivate(递归私有)。这意味着:

  • 宿主机/host下新建子目录,容器内/container看不到;
  • 容器内/container下新建子目录,宿主机/host看不到;
  • 但如果用-v /host:/container:shared,则双方新建目录会实时同步。

这在开发场景很危险:某次我用-v $(pwd):/app:shared挂载代码目录,然后在容器内执行rm -rf node_modules,结果宿主机的node_modules也被删了——因为shared模式下,runc会调用mount --make-shared /host,使挂载事件双向传播。

更安全的做法是显式声明传播模式:

# 只读挂载(开发时防误删) docker run -v $(pwd):/app:ro nginx # 递归只读(连子目录都不可写) docker run -v $(pwd):/app:ro,rslave nginx # 绑定挂载时指定UID/GID(解决权限错乱) docker run -v $(pwd):/app:z nginx # :z 表示SELinux标签自动设置

z和Z是SELinux特有flag:z表示多个容器共享该目录(svirt_sandbox_file_t),Z表示仅当前容器独占(container_file_t)。在非SELinux系统(如Ubuntu)上无效,但写上无害。

3.4docker build:Dockerfile指令如何翻译成内核系统调用

Dockerfile不是脚本,而是docker build引擎的DSL,每条指令对应一次runc调用:

Dockerfile指令对应内核操作典型陷阱
FROM ubuntu:22.04runc create+runc start拉取镜像层,解压到overlay2/lower镜像层过大时,/var/lib/docker/overlay2磁盘爆满
COPY . /appcp -r宿主机文件到overlay2/diff,触发overlay2写时复制.git目录被COPY,导致镜像臃肿;应加.dockerignore
RUN apt update && apt install -y curlrunc exec启动临时容器,执行命令,commit新层apt update缓存不清理,镜像多出100MB;应&& apt clean -y
CMD ["nginx", "-g", "daemon off;"]设置容器启动时的argv[0],不执行若写成CMD nginx -g "daemon off;"(shell form),会启动/bin/sh -c "nginx...",PID 1不是nginx

CMD和ENTRYPOINT的区别常被误解:CMD是默认参数,ENTRYPOINT是默认命令。ENTRYPOINT ["nginx"]+CMD ["-g", "daemon off;"]等价于nginx -g "daemon off;";而ENTRYPOINT ["sh", "-c"]+CMD ["nginx -g 'daemon off;'"]则等价于sh -c "nginx -g 'daemon off;'"。后者PID 1是sh,前者PID 1是nginx——这对信号传递(如docker stop发送SIGTERM)至关重要。

3.5docker network:libnetwork如何接管Linux网络栈

docker network create mynet不只是建个网桥,它在内核中创建了:

  • 一个Linux bridge(br-xxxx),作为容器网络的中心交换机;
  • 一对veth pair:一端接入bridge,另一端接入容器网络命名空间;
  • iptables规则:DOCKER-USER链处理入站流量,DOCKER-ISOLATION-STAGE-1链隔离不同网络。

当你执行docker run --network mynet nginx,dockerd会:

  1. 调用ip link add vethA type veth peer name vethB创建veth对;
  2. 将vethA加入br-xxxx,vethB移动到容器网络命名空间;
  3. 在容器内执行ip addr add 172.18.0.2/16 dev eth0分配IP;
  4. 在宿主机添加iptables -t nat -A POSTROUTING -s 172.18.0.0/16 ! -o br-xxxx -j MASQUERADE实现SNAT。

如果容器内无法访问外网,先检查iptables -t nat -L POSTROUTING是否有MASQUERADE规则;如果容器间ping不通,用brctl show br-xxxx确认veth是否接入网桥。

4. 生产环境避坑指南:那些文档里绝不会写的“血泪教训”

在某金融公司容器化项目中,我们曾因三个看似微小的配置失误,导致上线后连续三天服务抖动。这些坑不在官方文档里,但每个都价值数万元运维成本。

4.1 CPU限额的“虚假保障”:--cpus与--cpu-quota的本质差异

docker run --cpus 2 nginx看起来是给容器分配2个CPU核心,但实际是--cpu-quota=200000 --cpu-period=100000的语法糖。cpu-period是调度周期(微秒),cpu-quota是该周期内允许使用的CPU时间(微秒)。所以--cpus 2= 每100ms最多用200ms CPU时间 = 平均200%利用率。

但问题来了:如果宿主机只有2个物理核心,而你跑了10个--cpus 2的容器,总配额是2000%,内核调度器会公平分配——每个容器平均拿到20% CPU,远低于预期。更糟的是,--cpus不保证CPU亲和性(CPU affinity),容器进程可能在任意核心间迁移,导致L3缓存失效,性能下降30%。

正确做法:

  • 对延迟敏感服务(如Redis),用--cpus 1 --cpuset-cpus 0绑定到特定核心;
  • 对吞吐量服务(如Nginx),用--cpu-quota=150000 --cpu-period=100000限制为1.5核,留0.5核余量防突发;
  • 永远监控docker stats中的CPU %,若长期>90%,说明配额不足,而非“CPU不够”。

4.2 内存OOM Killer的“无声谋杀”:-m与--memory-swap的死亡组合

docker run -m 1g nginx设置了内存上限1GB,但--memory-swap默认等于-m,即--memory-swap=1g。这意味着容器最多使用1GB内存+0GB swap,一旦内存用满,内核OOM Killer会立即杀死容器内PID 1进程(nginx),日志里只有一行Killed process 12345 (nginx) total-vm:1024000kB, anon-rss:1024000kB。

要启用swap,必须显式设置--memory-swap=2g(内存+swap总和),或--memory-swap=-1(不限制swap)。但生产环境严禁开启swap——SSD随机读写延迟是内存的10万倍,一次swap就会让响应时间从毫秒级飙升至秒级。

终极方案:

  • 用-m 1g --memory-reservation=800m设置软限制(reservation),当宿主机内存紧张时,Docker会优先压缩此容器内存;
  • 同时设置--oom-kill-disable=true禁用OOM Killer(仅限关键服务),配合应用层健康检查自动重启。

4.3 日志爆炸的“定时炸弹”:json-file驱动的磁盘吞噬逻辑

默认json-file日志驱动会将所有stdout/stderr写入/var/lib/docker/containers/xxx/xxx-json.log,且永不自动删除。一个QPS 1000的API服务,每秒产生1KB日志,一天就是86GB!log-opts中的max-size和max-file是唯一防线。

但有个致命陷阱:max-size单位是字符串,必须带单位(10m,1g),写成10会被解释为10字节!某次配置失误,"max-size": "10"导致每个日志文件只有10字节,瞬间生成数万个碎片文件,ls /var/lib/docker/containers/xxx/直接卡死。

安全配置模板:

{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3", "labels": "production", "env": "os,version" } }

labels和env会将容器元数据注入日志,方便ELK检索。但注意:env中若含敏感信息(如DB_PASSWORD),日志会明文泄露——必须在docker run时用--env-file隔离敏感变量。

4.4 镜像拉取的“雪崩效应”:并发docker pull引发的内核OOM

在Kubernetes集群节点上,若同时有50个Pod启动,每个都docker pull registry.example.com/app:v1.2.3,Docker会为每个pull创建独立goroutine,每个goroutine占用约20MB内存。50个并发就是1GB内存,加上dockerd自身内存,极易触发宿主机OOM Killer杀掉dockerd进程。

缓解方案:

  • 在/etc/docker/daemon.json中添加{"max-concurrent-downloads": 3},限制并发下载数;
  • 使用registry-mirror减少网络延迟;
  • 对高频镜像,提前docker pull预热到本地缓存。

4.5 容器时间不同步:--privileged不是万能钥匙

容器内时间与宿主机不同步,会导致JWT token过期、数据库事务失败。很多人直接加--privileged,但这会开放所有设备文件,严重违反最小权限原则。

正确同步方式:

  • 挂载宿主机/etc/localtime:-v /etc/localtime:/etc/localtime:ro;
  • 挂载/dev/rtc设备(需--device /dev/rtc),让容器内ntpd可访问硬件时钟;
  • 或在容器启动脚本中执行runc exec -t <container-id> /bin/sh -c "ntpd -q -p /var/run/ntpd.pid"。

最优雅的方案是使用--uts=host,让容器共享宿主机UTS命名空间,hostname和domainname同步,时间自然一致。

5. Docker命令速查表:按场景分类的“抄作业”清单

以下命令均经过生产环境千次验证,可直接复制使用。每个命令都标注了适用场景、风险提示和替代方案。

5.1 开发调试场景:快速启动、挂载、进入

场景命令说明风险提示
本地开发Web服务docker run -it --rm -p 8080:80 -v $(pwd):/usr/share/nginx/html:ro nginx挂载当前目录为静态文件根,只读防止误删若$(pwd)含中文路径,需先cd到英文路径
调试Python应用docker run -it --rm -v $(pwd):/app -w /app -p 5000:5000 python:3.9 pip install -e . && python app.py挂载代码、设工作目录、一键安装依赖并启动pip install -e .需setup.py存在,否则改用pip install -r requirements.txt
进入运行中容器docker exec -it $(docker ps -q --filter ancestor=nginx --format="{{.ID}}") /bin/bash动态获取最近启动的nginx容器ID并进入若有多个nginx容器,ps -q返回多行,需加`

提示:docker ps -q --filter比docker ps | grep nginx | awk '{print $1}'更可靠,因为后者在列宽变化时会失效。

5.2 构建优化场景:减小镜像、加速CI

场景命令说明风险提示
多阶段构建(推荐)docker build -t myapp:latest --target production .Dockerfile中定义FROM python:3.9 as builder和FROM python:3.9-slim as production--target必须与Dockerfile中AS名称完全一致,大小写敏感
构建缓存穿透docker build --cache-from myapp:latest --cache-to type=inline,mode=max .从远程镜像拉取缓存,构建后内联保存新缓存mode=max需Docker 23.0+,旧版本用mode=min
构建时跳过测试docker build --build-arg SKIP_TESTS=true -t myapp:test .Dockerfile中用ARG SKIP_TESTS,RUN if [ "$SKIP_TESTS" != "true" ]; then pytest; fiARG必须在FROM之后、RUN之前声明,否则无效

5.3 运维监控场景:诊断、清理、备份

场景命令说明风险提示
查看容器详细资源docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"一次性输出所有容器CPU/内存/网络,--no-stream防滚动--format中\t是制表符,非空格,否则表格错位
清理悬空镜像docker image prune -f --filter "until=24h"删除24小时内未被引用的镜像层--filter支持label,before,reference,until最安全
导出容器文件系统`docker export $(docker ps -q --filter status=exitedhead -n1) > backup.tar`将已退出容器的整个rootfs打包

5.4 网络故障排查场景:连通性、端口、DNS

场景命令说明风险提示
测试容器DNSdocker run --rm -it --network host alpine nslookup google.com使用宿主机网络栈测试DNS解析--network host绕过Docker网络,直接验证系统DNS
抓取容器网络包docker run -it --rm --net container:myapp --pid container:myapp nicolaka/netshoot tcpdump -i eth0 port 80复用myapp容器的网络和PID命名空间抓包nicolaka/netshoot是专用调试镜像,比alpine更全
查看容器端口映射docker port $(docker ps -q --filter ancestor=nginx)输出80/tcp -> 0.0.0.0:8080,确认端口绑定若容器用-P随机端口,此命令必用,-p固定端口可省略

注意:所有docker ps -q命令都应加--filter限定范围,避免-q返回空导致后续命令失败。例如docker stop $(docker ps -q)会停止所有容器,灾难性操作。

6. Docker之外的思考:当容器不再是银弹

在某次微服务重构中,我们把所有Java应用打包成Docker镜像,却发现GC停顿时间

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询