Linux伺服器 Linux伺服器

資工所專業課程上課教材

資工所專業課程 > 課程內容 > 專題 - Docker 容器實際操作

專題 - Docker 容器實際操作

上次更新日期 2021/01/xx

docker 的基礎操作之外,基本管理與實戰也是很重要的一環。上一章節談到的許多指令,其實並沒有談到如何管理, 大部分都是在簡單的操作而已,如何處理映像檔、資料交流與網路等,則沒有一個好的說明。這個章節就讓我們來聊聊如何簡單的管理, 以及進行一些基礎的實戰。

映像檔 (image) 與倉儲 (repository) 管理

  1. 使用官網的映像檔建立屬於自己的新映像檔:
    1. 使用容器,處理既有的映像檔資料:

      上一章我們曾經從官網 (docker hub) 下載過 centos 這個映像檔,但是,這個映像檔可能不是我們要的, 所以,我們可以針對這個系統進行加工的行為:

      # 1. 先檢查上週下載的映像檔是否還存在?
      # docker images
      REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
      centos       latest    300e315adb2f   3 weeks ago   209MB
      ubuntu       latest    f643c72bc252   5 weeks ago   72.9MB
      
      # 2. 啟動名為 centos_new 的容器,使用的就是這個 centos 映像檔,且執行 bash:
      # docker run -it --name centos_new centos bash
      [root@2a0e0964347a /]#  # 注意,特殊字元就是這個容器的獨一無二的 ID 喔!
      
      # 3. 檢查 (1)是否有網路 (2)是否有容量 (3)是否有 repository 設定
      [root@2a0e0964347a /]# ip addr show
      10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP...
          link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
          inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
             valid_lft forever preferred_lft forever
      
      [root@2a0e0964347a /]# df -h | grep -v tmpfs
      Filesystem               Size  Used Avail Use% Mounted on
      overlay                   10G  5.1G  5.0G  51% /
      shm                       64M     0   64M   0% /dev/shm
      /dev/mapper/centos-root   10G  5.1G  5.0G  51% /etc/hosts
      
      [root@2a0e0964347a /]# yum repolist
      Failed to set locale, defaulting to C.UTF-8
      repo id                  repo name
      appstream                CentOS Linux 8 - AppStream
      baseos                   CentOS Linux 8 - BaseOS
      extras                   CentOS Linux 8 - Extras
      
      # 4. 開始安裝幾個常見的需要的軟體,提供 Linux 用戶練習
      [root@2a0e0964347a /]# yum install vim-enhanced bash-completion net-tools wget bind-utils
      
      # 5. 離開並觀察容器資訊
      [root@2a0e0964347a /]# exit
      # docker ps -a
      CONTAINER ID IMAGE   COMMAND  CREATED          STATUS                     NAMES
      2a0e0964347a centos  "bash"   13 minutes ago   Exited (0) 3 minutes ago   centos_new
      

      假設這個時候就將我們的環境建置妥當,這個容器的內容想要做成映像檔,方便未來提供大家大量使用。

    2. 使用 commit 上傳容器成為新的映像檔:

      上傳容器成為新的映像檔,方式真的很像 git 喔!基本語法是這樣的:

      # docker commit --help
      
      Usage:  docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
      
      Create a new image from a container's changes
      
      Options:
        -a, --author string    Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
        -c, --change list      Apply Dockerfile instruction to the created image
        -m, --message string   Commit message
        -p, --pause            Pause container during commit (default true)
      

      現在,我們處理 centos_new 成為新的映像檔,映像檔名稱為 centos8_inst 好了:

      # docker commit -m 'installed tools' -a 'vbird tsai' centos_new centos8_inst
      sha256:41e458d7a84c043869f4e3882b8a3cb121d57d69e366b4daebf4dfd067b9b881
      
      # docker images
      REPOSITORY     TAG       IMAGE ID       CREATED          SIZE
      centos8_inst   latest    41e458d7a84c   28 seconds ago   337MB
      centos         latest    300e315adb2f   3 weeks ago      209MB
      ubuntu         latest    f643c72bc252   5 weeks ago      72.9MB
      
    3. 使用新的映像檔來進行容器建置測試看看:
      # docker run -it --name centos_new2 centos8_inst bash
      [root@b7bb796a003d /]# rpm -q net-tools
      net-tools-2.0-0.52.20160912git.el8.x86_64
      [root@b7bb796a003d /]# exit
      

      確認是有安裝 net-tools 的!所以,我們未來啟用這個映像檔,就不需要重複那些安裝的行為等, 操作上面會比較快速!

  2. 將映像檔備份為 tarball 單一檔案,或由單一檔案復原:
    1. 映像檔的備份:

      假設上面這個 centos8_inst 映像檔整理得很不錯,所以我想要將這個映像檔備份下來, 這時,你可以使用 save 的功能來處理映像檔的備份喔。

      # docker save -o centos8_inst.tar centos8_inst
      # ll -h centos8_inst.tar
      -rw-------. 1 root root 331M  1月  4 02:30 centos8_inst.tar
      
      # tar -tvf centos8_inst.tar
      -rw-r--r-- 0/0       2178 2021-01-04 02:01 41e458d7a84c043869...4dfd067b9b881.json
      drwxr-xr-x 0/0          0 2021-01-04 02:01 789dbb27ef49925cb9...19c74344c4b20/
      -rw-r--r-- 0/0          3 2021-01-04 02:01 789dbb27ef49925cb9...19c74344c4b20/VERSION
      -rw-r--r-- 0/0       1456 2021-01-04 02:01 789dbb27ef49925cb9...19c74344c4b20/json
      -rw-r--r-- 0/0  129692672 2021-01-04 02:01 789dbb27ef49925cb9...19c74344c4b20/layer.tar
      drwxr-xr-x 0/0          0 2021-01-04 02:01 ba21ed01ee58f0af0f...1d02205b59ff0/
      -rw-r--r-- 0/0          3 2021-01-04 02:01 ba21ed01ee58f0af0f...1d02205b59ff0/VERSION
      -rw-r--r-- 0/0        406 2021-01-04 02:01 ba21ed01ee58f0af0f...1d02205b59ff0/json
      -rw-r--r-- 0/0  216524800 2021-01-04 02:01 ba21ed01ee58f0af0f...1d02205b59ff0/layer.tar
      -rw-r--r-- 0/0        285 1970-01-01 08:00 manifest.json
      -rw-r--r-- 0/0         95 1970-01-01 08:00 repositories
      
      這樣就可以壓製成為一個 tar 的檔案,雖然可以查閱該檔案內容,不過,除了 json 檔案之外, 想要知道詳細的檔案檔名資訊,可能還得要持續解開啊!總之,這樣我們就擁有一個備份檔案了。
    2. 由 tarball 復原映像檔:

      現在,假設你將 tarball 複製到其他 docker host,又或者是你的 image 不小心被自己殺掉了! 此時,如何使用備份檔案來回復系統呢?使用 load 即可。

      # 1. 先刪除使用 centos8_inst 的容器:
      # docker ps -adocker ps -a
      CONTAINER ID   IMAGE        COMMAND CREATED          STATUS                NAMES
      b7bb796a003d   centos8_inst "bash"  17 minutes ago   Exited (0) 16 minutes centos_new2
      2a0e0964347a   centos       "bash"  59 minutes ago   Exited (0) 49 minutes centos_new
      
      # docker rm centos_new2
      
      # 2. 刪除 centos8_inst 映像檔
      # docker rmi centos8_inst:latest
      Untagged: centos8_inst:latest
      Deleted: sha256:41e458d7a84c043869f4e3882b8a3cb121d57d69e366b4daebf4dfd067b9b881
      Deleted: sha256:d885f0d4608d43492385820afc027dac3d027289bd6c63a4a2e41407edb2b211
      
      # 3. 載入映像檔
      # docker image load -i centos8_inst.tar
      619782606bfb: Loading layer [=============================>]  129.7MB/129.7MB
      Loaded image: centos8_inst:latest
      
      # docker images
      REPOSITORY     TAG       IMAGE ID       CREATED          SIZE
      centos8_inst   latest    41e458d7a84c   40 minutes ago   337MB
      centos         latest    300e315adb2f   3 weeks ago      209MB
      ubuntu         latest    f643c72bc252   5 weeks ago      72.9MB
      
      這樣就可以進行映像檔的移動囉!
  3. 建立私有倉庫:
    1. 預設的倉庫為使用 docker.io 亦即是 docker hub 所提供的映像檔。你當然可以自己下載映像檔, 透過上面的方式整理好之後,以備份 tarball 的方式進行傳輸。但另一方面,你也可以自己設置私有倉庫, 讓你的用戶系統全部指向內部私有倉庫來進行映像檔的下載!這樣應該在傳輸上面,會比較快速一點哩!
    2. 下載與觀察私有倉庫映像檔:

      docker hub 有提供一個名為 registry 的映像檔,這個映像檔似乎就是提供倉庫運作的功能。我們先下載, 然後觀察一下這個映像檔的內容看看:

      # 1. 先搜尋有沒有 registry 這個映像檔
      # docker search registry
      NAME                   DESCRIPTION                      STARS OFFICIAL AUTOMATED
      registry               The Docker Registry 2.0 impl...  3153  [OK]
      distribution/registry  WARNING: NOT the registry of...  57             [OK]
      .....
      
      # 2. 開始下載:
      # docker image pull registry
      Using default tag: latest
      latest: Pulling from library/registry
      0a6724ff3fcd: Pull complete
      d550a247d74f: Pull complete
      1a938458ca36: Pull complete
      acd758c36fc9: Pull complete
      9af6d68b484a: Pull complete
      Digest: sha256:d5459fcb27aecc752520df4b492b08358....
      Status: Downloaded newer image for registry:latest
      docker.io/library/registry:latest
      
      # docker images
      REPOSITORY     TAG       IMAGE ID       CREATED       SIZE
      centos8_inst   latest    41e458d7a84c   2 hours ago   337MB
      registry       latest    678dfa38fcfa   2 weeks ago   26.2MB
      centos         latest    300e315adb2f   3 weeks ago   209MB
      ubuntu         latest    f643c72bc252   5 weeks ago   72.9MB
      
      # 3. 查看這個映像檔資訊
      # docker image inspect registry
      [
          {
              "Id": "sha256:678dfa38fcfa349ccbdb1b6d52ac113ace67d5746794b36dfbad9dd96a9d1c43",
              "RepoTags": [
                  "registry:latest"
      .....
              "DockerVersion": "19.03.12",
              "Author": "",
              "Config": {
                  "Hostname": "",
                  "Domainname": "",
                  "User": "",
                  "AttachStdin": false,
                  "AttachStdout": false,
                  "AttachStderr": false,
                  "ExposedPorts": {
                      "5000/tcp": {}
                  },
                  "Tty": false,
                  "OpenStdin": false,
                  "StdinOnce": false,
                  "Env": [
                      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
                  ],
                  "Cmd": [
                      "/etc/docker/registry/config.yml"
                  ],
                  "Image": "sha256:1d6cf98b1a921f8756b63c1103209dbe648e9dda42cf3ee2...",
                  "Volumes": {
                      "/var/lib/registry": {}
                  },
                  "WorkingDir": "",
                  "Entrypoint": [
                      "/entrypoint.sh"
                  ],
                  "OnBuild": null,
                  "Labels": null
              },
      
      .....
      
      會看到這個映像檔預社會開啟一個 port 5000 的埠口,提供使用者來處理。
    3. 建立 regitry 容器檔,且提供對外連線埠口:

      我們在上一個章節中,啟動的 centos1 容器中,可以透過 docker0 這個橋接界面進行網路設定, 因此,所有的 docker 容器都是可以具有 IP 位址的。問題是,這個 docker0 是在 Linux 作業系統內的虛擬橋接器, 預設是不給直接連線的。因此,我們得要透過 port mapping 的功能進行轉 port 的工作。

      docker 可以根據你啟動容器時的指令,來主動提供 port mapping 喔!整體流程架構也挺簡單的:

      # docker run -d -it -p hostport:dockerport \
      > -v volume --name dockername registry
      
      -d        讓 docker 以 daemon 的方式,直接啟動在背景持續執行
      -p hostport:dockerport 讓連線到本機 hostport 埠口的連線,導向 docker 的 dockerport 埠口
      -v volume 讓 docker 容器掛載某個本機的目錄,這樣可以持續經營
      

      上述的 -v 比較有趣,它的內容主要是針對本機目錄與 docker 內的目錄做連結, 最簡單的用法就是,讓本機的 /srv/registry 掛載到 docker 內的 /tmp/registry 目錄時, 可以使用『 -v /srv/registry:/tmp/registry 』這種格式來處理。

      現在,讓我們使用底下的機制來處理這個容器:

      • 使用 daemon 的方式啟動容器
      • 本機埠口 5000 對應到容器 port 5000 上面
      • 本機的 /srv/registry 掛載到容器的 /var/lib/registry 上
      • 容器的名稱為 myregistry
      # docker run -d -it -p 5000:5000 -v /srv/registry:/var/lib/registry \
      > --name myregistry registry
      
      # docker ps -a
      CONTAINER ID   IMAGE      COMMAND                 PORTS                    NAMES
      16c328633cc2   registry   "/entrypoint.sh /etc.." 0.0.0.0:5000->5000/tcp   myregistry
      2a0e0964347a   centos     "bash"                                           centos_new
      
      # iptables-save| grep -i docker
      :DOCKER - [0:0]
      :DOCKER-ISOLATION-STAGE-1 - [0:0]
      :DOCKER-ISOLATION-STAGE-2 - [0:0]
      :DOCKER-USER - [0:0]
      -A FORWARD -j DOCKER-USER
      -A FORWARD -j DOCKER-ISOLATION-STAGE-1
      -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
      -A FORWARD -o docker0 -j DOCKER
      -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
      -A FORWARD -i docker0 -o docker0 -j ACCEPT
      -A DOCKER -d 172.18.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT
      -A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
      -A DOCKER-ISOLATION-STAGE-1 -j RETURN
      -A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
      -A DOCKER-ISOLATION-STAGE-2 -j RETURN
      -A DOCKER-USER -j RETURN
      :DOCKER - [0:0]
      -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
      -A POSTROUTING -s 172.18.0.0/16 ! -o docker0 -j MASQUERADE
      -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
      -A DOCKER -i docker0 -j RETURN
      -A DOCKER ! -i docker0 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.18.0.2:5000
      

      我們可以很快速的發現到,剛剛建立的新的 myregistry 容器,他的 IP 應該是 172.18.0.2 才對,而且相關的防火牆對應也做好了, 相當簡單快速。

    4. 將本機的映像檔上傳到私有倉庫上:

      接下來,讓我們將本機的映像檔上傳到私有倉庫,以提供自己的夥伴們下載使用。處理的方式很簡單:

      # docker tag --help
      
      Usage:  docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
      
      Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
      

      那個 TARGET_IMAGE[:TAG] 如果是放置到外部倉庫上,而不是本機的映像檔資訊時,應該會寫成:

      registryhost/[username/]img_name[:tag]
      

      現在,請將 centos8_inst 製作出你本機 IP 的映像檔名稱:

      # 1. 觀察本機的映像檔
      # docker images
      REPOSITORY     TAG       IMAGE ID       CREATED       SIZE
      centos8_inst   latest    41e458d7a84c   3 hours ago   337MB
      registry       latest    678dfa38fcfa   2 weeks ago   26.2MB
      centos         latest    300e315adb2f   3 weeks ago   209MB
      ubuntu         latest    f643c72bc252   5 weeks ago   72.9MB
      
      # 2. 對上面的映像檔,製作出給本機 IP 對應的映像檔檔名:
      # ip addr show | grep 'inet '
          inet 127.0.0.1/8 scope host lo
          inet 192.168.40.200/24 brd 192.168.40.255 scope global noprefixroute ens3
          inet 172.17.200.254/24 brd 172.17.200.255 scope global noprefixroute ens7
          inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
          inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
      
      # docker tag centos8_inst 192.168.40.200:5000/centos8_inst
      # docker images
      REPOSITORY                         TAG       IMAGE ID       CREATED       SIZE
      192.168.40.200:5000/centos8_inst   latest    41e458d7a84c   4 hours ago   337MB
      centos8_inst                       latest    41e458d7a84c   4 hours ago   337MB
      

      製作好對應的映像檔參數後,就可以直接將映像檔上傳了:

      # docker push 192.168.40.200:5000/centos8_inst
      Using default tag: latest
      The push refers to repository [192.168.40.200:5000/centos8_inst]
      Get https://192.168.40.200:5000/v2/: http: server gave HTTP response to HTTPS client
      

      出現上面的問題,主要原因是 docker 的安全性問題,需要修改 docker 的設定檔,同意放行私有倉庫才行。 處理的方法如下:

      # 1. 修改 docker 設定檔:
      # vim /etc/docker/daemon.json
      {
       "live-restore": true,
       "group": "dockerroot",
       "insecure-registries": ["192.168.40.200:5000"]
      }
      
      # 2. 重新啟動 docker
      # systemctl restart docker
      
      # 3. 重新啟動 myregistry 容器
      # docker ps -a
      CONTAINER ID   IMAGE      COMMAND           CREATED    STATUS               NAMES
      13c2c53af60d   registry   "/entrypoint.."   6 minute   Exited (2) 46 se     myregistry
      2a0e0964347a   centos     "bash"            5 hours    Exited (0) 4 hou     centos_new
      
      # docker start myregistry
      
      # 4. 最後,再次上傳 images !
      # docker image push 192.168.40.200:5000/centos8_inst
      Using default tag: latest
      The push refers to repository [192.168.40.200:5000/centos8_inst]
      619782606bfb: Pushed
      2653d992f4ef: Pushed
      latest: digest: sha256:e22009d955f2dcd1cf99a6006569ec493a0624e5... size: 741
      
      # 5. 再看看剛剛提供給 docker 容器的目錄
      # ll /srv/registry/docker/registry/v2/repositories/
      drwxr-xr-x. 5 root root 55  1月  4 06:16 centos8_inst
      
  4. 完成底下的實做
    1. 用 student 的身份,在 server 上面,設計一隻名為 install_docker.yml 的 play book 檔案,主要目的是要讓 managed host 成為 docker host。需要的任務有:
      • 先上傳 docker 的 repository 檔案,才能夠做 yum 安裝
      • 完整的將 docker 安裝上去 (包括移除不要的軟體、安裝需要的軟體等)
      • 上傳需要的 /etc/docker/daemon.json 設定檔
      • 啟動且開機預設啟動 docker 軟體
    2. 前往 webserver1,使用 root 進行映像檔下載與容器處理:
      • 嘗試從 192.168.40.*:5000 下載你剛剛上傳的 image,並且觀察 image 的設定 (inspect)
      • 重新 tag 一個新的 image ,名稱為 centos8_my,來源就是上面這個映像檔
      • 啟動一個名為 mycentos8 的容器,使用 mycentos_my 的映像檔,且啟動到背景中
    3. 前往 webserver1,使用 root 執行映像檔重新建立:
      • 在 mycentos8 的容器當中,嘗試啟動 sshd 服務,你可能需要 (1)安裝 openssh-server (2)手動啟動 sshd (絕對路徑) (3)找不到 host key 的情境下,執行 ssh-keygen -A 產生 (4)重新執行 sshd 指令 (5)使用 netstat 檢查埠口。
      • 停止 mycentos8,將此容器製作成為映像檔,然後上傳到 192.168.40.*:5000 上面去。
    4. 在 server 的環境下進行如下的行為:
      • 將剛剛 webserver1 傳上來的映像檔,下載到本機上
      • 啟動一個容器,這個容器會有 port 2222 對應到 22 的功能,且會執行 sshd 喔!
      • 從本機上面使用 ssh 嘗試連線到 2222 ,看看會有什麼情況發生。

...

參考資料