Docker镜像构建的最佳实践
生产级Docker镜像不是把代码拷进容器就完事了。一个合格的镜像要满足三个条件:体积小、构建快、安全无漏洞。从Dockerfile的写法开始就有讲究。
多阶段构建是减小镜像体积的核心手段:
# 阶段1:构建依赖
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --production=false
COPY . .
RUN npm run build
# 阶段2:运行时镜像
FROM node:20-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
# 安全加固
RUN addgroup -S appgroup && adduser -S appuser -G appgroup && chown -R appuser:appgroup /app
USER appuser
EXPOSE 3000
CMD ["node", "dist/main.js"]
这个Dockerfile把最终镜像从1.2GB压缩到180MB左右,构建缓存命中率从40%提升到85%。关键操作:构建依赖不进运行时镜像,非root用户运行,alpine替代full镜像。
镜像层缓存优化策略
Docker构建速度取决于缓存命中。Dockerfile指令顺序决定缓存层:
# 错误写法:代码变更导致所有层重建
COPY . /app
RUN npm install
RUN npm run build
# 正确写法:依赖不变则跳过install层
COPY package*.json /app/
RUN npm ci
COPY . /app
RUN npm run build
原则:变化频率低的指令放前面,变化频率高的放后面。package.json的变更频率远低于源代码,先拷贝依赖文件再安装,只有package.json变化时才重新执行npm ci。
.dockerignore文件同样关键:
# .dockerignore
node_modules
.git
.github
.env*
*.log
coverage
.vscode
dist
.next
__pycache__
*.pyc
.pytest_cache
把不需要的文件排除在外,一是减小构建上下文体积,二是防止敏感信息(.env文件)意外进入镜像。
Docker Compose多服务编排
单容器部署只适合简单场景,微服务架构至少需要应用、数据库、缓存多个容器协同。Docker Compose编排的关键是依赖关系和健康检查:
version: "3.8"
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: appuser
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
interval: 5s
timeout: 3s
retries: 10
redis:
image: redis:7-alpine
command: redis-server --maxmemory 512mb --maxmemory-policy allkeys-lru
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
DATABASE_URL: postgresql://appuser:password@postgres:5432/appdb
REDIS_URL: redis://redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
volumes:
pgdata:
depends_on配合condition: service_healthy确保应用容器只在数据库和缓存就绪后启动。Docker Secrets管理敏感配置,不把密码写进docker-compose.yml。
CI/CD流水线集成Docker部署
GitLab CI的完整Docker部署流水线:
# .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
LATEST_TAG: $CI_REGISTRY_IMAGE:latest
test:
stage: test
image: node:20-alpine
script:
- npm ci
- npm run test:ci
- npm run lint
build:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
- docker pull $LATEST_TAG || true
- docker build --cache-from $LATEST_TAG -t $IMAGE_TAG -t $LATEST_TAG .
- docker push $IMAGE_TAG
- docker push $LATEST_TAG
only:
- main
deploy:
stage: deploy
image: alpine:3.19
script:
- ssh -o StrictHostKeyChecking=no deploy@prod-server "docker pull $IMAGE_TAG && docker-compose up -d --force-recreate app"
only:
- main
when: manual
生产部署放在manual触发,避免测试环境变更直接上生产。docker pull配合up –force-recreate实现零停机更新。
生产环境Docker安全加固
容器的安全边界比虚拟机弱得多,默认配置下容器逃逸并不困难。生产环境必须做的加固:
1. 只读根文件系统:
app:
read_only: true
tmpfs:
- /tmp:size=100M
- /app/logs:size=50M
2. 限制内核能力:
app:
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
3. 资源限制防DoS:
app:
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
pids: 100
4. 镜像漏洞扫描:
trivy image --exit-code 1 --severity HIGH,CRITICAL $IMAGE_TAG
5. 非root运行:所有生产容器必须指定USER指令,禁止以root身份运行应用进程。
Docker自动化部署的核心不是写Dockerfile,而是把镜像构建、依赖编排、流水线集成、安全加固串成一个闭环。任何一环缺失,生产环境的稳定性就会打折。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/docker-zi-dong-hua-bu-shu-quan-liu-cheng-shi-zhan-cong-jing/