iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 16
0
Cloud Native

30 天準備 LPI DevOps Tools Engineer 證照系列 第 16

[Day 16] Docker (2)

昨天介紹了 Docker 的基本概念,以及跟著 Get Started 教學文件編寫 Dockerfile、建立映像檔並執行。在從容器層級進入服務層級之前,先看一下如何利用 registry 來分享建立好的映像檔。

利用 registry 分享映像檔

Registry 是存放儲存庫 (repository) 的地方,而一個儲存庫可有多個映像檔(以標籤 tag 區別之),類似的概念已經在 Vagrant 中看過了。和 Vagrant 一樣,Docker 也有一個公有的 registry 叫 Docker Hub https://hub.docker.com,可創建帳號並存放自己建置的映像檔以供自己或他人使用。

這裡使用 Docker Hub 作為存放映像檔的 registry,在企業內部也可以使用自建的私有 registry。請先到 Docker Hub 申請一個帳號,接下來要在本機使用 Docker CLI 進行登入,指令為 docker login。預設 registry 為 Docker Hub,所以直接輸入使用者名稱及帳號登入即可。

接下來要將映像檔給予儲存庫名稱及標籤,並加上使用者名稱,指令格式為:

$ docker tag image username/repository:tag

將昨天的映像檔 friendlyhello 儲存庫命名為 get-started,標籤為 part2,要加上自己的使用者名稱,例如:

$ docker tag friendlyhello odie1111/get-started:part2

docker image ls 查看,會看到剛才新 tag 的映像檔,它的 REPOSITORY 是使用者名稱加上儲存庫名稱,ID 會和 friendlyhello 相同,因為內容是一樣的。

接下來把這個映像檔推到已登入的 registry (Docker Hub),指令為

$ docker push odie1111/get-started:part2

刪除本地的 get-started:part2 映像檔,並以 docker run 來執行此映像檔,證明若此映像檔不存在於本機, Docker 會到 Docker Hub 拉回。

$ docker image rm odie1111/get-started:part2
$ docker run -d -p 4000:80 odie1111/get-started:part2

以瀏覽器拜訪 http://localhost:4000 確認映像檔已被拉回本機並執行。

執行服務

Get Started 實作的第二部分是服務,文件中說要先安裝 Docker Compose 這個工具,在 Docker for MacDocker for Windows 已經內建,Linux 請參考 https://github.com/docker/compose/releases 安裝。

在一個分散式的應用程式中,應用程式的各個組成部分 (different pieces of the app) 被稱為「服務」。服務就只是「生產環境中的容器」(container in production),一個服務只會運行一個映像檔,但它會規範這個映像檔要如何被執行,諸如用那些 port,為了足夠承載容量或流量,需要啟動多少個容器實體等等。擴展一個服務會改變執行該服務軟體的容器實例 (instance) 數量,以分配給該行程更多的運算資源。用來定義、執行、擴展服務的設定檔是 docker-compse.yml。接續昨天的範例,下面是這個應用程式中用到的 docker-compose.yml 的內容:

version: "3"
services:
  web:
    # replace username/repo:tag with your name and image details
    image: username/repo:tag
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "4000:80"
    networks:
      - webnet
networks:
  webnet:

這個 docker-compse.yml 中定義了一個服務,命名為 web,它使用的映像檔案定義在第 5 行,請填入昨天建好的映像檔,可以用 friendlyhello 或者是剛才的 <username>/get-started:part2。它會跑 5 個容器實例,每個容器限制使用 CPU 10% 及 50MB 的記憶體資源,如果有容器失敗會立刻重啟。它把 web 這個服務的 80 port 綁定到 host 的 4000 port 並使用預設的 load-balanced 網路作為這個服務的網路,命名為 webnet,它會把所有容器的 80 port 導到這個服務的 80 port。

接下來我們就可以來執行這個應用程式了,但首先要先執行 docker swarm init 讓本機成為 swarm 模式中的節點,否則下一個指令會報錯,之後會再介紹為什麼要先執行這個指令。

現在執行下列這個指令,並觀察其輸出:

$ docker stack deploy -c docker-compose.yml getstartedlab
Creating network getstartedlab_webnet
Creating service getstartedlab_web

-c--compose-file 的意義,表示 compose-file 的路徑。

給這個應用程式(或服務堆疊)一個名稱 getstartedlab。先以 docker stack ls 列出目前的服務堆疊,結果如下:

NAME                SERVICES            ORCHESTRATOR
getstartedlab       1                   Swarm

目前這個應用程式只有一個服務,稱為 web(在 docker-compose.yml 的第 3 行)。以 docker service ls 查看目前的服務,服務名稱會前綴應用程式的名稱,這裡 web 服務的名稱為 getstartedlab_web

ID                  NAME                MODE                REPLICAS            IMAGE                  PORTS
sd64tsvcaan3        getstartedlab_web   replicated          5/5                 friendlyhello:latest   *:4000->80/tcp

在服務中運行的單一容器稱為一個 task,以 docker service ps <service> 來查看,會看到有 5 個 task,就是剛才在 docker-compose.yml 定義的服務 replicas 數量,每個 task 有自己的 ID 及名稱。

ID                  NAME                      IMAGE                  NODE                    DESIRED STATE       CURRENT STATE            ERROR                         PORTS
h9knbu1ko6mc        getstartedlab_web.1       friendlyhello:latest   linuxkit-025000000001   Running             Running 10 minutes ago
dyj5wl6kaahz        getstartedlab_web.2       friendlyhello:latest   linuxkit-025000000001   Running             Running 10 minutes ago
dotr2xofz5tk        getstartedlab_web.3       friendlyhello:latest   linuxkit-025000000001   Running             Running 10 minutes ago
rkx62zzm033p        getstartedlab_web.4       friendlyhello:latest   linuxkit-025000000001   Running             Running 10 minutes ago
20ri7n0qv0y3        getstartedlab_web.5       friendlyhello:latest   linuxkit-025000000001   Running             Running 10 minutes ago

也可以用 docker container ls 查看,但這裡的 CONTAINER ID 和上面的 ID 會不一樣。

CONTAINER ID        IMAGE                  COMMAND             CREATED             STATUS              PORTS               NAMES
bde56f5772d1        friendlyhello:latest   "python app.py"     33 minutes ago      Up 33 minutes       80/tcp              getstartedlab_web.3.dotr2xofz5tk361po2fxbz3m8
5c541b44fca0        friendlyhello:latest   "python app.py"     35 minutes ago      Up 35 minutes       80/tcp              getstartedlab_web.1.h9knbu1ko6mc5sakopecdhjy2
18de0b3a3996        friendlyhello:latest   "python app.py"     35 minutes ago      Up 35 minutes       80/tcp              getstartedlab_web.5.20ri7n0qv0y3bplbmv4yoowul
a715e44fdf05        friendlyhello:latest   "python app.py"     35 minutes ago      Up 35 minutes       80/tcp              getstartedlab_web.4.rkx62zzm033pujz10jqxaqz1e
52ca4494c847        friendlyhello:latest   "python app.py"     35 minutes ago      Up 35 minutes       80/tcp              getstartedlab_web.2.dyj5wl6kaahzajz0tjlbtrp9w

一樣以瀏覽器連接 http://localhost:8000,並重新整理瀏覽器若干次,會看到畫面中 Hostname 欄位改變,證實連接到不同容器中的應用程式。

現在來擴展應用程式。調整 docker-compose.ymlweb 服務 replicas 的值,並重新執行 docker stack deploy -c docker-compose.yml getstartedlab,接著以上述指令查看,應該會動態調整容器數量。

現在將應用程式卸除,指令為 docker stack rm getstartedlab,並將本機脫離 swarm 模式,指令為 docker swarm leave --force

Docker Swarm

我們在第二部分 docker stack 用到 docker swarm init 指令,先介紹什麼是 swarm。swarm 是一組運行 Docker 並加入同一個叢集的機器,一個 swarm 會包含一台 swarm manager 及其他作為 worker 的節點。swarm manager 負責處理對這個 swarm 所進行的 docker 操作,以及授權其他機器加入這個 swarm 成為 worker。

接下來要將應用程式部署到擁有多個節點的叢集。文件中利用 Docker Machine 來建立 swarm 叢集所需的節點。Docker Machine 是一台執行 Docker 的虛擬機,因為我們要建立一個多節點的 swarm 叢集,所以需要建立多台 Docker Machine。我們先看一下文件中的叢集架構,首先利用 Docker Machine 建立兩台虛擬機,其中一台作為 swarm manager,另一台是 swarm worker,而應用程式的部署則是在本機上對 swarm manager 進行操作。

要如何在本機對 swarm manager 進行操作,這裡有兩個方式,第一個方法是利用 docker-machine ssh <machine> <command>,透過 docker-machine 替我們以 SSH 方式連進 swarm manager 執行指令,第二個方式是設置環境變數,使得在本機執行 docker 指令就等同在 swarm manger 上執行。第二個方式其實也就是之前提過在 macOS 或 Windows 利用 Docker Toolbox 執行 docker 的作法。這兩種方式的主要差異點,在於第二個方式不需要處理將 docker-compose.yml 這類型的設定檔,由本機複製到 swarm manager 的問題。

因為應試能力目標中有提到 Docker Machine,所以我們先跟著文件使用 Docker Machine 來建立 swarm 叢集。但其實我們手上已經有了建立好的虛擬機,應該也可以嘗試直接使用這些虛擬機來建立,之後有機會再來試驗。

利用 Docker Machine 建立 swarm 叢集

因為 Docker Machine 是虛擬機,會需要 hypervisor 支援,所以下面的操作都是在本機上進行,本機上的 hypervisor 一樣是 VirtualBox。和 Docker Compose 一樣,在 Linux 上必須並外安裝,若使用 Docker for Mac 或 Docker for Windows 則已內建,安裝請參考 https://docs.docker.com/machine/install-machine/

首先建立兩台虛擬機,分別命名為 myvm1myvm2--driver virtualbox 表示使用 VirtualBox 作為 hypervisor。

$ docker-machine create --driver virtualbox myvm1
$ docker-machine create --driver virtualbox myvm2

使用 docker-machine ls 查看目前建立的虛擬機器及其 IP。

$ docker-machine ls
NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
myvm1   -        virtualbox   Running   tcp://192.168.99.100:2376           v18.06.1-ce
myvm2   -        virtualbox   Running   tcp://192.168.99.101:2376           v18.06.1-ce

現在有了兩台虛擬機,接下來要來建立 swarm 叢集。首先以 docker swarm init 來指定 myvm1 作為 swarm manager,這個指令是在 myvm1 中操作的,所以利用 docker-machine ssh 來連進 myvm1,必須提供 myvm1 的 IP 作為參數,因為虛擬機會有若干不同的網路介面及 IP,必須指定一個 IP 讓其在 swarm 網路中使用。範例及輸出如下:

$ docker-machine ssh myvm1 "docker swarm init --advertise-addr 192.168.99.100"
Swarm initialized: current node (drc80qsjzoh7sqnwemc3k93t1) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-58lxdqep9apdprne95g0yjss7jbv4o3fjrnjrn0a4d2ypg3diz-5ixr698rxucrzp106s4471d0o 192.168.99.100:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

接下來將 myvm2 加入這個 swarm 中,請看剛才的輸出內容,會有加入 swarm 所需的 token 訊息。

$ docker-machine ssh myvm2 "docker swarm join --token \
SWMTKN-1-58lxdqep9apdprne95g0yjss7jbv4o3fjrnjrn0a4d2ypg3diz-5ixr698rxucrzp106s4471d0o 192.168.99.100:2377"

This node joined a swarm as a worker.

在 swarm manager 中可以透過 docker node ls 來查看目前參與各 swarm 的節點,輸出如下:

$ docker-machine ssh myvm1 "docker node ls"
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
drc80qsjzoh7sqnwemc3k93t1 *   myvm1               Ready               Active              Leader              18.06.1-ce
z7qijmsvizgxfpi1s0zvd14ts     myvm2               Ready               Active                                  18.06.1-ce

如果某一個 worker 想要離開 swarm,在該 worker 中執行 docker swarm leave

以上提到的是第一種方法,透過 docker-machine ssh 來執行指令。第二種方法則是設定環境變數,先使用 docker-machine env myvm1 查看關於 myvm1 的變數。

export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/Users/odie/.docker/machine/machines/myvm1"
export DOCKER_MACHINE_NAME="myvm1"
# Run this command to configure your shell:
# eval $(docker-machine env myvm1)

根據輸出,執行 eval $(docker-machine env myvm1) 來設定環境變數,即可在本機使用 docker 指令,但作用於 myvm1。例如執行 docker node ls,其輸出結果會和剛才執行 docker-machine ssh myvm1 "docker node ls" 的結果相同。如果要取消環境變數的設定,讓 docker 指令直接作用於本機,執行 eval $(docker-machine env -u),或者直接另開一個新的 shell,因環境變數的設定只會作用於當前的 shell 之中。

在 swarm cluster 中部署應用程式

現在在剛建好的 swarm 叢集中部署應用程式,其實跟在單個節點的 docker swarm 是一樣的,必須透過 swarm manager 來部署,而剛才已經在本機設定過 shell,可以直接對 myvm1,也就是這個 swarm cluster 的 manager 執行 docker 指令。在本機上執行下列指令:

$ docker stack deploy -c docker-compose.yml getstartedlab

這裡有一個地方要注意,就是 docker-compose.ymlweb 服務所使用的映像檔,如果指定原本在本機上的映像檔,因為現在 docker 指令是在 myvm1 上執行,會找不到該映像檔,請把設定檔的映像檔指向上傳到 Docker Hub 中的位置,也就是 <username>/get-started:part-2。因為要下載映像檔,這個指定會花比較多的時間執行。用 docker service ps getstartedlab_web 來查看這個服務的部署狀況:

$ docker service ps getstartedlab_web
ID                  NAME                  IMAGE                        NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
prsht6hxidw4        getstartedlab_web.1   odie1111/get-started:part2   myvm2               Running             Running about a minute ago
tm1yy0ilfcwe        getstartedlab_web.2   odie1111/get-started:part2   myvm1               Running             Running about a minute ago
ykoascfsay79        getstartedlab_web.3   odie1111/get-started:part2   myvm2               Running             Running about a minute ago
hdvbdp5i1pix        getstartedlab_web.4   odie1111/get-started:part2   myvm1               Running             Running about a minute ago
lx61el7zqon2        getstartedlab_web.5   odie1111/get-started:part2   myvm2               Running             Running about a minute ago

請注意這個服務的 5 個 replica 會分布在兩個 swarm 節點上。要以瀏覽器訪問此服務,可選擇連接兩個節點任一的 4000 port。這個 swarm 使用的網路是 load balancing,不管連向那個節點,刷新頁面,應該會發現一共有 5 個不同的容器出現,即使該容器並不在訪問的節點上。(因為連到那個容器是隨機的(有可能會考慮 load balancing),有可能某個容器一直不會連接到。)

接下來可以試驗調整服務 replicas 的數量,或者新建一個 node 加入這個 docker swarm。試驗結束後,依剛才的順序反向回復環境,首先將服務卸除。

$ docker stack rm getstartedlab

下一步取消 shell 的環境變數。

$ eval $(docker-machine env -u)

將 myvm2 及 myvm1 脫離 docker swarm。

$ docker-machine ssh myvm2 "docker swarm leave"
$ docker-machine ssh myvm1 "docker swarm leave --force"

停止 myvm1 及 myvm2 兩台虛擬機。

$ docker-machine stop myvm1
$ docker-machine stop myvm2

若要將機器再次重啟,使用 docker-machine start <machine> 指令,或者用 docker-machine rm <machine> 來移除此虛擬機。因為教學文件還會用到這兩台機器,請先保留它們。

小結

今天首先瞭解了 registry 的作用,將昨天建立好的映像檔傳送到 Docker Hub,Docker 公開的 registry。接下來使用 docker-compose.yml 在單個節點的 swarm 部署應用程式。之後利用 Docker Machine 建立了一個具有兩個節點的 swarm cluster,並在其中部署了應用程式。明天會將應用程式加入其他服務,使其在功能上更為完整,成為具多個服務的服務堆疊 (service stack)。


上一篇
[Day 15] Docker (1)
下一篇
[Day 17] Docker (3)
系列文
30 天準備 LPI DevOps Tools Engineer 證照30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言