mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
2005 字
6 分钟
Docker 从入门到实践
2020-04-20

一、容器技术的前世今生#

1.1 从 LXC 到 Docker#

容器技术的概念可以追溯到 1979 年 Unix V7 的 chroot 系统调用,它实现了文件系统的隔离。2000 年 FreeBSD 推出了 Jail,将隔离扩展到了进程树、网络和用户权限。2006 年 Google 贡献了 Cgroups(Control Groups),实现了资源限制和审计。2008 年 Linux 内核引入了 Namespaces,提供了进程、网络、挂载点等维度的隔离能力。

LXC(LinuX Containers) 在 2008 年诞生,它是第一个真正意义上完整的 Linux 容器实现,整合了 Cgroups 和 Namespaces。然而 LXC 的操作复杂,缺乏标准化的工具链和镜像管理机制。

2013 年,dotCloud 公司(后更名为 Docker Inc.)开源了 Docker。Docker 在 LXC 的基础上进行了重大创新:

  • 分层镜像:Union File System 实现镜像的分层存储,极大提升了存储效率和分发速度
  • 标准化格式:统一的镜像格式和分发协议
  • 开发工具链:简单易用的命令行工具
  • 生态建设:Docker Hub 提供公共镜像仓库

Docker 的出现使得容器技术真正走向了大众开发者,开启了云原生时代的大门。

1.2 Docker 的核心价值#

graph LR A[传统部署] -->|环境不一致| B[问题频发] C[虚拟机部署] -->|资源开销大| D[成本高昂] E[Docker 容器] -->|轻量可移植| F[一次构建到处运行] style E fill:#2496ed,color:#fff style F fill:#28a745,color:#fff

Docker 解决的核心问题:

痛点传统方案Docker 方案
环境一致性手动配置环境镜像固化环境
资源利用率每个应用独占 VM多容器共享宿主机内核
部署效率小时级部署秒级启动
版本管理依赖文档记录镜像版本化追踪
微服务架构复杂的网络配置原生服务发现机制

二、Docker 核心概念#

2.1 三大核心组件#

Docker 架构由三个核心组件构成:

1. 镜像(Image)

镜像是一个只读的容器模板,包含了运行应用所需的所有内容:代码、运行时、库、环境变量和配置文件。镜像采用分层存储设计,每个镜像由多个只读层叠加而成。

block-beta columns 1 block:layer1:1 A["应用代码层 (App Code)"] end block:layer2:1 B["依赖层 (npm/pip/gem)"] end block:layer3:1 C["运行时层 (Node/Python)"] end block:layer4:1 D["基础层 (Alpine/Ubuntu)"] end

分层存储优势

flowchart LR subgraph 镜像A A1[Alpine Base] A2[Node.js] A3[App A] end subgraph 镜像B B1[Alpine Base] B2[Node.js] B3[App B] end subgraph 共享层 C1[Alpine Base<br/>共享] C2[Node.js<br/>共享] end A1 -.-> C1 A2 -.-> C2 B1 -.-> C1 B2 -.-> C2 style C1 fill:#4CAF50,color:#fff style C2 fill:#4CAF50,color:#fff
特性说明
存储优化相同层只存储一份,节省磁盘空间
构建加速本地已有的层无需重新下载
分发高效推送/拉取时只传输差异层
构建缓存Dockerfile 指令未变则复用缓存层

2. 容器(Container)

容器是镜像的运行实例。与镜像不同,容器在运行时会添加一个可写层(Container Layer),所有对容器的修改都发生在这一层。容器之间相互隔离,每个容器拥有自己的:

  • 进程空间(PID Namespace)
  • 网络接口(Network Namespace)
  • 文件系统挂载点(Mount Namespace)
  • 用户权限(User Namespace)
  • IPC 通信(IPC Namespace)

3. 仓库(Registry)

仓库用于存储和分发镜像。Docker Hub 是最大的公共仓库,企业也可以搭建私有仓库(如 Harbor)。

# 仓库地址格式
registry.example.com:port/namespace/repository:tag
# 示例
docker.io/library/nginx:latest # Docker Hub 官方镜像
gcr.io/google-containers/pause:3.1 # Google Container Registry
harbor.company.com/prod/web:v2.0 # 私有仓库镜像

2.2 Docker 架构图#

graph TB subgraph Client A[docker build] B[docker pull] C[docker run] end subgraph "Docker Host" D[Docker Daemon] E[Images] F[Containers] G[Volumes] H[Networks] end subgraph Registry I[Docker Hub] J[Private Registry] end A --> D B --> D C --> D D --> E D --> F D --> G D --> H D <--> I D <--> J

三、Docker 安装与配置#

3.1 主流平台安装方式#

Ubuntu/Debian

# 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc
# 安装依赖
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release
# 添加 Docker 官方 GPG 密钥
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 添加软件源
echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装 Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin

CentOS/RHEL

# 安装必要工具
sudo yum install -y yum-utils
# 添加 Docker 仓库
sudo yum-config-manager --add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
# 安装 Docker
sudo yum install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
# 启动服务
sudo systemctl enable --now docker

macOS

推荐使用 OrbStack 替代 Docker Desktop,性能更优:

# 使用 Homebrew 安装
brew install orbstack
# 或下载安装包
# https://orbstack.dev/
Tip

OrbStack 相比 Docker Desktop 启动速度快 10 倍,内存占用更低,且完美兼容 Docker CLI。

3.2 Daemon 配置优化#

编辑 /etc/docker/daemon.json

{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"storage-driver": "overlay2",
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com",
"https://docker.mirrors.ustc.edu.cn"
],
"live-restore": true,
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 65535,
"Soft": 65535
}
}
}

配置说明:

配置项说明
log-opts限制日志文件大小,避免磁盘被撑满
storage-driver存储驱动,overlay2 性能最优
registry-mirrors镜像加速,国内网络必备
live-restoreDaemon 重启时容器保持运行
# 重载配置
sudo systemctl daemon-reload
sudo systemctl restart docker

3.3 非 root 用户使用 Docker#

# 将当前用户加入 docker 组
sudo usermod -aG docker $USER
# 重新登录或执行
newgrp docker
# 验证
docker run hello-world

四、Docker 镜像管理#

4.1 镜像的获取与查看#

# 从 Docker Hub 拉取镜像
docker pull nginx:1.25-alpine
# 指定平台拉取
docker pull --platform linux/arm64 nginx:1.25-alpine
# 查看本地镜像
docker images
# 输出示例
# REPOSITORY TAG IMAGE ID CREATED SIZE
# nginx 1.25-alpine 78b8b9da20c4 2 weeks ago 41.2MB
# redis 7-alpine 3e5a4774bfbf 3 weeks ago 32.5MB
# 查看镜像详情
docker image inspect nginx:1.25-alpine
# 查看镜像分层历史
docker history nginx:1.25-alpine

4.2 镜像的导出与导入#

# 导出镜像为 tar 文件
docker save nginx:1.25-alpine -o nginx.tar
# 压缩导出
docker save nginx:1.25-alpine | gzip > nginx.tar.gz
# 导入镜像
docker load -i nginx.tar
# 从 tar 文件导入并命名
docker load -i nginx.tar && docker tag <IMAGE_ID> my-nginx:v1

4.3 镜像标签管理#

# 创建标签(类似软链接)
docker tag nginx:1.25-alpine my-registry/nginx:latest
docker tag nginx:1.25-alpine my-registry/nginx:v1.25
# 推送到私有仓库
docker push my-registry/nginx:v1.25
# 删除镜像
docker rmi nginx:1.25-alpine
# 强制删除(即使有容器在使用)
docker rmi -f nginx:1.25-alpine
# 清理悬空镜像(没有标签的镜像)
docker image prune
# 清理所有未使用的镜像
docker image prune -a

4.4 多架构镜像构建#

使用 docker buildx 构建跨平台镜像:

# 创建多架构构建器
docker buildx create --name multiarch --use
# 启用 qemu 模拟器(构建非宿主机架构)
docker run --privileged --rm tonistiigi/binfmt --install all
# 构建并推送多架构镜像
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t my-registry/app:v1.0 \
--push \
.

五、Docker 容器操作#

5.1 容器的生命周期#

stateDiagram-v2 [*] --> Created: docker create Created --> Running: docker start Running --> Paused: docker pause Paused --> Running: docker unpause Running --> Stopped: docker stop Stopped --> Running: docker start Running --> [*]: docker rm Stopped --> [*]: docker rm

5.2 容器创建与运行#

# 创建并启动容器(最常用)
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# 常用参数详解
docker run -d \ # 后台运行
--name web-server \ # 指定容器名称
--restart unless-stopped \ # 重启策略
-p 8080:80 \ # 端口映射(宿主机:容器)
-v /host/path:/container/path \ # 挂载卷
-e MYSQL_ROOT_PASSWORD=secret \ # 环境变量
--memory="512m" \ # 内存限制
--cpus="1.5" \ # CPU 限制
--network mynet \ # 指定网络
nginx:1.25-alpine
# 交互式运行
docker run -it --name my-ubuntu ubuntu:22.04 /bin/bash
# 一次性运行(执行后自动删除)
docker run --rm alpine:latest echo "Hello Docker"

5.3 重启策略#

策略行为
no不自动重启(默认)
on-failure[:max-retries]退出码非 0 时重启,可设置最大重试次数
always总是重启,除非手动停止
unless-stopped除非手动停止,否则总是重启
# 生产环境推荐
docker run -d --restart unless-stopped nginx:latest

5.4 容器管理命令#

# 查看运行中的容器
docker ps
# 查看所有容器(包括已停止)
docker ps -a
# 查看容器资源占用
docker stats
# 格式化输出
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# 启动/停止/重启容器
docker start web-server
docker stop web-server # 发送 SIGTERM,优雅关闭
docker kill web-server # 发送 SIGKILL,强制关闭
docker restart web-server
# 暂停/恢复容器
docker pause web-server
docker unpause web-server
# 查看容器日志
docker logs web-server
docker logs -f --tail 100 web-server # 实时跟踪最近 100 行
# 进入容器执行命令
docker exec -it web-server /bin/sh
# 以 root 用户进入(适用于 alpine 等)
docker exec -it -u 0 web-server /bin/sh
# 复制文件
docker cp ./app.conf web-server:/etc/nginx/
docker cp web-server:/var/log/nginx ./logs/
# 查看容器进程
docker top web-server
# 查看容器详细信息
docker inspect web-server
# 导出容器为镜像
docker export web-server | docker import - my-nginx:exported
# 提交容器为镜像(不推荐用于生产)
docker commit web-server my-nginx:modified

5.5 容器资源限制#

# 内存限制
docker run -d --memory="512m" nginx # 限制 512MB
docker run -d --memory="1g" --memory-swap="2g" nginx # 内存+交换分区共 2GB
# CPU 限制
docker run -d --cpus="1.5" nginx # 1.5 个 CPU 核心
docker run -d --cpuset-cpus="0,2" nginx # 绑定到 CPU 0 和 2
# IO 限制
docker run -d \
--device-read-bps=/dev/sda:10mb \ # 读速率限制
--device-write-bps=/dev/sda:10mb \ # 写速率限制
nginx
# 综合示例
docker run -d \
--name app \
--memory="1g" \
--memory-reservation="512m" \
--cpus="2" \
--cpu-shares=1024 \
--pids-limit=100 \
my-app:latest

5.6 容器清理#

# 停止所有运行中的容器
docker stop $(docker ps -q)
# 删除所有已停止的容器
docker container prune
# 删除所有容器(危险操作)
docker rm -f $(docker ps -aq)
# 一键清理(容器、网络、悬空镜像、构建缓存)
docker system prune
# 深度清理(包括未使用的镜像)
docker system prune -a
# 查看磁盘占用
docker system df

六、Docker 数据卷管理#

6.1 三种数据持久化方式#

graph TB subgraph 容器 A[可写层] end subgraph 数据持久化 B[Volume] C[Bind Mount] D[tmpfs] end E[宿主机文件系统] --> C F[Docker 管理区域] --> B G[内存] --> D style B fill:#28a745,color:#fff style C fill:#ffc107 style D fill:#dc3545,color:#fff

6.2 Volume(推荐方式)#

Docker 管理的存储卷,存放在 /var/lib/docker/volumes/ 目录。

# 创建卷
docker volume create mydata
# 查看所有卷
docker volume ls
# 查看卷详情
docker volume inspect mydata
# 使用卷
docker run -d \
-v mydata:/app/data \
nginx:latest
# 匿名卷(不指定名称)
docker run -d -v /app/data nginx:latest
# 命名卷 + 只读
docker run -d \
-v mydata:/app/data:ro \
nginx:latest
# 删除卷
docker volume rm mydata
# 清理未使用的卷
docker volume prune

6.3 Bind Mount(绑定挂载)#

将宿主机目录直接映射到容器。

# 绑定挂载
docker run -d \
-v /host/www:/usr/share/nginx/html \
nginx:latest
# 使用 $PWD 简化路径
docker run -d \
-v $(pwd)/html:/usr/share/nginx/html \
nginx:latest
# 挂载单个文件
docker run -d \
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
nginx:latest
# 使用 --mount 语法(更明确)
docker run -d \
--mount type=bind,src=$(pwd)/html,dst=/usr/share/nginx/html,readonly \
nginx:latest

6.4 tmpfs(临时文件系统)#

数据仅存储在内存中,容器停止后数据消失。

# 适用于敏感数据或临时缓存
docker run -d \
--tmpfs /tmp:rw,size=100m,mode=1777 \
nginx:latest
# 使用 --mount 语法
docker run -d \
--mount type=tmpfs,dst=/tmp,tmpfs-size=104857600 \
nginx:latest

6.5 数据卷容器#

用于容器间共享数据:

# 创建数据卷容器
docker create -v /shared-data --name data-container alpine
# 其他容器引用
docker run -d \
--volumes-from data-container \
--name app1 \
nginx
docker run -d \
--volumes-from data-container \
--name app2 \
nginx
# 备份数据卷
docker run --rm \
--volumes-from data-container \
-v $(pwd):/backup \
alpine tar czf /backup/data-backup.tar.gz /shared-data
# 恢复数据卷
docker run --rm \
--volumes-from data-container \
-v $(pwd):/backup \
alpine tar xzf /backup/data-backup.tar.gz -C /

6.6 Volume 对比表#

特性VolumeBind Mounttmpfs
存储位置Docker 管理目录宿主机任意位置内存
可移植性优秀不适用
性能良好优秀极佳
备份迁移简单需自行管理不支持
多容器共享支持支持不支持
适用场景生产环境持久化开发环境挂载源码敏感数据/缓存

七、Docker 网络配置#

7.1 默认网络类型#

Docker 安装后自动创建三种网络:

flowchart TB subgraph Docker Networks subgraph Bridge["bridge (默认)"] B1[容器 A<br/>172.17.0.2] B2[容器 B<br/>172.17.0.3] B3["通过 NAT 访问外网"] end subgraph Host["host 模式"] H1[容器 C<br/>共享宿主机网络] H2["无网络隔离<br/>性能最优"] end subgraph None["none 模式"] N1[容器 D<br/>无网络接口] N2["完全隔离<br/>安全场景"] end end B1 --> B3 B2 --> B3 H1 -.->|直接使用| HOST["宿主机网络栈"] style B3 fill:#4CAF50,color:#fff style H2 fill:#2196F3,color:#fff style N2 fill:#f44336,color:#fff
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
c7b8f5b9a3e0 bridge bridge local
d4e8a3b1c2f6 host host local
f1b2a3c4d5e7 none null local

1. bridge(默认)

每个容器分配独立 IP,通过 NAT 访问外部网络。

# 默认使用 bridge 网络
docker run -d --name web nginx
# 查看容器网络信息
docker inspect web --format '{{.NetworkSettings.IPAddress}}'

2. host

容器共享宿主机网络命名空间,无网络隔离。

# 使用 host 网络
docker run -d --network host nginx
# 注意:无需端口映射,直接使用宿主机端口

3. none

禁用容器网络。

# 完全禁用网络
docker run -d --network none alpine

7.2 自定义网络#

推荐使用自定义网络实现容器间通信:

flowchart TB subgraph 宿主机 subgraph Docker网络["自定义网络: mynet (172.20.0.0/16)"] GW["网关<br/>172.20.0.1"] DB["PostgreSQL<br/>172.20.0.2<br/>容器名: db"] WEB["Nginx<br/>172.20.0.3<br/>容器名: web"] APP["App<br/>172.20.0.4<br/>容器名: app"] end WEB -->|"DNS: db:5432"| DB APP -->|"DNS: db:5432"| DB APP -->|"DNS: web:80"| WEB end CLIENT["外部请求"] -->|"端口映射 80:80"| WEB style GW fill:#9C27B0,color:#fff style DB fill:#4CAF50,color:#fff style WEB fill:#2196F3,color:#fff style APP fill:#FF9800,color:#fff

自定义网络优势

特性默认 bridge自定义 bridge
容器间通信需使用 IP 地址可使用容器名(DNS)
网络隔离所有容器同一网络按项目/环境隔离
动态连接不支持运行时动态连接/断开
IP 地址管理自动分配可指定子网和网关
# 创建 bridge 网络
docker network create --driver bridge mynet
# 指定子网
docker network create \
--driver bridge \
--subnet=172.20.0.0/16 \
--gateway=172.20.0.1 \
mynet
# 容器连接到自定义网络
docker run -d --network mynet --name db postgres:15
docker run -d --network mynet --name web nginx
# 容器间通过名称通信
docker exec web ping db
# 运行时连接网络
docker network connect mynet existing-container
# 断开网络
docker network disconnect mynet web
# 删除网络
docker network rm mynet

7.3 端口映射#

# 随机端口映射
docker run -d -P nginx
# -P 自动映射 Dockerfile 中 EXPOSE 的端口
# 指定端口映射
docker run -d -p 8080:80 nginx
# 格式:宿主机端口:容器端口
# 指定 IP 和端口
docker run -d -p 127.0.0.1:8080:80 nginx
# UDP 端口
docker run -d -p 53:53/udp dns-server
# 多端口映射
docker run -d \
-p 80:80 \
-p 443:443 \
nginx
# 查看端口映射
docker port web

7.4 网络模式对比#

模式特点适用场景
bridge默认模式,NAT 访问外网大多数场景
host性能最优,无隔离高性能网络应用
none无网络安全隔离场景
自定义 bridge支持 DNS 解析、动态连接微服务架构
overlay跨主机通信Docker Swarm

八、Dockerfile 最佳实践#

8.1 Dockerfile 基础语法#

# 基础镜像
FROM node:20-alpine
# 维护者信息
LABEL maintainer="team@example.com"
# 设置工作目录
WORKDIR /app
# 复制依赖文件(利用缓存层)
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制应用代码
COPY . .
# 暴露端口
EXPOSE 3000
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 非 root 用户
USER node
# 启动命令
CMD ["node", "server.js"]

8.2 关键指令详解#

指令说明最佳实践
FROM基础镜像优先选择 Alpine 或 Distroless
WORKDIR工作目录使用绝对路径
COPY复制文件分层复制,利用缓存
RUN执行命令合并多条命令 &&
ENV环境变量设置固定值
ARG构建参数敏感信息传递
EXPOSE声明端口仅作文档用途
CMD默认命令使用 JSON 数组格式
ENTRYPOINT入口点配合 CMD 实现灵活配置
HEALTHCHECK健康检查生产环境必备

8.3 CMD 与 ENTRYPOINT 区别#

# CMD 方式:可被 docker run 参数覆盖
FROM alpine
CMD ["echo", "hello"]
# docker run myimage echo world → 输出 world
# ENTRYPOINT 方式:docker run 参数作为追加参数
FROM alpine
ENTRYPOINT ["echo"]
CMD ["hello"]
# docker run myimage world → 输出 hello world
# docker run myimage --help → 输出 echo --help
# 组合使用:ENTRYPOINT 定义命令,CMD 定义默认参数
FROM alpine
ENTRYPOINT ["ping"]
CMD ["localhost"]
# docker run myimage google.com → ping google.com

8.4 多阶段构建#

减小镜像体积的核心技术:

flowchart TB subgraph 构建阶段["Builder Stage"] A1["FROM golang:1.21-alpine"] --> A2["COPY go.mod go.sum"] A2 --> A3["RUN go mod download"] A3 --> A4["COPY . ."] A4 --> A5["RUN go build -o /server"] end subgraph 运行阶段["Runtime Stage"] B1["FROM alpine:3.19"] --> B2["RUN apk add ca-certificates"] B2 --> B3["COPY --from=builder /server"] B3 --> B4["ENTRYPOINT [\"/server\"]"] end A5 -.->|只复制二进制文件| B3 style A5 fill:#4CAF50,color:#fff style B4 fill:#2196F3,color:#fff

多阶段构建前后对比

block-beta columns 2 block:单阶段构建:2 columns 1 S1["源代码 50MB"] S2["Go 工具链 300MB"] S3["依赖包 100MB"] S4["构建产物 15MB"] end block:多阶段构建:2 columns 1 M1["构建产物 15MB"] M2["Alpine 基础 5MB"] end A["总大小: ~800MB"] B["总大小: ~20MB"] S4 --> A M2 --> B style A fill:#f44336,color:#fff style B fill:#4CAF50,color:#fff
# 构建阶段
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /server .
# 运行阶段
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
COPY --from=builder /server .
EXPOSE 8080
USER nobody
ENTRYPOINT ["/server"]
# 构建镜像
docker build -t my-server:v1.0 .
# 镜像大小对比
# 单阶段构建:~800MB
# 多阶段构建:~15MB

8.5 镜像优化技巧#

# 不推荐:每一层都增加体积
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
RUN apt-get clean
# 推荐:合并命令,单层完成
RUN apt-get update && \
apt-get install -y --no-install-recommends \
curl \
vim && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 不推荐:复制所有文件
COPY . /app
# 推荐:配合 .dockerignore
# .dockerignore 文件内容:
# node_modules
# .git
# *.log
# Dockerfile
COPY . /app
# 使用 Alpine 或 Distroless 基础镜像
FROM node:20-alpine # ~50MB
FROM gcr.io/distroless/nodejs20 # ~20MB,无 shell

8.6 安全加固实践#

FROM node:20-alpine
# 创建非 root 用户
RUN addgroup -g 1000 -S appgroup && \
adduser -u 1000 -S appuser -G appgroup
WORKDIR /app
# 设置文件权限
COPY --chown=appuser:appgroup . .
# 只读文件系统
# docker run --read-only --tmpfs /tmp ...
# 禁用特权
# docker run --cap-drop ALL --cap-add CHOWN ...
USER appuser
# 扫描漏洞
# docker scout cves myimage:v1

九、Docker Compose 服务编排#

9.1 docker-compose.yml 基础#

version: "3.8"
services:
web:
image: nginx:1.25-alpine
container_name: web-server
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro
environment:
- TZ=Asia/Shanghai
networks:
- frontend
depends_on:
- app
app:
build:
context: ./app
dockerfile: Dockerfile
args:
NODE_ENV: production
restart: unless-stopped
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
networks:
- frontend
- backend
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
db:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis-data:/data
networks:
- backend
networks:
frontend:
backend:
volumes:
postgres-data:
redis-data:

9.2 常用命令#

# 启动服务
docker compose up -d
# 查看服务状态
docker compose ps
# 查看日志
docker compose logs -f web
# 进入容器
docker compose exec app /bin/sh
# 重新构建并启动
docker compose up -d --build
# 停止并删除服务
docker compose down
# 删除服务和数据卷
docker compose down -v
# 扩容服务
docker compose up -d --scale app=3
# 查看配置
docker compose config
# 只启动特定服务
docker compose up -d db cache

9.3 环境变量管理#

docker-compose.yml
services:
app:
image: my-app:latest
env_file:
- .env
- .env.local
environment:
- NODE_ENV=production
- DATABASE_URL=${DB_URL}
# .env 文件
DB_URL=postgres://user:pass@db:5432/mydb
REDIS_URL=redis://cache:6379
# 命令行覆盖
DB_URL=postgres://prod:pass@prod-db:5432/mydb docker compose up -d

9.4 多环境配置#

# 项目结构
├── docker-compose.yml # 基础配置
├── docker-compose.override.yml # 开发环境(自动加载)
├── docker-compose.prod.yml # 生产环境
└── docker-compose.staging.yml # 预发布环境
# 开发环境(自动合并 docker-compose.yml 和 override)
docker compose up -d
# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 使用 --profile 启动可选服务
docker compose --profile debug up -d
docker-compose.prod.yml
services:
app:
image: registry.example.com/app:${VERSION:-latest}
deploy:
replicas: 3
resources:
limits:
cpus: "1"
memory: 512M
reservations:
memory: 256M
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3

十、实战案例#

10.1 案例 1:Web 应用完整部署#

项目结构

my-webapp/
├── app/
│ ├── src/
│ ├── package.json
│ └── Dockerfile
├── nginx/
│ ├── nginx.conf
│ └── Dockerfile
├── docker-compose.yml
└── .env

app/Dockerfile

FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:1.25-alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

docker-compose.yml

version: "3.8"
services:
web:
build:
context: .
dockerfile: app/Dockerfile
ports:
- "80:80"
restart: unless-stopped

10.2 案例 2:CI/CD 流水线集成#

GitLab CI 示例

.gitlab-ci.yml
stages:
- build
- test
- deploy
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
build:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
test:
stage: test
image: $IMAGE_TAG
script:
- npm test
deploy:
stage: deploy
image: docker:24
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker pull $IMAGE_TAG
- docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
only:
- main

10.3 案例 3:本地开发环境#

docker-compose.dev.yml
version: "3.8"
services:
app:
build:
context: .
target: development # 使用 Dockerfile 中的开发阶段
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true
command: npm run dev
db:
image: postgres:15-alpine
ports:
- "5432:5432"
environment:
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
POSTGRES_DB: myapp
volumes:
- dev-db:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
mailhog:
image: mailhog/mailhog
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
volumes:
dev-db:

十一、常见问题排查#

11.1 容器无法启动#

# 查看容器退出原因
docker logs <container_id>
docker inspect <container_id> --format '{{.State.ExitCode}}'
docker inspect <container_id> --format '{{.State.Error}}'
# 常见错误码
# 0 - 正常退出
# 1 - 应用错误
# 137 - 被 SIGKILL 杀死(OOM)
# 139 - 段错误
# 143 - 被 SIGTERM 终止

11.2 网络连接问题#

# 进入容器网络命名空间
docker exec -it <container_id> sh
# 测试 DNS 解析
nslookup google.com
# 测试网络连通性
ping -c 3 8.8.8.8
curl -v https://google.com
# 查看 NAT 规则
iptables -t nat -L -n
# 重建 Docker 网络
docker network prune

11.3 磁盘空间不足#

# 查看磁盘占用
docker system df -v
# 清理策略
docker system prune -a --volumes # 清理所有未使用资源
# 迁移 Docker 数据目录
# 1. 停止 Docker
sudo systemctl stop docker
# 2. 修改 /etc/docker/daemon.json
{
"data-root": "/mnt/docker-data"
}
# 3. 迁移数据
sudo rsync -aP /var/lib/docker/ /mnt/docker-data/
# 4. 启动 Docker
sudo systemctl start docker

11.4 权限问题#

# 容器内访问宿主机文件权限错误
# 方案 1:使用用户命名空间映射
docker run --user $(id -u):$(id -g) ...
# 方案 2:设置正确的文件权限
sudo chown -R 1000:1000 ./data
# 方案 3:在 Dockerfile 中创建匹配用户
ARG USER_ID=1000
RUN useradd -u $USER_ID -m appuser
USER appuser

十二、参考资料#

Note

本文基于 Docker 26.x 版本编写,部分命令在不同版本间可能存在差异。建议定期关注 Docker 官方更新日志以获取最新特性。


参考#

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

Docker 从入门到实践
https://blog.souloss.com/posts/tools/docker-get-started/
作者
Souloss
发布于
2020-04-20
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时