Docker 部署 #
Docker 部署概述 #
Heroku 支持使用 Docker 容器部署应用,这为开发者提供了更灵活的运行时环境控制。
Docker vs Buildpack #
| 特性 | Buildpack | Docker |
|---|---|---|
| 配置复杂度 | 低 | 中 |
| 环境控制 | 有限 | 完全控制 |
| 本地一致性 | 一般 | 高 |
| 依赖管理 | 自动 | 手动配置 |
| 适用场景 | 标准应用 | 定制需求 |
Container Registry #
登录 Container Registry #
bash
# 登录 Heroku Container Registry
heroku container:login
# 输出示例
# Login Succeeded
推送镜像 #
bash
# 构建并推送镜像
heroku container:push web
# 推送特定进程类型
heroku container:push worker
# 推送所有进程类型
heroku container:push --recursive
# 推送到指定应用
heroku container:push web -a myapp
发布镜像 #
bash
# 发布镜像
heroku container:release web
# 发布多个进程类型
heroku container:release web worker
# 推送并发布
heroku container:push web && heroku container:release web
Dockerfile 编写 #
基本结构 #
dockerfile
# 使用官方 Node.js 镜像
FROM node:20-alpine
# 设置工作目录
WORKDIR /app
# 复制 package 文件
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制应用代码
COPY . .
# 暴露端口
EXPOSE $PORT
# 启动命令
CMD ["node", "index.js"]
多进程 Dockerfile #
dockerfile
# Dockerfile.web
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE $PORT
CMD ["node", "server.js"]
# Dockerfile.worker
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
CMD ["node", "worker.js"]
多阶段构建 #
dockerfile
# 构建阶段
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 生产阶段
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
EXPOSE $PORT
CMD ["node", "dist/main.js"]
heroku.yml #
基本配置 #
yaml
# heroku.yml
build:
docker:
web: Dockerfile
worker: Dockerfile.worker
完整配置示例 #
yaml
# heroku.yml
setup:
addons:
- plan: heroku-postgresql:mini
- plan: heroku-redis:mini
build:
docker:
web: Dockerfile.web
worker: Dockerfile.worker
run:
web: node server.js
worker: node worker.js
配置说明 #
| 字段 | 说明 |
|---|---|
| setup.addons | 自动创建 Add-ons |
| build.docker | 指定 Dockerfile |
| run | 定义启动命令 |
部署流程 #
标准部署流程 #
bash
# 1. 创建应用
heroku create myapp
# 2. 设置堆栈为 container
heroku stack:set container
# 3. 创建 heroku.yml
cat > heroku.yml << 'EOF'
build:
docker:
web: Dockerfile
EOF
# 4. 推送部署
git add heroku.yml
git commit -m "Add heroku.yml"
git push heroku main
Container Registry 部署流程 #
bash
# 1. 登录 Container Registry
heroku container:login
# 2. 创建应用
heroku create myapp
# 3. 构建并推送镜像
heroku container:push web -a myapp
# 4. 发布镜像
heroku container:release web -a myapp
# 5. 查看日志
heroku logs --tail -a myapp
部署流程图 #
text
┌─────────────────────────────────────────────────────┐
│ Docker 部署流程 │
├─────────────────────────────────────────────────────┤
│ │
│ 方式一:Git + heroku.yml │
│ ┌─────────────────────────────────────────────┐ │
│ │ Dockerfile + heroku.yml │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ git push heroku main │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Heroku 构建镜像并部署 │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ 方式二:Container Registry │
│ ┌─────────────────────────────────────────────┐ │
│ │ 本地 Dockerfile │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ heroku container:push web │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ heroku container:release web │ │
│ └─────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘
不同语言示例 #
Node.js #
dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE $PORT
CMD ["node", "index.js"]
Python #
dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE $PORT
CMD ["gunicorn", "--bind", "0.0.0.0:$PORT", "app:app"]
Java #
dockerfile
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY target/myapp.jar .
EXPOSE $PORT
CMD ["java", "-jar", "myapp.jar"]
Go #
dockerfile
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o main .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE $PORT
CMD ["./main"]
Ruby #
dockerfile
FROM ruby:3.3-alpine
RUN apk add --no-cache build-base postgresql-dev
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install --without development test
COPY . .
EXPOSE $PORT
CMD ["rails", "server", "-b", "0.0.0.0", "-p", "$PORT"]
环境变量处理 #
在 Dockerfile 中使用环境变量 #
dockerfile
FROM node:20-alpine
# 设置默认环境变量
ENV NODE_ENV=production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# Heroku 会注入 PORT 环境变量
EXPOSE $PORT
CMD ["node", "index.js"]
启动脚本处理环境变量 #
dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
COPY start.sh /start.sh
RUN chmod +x /start.sh
EXPOSE $PORT
CMD ["/start.sh"]
bash
#!/bin/sh
# start.sh
echo "Starting application..."
echo "PORT: $PORT"
echo "NODE_ENV: $NODE_ENV"
exec node index.js
多进程应用 #
heroku.yml 配置 #
yaml
build:
docker:
web: Dockerfile.web
worker: Dockerfile.worker
clock: Dockerfile.clock
run:
web: node server.js
worker: node worker.js
clock: node scheduler.js
推送多进程镜像 #
bash
# 推送所有进程类型
heroku container:push --recursive
# 推送特定进程类型
heroku container:push web worker
# 发布所有进程类型
heroku container:release web worker clock
镜像管理 #
查看镜像 #
bash
# 查看本地镜像
docker images
# 查看 Heroku 镜像
docker images registry.heroku.com/myapp/web
镜像标签 #
bash
# 标签镜像
docker tag myapp:latest registry.heroku.com/myapp/web
# 推送标签
docker push registry.heroku.com/myapp/web
清理镜像 #
bash
# 删除本地镜像
docker rmi registry.heroku.com/myapp/web
# 清理未使用的镜像
docker image prune
本地开发 #
本地运行容器 #
bash
# 构建镜像
docker build -t myapp .
# 运行容器
docker run -p 3000:3000 -e PORT=3000 myapp
# 使用环境变量文件
docker run -p 3000:3000 --env-file .env myapp
Docker Compose #
yaml
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- PORT=3000
- DATABASE_URL=postgres://postgres:postgres@db:5432/myapp
depends_on:
- db
- redis
db:
image: postgres:16-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_PASSWORD=postgres
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
postgres_data:
bash
# 启动服务
docker-compose up
# 后台运行
docker-compose up -d
# 停止服务
docker-compose down
最佳实践 #
1. 优化镜像大小 #
dockerfile
# 使用 Alpine 基础镜像
FROM node:20-alpine
# 多阶段构建
FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/main.js"]
2. 安全最佳实践 #
dockerfile
# 使用非 root 用户
FROM node:20-alpine
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
WORKDIR /app
COPY --chown=nextjs:nodejs . .
USER nextjs
CMD ["node", "index.js"]
3. 健康检查 #
dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE $PORT
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:$PORT/health || exit 1
CMD ["node", "index.js"]
故障排查 #
镜像推送失败 #
bash
# 检查登录状态
heroku container:login
# 检查镜像名称
docker images | grep heroku
# 手动推送
docker push registry.heroku.com/myapp/web
应用启动失败 #
bash
# 查看日志
heroku logs --tail
# 检查环境变量
heroku config
# 本地测试
docker run -e PORT=3000 -p 3000:3000 myapp
端口绑定问题 #
javascript
// 确保使用 PORT 环境变量
const PORT = process.env.PORT || 3000;
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server listening on port ${PORT}`);
});
下一步 #
Docker 部署掌握后,接下来学习 多环境管理 了解如何管理开发、测试、生产环境!
最后更新:2026-03-28