Docker 从入门到精通实战手册
版本基线:Docker Engine 24+、Docker Compose v2(docker compose)、Harbor 2.x、Docker Swarm(经典模式)
目录
第 1 章 Docker 是什么 1.1 为什么需要容器 传统部署面临的核心问题:
环境不一致 :开发环境能跑,测试/生产环境跑不起来(”在我机器上没问题”)。
依赖冲突 :多个应用需要不同版本的库、运行时(Python 2/3、OpenSSL、glibc 等)。
资源浪费 :虚拟机整套操作系统开销大,启动慢,单机利用率低。
交付复杂 :从代码到运行的路径长,依赖部署文档和人工操作。
容器(Container)把应用及其全部依赖打包成标准制品(镜像) ,在任何装有容器运行时的机器上以几乎一致的方式运行,从而解决上述问题。
1.2 容器 vs 虚拟机
对比项
虚拟机 (VM)
容器 (Container)
隔离级别
硬件级虚拟化(Hypervisor)
操作系统级隔离(namespace + cgroup)
内核
每个 VM 独享一个 Guest OS 内核
共享宿主机内核
启动速度
分钟级
秒级(毫秒~秒)
镜像体积
GB 级
MB ~ 数百 MB
资源占用
高(OS 开销)
低(仅进程 + 依赖)
隔离强度
强(独立内核)
弱(共享内核,逃逸风险更高)
密度
低
高(一台机器可跑上百个容器)
典型工具
VMware、KVM、VirtualBox、Hyper-V
Docker、containerd、Podman
结论 :虚拟机更安全、更隔离;容器更轻量、更高效。生产环境中常混合使用(VM 内跑容器)。
1.3 Docker 核心组件
组件
说明
镜像 (Image)
只读模板,由多层文件系统叠加而成,是容器的”模板/类”
容器 (Container)
镜像的运行实例,可读可写,是”对象”
仓库 (Registry)
存放和分发镜像的服务,如 Docker Hub、Harbor、私有 Registry
Dockerfile
描述如何构建镜像的脚本(声明式)
Docker Engine
核心守护进程,负责构建、运行、管理容器
Docker Client
命令行/API 客户端,与守护进程通信
Docker Compose
多容器编排工具(YAML 声明服务)
Docker Swarm / Kubernetes
容器集群编排平台
1.4 Docker 架构 Docker 采用 C/S(客户端/服务端)架构 :
1 2 3 4 5 6 7 8 9 10 +------------------+ REST API (Unix Socket / TCP) +---------------------------+ | Docker Client | <----------------------------------------> | Docker Daemon (dockerd) | | (docker CLI) | unix:///var/run/docker.sock | | +------------------+ | +---------------------+ | | | containerd | | | | +---------------+ | | | | | runc (OCI) | | | | | +---------------+ | | | +---------------------+ | +---------------------------+
dockerd :Docker 守护进程,负责镜像管理、容器生命周期、网络、存储。
containerd :行业标准容器运行时,负责容器执行(由 CNCF 托管)。
runc :OCI 运行时,真正通过 Linux 内核特性(namespace/cgroup)创建容器。
docker CLI :用户操作的命令行工具,也支持 docker context 管理多个远程 daemon。
1.5 Docker 工作原理简述 容器不是”轻量虚拟机”,而是被隔离的普通进程 :
namespace(命名空间) :让进程拥有独立的 PID、网络、挂载点、用户、UTS、IPC 视图,实现”看起来像一台独立机器”。
cgroup(控制组) :限制和统计 CPU、内存、磁盘 IO 等资源使用。
overlayfs / 存储驱动 :把镜像的只读层和容器的可写层叠加成统一视图,实现分层与写时复制(CoW)。
镜像分层 :多容器共享底层只读层,节省磁盘与内存,拉取更快。
详细原理见 第 15 章 。
第 2 章 安装与基础配置 2.1 环境与版本要求
平台
要求
Linux
内核 ≥ 3.10(推荐 5.x+),x86_64 / arm64
Windows
Windows 10/11 专业版/企业版,或 Windows Server 2019+,需开启 WSL2 或 Hyper-V
macOS
macOS 11+(Big Sur 以上)
内存
建议 ≥ 4GB(Docker Desktop 默认分配 2GB)
查看内核与系统信息:
1 2 uname -rcat /etc/os-release
2.2 Ubuntu 安装(推荐方式:官方 apt 源) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 sudo apt-get remove docker docker-engine docker.io containerd runcsudo apt-get updatesudo apt-get install -y ca-certificates curl gnupg lsb-releasesudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpgsudo chmod a+r /etc/apt/keyrings/docker.gpgecho "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullsudo apt-get updatesudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginsudo docker versionsudo docker run --rm hello-world
2.3 CentOS / Rocky / AlmaLinux 安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 sudo yum remove docker docker-client docker-common docker-selinux docker-enginesudo yum install -y yum-utils device-mapper-persistent-data lvm2sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.reposudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-pluginsudo systemctl enable --now docker
2.4 Windows / macOS:Docker Desktop
到 docker.com/products/docker-desktop 下载安装包。
Windows 需先在”启用或关闭 Windows 功能”中开启 WSL2 (推荐)或 Hyper-V ,并安装 WSL2 内核更新包。
安装完成后,在 Settings → Resources 中调整 CPU / 内存 / 磁盘配额。
配置镜像加速(见 2.5),国内网络建议开启。
生产服务器通常是 Linux;Windows/macOS 的 Docker Desktop 主要面向开发。
2.5 配置国内镜像加速 编辑 /etc/docker/daemon.json:
1 2 3 4 5 6 7 8 9 { "registry-mirrors" : [ "https://docker.m.daocloud.io" , "https://dockerproxy.com" , "https://docker.nju.edu.cn" ] , "log-driver" : "json-file" , "log-opts" : { "max-size" : "50m" , "max-file" : "5" } }
重启生效:
1 2 sudo systemctl restart docker docker info | grep -A5 Registry
镜像加速只对 Docker Hub 生效;私有仓库不受影响。加速地址稳定性会变化,请以最新可用源为准。
2.6 非 root 用户管理 Docker 1 2 3 4 5 sudo usermod -aG docker $USER newgrp docker docker ps
安全警告 :加入 docker 组等同于获得 root 权限(可以挂载宿主机目录、执行特权操作),生产环境请谨慎授权。
2.7 卸载 Docker 1 2 3 4 5 6 7 sudo apt-get purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginsudo rm -rf /var/lib/docker /var/lib/containerdsudo yum remove -y docker-ce docker-ce-cli containerd.iosudo rm -rf /var/lib/docker /var/lib/containerd
2.8 安装后的基线检查 1 2 3 4 docker version docker info docker run --rm hello-world systemctl status docker
安装检查清单:
第 3 章 镜像管理 3.1 镜像概念与分层 镜像(Image) 是一个只读的、分层的文件系统快照,包含了运行应用所需的一切:操作系统基础文件、运行时、依赖、应用代码、启动命令。
1 2 3 4 5 6 7 8 +-----------------------------+ | Layer 6: 应用代码 | <- 构建时添加 | Layer 5: 依赖安装 | | Layer 4: 基础工具 | | Layer 3: 基础镜像更新 | | Layer 2: 基础镜像 (如 ubuntu) | | Layer 1: 引导文件系统 (bootfs) | +-----------------------------+
每一层只记录相对上一层的变更 (新增/修改/删除文件)。
多个镜像共享相同底层时,磁盘上只存一份(层共享 )。
容器运行时,Docker 在镜像之上加一层可写层 (容器层),对文件的修改采用写时复制(CoW) 。
因此:镜像不可变 ,任何变更都产生新层;容器退出后修改即丢失(除非写入挂载卷)。
删除镜像时如果有多层被其他镜像引用,只会删除未被引用的层。
3.2 镜像常用命令速查
命令
作用
docker search <关键词>
在仓库搜索镜像
docker pull <镜像>
拉取镜像到本地
docker push <镜像>
推送镜像到仓库
docker images / docker image ls
列出本地镜像
docker image inspect <镜像>
查看镜像详细信息(JSON)
docker history <镜像>
查看镜像构建历史(各层命令)
docker tag <镜像> <新标签>
打标签
docker rmi <镜像>
删除镜像
docker save -o file.tar <镜像>
导出镜像为 tar 文件
docker load -i file.tar
从 tar 导入镜像
docker image prune
清理悬空镜像(dangling)
docker image ls -f dangling=true
查看悬空镜像
docker manifest inspect <镜像>
查看多架构清单
docker buildx
多架构构建工具(见 5.7)
3.3 拉取与查看 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 docker pull nginx docker pull mysql:8.0 docker pull redis:7-alpine docker pull registry.example.com/app/api:v1.2.3 docker pull --platform linux/arm64 nginx:alpine docker images docker image ls --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" docker image inspect nginx | head -50 docker history nginx
3.4 标签与删除 1 2 3 4 5 6 7 docker tag nginx:latest myregistry.com/nginx:v1.0 docker rmi nginx:latest docker rmi $(docker images -q) docker rmi $(docker images -f dangling=true -q)
3.5 save/load 与 export/import 的区别
操作
内容
保留信息
典型场景
docker save / load
镜像 (含全部层、标签、历史)
完整
离线分发、备份、迁移
docker export / import
容器文件系统快照 (打平为一层)
丢失历史/标签/层
急救导出容器数据
1 2 3 4 5 6 7 8 9 10 11 12 docker save -o nginx.tar nginx:latest scp nginx.tar user@10.0.0.5:/tmp/ ssh user@10.0.0.5 'docker load -i /tmp/nginx.tar' docker save -o all.tar nginx:latest mysql:8.0 redis:7 docker load < all.tar docker export mycontainer > rootfs.tarcat rootfs.tar | docker import - myimage:v1
生产规范:分发走 Registry(仓库) ,不推荐大文件 tar 传递;save/load 仅用于断网机房等场景。
3.6 搭建私有 Registry(简单版) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 docker run -d --name registry \ -p 5000:5000 \ -v /data/registry:/var/lib/registry \ --restart=always \ registry:2 docker tag nginx:latest localhost:5000/nginx:v1 docker push localhost:5000/nginx:v1 docker pull localhost:5000/nginx:v1 curl -s http://localhost:5000/v2/_catalog | python3 -m json.tool
跨主机推送需配置 HTTP 信任 (daemon.json):
1 2 3 { "insecure-registries" : [ "192.168.1.10:5000" ] }
单节点 Registry 无认证、无高可用、无漏洞扫描,适合内网小规模;企业级请用 Harbor(见 3.7)。
3.7 Harbor 企业级镜像仓库 Harbor 提供:基于角色的访问控制(RBAC)、镜像复制、漏洞扫描(Trivy)、签名(Notation/Cosign)、审计日志、项目隔离。
1 2 3 4 5 6 7 wget https://github.com/goharbor/harbor/releases/download/v2.11.0/harbor-offline-installer-v2.11.0.tgz tar -xzf harbor-offline-installer-v2.11.0.tgzcd harborcp harbor.yml.tmpl harbor.yml
harbor.yml 关键配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 hostname: registry.example.com http: port: 80 https: port: 443 certificate: /data/cert/server.crt private_key: /data/cert/server.key harbor_admin_password: ChangeMe123! database: password: db_pass_123 data_volume: /data/harbor trivy: ignore_unfixed: true
1 2 3 4 5 6 7 8 9 10 11 sudo ./install.sh --with-trivy --with-chartmuseum docker ps curl -sk https://registry.example.com/api/v2.0/ping docker login registry.example.com docker tag nginx:latest registry.example.com/library/nginx:v1 docker push registry.example.com/library/nginx:v1
Harbor 运维要点:
升级前必须备份数据库 (PostgreSQL)和 /data 目录。
镜像复制(Replication)可做跨机房/双活同步。
定期执行 GC(垃圾回收)清理未引用的 blob。
为每个项目设置保留策略 (如只保留最近 30 个 tag)。
3.8 镜像命名规范 标准镜像名格式:[registry/][namespace/]repository[:tag][@digest]
1 2 3 docker.io/library/nginx:latest # 官方镜像全名 registry.example.com/devops/api:v1.0.0 # 私有仓库项目 nginx@sha256:3f8d0d1e... # 按摘要引用(不可变、最安全)
团队命名规范建议:
1 2 3 {仓库域名}/{项目组}/{应用名}:{版本}-{环境} registry.example.com/order/api:2.3.1-prod registry.example.com/order/api:20260802-001
生产禁用 latest ,必须使用语义化版本或 commit hash。
重要镜像同时记录 @sha256 摘要,保证可复现。
第 4 章 容器生命周期管理 4.1 容器状态机 1 2 3 4 5 6 7 Created(已创建) -> Running(运行中) -> Paused(暂停) / Exited(退出) \ | | \ v v \--------> Dead(死亡) <----- Stopped(停止) | v Removed(删除)
常用状态字段:created / running / restarting / exited / paused / dead。
4.2 常用命令速查
命令
作用
docker run <镜像>
创建并启动容器
docker create <镜像>
只创建不启动
docker start/stop/restart <容器>
启停/重启
docker pause/unpause <容器>
暂停/恢复(SIGSTOP,不释放资源)
docker rm <容器>
删除已停止容器
docker rm -f <容器>
强制删除运行中容器
docker ps / docker ps -a
列出运行中/全部容器
docker exec -it <容器> bash
进入容器
docker logs [-f] <容器>
查看/跟踪日志
docker top <容器>
查看容器内进程
docker stats
实时资源占用
docker inspect <容器>
查看容器元数据(JSON)
docker cp 宿主机文件 <容器>:路径
宿主机与容器互拷文件
docker rename <旧名> <新名>
重命名
docker container prune
清理已停止容器
4.3 run 参数详解(高频) 1 2 3 4 5 6 7 docker run [OPTIONS] IMAGE [COMMAND] [ARG...] docker run -d --name web -p 8080:80 nginx
参数分类详解:
分类
参数
说明
前台/后台
-d / -it
后台运行 / 交互式终端
端口
-p 8080:80、-p 127.0.0.1:8080:80、-P
端口映射 / 随机高端口 / 自动映射所有 EXPOSE
数据
-v /host:/container、-v myvol:/data、--mount
绑定挂载 / 命名卷
环境
-e KEY=value、--env-file .env
环境变量
网络
--network mynet、--network host、--ip 172.20.0.10
网络模式与固定 IP
资源
-m 512m、--cpus 1.5、--memory-swap
内存/CPU 限制
重启
--restart always、--restart unless-stopped、--restart on-failure:5
自动重启策略
权限
--user 1000:1000、--privileged、--cap-add
运行用户/特权
健康
--health-cmd、--health-interval
健康检查
主机名
-h web01、--add-host a.com:1.2.3.4
主机名/hosts
只读
--read-only
根文件系统只读
其他
--rm、--init、--restart
退出即删 / 内置 init(僵尸进程回收)
4.4 创建、启动、停止、删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 docker create --name web -p 8080:80 nginx docker start web docker stop web docker restart web docker rm web docker rm -f web docker stop $(docker ps -q) docker rm $(docker ps -aq) docker container prune -f
docker stop 先发 SIGTERM(应用可优雅退出),超时后发 SIGKILL;docker kill 直接 SIGKILL。
4.5 进入容器与执行命令 1 2 3 4 5 6 7 8 9 10 11 12 13 docker exec -it web bash docker exec web ls /etc/nginx docker exec -d web touch /tmp/flag docker attach web docker cp ./index.html web:/usr/share/nginx/html/ docker cp web:/var/log/nginx/access.log ./access.log
生产排查原则:优先看日志,尽量不进入容器修改 ;容器内修改在容器重建后丢失。
4.6 端口映射 1 2 3 4 5 6 7 8 9 docker run -d --name web -p 8080:80 nginx docker run -d --name web -p 127.0.0.1:8080:80 nginx docker run -d --name web -p 8080:80/tcp -p 8080:80/udp app docker run -d --name web -P nginx docker port web
常见问题:
端口冲突报错:Bind for 0.0.0.0:8080 failed: port is already allocated —— 用 ss -tlnp | grep 8080 查占用并处理。
-p 只对 bridge 网络生效;host 模式下不需要也不允许端口映射。
4.7 环境变量 1 2 3 4 5 6 7 8 9 10 11 12 13 docker run -d --name db -e MYSQL_ROOT_PASSWORD=root123 -e MYSQL_DATABASE=appdb mysql:8.0cat > .env <<EOF MYSQL_ROOT_PASSWORD=root123 MYSQL_DATABASE=appdb EOF docker run -d --name db --env-file .env mysql:8.0 docker exec db env docker inspect db --format '{{range .Config.Env}}{{println .}}{{end}}'
生产规范:敏感信息不要硬编码进镜像或命令行 ,使用 Docker Secrets(Swarm)、K8s Secret,或环境变量注入 + 密钥管理平台(Vault)。
4.8 资源限制 1 2 3 4 5 6 7 8 9 10 11 12 13 docker run -d --cpus=1.5 --name app myapp docker run -d -m 512m --memory-reservation 384m --name app myapp docker run -d -m 512m --memory-swap 1g --name app myapp docker inspect app --format '{{.HostConfig.Memory}} {{.HostConfig.NanoCpus}}' docker stats docker update --cpus 2 -m 1g app
内存参数说明:
-m 512m = 限制容器最多使用 512MB 内存。
--memory-swap 1g = 内存 512MB + swap 可再借 512MB(总量 1G)。
进程被 OOM 杀掉时,docker inspect 的 State.OOMKilled 为 true。
4.9 重启策略
策略
行为
适用
no(默认)
不自动重启
调试、一次性任务
on-failure[:N]
非零退出码时重启,最多 N 次
短暂崩溃可自愈的任务
always
总是重启(含 daemon 重启后)
常驻服务
unless-stopped
除非手动 stop,否则总是重启
生产常驻服务(推荐)
1 2 docker run -d --restart always --name web nginx docker update --restart unless-stopped web
always 与 unless-stopped 的区别:daemon 重启后 always 会拉回手动停止的容器,unless-stopped 不会。
4.10 容器退出码与排障
退出码
含义
常见原因
0
正常退出
命令执行完毕
1
一般错误
启动命令失败、配置错误
126
命令无法执行
权限不足、二进制无执行 权限
127
命令不存在
command not found
137
SIGKILL
被 OOM Kill 或 docker kill
139
SIGSEGV
段错误,多为应用崩溃
143
SIGTERM
被优雅停止(docker stop)
1 docker inspect web --format '{{.State.ExitCode}} {{.State.Error}} {{.State.OOMKilled}}'
第 5 章 Dockerfile 详解与镜像构建 5.1 Dockerfile 指令全解 FROM —— 指定基础镜像(必选,第一条指令) 1 2 3 FROM ubuntu:24.04 FROM node:20 -alpineFROM scratch
最佳实践:
优先使用官方镜像;固定版本号,不要用 latest 。
生产建议同时锁定摘要:FROM node:20-alpine@sha256:xxxx。
选用 alpine (小)或 distroless (更安全)来减小体积。
RUN —— 构建时执行命令(每一条 RUN 产生一层) 1 2 RUN apt-get update && apt-get install -y --no-install-recommends curl \ && rm -rf /var/lib/apt/lists/*
关键点:
把多条命令用 && 连接成一条 RUN,减少层数。
apt/yum 安装后立即清理缓存 ,避免镜像膨胀。
避免在 RUN 中写死网络不可达的源;国内构建可换阿里源。
COPY / ADD —— 拷贝文件 1 2 3 COPY ./app /app ADD ./jdk.tar.gz /opt/ COPY --chown =app:app app.js /app/
区别:
COPY:仅本地文件拷贝,推荐首选。
ADD:支持 URL 下载和 tar 自动解压;URL 下载不可缓存且不安全,能不用就不用 。
CMD / ENTRYPOINT —— 启动命令 1 2 3 4 5 CMD ["nginx" , "-g" , "daemon off;" ] CMD nginx -g "daemon off;" ENTRYPOINT ["python" , "app.py" ] CMD ["--port" , "8080" ]
CMD :容器启动时的默认命令,可被 docker run 后面的参数覆盖 。
ENTRYPOINT :不可被覆盖(可用 --entrypoint 强制覆盖),适合定义固定入口。
推荐组合:ENTRYPOINT 固定程序,CMD 提供默认参数。
两者都支持 exec 形式(推荐,信号可直达)和 shell 形式。
EXPOSE —— 声明端口(文档性,不真正发布) 1 2 EXPOSE 80 443 EXPOSE 3306 /tcp
只是元数据;真正发布端口靠 docker run -p 或 Compose 的 ports。
ENV / ARG —— 环境变量与构建参数 1 2 3 ARG VERSION=1.0 ENV APP_VERSION=$VERSION ENV PATH=/usr/local/bin:$PATH
1 docker build --build-arg VERSION=2.0 -t app:2.0 .
ARG:构建期,docker build --build-arg 传入,不进镜像(除非被 ENV 引用)。
ENV:写入镜像,运行期可用。
敏感信息不要 写进 ENV(镜像层可见),用运行时注入。
WORKDIR —— 工作目录 1 2 3 WORKDIR /app RUN npm install CMD ["npm" , "start" ]
后续 RUN/CMD/ENTRYPOINT/COPY 的相对路径都基于 WORKDIR,推荐每个阶段开头设置。
USER —— 运行用户(安全关键) 1 2 RUN useradd -r -u 1001 appuser USER appuser
默认 root 运行容器是生产环境大忌;尽量用非 root 用户,配合只读根文件系统(--read-only)。
VOLUME —— 声明匿名卷
作用是防止容器写数据到可写层;实际生产用 -v 或 Compose 显式命名卷更可控。
HEALTHCHECK —— 健康检查 1 2 HEALTHCHECK --interval=30s --timeout =3s --start-period=10s --retries=3 \ CMD curl -f http://localhost:8080/health || exit 1
容器状态会显示 healthy / unhealthy / starting。
Swarm 负载均衡和 K8s 探针都依赖它。
LABEL / MAINTAINER 1 2 LABEL org.opencontainers.image.source="https://github.com/team/app" LABEL version="1.0" description="订单服务"
MAINTAINER 已废弃,改用 LABEL;给镜像打组织/版本/负责人标签便于审计。
ONBUILD —— 触发指令(了解即可) 1 2 3 FROM node:20 -alpineONBUILD COPY . /app ONBUILD RUN npm install
当该镜像被其他 Dockerfile 作为 FROM 时自动触发,常用于制作语言基础镜像。
.dockerignore —— 排除文件 1 2 3 4 5 6 7 8 9 node_modules dist .git .gitignore *.log Dockerfile* .dockerignore .env
不排除会导致构建上下文巨大、慢、甚至把密钥打进镜像。
5.2 完整 Dockerfile 示例:Python Flask 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 FROM python:3.12 -slim AS builderWORKDIR /build COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -t /deps FROM python:3.12 -slimWORKDIR /app COPY --from=builder /deps /usr/local/lib/python3.12/site-packages COPY app.py . RUN useradd -r -u 1001 app && chown -R app:app /app USER appEXPOSE 8000 HEALTHCHECK --interval=30s --timeout =3s CMD python -c "import urllib.request;urllib.request.urlopen('http://127.0.0.1:8000/health')" CMD ["gunicorn" , "-b" , "0.0.0.0:8000" , "-w" , "2" , "app:app" ]
5.3 多阶段构建(Multi-stage Build) 解决”构建环境大、运行环境小”的矛盾:一个 Dockerfile 里多个 FROM,最终只保留最后一个阶段的文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 FROM golang:1.22 -alpine AS builderWORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app/server FROM alpine:3.20 RUN adduser -D -u 1001 app COPY --from=builder /app/server /usr/local/bin/server USER appEXPOSE 8080 ENTRYPOINT ["server" ]
Java 多阶段示例:
1 2 3 4 5 6 7 8 9 10 11 12 FROM maven:3.9 -eclipse-temurin-17 AS buildWORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests FROM eclipse-temurin:17 -jre-alpineRUN adduser -D -u 1001 app COPY --from=build /build/target/app.jar /app/app.jar USER appENTRYPOINT ["java" , "-jar" , "/app/app.jar" ]
Node 多阶段示例:
1 2 3 4 5 6 7 8 9 10 11 FROM node:20 -alpine AS buildWORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM nginx:1.27 -alpineCOPY --from=build /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80
5.4 构建流程与缓存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 docker build -t myapp:v1 . docker build -t myapp:v1 -f docker/Dockerfile.prod . docker build -t myapp:v1 -f Dockerfile ./app docker build --no-cache -t myapp:v1 . docker history myapp:v1 DOCKER_BUILDKIT=1 docker build --progress=plain -t myapp:v1 .
缓存命中规则(逐条):
从第一条指令开始逐层比对,若某条指令的输入(基础镜像、上下文文件、参数)没变,则复用缓存层。
文件发生变化会导致该 RUN/COPY 及其之后所有层 失效。
因此把变化少的指令放前面 (如 COPY package*.json → RUN npm ci → COPY . .)。
ADD 的 URL 内容变化可能导致缓存失效;ARG 变化也会使后续层失效。
经典错误: 先 COPY . . 再 RUN npm install —— 任何代码改动都触发重新安装依赖。正确顺序:
1 2 3 COPY package.json package-lock.json ./ RUN npm ci COPY . .
5.5 镜像瘦身技巧
方法
效果
做法
多阶段构建
大幅减小
构建阶段与运行阶段分离
alpine/distroless 基础镜像
体积减半以上
FROM alpine / FROM gcr.io/distroless/static
合并 RUN
减少层数
apt-get update && install && rm -rf ...
清理包管理缓存
减几十~上百 MB
rm -rf /var/lib/apt/lists/*
静态编译 Go/Rust
可到 10MB 级
CGO_ENABLED=0
--squash(实验性,慎用)
压平所有层
不推荐,破坏层共享
docker buildx --squash
同上
BuildKit 支持
1 2 docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
5.6 构建多架构镜像(buildx) 1 2 3 4 5 6 7 8 9 10 11 12 docker buildx create --name multiarch --use docker run --privileged --rm tonistiigi/binfmt --install all docker buildx build \ --platform linux/amd64,linux/arm64 \ -t registry.example.com/myapp:v1 \ --push . docker buildx imagetools inspect registry.example.com/myapp:v1
5.7 Dockerfile 编写检查清单
第 6 章 数据管理 6.1 三种数据存储方式对比
方式
生命周期
位置
适用场景
容器可写层
随容器删除而消失
容器内
临时文件(不建议存重要数据)
绑定挂载 (bind mount)
随宿主机文件存在
宿主机任意路径
配置文件、日志、开发热更新
命名卷 (volume)
Docker 管理,独立于容器
/var/lib/docker/volumes/
数据库等持久化数据(推荐)
tmpfs
仅内存,重启消失
内存
敏感临时数据、性能要求高的缓存
6.2 数据卷(Volume,推荐) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 docker volume create mysql-data docker run -d --name mysql -v mysql-data:/var/lib/mysql mysql:8.0 docker run -d --name mysql \ --mount type =volume,source =mysql-data,target=/var/lib/mysql \ mysql:8.0 docker run -d -v /var/lib/mysql mysql:8.0 docker volume ls docker volume inspect mysql-data docker volume rm mysql-data docker volume prune
为什么推荐命名卷:
数据与容器解耦,删除/重建容器数据不丢。
Docker 自动管理目录权限与属主。
备份/迁移/跨主机复制(卷驱动)更方便。
支持卷驱动(如 NFS、云盘插件)实现共享存储。
6.3 绑定挂载(Bind Mount) 1 2 3 4 5 6 7 8 9 10 11 12 13 docker run -d --name web -v /srv/www:/usr/share/nginx/html:ro nginx docker run -d --name web \ --mount type =bind ,source =/srv/www,target=/usr/share/nginx/html,readonly \ nginx docker run -d --name nginx \ -v /etc/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf \ nginx
绑定挂载注意事项:
宿主机目录不存在时,-v 会自动创建 (以 root 创建,可能权限异常);--mount 不会自动创建。
挂载后容器内看到的属主是宿主机文件的属主,注意 UID 对齐(常见坑:数据库权限报错)。
目录内容会覆盖 容器内原有目录(例如把宿主机空目录挂到 /var/lib/mysql 会覆盖初始化数据)。
开发环境热更新很方便:改宿主机代码容器立即生效。
6.4 tmpfs 挂载 1 2 3 docker run -d --name cache \ --tmpfs /tmp:rw,noexec,nosuid,size=128m \ redis:7
数据在内存中,容器停止即清空;适合 session、临时缓存。
6.5 数据备份与恢复 1 2 3 4 5 6 7 8 9 10 11 12 13 14 docker run --rm \ -v mysql-data:/data \ -v /backup:/backup \ ubuntu:24.04 tar czf /backup/mysql-data-$(date +%F).tar.gz -C /data . docker run --rm \ -v mysql-data:/data \ -v /backup:/backup \ ubuntu:24.04 tar xzf /backup/mysql-data-2026-08-02.tar.gz -C /data docker exec mysql sh -c 'exec mysqldump --all-databases -uroot -p"$MYSQL_ROOT_PASSWORD"' > /backup/dump.sql
6.6 卷使用规范
生产数据库(MySQL/PostgreSQL/Redis 持久化)必须使用命名卷 ,并定期备份+验证恢复。
日志目录挂载宿主机路径或卷,配合日志轮转,避免撑爆磁盘。
配置类文件用 bind mount + 只读(:ro)。
命名规则建议:{应用}-{用途}-{环境},如 order-db-prod。
定期 docker volume prune 前先确认卷未被引用(docker ps -a 检查)。
第 7 章 网络管理 7.1 网络模式总览
模式
说明
使用场景
bridge(默认)
容器通过虚拟网桥(docker0)与外界通信,可端口映射
单机多容器,最常用
host
容器直接使用宿主机网络栈(无隔离、无端口映射)
高性能场景、依赖本机端口的服务
none
无网络
纯计算任务、安全隔离
container:<其他容器>
与指定容器共享网络栈
调试、代理场景
overlay
跨主机网络(Swarm/K8s 使用)
集群场景
7.2 bridge 网络(默认) 1 2 3 4 5 6 7 宿主机 +--------------------------------------------------+ | docker0 (172.17.0.1/16) | | | | | +---- veth-pair ---- 容器 eth0 (172.17.0.2) | | +---- veth-pair ---- 容器 eth0 (172.17.0.3) | +--------------------------------------------------+
每个容器有一对 veth 虚拟网卡,一端在容器内(eth0),一端连到 docker0 网桥。
容器间默认可以互通(通过网桥)。
容器访问外网走 NAT(MASQUERADE【SNAT 的动态版本】) ;外网访问容器需端口映射(DNAT/iptables)。
1 2 3 docker inspect web --format '{{.NetworkSettings.Networks.bridge.IPAddress}}' docker network inspect bridge
7.3 host 模式 1 docker run -d --name web --network host nginx
特点与坑:
性能最好(无 NAT 开销),容器内看到的就是宿主机网络。
不能同时用 -p (会警告且无效)。
容器内端口冲突即宿主机端口冲突。
网络隔离丧失,安全性下降。
7.4 none 模式 1 docker run -d --name worker --network none myjob
容器只有 lo 回环接口,适合离线计算任务或需要严格网络隔离的场景。
7.5 自定义网络(生产推荐) 默认 bridge 网络不支持容器名 DNS 解析 (老版本),且与宿主网络耦合。生产用自定义 bridge:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 docker network create -d bridge --subnet 172.20.0.0/16 --gateway 172.20.0.1 app-net docker run -d --name web --network app-net nginx docker run -d --name api --network app-net myapi docker run -d --name db --network app-net mysql:8.0 docker network connect app-net web docker network disconnect app-net web docker run -d --name db --network app-net --ip 172.20.0.10 mysql:8.0 docker network rm app-net
7.6 容器间通信(服务发现) 在同一个自定义网络 内,容器可用容器名 直接访问(Docker 内置 DNS):
1 2 3 4 5 6 docker exec web curl http://api:8080/health docker exec web getent hosts api
连通性规则:
同自定义网络:容器名互访,推荐。
默认 bridge:可 IP 互访,但无容器名 DNS (需 --link,已废弃,不推荐)。
不同网络:默认隔离,需 docker network connect 加入才能通信。
多网络容器:同时拥有多个网络的 IP,可做”跳板”桥接。
7.7 端口发布原理 1 2 3 4 5 6 7 外网/宿主机 8080 端口 | v iptables DNAT 规则 (docker-proxy / hairpin) | v docker0 网桥 -> 容器 eth0:80
-p 8080:80 会同时创建 iptables 规则(DOCKER 链)和 docker-proxy 用户态进程。
查看规则:
1 2 iptables -t nat -L -n | grep 8080 ss -tlnp | grep 8080
7.8 跨主机网络(overlay 简介) 单机 bridge 无法跨主机。Docker Swarm 模式内置 overlay 网络(VXLAN 封装):
1 2 3 4 5 docker network create -d overlay --attachable prod-net docker service create --network prod-net --name api myapp
云原生场景更常用 K8s 的 CNI 插件(Calico/Flannel/Cilium)实现跨主机网络。
7.9 网络排障速查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 docker exec web ip addr docker exec web ip route docker exec web getent hosts db curl http://127.0.0.1:8080 ss -tlnp | grep -E '8080|3306'
第 8 章 Docker Compose 编排 8.1 什么是 Compose Compose 用一个 YAML 文件 声明多个容器(服务)、网络、卷、依赖关系,一条命令完成整栈的启动/停止/重建。适合:开发环境、单机多服务、测试环境、CI 中的集成测试。
注意区分:Compose v1 (docker-compose,Python 实现,已停更)与 Compose v2 (docker compose,Go 插件,Docker 24+ 自带)。本文使用 v2。
8.2 安装 1 2 3 4 5 6 7 8 9 10 docker compose version DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME /.docker} mkdir -p $DOCKER_CONFIG /cli-plugins curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 \ -o $DOCKER_CONFIG /cli-plugins/docker-composechmod +x $DOCKER_CONFIG /cli-plugins/docker-compose docker compose version
8.3 compose.yaml 核心语法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 name: myapp services: web: image: nginx:1.27-alpine container_name: myapp-web ports: - "8080:80" environment: - TZ=Asia/Shanghai - APP_ENV=prod env_file: - .env volumes: - ./html:/usr/share/nginx/html:ro - web-logs:/var/log/nginx networks: - app-net depends_on: - api restart: unless-stopped healthcheck: test: ["CMD" , "curl" , "-fs" , "http://localhost/" ] interval: 30s timeout: 3s retries: 3 api: build: context: ./api dockerfile: Dockerfile image: registry.example.com/api:1.0 ports: - "8081:8080" environment: DB_HOST: db DB_PORT: "3306" depends_on: db: condition: service_healthy deploy: resources: limits: cpus: "1.0" memory: 512M db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root123} MYSQL_DATABASE: appdb volumes: - db-data:/var/lib/mysql - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro ports: - "3306:3306" networks: - app-net healthcheck: test: ["CMD" , "mysqladmin" , "ping" , "-h" , "127.0.0.1" , "-uroot" , "-p$$MYSQL_ROOT_PASSWORD" ] interval: 10s timeout: 5s retries: 5 networks: app-net: driver: bridge ipam: config: - subnet: 172.28 .0 .0 /16 volumes: db-data: web-logs:
关键要点:
name 定义项目名;默认项目名 = 目录名。
同一 Compose 项目下的服务自动加入同名默认网络,可用服务名 互相访问(如 db、api)。
$$ 表示转义的 $(避免 Compose 提前插值,如密码中的 $)。
depends_on 控制启动顺序;condition: service_healthy 可等待健康检查通过。
build + image 同时存在时:本地构建,成功后打上 image 标签。
8.4 常用命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 docker compose up -d docker compose up -d --build docker compose up -d --scale web=3 docker compose ps docker compose ps -a docker compose logs -f --tail 100 web docker compose top docker compose exec web bash docker compose run --rm web pytest docker compose stop docker compose down docker compose down -v docker compose down --rmi all docker compose config docker compose config --services docker compose config --volumes docker compose ps --format "table {{.Name}}\t{{.Status}}"
8.5 变量与多环境 1 2 image: mysql:${MYSQL_VERSION:-8.0}
1 2 3 4 MYSQL_VERSION=8.0 MYSQL_ROOT_PASSWORD=secret123 APP_ENV=prod
多环境方案:
多个 compose 文件叠加:docker compose -f base.yaml -f dev.yaml up -d
每个环境一个目录 + 各自的 .env。
不要提交 .env 到 Git;用 .env.example 模板。
1 2 3 docker compose -f compose.yaml -f compose.prod.yaml up -d docker compose -f compose.prod.yaml config > 最终配置校验.yaml
8.6 实战:LNMP 完整栈 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 name: lnmp services: nginx: image: nginx:1.27-alpine ports: - "80:80" - "443:443" volumes: - ./www:/usr/share/nginx/html:ro - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/ssl:/etc/nginx/ssl:ro - ./logs/nginx:/var/log/nginx depends_on: - php restart: unless-stopped php: build: ./php volumes: - ./www:/var/www/html environment: - TZ=Asia/Shanghai depends_on: - mysql restart: unless-stopped mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: appdb volumes: - mysql-data:/var/lib/mysql - ./init:/docker-entrypoint-initdb.d:ro healthcheck: test: ["CMD" , "mysqladmin" , "ping" , "-h" , "localhost" ] interval: 10s timeout: 5s retries: 5 restart: unless-stopped redis: image: redis:7-alpine command: ["redis-server" , "--appendonly" , "yes" ] volumes: - redis-data:/data restart: unless-stopped volumes: mysql-data: redis-data:
8.7 Compose 最佳实践
每个项目一个目录、一份 compose 文件、一份 .env。
生产数据库端口不要 映射到宿主机(仅容器内访问)。
使用 depends_on + healthcheck 处理启动依赖,而不是 sleep。
敏感配置用 env_file 或外部注入,不入库。
用 docker compose config 校验后再 up。
版本升级前 docker compose down + 备份卷。
Compose 适合单机 ;多机集群用 Swarm/K8s。
第 9 章 实战部署案例 9.1 Nginx 部署静态网站 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 mkdir -p /srv/wwwecho "<h1>Hello Docker</h1>" > /srv/www/index.html docker run -d --name web \ -p 80:80 \ -v /srv/www:/usr/share/nginx/html:ro \ --restart unless-stopped \ nginx:1.27-alpinecat > /srv/nginx/default.conf <<'EOF' server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; gzip on; gzip_types text/plain text/css application/javascript; location / { try_files $uri $uri / /index.html; } location /api/ { proxy_pass http://api:8080/; proxy_set_header Host $host ; proxy_set_header X-Real-IP $remote_addr ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; } } EOF docker run -d --name web \ -p 80:80 \ -v /srv/www:/usr/share/nginx/html:ro \ -v /srv/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro \ --network app-net \ nginx:1.27-alpine docker exec web nginx -t curl -I http://127.0.0.1/ docker logs --tail 20 web
Nginx 容器运维要点:
修改配置后 docker exec web nginx -t 校验,再 docker exec web nginx -s reload 平滑重载(无需重启容器)。
日志挂载到宿主机,便于统一采集。
HTTPS 证书挂载进 /etc/nginx/ssl,并配置 listen 443 ssl。
9.2 MySQL 8 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 docker run -d --name mysql \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=Root123! \ -e MYSQL_DATABASE=appdb \ -e MYSQL_USER=app \ -e MYSQL_PASSWORD=App123! \ -v mysql-data:/var/lib/mysql \ --restart unless-stopped \ mysql:8.0mkdir -p /srv/mysql-initcat > /srv/mysql-init/01_init.sql <<'EOF' CREATE DATABASE IF NOT EXISTS orders DEFAULT CHARACTER SET utf8mb4; CREATE USER IF NOT EXISTS 'orders' @'%' IDENTIFIED BY 'Orders123!' ; GRANT ALL PRIVILEGES ON orders.* TO 'orders' @'%' ; FLUSH PRIVILEGES; EOF docker run -d --name mysql \ -e MYSQL_ROOT_PASSWORD=Root123! \ -v mysql-data:/var/lib/mysql \ -v /srv/mysql-init:/docker-entrypoint-initdb.d:ro \ mysql:8.0 docker exec -it mysql mysql -uroot -p docker exec mysql mysqladmin ping docker exec mysql sh -c 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" appdb' > /backup/appdb.sql
关键参数建议(生产):
1 2 3 4 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --default-authentication-plugin=caching_sha2_password --max_connections=500
1 2 3 4 5 6 7 8 docker run -d --name mysql \ -e MYSQL_ROOT_PASSWORD=Root123! \ --cpus 4 -m 4g \ -v mysql-data:/var/lib/mysql \ mysql:8.0 \ --character-set-server=utf8mb4 \ --collation-server=utf8mb4_unicode_ci \ --max_connections=500
重要 :MySQL 数据必须挂卷;初始化 SQL 只在数据目录为空时执行一次;升级大版本前先备份再换镜像 tag,并核对数据兼容性。
9.3 Redis 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 docker run -d --name redis -p 6379:6379 redis:7-alpine docker run -d --name redis \ -p 6379:6379 \ -v redis-data:/data \ redis:7-alpine \ redis-server --appendonly yes --requirepass Redis123! docker exec -it redis redis-cli -a Redis123! ping docker exec -it redis redis-cli -a Redis123!
Redis 配置建议:
开启 appendonly yes(AOF)或 RDB(默认),生产建议 AOF + RDB 都开。
设置 maxmemory(如 --maxmemory 512mb --maxmemory-policy allkeys-lru),防止内存耗尽宿主机。
哨兵/集群部署复杂,单机用 redis.conf 挂载:
1 2 3 4 5 6 7 8 9 10 11 12 13 cat > /srv/redis/redis.conf <<'EOF' appendonly yes requirepass Redis123! maxmemory 512mb maxmemory-policy allkeys-lru save 900 1 save 300 10 EOF docker run -d --name redis \ -p 6379:6379 \ -v /srv/redis/redis.conf:/usr/local/etc/redis/redis.conf:ro \ -v redis-data:/data \ redis:7-alpine redis-server /usr/local/etc/redis/redis.conf
9.4 PostgreSQL 1 2 3 4 5 6 7 8 9 10 11 12 13 14 docker run -d --name pg \ -p 5432:5432 \ -e POSTGRES_PASSWORD=PG123! \ -e POSTGRES_USER=app \ -e POSTGRES_DB=appdb \ -v pg-data:/var/lib/postgresql/data \ --restart unless-stopped \ postgres:16-alpine docker exec -it pg psql -U app -d appdb docker exec pg pg_dump -U app appdb > /backup/appdb.dump
注意:PostgreSQL 数据目录挂载路径是 /var/lib/postgresql/data(镜像版本不同可能变化,用官方文档为准)。
9.5 Java Spring Boot 应用 Dockerfile(多阶段):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 FROM maven:3.9 -eclipse-temurin-17 AS buildWORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn package -DskipTests -B FROM eclipse-temurin:17 -jre-alpineWORKDIR /app RUN adduser -D -u 1001 app && chown -R app:app /app COPY --from=build /build/target/app.jar app.jar USER appEXPOSE 8080 HEALTHCHECK --interval=30s --timeout =5s --retries=3 \ CMD wget -q --spider http://127.0.0.1:8080/actuator/health || exit 1 ENTRYPOINT ["java" , "-XX:MaxRAMPercentage=75.0" , "-jar" , "app.jar" ]
1 2 3 4 5 6 7 8 9 10 11 12 docker build -t myapp:1.0.0 . docker run -d --name myapp \ -p 8080:8080 \ -e SPRING_PROFILES_ACTIVE=prod \ -e DB_HOST=db -e DB_PORT=3306 -e DB_PASSWORD=xxxx \ --cpus 2 -m 2g \ --restart unless-stopped \ myapp:1.0.0 curl http://127.0.0.1:8080/actuator/health docker inspect myapp --format '{{.State.Health.Status}}'
JVM 容器最佳实践:
使用 -XX:MaxRAMPercentage=75.0 让 JVM 感知 cgroup 内存限制(不要写死 -Xmx)。
镜像用 JRE(运行时),不用 JDK(构建时)。
日志输出到 stdout(SLF4J 控制台 appender),由 Docker 收集。
9.6 Node.js 应用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 FROM node:20 -alpine AS buildWORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:20 -alpineWORKDIR /app ENV NODE_ENV=productionRUN adduser -D -u 1001 app COPY --from=build --chown =app:app /app/package*.json ./ COPY --from=build --chown =app:app /app/node_modules ./node_modules COPY --from=build --chown =app:app /app/dist ./dist USER appEXPOSE 3000 CMD ["node" , "dist/main.js" ]
1 2 docker build -t nodeapp:1.0 . docker run -d --name nodeapp -p 3000:3000 --restart unless-stopped nodeapp:1.0
npm ci 需要 package-lock.json;生产镜像只装 --omit=dev 依赖,或用上面”复制 node_modules”的方式避免重复安装。
9.7 Python Flask/Django 1 2 3 4 5 6 7 8 9 10 FROM python:3.12 -slimWORKDIR /app ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . RUN useradd -r -u 1001 app && chown -R app:app /app USER appEXPOSE 8000 CMD ["gunicorn" , "-b" , "0.0.0.0:8000" , "-w" , "4" , "app:app" ]
1 2 docker build -t pyapp:1.0 . docker run -d --name pyapp -p 8000:8000 -e DATABASE_URL=mysql://app:pass@db:3306/appdb pyapp:1.0
9.8 前端项目构建部署(Nginx + 静态资源) 1 2 3 4 5 6 7 8 9 10 11 12 FROM node:20 -alpine AS buildWORKDIR /app COPY package*.json ./ RUN npm ci COPY . . ARG VITE_API_BASE=/apiRUN npm run build FROM nginx:1.27 -alpineCOPY --from=build /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80
1 2 docker build --build-arg VITE_API_BASE=https://api.example.com -t frontend:1.0 . docker run -d --name frontend -p 80:80 frontend:1.0
9.9 ELK/EFK 日志栈
轻量替代方案:Filebeat → Elasticsearch → Kibana ,或用 Loki + Promtail + Grafana (更轻)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 name: elk services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.14.0 environment: - discovery.type=single-node - ES_JAVA_OPTS=-Xms1g -Xmx1g - xpack.security.enabled=false volumes: - es-data:/usr/share/elasticsearch/data ports: - "9200:9200" restart: unless-stopped kibana: image: docker.elastic.co/kibana/kibana:8.14.0 environment: - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 ports: - "5601:5601" depends_on: - elasticsearch restart: unless-stopped filebeat: image: docker.elastic.co/beats/filebeat:8.14.0 user: root volumes: - ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro - /var/lib/docker/containers:/var/lib/docker/containers:ro - /var/run/docker.sock:/var/run/docker.sock:ro depends_on: - elasticsearch restart: unless-stopped volumes: es-data:
1 2 3 4 5 6 7 8 9 filebeat.inputs: - type: container paths: - /var/lib/docker/containers/*/*.log output.elasticsearch: hosts: ["elasticsearch:9200" ] index: "docker-logs-%{+yyyy.MM.dd} "
1 2 3 4 docker compose up -d curl http://127.0.0.1:9200/_cat/indices
9.10 监控栈:cAdvisor + Prometheus + Grafana 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 name: monitoring services: cadvisor: image: gcr.io/cadvisor/cadvisor:v0.49.1 ports: - "8080:8080" volumes: - /:/rootfs:ro - /var/run:/var/run:ro - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro - /dev/disk/:/dev/disk:ro privileged: true restart: unless-stopped prometheus: image: prom/prometheus:v2.53.0 ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro - prom-data:/prometheus command: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.retention.time=30d restart: unless-stopped grafana: image: grafana/grafana:11.1.0 ports: - "3000:3000" environment: - GF_SECURITY_ADMIN_PASSWORD=admin123 volumes: - grafana-data:/var/lib/grafana restart: unless-stopped volumes: prom-data: grafana-data:
1 2 3 4 5 6 7 global: scrape_interval: 15s scrape_configs: - job_name: "docker" static_configs: - targets: ["cadvisor:8080" ]
常用指标:
容器 CPU:rate(container_cpu_usage_seconds_total{name=~".+"}[5m])
容器内存:container_memory_usage_bytes
容器网络:rate(container_network_receive_bytes_total[5m])
容器磁盘:container_fs_usage_bytes
9.11 高频踩坑与对应解法
现象
原因
解法
容器秒退(Exited 0/1)
前台进程不存在/启动失败
看 docker logs;用 -it 调试
端口已分配
端口冲突
ss -tlnp 找占用进程,改端口或停旧容器
permission denied 挂载目录
宿主目录属主与容器 UID 不一致
用 --user 对齐 UID,或 chown -R
数据丢失
没用卷/用了匿名卷且删容器
用命名卷,备份
时区错误
镜像默认 UTC
加 -e TZ=Asia/Shanghai 或挂 /etc/localtime
中文乱码
缺 locale
基础镜像装 locales 并设置 LANG=C.UTF-8
DNS 解析不到服务名
不在同一自定义网络
都加入同一 --network
docker.sock 权限
容器内用户无权限
仅可信容器挂载,user=root 或加组
镜像拉取超时
网络/加速问题
配置 registry-mirrors
磁盘被日志/镜像占满
无日志轮转、无清理
配 max-size/max-file + 定期 docker system prune
第 10 章 安全加固 10.1 安全威胁模型
攻击面
风险
镜像供应链
恶意镜像、带漏洞依赖、被投毒的基础镜像
容器逃逸
内核漏洞、特权容器、危险挂载(docker.sock)
配置不当
root 运行、弱密码、敏感信息入镜像、端口全开
网络
容器间过度信任、暴露不必要的端口
守护进程
docker.sock 未保护、2375 端口裸奔
10.2 镜像安全 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 docker pull nginx:1.27-alpine@sha256:xxxxxxxx docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy image nginx:1.27-alpine docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ aquasec/trivy image --severity HIGH,CRITICAL --ignore-unfixed myapp:1.0 trivy repo https://github.com/org/app trivy fs --scanners vuln,secret,config . docker run --rm -v $PWD :/project hadolint/hadolint /bin/hadolint /project/Dockerfile
镜像安全清单:
10.3 运行时安全 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 docker run -d --user 1000:1000 myapp docker run -d --cap-drop ALL --cap-add NET_BIND_SERVICE myapp docker run -d --read-only --tmpfs /tmp:rw,size=64m myapp docker run -d --security-opt no-new-privileges myapp docker run -d --security-opt seccomp=./myseccomp.json myapp docker run -d -m 512m --cpus 1 --pids-limit 256 myapp docker network create --internal-only docker run -d --network none myjob
10.4 守护进程安全 1 2 3 4 5 6 7 8 9 10 11 12 13 ls -l /var/run/docker.sock auditctl -w /var/lib/docker -k docker
10.5 安全检查工具 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 git clone https://github.com/docker/docker-bench-security.gitcd docker-bench-securitysudo sh docker-bench-security.sh trivy image myapp:1.0 trivy config . trivy fs --scanners secret . docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ anchore/grype nginx:latest docker run -d --name falco \ --privileged \ -v /var/run/docker.sock:/var/run/docker.sock \ falcosecurity/falco
10.6 生产安全基线(对照清单)
第 11 章 日志与监控 11.1 日志驱动 容器日志默认由 json-file 驱动写入宿主机 /var/lib/docker/containers/<id>/<id>-json.log。
1 2 3 4 5 6 7 8 docker logs web docker logs --tail 200 web docker logs -f --since 5m web docker logs --since 2026-08-02T00:00:00 --until 2026-08-02T08:00:00 web docker inspect web --format '{{.HostConfig.LogConfig.Type}} {{.HostConfig.LogConfig.Config}}'
配置日志轮转(daemon.json 全局或运行参数):
1 2 3 4 5 6 7 { "log-driver" : "json-file" , "log-opts" : { "max-size" : "50m" , "max-file" : "5" } }
1 2 docker run -d --log-driver json-file --log-opt max-size=20m --log-opt max-file=3 web
常用日志驱动对比:
驱动
特点
适用
json-file(默认)
落盘 JSON,易轮转
通用,最常用
journald
写入 systemd journal
systemd 环境统一收集
syslog
发送到 syslog 服务
有 syslog 中心
fluentd
直接发到 Fluentd
日志管道场景
gelf
发到 Graylog
Graylog 用户
awslogs / gcplogs / azurelogs
云厂商日志
云环境
none
丢弃日志
高吞吐、日志走应用直传
1 2 3 docker run -d --log-driver journald --name web web journalctl -u docker -f | grep web
11.2 日志采集架构 1 2 3 4 5 6 7 8 9 10 容器 stdout | v json-file (宿主机) | v Filebeat / Promtail / Fluent Bit | v Elasticsearch + Kibana / Loki + Grafana / ClickHouse
生产建议:应用只写 stdout ,由采集器统一收集,避免容器内多份文件日志导致采集混乱。若必须写文件,挂载卷并让采集器读卷。
11.3 容器监控体系
层
工具
作用
主机层
Node Exporter + Prometheus
CPU/内存/磁盘/网络
容器层
cAdvisor
容器资源指标(容器视角)
应用层
应用指标端点(/metrics、/actuator)
业务与性能指标
展示
Grafana
面板与告警
告警
Alertmanager
规则触发通知(邮件/钉钉/企微/Slack)
告警规则示例(Prometheus):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 groups: - name: docker-alerts rules: - alert: ContainerDown expr: time() - container_last_seen{name=~".+"} > 60 for: 1m labels: severity: critical annotations: summary: "容器 {{ $labels.name }} 已停止响应" - alert: HighContainerMemory expr: container_memory_usage_bytes{name=~".+"} / container_spec_memory_limit_bytes{name=~".+"} > 0.85 for: 5m labels: severity: warning
11.4 事件与审计 1 2 3 4 docker events docker events --filter type =container --filter event=die docker events --since 1h
第 12 章 Docker Swarm 集群 12.1 Swarm 架构 1 2 3 4 5 6 7 8 +------------------+ +------------------+ +------------------+ | Manager 节点 | | Manager 节点 | | Manager 节点 | | (Raft, 调度) | <----> | (Raft, 调度) | <----> | (Raft, 调度) | +------------------+ +------------------+ +------------------+ | | | +------------------+ +------------------+ +------------------+ | Worker 节点 | | Worker 节点 | | Worker 节点 | +------------------+ +------------------+ +------------------+
Manager 节点 :Raft 共识存储集群状态,负责任务调度、服务发现、集群管理(建议奇数台,≥3 高可用)。
Worker 节点 :只运行任务(Task),把任务结果上报。
Service :长期运行的服务声明(镜像、副本数、网络、更新策略)。
Task :Service 的一个运行实例(一个容器)。
与 Kubernetes 对应关系:Service≈Deployment,Task≈Pod,Manager≈Control Plane,Worker≈Node。
12.2 初始化集群 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 docker swarm init --advertise-addr 192.168.1.10 docker swarm join --token SWMTKN-1-xxxx 192.168.1.10:2377 docker swarm join-token manager docker swarm join --token SWMTKN-1-yyyy 192.168.1.10:2377 docker node ls docker node inspect node2 docker node update --availability drain node2 docker node rm node2
安全要点:
2377 端口用于集群管理通信;4789(VXLAN)、7946(gossip)用于 overlay 网络。
定期轮换令牌:docker swarm join-token --rotate worker。
生产环境控制台(Manager)不要跑业务负载。
12.3 部署服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 docker service create \ --name web \ --replicas 3 \ --publish published=8080,target=80 \ --update-delay 10s \ --restart-condition any \ --with-registry-auth \ registry.example.com/web:1.0.0 docker service ls docker service ps web docker service inspect web docker service logs -f web docker service scale web=5 docker service scale web=2 docker service update --image registry.example.com/web:1.0.1 web docker service update --env-add TZ=Asia/Shanghai web docker service rollback web docker service rm web
服务更新策略:
1 2 3 4 5 6 7 8 docker service create \ --update-order start-first \ --update-parallelism 1 \ --update-delay 30s \ --rollback-parallelism 1 \ --rollback-delay 10s \ --restart-condition any \ --name app app:2.0
12.4 Swarm 网络与数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 docker network create -d overlay --attachable prod-net docker service create --name api --network prod-net myapi docker service create --name web --network prod-net --publish published=80,target=80 nginx docker volume create --driver local \ --opt type =nfs --opt o=addr=192.168.1.20,rw \ --opt device=:/srv/nfs/share shared-vol docker service create --mode global \ --mount type =volume,source =node-local,target=/data \ --constraint node.hostname==worker1 \ --name db mysql:8.0
需要状态的数据(数据库)放 Swarm 前务必想清楚存储方案;无状态应用最适合 Swarm。
12.5 约束与标签 1 2 3 4 5 6 7 8 9 10 11 12 docker node update --label-add disk=ssd node2 docker node update --label-add role=db node3 docker service create \ --constraint node.labels.disk==ssd \ --constraint node.role==worker \ --name web nginx docker service create --mode global --name exporter prom/node-exporter
12.6 Swarm 运维 1 2 3 4 5 6 7 8 9 10 docker node ls docker service ls docker stack ls docker stack deploy -c stack.yaml myapp docker stack services myapp docker stack ps myapp docker stack rm myapp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 version: "3.8" services: web: image: registry.example.com/web:1.0.0 ports: - "80:80" networks: - prod-net deploy: replicas: 3 update_config: order: start-first restart_policy: condition: any api: image: registry.example.com/api:1.0.0 environment: - DB_HOST=db networks: - prod-net deploy: replicas: 2 db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_pw secrets: - db_root_pw volumes: - db-data:/var/lib/mysql networks: - prod-net deploy: replicas: 1 networks: prod-net: driver: overlay volumes: db-data: secrets: db_root_pw: file: ./secrets/db_root_pw.txt
故障演练与巡检:
1 2 3 4 5 6 7 docker service ps --no-trunc web docker node ps $(docker node ls -q) docker stats --no-stream docker service ls --format "table {{.Name}}\t{{.Replicas}}\t{{.UpdateStatus.State}}"
Swarm 定位:轻量集群 ,适合中小规模、无状态/少状态服务;大型复杂场景(自动扩缩容、存储、Ingress、多集群)建议 Kubernetes。
第 13 章 CI/CD 集成 13.1 标准流水线 1 2 代码提交 -> 触发流水线 -> 单元测试 -> 构建镜像 -> 漏洞扫描 -> 推送私有仓库 -> 部署(SSH/Ansible/Swarm/K8s)-> 健康检查 -> 通知
13.2 GitLab CI 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 stages: - test - build - scan - deploy variables: IMAGE: registry.example.com/myapp:$CI_COMMIT_SHORT_SHA test: stage: test image: node:20-alpine script: - npm ci - npm test rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" build: stage: build image: docker:27 services: - docker:27-dind script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker build -t $IMAGE . - docker push $IMAGE rules: - if: $CI_COMMIT_BRANCH == "main" scan: stage: scan image: aquasec/trivy:latest script: - trivy image --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 $IMAGE needs: ["build" ] rules: - if: $CI_COMMIT_BRANCH == "main" deploy: stage: deploy image: docker:27 before_script: - apk add --no-cache openssh-client - mkdir -p ~/.ssh && echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa script: - ssh deploy@10.0.0.5 "docker login ... && docker pull $IMAGE && docker service update --image $IMAGE myapp" environment: name: production rules: - if: $CI_COMMIT_BRANCH == "main" when: manual
要点:
用 docker:27-dind(Docker in Docker)执行构建。
凭据用 CI 变量/受保护变量,禁止写死在代码里。
部署阶段加 when: manual 人工确认,或接审批流。
13.3 GitHub Actions 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 name: Build & Deploy on: push: branches: [main ]jobs: build: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@v4 - name: Login to Registry uses: docker/login-action@v3 with: registry: registry.example.com username: ${{ secrets.REGISTRY_USER }} password: ${{ secrets.REGISTRY_PASSWORD }} - name: Build and push uses: docker/build-push-action@v6 with: context: . push: true tags: | registry.example.com/myapp:${{ github.sha }} registry.example.com/myapp:latest cache-from: type=gha cache-to: type=gha,mode=max - name: Scan image uses: aquasecurity/trivy-action@master with: image-ref: registry.example.com/myapp:${{ github.sha }} severity: HIGH,CRITICAL exit-code: "1"
13.4 Jenkins 流水线 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 pipeline { agent { label 'docker' } environment { IMAGE = "registry.example.com/myapp:${env.BUILD_NUMBER}" } stages { stage('Test' ) { steps { sh 'npm ci && npm test' } } stage('Build & Push' ) { steps { sh "docker login -u ${env.REG_USER} -p ${env.REG_PASS} registry.example.com" sh "docker build -t ${IMAGE} . && docker push ${IMAGE}" } } stage('Deploy' ) { steps { sh "ssh deploy@10.0.0.5 'docker pull ${IMAGE} && docker service update --image ${IMAGE} myapp'" } } } post { failure { slackSend color: 'danger' , message: "部署失败: ${env.BUILD_NUMBER}" } } }
13.5 部署策略对比
策略
实现
特点
重建(Recreate)
stop 旧的 → 起新的
简单、有停机窗口
滚动更新(Rolling)
逐个替换副本
Swarm/K8s 默认,无停机
蓝绿(Blue-Green)
两套环境切流量
回滚快、成本高
金丝雀(Canary)
先放少量新版本
风险最小,配合网关/服务网格
Swarm 蓝绿示例:
1 2 3 docker service create --name myapp-blue --replicas 3 --publish published=8081,target=8080 --network prod-net myapp:1.0 docker service create --name myapp-green --replicas 3 --publish published=8082,target=8080 --network prod-net myapp:1.1
13.6 镜像拉取凭据(K8s 场景提一下) 1 2 3 4 5 6 7 8 kubectl create secret docker-registry regcred \ --docker-server=registry.example.com \ --docker-username=robot$project \ --docker-password=xxxx
第 14 章 性能调优与故障排查 14.1 排障方法论 1 2 3 4 5 1. 收集信息:docker ps -a、docker logs、docker inspect、系统日志 2. 定位阶段:创建失败?启动失败?运行崩溃?性能问题? 3. 缩小范围:对比正常/异常容器、检查依赖与网络 4. 修复验证:最小改动,验证后固化(更新配置/镜像/编排) 5. 记录复盘:更新文档与监控告警
信息收集命令:
1 2 3 4 5 6 7 8 docker ps -a docker logs --tail 200 -f <容器> docker inspect <容器> --format '{{json .State}}' docker inspect <容器> --format '{{json .HostConfig}}' docker top <容器> docker stats --no-stream journalctl -u docker -n 100 dmesg -T | tail -50
14.2 常见故障与处置 场景1:容器一直重启(Restarting) 1 2 3 4 5 6 7 8 9 docker ps -a docker logs <容器> --tail 100 docker inspect <容器> --format '{{.State.ExitCode}} {{.State.Error}}'
场景2:磁盘被占满(经典事故) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 df -h /var/lib/dockerdu -sh /var/lib/docker/* | sort -h | tail du -sh /var/lib/docker/containers | sort -h docker system df docker system prune -a -f --volumes docker builder prune -f docker ps -sdu -sh /var/lib/docker/volumes/* | sort -h | tail
预防措施(写进制度):
全局配置日志轮转(max-size/max-file)。
部署定时清理任务:docker system prune -f(保留在用资源)。
数据卷与日志目录规划好容量并监控告警(>80% 告警)。
镜像瘦身,避免拉取大镜像撑满磁盘。
场景3:OOM(内存被杀死) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 docker inspect <容器> --format '{{.State.OOMKilled}}' dmesg -T | grep -i "out of memory" docker stats --no-stream docker exec <容器> top docker update -m 2g --memory-swap 2g <容器>
场景4:网络不通 1 2 3 4 5 6 7 8 9 10 11 12 13 curl http://127.0.0.1:8080 docker exec web curl http://api:8080 docker exec web getent hosts api docker network inspect app-net iptables -t nat -L -n | grep 8080 ss -tlnp | grep 8080
场景5:镜像拉取慢/失败 1 2 3 4 5 6 7 8 docker pull nginx
场景6:docker 命令报权限
14.3 性能调优 daemon 层:
1 2 3 4 5 6 7 8 { "storage-driver" : "overlay2" , "max-concurrent-downloads" : 5 , "max-concurrent-uploads" : 5 , "live-restore" : true , "log-driver" : "json-file" , "log-opts" : { "max-size" : "50m" , "max-file" : "5" } }
live-restore:daemon 重启不杀容器(生产推荐,注意与 swarm 的兼容)。
overlay2 是当前主流存储驱动,性能与稳定性最佳。
日志驱动默认 json-file 对高吞吐应用有 I/O 开销,可换 journald 或 fluentd。
容器层:
1 2 3 4 5 6 7 docker run --cpus 2 --cpu-shares 1024 --cpuset-cpus 0-1 app docker run --device-read-bps /dev/sda:50mb --device-write-bps /dev/sda:50mb app docker run --sysctl net.core.somaxconn=1024 app
应用层:
JVM:-XX:MaxRAMPercentage=75,避免 -Xmx 写死。
Nginx:worker_processes auto,打开 epoll。
MySQL:innodb_buffer_pool_size 设为内存的 60%~70%(在容器限制内)。
Redis:关闭持久化或调低 save 频率可大幅提升写性能(按需取舍)。
基准测试:
1 2 3 4 5 6 docker run --rm --network host appropriate/curl -s http://127.0.0.1:8080 | wc -c docker run --rm alpine:3.20 dd if =/dev/zero of=/tmp/test bs=1M count=1024 docker run --rm --network host williamyeh/wrk -t4 -c100 -d30s http://127.0.0.1:8080/
14.4 清理大法(系统回收) 1 2 3 4 5 6 7 8 9 docker system df docker system prune -f docker system prune -a -f docker system prune -a -f --volumes docker builder prune -f docker container prune -f docker image prune -f docker network prune -f docker volume prune -f
14.5 应急处理流程(示例) 1 2 3 4 5 6 告警:某服务不可用 1. docker service ps <svc> # 看任务状态 2. docker service logs <svc> # 看错误 3. 判断:配置问题 / 资源问题 / 数据问题 / 代码问题 4. 紧急回滚:docker service rollback <svc> 5. 止血后修复并复盘:更新镜像、加监控、加预案
第 15 章 深入原理 15.1 namespace:隔离视图 容器通过 Linux namespace 让进程看到”独立的系统”:
namespace
隔离内容
相关参数
PID
进程号空间
CLONE_NEWPID
Network
网络栈(网卡/IP/路由/iptables)
CLONE_NEWNET
Mount
挂载点视图
CLONE_NEWNS
UTS
主机名与域名
CLONE_NEWUTS
IPC
进程间通信(消息队列等)
CLONE_NEWIPC
User
用户与 UID 映射
CLONE_NEWUSER
Cgroup
cgroup 根视图
CLONE_NEWCGROUP
1 2 3 docker top <容器> ls -l /proc/<PID>/ns/
注意 :namespace 只是”视图隔离”,容器内看到的所有进程都在宿主机内核管理下;PID namespace 里是 1 的进程在宿主上可能是几千。
15.2 cgroup:资源限制 cgroup(control group)控制并统计进程组的资源使用:
cgroup v1 :CPU、内存、blkio、net_prio 等各自独立子系统。
cgroup v2 (内核 5.2+ 主流):统一层次结构,接口更简洁(/sys/fs/cgroup/)。
1 2 3 4 5 6 7 cat /proc/$(docker inspect -f '{{.State.Pid}}' <容器>)/cgroupls /sys/fs/cgroup/system.slice/docker-<容器ID>.scope/cat /sys/fs/cgroup/system.slice/docker-<id >.scope/memory.maxcat /sys/fs/cgroup/system.slice/docker-<id >.scope/memory.current
关键事实:
docker run -m 对应 cgroup 的 memory.max(v2)或 memory.limit_in_bytes(v1)。
--cpus 对应 CPU 权重/配额;cgroup v2 用 cpu.max。
OOM Killer 由 cgroup 触发,杀掉组内进程(退出码 137)。
容器内看到的 /proc/meminfo 等已被 cgroup 视角修正(Docker 会处理),但部分应用仍会读到宿主值,这是老问题,注意结合 --memory 与应用自身配置。
15.3 存储驱动 overlayfs 1 2 3 4 5 6 7 8 镜像层:下层镜像层,只读(Docker 基础镜像、各镜像分层都在这里) lowerdir: /var/lib/docker/overlay2/<layer1>/diff /var/lib/docker/overlay2/<layer2>/diff ... 容器层:上层容器读写层,可写 upperdir: /var/lib/docker/overlay2/<container>/diff 合并视图:对外呈现的统一视图(容器内看到的根目录) merged: /var/lib/docker/overlay2/<container>/merged
读文件:从上往下找第一个存在的层。
写文件:写时复制 —— 文件在低层 则先拷贝到上层再改(CoW),删除文件则在上层放”白名单”标记。
目录修改:overlayfs 的目录合并是”浅合并”,删目录行为有特殊语义。如果上下层出现同名普通文件 :上层文件直接遮蔽下层文件,只展示 upper 的文件,下层文件完全隐藏。
优点:层共享省磁盘、启动快;缺点:大量小文件写有开销。
1 2 docker info | grep "Storage Driver" df -h | grep overlay
15.4 容器隔离的局限(为什么容器不等于虚拟机)
共享内核 :容器无法运行与宿主不同内核(如 Linux 容器不能在 Windows 内核上跑,需 WSL2 虚拟机)。
内核级漏洞可逃逸 :历史逃逸漏洞(CVE-2019-5736 runc、CVE-2022-0492 cgroup 等)说明容器边界不是安全边界。
资源争抢 :CPU 缓存、磁盘 IO 等仍会互相影响(cgroup 对 IO 控制弱)。
时间与系统调用 :共享系统时钟;部分系统调用未隔离。
结论:容器是”进程隔离”而非”安全隔离” ,安全设计要按共享内核前提来做。
15.5 从 Docker 到 Kubernetes
能力
Docker 单机
Docker Swarm
Kubernetes
容器编排
✗
✓(轻量)
✓(重量级)
自动扩缩容
✗
手动 scale
HPA 自动
服务发现/负载均衡
手动
内置
Service + Ingress
存储编排
卷
卷(依赖插件)
PV/PVC 生态
配置/密钥
env
secrets
ConfigMap/Secret
滚动更新/回滚
手动
✓
✓(策略丰富)
故障自愈
restart 策略
任务重调度
全面(节点/容器/驱逐)
生态与插件
-
少
极丰富(CNI/CSI/CRD/Operator)
运维复杂度
低
中
高
学习路线建议:
1 2 3 4 Docker 基础(镜像/容器/网络/卷) -> Docker Compose(单机多服务) -> Docker Swarm(轻量集群,理解编排概念) -> Kubernetes(生产主流:Pod/Deployment/Service/Ingress/Storage/Helm)
第 16 章 常用命令速查表 16.1 镜像 1 2 3 4 5 6 7 8 9 10 11 12 13 14 docker search nginx docker pull nginx:alpine docker images docker image inspect nginx docker history nginx docker tag nginx nginx:v1 docker rmi nginx:v1 docker build -t myapp:1.0 . docker save -o nginx.tar nginx docker load -i nginx.tar docker push registry/x/nginx:v1 docker login registry.example.com docker system df docker image prune -f
16.2 容器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 docker ps docker ps -a docker run -d --name web -p 80:80 nginx docker start/stop/restart web docker pause/unpause web docker rm -f web docker exec -it web bash docker logs -f web docker top web docker stats docker inspect web docker cp web:/etc/nginx/nginx.conf ./ docker cp ./index.html web:/tmp/ docker port web docker diff web docker events docker container prune -f
16.3 网络 / 卷 / Compose / Swarm 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 docker network ls docker network create app-net docker network connect app-net web docker network disconnect app-net web docker network rm app-net docker network inspect app-net docker volume ls docker volume create data-vol docker volume inspect data-vol docker volume rm data-vol docker volume prune -f docker compose up -d docker compose ps docker compose logs -f docker compose exec web bash docker compose down docker compose down -v docker compose config docker swarm init --advertise-addr 192.168.1.10 docker swarm join --token xxx manager:2377 docker node ls docker service create --name web --replicas 3 -p 80:80 nginx docker service ls docker service ps web docker service scale web=5 docker service update --image app:2.0 web docker service rollback web docker service logs -f web docker service rm web docker stack deploy -c stack.yaml app docker stack rm app
16.4 系统级 1 2 3 4 5 6 7 docker version docker info docker system df docker system prune -a -f docker system events docker context ls docker context use prod
附录 A. 术语表
术语
含义
Image
只读分层模板
Container
镜像的运行实例
Registry
镜像仓库(Docker Hub / Harbor)
Dockerfile
镜像构建脚本
Layer
镜像中的一层变更
Volume
Docker 管理的数据卷
Bind Mount
宿主机目录挂载
Dockerfile ARG/ENV
构建参数 / 运行环境变量
Multi-stage
多阶段构建
Compose
单机多容器编排
Swarm
Docker 内置集群
Overlay 网络
跨主机网络(VXLAN)
OCI
开放容器标准(image/runtime spec)
containerd
标准容器运行时
runc
OCI 运行时实现
B. 官方与推荐资料
C. 常见错误信息对照表
报错
含义
处理
Cannot connect to the Docker daemon
daemon 未启动/权限不足
systemctl start docker;加 docker 组
port is already allocated
端口被占用
ss -tlnp 定位,改端口
No such image
镜像不存在/名写错
docker images 核对;重新 pull
OCI runtime exec failed
容器内没有该命令
换镜像/改用 sh
permission denied
权限不足
检查用户/挂载属主/只读挂载
no space left on device
磁盘满
清理日志/镜像/卷(见 14.2)
tls handshake timeout
网络问题
换加速源/代理/离线导入
Exec format error
架构不匹配
拉对应 --platform 镜像
exit status 137
被 OOM/强杀
检查内存限制与 OOMKilled
conflict: container name is already in use
容器名重复
改 --name 或删旧容器
join token expired
Swarm 令牌过期
docker swarm join-token 重新获取
D. 进阶学习清单(掌握程度自测)
祝你在容器化的路上越走越稳。学完本手册后,建议到 Kubernetes 继续深入:Pod、Service、Ingress、Helm、Kubeadm/K3s 部署实践。