Docker 初级指南
Docker 是一个开源的容器化平台,允许开发者将应用及其依赖打包到一个轻量级、可移植的容器中,在任何环境中一致地运行。 Image(镜像) 镜像是一个只读的模板,用于创建容器。它包含了运行应用所需的所有文件:操作系统文件、运行时、库、配置等。镜像采用分层结构,每一层都是只读的。 Container(容器) 容器是镜像的运行实例。在只读的镜像层之上,容器有一个可读写层。容器之间相互隔离,但共享宿主机的操作系统内核。容器停止后其可写层数据默认消失(需要卷来持久化)。 Registry(仓库) Registry 是存储和分发镜像的服务。常见的 Registr
官方文档:https://docs.docker.com/
适用版本:Docker Engine 24+(2026-05-07 核实)
Docker 是一个开源的容器化平台,允许开发者将应用及其依赖打包到一个轻量级、可移植的容器中,在任何环境中一致地运行。
1. Docker 是什么
容器 vs 虚拟机
| 对比项 | 容器(Container) | 虚拟机(VM) |
|---|---|---|
| 启动速度 | 秒级(毫秒级) | 分钟级 |
| 资源占用 | 极低,共享宿主机内核 | 较高,每个 VM 独立内核 |
| 隔离级别 | 进程级隔离(namespace/cgroup) | 硬件级隔离(Hypervisor) |
| 镜像大小 | 通常几十 MB 到数百 MB | 通常几 GB 到几十 GB |
| 可移植性 | 极高,跨平台一致运行 | 较高,但文件体积大 |
| 密度 | 单机可运行数百个容器 | 单机通常运行数十个 VM |
| 安全性 | 相对较低(共享内核) | 较高(完全隔离) |
核心概念
Image(镜像)
镜像是一个只读的模板,用于创建容器。它包含了运行应用所需的所有文件:操作系统文件、运行时、库、配置等。镜像采用分层结构,每一层都是只读的。
Container(容器)
容器是镜像的运行实例。在只读的镜像层之上,容器有一个可读写层。容器之间相互隔离,但共享宿主机的操作系统内核。容器停止后其可写层数据默认消失(需要卷来持久化)。
Registry(仓库)
Registry 是存储和分发镜像的服务。常见的 Registry:
- Docker Hub(
hub.docker.com):官方公共仓库 - 阿里云容器镜像服务:国内加速
- GitHub Container Registry(
ghcr.io) - 私有 Registry:自建的 Harbor 等
Dockerfile
Dockerfile 是一个文本文件,包含一系列指令,描述如何构建一个镜像。每条指令对应镜像中的一层。
Docker Compose
Docker Compose 是用于定义和运行多容器 Docker 应用的工具。通过一个 docker-compose.yml 文件配置所有服务,然后用一条命令启动整个应用栈。
为什么使用 Docker
- 环境一致性:开发、测试、生产环境完全一致,消除"在我机器上能跑"的问题
- 快速部署:镜像一旦构建,几秒内即可启动容器,大幅缩短部署时间
- 资源隔离:每个容器拥有独立的文件系统、网络、进程空间,互不干扰
- 版本管理:镜像版本化,可以随时回滚到任意历史版本
- 微服务友好:天然适合微服务架构,每个服务独立打包、独立扩展
2. 安装
Linux(Ubuntu / Debian)
# 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc
# 更新包索引并安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
# 添加 Docker 官方 GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# 添加 Docker 仓库
echo \
"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/null
# 安装 Docker Engine
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 将当前用户加入 docker 组(避免每次 sudo)
sudo usermod -aG docker $USER
newgrp docker
# 启动并设置开机启动
sudo systemctl enable docker
sudo systemctl start docker
macOS(Docker Desktop)
- 访问 https://www.docker.com/products/docker-desktop
- 下载 macOS 版本(区分 Intel 和 Apple Silicon)
- 安装
.dmg文件,拖拽到 Applications - 启动 Docker Desktop,等待鲸鱼图标稳定
也可使用 Homebrew:
brew install --cask docker
Windows(Docker Desktop + WSL2)
前置条件:
# 以管理员身份在 PowerShell 中执行
# 启用 WSL
wsl --install
# 安装完成后重启,然后安装 Ubuntu 发行版
wsl --install -d Ubuntu
然后:
- 访问 https://www.docker.com/products/docker-desktop
- 下载 Windows 版 Docker Desktop
- 安装时勾选"Use WSL 2 instead of Hyper-V"
- 安装完成后重启
- 在 Docker Desktop 设置中,确认 WSL Integration 已开启
验证安装
# 查看版本
docker --version
# 输出示例:Docker version 24.0.5, build ced0996
# 查看详细信息(守护进程是否运行)
docker info
# 运行 hello-world 测试
docker run hello-world
hello-world 输出 "Hello from Docker!" 表示安装成功,它完整演示了 Docker 从拉取镜像到运行容器的全流程。
3. 核心概念与架构
Docker 架构
Docker 采用客户端-服务端架构:
Docker Client (docker CLI)
|
| REST API / Unix socket
v
Docker Daemon (dockerd) <-----> Registry (Docker Hub 等)
|
+-- Images(本地镜像存储)
+-- Containers(运行中的容器)
+-- Networks(网络)
+-- Volumes(卷)
- Docker Client:用户与 Docker 交互的命令行工具,发送命令给 Daemon
- Docker Daemon(dockerd):后台运行的服务,负责构建、运行、管理容器
- Registry:镜像仓库,Daemon 从这里拉取(pull)和推送(push)镜像
镜像分层机制(Layer)
Docker 镜像由多个只读层叠加而成。以下 Dockerfile 会产生多层:
FROM ubuntu:22.04 # 第1层:基础镜像
RUN apt-get update # 第2层:更新包索引
RUN apt-get install -y nginx # 第3层:安装 nginx
COPY index.html /var/www/html/ # 第4层:复制文件
层的核心特性:
- 每条
RUN、COPY、ADD指令产生一个新层 - 层是只读且可复用的:多个镜像共享相同的层,节省磁盘空间
- 拉取镜像时,已有的层不会重复下载
- 构建时,未变化的层从缓存读取,加速构建
Union File System(联合文件系统)
Docker 使用联合文件系统(如 overlay2)将多个只读层和一个可写层合并成一个统一的文件系统视图。容器对文件的修改写入最顶层的可写层(容器层),不影响下方的只读镜像层。容器删除后,可写层随之消失。
4. 常用镜像操作命令
docker pull
从 Registry 拉取镜像。
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-a, --all-tags |
flag | false | 拉取仓库中所有 tag 的镜像 |
--platform |
string | 当前平台 | 指定目标平台,如 linux/amd64、linux/arm64 |
-q, --quiet |
flag | false | 静默模式,不显示进度条 |
--disable-content-trust |
flag | true | 跳过镜像签名验证 |
# 拉取最新版 nginx(等同于 nginx:latest)
docker pull nginx
# 拉取指定 tag
docker pull nginx:alpine
docker pull ubuntu:22.04
# 拉取指定 digest(精确版本,不可变)
docker pull nginx@sha256:abc123...
# 为 ARM 设备拉取 AMD64 镜像
docker pull --platform linux/amd64 nginx
docker images / docker image ls
列出本地镜像。
docker images [OPTIONS] [REPOSITORY[:TAG]]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-a, --all |
flag | false | 显示所有镜像,含构建中间层 |
-q, --quiet |
flag | false | 只输出镜像 ID |
--format |
string | - | 自定义输出格式(Go 模板) |
-f, --filter |
string | - | 过滤条件,如 dangling=true |
--no-trunc |
flag | false | 显示完整的镜像 ID |
--digests |
flag | false | 显示 digest 信息 |
# 列出所有本地镜像
docker images
# 只输出 ID(用于批量操作)
docker images -q
# 过滤悬空镜像(没有 tag 的镜像,通常是构建遗留)
docker images -f dangling=true
# 自定义格式输出
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
# 只显示 nginx 相关镜像
docker images nginx
docker rmi / docker image rm
删除本地镜像。
docker rmi [OPTIONS] IMAGE [IMAGE...]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-f, --force |
flag | false | 强制删除,即使有容器依赖此镜像 |
--no-prune |
flag | false | 不删除未被 tag 的父镜像 |
# 删除指定镜像
docker rmi nginx:alpine
# 强制删除(有容器在用时)
docker rmi -f nginx:alpine
# 删除多个镜像
docker rmi nginx ubuntu:22.04
# 通过 ID 删除
docker rmi abc123def456
# 批量删除所有悬空镜像
docker image prune
# 清理所有未被任何容器使用的镜像(谨慎)
docker image prune -a
# 删除所有镜像(极度谨慎)
docker rmi $(docker images -q)
docker search
在 Docker Hub 中搜索镜像。
docker search [OPTIONS] TERM
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--limit |
int | 25 | 最大返回结果数 |
-f, --filter |
string | - | 过滤条件 |
--format |
string | - | 自定义输出格式 |
--no-trunc |
flag | false | 显示完整描述 |
常用过滤条件:
| 过滤器 | 示例 | 说明 |
|---|---|---|
stars=N |
stars=100 |
最少 N 个 star |
is-official=true |
is-official=true |
只显示官方镜像 |
is-automated=true |
is-automated=true |
只显示自动构建镜像 |
# 搜索 nginx
docker search nginx
# 只看官方镜像且 star 数超过 1000
docker search --filter is-official=true --filter stars=1000 nginx
# 限制结果数
docker search --limit 5 python
docker tag
为镜像打标签,常用于推送前重命名。
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
# 为本地镜像打 tag,准备推送到私有仓库
docker tag myapp:1.0 registry.example.com/myteam/myapp:1.0
# 打 latest tag
docker tag myapp:1.0 myapp:latest
# 推送到 Docker Hub(需先 docker login)
docker tag myapp:1.0 username/myapp:1.0
docker push username/myapp:1.0
5. 容器操作命令
docker run
创建并启动一个新容器,是最核心的命令。
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-d, --detach |
flag | false | 后台运行容器,返回容器 ID |
-i, --interactive |
flag | false | 保持 stdin 打开 |
-t, --tty |
flag | false | 分配伪终端(TTY) |
--name |
string | 随机生成 | 指定容器名称,便于引用 |
-p, --publish |
string | - | 端口映射,格式:HOST_PORT:CONTAINER_PORT 或 HOST_PORT:CONTAINER_PORT/tcp |
-P, --publish-all |
flag | false | 将所有 EXPOSE 的端口映射到宿主机随机端口 |
-v, --volume |
string | - | 挂载卷,格式:HOST_PATH:CONTAINER_PATH[:ro] |
--mount |
string | - | 更清晰的挂载语法,推荐用于复杂挂载 |
-e, --env |
string | - | 设置环境变量,如 -e KEY=VALUE |
--env-file |
string | - | 从文件批量读取环境变量 |
--network |
string | bridge | 网络模式:bridge、host、none、自定义网络名 |
--rm |
flag | false | 容器退出后自动删除容器(不删除卷) |
--restart |
string | no | 重启策略 |
-w, --workdir |
string | - | 容器内的工作目录 |
-u, --user |
string | - | 运行命令的用户,格式:user 或 user:group |
-m, --memory |
string | - | 内存限制,如 512m、1g |
--memory-swap |
string | - | 内存+Swap 总限制 |
--cpus |
float | - | CPU 核数限制,如 0.5、2 |
--cpu-shares |
int | 1024 | CPU 相对权重 |
--platform |
string | 当前平台 | 指定镜像平台 |
--entrypoint |
string | - | 覆盖镜像的 ENTRYPOINT |
-l, --label |
string | - | 添加元数据标签 |
--hostname |
string | 容器ID | 容器主机名 |
--add-host |
string | - | 添加 hosts 记录,格式 host:ip |
--privileged |
flag | false | 给予容器扩展权限(谨慎使用) |
--read-only |
flag | false | 将容器根文件系统设为只读 |
重启策略说明:
| 策略 | 说明 |
|---|---|
no |
不自动重启(默认) |
always |
总是重启,包括 Docker 守护进程重启后 |
on-failure[:N] |
仅在非零退出码时重启,可限制次数 |
unless-stopped |
总是重启,除非手动 stop |
# 后台运行 nginx,将宿主机 80 端口映射到容器 80 端口
docker run -d -p 80:80 --name my-nginx nginx
# 交互式运行 ubuntu,退出后自动删除容器
docker run -it --rm ubuntu:22.04 bash
# 挂载当前目录到容器 /app,设置工作目录,安装依赖
docker run -v $(pwd):/app -w /app node:18 npm install
# 传入环境变量运行 PostgreSQL
docker run -d \
--name my-postgres \
-e POSTGRES_USER=admin \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=mydb \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/data \
--restart unless-stopped \
postgres:15
# 限制资源运行
docker run -d --memory 512m --cpus 1.0 --name limited-app myapp
# 从环境变量文件启动
docker run --env-file .env myapp
# 只读根文件系统(安全加固)
docker run --read-only --tmpfs /tmp myapp
# 覆盖 ENTRYPOINT,进入 shell 调试
docker run -it --entrypoint bash nginx
docker start / stop / restart / kill
对已有容器进行生命周期管理。
# docker start:启动已停止的容器
docker start [OPTIONS] CONTAINER [CONTAINER...]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-a, --attach |
flag | false | 附加到容器的 stdout/stderr |
-i, --interactive |
flag | false | 附加容器的 stdin |
# docker stop:优雅停止容器(发送 SIGTERM,等待后发送 SIGKILL)
docker stop [OPTIONS] CONTAINER [CONTAINER...]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-t, --time |
int | 10 | 等待容器停止的超时秒数 |
-s, --signal |
string | SIGTERM | 发送的停止信号 |
# 示例
docker start my-nginx
docker stop my-nginx
docker stop -t 30 my-nginx # 等待最多 30 秒
docker restart my-nginx
docker restart -t 5 my-nginx # 5 秒后强制重启
# docker kill:立即发送信号(默认 SIGKILL)
docker kill [OPTIONS] CONTAINER [CONTAINER...]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-s, --signal |
string | KILL | 发送的信号名或编号 |
docker kill my-nginx
docker kill -s SIGINT my-nginx
docker ps / docker container ls
列出容器。
docker ps [OPTIONS]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-a, --all |
flag | false | 显示所有容器,含已停止的 |
-q, --quiet |
flag | false | 只输出容器 ID |
-f, --filter |
string | - | 过滤条件 |
--format |
string | - | 自定义输出格式 |
-n, --last |
int | -1 | 显示最近创建的 N 个容器 |
-l, --latest |
flag | false | 显示最近创建的容器 |
-s, --size |
flag | false | 显示容器文件大小 |
--no-trunc |
flag | false | 不截断输出 |
常用过滤条件:
| 过滤器 | 示例 | 说明 |
|---|---|---|
status |
status=running |
按状态过滤:created/running/paused/exited/dead |
name |
name=my-nginx |
按名称过滤(支持正则) |
label |
label=env=prod |
按标签过滤 |
ancestor |
ancestor=nginx |
基于某镜像创建的容器 |
# 查看运行中的容器
docker ps
# 查看所有容器(含已停止)
docker ps -a
# 只看 ID(用于批量操作)
docker ps -q
# 查看已停止的容器
docker ps -f status=exited
# 自定义输出格式
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# 停止所有运行中的容器
docker stop $(docker ps -q)
docker exec
在运行中的容器内执行命令。
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-i, --interactive |
flag | false | 保持 stdin 打开 |
-t, --tty |
flag | false | 分配伪终端 |
-d, --detach |
flag | false | 在后台执行命令 |
-e, --env |
string | - | 设置环境变量 |
-u, --user |
string | - | 指定执行用户 |
-w, --workdir |
string | - | 指定工作目录 |
--privileged |
flag | false | 给予命令扩展权限 |
# 进入容器 bash(最常用)
docker exec -it my-nginx bash
# 进入容器 sh(当容器没有 bash 时)
docker exec -it my-nginx sh
# 在容器内执行单条命令
docker exec my-nginx nginx -t
# 以 root 身份进入容器
docker exec -it -u root my-app bash
# 在指定工作目录执行
docker exec -w /app my-app ls -la
# 后台执行命令
docker exec -d my-nginx touch /tmp/testfile
docker logs
获取容器的日志输出。
docker logs [OPTIONS] CONTAINER
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-f, --follow |
flag | false | 实时跟踪日志输出 |
--tail |
string | all | 只显示最后 N 行 |
--since |
string | - | 显示指定时间点之后的日志,如 2024-01-01、1h |
--until |
string | - | 显示指定时间点之前的日志 |
-t, --timestamps |
flag | false | 显示时间戳 |
--details |
flag | false | 显示额外的日志详情 |
# 查看容器日志
docker logs my-nginx
# 实时跟踪(类似 tail -f)
docker logs -f my-nginx
# 只看最后 100 行
docker logs --tail 100 my-nginx
# 实时跟踪最后 50 行开始
docker logs -f --tail 50 my-nginx
# 显示时间戳
docker logs -t my-nginx
# 查看最近 1 小时的日志
docker logs --since 1h my-nginx
# 查看指定时间范围
docker logs --since "2024-01-01T10:00:00" --until "2024-01-01T11:00:00" my-nginx
docker cp
在容器与宿主机之间复制文件。
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-a, --archive |
flag | false | 保留文件所有者和权限(Archive 模式) |
-L, --follow-link |
flag | false | 跟随 SRC_PATH 中的符号链接 |
-q, --quiet |
flag | false | 静默模式 |
# 从容器复制文件到宿主机
docker cp my-nginx:/etc/nginx/nginx.conf ./nginx.conf
# 从宿主机复制文件到容器
docker cp ./index.html my-nginx:/usr/share/nginx/html/index.html
# 复制整个目录
docker cp my-app:/app/logs ./logs
# 复制到容器时保留权限
docker cp -a ./config.json my-app:/etc/app/config.json
docker rm
删除已停止的容器。
docker rm [OPTIONS] CONTAINER [CONTAINER...]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-f, --force |
flag | false | 强制删除运行中的容器(发送 SIGKILL) |
-v, --volumes |
flag | false | 同时删除容器关联的匿名卷 |
-l, --link |
flag | false | 删除容器间的网络连接(不删除容器) |
# 删除已停止的容器
docker rm my-nginx
# 强制删除运行中的容器
docker rm -f my-nginx
# 删除容器及其匿名卷
docker rm -v my-nginx
# 清理所有已停止的容器
docker container prune
# 删除所有容器(包括运行中的,谨慎)
docker rm -f $(docker ps -aq)
docker inspect
获取容器或镜像的详细配置信息(JSON 格式)。
docker inspect [OPTIONS] NAME|ID [NAME|ID...]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-f, --format |
string | - | 使用 Go 模板格式化输出 |
--type |
string | - | 指定类型:container、image、network、volume |
-s, --size |
flag | false | 显示容器文件系统大小(仅容器) |
# 查看容器全部信息
docker inspect my-nginx
# 只获取容器 IP 地址
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-nginx
# 获取挂载点信息
docker inspect -f '{{json .Mounts}}' my-nginx | python3 -m json.tool
# 获取环境变量
docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' my-nginx
# 获取容器重启策略
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' my-nginx
docker stats
实时显示容器资源使用情况。
docker stats [OPTIONS] [CONTAINER...]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-a, --all |
flag | false | 显示所有容器(含停止的) |
--no-stream |
flag | false | 只输出一次,不持续刷新 |
--no-trunc |
flag | false | 不截断容器 ID |
--format |
string | - | 自定义输出格式 |
# 实时查看所有运行容器的资源使用
docker stats
# 查看指定容器
docker stats my-nginx my-postgres
# 只输出一次(用于脚本)
docker stats --no-stream
# 自定义格式
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
6. Dockerfile 基础
基本结构示例
以下是一个完整的 Node.js 应用 Dockerfile:
# 指定基础镜像和版本(使用具体版本而非 latest)
FROM node:18-alpine
# 设置维护者信息(元数据)
LABEL maintainer="[email protected]"
LABEL version="1.0"
# 设置工作目录(不存在会自动创建)
WORKDIR /app
# 先复制 package 文件,利用层缓存
# 只有 package.json 变化时才重新 npm install
COPY package*.json ./
# 安装生产依赖(alpine 镜像已有 npm)
RUN npm ci --only=production
# 再复制其余源代码
COPY . .
# 声明应用监听的端口(文档作用,不自动映射)
EXPOSE 3000
# 创建非 root 用户提升安全性
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# 设置健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
# 容器启动时执行的命令(exec 形式,推荐)
CMD ["node", "server.js"]
常用指令完整参考
| 指令 | 语法示例 | 说明 |
|---|---|---|
FROM |
FROM ubuntu:22.04 |
指定基础镜像,必须是第一条非注释指令 |
RUN |
RUN apt-get install -y curl |
执行命令并提交结果为新层 |
COPY |
COPY src/ /app/src/ |
从构建上下文复制文件,推荐使用 |
ADD |
ADD archive.tar.gz /app/ |
类似 COPY,额外支持 URL 和自动解压 tar |
WORKDIR |
WORKDIR /app |
设置后续指令的工作目录 |
ENV |
ENV NODE_ENV=production |
设置环境变量,运行时可见 |
ARG |
ARG BUILD_VERSION=1.0 |
构建参数,仅构建阶段可见 |
EXPOSE |
EXPOSE 8080 |
声明容器监听端口(仅文档作用) |
CMD |
CMD ["node", "app.js"] |
默认启动命令,可被 docker run 末尾参数覆盖 |
ENTRYPOINT |
ENTRYPOINT ["nginx", "-g", "daemon off;"] |
入口点,不易被覆盖 |
LABEL |
LABEL version="1.0" |
添加键值元数据 |
USER |
USER 1001 |
切换运行用户 |
VOLUME |
VOLUME ["/data"] |
声明挂载点 |
HEALTHCHECK |
HEALTHCHECK CMD curl -f http://localhost/ |
定义健康检查命令 |
ONBUILD |
ONBUILD COPY . /app |
仅当此镜像作为基础镜像时触发 |
STOPSIGNAL |
STOPSIGNAL SIGTERM |
设置停止容器时发送的信号 |
FROM
# 基本用法
FROM ubuntu:22.04
# 使用摘要锁定版本
FROM ubuntu@sha256:abc123...
# 多阶段构建中命名阶段
FROM node:18 AS builder
FROM nginx:alpine AS final
RUN
RUN 有两种形式:
# Shell 形式(通过 /bin/sh -c 执行,支持 shell 特性)
RUN apt-get update && apt-get install -y \
curl \
wget \
&& rm -rf /var/lib/apt/lists/*
# Exec 形式(直接执行,不通过 shell,推荐用于明确路径)
RUN ["apt-get", "install", "-y", "curl"]
建议在一条 RUN 中链接多条命令,减少层数:
# 不推荐:产生多个层
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# 推荐:一层完成,且清理缓存
RUN apt-get update \
&& apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/*
COPY vs ADD
| 特性 | COPY | ADD |
|---|---|---|
| 复制本地文件 | 支持 | 支持 |
| 支持 URL 下载 | 不支持 | 支持 |
| 自动解压 tar | 不支持 | 支持(本地 tar) |
| 可预测性 | 高 | 低(行为复杂) |
| 推荐程度 | 推荐 | 不推荐(除非需要解压) |
# COPY 用法
COPY package.json /app/
COPY src/ /app/src/
COPY --chown=appuser:appgroup . /app/ # 指定所有者
# ADD 用法(仅在需要解压时使用)
ADD https://example.com/file.tar.gz /tmp/ # 下载并解压
ADD archive.tar.gz /app/ # 自动解压
ENV 与 ARG
# ENV:运行时环境变量,容器运行后也可见
ENV NODE_ENV=production
ENV PORT=3000 HOST=0.0.0.0 # 一行设置多个
# ARG:构建参数,仅在 docker build 时可用
ARG BUILD_DATE
ARG VERSION=1.0.0
# ARG 和 ENV 结合使用
ARG APP_VERSION=latest
ENV APP_VERSION=${APP_VERSION} # 将构建参数转为环境变量
# 构建时传入 ARG 值
docker build --build-arg BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
--build-arg VERSION=2.0.0 \
-t myapp:2.0.0 .
HEALTHCHECK
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--interval |
duration | 30s | 两次检查的间隔时间 |
--timeout |
duration | 30s | 单次检查的超时时间 |
--start-period |
duration | 0s | 容器启动后等待多久才开始检查 |
--retries |
int | 3 | 连续失败多少次才判定为 unhealthy |
# HTTP 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 禁用基础镜像的健康检查
HEALTHCHECK NONE
CMD 与 ENTRYPOINT 的关系
| 组合方式 | ENTRYPOINT | CMD | 实际执行命令 |
|---|---|---|---|
| 只有 CMD | 无 | ["node", "app.js"] |
node app.js |
| 只有 ENTRYPOINT | ["node"] |
无 | node |
| 两者都有 | ["node"] |
["app.js"] |
node app.js |
| docker run 附加参数 | ["node"] |
["app.js"] |
node <你的参数>(CMD 被覆盖) |
| docker run --entrypoint | <被覆盖> |
["app.js"] |
<新entrypoint> app.js |
最佳实践:
- ENTRYPOINT 定义固定的可执行程序
- CMD 提供默认参数,允许用户在
docker run时覆盖 - 两者都应使用 exec 形式(JSON 数组),避免 shell 形式(PID 1 问题)
# 推荐写法:exec 形式
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
# 不推荐:shell 形式(nginx 不是 PID 1,无法接收信号)
ENTRYPOINT nginx -g "daemon off;"
.dockerignore 文件
类似 .gitignore,防止不必要的文件进入构建上下文,加快构建速度:
# .dockerignore
# 版本控制
.git
.gitignore
# Node.js
node_modules
npm-debug.log
# 构建产物
dist
build
*.log
# 环境配置
.env
.env.*
*.env
# IDE 配置
.idea
.vscode
*.swp
# 文档
README.md
docs/
# 测试
__tests__
*.test.js
coverage/
# Docker 相关(避免递归)
Dockerfile*
docker-compose*
.dockerignore
构建命令 docker build
docker build [OPTIONS] PATH | URL | -
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-t, --tag |
string | - | 镜像名称和标签,格式 name:tag |
-f, --file |
string | ./Dockerfile |
指定 Dockerfile 文件路径 |
--no-cache |
flag | false | 不使用缓存,全量重建所有层 |
--build-arg |
string | - | 传入 ARG 变量值 |
--platform |
string | 当前平台 | 目标平台,如 linux/amd64 |
--target |
string | - | 多阶段构建中指定目标阶段名称 |
--progress |
string | auto | 输出格式:auto、plain、tty |
--pull |
flag | false | 总是拉取最新基础镜像 |
--label |
string | - | 为镜像设置元数据 |
-q, --quiet |
flag | false | 静默模式,只输出镜像 ID |
# 基本构建
docker build -t myapp:1.0 .
# 指定 Dockerfile
docker build -f docker/Dockerfile.prod -t myapp:prod .
# 不使用缓存(排查问题时使用)
docker build --no-cache -t myapp:1.0 .
# 传入构建参数
docker build --build-arg VERSION=2.0 --build-arg ENV=prod -t myapp:2.0 .
# 多阶段构建,只构建到 builder 阶段
docker build --target builder -t myapp:builder .
# 跨平台构建(需要 buildx)
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:1.0 --push .
7. Docker Compose 基础
安装
Docker Compose 有两个版本:
| 版本 | 命令 | 说明 |
|---|---|---|
| V2 插件版(推荐) | docker compose |
集成在 Docker CLI 中,Docker Desktop 默认包含 |
| V1 独立工具(旧) | docker-compose |
独立安装的 Python 脚本,已停止维护 |
# 验证 Compose 插件版本
docker compose version
# Linux 手动安装 Compose 插件(如果没有)
sudo apt-get install docker-compose-plugin
# 查看 V1 版本(如果已安装)
docker-compose --version
基本 docker-compose.yml 结构
以下是一个包含 Nginx、Node.js 应用、PostgreSQL 的完整示例:
# docker-compose.yml
version: "3.9" # Compose 文件规范版本
services:
# Nginx 反向代理
nginx:
image: nginx:alpine
container_name: proxy
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- app
networks:
- frontend
restart: unless-stopped
# Node.js 应用
app:
build:
context: .
dockerfile: Dockerfile
args:
NODE_ENV: production
container_name: node-app
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://admin:secret@db:5432/mydb
- PORT=3000
env_file:
- .env
volumes:
- app-logs:/app/logs
depends_on:
db:
condition: service_healthy
networks:
- frontend
- backend
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# PostgreSQL 数据库
db:
image: postgres:15-alpine
container_name: postgres
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
ports:
- "5432:5432" # 开发环境暴露,生产环境可移除
networks:
- backend
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U admin -d mydb"]
interval: 10s
timeout: 5s
retries: 5
# 具名卷(由 Docker 管理,数据持久化)
volumes:
pgdata:
driver: local
app-logs:
driver: local
# 自定义网络
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # 内部网络,不能访问外网
常用 Compose 命令
| 命令 | 说明 |
|---|---|
docker compose up |
创建并启动所有服务(前台运行) |
docker compose up -d |
创建并启动所有服务(后台运行) |
docker compose up --build |
构建镜像后启动(有代码变更时) |
docker compose up service_name |
只启动指定服务 |
docker compose down |
停止并删除容器、网络 |
docker compose down -v |
停止并删除容器、网络、卷 |
docker compose down --rmi all |
停止并删除容器、镜像 |
docker compose ps |
查看服务状态 |
docker compose logs |
查看所有服务日志 |
docker compose logs -f app |
跟踪指定服务日志 |
docker compose exec app bash |
进入指定服务容器 |
docker compose build |
构建所有服务的镜像 |
docker compose build app |
只构建指定服务 |
docker compose pull |
拉取所有服务镜像 |
docker compose restart |
重启所有服务 |
docker compose restart app |
重启指定服务 |
docker compose stop |
停止所有服务(不删除容器) |
docker compose start |
启动已停止的服务 |
docker compose rm |
删除已停止的服务容器 |
docker compose config |
验证并输出最终配置 |
docker compose run app npm test |
一次性运行命令(新建容器) |
docker compose scale app=3 |
扩展服务实例数量 |
# 启动所有服务(后台)
docker compose up -d
# 有代码变更时重新构建并启动
docker compose up -d --build
# 查看实时日志
docker compose logs -f
# 进入 app 服务容器
docker compose exec app bash
# 在 app 容器中运行迁移(临时容器,结束自动删除)
docker compose run --rm app npm run migrate
# 停止所有服务但保留容器和卷
docker compose stop
# 彻底清理(删除容器、网络、卷)
docker compose down -v
8. 数据管理基础
三种挂载方式对比
| 类型 | 命令行语法 | 数据位置 | 适用场景 |
|---|---|---|---|
| Volume(具名卷) | -v myvolume:/app/data |
Docker 管理目录 | 生产数据持久化(推荐) |
| Bind Mount | -v /host/path:/container/path |
宿主机任意路径 | 开发时同步代码、配置 |
| tmpfs | --tmpfs /app/tmp |
宿主机内存 | 临时数据,不持久化,高性能 |
具名卷 vs 匿名卷 vs Bind Mount 详细对比:
| 特性 | 具名卷 | 匿名卷 | Bind Mount |
|---|---|---|---|
| 名称 | 自定义,如 pgdata |
Docker 自动生成 UUID | 宿主机路径 |
| 创建方式 | docker volume create 或 Compose |
docker run -v /path |
docker run -v /host:/container |
| 生命周期 | 独立于容器,容器删除后保留 | 容器删除时默认保留(-v 删除) |
由宿主机管理 |
| 备份难度 | 简单 | 困难(难以找到路径) | 直接操作宿主机路径 |
| 推荐程度 | 生产环境推荐 | 不推荐 | 开发环境推荐 |
Volume 管理命令
# 创建具名卷
docker volume create mydata
# 列出所有卷
docker volume ls
# 查看卷详细信息
docker volume inspect mydata
# 删除指定卷
docker volume rm mydata
# 清理未使用的卷(谨慎)
docker volume prune
# 在 docker run 中使用具名卷
docker run -v mydata:/app/data myapp
# 只读挂载
docker run -v mydata:/app/data:ro myapp
--mount 语法(更清晰的挂载方式)
# Volume 挂载
docker run --mount type=volume,source=mydata,target=/app/data myapp
# Bind Mount
docker run --mount type=bind,source=/host/path,target=/container/path myapp
# 只读 Bind Mount
docker run --mount type=bind,source=/host/config,target=/etc/app,readonly myapp
# tmpfs
docker run --mount type=tmpfs,target=/app/tmp,tmpfs-size=100m myapp
Compose 中声明卷
services:
app:
volumes:
- pgdata:/var/lib/postgresql/data # 具名卷
- ./src:/app/src # Bind Mount
- /tmp/cache:/app/cache # 绑定到宿主机临时目录
volumes:
pgdata: # 由 Docker 管理
external-vol:
external: true # 使用已存在的外部卷(不由 Compose 创建)
9. 网络基础
默认网络模式对比
| 模式 | 说明 | 使用场景 |
|---|---|---|
bridge |
默认模式,容器有独立网络栈,通过虚拟网桥与宿主机通信 | 单机多容器通信 |
host |
容器共享宿主机网络命名空间,无端口映射,性能最高 | 高性能网络,Linux 专用(macOS/Windows 不支持) |
none |
容器无网络接口,完全隔离 | 安全敏感的无网络场景 |
container:NAME |
共享另一个容器的网络命名空间 | sidecar 模式 |
| 自定义 bridge | 用户创建的桥接网络,支持容器名 DNS 解析 | 多容器应用(推荐) |
注意:默认 bridge 网络不支持通过容器名互访(需 IP),自定义 bridge 网络支持容器名 DNS 解析。
常用网络命令
# 创建自定义网络
docker network create mynet
# 创建指定子网的网络
docker network create --subnet 172.20.0.0/16 mynet
# 列出所有网络
docker network ls
# 查看网络详细信息
docker network inspect mynet
# 删除网络
docker network rm mynet
# 清理未使用的网络
docker network prune
# 将运行中的容器加入网络
docker network connect mynet my-nginx
# 将容器从网络移除
docker network disconnect mynet my-nginx
# 运行时指定网络
docker run --network mynet --name app1 myapp
# 同一自定义网络中,容器可通过容器名互访
docker run --network mynet --name db postgres
docker run --network mynet --name app myapp
# app 容器内可通过 db:5432 访问数据库
10. 常见问题与踩坑
问题 1:docker run 没加 -d 导致终端卡住
现象:运行 docker run nginx 后终端被占用,Ctrl+C 会停止容器。
解决:后台运行加 -d 参数。
# 错误方式
docker run nginx
# 正确方式
docker run -d nginx
# 如果需要查看日志
docker logs -f <container_id>
问题 2:端口冲突
现象:docker run -p 80:80 nginx 报错 Bind for 0.0.0.0:80 failed: port is already allocated。
解决:
# 查找占用端口的进程(Linux/macOS)
lsof -i :80
# Windows
netstat -ano | findstr :80
# 方案一:停止占用端口的进程
# 方案二:映射到其他端口
docker run -d -p 8080:80 nginx
问题 3:容器内无法连接宿主机服务
现象:容器内访问 localhost:3000 连接失败(如容器内应用访问宿主机上的数据库)。
原因:容器有独立网络命名空间,localhost 是容器自身,不是宿主机。
解决:
# macOS 和 Windows Docker Desktop:使用特殊域名
host.docker.internal
# Linux:使用 docker0 网关 IP(通常是 172.17.0.1)
# 或者运行时加 --add-host
docker run --add-host=host.docker.internal:host-gateway myapp
# 在代码中
# 错误:localhost:5432
# 正确:host.docker.internal:5432
问题 4:容器删除后数据丢失
现象:删除容器后,容器内写入的数据全部消失。
原因:容器层是临时可写层,容器删除时一并删除。
解决:必须使用卷或 Bind Mount 持久化数据。
# 数据库必须挂载卷
docker run -d \
-v pgdata:/var/lib/postgresql/data \
postgres:15
# 验证数据持久化
docker rm postgres-container
docker run -d \
-v pgdata:/var/lib/postgresql/data \
postgres:15
# 数据仍然存在
问题 5:镜像层缓存导致全量重建
现象:修改一行代码后,docker build 需要重新安装所有依赖,耗时很长。
原因:COPY 的顺序不当,源代码与依赖文件一起复制,任何改动都使缓存失效。
解决:利用层缓存,先复制依赖描述文件,安装依赖,再复制源代码。
# 错误顺序:每次代码变更都重新 npm install
COPY . .
RUN npm install
# 正确顺序:只有 package.json 变更才重新 npm install
COPY package*.json ./
RUN npm ci
COPY . .
问题 6:时区问题
现象:容器内日志时间比实际时间少 8 小时,应用输出 UTC 时间。
原因:Docker 容器默认使用 UTC 时区。
解决:
# 方式一:运行时设置环境变量
docker run -e TZ=Asia/Shanghai myapp
# 方式二:Dockerfile 中设置
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 方式三:挂载宿主机时区文件(Linux)
docker run -v /etc/localtime:/etc/localtime:ro myapp
问题 7:Linux bind mount 权限不匹配
现象:容器无法读写 bind mount 的文件,报 Permission denied。
原因:容器内进程的 UID/GID 与宿主机文件所有者不匹配。
解决:
# 方式一:在 Dockerfile 中创建与宿主机 UID 匹配的用户
ARG UID=1000
RUN adduser --uid ${UID} --disabled-password appuser
USER appuser
# 方式二:运行时指定用户(匹配宿主机当前用户 UID)
docker run -u $(id -u):$(id -g) -v $(pwd):/app myapp
# 方式三:修改宿主机目录权限(不推荐,影响安全)
chmod -R 777 ./data
常用清理命令汇总
# 清理停止的容器
docker container prune
# 清理未使用的镜像
docker image prune
# 清理未使用的镜像(包括有 tag 的)
docker image prune -a
# 清理未使用的卷(谨慎!)
docker volume prune
# 清理未使用的网络
docker network prune
# 一键清理所有未使用的资源(容器、镜像、网络、构建缓存)
docker system prune
# 包括卷的全面清理(极度谨慎)
docker system prune --volumes
# 查看 Docker 磁盘占用
docker system df
最佳实践
Dockerfile 指令按变化频率从低到高排列:变化少的指令(FROM、RUN apt install)放在前面,变化多的(COPY . .、RUN pip install -r requirements.txt)放在后面,充分利用构建缓存,避免每次修改代码都重新安装依赖。
FROM python:3.12-slim
# 先安装依赖(很少变化)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 再复制代码(经常变化)
COPY . .
CMD ["python", "app.py"]
使用 .dockerignore 减小构建上下文大小:COPY . . 会把整个构建目录发送给 Docker daemon,.git、node_modules、__pycache__、.env 等无需进入镜像的内容应写入 .dockerignore,避免镜像体积膨胀和构建变慢。
容器内不要以 root 用户运行:默认 root 用户在容器逃逸时具有主机 root 权限,存在安全风险。在 Dockerfile 末尾创建非 root 用户并切换。
RUN useradd -m appuser
USER appuser
一个容器只跑一个进程:不要在单个容器中同时运行 Web 服务 + 数据库 + Nginx,违反单一职责使容器难以扩展、监控和重启。多进程应拆分为多个容器,用 docker compose 协调。
用多阶段构建减小生产镜像体积:编译/构建阶段只在第一个 FROM 中完成,最终镜像只包含运行所需的产物。Go / Node / Java 项目镜像体积可减小 10 倍以上。
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
常见陷阱
陷阱:每次修改代码都触发依赖重新安装
现象: docker build 时,即使只改了一行业务代码,npm install 或 pip install 也重新执行,构建速度极慢。
原因: COPY . . 放在安装依赖之前,任何文件变化都会使缓存失效,导致后续所有层重新执行。
解决: 先只 COPY 依赖文件(package.json、requirements.txt),安装完成后再 COPY 其余代码。
陷阱:容器内修改文件后重启丢失
现象: 在运行中的容器内创建了文件或修改了数据库,容器重启后数据消失。
原因: 容器的可写层(writable layer)在容器删除或重新创建时被丢弃,容器内的文件系统不持久化。
解决: 使用 docker volume 将需要持久化的目录挂载为卷,或用 -v /host/path:/container/path 绑定挂载。
docker run -v db_data:/var/lib/postgresql/data postgres
陷阱:镜像中包含敏感信息(密码、密钥)
现象: 将密钥写入 Dockerfile 的 ENV 或 RUN 层,构建后任何人 docker history 都能看到明文。
原因: Dockerfile 中每一层的内容都保存在镜像的 layer 元数据中,即使在后续层删除了该 ENV,仍可通过历史层读取。
解决: 绝不在 Dockerfile 中硬编码密钥;运行时通过环境变量(docker run -e)或 Docker Secrets 注入,配合 .dockerignore 排除 .env 文件。