dockerfile

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Dockerfile 编写

Dockerfile 编写

概述

概述

Dockerfile 最佳实践、安全扫描等技能。
Dockerfile 最佳实践、安全扫描等技能。

指令详解

指令详解

FROM

FROM

dockerfile
undefined
dockerfile
undefined

基础镜像

基础镜像

FROM ubuntu:22.04 FROM node:18-alpine FROM python:3.11-slim
FROM ubuntu:22.04 FROM node:18-alpine FROM python:3.11-slim

多阶段构建

多阶段构建

FROM node:18 AS builder FROM nginx:alpine AS production
FROM node:18 AS builder FROM nginx:alpine AS production

使用 ARG 动态指定

使用 ARG 动态指定

ARG BASE_IMAGE=node:18-alpine FROM ${BASE_IMAGE}
undefined
ARG BASE_IMAGE=node:18-alpine FROM ${BASE_IMAGE}
undefined

WORKDIR

WORKDIR

dockerfile
undefined
dockerfile
undefined

设置工作目录(推荐使用绝对路径)

设置工作目录(推荐使用绝对路径)

WORKDIR /app WORKDIR /home/node/app
WORKDIR /app WORKDIR /home/node/app

多次使用会切换目录

多次使用会切换目录

WORKDIR /app WORKDIR src
WORKDIR /app WORKDIR src

当前目录: /app/src

当前目录: /app/src

undefined
undefined

COPY 与 ADD

COPY 与 ADD

dockerfile
undefined
dockerfile
undefined

COPY(推荐)

COPY(推荐)

COPY package.json ./ COPY src/ ./src/ COPY --chown=node:node . .
COPY package.json ./ COPY src/ ./src/ COPY --chown=node:node . .

多文件复制

多文件复制

COPY package.json package-lock.json ./
COPY package.json package-lock.json ./

ADD(支持 URL 和解压)

ADD(支持 URL 和解压)

ADD https://example.com/file.tar.gz /app/ ADD archive.tar.gz /app/ # 自动解压
ADD https://example.com/file.tar.gz /app/ ADD archive.tar.gz /app/ # 自动解压

推荐:优先使用 COPY,除非需要 ADD 的特殊功能

推荐:优先使用 COPY,除非需要 ADD 的特殊功能

undefined
undefined

RUN

RUN

dockerfile
undefined
dockerfile
undefined

Shell 形式

Shell 形式

RUN apt-get update && apt-get install -y curl
RUN apt-get update && apt-get install -y curl

Exec 形式

Exec 形式

RUN ["apt-get", "update"]
RUN ["apt-get", "update"]

最佳实践:合并命令减少层数

最佳实践:合并命令减少层数

RUN apt-get update &&
apt-get install -y --no-install-recommends
curl
wget
git &&
rm -rf /var/lib/apt/lists/*
undefined
RUN apt-get update &&
apt-get install -y --no-install-recommends
curl
wget
git &&
rm -rf /var/lib/apt/lists/*
undefined

CMD 与 ENTRYPOINT

CMD 与 ENTRYPOINT

dockerfile
undefined
dockerfile
undefined

CMD - 默认命令(可被覆盖)

CMD - 默认命令(可被覆盖)

CMD ["node", "app.js"] CMD ["npm", "start"]
CMD ["node", "app.js"] CMD ["npm", "start"]

ENTRYPOINT - 入口点(不易被覆盖)

ENTRYPOINT - 入口点(不易被覆盖)

ENTRYPOINT ["docker-entrypoint.sh"] CMD ["postgres"]
ENTRYPOINT ["docker-entrypoint.sh"] CMD ["postgres"]

组合使用

组合使用

ENTRYPOINT ["python"] CMD ["app.py"]
ENTRYPOINT ["python"] CMD ["app.py"]

运行: python app.py

运行: python app.py

docker run myimage other.py -> python other.py

docker run myimage other.py -> python other.py

undefined
undefined

ENV 与 ARG

ENV 与 ARG

dockerfile
undefined
dockerfile
undefined

ENV - 运行时环境变量

ENV - 运行时环境变量

ENV NODE_ENV=production ENV PORT=3000 HOST=0.0.0.0
ENV NODE_ENV=production ENV PORT=3000 HOST=0.0.0.0

ARG - 构建时参数

ARG - 构建时参数

ARG VERSION=1.0 ARG BUILD_DATE
ARG VERSION=1.0 ARG BUILD_DATE

ARG 转 ENV

ARG 转 ENV

ARG APP_VERSION ENV APP_VERSION=${APP_VERSION}
ARG APP_VERSION ENV APP_VERSION=${APP_VERSION}

使用构建参数

使用构建参数

docker build --build-arg VERSION=2.0 .

docker build --build-arg VERSION=2.0 .

undefined
undefined

EXPOSE

EXPOSE

dockerfile
undefined
dockerfile
undefined

声明端口(文档作用)

声明端口(文档作用)

EXPOSE 80 EXPOSE 443 EXPOSE 3000/tcp EXPOSE 5000/udp
undefined
EXPOSE 80 EXPOSE 443 EXPOSE 3000/tcp EXPOSE 5000/udp
undefined

VOLUME

VOLUME

dockerfile
undefined
dockerfile
undefined

声明挂载点

声明挂载点

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

USER

USER

dockerfile
undefined
dockerfile
undefined

切换用户

切换用户

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

或使用 UID

或使用 UID

USER 1000:1000
undefined
USER 1000:1000
undefined

HEALTHCHECK

HEALTHCHECK

dockerfile
undefined
dockerfile
undefined

健康检查

健康检查

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3
CMD curl -f http://localhost/ || exit 1
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3
CMD curl -f http://localhost/ || exit 1

禁用健康检查

禁用健康检查

HEALTHCHECK NONE
undefined
HEALTHCHECK NONE
undefined

最佳实践模板

最佳实践模板

Node.js 应用

Node.js 应用

dockerfile
FROM node:18-alpine
dockerfile
FROM node:18-alpine

创建非 root 用户

创建非 root 用户

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app

先复制依赖文件

先复制依赖文件

COPY package*.json ./
COPY package*.json ./

安装依赖

安装依赖

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

复制源代码

复制源代码

COPY --chown=appuser:appgroup . .
COPY --chown=appuser:appgroup . .

切换用户

切换用户

USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "app.js"]
undefined
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "app.js"]
undefined

Python 应用

Python 应用

dockerfile
FROM python:3.11-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app
dockerfile
FROM python:3.11-slim

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

WORKDIR /app

安装依赖

安装依赖

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

创建非 root 用户

创建非 root 用户

RUN useradd -m -r appuser && chown appuser:appuser /app USER appuser
COPY --chown=appuser:appuser . .
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
undefined
RUN useradd -m -r appuser && chown appuser:appuser /app USER appuser
COPY --chown=appuser:appuser . .
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
undefined

Go 应用

Go 应用

dockerfile
undefined
dockerfile
undefined

构建阶段

构建阶段

FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./ RUN go mod download
COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o main .
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./ RUN go mod download
COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o main .

生产阶段

生产阶段

FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /app/main /main
EXPOSE 8080
ENTRYPOINT ["/main"]
undefined
FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /app/main /main
EXPOSE 8080
ENTRYPOINT ["/main"]
undefined

Java 应用

Java 应用

dockerfile
undefined
dockerfile
undefined

构建阶段

构建阶段

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 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
RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser
WORKDIR /app COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
undefined
FROM eclipse-temurin:17-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser
WORKDIR /app COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
undefined

.dockerignore

.dockerignore

dockerignore
undefined
dockerignore
undefined

Git

Git

.git .gitignore
.git .gitignore

Node

Node

node_modules npm-debug.log
node_modules npm-debug.log

Python

Python

pycache *.pyc .venv venv
pycache *.pyc .venv venv

IDE

IDE

.idea .vscode *.swp
.idea .vscode *.swp

Docker

Docker

Dockerfile* docker-compose* .dockerignore
Dockerfile* docker-compose* .dockerignore

文档

文档

*.md LICENSE
*.md LICENSE

测试

测试

test tests coverage
test tests coverage

其他

其他

.env .env.* *.log tmp
undefined
.env .env.* *.log tmp
undefined

安全检查

安全检查

镜像扫描

镜像扫描

bash
undefined
bash
undefined

Docker Scout

Docker Scout

docker scout cves myimage:tag docker scout recommendations myimage:tag
docker scout cves myimage:tag docker scout recommendations myimage:tag

Trivy

Trivy

trivy image myimage:tag
trivy image myimage:tag

Snyk

Snyk

snyk container test myimage:tag
undefined
snyk container test myimage:tag
undefined

Dockerfile 检查

Dockerfile 检查

bash
undefined
bash
undefined

Hadolint

Hadolint

docker run --rm -i hadolint/hadolint < Dockerfile
docker run --rm -i hadolint/hadolint < Dockerfile

Dockle

Dockle

dockle myimage:tag
undefined
dockle myimage:tag
undefined

常见场景

常见场景

场景 1:入口脚本

场景 1:入口脚本

dockerfile
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["app"]
bash
#!/bin/bash
set -e
dockerfile
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["app"]
bash
#!/bin/bash
set -e

初始化逻辑

初始化逻辑

if [ "$1" = 'app' ]; then # 等待依赖服务 until nc -z db 5432; do echo "Waiting for database..." sleep 1 done fi
exec "$@"
undefined
if [ "$1" = 'app' ]; then # 等待依赖服务 until nc -z db 5432; do echo "Waiting for database..." sleep 1 done fi
exec "$@"
undefined

场景 2:多架构构建

场景 2:多架构构建

bash
undefined
bash
undefined

创建 builder

创建 builder

docker buildx create --name mybuilder --use
docker buildx create --name mybuilder --use

多架构构建并推送

多架构构建并推送

docker buildx build --platform linux/amd64,linux/arm64
-t myrepo/myimage:tag --push .
undefined
docker buildx build --platform linux/amd64,linux/arm64
-t myrepo/myimage:tag --push .
undefined

场景 3:构建缓存

场景 3:构建缓存

bash
undefined
bash
undefined

使用 BuildKit 缓存

使用 BuildKit 缓存

docker build --build-arg BUILDKIT_INLINE_CACHE=1 -t myimage:tag .
docker build --build-arg BUILDKIT_INLINE_CACHE=1 -t myimage:tag .

使用缓存

使用缓存

docker build --cache-from myimage:tag -t myimage:new .
undefined
docker build --cache-from myimage:tag -t myimage:new .
undefined

故障排查

故障排查

问题排查方法
构建慢优化 COPY 顺序、使用缓存
镜像大多阶段构建、精简基础镜像
权限问题检查 USER、文件权限
依赖问题检查网络、使用国内镜像源
问题排查方法
构建慢优化 COPY 顺序、使用缓存
镜像大多阶段构建、精简基础镜像
权限问题检查 USER、文件权限
依赖问题检查网络、使用国内镜像源