技术备忘 Docker容器

前言

Docker 是一个容器化平台1Docker 由 Solomon Hykes 于 2013 年创建,现已成为容器技术的事实标准。,让开发者可以把应用及其依赖打包到一个轻量级、可移植的容器中,然后运行在任何 Linux 机器上。相比传统虚拟机,Docker 容器共享主机内核2容器共享宿主机内核,因此比虚拟机更轻量。这也是为什么 Linux 容器无法直接在 Windows 上运行的原因。,启动快、资源占用小。

Docker 简化了”在我机器上能跑”的问题——把整个环境做成镜像,哪里都能跑。

核心概念

镜像 vs 容器 vs 仓库

概念类比说明
镜像(Image)类 / 安装包一个只读的模板,包含运行应用所需的文件系统和配置
容器(Container)实例 / 运行中的程序镜像的运行实例,可读可写,可启动、停止、删除
仓库(Registry)GitHub / 应用商店存储和分发镜像的地方(Docker Hub、私有仓库)
bash
# 从镜像启动容器
docker run nginx:latest
# 从 Dockerfile 构建镜像
docker build -t my-app:1.0 .
# 推送镜像到仓库
docker push my-app:1.0

Docker 工作流程

编写 Dockerfile

docker build

生成镜像 Image

docker run

运行容器 Container

docker push

推送到仓库 Registry

关键路径

  • 开发阶段:Dockerfile → 构建镜像 → 本地测试容器
  • 部署阶段:推送镜像到仓库 → 服务器拉取 → 启动容器
NOTE

镜像是只读的,容器启动时会在镜像层之上加一个可写层(Container Layer)。

WARNING

所有修改都在容器层,删除容器后修改丢失。要持久化数据,必须使用 volume(数据卷)。

镜像分层

Docker 镜像由多个只读层(Layer)堆叠而成,每层对应 Dockerfile 中的一条指令。

镜像层 只读

容器运行时

容器层 Container Layer

可读写,删除即丢失

Layer 4: RUN npm install

📦 node_modules/

Layer 3: COPY package.json

📄 package.json

Layer 2: WORKDIR /app

📁 设置工作目录

Layer 1: FROM node:20

🐳 基础镜像

分层的好处:层可以被缓存和复用1Docker 由 Solomon Hykes 于 2013 年创建,现已成为容器技术的事实标准。。修改 Dockerfile 时,只有修改行之后的层需要重新构建,未修改的层直接用缓存,大幅加速构建。

安装与基础配置

bash
# macOS(Homebrew)
brew install --cask docker
# 验证
docker --version
docker compose version
# 国内镜像加速(Docker Desktop → Settings → Docker Engine)
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
]
}

常用命令

镜像管理

命令说明
docker images列出本地镜像
docker pull nginx:alpine拉取镜像
docker rmi nginx:alpine删除镜像
docker build -t my-app:1.0 .从 Dockerfile 构建镜像
docker tag my-app:1.0 user/my-app:latest给镜像打标签
docker push user/my-app:latest推送到仓库
docker save -o backup.tar my-app:1.0导出镜像到文件
docker load -i backup.tar从文件导入镜像

容器生命周期

命令说明
docker run nginx启动新容器(前台运行)
docker run -d nginx后台运行(detach)
docker run --name my-nginx nginx指定容器名
docker run -p 8080:80 nginx端口映射 主机:容器
docker ps列出运行中的容器
docker ps -a列出所有容器(含已停止的)
docker stop <container>停止容器(发送 SIGTERM)
docker kill <container>强制停止(发送 SIGKILL)
docker start <container>启动已停止的容器
docker restart <container>重启容器
docker rm <container>删除容器
docker rm -f <container>强制删除运行中的容器
docker container prune清理所有已停止的容器

进入容器与执行命令

bash
# 在运行中的容器内执行命令
docker exec <container> ls -la
# 进入交互式 Shell
docker exec -it <container> /bin/sh # 多数 Alpine 镜像
docker exec -it <container> /bin/bash # Ubuntu/Debian 类
# 查看容器日志
docker logs <container>
docker logs -f <container> # 持续跟踪
docker logs --tail 100 <container> # 只看最后 100 行

Dockerfile

Dockerfile 是一个文本文件,定义了如何构建镜像。

Dockerfile 常用指令

指令作用示例
FROM指定基础镜像FROM node:20-alpine
WORKDIR设置工作目录WORKDIR /app
COPY复制文件到镜像COPY package.json ./
ADD复制文件(支持 URL、自动解压 tar)ADD archive.tar.gz /tmp/
RUN构建时执行命令RUN npm install
CMD容器启动时的默认命令CMD ["node", "app.js"]
ENTRYPOINT容器启动入口(不可被覆盖)ENTRYPOINT ["nginx", "-g"]
ENV设置环境变量ENV NODE_ENV=production
EXPOSE声明容器监听的端口EXPOSE 3000
ARG构建参数(仅在构建时可用)ARG VERSION=1.0
VOLUME声明挂载点VOLUME /data
USER指定运行用户USER node
LABEL添加元数据LABEL version="1.0"

CMD vs ENTRYPOINT

CMDENTRYPOINT
是否可被覆盖docker run 传参可覆盖不可覆盖,除非 --entrypoint
用途指定默认命令和参数容器”主程序”入口
组合使用CMD 作为 ENTRYPOINT 的默认参数固定入口程序
dockerfile
# CMD 示例:默认执行 node app.js,可覆盖
CMD ["node", "app.js"]
# docker run my-image node server.js → 覆盖
# ENTRYPOINT 示例:固定为 ping
ENTRYPOINT ["ping"]
CMD ["localhost"]
# docker run my-image → ping localhost
# docker run my-image google.com → ping google.com

多阶段构建

多阶段构建可以极大减小最终镜像体积:一个阶段用来编译/构建,另一个阶段只复制最终产物。

dockerfile
# 第一阶段:构建
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o server
# 第二阶段:运行(极小的 scratch 镜像)
FROM scratch
WORKDIR /
COPY --from=builder /app/server /server
EXPOSE 8080
CMD ["/server"]
对比单阶段多阶段
镜像大小~800MB(含 Go 工具链)~10MB(只有二进制文件)
安全性包含不需要的工具攻击面更小
构建速度几乎一致一致

镜像体积实测对比

{"title":{"text":"单阶段 vs 多阶段构建镜像体积对比","left":"center"},"tooltip":{"trigger":"axis","axisPointer":{"type":"shadow"}},"xAxis":{"type":"category","data":["Go API","Node.js App","React 前端"]},"yAxis":{"type":"value","name":"镜像大小 (MB)"},"series":[{"name":"单阶段构建","type":"bar","data":[842,1200,380],"itemStyle":{"color":"#ff6b6b"}},{"name":"多阶段构建","type":"bar","data":[12,180,45],"itemStyle":{"color":"#51cf66"}}]}
项目类型单阶段多阶段缩减比例
Go API842 MB12 MB98.6%
Node.js App1.2 GB180 MB85%
React 前端380 MB45 MB88%
TIP

部署提速:12MB 镜像通过 100Mbps 网络传输只需 1 秒,而 842MB 需要 67 秒。容器启动时间从 8 秒降到 0.3 秒。

Node.js 项目 Dockerfile 示例

dockerfile
# ===== 生产环境 =====
FROM node:20-alpine AS production
# 安全:使用非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
# 先复制依赖文件(利用 Docker 缓存层)
COPY package.json pnpm-lock.yaml ./
RUN npm config set registry https://registry.npmmirror.com && \
npm install --production
# 再复制源码
COPY . .
USER appuser
EXPOSE 3000
CMD ["node", "app.js"]

Docker Compose

Docker Compose 用 YAML 文件定义和管理多容器应用。

基本结构

yaml
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
- db
environment:
- DB_HOST=db
- DB_PORT=5432
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: secret
volumes:
pgdata:

Compose 文件常用字段

字段说明
services定义各个容器服务
build指定 Dockerfile 路径
image使用现有镜像
ports端口映射 "主机:容器"
volumes数据卷挂载
environment环境变量
depends_on启动依赖顺序
restart重启策略:no / always / unless-stopped / on-failure
networks网络配置
command覆盖容器默认命令
healthcheck健康检查

Compose 常用命令

bash
# 启动所有服务(-d 后台运行)
docker compose up -d
# 重新构建并启动
docker compose up -d --build
# 查看日志
docker compose logs -f
# 查看运行状态
docker compose ps
# 执行命令
docker compose exec web node app.js
# 停止服务
docker compose down
# 停止并删除数据卷(注意:数据会丢失)
docker compose down -v
# 重新创建容器
docker compose up -d --force-recreate

不同环境的 Compose 配置

:::tabs == 开发环境

yaml
# docker-compose.dev.yml
version: '3.8'
services:
web:
build:
context: .
target: development
volumes:
- .:/app # 源码热重载
- /app/node_modules # 屏蔽宿主机 node_modules
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DEBUG=app:* # 开启调试日志
redis:
image: redis:7-alpine
ports:
- "6379:6379" # 暴露端口,方便 Redis 客户端连接
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev
POSTGRES_DB: myapp_dev
ports:
- "5432:5432" # 暴露端口,方便 GUI 工具连接
volumes:
pgdata:

启动docker compose -f docker-compose.dev.yml up -d

== 生产环境

yaml
# docker-compose.prod.yml
version: '3.8'
services:
web:
image: myapp:latest
restart: unless-stopped
environment:
- NODE_ENV=production
- DB_HOST=db
- REDIS_HOST=redis
# 不暴露端口,通过 Nginx 反向代理
networks:
- backend
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis_data:/data
# 不暴露端口,仅内部通信
networks:
- backend
db:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_USER: ${DB_USER} # 从 .env 读取
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
# 不暴露端口
networks:
- backend
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- web
networks:
- backend
volumes:
pgdata:
redis_data:
networks:
backend:

启动docker compose -f docker-compose.prod.yml up -d

== 测试环境

yaml
# docker-compose.test.yml
version: '3.8'
services:
web:
build:
context: .
target: test
environment:
- NODE_ENV=test
- CI=true
depends_on:
- db
- redis
command: npm run test:e2e
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: test_db
tmpfs:
- /var/lib/postgresql/data # 临时文件系统,测试结束即销毁
redis:
image: redis:7-alpine

用途:CI/CD 流水线中的自动化测试

:::

NOTE

关键差异

  • 开发环境:暴露所有端口、挂载源码、详细日志
  • 生产环境:隐藏内部端口、环境变量保密、健康检查
  • 测试环境:使用临时存储、独立数据库、自动销毁

数据持久化(Volume)

容器删除后,容器内产生的数据也会消失。Volume 是 Docker 推荐的数据持久化方式。

三种挂载方式

方式位置由谁管理用途
Volume/var/lib/docker/volumes/Docker 管理推荐:数据库、持久化数据
Bind Mount宿主机任意路径用户指定开发时源码热重载
tmpfs mount内存宿主机敏感信息,不需要持久化
bash
# Volume
docker volume create mydata
docker run -v mydata:/data nginx
# Bind Mount(开发用)
docker run -v $(pwd):/app nginx
# tmpfs
docker run --tmpfs /tmp nginx

docker-compose 中的 volume 配置

yaml
services:
db:
image: postgres:16-alpine
volumes:
# 命名 volume(推荐)
- pgdata:/var/lib/postgresql/data
# bind mount(开发用)
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
pgdata: # 声明命名 volume
NOTE

不要在生产中使用 Bind Mount。Bind Mount 依赖宿主机的目录结构,与 Docker 的隔离理念相悖。生产环境始终使用命名 Volume 或临时存储。

网络配置

Docker 网络模式

网络模式说明适用场景
bridge(默认)容器通过虚拟网桥互通,与宿主机隔离单机多容器通信
host容器直接使用宿主机网络栈(无隔离)性能敏感场景(如 Nginx)
none容器无网络完全隔离的离线任务
overlay跨宿主机容器通信(Swarm 模式)集群部署

网络模式性能与隔离性对比

{"title":{"text":"Docker 网络模式特性雷达图","left":"center"},"radar":{"indicator":[{"name":"网络隔离","max":10},{"name":"性能","max":10},{"name":"易用性","max":10},{"name":"跨主机","max":10},{"name":"安全性","max":10}]},"series":[{"type":"radar","data":[{"value":[8,7,9,0,7],"name":"bridge","areaStyle":{"color":"rgba(81, 207, 102, 0.3)"}},{"value":[0,10,8,0,3],"name":"host","areaStyle":{"color":"rgba(255, 107, 107, 0.3)"}},{"value":[9,5,6,10,8],"name":"overlay","areaStyle":{"color":"rgba(74, 144, 226, 0.3)"}}]}]}

选择建议

  • 🏠 单机开发/测试bridge(默认,最常用)
  • 性能敏感应用(Nginx 反向代理)→ host
  • 🌐 跨服务器集群overlay(Docker Swarm/Kubernetes)
bash
# 创建自定义 bridge 网络
docker network create mynetwork
# 在指定网络中运行容器
docker run --network mynetwork --name web nginx
# 同一网络中的容器可以通过服务名互相访问
docker run --network mynetwork --name app alpine
# app 容器里可以直接 ping web

Compose 中的网络

默认情况下,docker compose up 会自动创建一个网络,所有服务加入其中,通过服务名互相访问。

yaml
services:
web:
build: .
networks:
- frontend
- backend
api:
image: my-api
networks:
- backend
db:
image: postgres
networks:
- backend
# 不加入 frontend,更安全
networks:
frontend:
backend:

常用最佳实践

Dockerfile 最佳实践

实践说明
选择小的基础镜像node:20-alpine (120MB) vs node:20 (1GB)
利用构建缓存先复制 package.json,再复制源码
减少层数合并 RUN 命令:RUN apt-get update && apt-get install -y pkg
多阶段构建编译产物小几十 MB,比带整个 SDK 小太多
使用非 root 用户USER node 或创建专用用户
添加 .dockerignore排除 node_modules.git
dockerfile
# ❌ 不推荐
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get clean
# ✅ 推荐:合并在一个 RUN 中
RUN apt-get update && \
apt-get install -y curl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

.dockerignore

node_modules
.git
.gitignore
*.md
docker-compose*.yml
.env
.env.*

安全性

建议具体操作
不运行 rootRUN adduser -D appuser + USER appuser
不存储密钥在镜像中用环境变量或 Secret 注入
扫描镜像漏洞docker scan 或 Trivy
使用受信任的基础镜像优先选官方镜像,带签名验证的
保持镜像更新定期 docker pull 更新基础镜像

实际项目结构示例

    • .dockerignore
    • Dockerfile
    • docker-compose.yml
    • docker-compose.dev.yml
      • index.js
    • package.json
    • package-lock.json
    • README.md

CI/CD 自动化部署

完整部署流程

VPS 服务器Docker HubGitHub ActionGitHub开发者VPS 服务器Docker HubGitHub ActionGitHub开发者git push触发 workflow执行测试构建镜像docker push推送成功SSH 连接docker pulldocker-compose up -d部署完成通知

GitHub Action 配置示例

yaml
# .github/workflows/docker-deploy.yml
name: Docker Build and Deploy
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: user/myapp:latest
cache-from: type=registry,ref=user/myapp:buildcache
cache-to: type=registry,ref=user/myapp:buildcache,mode=max
- name: Deploy to VPS
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /srv/myapp
docker-compose pull
docker-compose up -d --remove-orphans
docker image prune -f
TIP

详细配置可参考:GitHub Action 自动部署

部署成功率统计

{"title":{"text":"过去 30 天部署成功率","left":"center"},"tooltip":{"trigger":"axis"},"xAxis":{"type":"category","data":["Week 1","Week 2","Week 3","Week 4"]},"yAxis":{"type":"value","max":100,"name":"成功率 (%)"},"series":[{"name":"部署成功率","type":"line","data":[92,95,98,99],"smooth":true,"lineStyle":{"width":3,"color":"#51cf66"},"areaStyle":{"color":{"type":"linear","x":0,"y":0,"x2":0,"y2":1,"colorStops":[{"offset":0,"color":"rgba(81, 207, 102, 0.3)"},{"offset":1,"color":"rgba(81, 207, 102, 0.05)"}]}},"markLine":{"data":[{"type":"average","name":"平均值"}]}}]}

常见问题

如何清理磁盘空间?
bash
# 查看磁盘占用
docker system df
# 一键清理所有未使用资源(⚠️ 谨慎使用)
docker system prune -a --volumes
# 按需清理
docker container prune # 清理停止的容器
docker image prune # 清理悬空镜像
docker volume prune # 清理未使用的 volume
docker builder prune # 清理构建缓存
资源类型清理命令风险等级
停止的容器container prune🟢 低
悬空镜像image prune🟢 低
未使用的 Volumevolume prune🔴 高(数据丢失)
构建缓存builder prune🟡 中(重建变慢)
容器时区不对?

很多基础镜像(如 Alpine)默认使用 UTC 时区,导致日志时间错误。

解决方案 1:Dockerfile 中设置

dockerfile
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
echo $TZ > /etc/timezone

解决方案 2:docker run 时挂载

bash
docker run -v /etc/localtime:/etc/localtime:ro nginx

验证

bash
docker exec <container> date
# 应该显示 CST(中国标准时间)
端口冲突怎么办?

错误信息

Error response from daemon: driver failed programming external connectivity
on endpoint web: Bind for 0.0.0.0:8080 failed: port is already allocated

解决方法

bash
# 1. 查看端口占用
lsof -i :8080
# 或
netstat -tuln | grep 8080
# 2. 换一个映射端口
docker run -p 8081:80 nginx
# 3. 或停止占用端口的进程
kill -9 <PID>
容器无法访问宿主机服务?

场景:容器内的应用需要连接宿主机的数据库或 API。

❌ localhost

✅ host.docker.internal

容器 App

宿主机服务

解决方案

bash
# macOS / Windows
docker run -e DB_HOST=host.docker.internal my-app
# Linux(使用 docker0 网桥 IP)
ip addr show docker0 | grep inet
# 假设输出 172.17.0.1
docker run -e DB_HOST=172.17.0.1 my-app

或者使用 host 网络模式(仅 Linux):

bash
docker run --network host my-app
如何限制容器资源?

防止单个容器占用过多资源导致宿主机卡死。Docker 使用 Linux cgroups3Control Groups(控制组)是 Linux 内核提供的一种机制,用于限制、记录和隔离进程组的资源使用(CPU、内存、磁盘 I/O 等)。 技术实现资源限制。

bash
# 限制内存和 CPU
docker run \
--memory="512m" \
--memory-swap="1g" \
--cpus="1.5" \
nginx
# 实时监控
docker stats

在 docker-compose 中

yaml
services:
web:
image: nginx
deploy:
resources:
limits:
cpus: '1.5'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M

资源占用趋势图

{"title":{"text":"容器资源占用监控","left":"center"},"tooltip":{"trigger":"axis"},"legend":{"data":["CPU 使用率 (%)","内存使用率 (%)"],"bottom":10},"xAxis":{"type":"category","data":["00:00","04:00","08:00","12:00","16:00","20:00","24:00"]},"yAxis":{"type":"value","max":100},"series":[{"name":"CPU 使用率 (%)","type":"line","data":[15,18,45,60,55,40,20],"smooth":true,"lineStyle":{"color":"#4a90e2"}},{"name":"内存使用率 (%)","type":"line","data":[30,32,48,65,70,55,35],"smooth":true,"lineStyle":{"color":"#ff6b6b"}}]}

学习路线图

  • 理解镜像、容器、仓库三个核心概念
  • 掌握 Dockerfile 的常用指令
  • 能写简单的 Dockerfile 并构建镜像
  • 用 docker-compose 编排多容器应用
  • 理解数据卷和数据持久化方案
  • 配置 docker 网络实现容器通信
  • 能进行多阶段构建优化镜像体积
  • 了解部署到服务器的基础流程(→ Hexo 部署到搬瓦工 VPS

总结

知识点要点核心命令/指令
镜像构建分层结构、缓存利用、多阶段构建FROMCOPYRUNdocker build
容器管理生命周期、端口映射、exec 进入docker runexecstoprm
Compose多容器编排,YAML 定义服务docker compose updown
数据持久化Volume 是推荐方式,bind mount 用于开发-vvolumes:
网络bridge 单机通信,overlay 跨主机docker networknetworks:
安全非 root 用户、镜像扫描、密钥注入USERdocker scan

Docker 的入门门槛不高,不需要死记硬背所有命令,理解镜像分层和容器生命周期这两个核心概念后,其他都可以边用边查。


参考来源

脚注
  1. Docker 由 Solomon Hykes 于 2013 年创建,现已成为容器技术的事实标准。

    ↩¹↩²
  2. 容器共享宿主机内核,因此比虚拟机更轻量。这也是为什么 Linux 容器无法直接在 Windows 上运行的原因。

  3. Control Groups(控制组)是 Linux 内核提供的一种机制,用于限制、记录和隔离进程组的资源使用(CPU、内存、磁盘 I/O 等)。