从入门到实战,系统掌握 Docker 容器化技术
目录
- [Docker 简介](#Docker 简介)
- 核心概念
- [安装 Docker](#安装 Docker)
- 镜像管理
- 容器管理
- 数据管理
- 网络管理
- [Dockerfile 详解](#Dockerfile 详解)
- [Docker Compose](#Docker Compose)
- 仓库管理
- 实战案例
- 常用命令速查表
- 最佳实践与排错
1. Docker 简介
1.1 什么是 Docker
Docker 是一个开源的应用容器引擎,基于 Go 语言开发,让开发者可以将应用及其依赖打包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上。
核心思想 :Build, Ship, and Run Any App, Anywhere(构建、交付、运行任何应用, anywhere)
1.2 为什么使用 Docker
| 特性 | 说明 |
|---|---|
| 快速交付 | 秒级启动,开发、测试、生产环境一致 |
| 环境隔离 | 容器间相互独立,互不影响 |
| 轻量级 | 共享宿主机内核,资源占用远低于虚拟机 |
| 可移植 | 一次构建,到处运行 |
| 版本控制 | 镜像分层,支持版本回滚 |
| 生态丰富 | Docker Hub 提供海量官方镜像 |
1.3 Docker vs 虚拟机
传统虚拟机架构:
┌─────────────────────────────────────┐
│ Guest OS (Ubuntu) │ ← 完整操作系统
│ ┌───────────┐ ┌───────────┐ │
│ │ App A │ │ App B │ │
│ └───────────┘ └───────────┘ │
├─────────────────────────────────────┤
│ Hypervisor │
├─────────────────────────────────────┤
│ Host OS │
└─────────────────────────────────────┘
Docker 容器架构:
┌─────────────────────────────────────┐
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │App A │ │App B │ │App C │ │ ← 容器(仅含应用+依赖)
│ └────────┘ └────────┘ └────────┘ │
├─────────────────────────────────────┤
│ Docker Engine │
├─────────────────────────────────────┤
│ Host OS (共享内核) │
└─────────────────────────────────────┘
对比表:
| 维度 | Docker 容器 | 虚拟机 |
|---|---|---|
| 启动速度 | 秒级 | 分钟级 |
| 资源占用 | MB 级 | GB 级 |
| 性能 | 接近原生 | 有损耗 |
| 隔离性 | 进程级隔离 | 系统级隔离 |
| 操作系统 | 共享宿主机内核 | 独立完整 OS |
2. 核心概念
2.1 镜像(Image)
-
镜像是一个只读模板,包含运行应用所需的代码、运行时、库、环境变量和配置文件
-
镜像由多层(Layer)组成,每层对应 Dockerfile 中的一条指令
-
镜像可以基于其他镜像构建(继承关系)
-
镜像通过名称和标签(Tag)标识,如
nginx:1.25镜像分层示例(nginx:latest):
┌─────────────────────┐
│ Layer 4: CMD │ ← 可写层(容器运行时)
├─────────────────────┤
│ Layer 3: EXPOSE │
├─────────────────────┤
│ Layer 2: COPY │
├─────────────────────┤
│ Layer 1: FROM ubuntu│ ← 基础镜像层
└─────────────────────┘
2.2 容器(Container)
- 容器是镜像的运行实例,是一个可读写的运行环境
- 容器 = 镜像 + 可写层 + 运行状态
- 容器可以被创建、启动、停止、删除、暂停
- 容器之间相互隔离,但可以通过网络通信
2.3 仓库(Repository)
- 仓库是集中存放镜像的地方
- Docker Hub 是 Docker 官方的公共仓库
- 私有仓库可以通过 Docker Registry 自建
- 一个仓库可以包含多个标签的镜像(如
ubuntu:18.04,ubuntu:20.04,ubuntu:22.04)
2.4 三者关系
构建(build) 运行(run)
镜像 ──────────→ 容器 ──────────→ 运行中的应用
↑ │
│ 推送(push)/拉取(pull) │
└──────────── 仓库 ──────────────────┘
3. 安装 Docker
3.1 Ubuntu 安装
bash
# 1. 更新包索引
sudo apt-get update
# 2. 安装依赖包
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# 3. 添加 Docker 官方 GPG 密钥
sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 4. 设置稳定版仓库
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
# 5. 安装 Docker Engine
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 6. 验证安装
docker --version
sudo docker run hello-world
3.2 CentOS 安装
bash
# 1. 卸载旧版本
sudo yum remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
# 2. 安装 yum-utils
sudo yum install -y yum-utils
# 3. 添加 Docker 仓库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 4. 安装 Docker
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 5. 启动并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
# 6. 验证
sudo docker run hello-world
3.3 macOS / Windows 安装
下载安装 Docker Desktop:
- macOS: https://www.docker.com/products/docker-desktop/
- Windows: https://www.docker.com/products/docker-desktop/
3.4 配置非 root 用户运行 Docker
bash
# 创建 docker 组(通常已存在)
sudo groupadd docker
# 将当前用户加入 docker 组
sudo usermod -aG docker $USER
# 刷新组权限(或重新登录)
newgrp docker
# 验证无需 sudo
docker run hello-world
3.5 配置镜像加速器(国内必做)
bash
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.mirrors.ustc.edu.cn"
]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
# 验证加速器配置
docker info | grep -A 5 "Registry Mirrors"
4. 镜像管理
4.1 搜索镜像
bash
# 搜索 Docker Hub 上的镜像
docker search nginx
# 只显示官方镜像
docker search --filter "is-official=true" nginx
# 限制输出数量
docker search --limit 5 nginx
输出说明:
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build... 19000 [OK]
4.2 拉取镜像
bash
# 拉取最新版镜像
docker pull nginx
# 拉取指定版本
docker pull nginx:1.25.3
# 拉取指定架构
docker pull --platform linux/arm64 nginx
# 从私有仓库拉取
docker pull registry.example.com/myapp:v1.0
4.3 查看镜像
bash
# 列出本地所有镜像
docker images
# 或
docker image ls
# 只显示镜像 ID
docker images -q
# 显示完整镜像 ID(不截断)
docker images --no-trunc
# 按仓库名过滤
docker images nginx
# 显示镜像的详细信息(元数据)
docker inspect nginx:latest
# 查看镜像历史(分层信息)
docker history nginx:latest
4.4 给镜像打标签
bash
# 为镜像打新标签(相当于复制一份引用)
docker tag nginx:latest my-nginx:v1.0
# 打标签并指定仓库地址(用于推送到私有仓库)
docker tag nginx:latest registry.example.com/myteam/nginx:v1.0
4.5 删除镜像
bash
# 删除指定镜像(按名称或 ID)
docker rmi nginx:1.25.3
# 或
docker image rm nginx:1.25.3
# 强制删除(即使有容器使用)
docker rmi -f nginx:1.25.3
# 删除所有未使用的镜像(悬空镜像)
docker image prune
# 删除所有未被任何容器使用的镜像
docker image prune -a
# 批量删除所有镜像(谨慎使用)
docker rmi $(docker images -q)
4.6 镜像的导入导出
bash
# 导出镜像为 tar 文件
docker save -o nginx.tar nginx:latest
# 或
docker save nginx:latest > nginx.tar
# 从 tar 文件导入镜像
docker load -i nginx.tar
# 或
docker load < nginx.tar
# 导出容器为镜像(将容器当前状态保存为镜像)
docker export -o mycontainer.tar container_name
# 从容器快照导入为镜像
docker import mycontainer.tar myimage:v1
savevsexport区别:
docker save:保存完整镜像(含所有层和历史),用于镜像迁移docker export:导出容器文件系统快照,不含层历史,体积更小
5. 容器管理
5.1 创建并启动容器
bash
# 基本语法
docker run [选项] 镜像 [命令] [参数...]
# 常用选项:
# -d, --detach 后台运行
# -it 交互式终端(-i 保持输入, -t 分配终端)
# --name 指定容器名称
# -p, --publish 端口映射 主机端口:容器端口
# -P 随机端口映射
# -v, --volume 数据卷挂载
# -e, --env 设置环境变量
# --rm 容器停止后自动删除
# --network 指定网络
# --restart 重启策略
# --privileged 授予扩展权限
实例 1:运行交互式容器
bash
# 启动 Ubuntu 容器并进入 bash
docker run -it --name my-ubuntu ubuntu:22.04 /bin/bash
# 在容器内执行命令
root@abc123:/# cat /etc/os-release
root@abc123:/# apt-get update && apt-get install -y curl
root@abc123:/# exit # 退出并停止容器
实例 2:后台运行 Nginx
bash
# 后台运行 Nginx,映射 8080 端口
docker run -d --name my-nginx -p 8080:80 nginx:latest
# 访问测试
curl http://localhost:8080
实例 3:带环境变量和数据卷的容器
bash
docker run -d \
--name mysql-test \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=mydb \
-v mysql_data:/var/lib/mysql \
mysql:8.0
实例 4:一次性容器(执行完即删)
bash
# 运行一个命令后自动删除容器
docker run --rm alpine:latest echo "Hello Docker"
# 用容器执行一次性任务
docker run --rm -v $(pwd):/work -w /work alpine:latest ls -la
5.2 查看容器
bash
# 查看正在运行的容器
docker ps
# 查看所有容器(包括已停止的)
docker ps -a
# 只显示容器 ID
docker ps -q
# 显示容器大小
docker ps -s
# 显示最近创建的容器
docker ps -l
# 显示最后 N 个容器
docker ps -n 5
# 查看容器详细信息
docker inspect my-nginx
# 查看容器内运行的进程
docker top my-nginx
# 查看容器资源使用情况
docker stats
docker stats my-nginx
# 查看容器端口映射
docker port my-nginx
5.3 容器的启停操作
bash
# 启动已停止的容器
docker start my-nginx
# 停止运行中的容器(发送 SIGTERM,10秒后强制)
docker stop my-nginx
# 强制停止(发送 SIGKILL)
docker kill my-nginx
# 重启容器
docker restart my-nginx
# 暂停容器(冻结进程)
docker pause my-nginx
# 恢复暂停的容器
docker unpause my-nginx
# 等待容器停止并返回退出码
docker wait my-nginx
5.4 进入运行中的容器
bash
# 方式一:exec(推荐,在容器中执行新命令)
docker exec -it my-nginx /bin/bash
docker exec -it my-nginx sh # Alpine 等精简镜像用 sh
# 在容器中执行单条命令
docker exec my-nginx cat /etc/nginx/nginx.conf
docker exec -u root my-nginx whoami # 指定用户
# 方式二:attach(附加到容器主进程,退出会导致容器停止)
docker attach my-nginx
# 按 Ctrl+P 然后 Ctrl+Q 可 detach 而不停止容器
execvsattach:
exec:在容器内启动一个新进程,安全,推荐使用attach:连接到容器的主进程 STDIN/STDOUT,退出会影响容器
5.5 删除容器
bash
# 删除已停止的容器
docker rm my-ubuntu
# 强制删除运行中的容器
docker rm -f my-nginx
# 删除容器并删除关联的数据卷
docker rm -v my-container
# 批量删除所有已停止的容器
docker container prune
# 批量删除所有容器(谨慎!)
docker rm -f $(docker ps -aq)
5.6 查看容器日志
bash
# 查看容器日志
docker logs my-nginx
# 实时跟踪日志(类似 tail -f)
docker logs -f my-nginx
# 显示最后 100 行
docker logs --tail 100 my-nginx
# 显示时间戳
docker logs -t my-nginx
# 显示最近 30 分钟的日志
docker logs --since 30m my-nginx
# 显示指定时间之后的日志
docker logs --since "2024-01-01T00:00:00" my-nginx
5.7 容器与宿主机文件复制
bash
# 从宿主机复制文件到容器
docker cp ./index.html my-nginx:/usr/share/nginx/html/
# 从容器复制文件到宿主机
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf
# 复制目录(加不加 / 有区别)
docker cp ./html/ my-nginx:/usr/share/nginx/
5.8 容器重命名与更新
bash
# 重命名容器
docker rename old-name new-name
# 更新容器配置(如重启策略、资源限制)
docker update --restart=always my-nginx
docker update --memory 512m --cpus 1 my-nginx
6. 数据管理
6.1 数据管理的三种方式
┌─────────────────────────────────────────────────┐
│ 宿主机 │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Volumes │ │ Bind Mount│ │ tmpfs mount │ │
│ │ /var/lib │ │ 任意路径 │ │ 内存中 │ │
│ │ /docker/ │ │ │ │ │ │
│ │ volumes/ │ │ │ │ │ │
│ └────┬─────┘ └────┬─────┘ └──────┬───────┘ │
│ │ │ │ │
│ ┌────▼──────────────▼────────────────▼───────┐ │
│ │ Docker 容器 │ │
│ └──────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────┘
| 类型 | 说明 | 适用场景 |
|---|---|---|
| Volume(数据卷) | Docker 管理,存于 /var/lib/docker/volumes/ |
生产环境推荐,数据持久化 |
| Bind Mount(绑定挂载) | 挂载宿主机任意路径 | 开发环境,配置文件共享 |
| tmpfs(临时文件系统) | 挂载到内存,不持久化 | 敏感数据、高性能临时存储 |
6.2 Volume 数据卷
bash
# 创建数据卷
docker volume create my-data
# 列出所有数据卷
docker volume ls
# 查看数据卷详情
docker volume inspect my-data
# 删除数据卷
docker volume rm my-data
# 删除所有未使用的数据卷
docker volume prune
# 使用数据卷启动容器
docker run -d --name db -v my-data:/var/lib/mysql mysql:8.0
# 匿名数据卷(不指定名称,Docker 自动生成)
docker run -d -v /var/lib/mysql mysql:8.0
6.3 Bind Mount 绑定挂载
bash
# 挂载宿主机目录到容器
docker run -d \
--name my-nginx \
-p 8080:80 \
-v /home/user/nginx/html:/usr/share/nginx/html \
-v /home/user/nginx/conf:/etc/nginx/conf.d \
nginx:latest
# 使用相对路径($(pwd) 表示当前目录)
docker run -d -v $(pwd)/html:/usr/share/nginx/html nginx
# 只读挂载(容器内不能修改)
docker run -d -v $(pwd)/html:/usr/share/nginx/html:ro nginx
6.4 数据卷容器
bash
# 创建数据卷容器(仅提供数据,不运行应用)
docker create -v /data --name data-store alpine:latest /bin/true
# 其他容器通过 --volumes-from 共享数据
docker run -d --volumes-from data-store --name app1 nginx
docker run -d --volumes-from data-store --name app2 nginx
6.5 数据备份与恢复
bash
# 备份数据卷到 tar 文件
docker run --rm \
-v my-data:/data \
-v $(pwd):/backup \
alpine:latest \
tar czf /backup/backup.tar.gz -C /data .
# 从备份恢复数据卷
docker run --rm \
-v my-data:/data \
-v $(pwd):/backup \
alpine:latest \
tar xzf /backup/backup.tar.gz -C /data
7. 网络管理
7.1 Docker 网络模式
| 网络模式 | 说明 |
|---|---|
| bridge | 默认模式,容器通过虚拟网桥通信,有独立 IP |
| host | 共享宿主机网络命名空间,无独立 IP |
| none | 无网络,容器完全隔离 |
| container | 共享另一个容器的网络命名空间 |
| 自定义网络 | 用户创建的 bridge/overlay/macvlan 网络 |
7.2 网络基本操作
bash
# 列出所有网络
docker network ls
# 查看网络详情
docker network inspect bridge
# 创建自定义 bridge 网络
docker network create my-network
# 创建网络并指定子网和网关
docker network create --subnet=172.20.0.0/16 --gateway=172.20.0.1 my-network
# 删除网络
docker network rm my-network
# 删除所有未使用的网络
docker network prune
7.3 容器网络连接
bash
# 启动容器时指定网络
docker run -d --name app --network my-network nginx
# 运行中的容器连接到网络
docker network connect my-network my-nginx
# 容器断开网络
docker network disconnect my-network my-nginx
# 容器连接网络时指定 IP
docker network connect --ip 172.20.0.10 my-network my-nginx
# 容器设置别名(网络内可通过别名访问)
docker network connect --alias web my-network my-nginx
7.4 容器间通信实例
bash
# 1. 创建自定义网络
docker network create app-net
# 2. 启动 MySQL 容器,加入网络
docker run -d \
--name mysql \
--network app-net \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:8.0
# 3. 启动应用容器,加入同一网络,通过容器名访问 MySQL
docker run -d \
--name myapp \
--network app-net \
-e DB_HOST=mysql \
-e DB_PORT=3306 \
myapp:latest
# 4. 测试连通性
docker exec -it myapp ping mysql
docker exec -it myapp mysql -h mysql -u root -p123456
关键 :同一自定义网络中的容器可以通过容器名互相解析访问(Docker 内置 DNS)。默认 bridge 网络不支持此功能。
7.5 端口映射详解
bash
# 映射指定端口
docker run -d -p 8080:80 nginx
# 映射多个端口
docker run -d -p 8080:80 -p 8443:443 nginx
# 映射到指定网卡
docker run -d -p 127.0.0.1:8080:80 nginx # 仅本地可访问
docker run -d -p 0.0.0.0:8080:80 nginx # 所有网卡
# 随机映射宿主机端口
docker run -d -P nginx # 容器暴露的端口随机映射到宿主机高位端口
# UDP 端口映射
docker run -d -p 53:53/udp dnsmasq
8. Dockerfile 详解
8.1 什么是 Dockerfile
Dockerfile 是一个文本文件,包含一系列指令,用于自动化构建 Docker 镜像。
构建流程:
Dockerfile → docker build → 镜像(Image)→ docker run → 容器(Container)
8.2 Dockerfile 指令大全
| 指令 | 说明 |
|---|---|
FROM |
指定基础镜像(必须是第一条指令) |
MAINTAINER |
维护者信息(已弃用,用 LABEL) |
LABEL |
镜像元数据(作者、版本、描述等) |
RUN |
构建时执行命令 |
CMD |
容器启动时默认执行的命令 |
ENTRYPOINT |
容器启动时执行的命令(不可被 run 参数覆盖) |
COPY |
复制文件到镜像 |
ADD |
复制文件(支持 URL 和自动解压 tar) |
WORKDIR |
设置工作目录 |
ENV |
设置环境变量 |
ARG |
构建时变量(可通过 --build-arg 传入) |
EXPOSE |
声明容器监听的端口 |
VOLUME |
声明数据卷 |
USER |
设置运行用户 |
HEALTHCHECK |
健康检查 |
ONBUILD |
触发器,当该镜像被作为基础镜像时执行 |
SHELL |
指定默认 shell |
STOPSIGNAL |
设置停止容器的信号 |
8.3 完整 Dockerfile 实例
实例 1:Node.js 应用
dockerfile
# 1. 基础镜像
FROM node:18-alpine
# 2. 元数据
LABEL maintainer="dev@example.com"
LABEL version="1.0"
LABEL description="My Node.js App"
# 3. 设置工作目录
WORKDIR /app
# 4. 先复制依赖文件(利用缓存层)
COPY package*.json ./
# 5. 安装依赖
RUN npm ci --only=production
# 6. 复制应用代码
COPY . .
# 7. 构建(如果需要)
RUN npm run build
# 8. 暴露端口
EXPOSE 3000
# 9. 设置非 root 用户
USER node
# 10. 健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 11. 启动命令
CMD ["node", "server.js"]
实例 2:Python Flask 应用
dockerfile
FROM python:3.11-slim
LABEL maintainer="dev@example.com"
WORKDIR /app
# 安装系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
# 复制依赖并安装
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制代码
COPY . .
# 创建非 root 用户
RUN useradd -m appuser
USER appuser
EXPOSE 5000
ENV FLASK_APP=app.py
ENV FLASK_ENV=production
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
实例 3:多阶段构建(Go 应用)
dockerfile
# ===== 构建阶段 =====
FROM golang:1.21-alpine AS builder
WORKDIR /src
# 复制 go mod 文件
COPY go.mod go.sum ./
RUN go mod download
# 复制源码
COPY . .
# 静态编译
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server .
# ===== 运行阶段 =====
FROM alpine:3.18
# 安装 CA 证书(HTTPS 请求需要)
RUN apk --no-cache add ca-certificates tzdata
# 设置时区
ENV TZ=Asia/Shanghai
# 创建非 root 用户
RUN adduser -D -u 10001 appuser
WORKDIR /app
# 从构建阶段复制二进制文件
COPY --from=builder /app/server .
# 复制静态资源(如果有)
COPY --from=builder /src/templates ./templates
COPY --from=builder /src/static ./static
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=10s --timeout=2s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1
ENTRYPOINT ["./server"]
多阶段构建优势:最终镜像只包含运行所需文件,不含编译工具和源码,镜像体积大幅减小。
8.4 CMD vs ENTRYPOINT
dockerfile
# CMD:可被 docker run 后面的参数覆盖
CMD ["nginx", "-g", "daemon off;"]
# docker run myimage bash → 会覆盖 CMD,执行 bash
# ENTRYPOINT:不可被覆盖,docker run 参数会作为其参数传入
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"] # 默认参数
# docker run myimage -t → 执行 nginx -t
8.5 COPY vs ADD
dockerfile
# COPY:简单复制,推荐优先使用
COPY ./src /app/src
COPY config*.yaml /app/
# ADD:额外功能(自动解压 tar、支持 URL)
ADD archive.tar.gz /app/ # 自动解压
ADD https://example.com/file /app/ # 从 URL 下载(不推荐,用 curl 更好)
最佳实践 :优先使用
COPY,只有需要自动解压 tar 时才用ADD。
8.6 构建镜像
bash
# 基本构建(当前目录的 Dockerfile)
docker build -t myapp:v1.0 .
# 指定 Dockerfile 路径
docker build -f ./docker/Dockerfile.prod -t myapp:v1.0 .
# 传入构建参数(ARG)
docker build --build-arg NODE_ENV=production -t myapp:v1.0 .
# 不使用缓存构建
docker build --no-cache -t myapp:v1.0 .
# 构建时指定目标阶段(多阶段构建)
docker build --target builder -t myapp:build .
# 安静模式(只输出镜像 ID)
docker build -q -t myapp:v1.0 .
8.7 .dockerignore
创建 .dockerignore 文件排除不需要的文件,减小构建上下文:
# 版本控制
.git
.gitignore
# 依赖目录
node_modules
npm-debug.log
vendor
# 构建产物
dist
build
*.log
# IDE
.vscode
.idea
*.swp
# 环境文件
.env
.env.local
# 系统文件
.DS_Store
Thumbs.db
# Docker 相关
Dockerfile
docker-compose.yml
9. Docker Compose
9.1 什么是 Docker Compose
Docker Compose 是用于定义和运行多容器 Docker 应用的工具。通过一个 YAML 文件配置应用的服务,然后一条命令启动所有服务。
9.2 安装
bash
# Docker Desktop 自带,Linux 需单独安装
# Docker Compose V2(插件方式,推荐)
docker compose version
# 旧版 V1(独立命令)
docker-compose --version
9.3 docker-compose.yml 完整实例
yaml
# docker-compose.yml
version: "3.8"
services:
# Web 服务(Nginx)
web:
image: nginx:1.25-alpine
container_name: myweb
ports:
- "8080:80"
- "8443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./nginx/html:/usr/share/nginx/html:ro
- ./nginx/logs:/var/log/nginx
depends_on:
- app
networks:
- frontend
- backend
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost/"]
interval: 30s
timeout: 5s
retries: 3
# 应用服务(Node.js)
app:
build:
context: ./app
dockerfile: Dockerfile
args:
NODE_ENV: production
container_name: myapp
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DB_HOST=db
- DB_PORT=3306
- DB_USER=root
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_HOST=redis
volumes:
- app-data:/app/data
- ./app/uploads:/app/uploads
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
networks:
- backend
restart: unless-stopped
# 数据库服务(MySQL)
db:
image: mysql:8.0
container_name: mydb
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-root123}
MYSQL_DATABASE: myapp_db
MYSQL_USER: appuser
MYSQL_PASSWORD: ${DB_PASSWORD:-app123}
volumes:
- db-data:/var/lib/mysql
- ./mysql/init:/docker-entrypoint-initdb.d:ro
ports:
- "3306:3306"
networks:
- backend
restart: unless-stopped
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_PASSWORD:-root123}"]
interval: 10s
timeout: 5s
retries: 5
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
# 缓存服务(Redis)
redis:
image: redis:7-alpine
container_name: myredis
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- backend
restart: unless-stopped
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-redis123}
# 数据卷
volumes:
db-data:
driver: local
redis-data:
driver: local
app-data:
driver: local
# 网络
networks:
frontend:
driver: bridge
backend:
driver: bridge
9.4 Compose 常用命令
bash
# 启动所有服务(后台运行)
docker compose up -d
# 启动并构建镜像
docker compose up -d --build
# 启动指定服务
docker compose up -d web
# 查看服务状态
docker compose ps
# 查看服务日志
docker compose logs
docker compose logs -f app # 跟踪指定服务日志
docker compose logs --tail=100 # 最后100行
# 停止服务
docker compose stop # 停止但不删除容器
docker compose down # 停止并删除容器、网络
docker compose down -v # 同时删除数据卷(谨慎!)
docker compose down --rmi all # 同时删除镜像
# 重启服务
docker compose restart
docker compose restart app
# 进入服务容器
docker compose exec app /bin/bash
docker compose exec db mysql -u root -p
# 构建服务镜像
docker compose build
docker compose build --no-cache app
# 拉取最新镜像
docker compose pull
# 查看配置(验证 YAML 是否正确)
docker compose config
# 查看镜像列表
docker compose images
# 暂停/恢复服务
docker compose pause
docker compose unpause
# 缩放服务实例数
docker compose up -d --scale app=3
9.5 环境变量文件 .env
bash
# .env 文件(与 docker-compose.yml 同目录)
DB_PASSWORD=mysecretpassword
REDIS_PASSWORD=myredispassword
TAG=v1.0
在 docker-compose.yml 中通过 ${VAR_NAME} 引用:
yaml
services:
app:
image: myapp:${TAG:-latest}
environment:
- DB_PASSWORD=${DB_PASSWORD}
9.6 多环境配置
项目结构:
├── docker-compose.yml # 基础配置
├── docker-compose.override.yml # 默认覆盖(开发环境)
├── docker-compose.prod.yml # 生产环境覆盖
└── .env
bash
# 开发环境(默认使用 override)
docker compose up -d
# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
10. 仓库管理
10.1 Docker Hub 操作
bash
# 登录 Docker Hub
docker login
# 输入用户名和密码
# 退出登录
docker logout
# 推送镜像到 Docker Hub
docker tag myapp:v1.0 username/myapp:v1.0
docker push username/myapp:v1.0
# 拉取私有镜像
docker pull username/myapp:v1.0
10.2 搭建私有仓库(Registry)
bash
# 启动本地 Registry 容器
docker run -d \
--name registry \
-p 5000:5000 \
-v registry-data:/var/lib/registry \
--restart=always \
registry:2
# 打标签并推送
docker tag nginx:latest localhost:5000/my-nginx:v1.0
docker push localhost:5000/my-nginx:v1.0
# 拉取私有仓库镜像
docker pull localhost:5000/my-nginx:v1.0
# 查看仓库中的镜像
curl http://localhost:5000/v2/_catalog
# 查看镜像的标签
curl http://localhost:5000/v2/my-nginx/tags/list
10.3 配置不安全仓库(HTTP)
bash
# 编辑 /etc/docker/daemon.json
{
"insecure-registries": ["registry.example.com:5000"]
}
# 重启 Docker
sudo systemctl restart docker
11. 实战案例
11.1 案例一:搭建 LNMP 环境
项目结构:
lnmp-docker/
├── docker-compose.yml
├── nginx/
│ └── conf.d/
│ └── default.conf
├── mysql/
│ └── init/
│ └── 01-init.sql
├── php/
│ └── Dockerfile
└── www/
└── index.php
docker-compose.yml:
yaml
version: "3.8"
services:
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./www:/var/www/html:ro
depends_on:
- php
networks:
- lnmp
php:
build: ./php
volumes:
- ./www:/var/www/html
networks:
- lnmp
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: testdb
volumes:
- mysql-data:/var/lib/mysql
- ./mysql/init:/docker-entrypoint-initdb.d:ro
ports:
- "3306:3306"
networks:
- lnmp
volumes:
mysql-data:
networks:
lnmp:
nginx/conf.d/default.conf:
nginx
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
php/Dockerfile:
dockerfile
FROM php:8.2-fpm-alpine
RUN docker-php-ext-install pdo pdo_mysql mysqli
www/index.php:
php
<?php
phpinfo();
启动:
bash
cd lnmp-docker
docker compose up -d
# 访问 http://localhost
11.2 案例二:WordPress 博客
yaml
# docker-compose.yml
version: "3.8"
services:
wordpress:
image: wordpress:6.4-apache
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppassword
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress-data:/var/www/html
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppassword
MYSQL_ROOT_PASSWORD: rootpassword
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
wordpress-data:
db-data:
bash
docker compose up -d
# 访问 http://localhost:8080 完成安装
11.3 案例三:ELK 日志分析栈
yaml
version: "3.8"
services:
elasticsearch:
image: elasticsearch:8.11.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- es-data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
networks:
- elk
logstash:
image: logstash:8.11.0
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline:ro
ports:
- "5044:5044"
depends_on:
- elasticsearch
networks:
- elk
kibana:
image: kibana:8.11.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
networks:
- elk
volumes:
es-data:
networks:
elk:
11.4 案例四:Jenkins CI/CD
yaml
version: "3.8"
services:
jenkins:
image: jenkins/jenkins:lts
container_name: jenkins
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins-data:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock # 挂载 Docker 套接字
environment:
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false
restart: unless-stopped
user: root
volumes:
jenkins-data:
bash
# 启动
docker compose up -d
# 查看初始管理员密码
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
12. 常用命令速查表
12.1 镜像命令
| 命令 | 说明 |
|---|---|
docker images |
列出本地镜像 |
docker pull <image> |
拉取镜像 |
docker push <image> |
推送镜像 |
docker rmi <image> |
删除镜像 |
docker build -t <name> . |
构建镜像 |
docker tag <src> <dst> |
打标签 |
docker save -o file.tar <img> |
导出镜像 |
docker load -i file.tar |
导入镜像 |
docker history <img> |
查看镜像历史 |
docker inspect <img> |
查看镜像详情 |
12.2 容器命令
| 命令 | 说明 |
|---|---|
docker run |
创建并启动容器 |
docker ps |
列出运行中的容器 |
docker ps -a |
列出所有容器 |
docker start <id> |
启动容器 |
docker stop <id> |
停止容器 |
docker restart <id> |
重启容器 |
docker rm <id> |
删除容器 |
docker exec -it <id> bash |
进入容器 |
docker logs <id> |
查看日志 |
docker cp <src> <dst> |
复制文件 |
docker stats |
查看资源使用 |
docker top <id> |
查看容器进程 |
docker pause/unpause <id> |
暂停/恢复 |
docker kill <id> |
强制停止 |
12.3 数据卷与网络
| 命令 | 说明 |
|---|---|
docker volume ls |
列出数据卷 |
docker volume create <name> |
创建数据卷 |
docker volume rm <name> |
删除数据卷 |
docker volume prune |
清理未使用卷 |
docker network ls |
列出网络 |
docker network create <name> |
创建网络 |
docker network rm <name> |
删除网络 |
docker network connect <net> <id> |
连接网络 |
docker network disconnect <net> <id> |
断开网络 |
12.4 系统与清理
| 命令 | 说明 |
|---|---|
docker info |
查看系统信息 |
docker version |
查看版本 |
docker system df |
查看磁盘使用 |
docker system prune |
清理所有未使用资源 |
docker system prune -a |
深度清理(含未使用镜像) |
docker events |
实时查看事件 |
13. 最佳实践与排错
13.1 镜像构建最佳实践
- 使用官方基础镜像 :优先选择
alpine或slim版本减小体积 - 多阶段构建:编译和运行分离,最终镜像只含运行时
- 合理利用缓存:将变化少的指令(如依赖安装)放在前面
- 合并 RUN 指令 :减少镜像层数,如
RUN apt update && apt install -y ... - 清理缓存:安装依赖后清理包管理器缓存
- 使用 .dockerignore:减小构建上下文
- 非 root 用户运行:安全最佳实践
- 指定具体标签 :不要用
latest,生产环境锁定版本
13.2 容器运行最佳实践
- 一个容器一个进程:保持容器职责单一
- 容器无状态:数据存数据卷,容器可随时销毁重建
- 设置资源限制 :
--memory,--cpus防止资源耗尽 - 设置重启策略 :
--restart=unless-stopped - 健康检查:配置 HEALTHCHECK
- 日志输出到 stdout/stderr :不要写文件,用
docker logs查看 - 使用自定义网络:不要用默认 bridge,通过容器名通信
13.3 常见问题排查
问题 1:容器启动后立即退出
bash
# 查看退出原因
docker logs <container_id>
docker inspect <container_id> | grep -A 10 "State"
# 常见原因:
# - 前台进程退出(CMD/ENTRYPOINT 命令执行完了)
# - 配置错误导致进程崩溃
# - 权限不足
问题 2:端口映射不生效
bash
# 检查端口映射
docker port <container_id>
docker inspect <container_id> | grep -A 20 "Ports"
# 检查容器内服务是否监听 0.0.0.0(不是 127.0.0.1)
docker exec <container_id> netstat -tlnp
# 或
docker exec <container_id> ss -tlnp
问题 3:镜像构建慢
bash
# 检查 .dockerignore 是否排除了大文件
# 检查是否合理利用了缓存层
# 使用 --progress=plain 查看详细构建过程
docker build --progress=plain -t myapp .
问题 4:磁盘空间不足
bash
# 查看 Docker 磁盘使用
docker system df
# 清理未使用的资源
docker system prune -a --volumes
# 查看大镜像/容器
docker images --format "{{.Size}}\t{{.Repository}}:{{.Tag}}" | sort -h
问题 5:容器间无法通信
bash
# 确认在同一网络
docker network inspect <network_name>
# 测试连通性
docker exec <container1> ping <container2_name>
docker exec <container1> curl http://<container2_name>:port
# 注意:默认 bridge 网络不支持 DNS 解析容器名,需用自定义网络
问题 6:权限被拒绝(Permission denied)
bash
# 挂载目录权限问题
# 方式一:以 root 用户运行
docker run --user root ...
# 方式二:修改宿主机目录权限
chmod 777 /path/to/dir
# 或匹配容器内用户 UID
chown 1000:1000 /path/to/dir
# Docker 套接字权限
sudo chmod 666 /var/run/docker.sock
13.4 资源限制
bash
# 内存限制
docker run -d --memory 512m --memory-swap 1g nginx
# --memory: 最大内存
# --memory-swap: 内存+交换分区总量
# CPU 限制
docker run -d --cpus 1.5 nginx # 最多使用 1.5 个 CPU 核心
docker run -d --cpuset-cpus 0,1 nginx # 绑定到 CPU 0 和 1
# 磁盘 IO 限制
docker run -d --device-read-bps /dev/sda:1mb nginx
13.5 安全建议
- 不要在镜像中硬编码密码/密钥:使用环境变量或 secrets
- 定期更新基础镜像:修复安全漏洞
- 扫描镜像漏洞 :
docker scan <image>或使用 Trivy - 使用内容信任 :
export DOCKER_CONTENT_TRUST=1 - 最小权限原则 :容器以非 root 用户运行,不使用
--privileged - 只读文件系统 :
docker run --read-only ...,需要写的目录挂载 tmpfs
附录
A. Docker 资源文件位置(Linux)
| 资源 | 路径 |
|---|---|
| 镜像/容器数据 | /var/lib/docker/ |
| 数据卷 | /var/lib/docker/volumes/ |
| 配置文件 | /etc/docker/daemon.json |
| 日志(systemd) | journalctl -u docker |
| 服务文件 | /lib/systemd/system/docker.service |
B. 常用镜像地址
- Docker Hub: https://hub.docker.com
- 官方文档: https://docs.docker.com
- DaoCloud 镜像: https://docker.m.daocloud.io
- 中科大镜像: https://docker.mirrors.ustc.edu.cn
C. 学习路径建议
- 入门:安装 Docker → 运行 hello-world → 拉取 Nginx/MySQL → 理解镜像和容器
- 基础:掌握容器/镜像命令 → Dockerfile 编写 → 数据卷和网络
- 进阶:Docker Compose → 多阶段构建 → 私有仓库 → 日志和监控
- 高级:Docker Swarm/Kubernetes → CI/CD 集成 → 安全加固 → 性能调优
提示 :本教程基于 Docker Engine 24.x / Compose V2 编写,部分命令在旧版本中可能略有差异。建议使用
docker --help或docker <command> --help查看当前版本的完整命令说明。