多容器應用程式
到目前為止,你一直在處理單容器應用程式。現在,你將要把 MySQL 加入到應用程式堆疊中。這時通常會產生一個疑問:「MySQL 要在哪裡執行?是把它安裝在同一個容器中,還是分開執行?」一般而言,每個容器應該只做一件事,並且把它做好。以下是將容器分開執行的幾個原因:
- 很有可能你需要對 API 和前端進行不同於資料庫的擴展(Scale)。
- 獨立的容器讓你能夠隔離地進行版本控制和版本更新。
- 雖然你在本地端開發時可能會使用容器來執行資料庫,但在生產環境中,你可能更傾向於使用受管資料庫服務。那樣一來,你就不需要把資料庫引擎與應用程式打包在一起部署。
- 執行多個處理序(Process)需要一個處理序管理器(因為容器預設只啟動一個處理序),這會增加容器啟動和關閉的複雜度。
除此之外還有更多理由。因此,如同下方的圖示,將應用程式拆分為多個容器執行是最好的做法。

容器網路
請記住,容器預設是隔離的,它們對同一台機器上的其他處理序或容器一無所知。那麼,該如何讓一個容器與另一個容器通訊呢?答案就是網路(Networking)。只要將兩個容器放置在同一個網路中,它們就能夠互相通訊。
啟動 MySQL
將容器加入網路有兩種方式:
- 在啟動容器時指定網路。
- 將已經在執行的容器連接到網路。
在接下來的步驟中,你將會先建立一個網路,然後在啟動時將 MySQL 容器附加到該網路。
建立網路。
$ docker network create todo-app啟動一個 MySQL 容器並將其附加到網路。你還需要定義一些資料庫用來初始化的環境變數。若要深入了解 MySQL 環境變數,請參閱 MySQL Docker Hub 頁面中的「環境變數 (Environment Variables)」章節。
$ 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的磁碟區(Volume),它被掛載到/var/lib/mysql,這是 MySQL 儲存資料的地方。然而,你並沒有事先執行docker volume create指令。Docker 會自動識別到你想使用具名磁碟區,並為你自動建立。若要確認資料庫已成功啟動並執行,請嘗試連接資料庫以驗證是否能順利連線。
$ 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)退出 MySQL Shell 以返回你機器上的 Shell。
mysql> exit現在你已經有了
todos資料庫,且已準備就緒供你使用。
連接至 MySQL
既然你知道 MySQL 已經順利執行,接下來就可以開始使用它了。但該怎麼連線呢?如果你在同一個網路中執行另一個容器,該如何找到該容器?請記住,每個容器都有屬於自己的 IP 位址。
為了回答上述問題並更好地理解容器網路,你將會使用 nicolaka/netshoot 容器,它內建了許多對網路問題進行故障排除或除錯時非常實用的工具。
使用
nicolaka/netshoot映像檔啟動一個新容器。請確保將其連接到同一個網路。$ docker run -it --network todo-app nicolaka/netshoot在容器內部,你將使用
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」中,你會看到一筆關於
mysql的A記錄,它指向172.23.0.2(你的 IP 位址可能會有不同)。雖然mysql通常不是一個有效的主機名稱,但 Docker 成功地將其解析為擁有該網路別名 (network alias) 的容器 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 本身並不支援這些特定的環境變數,而是你的應用程式需要具備讀取該變數並取得檔案內容的能力。
現在你可以啟動準備好開發環境的容器了。
指定上述的每一個環境變數,並將容器連接到你的應用程式網路。請確保在執行此指令時,你位於
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"如果你查看容器的日誌 (
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在瀏覽器中開啟應用程式,並在待辦清單中新增幾項內容。
連接至 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 進行服務探索 (Service Discovery) 的概念。
相關資訊
下一步
你可能開始覺得啟動此應用程式所需的一切步驟有點令人不知所措。你需要建立網路、啟動容器、指定所有環境變數、曝露通訊埠等等。這有很多內容需要記憶,而且顯然增加了將專案交接給他人的難度。
在下一節中,你將學習 Docker Compose。有了 Docker Compose,你可以更輕鬆地分享你的應用程式堆疊,並讓其他人透過一個簡單的指令就能將其啟動。
使用 Docker Compose