9个Docker最佳实践,让你的容器更安全高效

  |   0 评论   |   40 浏览

Docker已经成为容器化的事实标准,但很多人用Docker的方式很有问题。镜像体积大、构建慢、安全性差…今天分享9个必须知道的Docker最佳实践。

我见过太多Dockerfile写得一塌糊涂,镜像几个GB,部署等到崩溃,用root用户跑应用安全风险巨大,构建一次要10分钟。


使用官方镜像

能从官方拿,就不要自己造。

自己构建基础镜像:
→ 可能有安全漏洞
→ 更新不及时
→ 兼容性问题

官方镜像:
→ 社区维护,定期更新
→ 安全漏洞修复快
→ 经过充分测试

常用官方镜像

镜像 用途
node Node.js应用
openjdk Java应用
python Python应用
nginx Web服务器
postgresql PostgreSQL数据库
redis Redis缓存
# ✅ 推荐
FROM node:18-alpine

# ❌ 不推荐
FROM my-custom-base-image

指定具体版本

永远不要用latest标签。

今天构建用的是node:18
明天构建可能变成node:19
应用炸了

latest指向最新的版本,你根本不知道会拉到什么版本
# ✅ 好
FROM node:18.17.0-alpine

# ❌ 坏
FROM node:latest

版本号建议

# 最严格:主版本.次版本.补丁版本
FROM node:18.17.0-alpine

# 次严格:主版本.次版本(允许补丁更新)
FROM node:18.17-alpine

# 一般情况:主版本
FROM node:18-alpine

看你对稳定性的要求选择。


多阶段构建

大幅减小镜像体积的神器。

构建Node.js应用:
需要安装npm依赖
需要编译TypeScript
需要打包前端资源

这些东西在运行时根本不需要
# ===== 构建阶段 =====
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# ===== 运行阶段 =====
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --production
CMD ["node", "dist/index.js"]

效果对比

方式 镜像体积
单阶段构建 800MB
多阶段构建 150MB

减少了85%。


使用.dockerignore

不需要的文件别拷进镜像。

构建镜像时,Docker会把整个上下文都拷进去
包括node_modules(几GB)、.git(几百MB)...

构建慢、镜像大,没必要

.dockerignore示例

node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.env.local
tests
*.md
coverage

效果

没有.dockerignore:
构建上下文大小:2.5GB
构建时间:8分钟

使用.dockerignore:
构建上下文大小:50MB
构建时间:30秒

使用最小权限用户

默认root用户风险很大。

容器被攻破
→ 如果是root用户
→ 攻击者可以做任何事
→ 可以修改系统文件
→ 可以安装恶意软件
# ✅ 使用非root用户
FROM node:18-alpine

# 创建专门的用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001

# 切换到该用户
USER nodejs

WORKDIR /app
COPY --chown=nodejs:nodejs package*.json ./
用户 容器逃逸风险
root 高,可以完全控制宿主机
非root 低,只能做受限操作

环境变量配置

通过环境变量实现配置外部化。

同一镜像不同环境:
- 开发环境:一套环境变量
- 测试环境:另一套环境变量
- 生产环境:又一套环境变量

镜像不用重新构建
# Dockerfile
ENV NODE_ENV=production
ENV PORT=3000
ENV DB_HOST=mongodb
# docker-compose.yml
services:
  app:
    image: myapp:1.0
    environment:
      - NODE_ENV=development
      - PORT=3000
      - DB_HOST=localhost
# Dockerfile设置默认值,docker-compose覆盖
ENV NODE_ENV=production
environment:
  - NODE_ENV=${NODE_ENV:-production}

优化缓存利用率

把变化少的放前面,变化多的放后面。

Docker构建时,每一层都会被缓存
如果这一层没变化,就用缓存
如果变化了,这层和后面所有层都要重新构建
# ❌ 缓存利用率低
COPY . .
RUN npm ci
RUN npm run build
# ✅ 缓存利用率高
COPY package*.json ./
RUN npm ci
COPY src ./src
RUN npm run build

效果对比

修改一行代码:

错误做法:
→ 拷贝代码(重新构建)
→ 安装依赖(重新构建,耗时2分钟)
→ 构建应用(重新构建,耗时1分钟)
总计:3分钟

正确做法:
→ 依赖没变,用缓存
→ 拷贝代码(重新构建)
→ 构建应用(重新构建,耗时1分钟)
总计:1分钟

给镜像打标签

标签要有意义,方便管理。

# 版本标签
docker tag myapp:1.0.0 myapp:1
docker tag myapp:1.0.0 myapp:latest

# 环境标签
docker tag myapp:1.0.0 myapp:dev
docker tag myapp:1.0.0 myapp:staging
docker tag myapp:1.0.0 myapp:prod

# Git提交标签
docker tag myapp:1.0.0 myapp:git-abc123
LABEL maintainer="your-email@example.com"
LABEL version="1.0.0"
LABEL description="My awesome application"
LABEL org.opencontainers.image.source="https://github.com/user/repo"
docker inspect myapp:1.0.0 | grep -A 10 "Labels"

扫描镜像漏洞

上线前必须扫描。

你以为基础镜像是安全的?
官方镜像也可能有漏洞

2019年发现的一个漏洞
影响了数百万个容器
只是因为大家用的基础镜像没及时更新

扫描工具

Docker内置扫描

docker scan myapp:1.0.0

# 输出示例:
✗ High: CVE-2023-1234 in openssl
✗ Medium: CVE-2023-5678 in node
✓ Low: CVE-2023-9012 in libssl

Trivy(推荐)

brew install trivy
trivy image myapp:1.0.0
trivy image --format json --output result.json myapp:1.0.0

集成到CI/CD

# GitHub Actions示例
- name: Build image
  run: docker build -t myapp:${{ github.sha }} .

- name: Scan image
  run: |
    trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:${{ github.sha }}

# 发现高危漏洞,构建直接失败

完整示例:优秀Dockerfile

# ==================== 构建阶段 ====================
FROM node:18.17.0-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# ==================== 运行阶段 ====================
FROM node:18.17.0-alpine

LABEL maintainer="your-email@example.com"
LABEL version="1.0.0"
LABEL description="My Node.js Application"

RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

WORKDIR /app
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/nodemodules ./nodemodules
COPY --chown=nodejs:nodejs package*.json ./

USER nodejs

ENV NODE_ENV=production
ENV PORT=3000

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=3s \
  CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"

CMD ["node", "dist/index.js"]

.dockerignore

node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.env.local
.env.*.local
tests
*.md
coverage
.vscode
.idea
dist

总结

9个Docker最佳实践:

  1. 使用官方镜像 - 安全可靠
  2. 指定具体版本 - 避免意外
  3. 多阶段构建 - 减小体积
  4. 使用.dockerignore - 加速构建
  5. 最小权限用户 - 提升安全
  6. 环境变量配置 - 灵活部署
  7. 优化缓存利用 - 提升效率
  8. 给镜像打标签 - 方便管理
  9. 扫描镜像漏洞 - 安全最后一道防线

Docker用得好是神器,用不好是灾难。遵循这些最佳实践,让你的容器化之路更顺畅。

善忘技术夹-公众号

评论

发表评论

validate