教程:创建具有 MySQL 和 Docker Compose 的多容器应用

在本教程中,你将了解如何创建多容器应用。 本教程以入门教程(Docker 和 Visual Studio Code 入门)为基础。 在本高级教程中,你将更新应用程序以使其按图中所述的方式工作,并了解如何:

  • 启动 MySQL。
  • 使用 MySQL 运行应用。
  • 创建 Compose 文件。
  • 运行应用程序堆栈。

Diagram shows two containers labeled Todo App and MySQL connected with a line.

通过使用多个容器,可以将容器专用于专业任务。 每个容器都应执行一项任务,而且应完成得很好。

下面是你可能想要使用多容器应用的一些原因:

  • 利用单独的容器,你能够以不同于数据库的方式管理 API 和前端。
  • 借助容器,你可使版本和更新版本相互隔离。
  • 虽然你可在本地对数据库使用容器,但在生产环境中,你可能需要对数据库使用托管服务。
  • 运行多个进程会要求使用进程管理器,这会让容器启动/关闭更加复杂。

先决条件

本教程将继续学习一系列教程,从创建容器应用开始。 从该教程开始,其中包括先决条件。 然后执行教程在应用中保留数据

还需要以下项:

  • Docker Compose

    适用于 Windows 或 Mac 的 Docker Desktop,包括 Docker Compose。 运行以下命令以进行验证:

    docker-compose version
    

    如果使用 Linux 操作系统,请安装 Docker Compose

与前面的教程一样,可以从 VS Code“资源管理器”视图或“DOCKER”视图完成大部分任务 。 可以选择“终端”>“新建终端”,在 VS Code 中打开命令行窗口 。 还可以在 Bash 窗口中运行命令。 除非指定,否则任何标记为 Bash 的命令都可以在 Bash 窗口或 VS Code 终端中运行。

启动 MySQL

默认情况下,容器以隔离方式运行。 它们对同一台计算机上的其他进程或容器一无所知。 若要允许容器间进行通信,请使用网络。

如果两个容器在同一网络上,那么它们可彼此通信。 如果没在同一网络上,则没法通信。

有两种方法可将容器放在网络上:在启动时进行分配,或者连接现有容器。 在此示例中,先创建网络,然后在启动时附加 MySQL 容器。

  1. 使用此命令创建网络。

    docker network create todo-app
    
  2. 启动 MySQL 容器并将它附加到网络。

    docker run -d 
        --network todo-app --network-alias mysql 
        -v todo-mysql-data:/var/lib/mysql 
        -e MYSQL_ROOT_PASSWORD=<your-password> 
        -e MYSQL_DATABASE=todos 
        mysql:5.7
    

    此命令还定义了环境变量。 有关详细信息,请参阅 MySQL Docker Hub 列表

    该命令指定了网络别名 mysql

  3. 使用 docker ps 命令获取容器 ID。

  4. 若要确认你已启动并运行数据库,请连接到该数据库。

    docker exec -it <mysql-container-id> mysql -p
    

    在出现提示时,输入所使用的密码。

  5. 在 MySQL shell 中,列出数据库并验证确保你看见 todos 数据库。

    SHOW DATABASES;
    

    你会看到以下输出。

    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    | todos              |
    +--------------------+
    5 rows in set (0.00 sec)
    
  6. 在准备好返回到终端命令提示符时输入 exit

使用 MySQL 运行应用

待办事项应用支持设置环境变量来指定 MySQL 连接设置。

  • MYSQL_HOST MySQL 服务器的主机名。
  • MYSQL_USER 要用于连接的用户名。
  • MYSQL_PASSWORD 要用于连接的密码。
  • MYSQL_DB 连接后要使用的数据库。

警告

对于开发,可以使用环境变量来设置连接设置。 在生产环境中运行应用程序时,不建议采用此做法。 有关详细信息,请参阅为何不该对机密数据使用环境变量

更安全的机制是使用容器编排框架提供的机密支持。 在大多数情况下,这些机密作为文件装载到正在运行的容器中。

此过程会启动应用,并将该容器连接到 MySQL 容器。

  1. 使用以下 docker run 命令。 该命令指定上面的环境变量。

    docker run -dp 3000:3000 
      -w /app -v ${PWD}:/app 
      --network todo-app 
      -e MYSQL_HOST=mysql 
      -e MYSQL_USER=root 
      -e MYSQL_PASSWORD=<your-password> 
      -e MYSQL_DB=todos 
      node:20-alpine 
      sh -c "yarn install && yarn run dev"
    
  2. 在 VS Code 的 Docker 视图中,右键单击应用容器,然后选择“查看日志”。 若要从命令行查看日志,请使用 docker logs 命令。

    结果中包含一行,指示该应用已连接到 MySQL 数据库。

    # Previous log messages omitted
    $ nodemon src/index.js
    [nodemon] 1.19.2
    [nodemon] to restart at any time, enter `rs`
    [nodemon] watching dir(s): *.*
    [nodemon] starting `node src/index.js`
    Connected to mysql db at host mysql
    Listening on port 3000
    
  3. 在浏览器中输入 http://localhost:3000。 将一些项添加到待办事项列表中。

  4. 按上一部分中的相同方式连接到 MySQL 数据库。 运行此命令可验证是否正在向数据库写入项。

    docker exec -ti <mysql-container-id> mysql -p todos
    

    在 MySQL shell 中,运行以下命令。

    use todos;
    select * from todo_items;
    

    结果将类似于以下输出。

    +--------------------------------------+--------------------+-----------+
    | id                                   | name               | completed |
    +--------------------------------------+--------------------+-----------+
    | c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! |         0 |
    | 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome!        |         0 |
    +--------------------------------------+--------------------+-----------+
    

此时,拥有一个将数据存储在外部数据库中的应用程序。 该数据库在单独的容器中运行。 你已学习了容器网络。

创建 Docker Compose 文件

Docker Compose 有助于定义和共享多容器应用程序。 使用 Docker Compose,你可以创建用于定义服务的文件。 使用单个命令,可以启动所有内容,也可以将其全部销毁。

可以在文件中定义应用程序堆栈,并将该文件保存在项目存储库的根目录下,受版本控制。 利用此方法,其他人也可以参与你的项目。 他们只需克隆你的存储库。

  1. 在应用项目的根目录中,创建名为 docker-compose.yml 的文件。

  2. 在 Compose 文件中,首先定义架构版本。

    version: "3.7"
    

    在大多数情况下,最好使用支持的最新版本。 有关当前架构版本和兼容性矩阵,请参阅 Compose 文件

  3. 定义要作为应用程序的一部分运行的服务(或容器)列表。

    version: "3.7"
    
    services:
    

    提示

    缩进在 .yml 文件中非常重要。 如果要在 VS Code 中进行编辑,Intellisense 会指示错误。

  4. 下面是用于应用容器的命令。 将此信息添加到 .yml 文件。

    docker run -dp 3000:3000 
      -w /app -v ${PWD}:/app 
      --network todo-app 
      -e MYSQL_HOST=mysql 
      -e MYSQL_USER=root 
      -e MYSQL_PASSWORD=<your-password> 
      -e MYSQL_DB=todos 
      node:20-alpine 
      sh -c "yarn install && yarn run dev"
    

    定义容器的服务项和映像。

    version: "3.7"
    
    services:
      app:
        image: node:20-alpine
    

    你可为服务选择任何名称。 该名称会自动成为网络别名,这在定义 MySQL 服务时非常有用。

  5. 添加命令。

    version: "3.7"
    
    services:
      app:
        image: node:20-alpine
        command: sh -c "yarn install && yarn run dev"
    
  6. 指定用于服务的端口,该端口对应于上述命令中的 -p 3000:3000

    version: "3.7"
    
    services:
      app:
        image: node:20-alpine
        command: sh -c "yarn install && yarn run dev"
        ports:
          - 3000:3000
    
  7. 指定工作目录和卷映射

    version: "3.7"
    
    services:
      app:
        image: node:20-alpine
        command: sh -c "yarn install && yarn run dev"
        ports:
          - 3000:3000
        working_dir: /app
        volumes:
          - ./:/app
    

    在 Docker Compose 卷定义中,你可以使用当前目录中的相对路径。

  8. 指定环境变量定义。

    version: "3.7"
    
    services:
      app:
        image: node:20-alpine
        command: sh -c "yarn install && yarn run dev"
        ports:
          - 3000:3000
        working_dir: /app
        volumes:
          - ./:/app
        environment:
          MYSQL_HOST: mysql
          MYSQL_USER: root
          MYSQL_PASSWORD: <your-password>
          MYSQL_DB: todos
    
  9. 添加 MySQL 服务的定义。 下面是你在上面所使用的命令:

    docker run -d 
      --network todo-app --network-alias mysql 
      -v todo-mysql-data:/var/lib/mysql 
      -e MYSQL_ROOT_PASSWORD=<your-password> 
      -e MYSQL_DATABASE=todos 
      mysql:5.7
    

    定义新服务,并将其命名为 mysql。 在 app 定义后以相同的缩进级别添加文本。

    version: "3.7"
    
    services:
      app:
        # The app service definition
      mysql:
        image: mysql:5.7
    

    该服务会自动获取网络别名。 指定要使用的映像。

  10. 定义卷映射。

    使用与 services: 相同级别的 volumes: 部分指定卷。 在映像下指定卷映射。

    version: "3.7"
    
    services:
      app:
        # The app service definition
      mysql:
        image: mysql:5.7
        volumes:
          - todo-mysql-data:/var/lib/mysql
    
    volumes:
      todo-mysql-data:
    
  11. 指定环境变量。

    version: "3.7"
    
    services:
      app:
        # The app service definition
      mysql:
        image: mysql:5.7
        volumes:
          - todo-mysql-data:/var/lib/mysql
        environment: 
          MYSQL_ROOT_PASSWORD: <your-password>
          MYSQL_DATABASE: todos
    
    volumes:
      todo-mysql-data:
    

此时,完整的 docker-compose.yml 如下所示:

version: "3.7"

services:
  app:
    image: node:20-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: <your-password>
      MYSQL_DB: todos

  mysql:
    image: mysql:5.7
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment: 
      MYSQL_ROOT_PASSWORD: <your-password>
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:

运行应用程序堆栈

现在,已拥有 docker-compose.yml 文件,请试运行看看。

  1. 确保应用和数据库的其他副本未运行。 在 Docker 扩展中,右键单击任何正在运行的容器,然后选择“移除”。 或者,在命令行中,使用 docker rm 命令,如前面的示例所示。

  2. 在“VS Code 资源管理器”中,右键单击 docker-compose.yml,然后选择“Compose Up”。 或者,在命令行中,使用此 docker 命令。

    docker-compose up -d
    

    利用参数 -d,可使命令在后台运行。

    你应该看到类似于以下结果的输出。

    [+] Building 0.0s (0/0)
    [+] Running 2/2
    ✔ Container app-app-1    Started                                                                                                       0.9s 
    ✔ Container app-mysql-1  Running
    

    已创建卷以及网络。 默认情况下,Docker Compose 会专门为应用程序堆栈创建网络。

  3. 在 Docker 扩展中,右键单击应用容器,然后选择“查看日志”。 若要从命令行查看日志,请使用 docker logs 命令。

    mysql_1  | 2019-10-03T03:07:16.083639Z 0 [Note] mysqld: ready for connections.
    mysql_1  | Version: '5.7.27'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
    app_1    | Connected to mysql db at host mysql
    app_1    | Listening on port 3000
    

    来自每个服务的日志交汇形成一个流。 通过此行为,可以监视与时间相关的问题。

    服务名称显示在行的开头,以帮助区分消息。 若要查看特定服务的日志,可以将服务名称添加到日志命令的末尾。

  4. 此时,你应能够打开应用。 在浏览器中输入 http://localhost:3000

使用完这些容器后,可以简单地将它们全部移除。

  • 在“VS Code 资源管理器”中,右键单击 docker-compose.yml,然后选择“Compose Down” 。
  • 在命令行上,运行 docker-compose down

容器停止。 网络已移除。

默认情况下,不会移除 compose 文件中已命名的卷。 如果要移除卷,请运行 docker-compose down --volumes

清理资源

你在本系列教程中使用的先决条件可用于未来的 Docker 开发。 无需删除或卸载任何内容。

后续步骤

在本教程中,你学习了多容器应用和 Docker Compose。 Docker Compose 有助于显著简化多服务应用程序的定义和共享。

下面是一些可能对你有用的资源: