» Node.js:使用Express构建REST API » 3. 部署 » 3.3 Docker和Docker Compose

Docker 和 Docker Compose

Docker 允许开发人员将他们的应用程序与所有依赖项一起打包到一个称为容器的单个单元中。这确保了在不同环境(如开发、测试和生产环境)之间的一致性,从而缓解了“它在我的机器上能跑”的问题。

安装 Docker:https://docs.docker.com/engine/install/

Dockerfile

Dockerfile 是一个包含构建 Docker 镜像指令的文本文件。它定义了创建 Docker 镜像所需的步骤,作为将来启动 Docker 容器的蓝图。

添加 Dockerfile

# Use the official Node.js image with specified version
FROM node:20.11-alpine3.18

# Set the working directory inside the container
WORKDIR /usr/src/app

# Copy package.json and package-lock.json to the working directory
COPY package*.json ./

# Install project dependencies
RUN npm install

# Copy the source code to the working directory
COPY src/ /usr/src/app/src
COPY tsconfig.json ./

# Build TypeScript code
RUN npm run build

# Expose the required ports
EXPOSE 3000

# Command to run the application
CMD ["node", "dist/main.js"]

Alpine Linux 是一个轻量级的安全的 Linux 发行版,特别适用于容器化环境、嵌入式系统和资源受限环境。这些环境下效率和安全性至关重要。

构建你的 docker 镜像:

docker build . -t lrbooks-nodejs:latest

注意
如果遇到权限问题,使用 sudo docker ...

执行 docker images 来检查镜像:

docker images

结果:

REPOSITORY                 TAG       IMAGE ID       CREATED          SIZE
lrbooks-nodejs             latest    47e247df12a8   46 seconds ago   215MB
...

Docker Compose

Docker Compose 是一个用于定义和运行多容器的 Docker 工具。它允许你使用 YAML 文件配置应用程序的服务、网络和卷,然后使用单个命令启动所有运行所需的容器,包括应用程序的依赖中间件和服务。

注意

安装 Docker Compose 最方便最推荐的方式是安装 Docker 桌面版。Docker 桌面版包含了 Docker 引擎,Docker CLI 和 Docker Compose。

按需安装 Compose:https://docs.docker.com/compose/install/

添加 compose/docker-compose.yml

services:
  lr-rest-books:
    image: lrbooks-nodejs:latest
    ports:
      - 3000:3000
    volumes:
      - ./config.json:/usr/src/app/config.json
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_started
      mongo:
        condition: service_started
  redis:
    image: docker.io/bitnami/redis:7.0
    environment:
      - REDIS_PASSWORD=${REDIS_PASSWORD}
    ports:
      - 6379:6379
  mysql:
    image: docker.io/bitnami/mysql:5.7.43
    environment:
      - MYSQL_DATABASE=lr_book
      - MYSQL_USER=test_user
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
    ports:
      - 3306:3306
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      timeout: 20s
      retries: 10
    volumes:
      - ~/lr-mysql-data:/bitnami/mysql/data
  mongo:
    image: bitnami/mongodb:latest
    environment:
      - ALLOW_EMPTY_PASSWORD=yes
    ports:
      - 27017:27017
    volumes:
      - ~/lr-mongodb-data:/bitnami/mongodb

添加专用于 Compose 的配置文件,compose/config.json

{
  "app": {
    "port": 3000,
    "page_size": 5,
    "token_secret": "LiteRank_in_Compose",
    "token_hours": 48
  },
  "db": {
    "file_name": "test.db",
    "dsn": "mysql://test_user:test_pass@mysql:3306/lr_book?charset=utf8mb4",
    "mongo_uri": "mongodb://mongo:27017",
    "mongo_db_name": "lr_book"
  },
  "cache": {
    "host": "redis",
    "port": 6379,
    "password": "test_pass",
    "db": 0,
    "timeout": 5000
  }
}

添加 compose/.env 用于设置环境变量:

REDIS_PASSWORD=test_pass
MYSQL_PASSWORD=test_pass
MYSQL_ROOT_PASSWORD=test_root_pass

警告.env 文件应该在 .gitignore 中添加以告知 git 忽略该文件。

更改 .gitignore

 test.db
+.env

执行:

cd compose
docker compose up

你应该看到类似下方内容:

[+] Running 4/4
 ✔ Container compose-redis-1          Created                                                                                                                                                                                          0.0s 
 ✔ Container compose-mysql-1          Recreated                                                                                                                                                                                        0.1s 
 ✔ Container compose-mongo-1          Recreated                                                                                                                                                                                        0.1s 
 ✔ Container compose-lr-rest-books-1  Recreated                                                                                                                                                                                         0.0s 
Attaching to lr-rest-books-1, mongo-1, mysql-1, redis-1
redis-1          | redis 13:24:52.38 
redis-1          | redis 13:24:52.39 Welcome to the Bitnami redis container
...

mongo-1          | mongodb 13:24:52.60 INFO  ==> 
mongo-1          | mongodb 13:24:52.60 INFO  ==> Welcome to the Bitnami mongodb container
mongo-1          | mongodb 13:24:52.61 INFO  ==> ** Starting MongoDB setup **
...
mysql-1          | mysql 13:24:52.61 
mysql-1          | mysql 13:24:52.62 Welcome to the Bitnami mysql container
mysql-1          | mysql 13:24:52.63 INFO  ==> ** Starting MySQL setup **
...

你不再需要手动安装那些数据库。它们都被 docker compose 搞定了。

如果你发一些请求到 3000 端口的 api 服务器,你将看到如下日志:

lr-rest-books-1  | ::ffff:192.168.65.1 - - [02/Mar/2024:12:35:11 +0000] "GET /books/4/reviews?q=masterpiece HTTP/1.1" 200 2 "-" "curl/8.1.2" - 19.527 ms
lr-rest-books-1  | ::ffff:192.168.65.1 - - [02/Mar/2024:12:35:21 +0000] "GET /books/2/reviews?q=masterpiece HTTP/1.1" 200 2 "-" "curl/8.1.2" - 3.162 ms
lr-rest-books-1  | ::ffff:192.168.65.1 - - [02/Mar/2024:12:35:26 +0000] "GET /books/2/reviews?q=masterpiece HTTP/1.1" 200 2 "-" "curl/8.1.2" - 2.242 ms
...

如果你想跳过 docker 镜像构建步骤,可以调整 compose/docker-compose.yml

@@ -1,6 +1,8 @@
 services:
   lr-rest-books:
-    image: lrbooks-nodejs:latest
+    build:
+      context: ../
+      dockerfile: Dockerfile
     ports:
       - 3000:3000
     volumes:

再次运行:

docker compose up

Compose 插件会自动按需构建你的镜像。

[+] Building 3.5s (13/13) FINISHED                                                                                                                                                                                     docker:desktop-linux
 => [lr-rest-books internal] load build definition from Dockerfile                                                                                                                                                                     0.0s
 => => transferring dockerfile: 593B                                                                                                                                                                                                   0.0s
 => [lr-rest-books internal] load metadata for docker.io/library/node:20.11-alpine3.18                                                                                                                                                 3.3s
 => [lr-rest-books auth] library/node:pull token for registry-1.docker.io                                                                                                                                                              0.0s
 => [lr-rest-books internal] load .dockerignore                                                                                                                                                                                        0.0s
 => => transferring context: 2B                                                                                                                                                                                                        0.0s
 => [lr-rest-books 1/7] FROM docker.io/library/node:20.11-alpine3.18@sha256:a02826c7340c37a29179152723190bcc3044f933c925f3c2d78abb20f794de3f                                                                                           0.0s
 => [lr-rest-books internal] load build context                                                                                                                                                                                        0.2s
 => => transferring context: 2.21kB                                                                                                                                                                                                    0.2s
 => CACHED [lr-rest-books 2/7] WORKDIR /usr/src/app                                                                                                                                                                                    0.0s
 => CACHED [lr-rest-books 3/7] COPY package*.json ./                                                                                                                                                                                   0.0s
 => CACHED [lr-rest-books 4/7] RUN npm install                                                                                                                                                                                         0.0s
 => CACHED [lr-rest-books 5/7] COPY src/ /usr/src/app/src                                                                                                                                                                              0.0s
 => CACHED [lr-rest-books 6/7] COPY tsconfig.json ./                                                                                                                                                                                   0.0s
 => CACHED [lr-rest-books 7/7] RUN npm run build                                                                                                                                                                                       0.0s
 => [lr-rest-books] exporting to image                                                                                                                                                                                                 0.0s
 => => exporting layers                                                                                                                                                                                                                0.0s
 => => writing image sha256:fe1c6e9f44aee52d0eb80032cdc1d9c961f2c6c2b18aeed69c763bb3464241f0                                                                                                                                           0.0s
 => => naming to docker.io/library/compose-lr-rest-books                                                                                                                                                                               0.0s
[+] Running 4/4
 ✔ Container compose-mongo-1          Created                                                                                                                                                                                          0.0s 
 ✔ Container compose-redis-1          Created                                                                                                                                                                                          0.0s 
 ✔ Container compose-mysql-1          Created                                                                                                                                                                                          0.0s 
 ✔ Container compose-lr-rest-books-1  Created                                                                                                                                                                                          0.1s 
Attaching to lr-rest-books-1, mongo-1, mysql-1, redis-1
...

现在,你可以用 curl 再次测试那些端点。它们应该表现得超预期的顺滑。

Kubernetes

如果你的云原生方案想更进一步的话,请尝试 Kubernetes

它也被称为 K8s,是一个用于自动化部署、扩展和管理容器化应用程序的开源系统。

创建一个如下的部署 yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: lr-books-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: lr-books
  template:
    metadata:
      labels:
        app: lr-books
    spec:
      containers:
      - name: lr-books-api
        image: lrbooks-nodejs:latest
        ports:
        - containerPort: 3000

然后执行如下命令使其生效:

kubectl apply -f lr-books-deployment.yaml