You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Github Actions和Docker部署SSR NextJS至Fargate与S3+CloudFront

Next.js SSR应用部署Fargate后添加CDN的实践方案

一、是否需要将/public和/.next/static同步到S3?

需要。这两个目录均为静态资源:

  • /public:存放手动上传的无需编译的静态文件(如图片、字体)
  • /.next/static:Next.js构建生成的带哈希命名的静态资源(如JS/CSS bundle、优化后的图片)

将这些资源放到S3并通过CDN分发,可减轻Fargate服务的带宽压力、提升全球访问速度,同时利用CDN缓存策略降低成本。

二、是否应该仅构建一次,拆分产物部署到Fargate和S3?

是的,必须这么做:

  • 保证静态资源与服务端代码版本一致,避免出现版本不兼容问题
  • 减少重复构建的时间与资源消耗

三、正确操作流程(不使用IaC工具)

调整GitHub Workflow与Dockerfile,将构建过程移至Workflow中,统一生成产物后分别部署:

1. 修改Dockerfile

移除镜像内的构建步骤,直接使用Workflow构建好的产物,缩小镜像体积:

FROM node:23-alpine

RUN npm i npm@latest -g

WORKDIR /usr/src/app

COPY package*.json ./
RUN npm ci --legacy-peer-deps --only=production

# 复制Workflow构建的产物
COPY .next/standalone ./
COPY .next/static ./.next/static
COPY public ./public

EXPOSE 3000

CMD ["node", "server.js"]

注:Next.js 15默认支持output: 'standalone',构建后生成的/.next/standalone包含运行所需最小依赖,可大幅压缩镜像体积。

2. 更新GitHub Workflow

添加构建步骤,同步静态资源到S3后再构建推送Docker镜像:

name: Deploy to AWS
on:
    push:
        branches:
            - 'main'
        tags:
            - '*'
jobs:
    deploy:
        runs-on: ubuntu-latest
        environment:
            name: production
        steps:
            - uses: actions/checkout@v4

            - name: Configure AWS credentials
              uses: aws-actions/configure-aws-credentials@v4
              with:
                  aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
                  aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
                  aws-region: ${{ vars.AWS_REGION }}

            # 配置Node环境并构建Next.js应用
            - name: Setup Node.js
              uses: actions/setup-node@v4
              with:
                  node-version: '23'
                  cache: 'npm'

            - name: Install dependencies
              run: npm ci --legacy-peer-deps

            - name: Build Next.js app
              run: npm run build

            # 同步静态资源到S3并设置缓存策略
            - name: Sync static assets to S3
              run: |
                  aws s3 sync ./public s3://${{ vars.S3_STATIC_BUCKET }}/public --delete
                  aws s3 sync ./.next/static s3://${{ vars.S3_STATIC_BUCKET }}/_next/static --delete
                  # 带哈希的静态资源设置永久缓存
                  aws s3 cp s3://${{ vars.S3_STATIC_BUCKET }}/_next/static s3://${{ vars.S3_STATIC_BUCKET }}/_next/static --metadata-directive REPLACE --cache-control "public, max-age=31536000, immutable" --recursive
                  # public目录资源设置短缓存
                  aws s3 cp s3://${{ vars.S3_STATIC_BUCKET }}/public s3://${{ vars.S3_STATIC_BUCKET }}/public --metadata-directive REPLACE --cache-control "public, max-age=86400" --recursive

            # 原ECR镜像构建推送步骤
            - name: Login to Amazon ECR
              id: login-ecr
              uses: aws-actions/amazon-ecr-login@v2

            - name: Build, tag, and push image to Amazon ECR
              id: build-image
              env:
                  ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
                  ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }}
                  IMAGE_TAG: ${{ github.sha }}
              run: |
                  docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
                  docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
                  echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"

            # 原ECS部署步骤
            - name: Download task definition
              run: |
                  aws ecs describe-task-definition --task-definition ${{ vars.ECR_REPOSITORY }} \
                  --query taskDefinition > task-definition.json

            - name: Fill in the new image ID in the Amazon ECS task definition
              id: task-def
              uses: aws-actions/amazon-ecs-render-task-definition@v1
              with:
                  task-definition: task-definition.json
                  container-name: ${{ vars.ECR_REPOSITORY }}
                  image: ${{ steps.build-image.outputs.image }}
                  environment-variables: |
                      my-env-vars

            - name: Deploy Amazon ECS task definition
              uses: aws-actions/amazon-ecs-deploy-task-definition@v2
              with:
                  task-definition: ${{ steps.task-def.outputs.task-definition }}
                  service: ${{ vars.ECR_REPOSITORY }}-svc
                  cluster: ${{ vars.ECS_CLUSTER }}
                  wait-for-service-stability: true

四、最佳实践与可用GitHub Actions

最佳实践

  1. 缓存策略区分:
    • /_next/static资源带哈希后缀,设置immutable和max-age=31536000(1年),避免浏览器重复验证
    • /public资源若无哈希,设置max-age=86400(1天),或手动给文件名添加哈希
  2. S3桶配置:
    • 启用版本控制,防止资源误删/覆盖
    • 配置桶策略允许CDN访问资源
  3. CDN(如CloudFront)配置:
    • 设置两个源:S3(静态资源)、Fargate的ALB(SSR请求)
    • 路由规则:/_next/static/*和/public/*转发到S3,其余请求转发到ALB
    • 启用HTTPS,使用AWS证书管理器的证书

可用GitHub Actions

  • aws-actions/configure-aws-credentials:已在使用,用于配置AWS凭证
  • actions/setup-node:配置Node环境,自带依赖缓存
  • aws-actions/amazon-ecr-login:已在使用,登录ECR
  • jakejarvis/s3-sync-action:替代AWS CLI的S3同步工具,配置更简洁:
    - name: Sync static assets to S3
      uses: jakejarvis/s3-sync-action@v0.5.1
      with:
        args: --delete --cache-control "public, max-age=31536000, immutable"
      env:
        AWS_S3_BUCKET: ${{ vars.S3_STATIC_BUCKET }}
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        AWS_REGION: ${{ vars.AWS_REGION }}
        SOURCE_DIR: ./.next/static
    

内容的提问来源于stack exchange,提问作者Royi Bernthal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 02:29:51