基于 GitHub Workflow和 Docker 构建 NextJS

简介: 基于 GitHub Workflow和 Docker 构建 NextJS

最近由于某个偶然的事件,突然对Docker、Github自动化部署产生了浓厚的兴趣,开始研究Docker部署Nextjs应用!


NextJS 是 vercel 创建的 JavaScript 框架。它允许你使用 React 构建无服务器 API、服务器端渲染和静态 Web 应用程序。 Vercel 提供与 GitHub、GitLab 和 BitHub 的开箱即用 CI/CD 集成。


但有时,我们希望将 NextJS 应用程序托管在 vercel 之外的其他平台上,例如 AWS、 Azure。


在本博客中,我们将了解如何使用 GitHub Workflow 和 Docker 构建 NextJS 应用程序。


设置 NextJS 应用程序

NextJS 建议使用 create-next-app ,它会自动为你设置所有内容。要创建项目,请运行:

npx create-next-app
# or
yarn create next-app

安装完成后,按照说明启动开发服务器。尝试编辑 pages/index.js 并在浏览器上查看结果。

设置 Dockerfile

我们将把 NextJS 应用程序打包到 Docker 镜像中。使用 Docker 的原因是当我们想要运行 NextJS 服务器时,我们不需要安装任何额外的软件包,如 nodejs、pm2 等。


Docker 会将所有内容捆绑在一起,并为我们提供可以在任何地方运行的镜像。以下是我的 NextJS 应用程序的示例 Dockerfile。

FROM node:lts-alpine

ENV NODE_ENV production
ENV NPM_CONFIG_LOGLEVEL warn

RUN mkdir /home/node/app/ && chown -R node:node /home/node/app

WORKDIR /home/node/app

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

USER node

RUN npm install --production

COPY --chown=node:node .next .next
COPY --chown=node:node public public

EXPOSE 3000

CMD npm start

现在,让我们逐步看看上面的 Dockerfile 中发生了什么。

  • 我们使用 node:lts-alpine 作为基础镜像。
  • 将环境变量设置为 production 。
  • 设置一个 app 文件夹,并以 node 用户作为所有者。
  • 将 package.json 和 package-lock.json 复制到映像中。
  • 运行 npm install production 仅安装生产依赖项。
  • 将 .next 和 public 文件夹复制到容器中。这是非常有趣的一步。为什么我们要复制文件夹而不是使用 next build 命令构建应用程序?我们将在下面详细讨论这一点。
  • 暴露端口 3000,以便我们的应用程序可以从容器中访问。
  • 最后,运行 npm start 命令来启动我们的 NextJS 应用服务器。

我们可以看到,我们没有对 Dockerfile 进行任何更改。它很容易理解并且简单明了。有趣的部分是我们将 .next 和 public 文件夹复制到容器中,而不是在容器内构建。

这是详细的解释:

  • 在NextJS应用程序中,我们可能需要使用NEXT_PUBLIC环境变量。构建时过程需要 NEXT_PUBLIC 变量。 (例如 Firebase Web 客户端)
  • 如果我们使用 Firebase Web 客户端,那么我们需要提供一些必需的变量,例如 firebase api_key、app_id、auth_domain。
  • 在本地开发应用程序时,我们将这些变量写入 .env 或 .env.local 文件中。但我们不、不应该也不得将此文件推送到 git 等 VCS 系统上。
  • 因此,当我们在本地构建应用程序时,它将使用 .env 中的这些变量,并且过程会顺利完成。但是,当我们使用 RUN next build 命令在 Docker 中构建应用程序时,构建命令将失败,因为我们没有在 docker 映像中提供这些变量。
  • 如果我们想在 docker 构建过程中构建 NextJS 应用程序,我们需要在 docker build 命令中使用 --build-args 来传递构建时变量。有两种方法可以做到这一点。

  1. 我们使用 ci 秘密变量并将它们传递到 docker build 命令中
  2. 我们创建一个 .env 文件,使用 base64 对其进行编码,将其作为 ci 秘密变量传递,在 docker 文件内使用 base64 对其进行解码,然后构建 docker 映像。
  • 如果我们的公共变量列表将来增长,这将变得非常难以传递和维护。
  • 因此,为了不使构建过程复杂化,我们将使用 ci job 在 docker 映像之外构建应用程序,然后将 .next 、 public 文件夹复制到 docker 映像中。
  • 要在 ci 中传递环境变量,有两种方法。
  1. 将环境变量作为秘密传递
  2. 传递 .env 文件的base64编码,在ci进程中对其进行解码,将文件写入我们项目文件夹的根目录,与本地开发相同并构建我们的应用程序。

GtiHub Workflow

Workflow是由一个或多个作业组成的可配置自动化流程。我们将使用 YAML 文件配置工作流程。你可以在这里阅读更多。

下面是工作流程文件,我们将使用相同的。将此文件保存在 PROJECT_ROOT_FOLDER/.github/workflows/main.yml ,以便 GitHub 可以读取 yaml 文件并相应地设置操作。

name: Build & Publish

on:
  push:
    branches:
      - "**"             # all branches
      - "!dependabot/**"      # exclude dependbot branches
  workflow_dispatch:      # Manually run the workflow

jobs:
  next-build:
    if: ${{ github.event_name == 'workflow_dispatch' }}       # Run only if triggered manually
    runs-on: ubuntu-latest
    container: node:lts          # Use node LTS container version, same as Dockerfile base image
    steps:
      - name: Checkout
        uses: actions/checkout@v2       # Checkout the code
      - run: npm ci            #install dependencies
      - run: npm run build
        env:
          NEXT_PUBLIC_FIREBASE_API_KEY: ${{secrets.NEXT_PUBLIC_FIREBASE_API_KEY}}
          NEXT_PUBLIC_FIREBASE_APP_ID: ${{secrets.NEXT_PUBLIC_FIREBASE_APP_ID}}
          NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN: ${{secrets.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN}}
          NEXT_PUBLIC_FIREBASE_PROJECT_ID: ${{secrets.NEXT_PUBLIC_FIREBASE_PROJECT_ID}}
          NEXT_PUBLIC_SENTRY_DSN: ${{secrets.NEXT_PUBLIC_SENTRY_DSN}}
      - name: Upload Next build          # Upload the artifact
        uses: actions/upload-artifact@v2
        with:
          name: build
          path: |
            .next
            public
          retention-days: 7         # artifact retention duration, can be upto 30 days
  docker-push:
    needs: next-build        # Job depends on next-build(above) job
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Download next build       # Download the above uploaded artifact
        uses: actions/download-artifact@v2
        with:
          name: build
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.CR_PAT }}
      - name: Build and Push Docker Images
        run: |
          export CURRENT_BRANCH=${GITHUB_REF#refs/heads/}
          export TAG=$([[ $CURRENT_BRANCH == "main" ]] && echo "latest" || echo $CURRENT_BRANCH)
          export GITHUB_REF_IMAGE=ghcr.io/$GITHUB_REPOSITORY:$GITHUB_SHA
          export GITHUB_BRANCH_IMAGE=ghcr.io/$GITHUB_REPOSITORY:$TAG
          docker build -t $GCR_IMAGE -t $GITHUB_REF_IMAGE -t $GITHUB_BRANCH_IMAGE .
          echo "Pushing Image to GitHub Container Registry"
          docker push $GITHUB_REF_IMAGE
          docker push $GITHUB_BRANCH_IMAGE

现在,让我们讨论一下 yaml 文件中发生了什么。

  • 我们需要传递要触发工作流程的事件的条件。在我们的例子中,我们希望它出现在推送事件上。它也可以像 [push, pull_request] 一样多个。你可以在这里阅读更多。
  • 我们可以定义分支,我们希望这个工作流运行来观看。 !意味着要排除这些分支。
  • workflow_dispatch 手动运行构建过程。如果我们不写这个,我们的工作流程将在每次推送到存储库的任何分支时运行。你可以在这里阅读更多。


  • 我们将构建过程分为 2 个作业。
  1. 下一个构建:
  • 在此步骤中,我们使用 node:lts 作为基础镜像,这必须与 Dockerfile 基础镜像相同
  • 我们保留这份作业手册,因为我们不希望每次推送代码时都运行该作业。所以我们在步骤中添加 if: ${{ github.event_name == ‘workflow_dispatch’ }} 条件。
  • 在 env 部分中,我们从机密中导出环境变量。所以我们需要在GitHub项目的secret中添加这些变量。请在此处阅读有关如何操作的更多信息。
  • 在下一步中,操作将检查代码,运行 npm ci 以安装依赖项,并运行 npm run build 使用导出的环境变量构建 NextJS 应用程序。
  • 最后,在成功构建后,CI 作业将使用 actions/upload-artifact@v2 操作将我们的构建文件夹作为工件上传到 GitHub 上,保留时间为 7 天,以便 ci 作业可以在 docker-build 作业中下载相同的文件夹并用它来构建图像。在构建文件夹中,我们包括 .next 和 public 文件夹。 .next 文件夹是由构建过程生成的,我们使用公共文件夹来存放 svgs、图像等资源。因此我们也希望保留该文件夹。


  1. docker-push:构建我们的 docker 镜像
  • 该作业依赖于 needs:next-build ,这意味着我们只有在成功 next-build 作业后才会看到该作业。如果我们不写这个,那么我们的两个作业将并行,并且该作业将失败,因为它将无法下载 build 工件。 next build 将上传工件,然后,ci 作业,我们将能够访问它。所以我们需要这样写,它将创建一个顺序作业,而不是并行作业。
  • CI 作业将检查代码,使用 actions/download-artifact@v2 下载构建工件文件夹并将其解压缩。
  • 我们希望将 docker 镜像托管在 GitHub 包上,为此,我们将使用 docker/login-action@v1 操作使用用户名和密码登录 GitHub 容器注册表服务器。我们还需要在存储库机密中传递 CR_PAT ,与 NEXT_PUBLIC 变量相同。我们还可以在此处添加其他注册表,例如 GCR、AWS ECR 等。
  • 接下来,ci 作业将获取 CURRENT_BRANCH 并相应地标记我们的 docker 构建。在这里,我们创建 2 个标签,一个是分支名称,如 dev 、 qa 、 uat 、 main ,另一个是 commit沙。
  • 之后,该作业将开始构建我们的 docker 镜像,并在成功构建后将其推送到 GitHub 包。在这里,我们也可以将其推送到其他注册表,例如 GCR、AWS ECR 等。
  • 最后,这个作业就会退出,我们的工作流程就成功通过了。

要运行作业,我们必须导航到存储库操作,你将在左侧边栏上看到带有 Build & Push 的工作流程。单击该链接,你将看到如下屏幕。n


有了这个,我们将能够构建和打包我们的 NextJS 应用程序。你将在屏幕截图下方看到操作屏幕。

帮助链接:

https://dev.to/thakkaryash94/build-nextjs-application-using-github-workflow-and-docker-3foj

相关文章
|
23天前
|
运维 监控 Docker
构建高效微服务架构:从理论到实践构建高效自动化运维体系:Ansible与Docker的完美融合
【5月更文挑战第31天】 在当今软件开发的世界中,微服务架构已经成为了实现可伸缩、灵活且容错的系统的关键策略。本文将深入探讨如何从零开始构建一个高效的微服务系统,涵盖从概念理解、设计原则到具体实施步骤。我们将重点讨论微服务设计的最佳实践、常用的技术栈选择、以及如何克服常见的挑战,包括服务划分、数据一致性、服务发现和网络通信等。通过实际案例分析,本文旨在为开发者提供一套实用的指南,帮助他们构建出既健壮又易于维护的微服务系统。
|
23天前
|
Kubernetes 开发者 Docker
构建高效微服务架构:Docker与Kubernetes的协同应用
【5月更文挑战第30天】 在当今软件开发领域,微服务架构已成为实现系统模块化、提升可维护性及扩展性的关键策略。本文深入探讨了如何通过Docker容器化技术和Kubernetes集群管理,共同构建一个既高效又可靠的后端微服务环境。我们将剖析Docker和Kubernetes的核心功能,以及它们如何相辅相成,支撑起现代化的云原生应用程序部署和管理。文章还将提供具体实践案例,帮助开发者理解将理论应用于实际开发过程中的步骤和考虑因素。
|
25天前
|
Kubernetes Cloud Native 开发者
构建高效的云原生应用:Docker与Kubernetes的完美搭档
【5月更文挑战第29天】 在现代软件开发领域,"云原生"这一术语已经成为高效、可扩展和弹性的代名词。本文将深入探讨如何通过Docker容器化技术和Kubernetes集群管理工具实现云原生应用的构建和管理。我们将剖析Docker的核心原理,揭示其轻量级和易于部署的特点,并进一步探索Kubernetes如何为这些容器提供编排,保证应用的高可用性与自动扩缩容。文章不仅讨论了二者的技术细节,还提供了实践案例,帮助开发者理解并运用这些技术构建和维护自己的云原生应用。
|
3天前
|
Java Docker 容器
使用 Spring Boot 构建 Docker 镜像并进行多模式部署
使用 Spring Boot 构建 Docker 镜像并进行多模式部署
13 2
|
4天前
|
数据安全/隐私保护 开发者 Docker
国内docker公开镜像站的关闭!别急,docker_image_pusher 使用Github Action将国外的Docker镜像转存到阿里云私有仓库
通过使用 docker_image_pusher 这样的开源项目,我们能够轻松地解决国内访问 Docker 镜像拉取速度慢及拉去失败的问题,同时保证了镜像的稳定性和安全性。利用 Github Action 的自动化功能,使得这一过程更加简单和高效。
231 2
|
16天前
|
Web App开发 缓存 移动开发
四万字符数带你使用 Vitepress 构建博客并部署到 github 平台
四万字符数带你使用 Vitepress 构建博客并部署到 github 平台 最近写了好多篇 Chrome 浏览器插件相关的文章,有十几二十篇,就想着构建个博客,用来放置相应的文章。 正好前段时间看到 VitePress 1.0.0 发布了,而且是用 markdown 写文章,正好写插件文章的时候文章都是 md 格式,所有用下这个然后顺便写一篇使用教程。 Chrome 插件开发博客地址:https://18055975947.github.io/extension/
27 0
|
24天前
|
运维 Kubernetes 持续交付
构建高效自动化运维体系:基于Docker和Kubernetes的实践
【5月更文挑战第30天】 在当今的快速迭代和持续部署的软件发布环境中,自动化运维的重要性愈发凸显。本文旨在探讨如何利用容器化技术与微服务架构,特别是Docker和Kubernetes,来构建一个高效、可伸缩且自愈的自动化运维体系。通过详细分析容器化的优势及Kubernetes的集群管理机制,文章将提供一个清晰的指南,帮助读者理解并实现现代软件部署的最佳实践。
|
25天前
|
运维 监控 安全
构建高效自动化运维体系:Ansible与Docker的完美结合
【5月更文挑战第28天】 在当今快速演变的IT环境中,自动化已成为维护系统稳定性与提高效率的关键。本文将探讨如何通过结合Ansible和Docker技术构建一个高效的自动化运维体系。文章不仅介绍两者的基本概念,还详细阐述了集成实践,以及通过真实案例分析其优势和潜在挑战,旨在为读者提供一套可行的解决方案,以优化他们的DevOps流程。
|
26天前
|
Kubernetes 持续交付 Docker
构建高效微服务架构:Docker与Kubernetes的完美结合
【5月更文挑战第28天】在现代软件开发中,微服务架构已成为提高系统可维护性和扩展性的关键。本文深入探讨了如何利用Docker容器化技术和Kubernetes集群管理工具共同打造一个高效、可靠的微服务环境。通过分析两者的核心优势及互补特性,我们展示了一种优化的部署策略,旨在帮助开发者和系统管理员理解和实践在复杂分布式系统中实现服务的有效管理和自动化部署。
|
9天前
|
缓存 Linux Docker
docker 跨平台构建镜像
docker 跨平台构建镜像
19 0