Docker 常用命令
大白话解释: Docker 就像"集装箱"。把你的应用和它需要的所有东西(代码、运行环境、依赖)打包成一个"集装箱"(镜像),然后在任何安装了 Docker 的机器上都能运行。
为什么要用 Docker?
- 环境一致性:开发环境和生产环境完全一样,不会出现"在我电脑上能跑"的问题
- 快速部署:打包成镜像后,一条命令就能启动,不用手动配置环境
- 隔离性:每个容器独立运行,互不影响
- 易于扩展:需要更多实例?复制几个容器就行
Docker 的核心概念:
- 镜像(Image):集装箱的模板,只读的
- 容器(Container):用镜像创建的实例,可以运行
- 卷(Volume):容器的"外挂硬盘",数据持久化
Docker 是容器化平台,用于打包、分发和运行应用。
安装
- Windows/macOS:安装 Docker Desktop
- Linux:参考 官方文档
# 验证安装
docker --version
docker compose versionDocker Desktop 常用功能
Docker Desktop 是 Windows/macOS 上的一体化 Docker 环境,内置以下功能:
| 功能 | 说明 |
|---|---|
| Docker Engine | 核心容器运行时 |
| Docker Compose | 多容器编排 |
| Docker Scout | 镜像安全漏洞扫描 |
| Kubernetes | 内置 K8s 集群(需在设置中启用) |
| Extensions | 插件扩展市场 |
| Dev Environments | 基于 Git 仓库快速创建开发环境 |
| Volume Management | 可视化管理数据卷 |
| Resource Limits | 在 Settings → Resources 中配置 CPU/内存/磁盘限制 |
# Docker Desktop 内置的实用命令
docker scout cves my-app:latest # 安全扫描
docker context ls # 查看上下文(含 Desktop 的远程引擎)💡 Docker Desktop 免费供个人开发和小型企业使用,大型企业需购买订阅。Linux 用户直接安装 Docker Engine 即可。
镜像操作
# 搜索镜像
docker search nginx
# 拉取镜像
docker pull nginx
docker pull nginx:1.24 # 指定版本
docker pull node:18-alpine # 推荐使用 alpine 版本(更小)
# 查看本地镜像
docker images
# 删除镜像
docker rmi nginx
docker image prune # 清理无用镜像私有镜像仓库
# 登录仓库
docker login # Docker Hub(默认)
docker login registry.example.com # 私有仓库
# 登出
docker logout
docker logout registry.example.com
# 打标签(准备推送到私有仓库)
docker tag my-app:latest registry.example.com/my-app:latest
docker tag my-app:latest registry.example.com/my-app:1.0
# 推送镜像
docker push registry.example.com/my-app:latest
# 拉取私有仓库镜像
docker pull registry.example.com/my-app:1.0
# 完整工作流
docker build -t my-app:1.0 .
docker tag my-app:1.0 registry.example.com/my-app:1.0
docker push registry.example.com/my-app:1.0| 常见仓库 | 地址格式 |
|---|---|
| Docker Hub | username/image:tag |
| GitHub Container Registry | ghcr.io/username/image:tag |
| 阿里云容器镜像服务 | registry.cn-hangzhou.aliyuncs.com/namespace/image:tag |
| Harbor | harbor.example.com/project/image:tag |
💡 推送前必须先
docker login,否则会报unauthorized错误。CI/CD 中可通过echo $PASSWORD | docker login -u $USER --password-stdin安全登录。
容器操作
创建 & 启动
# 创建并启动容器
docker run nginx
# 后台运行
docker run -d nginx
# 指定名称
docker run -d --name my-nginx nginx
# 端口映射(宿主机:容器)
docker run -d -p 8080:80 nginx
# 环境变量
docker run -d -e NODE_ENV=production node-app
# 挂载目录(宿主机:容器)
docker run -d -v /host/path:/container/path nginx
# 重启策略
docker run -d --restart=always nginx
# 临时容器(退出后自动删除,适合一次性任务)
docker run --rm nginx echo "hello"管理容器
# 查看运行中的容器
docker ps
# 查看所有容器(含停止的)
docker ps -a
# 停止容器
docker stop my-nginx
# 启动已停止的容器
docker start my-nginx
# 重启容器
docker restart my-nginx
# 删除容器
docker rm my-nginx
docker rm -f my-nginx # 强制删除运行中的容器
# 清理所有停止的容器
docker container prune进入容器
# 进入容器(交互模式)
# -i:保持 STDIN 打开(允许输入)
# -t:分配伪终端(获得命令行提示符)
# 通常一起使用 -it 才能正常交互
docker exec -it my-nginx /bin/bash
docker exec -it my-nginx sh # Alpine 镜像用 sh
# 在容器内执行命令
docker exec my-nginx ls /app
# 连接到容器主进程(共享 stdin/stdout)
docker attach my-nginxdocker attach vs exec 区别
| 对比 | docker attach | docker exec |
|---|---|---|
| 连接目标 | 容器主进程(PID 1) | 可在容器内启动新进程 |
| 退出影响 | 退出可能导致容器停止 | 退出不影响容器运行 |
| 多次使用 | 只能 attach 一次 | 可同时多个 exec 会话 |
| 适用场景 | 调试主进程、查看输出 | 执行命令、进入 shell |
| 推荐度 | ⚠️ 谨慎使用 | ✅ 推荐使用 |
💡 推荐使用
docker exec进入容器,docker attach退出时可能意外停止容器。如需安全 detach,使用Ctrl+P, Ctrl+Q。
日志查看
# 查看日志
docker logs my-nginx
# 实时追踪
docker logs -f my-nginx
# 最近 100 行
docker logs --tail 100 my-nginx
# 带时间戳
docker logs -t my-nginxDockerfile
基本结构
# 基础镜像
FROM node:18-alpine
# 工作目录
WORKDIR /app
# 复制依赖文件
COPY package.json yarn.lock ./
# 安装依赖
RUN yarn install --production
# 复制源代码
COPY . .
# 暴露端口
EXPOSE 3000
# 启动命令
CMD ["node", "app.js"]常用指令
# 设置环境变量
ENV NODE_ENV=production
# 添加元数据
LABEL maintainer="[email protected]"
# 构建时执行
RUN apt-get update && apt-get install -y curl
# 复制文件(推荐)
COPY . .
COPY --from=builder /app/dist ./dist
# 多阶段构建
FROM node:18 AS builder
WORKDIR /app
COPY . .
RUN yarn build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html指令详解
FROM
指定基础镜像,必须是 Dockerfile 的第一条指令。
FROM node:18-alpine
FROM nginx:1.24-alpine
FROM python:3.11-slim
FROM scratch # 空白镜像,用于构建最小镜像| 语法 | 说明 |
|---|---|
FROM <image> | 默认 latest 标签 |
FROM <image>:<tag> | 指定版本标签 |
FROM <image> AS <name> | 命名构建阶段(多阶段构建) |
WORKDIR
设置工作目录,后续指令在此目录下执行。目录不存在时会自动创建。
WORKDIR /app
WORKDIR /usr/src/app
WORKDIR /app # 可多次使用,支持相对路径
WORKDIR src # 相对于 /app,即 /app/src| 要点 | 说明 |
|---|---|
优于 RUN cd | WORKDIR 对后续所有指令生效 |
| 自动创建 | 目录不存在时自动 mkdir -p |
| 可多次使用 | 每次切换上下文 |
COPY
从构建上下文复制文件到镜像中。
COPY package.json ./
COPY src/ ./src/
COPY --from=builder /app/dist ./dist # 从其他构建阶段复制
COPY --chown=node:node . ./app # 复制时设置所有者
COPY --chmod=755 script.sh ./ # 复制时设置权限(BuildKit)| 语法 | 说明 |
|---|---|
COPY <src>... <dest> | 复制文件或目录 |
COPY --from=<stage> | 多阶段构建中从其他阶段复制 |
COPY --chown=<user>:<group> | 设置文件所有者 |
COPY --chmod=<perms> | 设置文件权限(需 BuildKit) |
ADD
与 COPY 类似,但功能更多。
ADD app.tar.gz /app/ # 自动解压 tar 文件
ADD https://example.com/file.txt /app/ # 支持远程 URL
ADD --chown=node:node . ./appCOPY vs ADD 区别
| 特性 | COPY | ADD |
|---|---|---|
| 基本文件复制 | ✅ | ✅ |
| 自动解压 tar | ❌ | ✅ |
| 支持远程 URL | ❌ | ✅ |
| 推荐使用 | ✅ 优先使用 | 仅在需要解压时使用 |
💡 最佳实践:优先使用 COPY,只在需要自动解压 tar 包时使用 ADD。COPY 语义更明确,行为更可预测。
RUN
在构建时执行命令,结果会保存为新的镜像层。
# shell 格式(通过 /bin/sh -c 执行)
RUN apt-get update && apt-get install -y curl
# exec 格式(直接执行,不经过 shell)
RUN ["node", "app.js"]
# 合并命令减少层数
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*| 要点 | 说明 |
|---|---|
| 合并 RUN | 用 && 合并多个命令,减少镜像层 |
| 清理缓存 | 安装后删除缓存文件减小体积 |
--no-install-recommends | 避免安装非必要依赖 |
CMD vs ENTRYPOINT
CMD — 容器启动时的默认命令,可被 docker run 参数覆盖。
# exec 格式(推荐)
CMD ["node", "app.js"]
# shell 格式
CMD node app.js
# 设置默认参数(配合 ENTRYPOINT)
CMD ["--port", "3000"]ENTRYPOINT — 容器的入口命令,不容易被覆盖。
# exec 格式(推荐)
ENTRYPOINT ["node", "app.js"]
# shell 格式
ENTRYPOINT node app.jsCMD 与 ENTRYPOINT 组合用法
ENTRYPOINT ["node"]
CMD ["app.js"]| docker run 命令 | 实际执行 |
|---|---|
docker run my-app | node app.js |
docker run my-app server.js | node server.js(CMD 被覆盖) |
docker run my-app --port 3000 | node --port 3000(CMD 被覆盖) |
| 对比 | CMD | ENTRYPOINT |
|---|---|---|
| 用途 | 默认参数 | 固定入口命令 |
被 docker run 参数覆盖 | ✅ 容易覆盖 | ❌ 需要 --entrypoint |
| 每个 Dockerfile 数量 | 只有最后一个生效 | 只有最后一个生效 |
| 组合使用 | 提供默认参数 | 提供主命令 |
💡 最佳实践:用 ENTRYPOINT 定义主命令,用 CMD 定义默认参数。用户可通过
docker run参数覆盖 CMD 部分。
EXPOSE
声明容器运行时监听的端口(仅文档作用,不会实际发布端口)。
EXPOSE 3000
EXPOSE 80/tcp
EXPOSE 443/tcp
EXPOSE 8080/udp💡 EXPOSE 只是声明,实际端口映射需要在
docker run -p或 docker-compose 的ports中配置。
ENV
设置环境变量,构建时和运行时都有效。
ENV NODE_ENV=production
ENV APP_HOME /app
ENV PATH=$APP_HOME/bin:$PATH
# 一次设置多个
ENV NODE_ENV=production \
APP_PORT=3000 \
APP_HOME=/appARG vs ENV 区别
| 特性 | ARG | ENV |
|---|---|---|
| 作用阶段 | 仅构建时 | 构建时 + 运行时 |
| 运行时可见 | ❌ | ✅ |
docker run -e 覆盖 | ❌ | ✅ |
docker build --build-arg 传入 | ✅ | ❌ |
| 默认值 | 可在 Dockerfile 中设置 | 可在 Dockerfile 中设置 |
# ARG 仅构建时可用
ARG NODE_VERSION=18
FROM node:${NODE_VERSION}-alpine
# ENV 构建时和运行时都可用
ENV NODE_ENV=productionVOLUME
声明匿名数据卷的挂载点。
VOLUME /app/data
VOLUME ["/app/data", "/app/logs"]💡 VOLUME 声明后,该路径的数据不会随镜像层保存。适合数据库数据、日志等需要持久化的目录。
USER
指定运行容器时的用户。
RUN groupadd -r node && useradd -r -g node node
USER node
# 或使用 UID:GID
USER 1000:1000HEALTHCHECK
定义容器健康检查。
# 每 30 秒检查一次,超时 10 秒,连续 3 次失败标记为 unhealthy
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 禁用基础镜像的健康检查
HEALTHCHECK NONE| 参数 | 说明 | 默认值 |
|---|---|---|
--interval | 检查间隔 | 30s |
--timeout | 超时时间 | 30s |
--start-period | 启动等待时间 | 30s |
--retries | 失败重试次数 | 3 |
LABEL
为镜像添加元数据。
LABEL maintainer="[email protected]"
LABEL version="1.0"
LABEL description="My application"
# 多行
LABEL org.opencontainers.image.title="My App" \
org.opencontainers.image.version="1.0" \
org.opencontainers.image.authors="[email protected]"SHELL
更改默认 shell。
# Linux 默认 ["/bin/sh", "-c"]
# Windows 默认 ["cmd", "/S", "/C"]
SHELL ["/bin/bash", "-c"]
RUN apt-get updateSTOPSIGNAL
设置停止容器时发送的系统信号。
STOPSIGNAL SIGQUIT
STOPSIGNAL 9ONBUILD
为将来的构建添加触发器,当该镜像作为其他镜像的基础镜像时执行。
ONBUILD COPY package.json ./
ONBUILD RUN yarn install.dockerignore 文件
在构建上下文中排除不需要的文件,减小构建上下文大小、加速构建、避免敏感文件泄露。
# 依赖目录
node_modules
vendor
# 构建产物
dist
build
*.log
# 版本控制
.git
.gitignore
# Docker 相关
Dockerfile*
docker-compose*.yml
.dockerignore
# IDE 和编辑器
.vscode
.idea
*.swp
*.swo
# 环境变量(敏感信息)
.env
.env.*
!.env.example
# 操作系统文件
.DS_Store
Thumbs.db
# 测试和文档
test
tests
*.md
LICENSE💡 .dockerignore 支持 glob 模式,
!前缀表示排除(即保留)。
docker init(交互式生成 Dockerfile)
docker init 可以根据项目类型自动生成 Dockerfile、.dockerignore 和 docker-compose.yml。
# 在项目目录下运行,交互式选择项目类型
docker init支持的项目模板:
| 模板 | 生成内容 |
|---|---|
| Go | Dockerfile + .dockerignore |
| Java | Dockerfile + .dockerignore |
| Node.js | Dockerfile + .dockerignore + compose.yaml |
| Python | Dockerfile + .dockerignore + compose.yaml |
| Rust | Dockerfile + .dockerignore |
| ASP.NET Core | Dockerfile + .dockerignore + compose.yaml |
| Other | 基础 Dockerfile + .dockerignore |
💡 生成的文件是起点而非终点,建议根据实际项目需求调整。例如添加多阶段构建、优化层缓存、配置健康检查等。
镜像优化最佳实践
多阶段构建
将构建环境和运行环境分离,最终镜像只包含运行所需的文件。
Vue/React 前端项目完整示例:
# ---- 构建阶段(builder)----
FROM node:18-alpine AS builder # 使用 Node.js 18 Alpine 版本作为构建环境,AS builder 命名此阶段
WORKDIR /app # 设置工作目录为 /app(不存在会自动创建)
COPY package.json yarn.lock ./ # 先复制依赖声明文件(利用 Docker 层缓存,依赖不变时跳过安装)
RUN yarn install --frozen-lockfile # 安装依赖,--frozen-lockfile 确保 lock 文件不被修改
COPY . . # 再复制源代码(源码变化不会触发重新安装依赖)
RUN yarn build # 执行构建,产物默认输出到 /app/dist
# ---- 运行阶段(最终镜像)----
FROM nginx:1.24-alpine # 使用 nginx Alpine 版本作为最终镜像(仅 ~25MB)
COPY --from=builder /app/dist /usr/share/nginx/html # 从 builder 阶段复制构建产物到 nginx 静态目录
COPY nginx.conf /etc/nginx/conf.d/default.conf # 复制自定义 nginx 配置(含 SPA history 路由)
EXPOSE 80 # 声明容器监听 80 端口
CMD ["nginx", "-g", "daemon off;"] # 启动 nginx 并保持前台运行(容器必需)Node.js API 项目完整示例:
# ---- 依赖阶段(deps):只安装生产依赖,体积最小 ----
FROM node:18-alpine AS deps # 命名为 deps 阶段
WORKDIR /app # 设置工作目录
COPY package.json yarn.lock ./ # 复制依赖声明文件
RUN yarn install --frozen-lockfile --production && \ # 只安装生产依赖(不含 devDependencies)
cp -R node_modules /prod_modules # 将生产依赖复制到单独目录,供运行阶段使用
# ---- 构建阶段(builder):安装全部依赖并编译 ----
FROM node:18-alpine AS builder # 新的构建阶段
WORKDIR /app
COPY package.json yarn.lock ./ # 复制依赖声明文件
RUN yarn install --frozen-lockfile # 安装全部依赖(含 devDependencies,编译可能需要)
COPY . . # 复制源代码
RUN yarn build # 编译 TypeScript 或打包
# ---- 运行阶段(最终镜像):只包含运行时必需文件 ----
FROM node:18-alpine # 最小的 Node.js 镜像
WORKDIR /app
COPY --from=deps /prod_modules ./node_modules # 从 deps 阶段复制生产依赖
COPY --from=builder /app/dist ./dist # 从 builder 阶段复制编译产物
COPY package.json ./ # 复制 package.json(用于 scripts 等)
EXPOSE 3000 # 声明监听端口
USER node # 使用非 root 用户运行(安全最佳实践)
CMD ["node", "dist/main.js"] # 启动应用Go 项目(最小镜像):
# ---- 构建阶段 ----
FROM golang:1.21-alpine AS builder # 使用 Go 1.21 Alpine 版本
WORKDIR /app # 设置工作目录
COPY go.mod go.sum ./ # 先复制依赖文件
RUN go mod download # 下载 Go 模块依赖(利用层缓存)
COPY . . # 复制源代码
RUN CGO_ENABLED=0 GOOS=linux go build -o main . # 静态编译,禁用 CGO,生成 Linux 可执行文件
# ---- 运行阶段 ----
FROM scratch # 空白镜像(0 字节),Go 静态编译无需任何运行时
COPY --from=builder /app/main /main # 从 builder 阶段复制编译好的二进制文件
EXPOSE 8080 # 声明监听端口
CMD ["/main"] # 运行程序减少镜像体积的方法
| 方法 | 说明 |
|---|---|
| 使用 Alpine 基础镜像 | node:18-alpine 比 node:18 小约 10 倍 |
| 多阶段构建 | 构建工具不进入最终镜像 |
| 合并 RUN 指令 | 减少镜像层数 |
| 清理包管理器缓存 | rm -rf /var/lib/apt/lists/* |
--no-install-recommends | 避免安装推荐包 |
使用 .dockerignore | 排除不需要的文件 |
| 选择合适基础镜像 | slim 比完整版小,alpine 更小 |
镜像分层原理
Docker 镜像由多个只读层(layer)叠加而成,每条 Dockerfile 指令生成一层。
| 概念 | 说明 |
|---|---|
| 只读层 | 镜像的每一层都是只读的,多个镜像可共享相同层 |
| 容器层 | 容器启动时在镜像顶部添加一个可写层(Copy-on-Write) |
| 层共享 | 基于相同基础镜像的容器共享底层,节省磁盘空间 |
| CoW 机制 | 修改文件时先从只读层复制到可写层再修改,不影响原始层 |
# 查看镜像分层
docker image inspect nginx:alpine --format '{{.RootFS.Layers}}'
# 查看每层大小和命令
docker history nginx:alpine💡 理解分层原理有助于优化 Dockerfile:将不常变化的指令放前面(利用缓存),频繁变化的放后面。
层缓存优化策略
Docker 按层缓存,某一层变化后其后所有层都会重新构建。
# ❌ 错误:任何文件变化都会重新安装依赖
COPY . .
RUN yarn install
# ✅ 正确:先复制依赖文件,利用缓存
COPY package.json yarn.lock ./
RUN yarn install
COPY . .| 策略 | 说明 |
|---|---|
| 依赖文件先复制 | package.json、yarn.lock、go.mod 等先复制并安装 |
| 频繁变化的文件后复制 | 源代码最后复制 |
| RUN 合并 | 将更新和安装合并为一条 RUN |
使用 --mount=type=cache | BuildKit 缓存包管理器目录(高级用法) |
# BuildKit 缓存示例
RUN --mount=type=cache,target=/var/cache/apt \
apt-get update && apt-get install -y curl
RUN --mount=type=cache,target=/root/.cache/yarn \
yarn install --frozen-lockfileAlpine vs Debian 基础镜像对比
| 特性 | Alpine | Debian/Ubuntu |
|---|---|---|
| 镜像大小 | ~5MB | ~50-120MB |
| 包管理器 | apk | apt-get |
| C 库 | musl | glibc |
| Shell | ash | bash |
| 适用场景 | 生产环境、追求最小体积 | 需要 glibc 兼容性 |
| 潜在问题 | musl 可能导致部分软件不兼容 | 体积较大 |
💡 Node.js 项目推荐
node:18-alpine,Python 项目推荐python:3.11-slim,Go 项目可用scratch或alpine。
构建镜像
# 构建
docker build -t my-app .
docker build -t my-app:1.0 .
# 指定 Dockerfile
docker build -f Dockerfile.prod -t my-app .
# 无缓存构建
docker build --no-cache -t my-app .
# 指定构建上下文为远程仓库
docker build -t my-app https://github.com/user/repo.git#main
# 构建时传入参数
docker build --build-arg NODE_VERSION=18 -t my-app .
# 查看构建过程(BuildKit)
DOCKER_BUILDKIT=1 docker build -t my-app .
# 构建多平台镜像
docker buildx build --platform linux/amd64,linux/arm64 -t my-app .docker buildx(多平台构建)
buildx 是 Docker 的扩展构建工具,支持多平台镜像构建、构建缓存导入导出等高级功能。
# 查看可用的构建器
docker buildx ls
# 创建专用构建器(支持多平台)
docker buildx create --name mybuilder --driver docker-container --use
docker buildx inspect --bootstrap
# 构建并推送多平台镜像
docker buildx build --platform linux/amd64,linux/arm64 \
-t registry.example.com/my-app:latest \
--push .
# 构建多平台镜像并加载到本地
docker buildx build --platform linux/amd64,linux/arm64 \
-t my-app:latest \
--load .
# 使用缓存加速构建
docker buildx build --platform linux/amd64,linux/arm64 \
-t my-app:latest \
--cache-from type=registry,ref=registry.example.com/my-app:cache \
--cache-to type=registry,ref=registry.example.com/my-app:cache,mode=max \
--push .| 参数 | 说明 |
|---|---|
--platform | 目标平台,多个用逗号分隔 |
--push | 构建后自动推送到仓库 |
--load | 构建后加载到本地(仅支持单平台,多平台需用 --push 或先指定单平台) |
--cache-from | 缓存来源 |
--cache-to | 缓存输出目标 |
--builder | 指定使用的构建器 |
💡 多平台构建常用于同时支持
amd64(x86 服务器)和arm64(Apple Silicon、ARM 服务器)。--load仅支持单平台,多平台需用--push。
docker context(多环境管理)
docker context 用于管理多个 Docker 环境(本地、远程服务器、Kubernetes 等),快速切换连接目标。
# 查看所有上下文
docker context ls
# 创建新上下文(连接远程 Docker)
docker context create remote-server \
--docker "host=ssh://[email protected]" \
--description "Production server"
# 切换上下文
docker context use remote-server
# 在指定上下文执行命令(不切换默认)
docker --context remote-server ps
# 查看当前上下文详情
docker context inspect
# 删除上下文
docker context rm remote-server| 字段 | 说明 |
|---|---|
| NAME | 上下文名称 |
| DESCRIPTION | 描述信息 |
| DOCKER ENDPOINT | Docker 引擎连接地址 |
| ERROR | 连接错误信息(如有) |
💡 Docker Desktop 创建的
desktop-linux/desktop-windows上下文是本地引擎。通过 context 可免去反复设置DOCKER_HOST环境变量。
Docker Compose
docker-compose.yml 示例
version: '3.8' # Compose 文件格式版本(新版 Docker 可省略此行)
services:
app: # 应用服务
build: . # 从当前目录的 Dockerfile 构建镜像
ports:
- "3000:3000" # 端口映射:宿主机 3000 → 容器 3000
environment:
- NODE_ENV=production # 环境变量:运行模式
- DB_HOST=db # 环境变量:数据库主机名(用服务名即可,Compose 自动解析)
depends_on:
- db # 依赖 db 服务,db 启动后才启动 app
restart: always # 重启策略:容器退出后总是重启
db: # 数据库服务
image: postgres:15 # 使用 postgres 15 官方镜像
environment:
- POSTGRES_PASSWORD=secret # 数据库密码
- POSTGRES_DB=myapp # 自动创建的数据库名
volumes:
- postgres_data:/var/lib/postgresql/data # 命名数据卷:持久化数据库文件
ports:
- "5432:5432" # 端口映射:宿主机 5432 → 容器 5432
volumes:
postgres_data: # 声明命名数据卷(删除容器后数据不丢失)完整配置字段说明
顶层字段
| 字段 | 说明 |
|---|---|
version | Compose 文件格式版本('3.8' 等,新版可省略) |
services | 定义各个服务 |
volumes | 定义命名数据卷 |
networks | 定义自定义网络 |
configs | 定义配置对象 |
secrets | 定义敏感数据 |
services 下的常用字段
image / build
services:
# 直接使用镜像
web:
image: nginx:1.24-alpine
# 从 Dockerfile 构建
app:
build:
context: .
dockerfile: Dockerfile.prod
args:
NODE_VERSION: 18
target: builder # 多阶段构建指定目标阶段ports / expose
services:
app:
ports:
- "3000:3000" # 宿主机:容器
- "8080:80"
- "127.0.0.1:3000:3000" # 仅本地访问
- "9090-9091:8080-8081" # 端口范围
expose:
- "3000" # 仅对其他服务暴露,不映射到宿主机environment / env_file
services:
# 列表格式
app:
environment:
- NODE_ENV=production
- DB_HOST=db
# 映射格式
api:
environment:
NODE_ENV: production
DB_HOST: db
# 从文件读取
web:
env_file:
- .env
- .env.production
# 指定文件路径和必填项(Compose v2.24+)
worker:
env_file:
- path: .env
required: true
- path: .env.local
required: falsevolumes
services:
app:
volumes:
- ./src:/app/src # 绑定挂载
- node_modules:/app/node_modules # 命名数据卷
- /app/logs # 匿名数据卷
- type: bind
source: ./config
target: /app/config
read_only: truedepends_on
services:
app:
depends_on:
db:
condition: service_healthy # 等待健康检查通过
redis:
condition: service_started # 等待启动完成| 条件 | 说明 |
|---|---|
service_started | 服务已启动(默认) |
service_healthy | 服务健康检查通过 |
service_completed_successfully | 服务成功退出 |
healthcheck
services:
# 健康检查配置
app:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# 禁用健康检查
worker:
healthcheck:
disable: truerestart
services:
app:
restart: always # 总是重启
api:
restart: on-failure # 仅失败时重启
web:
restart: unless-stopped # 除非手动停止
db:
restart: "no" # 不自动重启(默认)networks
services:
app:
networks:
- frontend
backend:
aliases:
- app.local # 网络别名
ipv4_address: 172.20.0.10 # 固定 IPlabels
services:
app:
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"logging
services:
# JSON 文件日志(默认)
app:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# syslog 日志
api:
logging:
driver: syslog
options:
syslog-address: "tcp://192.168.0.1:514"| 驱动 | 说明 |
|---|---|
json-file | 默认,JSON 格式日志文件 |
syslog | 发送到 syslog |
journald | 发送到 journald |
none | 禁用日志 |
deploy
services:
app:
deploy:
replicas: 3
resources:
limits:
cpus: "0.50"
memory: 512M
reservations:
cpus: "0.25"
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s多环境配置
方式一:docker-compose.override.yml
默认加载 docker-compose.yml + docker-compose.override.yml,适合开发环境覆盖配置。
# docker-compose.yml(基础配置)
services:
app:
build: .
environment:
- NODE_ENV=production# docker-compose.override.yml(开发环境覆盖,自动加载)
services:
app:
volumes:
- ./src:/app/src # 开发时挂载源码
environment:
- NODE_ENV=development
ports:
- "3000:3000"# docker-compose.prod.yml(生产环境配置)
services:
app:
restart: always
deploy:
resources:
limits:
memory: 512M方式二:-f 指定多个文件
# 开发环境(默认加载 override)
docker compose up -d
# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# 测试环境
docker compose -f docker-compose.yml -f docker-compose.test.yml up -d方式三:.env 文件自动变量替换
docker-compose.yml 中可直接引用 .env 文件中的变量,Compose 启动时自动读取。
# .env 文件(放在 docker-compose.yml 同级目录)
POSTGRES_PASSWORD=secret123
APP_PORT=3000
DB_NAME=myapp# docker-compose.yml 中使用 ${VAR} 引用
services:
app:
ports:
- "${APP_PORT}:3000"
environment:
- DB_NAME=${DB_NAME}
db:
image: postgres:15
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${DB_NAME}| 特性 | .env 文件 | env_file 字段 |
|---|---|---|
| 变量替换 | 替换 compose 文件中的 ${VAR} | 注入到容器环境变量 |
| 读取方式 | Compose 自动读取同级 .env | 需显式声明 env_file: .env |
| 优先级 | 较低(可被 shell 环境变量覆盖) | 较高 |
| 文件名 | 固定为 .env | 可指定任意文件名 |
💡
.env文件用于 compose 文件中的变量替换,env_file字段用于注入容器环境变量,两者用途不同。.env文件不要提交到版本库,应加入.gitignore。
Profiles
按需启动服务组,避免维护多个 Compose 文件。
services:
app:
image: node:18-alpine
# 无 profiles,始终启动
db:
image: postgres:15
profiles:
- dev
- prod
debug-tools:
image: busybox
profiles:
- dev # 仅开发环境
monitoring:
image: prometheus
profiles:
- prod # 仅生产环境# 启动默认服务(无 profiles 的服务)
docker compose up -d
# 启动 dev 相关服务
docker compose --profile dev up -d
# 启动多个 profiles
docker compose --profile dev --profile monitoring up -d
# 启动所有服务
docker compose --profile dev --profile prod up -d常用命令
# 启动所有服务
docker compose up -d
# 停止所有服务
docker compose down
# 停止并删除数据卷
docker compose down -v
# 查看日志
docker compose logs -f
docker compose logs -f app # 指定服务
# 重新构建并启动
docker compose up -d --build
# 查看服务状态
docker compose ps
# 进入某个服务
docker compose exec app sh
# 查看服务配置(解析后)
docker compose config
# 扩展服务副本数
docker compose up -d --scale app=3
# 只启动指定服务
docker compose up -d app db
# 查看服务端口映射
docker compose port app 3000
# 暂停/恢复服务
docker compose pause
docker compose unpause数据卷
# 创建数据卷
docker volume create my-vol
# 查看数据卷
docker volume ls
# 使用数据卷
docker run -d -v my-vol:/app/data nginx
# 删除数据卷
docker volume rm my-vol
# 清理无用数据卷
docker volume prune网络
基本操作
# 查看网络
docker network ls
# 创建网络
docker network create my-net
# 使用网络
docker run -d --network my-net --name app1 node-app
docker run -d --network my-net --name app2 node-app
# 容器间可通过名称访问
# app1 中可以直接访问 app2:port网络模式
| 模式 | 说明 | 适用场景 |
|---|---|---|
bridge | 默认模式,容器通过虚拟网桥通信 | 单机多容器通信 |
host | 容器直接使用宿主机网络栈 | 高性能网络、端口冲突不敏感 |
none | 无网络连接 | 安全隔离场景 |
overlay | 跨主机容器通信 | Docker Swarm 集群 |
macvlan | 容器拥有独立 MAC 地址 | 需要容器在物理网络中可见 |
bridge(默认)
# 使用默认 bridge 网络
docker run -d --name app nginx
# 使用自定义 bridge 网络(推荐)
docker network create my-net
docker run -d --network my-net --name app1 nginx
docker run -d --network my-net --name app2 nginx💡 自定义 bridge 网络支持自动 DNS 解析(容器间可用名称访问),默认 bridge 网络不支持。
host
# 容器直接使用宿主机网络,无需 -p 端口映射
docker run -d --network host nginx⚠️ host 模式下容器与宿主机共享网络命名空间,端口会冲突。仅支持 Linux。
none
# 完全禁用网络
docker run -d --network none alpineoverlay
# 需要先初始化 Swarm
docker swarm init
# 创建 overlay 网络
docker network create -d overlay my-overlay
# 在 Swarm 服务中使用
docker service create --network my-overlay --name web nginxmacvlan
# 创建 macvlan 网络
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
my-macvlan
docker run -d --network my-macvlan --ip 192.168.1.100 --name app nginx自定义网络
# 创建自定义 bridge 网络
docker network create \
--driver bridge \
--subnet 172.20.0.0/16 \
--ip-range 172.20.240.0/20 \
--gateway 172.20.0.1 \
my-net
# 查看网络详情
docker network inspect my-net
# 将容器连接到网络
docker network connect my-net my-container
# 将容器连接到网络并指定 IP
docker network connect --ip 172.20.0.100 my-net my-container
# 断开网络
docker network disconnect my-net my-container
# 删除网络
docker network rm my-net
# 清理无用网络
docker network prune容器间通信
# 创建自定义网络
docker network create app-net
# 启动数据库
docker run -d --network app-net --name db postgres:15
# 启动应用,通过容器名访问数据库
docker run -d --network app-net \
-e DB_HOST=db \
-e DB_PORT=5432 \
--name app \
my-node-app💡 在同一自定义网络中,容器间可直接使用容器名作为主机名进行通信,无需知道对方 IP。
Docker 安全最佳实践
非 root 用户运行
FROM node:18-alpine
# 创建非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup . .
# 切换到非 root 用户
USER appuser
CMD ["node", "app.js"]# 运行时指定用户
docker run --user 1000:1000 my-app
# 只读文件系统
docker run --read-only --tmpfs /tmp my-app只读文件系统
# 只读根文件系统 + 临时可写目录
docker run --read-only \
--tmpfs /tmp:rw,noexec,nosuid \
--tmpfs /app/cache:rw,noexec,nosuid \
my-app# Docker Compose 中配置
services:
app:
read_only: true
tmpfs:
- /tmp
- /app/cache资源限制
# 限制内存
docker run -m 512m my-app
docker run --memory=1g --memory-swap=2g my-app
# 限制 CPU
docker run --cpus=1.5 my-app
docker run --cpu-shares=512 my-app # 相对权重
# 限制内存 + CPU
docker run -m 512m --cpus=1.0 my-app| 参数 | 说明 |
|---|---|
-m / --memory | 最大内存 |
--memory-swap | 内存 + 交换空间总量 |
--cpus | 可用 CPU 核心数 |
--cpu-shares | CPU 相对权重(默认 1024) |
--pids-limit | 限制进程数(防止 fork 炸弹) |
# Docker Compose 中配置资源限制
services:
app:
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
pids: 100
reservations:
cpus: "0.25"
memory: 256M镜像安全扫描
# 使用 Docker Scout 扫描(Docker Desktop 内置)
docker scout cves my-app:latest
# 使用 Trivy 扫描
trivy image my-app:latest
# 扫描并仅显示高危漏洞
trivy image --severity HIGH,CRITICAL my-app:latest
# 扫描 Dockerfile
trivy config Dockerfile其他安全建议
| 实践 | 说明 |
|---|---|
| 使用官方镜像 | 优先选择 Docker Hub 官方镜像 |
| 固定镜像版本 | 避免使用 latest,使用具体版本号 |
| 不存储密钥在镜像中 | 使用环境变量或 Docker Secrets |
| 定期更新基础镜像 | 修复已知安全漏洞 |
| 最小化安装 | 只安装必要的包和工具 |
使用 --no-new-privileges | 防止容器内提权 |
禁用 --privileged | 避免授予容器所有权限 |
# 禁止提权
docker run --security-opt=no-new-privileges my-app
# 使用 seccomp 限制系统调用
docker run --security-opt seccomp=custom-profile.json my-app前端项目 Docker 化完整示例
Vue/React 项目多阶段构建
Dockerfile:
# ---- 构建阶段 ----
FROM node:18-alpine AS builder # 使用 Node.js 18 Alpine 作为构建环境
WORKDIR /app # 设置工作目录
COPY package.json yarn.lock ./ # 先复制依赖文件(利用层缓存)
RUN yarn install --frozen-lockfile # 安装依赖,frozen-lockfile 确保版本一致
COPY . . # 复制源代码
RUN yarn build # 执行构建
# ---- 运行阶段 ----
FROM nginx:1.24-alpine # 使用 nginx Alpine 版本作为运行环境
# 自定义 nginx 配置(包含 Vue SPA history 路由)
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 复制构建产物到 nginx 静态目录
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80 # 声明 80 端口
CMD ["nginx", "-g", "daemon off;"] # 前台启动 nginxnginx.conf(Vue Router history 模式):
server {
listen 80; # 监听 80 端口
server_name localhost; # 服务器域名
root /usr/share/nginx/html; # 静态文件根目录(对应构建产物)
index index.html; # 默认首页文件
# Vue/React SPA history 路由:所有未匹配的请求回退到 index.html
# 这样前端路由(如 /about、/user/123)才能正常工作
location / {
try_files $uri $uri/ /index.html; # 先找文件 → 再找目录 → 都没有就返回 index.html
}
# API 反向代理:将 /api/ 开头的请求转发给后端
location /api/ {
proxy_pass http://backend:3000/; # 转发到后端服务(去掉 /api 前缀)
proxy_set_header Host $host; # 传递原始 Host 头
proxy_set_header X-Real-IP $remote_addr; # 传递客户端真实 IP
}
# 静态资源长期缓存:文件名带 hash,内容变化时文件名也变
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y; # 缓存 1 年
add_header Cache-Control "public, immutable"; # 公共缓存,不可变
}
}Node.js API 项目
Dockerfile:
# ---- 构建阶段 ----
FROM node:18-alpine AS builder # 使用 Node.js 18 Alpine 作为构建环境
WORKDIR /app # 设置工作目录
COPY package.json yarn.lock ./ # 先复制依赖声明文件(利用层缓存)
RUN yarn install --frozen-lockfile # 安装全部依赖(含 devDependencies)
COPY . . # 复制源代码
RUN yarn build # 编译 TypeScript 或打包
# ---- 运行阶段 ----
FROM node:18-alpine # 使用 Node.js 18 Alpine 作为运行环境
WORKDIR /app # 设置工作目录
# 创建非 root 用户(安全最佳实践,避免容器以 root 身份运行)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# 从构建阶段复制文件,并设置文件所有者为 appuser
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
ENV NODE_ENV=production # 设置生产环境变量
EXPOSE 3000 # 声明监听 3000 端口
USER appuser # 切换到非 root 用户运行
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# 健康检查:每 30 秒检测一次 /health 端口,失败 3 次标记为不健康
CMD ["node", "dist/main.js"] # 启动应用docker-compose.yml 编排前后端 + 数据库
version: '3.8' # Compose 文件格式版本
services:
frontend: # 前端服务(Vue/React)
build:
context: ./frontend # 构建上下文为 frontend 目录
dockerfile: Dockerfile # 使用该目录下的 Dockerfile
ports:
- "80:80" # 对外暴露 80 端口(HTTP)
depends_on:
- backend # 依赖后端服务
restart: unless-stopped # 除非手动停止,否则总是重启
backend: # 后端服务(Node.js API)
build:
context: ./backend # 构建上下文为 backend 目录
dockerfile: Dockerfile
ports:
- "3000:3000" # 对外暴露 3000 端口
environment:
- NODE_ENV=production # 生产环境
- DB_HOST=postgres # 数据库地址(用服务名)
- DB_PORT=5432 # 数据库端口
- DB_NAME=myapp # 数据库名
- DB_USER=admin # 数据库用户
- DB_PASSWORD_FILE=/run/secrets/db_password # 从 Docker Secret 读取密码(更安全)
depends_on:
postgres:
condition: service_healthy # 等待数据库健康检查通过后再启动
redis:
condition: service_started # 等待 Redis 启动完成
secrets:
- db_password # 挂载 Docker Secret
volumes:
- uploads:/app/uploads # 命名数据卷:持久化上传文件
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M # 最大内存限制
cpus: "1.0" # 最大 CPU 核心数
postgres: # PostgreSQL 数据库
image: postgres:15-alpine # 使用 PostgreSQL 15 Alpine 版本
environment:
- POSTGRES_DB=myapp # 自动创建的数据库
- POSTGRES_USER=admin # 数据库超级用户
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password # 从 Secret 读取密码
secrets:
- db_password # 挂载 Docker Secret
volumes:
- postgres_data:/var/lib/postgresql/data # 持久化数据库文件
ports:
- "5432:5432" # 端口映射
healthcheck: # 健康检查:确保数据库就绪后再启动后端
test: ["CMD-SHELL", "pg_isready -U admin -d myapp"] # 检查数据库是否可连接
interval: 10s # 每 10 秒检查一次
timeout: 5s # 超时时间
retries: 5 # 重试次数
restart: unless-stopped
redis: # Redis 缓存
image: redis:7-alpine # 使用 Redis 7 Alpine 版本
volumes:
- redis_data:/data # 持久化 Redis 数据
restart: unless-stopped
volumes: # 声明命名数据卷
postgres_data: # PostgreSQL 数据
redis_data: # Redis 数据
uploads: # 用户上传文件
secrets: # 声明 Docker Secret(比环境变量更安全)
db_password:
file: ./secrets/db_password.txt # 从本地文件读取密码(不要提交到 Git)常用技巧
查看磁盘占用
docker system df
docker system df -v # 详细信息实时资源监控
# 查看所有容器的资源使用
docker stats
# 单次输出(不实时刷新)
docker stats --no-stream
# 指定容器
docker stats my-app my-db| 列 | 说明 |
|---|---|
| CPU % | CPU 使用率 |
| MEM USAGE / LIMIT | 内存使用 / 限制 |
| MEM % | 内存使用率 |
| NET I/O | 网络输入/输出 |
| BLOCK I/O | 磁盘输入/输出 |
| PIDS | 进程数 |
查看详细信息
# 查看容器详细信息
docker inspect my-app
# 获取特定字段(Go 模板)
docker inspect --format='{{.NetworkSettings.IPAddress}}' my-app
docker inspect --format='{{.State.Status}}' my-app
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-app
# 查看镜像详细信息
docker inspect nginx:alpine导出导入镜像
# 导出镜像为 tar 文件
docker save -o nginx.tar nginx:alpine
docker save nginx:alpine | gzip > nginx.tar.gz
# 导入镜像
docker load -i nginx.tar
docker load -i nginx.tar.gz # 支持 gzip
# 导出/导入容器(含运行状态)
docker export my-container > container.tar
docker import container.tar my-image:latest| 命令 | 用途 | 保留信息 |
|---|---|---|
docker save/load | 镜像导出/导入 | 镜像层、标签、元数据 |
docker export/import | 容器导出/导入 | 仅文件系统,丢失元数据 |
容器内文件拷贝
# 从容器复制到宿主机
docker cp my-app:/app/logs/app.log ./
# 从宿主机复制到容器
docker cp ./config.yml my-app:/app/config.yml
# 复制目录
docker cp my-app:/app/data ./backup/
# 从已停止的容器复制
docker cp stopped-container:/app/data ./backup/其他实用命令
# 查看容器内进程
docker top my-app
# 查看容器资源使用
docker stats my-app --no-stream
# 查看镜像构建历史(每层的命令)
docker history my-app
# 重命名容器
docker rename old-name new-name
# 查看 Docker 版本详情
docker version
docker info
# 清理所有未使用资源
docker system prune -a --volumes⚠️
docker system prune -a会删除所有未使用的镜像、停止的容器和未挂载的网络。未命名卷(anonymous volumes)默认不会被删除,需加--volumes参数。已命名卷(named volumes)永远不会被自动删除。
查看容器文件变更(docker diff)
# 查看容器内文件系统相对于镜像的变更
docker diff my-app| 标记 | 含义 |
|---|---|
A | 新增的文件或目录(Added) |
C | 修改的文件或目录(Changed) |
D | 删除的文件或目录(Deleted) |
# 常见用法:检查容器是否被意外修改
docker diff my-app
# 输出示例:
# C /var/run
# A /app/data/cache.tmp
# C /etc/nginx/nginx.conf💡
docker diff只显示容器可写层的变更,不显示镜像只读层的内容。适合排查容器运行时产生的文件变化。
从容器创建镜像(docker commit)
# 从运行中的容器创建镜像
docker commit my-app my-app-snapshot
# 带标签
docker commit my-app my-app:debug-v1
# 添加提交信息和元数据
docker commit -a "author" -m "added debug tools" my-app my-app:debug
# 修改启动命令
docker commit --change='CMD ["node", "debug.js"]' my-app my-app:debug⚠️
docker commit不推荐用于生产环境,因为它无法追溯构建过程、不利于版本管理。应优先使用 Dockerfile 构建镜像。典型用途是临时保存调试状态。
容器无法启动
# 查看容器日志
docker logs my-app
# 查看容器详情
docker inspect my-app镜像过大
# 使用多阶段构建
# 使用 alpine 基础镜像
# 合并 RUN 指令减少层数
# 使用 .dockerignore 排除不必要文件清理磁盘空间
# 清理所有未使用的资源(镜像、容器、网络、数据卷)
docker system prune -a
# 只清理镜像
docker image prune -a
# 查看磁盘使用
docker system df