使用容器进行 Node.js 开发

前提条件

完成 容器化 Node.js 应用程序

概述

在本节中,你将学习如何为你的容器化应用程序设置开发环境。这包括

  • 添加本地数据库并持久化数据
  • 配置你的容器以运行开发环境
  • 调试你的容器化应用程序

添加本地数据库并持久化数据

你可以使用容器来设置本地服务,例如数据库。在本节中,你将更新 `compose.yaml` 文件以定义数据库服务和用于持久化数据的卷。

  1. 在 IDE 或文本编辑器中打开你的 `compose.yaml` 文件。

  2. 取消数据库相关指令的注释。以下是更新后的 `compose.yaml` 文件。

    重要

    对于本节,请不要运行 `docker compose up`,除非有指示。在中间点运行该命令可能会错误地初始化你的数据库。

    compose.yaml
    # Comments are provided throughout this file to help you get started.
    # If you need more help, visit the Docker Compose reference guide at
    # https://docs.docker.top/go/compose-spec-reference/
    
    # Here the instructions define your application as a service called "server".
    # This service is built from the Dockerfile in the current directory.
    # You can add other services your application may depend on here, such as a
    # database or a cache. For examples, see the Awesome Compose repository:
    # https://github.com/docker/awesome-compose
    services:
      server:
        build:
          context: .
        environment:
          NODE_ENV: production
        ports:
          - 3000:3000
    
        # The commented out section below is an example of how to define a PostgreSQL
        # database that your application can use. `depends_on` tells Docker Compose to
        # start the database before your application. The `db-data` volume persists the
        # database data between container restarts. The `db-password` secret is used
        # to set the database password. You must create `db/password.txt` and add
        # a password of your choosing to it before running `docker-compose up`.
    
        depends_on:
          db:
            condition: service_healthy
      db:
        image: postgres
        restart: always
        user: postgres
        secrets:
          - db-password
        volumes:
          - db-data:/var/lib/postgresql/data
        environment:
          - POSTGRES_DB=example
          - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
        expose:
          - 5432
        healthcheck:
          test: ["CMD", "pg_isready"]
          interval: 10s
          timeout: 5s
          retries: 5
    volumes:
      db-data:
    secrets:
      db-password:
        file: db/password.txt

    注意

    要了解有关 Compose 文件中指令的更多信息,请参阅 Compose 文件参考

  3. 在 IDE 或文本编辑器中打开 `src/persistence/postgres.js`。你会注意到此应用程序使用 Postgres 数据库,并且需要一些环境变量才能连接到数据库。`compose.yaml` 文件尚未定义这些变量。

  4. 添加指定数据库配置的环境变量。以下是更新后的 `compose.yaml` 文件。

    compose.yaml
    # Comments are provided throughout this file to help you get started.
    # If you need more help, visit the Docker Compose reference guide at
    # https://docs.docker.top/go/compose-spec-reference/
    
    # Here the instructions define your application as a service called "server".
    # This service is built from the Dockerfile in the current directory.
    # You can add other services your application may depend on here, such as a
    # database or a cache. For examples, see the Awesome Compose repository:
    # https://github.com/docker/awesome-compose
    services:
      server:
        build:
          context: .
        environment:
          NODE_ENV: production
          POSTGRES_HOST: db
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD_FILE: /run/secrets/db-password
          POSTGRES_DB: example
        ports:
          - 3000:3000
    
        # The commented out section below is an example of how to define a PostgreSQL
        # database that your application can use. `depends_on` tells Docker Compose to
        # start the database before your application. The `db-data` volume persists the
        # database data between container restarts. The `db-password` secret is used
        # to set the database password. You must create `db/password.txt` and add
        # a password of your choosing to it before running `docker-compose up`.
    
        depends_on:
          db:
            condition: service_healthy
      db:
        image: postgres
        restart: always
        user: postgres
        secrets:
          - db-password
        volumes:
          - db-data:/var/lib/postgresql/data
        environment:
          - POSTGRES_DB=example
          - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
        expose:
          - 5432
        healthcheck:
          test: ["CMD", "pg_isready"]
          interval: 10s
          timeout: 5s
          retries: 5
    volumes:
      db-data:
    secrets:
      db-password:
        file: db/password.txt
  5. 在 `server` 服务下添加 `secrets` 部分,以便你的应用程序安全地处理数据库密码。以下是更新后的 `compose.yaml` 文件。

    compose.yaml
    # Comments are provided throughout this file to help you get started.
    # If you need more help, visit the Docker Compose reference guide at
    # https://docs.docker.top/go/compose-spec-reference/
    
    # Here the instructions define your application as a service called "server".
    # This service is built from the Dockerfile in the current directory.
    # You can add other services your application may depend on here, such as a
    # database or a cache. For examples, see the Awesome Compose repository:
    # https://github.com/docker/awesome-compose
    services:
      server:
        build:
          context: .
        environment:
          NODE_ENV: production
          POSTGRES_HOST: db
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD_FILE: /run/secrets/db-password
          POSTGRES_DB: example
        ports:
          - 3000:3000
    
        # The commented out section below is an example of how to define a PostgreSQL
        # database that your application can use. `depends_on` tells Docker Compose to
        # start the database before your application. The `db-data` volume persists the
        # database data between container restarts. The `db-password` secret is used
        # to set the database password. You must create `db/password.txt` and add
        # a password of your choosing to it before running `docker-compose up`.
    
        depends_on:
          db:
            condition: service_healthy
        secrets:
          - db-password
      db:
        image: postgres
        restart: always
        user: postgres
        secrets:
          - db-password
        volumes:
          - db-data:/var/lib/postgresql/data
        environment:
          - POSTGRES_DB=example
          - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
        expose:
          - 5432
        healthcheck:
          test: ["CMD", "pg_isready"]
          interval: 10s
          timeout: 5s
          retries: 5
    volumes:
      db-data:
    secrets:
      db-password:
        file: db/password.txt
  6. 在 `docker-nodejs-sample` 目录中,创建一个名为 `db` 的目录。

  7. 在 `db` 目录中,创建一个名为 `password.txt` 的文件。此文件将包含你的数据库密码。

    你现在应该在你的 `docker-nodejs-sample` 目录中至少包含以下内容。

    ├── docker-nodejs-sample/
    │ ├── db/
    │ │ └── password.txt
    │ ├── spec/
    │ ├── src/
    │ ├── .dockerignore
    │ ├── .gitignore
    │ ├── compose.yaml
    │ ├── Dockerfile
    │ ├── package-lock.json
    │ ├── package.json
    │ └── README.md
  8. 在 IDE 或文本编辑器中打开 `password.txt` 文件,并指定你选择的密码。你的密码必须在一行上,不能有其他行。确保该文件不包含任何换行符或其他隐藏字符。

  9. 确保你保存对已修改的所有文件的更改。

  10. 运行以下命令启动你的应用程序。

    $ docker compose up --build
    
  11. 打开浏览器并验证应用程序是否在 https://127.0.0.1:3000 运行。

  12. 向待办事项列表中添加一些项目以测试数据持久性。

  13. 向待办事项列表中添加一些项目后,按终端中的 `ctrl+c` 停止你的应用程序。

  14. 在终端中,运行 `docker compose rm` 删除你的容器。

    $ docker compose rm
    
  15. 运行 `docker compose up` 再次运行你的应用程序。

    $ docker compose up --build
    
  16. 刷新浏览器中的 https://127.0.0.1:3000 并验证待办事项是否持久化,即使在容器被删除并再次运行之后。

配置和运行开发容器

你可以使用绑定挂载将你的源代码挂载到容器中。然后,容器可以立即看到你对代码所做的更改,只要你保存文件即可。这意味着你可以在容器中运行进程(例如 nodemon)来监视文件系统更改并对其做出响应。要了解有关绑定挂载的更多信息,请参阅 存储概述

除了添加绑定挂载之外,你还可以配置你的 Dockerfile 和 `compose.yaml` 文件以安装开发依赖项并运行开发工具。

更新你的 Dockerfile 以用于开发

在 IDE 或文本编辑器中打开 Dockerfile。请注意,Dockerfile 不会安装开发依赖项,也不会运行 nodemon。你需要更新你的 Dockerfile 以安装开发依赖项并运行 nodemon。

与其为生产创建一 个 Dockerfile,为开发创建另一个 Dockerfile,不如使用一个多阶段 Dockerfile 来同时处理两者。

将你的 Dockerfile 更新为以下多阶段 Dockerfile。

Dockerfile
# syntax=docker/dockerfile:1

ARG NODE_VERSION=18.0.0

FROM node:${NODE_VERSION}-alpine as base
WORKDIR /usr/src/app
EXPOSE 3000

FROM base as dev
RUN --mount=type=bind,source=package.json,target=package.json \
    --mount=type=bind,source=package-lock.json,target=package-lock.json \
    --mount=type=cache,target=/root/.npm \
    npm ci --include=dev
USER node
COPY . .
CMD npm run dev

FROM base as prod
RUN --mount=type=bind,source=package.json,target=package.json \
    --mount=type=bind,source=package-lock.json,target=package-lock.json \
    --mount=type=cache,target=/root/.npm \
    npm ci --omit=dev
USER node
COPY . .
CMD node src/index.js

在 Dockerfile 中,你首先将标签 `as base` 添加到 `FROM node:${NODE_VERSION}-alpine` 语句。这使你可以在其他构建阶段引用此构建阶段。接下来,你添加一个名为 `dev` 的新构建阶段来安装你的开发依赖项并使用 `npm run dev` 启动容器。最后,你添加一个名为 `prod` 的阶段,该阶段省略了开发依赖项并使用 `node src/index.js` 运行你的应用程序。要了解有关多阶段构建的更多信息,请参阅 多阶段构建

接下来,你需要更新你的 Compose 文件以使用新阶段。

更新你的 Compose 文件以用于开发

要使用 Compose 运行 `dev` 阶段,你需要更新你的 `compose.yaml` 文件。在 IDE 或文本编辑器中打开你的 `compose.yaml` 文件,然后添加 `target: dev` 指令以从你的多阶段 Dockerfile 中定位 `dev` 阶段。

此外,还要为绑定挂载向服务器服务添加一个新的卷。对于此应用程序,你将把本地机器上的 `./src` 挂载到容器中的 `/usr/src/app/src`。

最后,发布端口 `9229` 用于调试。

以下是更新后的 Compose 文件。所有注释都已删除。

compose.yaml
services:
  server:
    build:
      context: .
      target: dev
    ports:
      - 3000:3000
      - 9229:9229
    environment:
      NODE_ENV: production
      POSTGRES_HOST: db
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD_FILE: /run/secrets/db-password
      POSTGRES_DB: example
    depends_on:
      db:
        condition: service_healthy
    secrets:
      - db-password
    volumes:
      - ./src:/usr/src/app/src
  db:
    image: postgres
    restart: always
    user: postgres
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    expose:
      - 5432
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt

运行你的开发容器并调试你的应用程序

运行以下命令使用对 `Dockerfile` 和 `compose.yaml` 文件的新更改运行你的应用程序。

$ docker compose up --build

打开浏览器并验证应用程序是否在 https://127.0.0.1:3000 运行。

对本地机器上应用程序源文件的任何更改现在都会立即反映在正在运行的容器中。

在 IDE 或文本编辑器中打开 `docker-nodejs-sample/src/static/js/app.js`,并将第 109 行上的按钮文本从 `Add Item` 更新为 `Add`。

+                         {submitting ? 'Adding...' : 'Add'}
-                         {submitting ? 'Adding...' : 'Add Item'}

在浏览器中刷新 https://127.0.0.1:3000,并验证更新后的文本是否显示。

现在您可以连接调试器客户端到您的应用程序进行调试。有关调试器客户端的更多详细信息,请参阅Node.js 文档

总结

在本节中,您学习了如何设置 Compose 文件以添加模拟数据库并持久化数据。您还学习了如何创建多阶段 Dockerfile 并为开发设置绑定挂载。

相关信息

后续步骤

在下一节中,您将学习如何使用 Docker 运行单元测试。