Docker 完整教程

从入门到实战,系统掌握 Docker 容器化技术


目录

  1. [Docker 简介](#Docker 简介)
  2. 核心概念
  3. [安装 Docker](#安装 Docker)
  4. 镜像管理
  5. 容器管理
  6. 数据管理
  7. 网络管理
  8. [Dockerfile 详解](#Dockerfile 详解)
  9. [Docker Compose](#Docker Compose)
  10. 仓库管理
  11. 实战案例
  12. 常用命令速查表
  13. 最佳实践与排错

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

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

save vs export 区别

  • 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 而不停止容器

exec vs attach

  • 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 镜像构建最佳实践

  1. 使用官方基础镜像 :优先选择 alpineslim 版本减小体积
  2. 多阶段构建:编译和运行分离,最终镜像只含运行时
  3. 合理利用缓存:将变化少的指令(如依赖安装)放在前面
  4. 合并 RUN 指令 :减少镜像层数,如 RUN apt update && apt install -y ...
  5. 清理缓存:安装依赖后清理包管理器缓存
  6. 使用 .dockerignore:减小构建上下文
  7. 非 root 用户运行:安全最佳实践
  8. 指定具体标签 :不要用 latest,生产环境锁定版本

13.2 容器运行最佳实践

  1. 一个容器一个进程:保持容器职责单一
  2. 容器无状态:数据存数据卷,容器可随时销毁重建
  3. 设置资源限制--memory, --cpus 防止资源耗尽
  4. 设置重启策略--restart=unless-stopped
  5. 健康检查:配置 HEALTHCHECK
  6. 日志输出到 stdout/stderr :不要写文件,用 docker logs 查看
  7. 使用自定义网络:不要用默认 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 安全建议

  1. 不要在镜像中硬编码密码/密钥:使用环境变量或 secrets
  2. 定期更新基础镜像:修复安全漏洞
  3. 扫描镜像漏洞docker scan <image> 或使用 Trivy
  4. 使用内容信任export DOCKER_CONTENT_TRUST=1
  5. 最小权限原则 :容器以非 root 用户运行,不使用 --privileged
  6. 只读文件系统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. 常用镜像地址

C. 学习路径建议

  1. 入门:安装 Docker → 运行 hello-world → 拉取 Nginx/MySQL → 理解镜像和容器
  2. 基础:掌握容器/镜像命令 → Dockerfile 编写 → 数据卷和网络
  3. 进阶:Docker Compose → 多阶段构建 → 私有仓库 → 日志和监控
  4. 高级:Docker Swarm/Kubernetes → CI/CD 集成 → 安全加固 → 性能调优

提示 :本教程基于 Docker Engine 24.x / Compose V2 编写,部分命令在旧版本中可能略有差异。建议使用 docker --helpdocker <command> --help 查看当前版本的完整命令说明。

相关推荐
java_logo1 小时前
Docker 一键部署 Jellyfin:快速搭建私有化媒体服务器
docker·私有化部署·jellyfin·硬件加速·媒体服务器·轩辕镜像·影音库
jianpeng的工程笔记2 小时前
WeKnora 搭建研发 EDA 知识库:Docker 部署、RAG 与 Wiki 实践
docker·知识库·eda·rag
ZeroNews内网穿透3 小时前
内网部署 Halo 建站,用 ZeroNews 免费内网穿透安全发布到公网(无公网 IP 也能 HTTPS 访问)
docker·https·内网穿透·nas·反向代理·halo·zeronews
九皇叔叔16 小时前
K8S 资源菜单
docker·容器·kubernetes·k8s
wdfk_prog17 小时前
ROS教程08:从 TransportTCP::connect() 追到 TCPROS Connection Header、序列化与 Socket 数据传输
运维·缓存·docker·容器·ros
zoutao9818 小时前
Gitea Actions 自定义 Runner 镜像与自动化部署实战
ci/cd·docker·gitea
小马同学-20 小时前
nerdctl实战:Docker风格的Containerd操作利器
docker·容器·containerd
九皇叔叔21 小时前
Kubernetes 命令式对象配置详解:使用 YAML 管理 Kubernetes 资源
docker·容器·k8s
CVer儿1 天前
docker内llama.cpp和TRT-LLM win11本机部署qwen3.8
docker