多容器應用程式Multi-container apps

到目前為止,您已在使用單一容器應用程式。Up to this point, you've been working with single-container apps. 但是,您現在會將 MySQL 新增至應用程式堆疊。But, you'll now add MySQL to the application stack. 下列問題通常會發生-「MySQL 會在哪裡執行?The following question often arises - "Where will MySQL run? 將它安裝在相同的容器中,或分別執行它?」Install it in the same container or run it separately?" 一般情況下, 每個容器都應該做一件事,並妥善處理。In general, each container should do one thing and do it well. 有幾個原因:A few reasons:

  • 您很可能需要以不同于資料庫的方式調整 Api 和前端There's a good chance you'd have to scale APIs and front-ends differently than databases
  • 個別容器可讓您以隔離方式進行版本和更新版本Separate containers let you version and update versions in isolation
  • 雖然您可以在本機使用資料庫的容器,但是您可能會想要在生產環境中使用資料庫的受控服務。While you may use a container for the database locally, you may want to use a managed service for the database in production. 您不想要使用您的應用程式來傳送資料庫引擎。You don't want to ship your database engine with your app then.
  • 執行多個進程將需要進程管理員 (容器只會啟動一個進程) ,這會增加容器啟動/關機的複雜度。Running multiple processes will require a process manager (the container only starts one process), which adds complexity to container startup/shutdown.

還有其他原因。And there are more reasons. 因此,您會更新您的應用程式,使其運作方式如下:So, you will update your application to work like this:

Todo 應用程式已連線至 MySQL 容器

容器網路服務Container networking

請記住,容器依預設會在隔離的情況下執行,且不會知道同一部電腦上其他進程或容器的任何相關資訊。Remember that containers, by default, run in isolation and don't know anything about other processes or containers on the same machine. 那麼,您要如何讓一個容器與另一個容器交談?So, how do you allow one container to talk to another? 答案是 網路功能。The answer is networking. 現在,您不必是網路工程師 (個歡呼! ) 。Now, you don't have to be a network engineer (hooray!). 只要記住此規則 .。。Simply remember this rule...

注意

如果有兩個容器位於相同的網路上,則可以互相溝通。If two containers are on the same network, they can talk to each other. 如果不是,則無法。If they aren't, they can't.

啟動 MySQLStart MySQL

有兩種方式可在網路上放置容器:在啟動時指派,或連接現有的容器。There are two ways to put a container on a network: assign it at start or connect an existing container. 目前,您將會先建立網路,並在啟動時附加 MySQL 容器。For now, you will create the network first and attach the MySQL container at startup.

  1. 建立網路。Create the network.

    docker network create todo-app
    
  2. 啟動 MySQL 容器並將它連結至網路。Start a MySQL container and attach it the network. 我們也會定義一些環境變數,資料庫將使用這些變數來初始化資料庫 (請參閱 MySQL Docker Hub 清單 中的「環境變數」一節) (\ ` 以 Windows PowerShell) 取代字元。We're also going to define a few environment variables that the database will use to initialize the database (see the "Environment Variables" section in the MySQL Docker Hub listing) (replace the \ characters with ` in Windows PowerShell).

    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:5.7
    

    您也會看到指定的 --network-alias 旗標。You'll also see you specified the --network-alias flag. 我們稍後會回頭討論。We'll come back to that in just a moment.

    提示

    您會注意到這裡會使用磁片區名稱並將它掛接到 todo-mysql-data ,也 /var/lib/mysql 就是 MySQL 儲存其資料的位置。You'll notice you're using a volume name todo-mysql-data here and mounting it at /var/lib/mysql, which is where MySQL stores its data. 不過,您永遠不會執行 docker volume create 命令。However, you never ran a docker volume create command. Docker 會辨識出您想要使用命名磁片區,並自動為您建立一個磁片區。Docker recognizes that you want to use a named volume and creates one automatically for you.

  3. 若要確認資料庫已啟動並執行,請連線到資料庫並確認它是否連接。To confirm you have the database up and running, connect to the database and verify it connects.

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

    當密碼提示出現時,請輸入 secretWhen the password prompt comes up, type in secret. 在 MySQL shell 中,列出資料庫並確認您看到 todos 資料庫。In the MySQL shell, list the databases and verify you see the todos database.

    mysql> SHOW DATABASES;
    

    您應該會看到如下輸出:You should see output that looks like this:

    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    | todos              |
    +--------------------+
    5 rows in set (0.00 sec)
    

    萬歲!Hooray! 您擁有 todos 資料庫,而且已準備好可供使用!You have your todos database and it's ready for use!

連線到 MySQLConnect to MySQL

現在您知道 MySQL 已啟動並執行,讓我們來使用!Now that you know MySQL is up and running, let's use it! 但是,問題是 .。。如何?But, the question is... how? 如果您在相同網路上執行另一個容器,您要如何找到容器 (請記住,每個容器都有自己的 IP 位址) ?If you run another container on the same network, how do you find the container (remember each container has its own IP address)?

為了瞭解這一點,您將使用 nicolaka/netshoot 容器,此容器隨附 許多 適用于疑難排解或偵測網路問題的工具。To figure it out, you're going to make use of the nicolaka/netshoot container, which ships with a lot of tools that are useful for troubleshooting or debugging networking issues.

  1. 使用映射啟動新的容器 nicolaka/netshootStart a new container using the nicolaka/netshoot image. 請務必將它連接到相同的網路。Make sure to connect it to the same network.

    docker run -it --network todo-app nicolaka/netshoot
    
  2. 在容器內使用 dig 命令,這是有用的 DNS 工具。Inside the container, use the dig command, which is a useful DNS tool. 查詢主機名稱的 IP 位址 mysqlLook up the IP address for the hostname mysql.

    dig mysql
    

    您將得到如下所示的輸出 .。。And you'll get an output like this...

    ; <<>> DiG 9.14.1 <<>> 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
    

    在 [回應] 區段中,您會看到一個 A 記錄 mysql ,該記錄會解析成 172.23.0.2 (您的 IP 位址很可能會有不同的) 值。In the "ANSWER SECTION," you will see an A record for mysql that resolves to 172.23.0.2 (your IP address will most likely have a different value). 雖然 mysql 通常不是有效的主機名稱,但 Docker 能夠將它解析成具有該網路別名的容器 IP 位址 (記住您稍 --network-alias 早使用的旗標? ) 。While mysql isn't normally a valid hostname, Docker was able to resolve it to the IP address of the container that had that network alias (remember the --network-alias flag you used earlier?).

    這表示 .。。您的應用程式只需要連接到名為的主機 mysql ,它就會與資料庫溝通!What this means is... your app only simply needs to connect to a host named mysql and it'll talk to the database! 它的速度不如這樣簡單!It doesn't get much simpler than that!

使用 MySQL 執行您的應用程式Run your app with MySQL

Todo 應用程式支援設定一些環境變數來指定 MySQL 連線設定。The todo app supports the setting of a few environment variables to specify MySQL connection settings. 這些包括:They are:

  • MYSQL_HOST -正在執行之 MySQL 伺服器的主機名稱MYSQL_HOST - the hostname for the running MySQL server
  • MYSQL_USER -用於連接的使用者名稱MYSQL_USER - the username to use for the connection
  • MYSQL_PASSWORD -用於連接的密碼MYSQL_PASSWORD - the password to use for the connection
  • MYSQL_DB -連接後要使用的資料庫MYSQL_DB - the database to use once connected

警告

透過環境變數設定連接設定雖然使用環境變數來設定連線設定通常可以進行開發,但是在生產環境中執行應用程式時,強烈建議您不要這麼做。Setting Connection Settings via Environment Variables While using environment variables to set connection settings is generally okay for development, it is highly discouraged when running applications in production. 若要瞭解原因,請參閱 為何不應針對秘密資料使用環境變數To understand why, see Why you shouldn't use environment variables for secret data. 更安全的機制是使用容器協調流程架構所提供的秘密支援。A more secure mechanism is to use the secret support provided by your container orchestration framework. 在大部分情況下,這些秘密會以檔案的形式掛接在執行中的容器中。In most cases, these secrets are mounted as files in the running container. 您會看到許多應用程式 (包括 MySQL 映射和 todo 應用程式) 也支援具有尾碼的 env 變數, _FILE 以指向包含檔案的檔案。You'll see many apps (including the MySQL image and the todo app) also support env vars with a _FILE suffix to point to a file containing the file. 例如,設定 MYSQL_PASSWORD_FILE var 會導致應用程式使用所參考檔案的內容做為連接密碼。As an example, setting the MYSQL_PASSWORD_FILE var will cause the app to use the contents of the referenced file as the connection password. Docker 不會進行任何動作來支援這些環境變數。Docker doesn't do anything to support these env vars. 您的應用程式必須知道,才能尋找變數並取得檔案內容。Your app will need to know to look for the variable and get the file contents.

在所有說明的情況下,啟動開發就緒的容器!With all of that explained, start your dev-ready container!

  1. 指定上述的每個環境變數,並將容器連接到您的應用程式網路 (以 \ ` Windows PowerShell) 取代中的字元。Specify each of the environment variables above, and connect the container to your app network (replace the \ characters with ` in Windows PowerShell).

    docker run -dp 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:12-alpine \
      sh -c "yarn install && yarn run dev"
    
  2. 如果您查看容器 (的記錄 docker logs <container-id>) ,您應該會看到訊息,指出其正在使用 MySQL 資料庫。If you look at the logs for the container (docker logs <container-id>), you should see a message indicating it's using the MySQL database.

    # 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. 在您的瀏覽器中開啟應用程式,並將一些專案新增至待辦事項清單。Open the app in your browser and add a few items to your todo list.

  4. 連接至 MySQL 資料庫,並證明正在將這些專案寫入資料庫中。Connect to the MySQL database and prove that the items are being written to the database. 請記住,密碼是 秘密Remember, the password is secret.

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

    在 MySQL shell 中執行下列命令:And in the MySQL shell, run the following:

    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 |
    +--------------------------------------+--------------------+-----------+
    

    很明顯地,您的資料表看起來會不同,因為它有您的專案。Obviously, your table will look different because it has your items. 但是,您應該會看到它們儲存在該處!But, you should see them stored there!

如果您快速查看 Docker 延伸模組,您會看到有兩個應用程式容器正在執行。If you take a quick look at the Docker extension, you'll see that you have two app containers running. 但是,並沒有真正的指示,它們會在單一應用程式中群組在一起。But, there's no real indication that they're grouped together in a single app. 您將瞭解如何讓它更好!You'll see how to make that better shortly!

顯示兩個已取消群組之應用程式容器的 Docker 擴充功能

概括回顧Recap

至此,您的應用程式現在會將其資料儲存在另一個容器中執行的外部資料庫。At this point, you have an application that now stores its data in an external database running in a separate container. 您稍微瞭解容器網路功能,並瞭解如何使用 DNS 來執行服務探索。You learned a little bit about container networking and saw how service discovery can be performed using DNS.

但是,當您啟動此應用程式時,很有可能會開始覺得您需要做的事情。But, there's a good chance you are starting to feel a little overwhelmed with everything you need to do to start up this application. 您必須建立網路、啟動容器、指定所有環境變數、公開端口,以及更多功能!You have to create a network, start containers, specify all of the environment variables, expose ports, and more! 這是很多要記住的,而且一定會讓事情更難傳遞給其他人。That's a lot to remember and it's certainly making things harder to pass along to someone else.

在下一節中,我們將討論 Docker Compose。In the next section, we'll talk about Docker Compose. 有了 Docker Compose,您就能以更簡單的方式共用應用程式堆疊,讓其他人使用單一 (和簡單的) 命令來加速處理!With Docker Compose, you can share your application stacks in a much easier way and let others spin them up with a single (and simple) command!

後續步驟Next steps

繼續進行本教學課程!Continue with the tutorial!