多容器應用程式

到目前為止,你一直在使用單容器應用程式。但是,現在你將嚮應用程式堆疊中新增 MySQL。經常會出現以下問題:“MySQL 將在哪裡執行?是將其安裝在同一個容器中,還是單獨執行?”一般來說,每個容器都應該做好一件事。以下是單獨執行容器的一些原因:

  • 你很可能需要以不同於資料庫的方式擴充套件 API 和前端。
  • 分離的容器允許你獨立地進行版本控制和更新版本。
  • 雖然你可以在本地使用容器來執行資料庫,但在生產環境中,你可能希望使用託管的資料庫服務。那時你就不想將資料庫引擎與你的應用程式一起釋出了。
  • 執行多個程序需要一個程序管理器(容器只啟動一個程序),這會增加容器啟動/關閉的複雜性。

還有更多原因。因此,如下圖所示,最好在多個容器中執行您的應用程式。

Todo App connected to MySQL container

容器網路

請記住,預設情況下,容器是隔離執行的,對同一臺機器上的其他程序或容器一無所知。那麼,你如何讓一個容器與另一個容器通訊呢?答案是網路。如果你將兩個容器放在同一個網路上,它們就可以相互通訊。

啟動 MySQL

有兩種方法可以將容器置於網路上:

  • 在啟動容器時分配網路。
  • 將一個已在執行的容器連線到一個網路。

在接下來的步驟中,你將首先建立網路,然後在啟動時附加 MySQL 容器。

  1. 建立網路。

    $ docker network create todo-app
    
  2. 啟動一個 MySQL 容器並將其附加到網路上。你還將定義一些環境變數,資料庫將使用這些變數來初始化資料庫。要了解有關 MySQL 環境變數的更多資訊,請參閱 MySQL Docker Hub 列表中的“環境變數”部分。

    $ docker run -d \
        --network todo-app --network-alias mysql \
        -v todo-mysql-data:/var/lib/mysql \
        -e MYSQL_ROOT_PASSWORD=secret \
        -e MYSQL_DATABASE=todos \
        mysql:8.0
    
    $ docker run -d `
        --network todo-app --network-alias mysql `
        -v todo-mysql-data:/var/lib/mysql `
        -e MYSQL_ROOT_PASSWORD=secret `
        -e MYSQL_DATABASE=todos `
        mysql:8.0
    $ docker run -d ^
        --network todo-app --network-alias mysql ^
        -v todo-mysql-data:/var/lib/mysql ^
        -e MYSQL_ROOT_PASSWORD=secret ^
        -e MYSQL_DATABASE=todos ^
        mysql:8.0
    

    在前面的命令中,你可以看到 --network-alias 標誌。在後面的部分,你將學到更多關於這個標誌的資訊。

    提示

    你會注意到在上面的命令中有一個名為 todo-mysql-data 的卷被掛載到 /var/lib/mysql,這是 MySQL 儲存其資料的地方。然而,你從未執行過 docker volume create 命令。Docker 識別出你想使用一個命名卷,並自動為你建立了一個。

  3. 要確認資料庫已啟動並執行,請連線到資料庫並驗證其是否連線成功。

    $ docker exec -it <mysql-container-id> mysql -u root -p
    

    當出現密碼提示時,輸入 secret。在 MySQL shell 中,列出資料庫並驗證你看到了 todos 資料庫。

    mysql> SHOW DATABASES;
    

    你應該看到類似這樣的輸出:

    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    | todos              |
    +--------------------+
    5 rows in set (0.00 sec)
  4. 退出 MySQL shell 以返回到你機器上的 shell。

    mysql> exit
    

    你現在有了一個 todos 資料庫,它已經準備好供你使用了。

連線到 MySQL

既然你知道 MySQL 已經啟動並執行,你就可以使用它了。但是,你該如何使用它呢?如果你在同一個網路上執行另一個容器,你如何找到這個容器?記住,每個容器都有自己的 IP 地址。

為了回答上述問題並更好地理解容器網路,你將使用 nicolaka/netshoot 容器,該容器附帶了許多用於故障排除或除錯網路問題的有用工具。

  1. 使用 nicolaka/netshoot 映象啟動一個新容器。確保將其連線到同一個網路。

    $ docker run -it --network todo-app nicolaka/netshoot
    
  2. 在容器內部,你將使用 dig 命令,這是一個有用的 DNS 工具。你將查詢主機名 mysql 的 IP 地址。

    $ dig mysql
    

    你應該得到如下輸出。

    ; <<>> DiG 9.18.8 <<>> mysql
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162
    ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
    
    ;; QUESTION SECTION:
    ;mysql.				IN	A
    
    ;; ANSWER SECTION:
    mysql.			600	IN	A	172.23.0.2
    
    ;; Query time: 0 msec
    ;; SERVER: 127.0.0.11#53(127.0.0.11)
    ;; WHEN: Tue Oct 01 23:47:24 UTC 2019
    ;; MSG SIZE  rcvd: 44

    在“ANSWER SECTION”(應答部分)中,你會看到一條 A 記錄,其中 mysql 解析為 172.23.0.2(你的 IP 地址很可能會有一個不同的值)。雖然 mysql 通常不是一個有效的主機名,但 Docker 能夠將其解析為擁有該網路別名的容器的 IP 地址。記住,你之前使用了 --network-alias

    這意味著你的應用程式只需要連線到一個名為 mysql 的主機,它就可以與資料庫通訊了。

使用 MySQL 執行你的應用程式

todo 應用程式支援設定一些環境變數來指定 MySQL 連線設定。它們是:

  • MYSQL_HOST - 正在執行的 MySQL 伺服器的主機名
  • MYSQL_USER - 用於連線的使用者名稱
  • MYSQL_PASSWORD - 用於連線的密碼
  • MYSQL_DB - 連線後使用的資料庫
注意

雖然使用環境變數來設定連線設定在開發中通常被接受,但在生產環境中執行應用程式時,這是非常不鼓勵的。Docker 前安全主管 Diogo Monica 寫了一篇很棒的部落格文章,解釋了其中的原因。

一個更安全的機制是使用你的容器編排框架提供的秘密(secret)支援。在大多數情況下,這些秘密會作為檔案掛載到正在執行的容器中。你會看到許多應用程式(包括 MySQL 映象和 todo 應用程式)也支援帶有 _FILE 字尾的環境變數,以指向包含該變數的檔案。

例如,設定 MYSQL_PASSWORD_FILE 變數將使應用程式使用所引用檔案的內容作為連線密碼。Docker 不會對這些環境變數提供任何特殊支援。你的應用程式需要知道去查詢該變數並獲取檔案內容。

你現在可以啟動你的開發就緒容器了。

  1. 指定前面提到的每個環境變數,並將容器連線到你的應用網路。確保在執行此命令時,你位於 getting-started-app 目錄中。

    $ docker run -dp 127.0.0.1:3000:3000 \
      -w /app -v "$(pwd):/app" \
      --network todo-app \
      -e MYSQL_HOST=mysql \
      -e MYSQL_USER=root \
      -e MYSQL_PASSWORD=secret \
      -e MYSQL_DB=todos \
      node:18-alpine \
      sh -c "yarn install && yarn run dev"
    

    在 Windows 中,請在 PowerShell 中執行此命令。

    $ docker run -dp 127.0.0.1:3000:3000 `
      -w /app -v "$(pwd):/app" `
      --network todo-app `
      -e MYSQL_HOST=mysql `
      -e MYSQL_USER=root `
      -e MYSQL_PASSWORD=secret `
      -e MYSQL_DB=todos `
      node:18-alpine `
      sh -c "yarn install && yarn run dev"

    在 Windows 中,在命令提示符中執行此命令。

    $ docker run -dp 127.0.0.1:3000:3000 ^
      -w /app -v "%cd%:/app" ^
      --network todo-app ^
      -e MYSQL_HOST=mysql ^
      -e MYSQL_USER=root ^
      -e MYSQL_PASSWORD=secret ^
      -e MYSQL_DB=todos ^
      node:18-alpine ^
      sh -c "yarn install && yarn run dev"
    
    $ docker run -dp 127.0.0.1:3000:3000 \
      -w //app -v "/$(pwd):/app" \
      --network todo-app \
      -e MYSQL_HOST=mysql \
      -e MYSQL_USER=root \
      -e MYSQL_PASSWORD=secret \
      -e MYSQL_DB=todos \
      node:18-alpine \
      sh -c "yarn install && yarn run dev"
    
  2. 如果你檢視容器的日誌(docker logs -f <container-id>),你應該會看到一條類似下面的訊息,表明它正在使用 mysql 資料庫。

    $ nodemon src/index.js
    [nodemon] 2.0.20
    [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. 在瀏覽器中開啟該應用程式,並向你的待辦事項列表中新增幾個專案。

  4. 連線到 mysql 資料庫,並證明這些專案正在被寫入資料庫。記住,密碼是 secret

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

    在 mysql shell 中,執行以下命令:

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

    你的表格看起來會有所不同,因為它包含你的專案。但是,你應該看到它們儲存在那裡。

摘要

至此,你已經擁有一個應用程式,它現在將其資料儲存在運行於獨立容器中的外部資料庫中。你還學習了一點關於容器網路和使用 DNS 進行服務發現的知識。

相關資訊

後續步驟

你很可能開始對啟動這個應用程式所需做的所有事情感到有些不知所措。你必須建立一個網路,啟動容器,指定所有的環境變數,暴露埠等等。這需要記住的東西太多了,而且無疑使得將它傳遞給別人變得更加困難。

在下一節中,你將學習 Docker Compose。使用 Docker Compose,你可以更輕鬆地共享你的應用程式堆疊,並讓其他人透過一個簡單、單一的命令來啟動它們。

使用 Docker Compose