Docker 實戰入門

概述

Docker 是一個提供開發, 運送, 以及運行應用的開放平台。 妥善地使用 Docker, 你可以將你的應用從基礎設施分離, 並且如管理應用般的管理基礎設施。 Docker 可讓你更快速的輸送你的程式碼, 更快速的測試, 以及更快速的部署, 讓開發到運行之間的週期更加縮短。

Docker 藉由使用工作流程以及工具來融合 kernel 容器化功能, 讓你可以管理以及部署你的應用。

Docker 容器可以直接地被使用在 Kubernetes, 這讓 Docker 可以很簡單的被運行在 Kubernetes Engine, 在學習 Docker 的一些重要的核心知識後, 你將擁有可以開始開發 Kubernetes 以及容器化應用的技能組合。


學習目標

在本教程中, 你將會學習到以下:

  • 如何建立, 運行, 以及 debug Docker 容器
  • 如何從 Docker Hub 以及 Google Container Registry 拉下 Docker 鏡像
  • 如何將 Docker 鏡像推上 Google Container Registry

事前準備

這是個入門等級的教程。 只有一點點, 或是完全沒有 Docker 相關知識也是 okay 的。 建議熟悉 Cloud Shell 以及其命令列工具, 但非必要。 如果你在尋找在一個範圍內更進階的學習素材的話, 可以參考以下的教程:

如果你已經準備好了, 往下滑開始設定教程環境吧




設定及要求

在你按下 Start Lab 按鈕之前

詳讀所有的教學。 Labs 是有時間限制的, 而且你不可以停止時間倒數。 倒數計時器在你按下 Start Lab 按鈕後開始倒數, 上面顯示的時間為你還能使用 Cloud 資源的時間。

Qwiklabs 的手把手環境, 讓你可以在真實環境中來操作進行 Qwiklabs 上提供的課程, 而不是在一個模擬或是展示的環境。 我們透過提供你一個全新的、暫時的帳號密碼, 在計時器歸零之前, 你可以用來登入並存取 Google Cloud Platform。

你需要什麼?

要完成這個 lab, 你需要:

  • 一個一般的網路瀏覽器(推薦 Chrome)
  • 完成這個 lab 的時間

備註: 如果你已經有你自己的個人 GCP 帳號或專案,請不要使用在這一個 lab

現在你已經開始你的 lab, 你將會登入 Google Cloud Shell 主控台, 然後開啟命令列工具

如何開始你的 lab, 然後登入 Console?

  • 按下 Start Lab 按鈕。 如果你需要付費, 會有一個彈出視窗來讓你選擇付費的方式。 在左方你會看到一個面板, 上面有暫時的帳號密碼, 你必須使用這些帳號密碼在此次 lab

  • 複製 username, 然後點擊 Open Google Console。 Lab 會開啟另外一個視窗, 顯示選擇帳號的頁面

tip: 開啟一個全新的視窗,然後跟原本的頁面並排

  • 選擇帳號頁面, 點擊 Use Another Account

  • 登入頁面開啟, 貼上之前複製的 username 以及 password, 然後貼上

重要:必須使用之前於 Connection Details 面板 取得的帳號密碼,不要使用你自己的 Qwiklabs 帳號密碼。 如果你有自己的 GCP 帳號, 請不要用在這裡(避免產生費用)

  • 點擊並通過接下來的頁面:
    • 接受 terms 以及 conditions
    • 不要增加 recovery optoinstwo factor authentication (因為這只是一個臨時帳號)
    • 不要註冊免費體驗

稍待一些時候, GCP 控制台將會在這個視窗開啟。

**注意:** 按下左上方位於 `Google Cloud Platform` 隔壁的 `Navigation menu`, 你可以瀏覽選單, 裡面有一系列的 GCP 產品以及服務


啟動 Google Cloud Shell

Google Cloud Shell 是載有開發工具的虛擬機器。 它提供了5GB的 home 資料夾, 並且運行在 Google Cloud 上。 Google Cloud Shell 讓你可以利用 command-line 存取 GCP 資源

  • GCP 控制台, 右上的工具列,點擊 Open Cloud Shell 按鈕

  • 在打開的對話框裡, 按下 START CLOUD SHELL:

你可以立即按下 START CLOUD SHELL 當對話視窗打開。

連結並提供環境會需要一點時間。 當你連結成功, 這代表你已成功獲得授權, 且此專案已被設為你的專案ID, 例如:

gcloudGoogle Cloud Platform 的 command-line 工具, 他已事先被安裝在 Cloud Shell 並且支援自動補齊

使用這個 command, 你可以列出有效帳戶名稱:

gcloud auth list

輸出:

Credentialed accounts:
- <myaccount>@<mydomain>.com (active)

範例輸出:

Credentialed accounts:
- google1623327_student@qwiklabs.net

你可以使用以下 command 來列出專案 ID

gcloud config list project

輸出:

[core]
project = <project_ID>

範例輸出:

[core]
project = qwiklabs-gcp-44776a13dea667a6

gcloud 的完整文件可以參閱 Google Cloud gcloud Overview




Hello World

打開 Cloud Shell 並輸入以下指令來運行一個 hello world 容器

docker run hello world

(輸出)

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
9db2ca6ccae0: Pull complete
Digest: sha256:4b8ff392a12ed9ea17784bd3c9a8b1fa3299cac44aca35a85c90c5e3c7afacdc
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

這個簡單的容器回傳 Hello from Docker! 到你的螢幕上。 儘管指令很簡單, 但仔細看看在輸出中, 執行步驟的數量。 Docker daemon 搜尋名為 hello-world 的鏡像, 但沒有在本地端找到, 所以從名為 Docker Hub 的公開 registry 拉下這個鏡像, 並且使用這個鏡像建立了容器, 最後運行容器。

執行以下的指令檢視從 Docker Hub 拉下的 Docker 鏡像

docker images

(輸出)

REPOSITORY     TAG      IMAGE ID       CREATED       SIZE
hello-world latest 1815c82652c0 6 days ago 1.84 kB

這是一個從 Docker Hub 公開 registry 被拉下的鏡像。 鏡像 ID 為 SHA256 加密 格式 - 這個欄位明確的指定被提供的鏡像。 當 docker daemon 無法在本地端找到鏡像, 預設會自動地從公開 registry 尋找鏡像。 讓我們再次運行容器:

docker run hello-world

(輸出)

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
...

注意到, 當你第二次執行這個指令, docker daemon 在本地端找到了這個鏡像並且使用這個鏡像運行容器。 不需要再從 Docker Hub 拉下鏡像。

最後, 執行以下指令來檢視運行中的容器:

docker ps

(輸出)

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

目前並沒有運行中的容器。 你之前運行的 hello-world 容器已經退出了。 如果要檢視所有的容器, 包含已經結束運行的容器, 可以執行 docker ps -a:

docker ps -a

(輸出)

CONTAINER ID      IMAGE           COMMAND      ...     NAMES
6027ecba1c39 hello-world "/hello" ... elated_knuth
358d709b8341 hello-world "/hello" ... epic_lewin

這會顯示 Container ID, 這是一個由 Docker 建立的 UUID, 用以辨識容器, 除此之外, 還有一些容器的資訊。 容器的 Names 也是隨機產生的, 但你也可以使用 docker run --name [container-name] hello-world 來指定它




建立

現在讓我們來建立一個基於 node 簡單應用的 Docker 鏡像。 執行以下指令來建立並且切換到這個名為 test 的資料夾

mkdir test && cd test

建立一個 Dockerfile:

cat > Dockerfile <<EOF
# Use an official Node runtime as the parent image
FROM node:6

# Set the working directory in the container to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

# Make the container's port 80 available to the outside world
EXPOSE 80

# Run app.js using node when the container launches
CMD ["node", "app.js"]
EOF

這個檔案指示 Docker daemon 該怎樣建立這個鏡像

  • 第一行指定了基礎鏡像, 在這個範例中為 node 版本 6 的官方 Docker 鏡像
  • 第二行, 我們指定了容器內的工作(目前)目錄
  • 第三行, 我們當層資料夾內的內容(容器外), 複製到容器內
  • 最後, 我們暴露了容器的 port, 所以此容器可經由該 port 被存取, 然後運行 node 的指令來運行應用

花一些時間看一下 Dockerfile command references 來了解 Dockerfile 中每一行的意思

現在, 你將寫入 node 應用, 且在那之後你將建立鏡像

執行以下指令來建立 node 應用

cat > app.js <<EOF
const http = require('http');

const hostname = '0.0.0.0';
const port = 80;

const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World\n');
});

server.listen(port, hostname, () => {
console.log('Server running at http://%s:%s/', hostname, port);
});

process.on('SIGINT', function() {
console.log('Caught interrupt signal and will exit');
process.exit();
});
EOF

這是一個簡單的 HTTP server, 監聽 port 80 且回傳 “Hello world.”

現在讓我們來建立鏡像吧

再次注意, "." 這個符號代表目前的資料夾為止, 所以你必須要在有 Dockerfile 的資料夾底下運行這個指令

docker build -t node-app:0.1 .

這個指令可能會花個幾分鐘才會執行完畢。 當它執行結束, 你的輸出應會如下:

Sending build context to Docker daemon 3.072 kB
Step 1 : FROM node:6
6: Pulling from library/node
...
...
...
Step 5 : CMD node app.js
---> Running in b677acd1edd9
---> f166cd2a9f10
Removing intermediate container b677acd1edd9
Successfully built f166cd2a9f10

-t 是為使用 name:tag 語法來命名鏡像的 tag, 鏡像名稱為 node-app, tag0.1, 高度建議當建立鏡像時一定要使用 tag 。 如果你沒指定 tag, tag 將預設為 latest, 這會讓你更難分辨新舊鏡像。 也請注意到, 在鏡像被建立的過程中, Dockerfile 中的每一行指令在中間的容器層中造成什麼樣的結果。

現在, 執行以下指令來檢視建立的鏡像:

docker images

輸出應如下:

REPOSITORY     TAG      IMAGE ID        CREATED            SIZE
node-app 0.1 f166cd2a9f10 25 seconds ago 656.2 MB
node 6 5a767079e3df 15 hours ago 656.2 MB
hello-world latest 1815c82652c0 6 days ago 1.84 kB

注意到, node 為基礎鏡像, 而 node-app 為你建立的鏡像。 在移除 node 鏡像之前, 你必須要先移除 node-app 。 鏡像的大小跟虛擬機比起來的話小了非常多。 其他版本的 node 鏡像, 像是 node:slim 以及 node:alpine 提供了更小的體積, 更好的攜帶性。 降低鏡像大小的主題, 你可探索更進階的主題。 你可以在 官方倉庫 檢視所有的版本




運行

在這個章節中, 執行這個程式碼, 使用之前建立的鏡像運行容器:

docker run -p 4000:80 --name my-app node-app:0.1

(輸出)

Server running at http://0.0.0.0:80/

--name flag 讓你可以自由的命名你的容器。 -p 指定容器映射本機端的 port 4000 到容器內的 port 80, 而現在你應可經由 http://localhost:4000 存取 server。 如果沒有做 port 映射, 你將無法經由 localhost 來存取容器

打開另外一個終端機 (在 Cloud Shell, 點擊 + icon), 然後測試 server

curl http://localhost:4000

(輸出)

Hello World

第一個終端機視窗開啟多久, 這個容器就會運行多久。 如果你想要將容器運行在背景中 (不會綁住終端機), 你需要指定 -d flag

關閉第一個終端機視窗, 然後執行以下指令來停止並移除容器:

docker stop my-app && docker rm my-app

現在執行以下指令來開始在背景運行另外一個容器:

docker run -p 4000:80 --name my-app -d node-app:0.1
docker ps

(輸出)

CONTAINER ID   IMAGE          COMMAND        CREATED         ...  NAMES
xxxxxxxxxxxx node-app:0.1 "node app.js" 16 seconds ago ... my-app

docker ps 的輸出, 我們可以看到容器正在運行中。 你可以執行 docker logs [container_id] 指令來檢視 logs

小技巧: 你不需要完整的容器 ID, 只要前幾個字是獨一無二足以代表這個容器。 舉例來說, 你可以執行 docker ps 17b, 如果你的容器 ID 是 17bcaca6f...

docker logs [container_id]

(輸出)

Server running at http://0.0.0.0:80/

現在讓我們來修改應用。 在 Cloud Shell, 打開之前建立的 test 資料夾:

cd test

使用你的編輯器 (比如 namo 或 vim) 來編輯 app.js, 然後將 “Hello World” 替換成其他字串:

....
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Welcome to Cloud\n');
});
....

建立這個新的鏡像, 並給予 tag 0.2:

docker build -t node-app:0.2 .

(輸出)

Step 1/5 : FROM node:6
---> 67ed1f028e71
Step 2/5 : WORKDIR /app
---> Using cache
---> a39c2d73c807
Step 3/5 : ADD . /app
---> a7087887091f
Removing intermediate container 99bc0526ebb0
Step 4/5 : EXPOSE 80
---> Running in 7882a1e84596
---> 80f5220880d9
Removing intermediate container 7882a1e84596
Step 5/5 : CMD node app.js
---> Running in f2646b475210
---> 5c3edbac6421
Removing intermediate container f2646b475210
Successfully built 5c3edbac6421
Successfully tagged node-app:0.2

注意到, 在 step 2我們使用了以存在的緩存層。 從 step 3 開始, 層被修改了因為我們在 app.js 中做了一些變更

使用新的鏡像版本啟動另外一個容器。 注意到我們是如何的映射本機端的 port 8080, 而不是 port 80, 我們不能在使用 port 4000 因為它已經在使用中了。

docker run -p 8080:80 --name my-app-2 -d node-app:0.2
docker ps

(輸出)

CONTAINER ID     IMAGE             COMMAND            CREATED             
xxxxxxxxxxxx node-app:0.2 "node app.js" 53 seconds ago ...
xxxxxxxxxxxx node-app:0.1 "node app.js" About an hour ago ...

測試容器

curl http://localhost:8080

(輸出)

Welcome to Cloud

現在測試第一個容器

curl http://localhost:4000

(輸出)

Hello World



Debug

現在我們已經熟悉如何建立以及運行容器, 來學習一下一些 debugging 的方法。
你可以使用 docker logs [container_id 來檢視容器的日誌, 如果你想要檢視即時的日誌輸出的話, 可以使用 -f 選項

docker logs -f [container_id]

(輸出)

Server running at http://0.0.0.0:80/

有時, 你可能會想要啟動一個容器內的 Bash, 你可以使用 docker exec 來做到這件事。 打開另外一個終端機 (在 Cloud Shell, 點擊 + 圖示), 然後執行以下指令:

docker exec -it [container_id] bash

-it flags 讓你可以分配一個虛擬的 TTY 給容器, 保持輸入開啟並與之互動。 注意到目前 bash 正運行在 Dockerfile 內指定的 WORKDIR 資料夾 (/app), 現在你有一個容器內的互動式的 shell 可以用來 debug 了

(輸出)

root@xxxxxxxxxxxx:/app#

檢視資料夾

ls

(輸出)

Dockerfile  app.js
root@xxxxxxxxxxxx:/app#

若要離開 Bash, 在 Bash 中輸入 exit:

exit

你可以使用以下指令來檢視容器的元資料

docker inspect [container_id]

(輸出)

[
{
"Id": "xxxxxxxxxxxx....",
"Created": "2017-08-07T22:57:49.261726726Z",
"Path": "node",
"Args": [
"app.js"
],
...

使用 --format 從回傳的 JSON 當中檢查特定的欄位。 例如說:

docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [container_id]

(輸出)

192.168.9.3

務必到以下的連結去參考更多有關 debugging 的資訊




發佈

現在, 你將推送你的鏡像到 Google Container Registry (gcr)。 在那之後, 你將會將本地的容器以及鏡像都刪除來模擬一個全新的環境, 然後從遠端拉取鏡像並啟動容器。 這將是 Docker 容器可攜性的最佳示範。

要推送鏡像到你的私人 gcr 的 registry, 你需要 tag registry 的名稱到這個鏡像。 格式就像是 [hostname]/[project-id]/[image]:[tag]

GCR 的格式:

  • [hostname] = gcr.io
  • [project-id] = 你的 project ID
  • [image] = 你的鏡像名稱
  • [tag] = 你選擇的 tag, 若無特別指定, 預設為 “latest”

你可以執行以下指令來找到你的 project ID

gcloud config list project

(輸出)

[core]
project = [project-id]

Your active configuration is: [default]

給予 tag node-app:0.2, 並將 [project-id] 替換成你的設定

docker tag node-app:0.2 gcr.io/[project-id]/node-app:0.2
docker images

(輸出)

REPOSITORY                      TAG         IMAGE ID          CREATED
node-app 0.2 76b3beef845e 22 hours ago
gcr.io/[project-id]/node-app 0.2 76b3beef845e 22 hours ago
node-app 0.1 f166cd2a9f10 26 hours ago
node 6 5a767079e3df 7 days ago
hello-world latest 1815c82652c0 7 weeks ago

將這個鏡像推送到 gcr, 記住要將 [project-id] 替換成你的設定

docker push gcr.io/[project-id]/node-app:0.2

(輸出)

The push refers to a repository [gcr.io/[project-id]/node-app]
057029400a4a: Pushed
342f14cb7e2b: Pushed
903087566d45: Pushed
99dac0782a63: Pushed
e6695624484e: Pushed
da59b99bbd3b: Pushed
5616a6292c16: Pushed
f3ed6cb59ab0: Pushed
654f45ecb7e3: Pushed
2c40c66f7667: Pushed
0.2: digest: sha256:25b8ebd7820515609517ec38dbca9086e1abef3750c0d2aff7f341407c743c46 size: 2419

從瀏覽器拜訪鏡像位址來確認鏡像已存在於 gcr, 你可以從主控台到 Tools > Container Registry 或是拜訪 http://gcr.io/[project-id]/node-app, 顯示頁面應如下:

現在讓我們測試這個鏡像。 你可以開啟一個新的 VM, ssh 到這個 VM, 然後安裝 gcloud, 但為求簡單化, 我們將簡單的移除所有的容器以及鏡像來模擬一個全新的環境。

停止並刪除所有的容器:

docker stop $(docker ps -q)
docker rm $(docker ps -aq)

在移除 node 鏡像之前, 你必須要移除它的子鏡像 (node:6 的), 運行已下指令, 記得將 [project-id] 替換成你自己的

docker rmi node-app:0.2 gcr.io/[project-id]/node-app node-app:0.1
docker rmi node:6
docker rmi $(docker images -aq) # remove remaining images
docker images

(輸出)

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

現在你應該有一個虛擬全新的環境。 拉取並運行鏡像。 記得將 [project-id] 替換成你自己的

docker pull gcr.io/[project-id]/node-app:0.2
docker run -p 4000:80 -d gcr.io/[project-id]/node-app:0.2
curl http://localhost:4000

(輸出)

Welcome to Cloud

測試進度

點擊 Check my progress 來確認目前的進度。如果你已經成功發布容器鏡像到 Container Registry, 你將獲得一個評價分數。

在這裏, 我們示範了容器的可攜帶性。 只要機器上有安裝 Docker (實機或虛擬機), 你可以從公開或私人的 registries 拉取鏡像並運行容器。 在你的機器上除了 Docker, 你不需要再安裝其他的應用相依。




恭喜

你已經完成本教程

Laravel - Digging Deeper - Queues (官方文件原子化翻譯筆記) Laradock 學習筆記

留言

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×