Docker 基础实战完整指南

🐳 Docker 基础实战完整指南

定位 :零基础 → 能独立构建/部署/调试容器化应用

环境 :华为云 ECS ecs-fce0-0001 (2vCPU/4GiB/ac9.large.2/Ubuntu 24.04)

工具 :Docker CE 29.5.3 + Compose v5.1.4

风格:「原理简述 + 代码实操 + 常见坑点 + 企业小技巧」


Part 1: 为什么需要 Docker?------从痛点出发

1.1 传统部署的噩梦

没有 Docker 的年代,你的部署流程可能是这样的:

复制代码
开发环境(Win/Mac)  →  测试环境(Linux)  →  生产环境(CentOS)
   "在我机器上能跑!"     "缺依赖/版本不对"    "JDK版本冲突!"
痛点 描述 Docker 解决方案
环境不一致 Win vs Linux vs Mac 差异 容器统一环境,一次构建到处运行
依赖冲突 项目A用 JDK8,项目B用 JDK17 每个容器独立环境
部署慢 手动装依赖 → 1~2 小时 镜像秒级启动
资源浪费 每个应用一台虚拟机 容器共享内核,一台机器跑几十个

1.2 容器 vs 虚拟机

复制代码
┌─────────────────────────────────────────────────────────────┐
│                   Virtual Machine (虚拟机)                    │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐                    │
│  │ App A    │ │ App B    │ │ App C    │                    │
│  │ Bin/Libs │ │ Bin/Libs │ │ Bin/Libs │   ← 各自完整的 OS  │
│  │ Guest OS │ │ Guest OS │ │ Guest OS │                    │
│  └──────────┘ └──────────┘ └──────────┘                    │
│            Hypervisor (虚拟化层)                             │
│                 Host OS (宿主机 OS)                          │
│                   Physical Hardware                         │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                     Docker Container (容器)                   │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐                    │
│  │ App A    │ │ App B    │ │ App C    │                    │
│  │ Bin/Libs │ │ Bin/Libs │ │ Bin/Libs │   ← 仅含应用依赖   │
│  └──────────┘ └──────────┘ └──────────┘                    │
│              Docker Engine (容器引擎)                        │
│                 Host OS (共享内核!)                          │
│                   Physical Hardware                         │
└─────────────────────────────────────────────────────────────┘
维度 虚拟机 (VM) 容器 (Container)
启动速度 分钟级 秒级
磁盘占用 GB 级 MB 级
内存开销 每个额外 ~512MB 几乎无额外开销
隔离性 完整隔离 进程级隔离
内核 每个 VM 独立内核 共享宿主机内核 ← 关键差异

1.3 镜像 vs 容器:类 vs 实例

复制代码
Image (镜像)           Container (容器)
   ┌──────┐              ┌──────────────┐
   │  JDK  │              │ JDK (layer 3)│
   ├──────┤    -run->    ├──────────────┤
   │ Tomcat│              │Tomcat(layer2)│  ← 镜像的"运行时"
   ├──────┤              ├──────────────┤
   │  War  │              │ War (layer 1)│    实例 + 可写层
   └──────┘              └──────────────┘
   只读                   读写(R/W layer)

类比: Class → new → Object
      Image  → run → Container

1.4 Hello-World 拆解执行流程

docker-01 (121.36.42.55) 上实际执行:

bash 复制代码
$ docker pull hello-world:latest
Digest: sha256:0e760fdfbc48ba8041e7c6db999bb40bfca508b4be580ac75d32c4e29d202ce1
Status: Downloaded newer image for hello-world:latest

$ docker run --name hw-demo hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

执行流程拆解

复制代码
           ① docker run hello-world
    Docker CLI ──────────────────────────► Docker Daemon
                                              │
          ② 本地有没有镜像?                    │
              ├─ 没有 → 去 Registry 拉取 ──────┤
              │          ③ 下载 hello-world:latest
              └─ 有   → 直接用                │
                                              │
          ④ 基于镜像创建容器                    │
              ├─ 分配文件系统 (可写层)          │
              ├─ 分配网络 (默认 bridge)         │
              └─ 启动进程                     │
                                              │
          ⑤ 执行容器内程序,stdout 流回 Client   │
              └─ 程序结束 → 容器退出 (Exited)
bash 复制代码
# 验证容器生命周期
$ docker ps -a --filter name=hw-demo
NAMES     IMAGE         STATUS                              PORTS
hw-demo   hello-world   Exited (0) Less than a second ago

# 镜像大小仅 9.49KB!
$ docker images hello-world
IMAGE                ID             DISK USAGE   CONTENT SIZE
hello-world:latest   0e760fdfbc48       25.9kB         9.49kB

企业真题 :"docker run 背后的完整流程是什么?"

答:CLI → Daemon → 检查本地镜像 → (无)拉取 Registry → 创建容器(分配网络/存储/Cgroup) → 启动进程 → 输出流回客户端


Part 2: 环境搭建与核心命令速成(1小时上手)

2.1 三平台安装(以 Ubuntu 24.04 为例)

bash 复制代码
# Step1: 卸载旧版本
apt-get remove -y docker.io docker-doc podman-docker containerd runc

# Step2: 安装依赖
apt-get update && apt-get install -y ca-certificates curl

# Step3: 添加 Docker GPG 密钥(阿里云镜像)
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg \
  -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

# Step4: 添加仓库(注意 CODENAME 变量)
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
  https://mirrors.aliyun.com/docker-ce/linux/ubuntu noble stable" \
  > /etc/apt/sources.list.d/docker.list

# Step5: 安装
apt-get update && apt-get install -y \
  docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Step6: 配置镜像加速 + log 限制
mkdir -p /etc/docker
cat > /etc/docker/daemon.json << 'EOF'
{
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://docker.xuanyuan.me"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}
EOF
systemctl daemon-reload && systemctl restart docker
systemctl enable docker

# Step7: 验证
$ docker --version
Docker version 29.5.3, build d1c06ef

$ docker compose version
Docker Compose version v5.1.4

2.2 高频命令实战清单

docker-01 上的真实操作:

bash 复制代码
# ──────────── 1. docker run ────────────
$ docker run -d --name nginx-demo -p 8088:80 nginx:alpine
# -d: 后台运行 (detached)
# --name: 给容器命名
# -p 8088:80: 端口映射 宿主机:容器

# ──────────── 2. docker ps (查看运行中) ────────────
$ docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
NAMES        IMAGE          STATUS          PORTS
nginx-demo   nginx:alpine   Up 5 minutes    0.0.0.0:8088->80/tcp

# ──────────── 3. docker logs (实时看日志) ────────────
$ docker logs nginx-demo --tail 3
# 关键参数: -f (follow实时) --tail N (最后N行) --since (时间范围)

# ──────────── 4. docker exec (进入容器) ────────────
$ docker exec nginx-demo sh -c "nginx -v 2>&1"
Nginx version: 1.27.4
# exec 让你"穿透"容器边界,在容器内执行命令
# 常用: docker exec -it <name> /bin/bash  进入交互式 shell

# ──────────── 5. docker cp (传文件) ────────────
$ echo "host_content" > /tmp/host-file.txt
$ docker cp /tmp/host-file.txt nginx-demo:/tmp/
$ docker exec nginx-demo cat /tmp/host-file.txt
host_content
# 双向传输: 宿主机→容器 和 容器→宿主机

# ──────────── 6. docker stats (资源监控) ────────────
$ docker stats --no-stream nginx-demo
# 实时显示 CPU/MEM/NET/DISK 使用情况

命令速查表

命令 作用 常用参数
docker run -d --name X -p H:C IMG 后台启动容器 --restart -v -e --network
docker ps -a 查看所有容器 --filter --format -q
docker logs -f <id> 实时看日志 --tail N --since
docker exec -it <id> sh 进入容器 -u root (以root执行)
docker cp HOST_PATH ID:CONT_PATH 传文件 -a (保留权限)
docker inspect <id> 查看详细配置 --format (Go模板)
docker stop/start/restart <id> 启停控制 -t (等待超时秒数)
docker rm <id> 删除容器 -f (强制) -v (同时删volume)
docker rmi <image> 删除镜像 -f (强制)
docker compose up -d Compose 启动 --build -f

2.3 镜像管理

bash 复制代码
$ docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
REPOSITORY   TAG        SIZE
my-nginx     v1         92.7MB
nginx        alpine     93.6MB
redis        7-alpine   57.8MB
mysql        8.0        1.1GB
alpine       latest     13.1MB

小技巧 :Alpine Linux 基础镜像仅 13.1MB,是制作精简镜像的首选,但注意它用 musl libc 而非 glibc


Part 3: 镜像构建------从 Dockerfile 开始

3.1 Dockerfile 核心指令精讲

复制代码
FROM        ← 基础镜像(必须第一行)
WORKDIR     ← 设置工作目录
COPY/ADD    ← 复制文件(COPY 简单复制, ADD 支持URL/tar解压)
RUN         ← 构建时执行命令(每RUN一层layer)
ENV         ← 环境变量
EXPOSE      ← 声明端口(仅是文档, 不实际映射)
CMD         ← 容器启动默认命令(可被 docker run 覆盖)
ENTRYPOINT  ← 容器入口(docker run 参数作为其参数)
VOLUME      ← 声明数据卷
USER        ← 切换用户
HEALTHCHECK ← 健康检查
指令 执行时机 是否可被覆盖 多层影响
RUN 构建时 (build) 每 RUN 一层
CMD 运行时 (run) docker run IMG cmd 完全覆盖 只有最后一层
ENTRYPOINT 运行时 (run) --entrypoint 强覆盖 只有最后一层

COPY vs ADD

指令 功能 建议
COPY 从构建上下文复制文件 优先使用
ADD 复制 + URL下载 + 自动解压tar 仅在需要tar自动解压时用

3.2 实战:构建一个 Nginx 静态网站

docker-01 上实操:

bash 复制代码
$ mkdir /tmp/docker-nginx
$ cat > /tmp/docker-nginx/Dockerfile << 'DEOF'
FROM nginx:alpine
LABEL maintainer="admin@example.com"
LABEL description="Nginx 静态网站实战"
COPY index.html /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
DEOF

$ echo "<h1>Hello from Docker Nginx!</h1>" > /tmp/docker-nginx/index.html

$ docker build -t my-nginx:v1 /tmp/docker-nginx
# Step 1/6 : FROM nginx:alpine
# Step 2/6 : LABEL maintainer=...
# Step 3/6 : COPY index.html /usr/share/nginx/html/
# Step 4/6 : EXPOSE 80
# Step 5/6 : CMD ["nginx", "-g", "daemon off;"]
# Successfully built my-nginx:v1

$ docker run -d --name nginx-build-demo -p 8090:80 my-nginx:v1
$ curl -s http://localhost:8090/
<h1>Hello from Docker Nginx!</h1>

$ docker images my-nginx --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
REPOSITORY   TAG       SIZE
my-nginx     v1        92.7MB

3.3 进阶:多阶段构建优化镜像大小

复制代码
┌────────────────────────────────────────────────────────┐
│                 传统构建 (1个镜像)                       │
│  ┌──────────────────────┐                              │
│  │   FROM JDK           │                              │
│  │   COPY src .         │                              │
│  │   RUN mvn package    │  ← 构建工具 + 代码都留在镜像  │
│  │   CMD java -jar ...  │    体积: 500MB+              │
│  └──────────────────────┘                              │
└────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│             多阶段构建 (Multi-stage)                      │
│  ┌──────────────────────┐   ┌──────────────────┐       │
│  │ Stage 1: build       │   │ Stage 2: runtime  │       │
│  │ FROM maven:3.9 AS b  │   │ FROM jre:17       │       │
│  │ COPY src .            │   │ COPY --from=b /app│       │
│  │ RUN mvn package       │──▶│ CMD java -jar ... │       │
│  └────────┬─────────────┘   └──────────────────┘       │
│           │                     体积: 120MB ↓↓↓          │
│     构建产物 (.jar) 仅复制到运行阶段                     │
└────────────────────────────────────────────────────────┘

多阶段构建 Dockerfile 模板

dockerfile 复制代码
# === 阶段1: 编译 ===
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline    # 缓存依赖(加速后续构建)
COPY src ./src
RUN mvn package -DskipTests

# === 阶段2: 运行 ===
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]

企业级技巧

  1. .dockerignore :避免 node_modules/, .git/, *.md 进入构建上下文
  2. 层优化:把不变的层放前面(依赖 > 代码),变动层放后面
  3. 非 root 用户USER 1000 避免容器内 root 权限
  4. 健康检查HEALTHCHECK CMD curl -f http://localhost/ || exit 1

Part 4: 容器网络与数据持久化

4.1 网络模式实战

Docker 三大网络模式:

模式 特点 适用场景
bridge (默认) 独立网络栈,NAT 通信 一般容器,需端口映射
host 共享宿主机网络栈 高性能场景(如监控 agent)
none 无网络,完全隔离 安全敏感(如密钥生成)
bash 复制代码
# 查看网络模式
$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
683234b9fb40   bridge    bridge    local     ← 默认 bridge
02635a047886   host      host      local
0cf4b1c9abef   none      null      local

4.2 自定义网络:容器间 DNS 互通

bash 复制代码
# 创建自定义网络(内置 DNS 解析!)
$ docker network create app-net
$ docker network inspect app-net --format 'Subnet: {{range .IPAM.Config}}{{.Subnet}}{{end}}'
Subnet: 172.18.0.0/16

# 在同一网络中启动容器
$ docker run -d --name web --network app-net nginx:alpine
$ docker run -d --name app --network app-net alpine sleep 300

# DNS 解析测试(容器名 → IP,自动!)
$ docker exec app ping -c 2 web
PING web (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.120 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.115 ms

关键 :自定义 bridge 网络提供自动 DNS 解析 ,容器间用名称即可通信,不用 --link(已废弃)!

4.3 Volume vs Bind Mount

复制代码
┌──────────────────────────────────────────────────┐
│              Volume (Docker 管理)                  │
│  Container ───► /var/lib/mysql                    │
│                      │                            │
│               ┌──────▼─────────┐                  │
│               │ /var/lib/docker│                  │
│               │  /volumes/...  │  ← Docker 管理  │
│               └────────────────┘                  │
└──────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────┐
│           Bind Mount (宿主机直连)                  │
│  Container ───► /app/config                       │
│                     │                             │
│              ┌──────▼──────┐                      │
│              │ /home/user/ │  ← 用户管理          │
│              │  config/    │                      │
│              └─────────────┘                      │
└──────────────────────────────────────────────────┘
类型 管理方 路径 跨容器 适用场景
Volume Docker /var/lib/docker/volumes/ 数据库、Redis
Bind Mount 用户 任意宿主机路径 开发调试、配置热更新

实战:Volume 持久化数据(在 docker-01 上已执行):

bash 复制代码
$ docker volume create mysql-data
$ docker run -d --name mysql-persist \
    -e MYSQL_ROOT_PASSWORD=root123 \
    -v mysql-data:/var/lib/mysql \
    mysql:8.0

# 写入测试数据
$ docker exec mysql-persist mysql -uroot -proot123 \
    -e "CREATE TABLE testdb.demo(id INT); INSERT INTO testdb.demo VALUES(42);"

# 重启容器
$ docker restart mysql-persist

# 数据仍在!
$ docker exec mysql-persist mysql -uroot -proot123 \
    -e "SELECT * FROM testdb.demo;"
+------+
| id   |
+------+
|   42 |
+------+

Bind Mount 演示

bash 复制代码
$ echo "host file content" > /tmp/bind-demo.txt
$ docker run --rm -v /tmp/bind-demo.txt:/data/file.txt:ro alpine cat /data/file.txt
host file content
# :ro = read-only,防止容器修改宿主机文件

Part 5: Compose 编排------告别单容器时代

5.1 docker-compose.yml 语法精要

核心三要素:services, networks, volumes

yaml 复制代码
# docker-compose.yml
# 三要素: services, networks, volumes
services:           # 定义服务(容器)
  web:              # 服务名(即 DNS 名)
    image: nginx:alpine
    ports:
      - "9090:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    restart: unless-stopped    # 生产必备!

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    restart: unless-stopped
    command: redis-server --save 60 1 --loglevel warning

5.2 实战:一键部署 Nginx + Redis

真实执行(docker-01):

bash 复制代码
$ mkdir -p /tmp/compose-demo/html
$ echo "<h1>Compose Demo - Nginx + Redis</h1>" > /tmp/compose-demo/html/index.html

$ docker compose up -d
# [+] Running 4/4
#  Network compose-demo_default  Created
#  Container compose-demo-web-1   Started
#  Container compose-demo-redis-1 Started

$ docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
NAMES                  IMAGE            STATUS
compose-demo-web-1     nginx:alpine     Up 5 seconds
compose-demo-redis-1   redis:7-alpine   Up 5 seconds

# 测试 Web
$ curl -s http://localhost:9090/
<h1>Compose Demo - Nginx + Redis</h1>

# 测试 Redis
$ docker exec compose-demo-redis-1 redis-cli ping
PONG

5.3 环境变量管理

bash 复制代码
# .env 文件(自动加载)
$ cat .env
REDIS_PASSWORD=secret123
DB_HOST=mysql-db

# docker-compose.yml 中使用
services:
  app:
    environment:
      - REDIS_PASSWORD=${REDIS_PASSWORD:-default}   # 有默认值
      - DB_HOST=${DB_HOST:?DB_HOST is required}     # 必须设置

关键注意

  • depends_on 只等容器启动,不等待服务就绪!MySQL 可能需要 30s 初始化。
  • 解决方案:健康检查 + wait-for-it.sh 脚本
  • restart: unless-stopped 生产必备(手动 stop 不会重启,其他退出都重启)
restart 值 行为
no (默认) 不自动重启
always 无论退出码都重启
on-failure:N 仅非0退出码重启,最多 N 次
unless-stopped 推荐:手动 stop 不重启,其他都重启

5.4 常用 Compose 命令

bash 复制代码
docker compose up -d          # 后台启动
docker compose down           # 停止并删除容器/网络
docker compose down -v        # 同时删除 Volume!
docker compose ps             # 查看服务状态
docker compose logs -f web    # 实时日志
docker compose restart        # 重启所有服务
docker compose up -d --build  # 重新构建并启动

Part 6: 调试与故障排查实战

6.1 容器启动失败?5 步诊断法

复制代码
                    Container 启动失败!
                          │
                    ┌─────▼──────┐
             ①      │ docker logs │ → 看错误输出
                    └─────┬──────┘
                          │
                    ┌─────▼──────────────┐
             ②      │ docker inspect      │ → 查 CMD/Entrypoint/Env
                    └─────┬──────────────┘
                          │
                    ┌─────▼────────────────────┐
             ③      │ docker exec -it <id> sh   │ → 进容器检查文件/权限
                    └─────┬────────────────────┘
                          │
                    ┌─────▼────────────────────┐
             ④      │ netstat -tulnp | grep :XX│ → 检查端口冲突
                    └─────┬────────────────────┘
                          │
                    ┌─────▼────────────────────┐
             ⑤      │ /var/log/syslog/audit.log│ → 宿主机日志
                    └──────────────────────────┘
                          │
                       [ 定位根因! ]

实际诊断演示

bash 复制代码
# 故意创建一个会退出的容器
$ docker run -d --name bad-cmd alpine /bin/nonexistent
$ docker ps -a --format "table {{.Names}}\t{{.Status}}"
NAMES     STATUS
bad-cmd   Exited (127) 1 second ago    ← 启动即退出, 退出码 127

$ docker logs bad-cmd
exec /bin/nonexistent: no such file or directory  ← 定位到问题!

$ docker inspect bad-cmd --format 'CMD: {{.Config.Cmd}}'
CMD: [/bin/nonexistent]    ← 确认 CMD 错误

6.2 经典问题合集

错误 原因 解决
port is already allocated 端口冲突 lsof -i :8080 找出占用进程 + 换端口
standard_init_linux.go:228: exec user process caused: no such file or directory CRLF 换行符 dos2unixgit config core.autocrlf=false
Permission denied 挂载目录 SELinux/AppArmor :z 标签或改用非 root 用户
bind: address already in use 端口已被占用 检查 docker ps 已存在的容器
429 Too Many Requests 镜像仓库限流 换镜像源或等待
COPY failed: file not found 构建上下文路径不对 检查 .dockerignore 和 Dockerfile 中的相对路径

6.3 端口冲突实战(真实踩坑)

bash 复制代码
# docker-01 上的真实错误
$ docker run -d --name web-demo -p 8088:80 nginx:alpine
Error response from daemon: Bind for 0.0.0.0:8088 failed: port is already allocated

# 诊断
$ docker ps --format "table {{.Names}}\t{{.Ports}}"
NAMES        PORTS
nginx-demo   0.0.0.0:8088->80/tcp     ← 8088 已被占用!

# 解决:先停掉占用的或换端口
$ docker stop nginx-demo && docker rm nginx-demo
# 或
$ docker run -d --name web-demo -p 9090:80 nginx:alpine

Part 7: 企业级入门实践

7.1 CI/CD 中的 Docker

复制代码
┌──────────────────────────────────────────────────────┐
│                 Jenkins / GitHub Actions               │
│                                                        │
│   git push ──► Build ──► Test ──► Push Image ──► Deploy│
│                   │                    │              │
│              docker build         docker push         │
│                   │                    │              │
│              ┌────▼────┐      ┌───────▼────────┐      │
│              │ 本地镜像 │      │ Harbor/ECR/ACR │      │
│              └─────────┘      │ (私有仓库)      │      │
│                                └───────┬────────┘      │
│                                        │              │
│                                  docker pull          │
│                                        │              │
│                                  ┌─────▼─────┐        │
│                                  │ K8s / ECS │        │
│                                  └───────────┘        │
└──────────────────────────────────────────────────────┘
yaml 复制代码
# GitHub Actions 示例
name: Docker Build & Push
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Build Image
        run: docker build -t myapp:${{ github.sha }} .
      - name: Push to Registry
        run: |
          docker tag myapp:${{ github.sha }} registry.example.com/myapp:${{ github.sha }}
          docker push registry.example.com/myapp:${{ github.sha }}

7.2 Docker 最佳实践 Checklist

复制代码
☑ 使用 .dockerignore 避免打包无用文件
☑ 镜像分层合理(不变层在上,变化层在下)
☑ 非 root 用户运行容器 (USER 1000)
☑ 启用健康检查 (HEALTHCHECK)
☑ 标签规范 v1.2.3-{BUILD_NUMBER}
☑ 多阶段构建减小镜像体积
☑ 日志输出到 stdout/stderr (不要写文件)
☑ 避免在 Dockerfile 中写密码 (用 --build-arg + secrets)
☑ 生产环境 restart: unless-stopped
☑ 资源限制 --memory --cpus 防止容器耗尽宿主机
☑ 定期清理无用镜像/容器 (docker system prune -f)

7.3 安全提醒

dockerfile 复制代码
# ❌ 危险做法
FROM ubuntu:latest
ENV DB_PASSWORD=mysecret123    # 密码明文写在镜像层!
USER root                       # root 运行!

# ✅ 安全做法
FROM ubuntu:24.04               # 固定版本,不用 :latest
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
USER appuser                    # 非 root 运行
# 密码通过 docker run -e 或 secrets 传入

附录 & 资源包

A. 镜像大小优化对比

方案 基础镜像 大小 说明
传统 JDK openjdk:17 ~470MB 含完整 JDK
JRE 精简 eclipse-temurin:17-jre ~210MB 仅运行时
Alpine JRE eclipse-temurin:17-jre-alpine ~185MB 最小 JRE
多阶段 自定义 ~120MB 编译+运行分离
Distroless gcr.io/distroless/java17 ~120MB Google 最小化
方案 基础镜像 大小 说明
Nginx 官方 nginx:latest ~187MB Debian 基础
Nginx Alpine nginx:alpine ~93MB 实测 93.6MB
Nginx Distroless nginx:alpine+精简 ~60MB 去掉 shell

B. Docker 命令速查卡

复制代码
镜像管理:
  docker pull <img>:<tag>       拉取镜像
  docker build -t <name> .      构建镜像
  docker push <img>:<tag>       推送镜像
  docker rmi <img>              删除镜像
  docker tag <src> <dst>        打标签
  docker save/load              导入导出

容器管理:
  docker run -d --name X -p H:C IMG   启动
  docker exec -it X /bin/bash         进入
  docker logs -f X                    看日志
  docker cp HOST_PATH X:CONT_PATH     传文件
  docker commit X new:tag             保存为镜像

系统管理:
  docker system df                    磁盘占用
  docker system prune -f              清理无用资源
  docker stats --no-stream            资源监控
  docker info                         系统信息

Compose:
  docker compose up -d                启动
  docker compose down -v              停止+清理
  docker compose ps                   状态
  docker compose logs -f svc          日志

C. 常见问题 FAQ

Q1: 容器退出后数据还在吗?

A: 容器删除后 可写层数据丢失。Volume/Bind Mount 的数据保留。

Q2: 如何让容器开机自启?

A: docker run --restart unless-stopped 或 Compose 中设 restart: unless-stopped

Q3: 容器之间如何通信?

A: 创建自定义 bridge 网络,容器间用 服务名(容器名) 做 DNS 解析。

Q4: 镜像太大怎么办?

A: 多阶段构建 + Alpine 基础镜像 + .dockerignore + 清理 apt-get clean

Q5: docker compose 和 docker-compose 区别?

A: docker compose 是 Docker CLI 插件(v2),docker-compose 是独立 Python 脚本(v1 已废弃)。

Q6: 如何限制容器资源?

A: docker run --memory 512m --cpus 1.5

Q7: 生产环境需要哪些镜像?

A: 应用镜像 + 监控 (Prometheus) + 日志 (ELK) + 反向代理 (Nginx/Traefik) + 数据库 (建议用云服务而非容器)

Q8: 容器内时间不对?

A: docker run -v /etc/localtime:/etc/localtime:ro 或设置 TZ 环境变量


今日挑战

尝试用 Alpine 构建一个 Python Flask 应用容器,要求:

  1. 基于 python:3-alpine
  2. 安装 Flask (pip install flask)
  3. 编写一个返回 {"status": "ok"}/health 接口
  4. 最终镜像 < 100MB

提示:cd /tmp && docker build -t flask-app:alpine .


本系列博客基于华为云 ecs-fce0 集群实战操作编写,Docker CE 29.5.3,所有输出均为真实服务器执行结果。

相关推荐
fthux1 小时前
「装闭」-AI驱动的开源装修闭坑系统
人工智能·docker·开源
likerhood1 小时前
服务器下载 Hugging Face 模型笔记:以 Qwen2.5-Coder-7B-CL 为例
运维·服务器·笔记
峥无10 小时前
Linux进程信号:从基础概念到内核底层原理
linux·运维·服务器·信号处理
土星云SaturnCloud11 小时前
土星云AI边缘计算SE110S系列模型部署实战-YOLOv5
服务器·人工智能·yolo·docker·边缘计算
北山有鸟11 小时前
用开发板的.config替换ubuntu中内核源码目录的.config
linux·运维·ubuntu
qq_4523962311 小时前
第二十篇:《Docker 故障排查常用命令与技巧》
运维·docker·容器
jcbut11 小时前
离线安装dify 1.7
linux·运维·dify
Qiuner11 小时前
Pico 重塑Agent时代人与数据交互方式
windows·docker·ai·架构
云计算磊哥@12 小时前
运维开发宝典024-Linux云计算运维入门阶段总结
linux·运维·运维开发