← 返回文章列表

Docker 容器技术从入门到精通完全指南

系统讲解 Docker 容器技术的核心知识点,涵盖 Dockerfile 编写、镜像构建、多阶段构建、容器运维等完整知识体系。

26 分钟阅读
字号

Docker 容器技术从入门到精通完全指南

本文系统梳理 Docker 容器技术的核心知识点,涵盖 Linux 内核三大机制、Dockerfile 编写、镜像构建优化、容器运维实践等完整知识体系。

概述

容器技术的本质是一种轻量级的虚拟化解决方案,它通过 Linux 内核的三大机制实现:

机制核心作用
Namespace隔离 — 让容器拥有独立的进程、网络、文件系统等视图
CGroup限制 — 限定容器对 CPU、内存、磁盘 IO 等资源的使用
UnionFS分层 — 通过写时复制实现镜像分层和容器轻量级创建

理解这三大机制的原理,是掌握容器技术的根基。


第一章:Linux 内核三大机制

1.1 Namespace 名称空间

名称空间提供了资源的视图隔离,让容器拥有"独立"某个资源的感觉,但实际上共享宿主机内核。Linux 提供以下 6 种名称空间:

名称空间隔离内容效果
PID进程 ID容器内只能看到自己的进程,PID=1 的进程通常是容器主进程
Network网络栈容器拥有自己独立的网络设备、IP 地址、端口、路由表等
Mount文件系统挂载点容器拥有自己独立的文件系统视图,看不到宿主机的挂载点
UTS主机名和域名容器可以有自己的 hostname,独立于宿主机
IPC进程间通信隔离 System V IPC、POSIX 消息队列等,防止容器间通信干扰
User用户和用户组 ID容器内的 root 用户(UID=0)在宿主机上只是普通用户,增强安全性

实现原理

从操作系统角度来说,一个容器本质就是一个进程。在 clone() 创建进程时,传入不同参数实现不同程度隔离:

  • CLONE_NEWPID:让进程内有独立的 PID 空间
  • CLONE_NEWNET:让进程拥有独立的网络设备、路由表
  • CLONE_NEWNS:让进程拥有独立的文件系统挂载点
  • CLONE_NEWIPC:隔离进程间通信资源
  • CLONE_NEWUTS:隔离主机名和域名
  • CLONE_NEWUSER:隔离用户和用户组 ID

实践中的有趣现象

隔离了 PID 后,在容器内执行 ps -aux 应该看不到宿主机进程,但实际却能看到所有进程。原因在于 pstop 等命令读取的是 /proc 文件系统,而 /proc 属于 Mount 名称空间。当只隔离 PID 而未隔离 Mount 时,父进程和子进程看到的文件是一样的。

只有当 Mount 也被隔离时,容器内才能真正做到"只见自己"的效果,仿佛真的创建了一个独立的系统。

1.2 CGroup(Control Group)

CGroup 实现资源的限制和核算,包括 CPU、内存、磁盘 IO、最大进程数等。

CPU 资源限制

CPU 的 CGroup 有几个关键参数:

  • cpu.cfs_period_us:CPU 调度周期,默认 100ms
  • cpu.cfs_quota_us:该控制组在一个调度周期内占用 CPU 的时间

例如 quota=50000period=100000,则 50000/100000 = 0.5,表示该控制组最多使用 0.5 个 CPU 核心。

  • cpu.shares:同一层级下多个控制组间的 CPU 分配比例

当 group1 的 shares=1024,group2 的 shares=4096 时,比值为 1:4。在 5 核机器上,如果两组都需要 5 个 CPU,则 group1 分配 1 个,group2 分配 4 个。

在 K8s 中:

  • requestscpu.shares:初始申请量,实际使用可超发
  • limitscpu.cfs_quota_us/cpu.cfs_period_us:资源使用上限

内存资源限制

  • memory.limit_in_bytes:容器内所有进程可占用的物理内存上限

注意:如果父级 CGroup 设置了 500M,子 CGroup 最多只能设置到 500M。

  • memory.oom_control:OOM 控制开关

    • 值为 0:开启 OOM Killer(默认)
    • 值为 1:关闭 OOM Killer
  • memory.usage_in_bytes:只读参数,记录容器内所有进程占用的物理内存总量

1.3 UnionFS

UnionFS(联合文件系统)是 Docker 镜像分层容器分层的基础,通过写时复制(Copy-on-Write)机制实现。

分层结构

  • lowerdir:只读镜像层
  • upperdir:可写层,容器运行时修改的内容
  • merged:展示最终的文件视图,合并上下层内容
┌─────────────────────────────────┐
│          merged (展现层)          │
├─────────────────────────────────┤
│  upperdir (容器层,可写)          │
├─────────────────────────────────┤
│  lowerdir (镜像层,只读)          │
└─────────────────────────────────┘

写时复制原理

当容器需要修改某个文件时,先从只读层复制到可写层,然后修改。这样既保证了镜像层不变,又实现了容器间的隔离。

强调:容器内挂载的文件系统类型不是 ext4、xfs,而是 overlay 文件系统。


第二章:Dockerfile 编写完全指南

Dockerfile 是构建镜像的指令文件,每一行是一条指令,构建时从上到下依次执行。

2.1 基础指令一览

指令作用
FROM指定基础镜像
LABEL添加元数据(作者、版本等)
WORKDIR设置工作目录
COPY复制本地文件到容器
ADD复制文件(支持 URL 和自动解压)
RUN执行命令
ENV设置环境变量
EXPOSE声明端口(文档作用)
USER切换用户
VOLUME声明卷挂载点
CMD容器启动命令(可被覆盖)
ENTRYPOINT容器启动命令(不可覆盖)

2.2 FROM — 指定基础镜像

# 使用官方镜像
FROM python:3.9-alpine
FROM ubuntu:20.04
FROM node:20
 
# 使用第三方镜像
FROM redis:7-alpine
 
# 从零开始(适用于 Go 等静态编译语言)
FROM scratch

最佳实践

  • 尽量用具体版本 tag,避免用 latest
  • 优先使用 alpineslim 等精简版本
  • 避免多层"二手镜像"(如 FROM ubuntu 再装 Python,不如直接用 FROM python:xxx

2.3 RUN — 执行命令

# 单条命令
RUN apt-get update && apt-get install -y curl
 
# 多条命令合并(减少镜像层数)
RUN apt-get update && \
    apt-get install -y curl vim git && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
 
# 安装 Python 依赖
RUN pip install --no-cache-dir -r requirements.txt
 
# 创建用户和用户组
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

最佳实践:多条 RUN 命令合并成一条,可以减少镜像层数,进而减小镜像体积。

2.4 COPY vs ADD — 复制文件

# COPY - 复制本地文件到容器(优先使用)
COPY requirements.txt /app/
COPY . /app/
 
# ADD - 类似 COPY,但支持 URL 和自动解压
ADD https://example.com/file.tar.gz /app/
ADD file.tar.gz /app/    # 自动解压到 /app/

推荐用 COPY。ADD 功能过于强大,不够直观,且在某些场景下行为不易理解。

2.5 WORKDIR — 设置工作目录

WORKDIR /app
COPY . .
RUN npm install
# 也可以设置不存在的目录,会自动创建
WORKDIR /app/subdir

最佳实践:用 WORKDIR 代替 cd /path && 这样的绝对路径写法,更清晰、更可维护。

2.6 ENV — 设置环境变量

ENV APP_HOME /app
ENV NODE_ENV production
ENV PORT=8080
# 运行时可覆盖
docker run -e NODE_ENV=development myimage

2.7 EXPOSE — 声明端口

EXPOSE 8080
EXPOSE 80 443

注意:这只是文档声明,实际端口映射靠 docker run -p 参数。容器实际监听的端口由应用本身决定。

2.8 USER — 切换用户

# 创建用户和用户组
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
 
# 切换用户(之后的命令以此用户身份执行)
USER appuser

安全规范:容器内应避免以 root 用户运行,使用非 root 用户可以减少安全风险。

2.9 VOLUME — 声明卷

VOLUME ["/data"]
VOLUME ["/data", "/logs"]

声明容器内的目录为卷挂载点,运行时可通过 -v 覆盖。


第三章:CMD vs ENTRYPOINT 详解

这是 Dockerfile 中最容易混淆的两个指令。

3.1 核心区别

特性CMDENTRYPOINT
能否被 docker run 覆盖不能(命令本身不可覆盖)
用途提供默认参数定义固定的可执行程序
典型场景变参数、变命令固定程序、固定工具

3.2 CMD — 可被覆盖

# 写法一:exec 格式(推荐)
CMD ["python", "app.py", "--port", "8080"]
 
# 写法二:shell 格式
CMD python app.py --port 8080
# 启动,CMD 的命令被完全覆盖
docker run myimage python other.py --arg1

3.3 ENTRYPOINT — 不可被覆盖

ENTRYPOINT ["python", "app.py"]
# 启动,追加的参数传给 app.py
docker run myimage --port 9000
# 实际执行: python app.py --port 9000

3.4 组合使用:ENTRYPOINT + CMD

这是最常见的用法:

ENTRYPOINT ["python", "app.py"]
CMD ["--help"]

效果

  • 默认执行:python app.py --help
  • 覆盖 CMD:docker run myimage --versionpython app.py --version

CMD 作为默认参数,可以被覆盖。

3.5 实际案例分析

案例 1:官方 Redis 镜像

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["redis-server"]
# 默认启动 redis-server
docker run redis
# 实际执行: docker-entrypoint.sh redis-server
 
# 覆盖 CMD 执行 redis-cli
docker run redis redis-cli
# 实际执行: docker-entrypoint.sh redis-cli

这里的 docker-entrypoint.sh 脚本会判断参数并做相应处理:

#!/bin/bash
set -e
 
if [ "$1" = 'redis-server' ]; then
    # 处理 redis-server 特有逻辑
    ...
fi
 
exec "$@"

案例 2:带默认参数的 Java 应用

ENTRYPOINT ["java", "-jar", "/app.jar"]
CMD ["--spring.profiles.active=prod"]
# 默认用 prod 配置启动
docker run myjava
 
# 覆盖为 dev 配置
docker run myjava --spring.profiles.active=dev

3.6 shell 格式 vs exec 格式

特性shell 格式exec 格式
信号处理会丢失 SIGTERM正确传递
环境变量展开会展开不会展开
PID=1由 shell 担任由命令本身担任
# 错误写法(CMD 作为 shell 的子进程,无法接收信号)
CMD python app.py
 
# 正确写法(app.py 是 PID=1)
CMD ["python", "-u", "app.py"]

3.7 信号传递问题

当你执行 docker stop 时:

  • exec 格式:SIGTERM 直接发给 app.py,可以优雅关闭
  • shell 格式:SIGTERM 发给 shell,shell 再转发,容易超时强制 kill

重要:如果应用需要优雅关闭(如处理完当前请求再退出),必须使用 exec 格式。

3.8 完全绕过 entrypoint

# 完全覆盖,包括 entrypoint
docker run --entrypoint redis-cli redis

这样 ENTRYPOINT 和 CMD 都会被覆盖,直接执行 redis-cli


第四章:多阶段构建详解

多阶段构建是 Docker 17.05+ 引入的特性,可以显著减小镜像体积。

4.1 为什么需要多阶段构建?

以 Python 应用为例:

  • 编译阶段:需要 Python、pip、gcc、数据库驱动编译依赖
  • 运行阶段:只需要 Python 运行时和已编译的依赖

传统方式需要在一个镜像里包含所有东西,体积很大。多阶段构建可以分离这两个阶段。

4.2 基础多阶段构建

# ============ 第一阶段:构建 ============
FROM node:20-alpine AS builder
 
WORKDIR /app
 
# 复制依赖文件并安装
COPY package*.json ./
RUN npm ci
 
# 复制源码并构建
COPY . .
RUN npm run build
 
# ============ 第二阶段:运行 ============
FROM node:20-alpine AS runner
 
WORKDIR /app
 
# 创建非 root 用户
RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs
 
# 从第一阶段复制构建产物
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
 
USER nextjs
 
EXPOSE 3000
 
CMD ["node", "dist/app.js"]

4.3 Next.js 项目多阶段构建示例

# ============ 第一阶段:构建 ============
FROM node:20-alpine AS builder
 
WORKDIR /app
 
# 先复制依赖文件(利用 Docker 缓存)
COPY package*.json ./
RUN npm ci
 
# 复制源码并构建
COPY . .
RUN npm run build
 
# ============ 第二阶段:运行 ============
FROM node:20-alpine AS runner
 
WORKDIR /app
 
# 创建非 root 用户
RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs
 
# 复制构建产物
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
 
USER nextjs
 
EXPOSE 3000
 
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
 
CMD ["node", "server.js"]

注意:Next.js 需要在 next.config.ts 中开启 standalone 模式:

const nextConfig = {
  output: 'standalone',
};

4.4 Go 项目多阶段构建

Go 是静态编译语言,可以实现极小镜像:

# ============ 第一阶段:构建 ============
FROM golang:1.21-alpine AS builder
 
WORKDIR /app
 
# 复制源码
COPY . .
 
# CGO_ENABLED=0 表示静态编译,不依赖 libc
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
 
# ============ 第二阶段:运行 ============
FROM scratch
 
# 只复制二进制文件
COPY --from=builder /app/main .
 
# 不需要任何运行时!
ENTRYPOINT ["./main"]

最终镜像可以只有几 MB

4.5 Java 项目多阶段构建

# ============ 第一阶段:构建 ============
FROM maven:3.9-eclipse-temurin-17 AS builder
 
WORKDIR /app
 
# 先复制依赖(利用缓存)
COPY pom.xml .
RUN mvn dependency:go-offline
 
# 复制源码并打包
COPY src ./src
RUN mvn package -DskipTests
 
# ============ 第二阶段:运行 ============
FROM eclipse-temurin:17-jre-alpine
 
WORKDIR /app
 
COPY --from=builder /app/target/myapp.jar ./app.jar
 
EXPOSE 8080
 
ENTRYPOINT ["java", "-jar", "app.jar"]

第五章:镜像优化实战

5.1 精简镜像原则

原则操作
减少层数多条 RUN 合并
清理缓存yum clean all -y / npm cache clean --force
精简基础镜像alpineslim 版本
避免二手镜像不从 ubuntu 再装 Python,用 python:3.9-alpine
调整指令顺序不变的放前面(利用缓存)
用非 root 用户USER 指令

5.2 清理缓存详解

yum/dnf 清理

RUN yum install -y https://packages.microsoft.com/config/rhel/8/packages-microsoft-prod.rpm \
    && yum install -y dotnet-sdk-8.0 \
    && yum clean all

npm 清理

RUN npm ci --only=production && npm cache clean --force

pip 清理

RUN pip install --no-cache-dir -r requirements.txt

5.3 利用 Docker 缓存

Docker 构建时会利用缓存,但如果某一行改变,之后的所有层都会重新构建。

优化技巧:把频繁变动的内容放后面,不变的内容放前面。

# 好的写法(不变的内容先处理)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .           # 代码变动,但 requirements 没变时,这行不会触发 npm install
 
# 不好的写法(每次代码变动都会重新安装依赖)
COPY . .
RUN pip install -r requirements.txt

5.4 .dockerignore 文件

和 .gitignore 类似,构建时排除不需要的文件。

# .dockerignore
node_modules
.npm
.git
.next
*.log
.env
.env.*
!.env.example
.vscode
.idea
*.md
README.md
Dockerfile
docker-compose.yml

第六章:镜像构建与运行

6.1 构建命令详解

# 基本构建
docker build -t myapp:1.0 .
 
# 指定 Dockerfile 路径
docker build -f /path/to/Dockerfile -t myapp:1.0 .
 
# 不使用缓存(强制重新构建)
docker build --no-cache -t myapp:1.0 .
 
# 传递构建参数
docker build --build-arg VERSION=1.0 --build-arg ENV=prod -t myapp:1.0 .
 
# 多平台构建
docker build --platform linux/amd64,linux/arm64 -t myapp:1.0 .
 
# 构建并指定输出
docker build -t myapp:1.0 -o ./dist .

6.2 查看镜像信息

# 查看镜像列表
docker images
 
# 查看镜像构建历史
docker history myapp:1.0
 
# 查看镜像详细信息
docker inspect myapp:1.0

6.3 运行容器

# 基本运行
docker run myapp:1.0
 
# 后台运行
docker run -d myapp:1.0
 
# 端口映射
docker run -d -p 8080:3000 myapp:1.0
 
# 环境变量
docker run -e NODE_ENV=production -e PORT=8080 myapp:1.0
 
# 挂载卷
docker run -v /host/path:/container/path myapp:1.0
 
# 交互式运行
docker run -it myapp:1.0 /bin/sh
 
# 运行完自动删除
docker run --rm myapp:1.0
 
# 覆盖 entrypoint
docker run --entrypoint /bin/sh myapp:1.0

6.4 完整示例:Flask 应用

目录结构

myflask/
├── Dockerfile
├── requirements.txt
├── app.py
└── .dockerignore

准备文件

# app.py
from flask import Flask
app = Flask(__name__)
 
@app.route('/')
def hello():
    return 'Hello World!'
 
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
# requirements.txt
flask

Dockerfile

FROM python:3.9-alpine
 
WORKDIR /app
 
# 先复制依赖文件(利用 Docker 缓存)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
 
# 再复制应用代码
COPY app.py .
 
EXPOSE 5000
 
# 开发模式直接运行
CMD ["python", "app.py"]

构建并运行

# 构建
docker build -t myflask:1.0 .
 
# 运行
docker run -d -p 8080:5000 --name myapp myflask:1.0
 
# 验证
curl localhost:8080

6.5 完整示例:React 应用

# ============ 构建阶段 ============
FROM node:20-alpine AS builder
 
WORKDIR /app
 
# 复制依赖文件
COPY package*.json ./
RUN npm ci
 
# 复制源码并构建
COPY . .
RUN npm run build
 
# ============ 运行阶段 ============
FROM nginx:alpine
 
# 从构建阶段复制构建产物到 nginx 目录
COPY --from=builder /app/dist /usr/share/nginx/html
 
# nginx 配置文件
COPY nginx.conf /etc/nginx/nginx.conf
 
EXPOSE 80
 
CMD ["nginx", "-g", "daemon off;"]
# nginx.conf
events {
    worker_connections 1024;
}
 
http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
 
    server {
        listen 80;
        server_name localhost;
        root /usr/share/nginx/html;
        index index.html;
 
        location / {
            try_files $uri $uri/ /index.html;
        }
    }
}

第七章:容器运维实践

7.1 容器生命周期

状态说明
Created基于镜像创建,文件系统已初始化,配置已加载,但主进程未启动
Running主进程正在执行,资源消耗开始
Paused主进程被 SIGSTOP 暂停,保留所有状态,不消耗 CPU
Exited主进程执行完毕或异常退出
Restarting停止的容器重新启动
Deleted容器被永久移除

7.2 常用容器命令

# 容器管理
docker ps                    # 查看运行中的容器
docker ps -a                 # 查看所有容器(包括已停止)
docker start myapp           # 启动容器
docker stop myapp             # 停止容器(发送 SIGTERM)
docker restart myapp          # 重启容器
docker rm myapp               # 删除容器(必须先 stop)
docker rm -f myapp            # 强制删除容器
 
# 进入容器
docker exec -it myapp /bin/sh
docker exec -it myapp ls /app
 
# 查看日志
docker logs myapp
docker logs -f myapp          # 实时跟踪
docker logs --tail 100 myapp   # 最后 100 行
 
# 查看资源使用
docker stats myapp
docker stats --no-stream myapp  # 只显示一次
 
# 复制文件
docker cp host/file myapp:/app/file
docker cp myapp:/app/file host/file
 
# 查看容器信息
docker inspect myapp           # 详细信息
docker port myapp              # 端口映射
docker top myapp              # 容器内进程

7.3 容器安全实践

使用非 root 用户

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

限制容器的 capabilities

# 只保留必要的 capabilities
docker run --cap-drop=all \
           --cap-add=NET_BIND_SERVICE \
           myapp

使用只读文件系统

docker run --read-only myapp

限制内存和 CPU

docker run -m 512m --memory-swap=1g \
           --cpus=1.5 \
           myapp

7.4 日志管理

配置日志轮转

# 全局配置 /etc/docker/daemon.json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "50m",
    "max-file": "3"
  }
}
# 运行时指定
docker run --log-opt max-size=10m --log-opt max-file=3 myapp

查看容器日志

# 实时跟踪
docker logs -f myapp
 
# 带时间戳
docker logs -t myapp
 
# 最后 N 行
docker logs --tail 100 myapp
 
# 过滤日志(需要 jq)
docker logs myapp 2>&1 | grep error

7.5 资源限制详解

内存限制

# 限制内存为 512MB
docker run -m 512m myapp
 
# 限制内存为 256MB,swap 为 512MB
docker run -m 256m --memory-swap=512m myapp
 
# 关闭 swap
docker run -m 256m --memory-swappiness=0 myapp

CPU 限制

# 限制为 1.5 个 CPU
docker run --cpus=1.5 myapp
 
# 限制为 0.5 个 CPU
docker run --cpus=0.5 myapp
 
# 限制特定 CPU 核心
docker run --cpuset-cpus="0,1" myapp

磁盘 IO 限制

# 限制读取速率 10MB/s
docker run --device-read-bps=/dev/sda:10m myapp
 
# 限制写入速率 10MB/s
docker run --device-write-bps=/dev/sda:10m myapp
 
# 限制 IOPS
docker run --device-read-iops=/dev/sda:100 myapp

第八章:常见问题与解决方案

8.1 构建失败:找不到包

# 错误:镜像源超时
RUN apt-get update && apt-get install -y curl
 
# 解决:更换镜像源或增加超时时间
RUN sed -i 's|http://archive.ubuntu.com|https://mirrors.aliyun.com|g' /etc/apt/sources.list && \
    apt-get update && apt-get install -y curl

8.2 构建失败:权限问题

# 错误:COPY 到系统目录
COPY . /usr/local/app/
 
# 解决:创建目录或使用非 root 用户
RUN mkdir -p /app && chown -R appuser:appgroup /app
COPY --chown=appuser:appgroup . /app

8.3 容器启动失败:端口被占用

# 查看端口占用
netstat -tlnp | grep 8080
 
# 或
ss -tlnp | grep 8080
 
# 使用其他端口
docker run -p 8081:8080 myapp

8.4 容器内中文乱码

ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8

8.5 构建缓存失效问题

# 问题:每次构建都重新安装依赖(因为 requirements.txt 内容变化)
COPY . .
RUN pip install -r requirements.txt
 
# 解决:先复制 requirements.txt,安装完成后再复制代码
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

8.6 多架构构建

# 启用 experimental 功能
export DOCKER_CLI_EXPERIMENTAL=enabled
 
# 查看构建机器支持的架构
docker buildx ls
 
# 创建新的 builder
docker buildx create --name mybuilder
docker buildx use mybuilder
 
# 构建多架构镜像
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:1.0 --push .

总结

Docker 容器技术通过 Linux 内核的 Namespace 实现资源隔离、CGroup 实现资源限制、UnionFS 实现分层存储,三者配合构成了容器运行时的基础。

编写 Dockerfile 的核心原则:

  1. 清晰简洁:使用 WORKDIR、避免过长的 RUN 命令
  2. 利用缓存:合理安排指令顺序
  3. 最小化:选择精简基础镜像、清理缓存、不安装不必要的依赖
  4. 安全第一:使用非 root 用户、必要时限制 capabilities
  5. 多阶段构建:分离构建环境和运行环境,减小镜像体积

理解这些底层原理和最佳实践,对于进行故障排查、性能优化和安全加固都至关重要。

分享

// RELATED_POSTS

0%