Linux伺服器 Linux伺服器

資工所專業課程上課教材

資工所專業課程 > 課程內容 > 專題 - Docker 容器基礎實做

專題 - Docker 容器基礎實做

上次更新日期 2021/01/03

除了虛擬機器 (VM) 之外,其實目前還有很多輕量級的虛擬化技術存在,例如所謂的容器 (container) 技術! 在 Linux 上面有個名為 docker 的容器技術,可以讓我們很輕鬆的就完成許多的軟體佈署,而且不會浪費很多的系統資源, 相當有趣!

容器 (container) 與 Docker

  1. 什麼是 container (容器):
    1. 根據維基百科 (wiki) 對於 container 的說明,在容器出現之前,我們稱為『作業系統層虛擬化 (Operating system-level virtualization)』,基本上,就是在核心層級執行一組被獨立出來的程式,這個程式會被獨自隔離,且這個程式雖然可以取得系統的所有資源, 然而這個程式只能看到屬於他自己的部份而已。一般來說,這種虛擬化也被稱為容器化技術 (Containerization)。[1]
    2. 承上,這一隻被隔離的軟體實體,就被稱為是一個容器 (container),或者是虛擬專用伺服器 (virtual private servers)。 對於每個程序的擁有者與執行者來說,他們使用的伺服器感覺好像就是獨自運作一般 (似乎與 Host 本身無關的感覺)。
    3. 事實上,最早的容器起源,可以思考的就是 chroot 這個機制! 我們可以將所有的系統資源掛載到某個目錄,然後將他變成根目錄 (chroot) 來處理的意思!回想一下 Linux 課程, 當你使用 chroot 去救援系統時,事實上,你使用的核心以及系統資源 (/sys, /proc, /dev 等等重要資源),都還是屬於原有的系統的! 這就是容器!
    4. 因為容器使用了原有系統的資源,因此所需要的獨立的資源相對小很多! 與目前主流的 VM 不一樣。因為 VM 是一個『獨立的作業系統』,而 container 只是在原有系統核心下的一個被隔離的程序! 兩者本質差很多。因此,VM 可以模擬不同的作業系統 (在 Windows 模擬 Linux,或在 Linux 模擬 Windows),但是 container 只能運作與母系統相同的作業系統環境 (因為共用核心資源!)
    5. 完成底下的實做:
      1. 開啟 VM,並且進入救援模式的流程:
        • 透過各種方法,啟動你的一個 VM 系統
        • 注意看出該系統的埠口號碼,然後直接使用 remote-viewer 去做 spice 連線
        • 連線取得終端機之後,進行 [ctrl]+[alt]+[del] 重新開機
        • 重新開機過程中,在選單模式輸入 rd.break 進入救援模式中
      2. 尚未掛載系統資源的 chroot 實做:
        • 在救援模式中查閱 /sysroot 的裝置以及相關的掛載資訊
        • 將 /sysroot 重新掛載為可讀寫
        • 使用 chroot /sysroot 變更成為根目錄系統
        • 使用 df 或 fdisk -l 或 gdisk -l 等等,能不能取得你的裝置分割表?
      3. 掛載系統資源之後,才進行 chroot 的實做!
        • 退出 chroot,使用 mount --bind 的方式,掛載 /dev, /proc, /sys.. 到 /sysroot 的相關目錄
        • 再次 chroot /sysroot,再次使用 df, fdisk -l, gdisk -l 等等,確認裝置存在否?
        • 思考一下 chroot 的相關資源是這次的救援系統?還是你原本的系統?
        • 離開 chroot 後,重新開機,再正確的進入系統中。
  2. 什麼是 docker:
    1. Docker 是一個開放原始碼的專案,這個專案就是以 Linux container 為基本的開發環境。 主要緣起於 2013 年 dotCloud 公司的內部專案,後來太熱門,因此 dotCloud 公司甚至改名為 Docker Inc。 後來在 0.9 版本之後,更以 Google 開發的 Go 程式語言進行函式庫的開發,在 Linux 核心上面玩弄容器技術 (Linux Container, LXC)。
    2. Docker 的商標圖示:
    3. Docker 的角色與 VM 角色的差異:
    4. 如上圖所示,右側的 VM 啟動時,需要完整的底層作業系統執行 (Guest Operating System), 如果你只是想要執行 VM 上面的一個軟體 (App A),還是得要執行完整的 VM 才行,因此消耗系統資源比較多。至於左側的 Docker 容器, 因為與 Host Operation System 幾乎是共用核心與資源,因此只需要直接執行 App A 而已,無須 Guest Operating System 的支援, 因此資源損耗比較少,啟動的速度與佈署的速度都比較快速![2]
    5. 你可以把 docker 容器理解為一個沙箱 (sandbox),每個容器內可以執行一個應用程式,而不同的容器都是互相獨立隔離的。不過, 容器之間可以透過某些機制來進行溝通。容器的啟動與關閉相當快速,就好像你在作業系統當中執行一個簡單的應用程式一般。 另外,很多時候,一個容器內,大部分也只啟動一個應用程式而已。
  3. Docker 的名詞與概念:
    1. Docker 的運作,基本上包含了三個基本概念:
      • 映像檔 (image)
      • 容器 (container)
      • 倉庫 (repository)
    2. 映像檔, Image:
      映像檔就是一個唯讀的模板。一個映像檔可以包含一個完整的 CentOS 作業系統環境,裡面只安裝了 Apache 或者是使用者需要的其他應用程式。事實上,你可以將他想成是一個 VM 的原始磁碟這樣的概念即可。映像檔主要的目的就是在產生 Docker 容器。
    3. Docker 容器 (container)
      容器就是從映像檔執行起來的軟體實體。容器可以被啟動、開始、停止、刪除等等。每個容器都是獨立且互相隔離的。 你可以想像成,每個容器就是由映像檔快照出來的、被執行的系統的存在。且映像檔 (image) 是唯讀的, 容器則是透過某些特殊的檔案系統,讓它可以寫入,且不影響原本的映像檔。
    4. 倉庫 (repository)
      倉庫就是放置 image 的所在,倉庫有公開的有私有的,例如 github 或者是 docker 官網所提供的映像檔, 那就是公開的倉庫。如果你想要有自己公司內部,或者是自己測試用的內部倉庫,那就自己幹一個,也就是私有的倉庫了。
    5. 基本上,上面這三個 docker 的元件,可以使用底下的圖示來簡易的了解: 雖然圖上的倉庫使用了 Registry,根據大眾的說法,你可以將 Registry 與 Repository 在 docker 軟體上,視為相同的東西來使用即可。 我們這個專案的目的在於取得可以使用的 Container 元件,所以,我們所進行的任何動作,都應該是在 Docker_host 那部機器上而已, 只要安裝好 Docker daemon ,然後下載適當的 Images 之後,就可以啟用 Container ,接下來只要連結到 container 上, 我們就可以快速的架構好所需要的服務囉!

Docker 安裝啟用以及 Image 處理

  1. Docker 的安裝與啟動
    1. 現在的 docker:

      由於 docker 分為商業版與自由版,因此軟體名稱已經不太一樣。 另外,你可以自行下載 RPM 來安裝,不過,比較好的作法,應該還是直接從 docker 官網下載適當版本的軟體倉儲比較好。 docker 預先提供了幾個常見 Linux distribution 的軟體倉儲提供用戶使用,你可以從底下的網址列查看:

      我們預計在 CentOS 8 上面安裝,所以點選 centos 之後,就會看到 docker-ce.repo 這一個倉儲設定檔, 直接將它抓下來放置到 /etc/yum.repos.d/ 裡面即可:(註:CentOS 7 與 CentOS 6 均有內建 docker 的支援, CentOS 8 反而需要自行從 docker 官網下載處理呢!)

      # 1. 先處理 CentOS 8 的 repository 軟體倉儲
      # cd /etc/yum.repos.d/
      # wget https://download.docker.com/linux/centos/docker-ce.repo
      # yum repolist
      軟體庫 ID                 軟體庫名稱
      AppStream                 CentOS-8 - AppStream
      BaseOS                    CentOS-8 - Base
      docker-ce-stable          Docker CE Stable - x86_64   # 重點是要看到這個項目!
      extras                    CentOS-8 - Extras
      
      # 2. 再克服原本 CentOS8 與 Docker 相衝突的軟體 -- 主要是移除
      # yum remove runc  # 因為跟新軟體有衝突
      
      # 3. 這時,安裝 docker 才不會有問題喔!
      # yum install docker-ce docker-ce-cli containerd.io
      # rpm -qa | grep docker
      docker-ce-rootless-extras-20.10.1-3.el8.x86_64
      docker-ce-cli-20.10.1-3.el8.x86_64
      docker-ce-20.10.1-3.el8.x86_64
      
    2. 啟動 docker 的方法也很簡單,直接啟動 docker 這個服務即可:
      # systemctl start  docker
      # systemctl enable docker
      
      很快的,這樣就安裝好了,而且也啟動了 docker!接下來,就可以開始處理 docker 的各項任務了!
    3. 接下來開始觀察一下,當你安裝並成功啟動 docker 之後,系統會多一個網路界面來給 docker container 使用。 我們可以來觀察一下這個界面:
      # 1. 先觀察界面變化:
      # ip addr show
      ....
      7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN ...
          link/ether 02:42:50:0d:14:4a brd ff:ff:ff:ff:ff:ff
          inet 172.18.0.1/16 brd 172.18.255.255 scope global docker0
             valid_lft forever preferred_lft forever
      ....
      # 會多一個名為 docker0 的界面,且其 IP 會是 172.18.0.1 的 class B 的界面喔!
      
      # 2. 再觀察防火牆的變化
      # firewall-cmd --get-active-zones
      docker
        interfaces: docker0
      external
        interfaces: ens3
      internal
        interfaces: ens7
      libvirt
        interfaces: virbr0
      # 會多一個名為 docker 的領域,這是專門用來放行 docker 容器的連線
      
      # 3. 查看一下 docker zone 的內容為何:
      # firewall-cmd --zone=docker --list-all
      docker (active)
        target: ACCEPT
        icmp-block-inversion: no
        interfaces: docker0
        sources:
        services:
        ports:
        protocols:
        masquerade: no
        forward-ports:
        source-ports:
        icmp-blocks:
        rich rules:
      
      為了擔心啟用 docker 之後,反而導致系統被攻擊,因此,docker 會主動的加入一些防火牆機制!特別注意這個情況喔!
  2. 處理 docker image
    1. 前面提到,docker 最重要的其實就是那個類似 VM 磁碟的 image。目前你的系統是否擁有 image 呢? 可以使用底下的指令來觀察:
      # docker images
      REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
      
      看起來目前是沒有任何的 docker image 存在的,現在, 我們可以找到 docker 官網提供的 image 資訊嘛?可以的,這樣處理:
      # docker search 'keyword'
      # docker search centos
      NAME                       DESCRIPTION                  STARS OFFICIAL  AUTOMATED
      centos                     The official build of Cen    6342  [OK]
      ansible/centos7-ansible    Ansible on Centos7           132             [OK]
      consol/centos-xfce-vnc     Centos container with "he    124             [OK]
      jdeathe/centos-ssh         OpenSSH / Supervisor / EP    117             [OK]
      centos/systemd             systemd enabled base cont    92              [OK]
      centos/mysql-57-centos7    MySQL 5.7 SQL database se    86
      imagine10255/centos6-lnm   centos6-lnmp-php56           58              [OK]
      tutum/centos               Simple CentOS docker imag    46
      centos/postgresql-96-cen   PostgreSQL is an advanced    45
      kinogmt/centos-ssh         CentOS with SSH              29              [OK]
      pivotaldata/centos-gpdb-   CentOS image for GPDB dev    13
      guyton/centos6             From official centos6 con    10              [OK]
      nathonfowlie/centos-jre    Latest CentOS image with     8               [OK]
      centos/tools               Docker image that has sys    7               [OK]
      drecom/centos-ruby         centos ruby                  6               [OK]
      pivotaldata/centos         Base centos, freshened up    5
      darksheer/centos           Base Centos Image -- Upda    3               [OK]
      pivotaldata/centos-gcc-t   CentOS with a toolchain,     3
      pivotaldata/centos-mingw   Using the mingw toolchain    3
      blacklabelops/centos       CentOS Base Image! Built     1               [OK]
      mcnaughton/centos-base     centos base image            1               [OK]
      indigo/centos-maven        Vanilla CentOS 7 with Ora    1               [OK]
      smartentry/centos          centos with smartentry       0               [OK]
      pivotaldata/centos6.8-de   CentosOS 6.8 image for GP    0
      pivotaldata/centos7-dev    CentosOS 7 image for GPDB    0
      
      上述的資料中:
      • INDEX:主要的倉儲
      • NAME: image 的名稱,如果有 / 的,代表的是某個人或單位所提供的
      • DESCRIPTION: 單純的描述
      • STARS: 受歡迎程度,有點像是 FB 的『贊』數量
      • OFFICIAL: 是否為官網所提供的映像檔之意。
      • AUTOMATED: 是否允許使用者驗證映像檔來源與內容
    2. 下載映像檔到本地端:
      # docker pull docker.io/centos
      Using default tag: latest
      latest: Pulling from library/centos
      7a0437f04f83: Pull complete
      Digest: sha256:5528e8b1b1719d34604c87e11dcd1c0a20bedf46e83b5632cdeac91b8c04efc1
      Status: Downloaded newer image for centos:latest
      docker.io/library/centos:latest
      
      事實上,每個 image 可能有很多個版本,例如 centos 有 7 與 8 等不同版本,或者是 8.1~8.3 等版本, 因此,創造 images 的用戶,也可能提供多種不同的版本!如果沒有加上任何版本,預設會下載最新版。 如果你有意思要使用舊的版本,那就得要加上特定的版本號碼:
      # docker pull docker.io/centos:[tags]
      # docker images
      REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
      centos       latest    300e315adb2f   2 weeks ago   209MB
      
      如上所示,最後我們就有一個名為 docker.io/centos:latest 的 docker image 存在了!
    3. 另外,由於我們目前僅有註冊一個倉庫在 docker.io 當中,所以,上述的 image 下載,也可以略過 repository 的說明, 直接 pull 即可。例如:
      # docker pull centos
      
  3. 完成底下的實做:
    1. 搜尋 ubuntu 這個映像檔檔名,並找到官方提供的版本
    2. 下載 ubuntu 映像檔到本地端
    3. 查詢目前系統有的映像檔資料
    4. 查詢 http 伺服器映像檔,並且下載官方提供的映像檔資源,同時觀察下載後的結果。此行為之後的結果, 應該是會有點像底下這樣子:
      REPOSITORY          TAG        IMAGE ID         CREATED         SIZE
      docker.io/httpd     latest     d4a07e6ce470     2 weeks ago     132 MB
      docker.io/centos    latest     9f38484d220f     5 weeks ago     202 MB
      docker.io/ubuntu    latest     94e814e2efa8     5 weeks ago     88.9 MB
      
    5. 目前我們沒有要使用 httpd,請移除這個映像檔 (可以使用 docker rmi httpd 來處理)
    6. 最終,請找出 /var/lib/docker 底下容量最大的目錄,並比對 images 的容量,大概就能知道 image 是放在系統的哪個地方了。

使用 docker 容器

  1. 開始 docker 容器的啟用與簡易操作:
    1. 不要忘記,透過執行類似映像檔的快照行為,就可以啟動一個 docker 容器! 這個容器可以視為是一個軟體的實體~只是他快照到 docker 映像檔而已。
    2. 容器的啟動:容器的啟動直接透過 docker run 來處理!語法是:
      # docker run -it --name container_name image_name
      
      上述指令中:
      • -i:讓容器的標準輸入 (standard input) 維持在啟動的狀態
      • -t:讓 Docker 配置一個虛擬終端機,並綁定容器到標準輸入的狀態!因此, -it 通常是合在一起執行。
      • --name:配置一個名稱給容器,未來比較好記憶與操作!
      • image_name:就是映像檔的名稱,要注意,image_name 最好一定放在最後面喔!
    3. 實際執行的流程:
      # docker run -it --name centos1 centos
      [root@34c3c830df4e /]# <==此時就在 docker 的容器裡面了!
      

      此時你可以在 docker 產生的容器裡面隨意逛逛~不過需要留意的是,這個在 docker 內的 centos ,基本上,使用的資源都是 host 本機的! 因此使用 uname -r 或者是 df 或是 mount 等等,看到的大多都是 host 本機的資源!所以,基本上,你是無法使用 fdisk 等功能的! 這部份要先理解。

      # 1. 先執行 df -h 查看一下系統的分割資訊
      # df -h
      Filesystem               Size  Used Avail Use% Mounted on
      overlay                   10G  5.2G  4.9G  52% /
      tmpfs                     64M     0   64M   0% /dev
      tmpfs                    914M     0  914M   0% /sys/fs/cgroup
      shm                       64M     0   64M   0% /dev/shm
      /dev/mapper/centos-root   10G  5.2G  4.9G  52% /etc/hosts
      tmpfs                    914M     0  914M   0% /proc/asound
      tmpfs                    914M     0  914M   0% /proc/acpi
      tmpfs                    914M     0  914M   0% /proc/scsi
      tmpfs                    914M     0  914M   0% /sys/firmware
      
      # 2. 查一下系統根目錄
      # ls -l /
      lrwxrwxrwx.   1 root root   7 Nov  3 15:22 bin -> usr/bin
      drwxr-xr-x.   5 root root 360 Dec 28 08:30 dev
      drwxr-xr-x.   1 root root  66 Dec 28 08:30 etc
      drwxr-xr-x.   2 root root   6 Nov  3 15:22 home
      lrwxrwxrwx.   1 root root   7 Nov  3 15:22 lib -> usr/lib
      lrwxrwxrwx.   1 root root   9 Nov  3 15:22 lib64 -> usr/lib64
      drwx------.   2 root root   6 Dec  4 17:37 lost+found
      drwxr-xr-x.   2 root root   6 Nov  3 15:22 media
      drwxr-xr-x.   2 root root   6 Nov  3 15:22 mnt
      drwxr-xr-x.   2 root root   6 Nov  3 15:22 opt
      dr-xr-xr-x. 184 root root   0 Dec 28 08:30 proc
      dr-xr-x---.   2 root root 162 Dec  4 17:37 root
      drwxr-xr-x.  11 root root 163 Dec  4 17:37 run
      lrwxrwxrwx.   1 root root   8 Nov  3 15:22 sbin -> usr/sbin
      drwxr-xr-x.   2 root root   6 Nov  3 15:22 srv
      dr-xr-xr-x.  13 root root   0 Dec 27 22:15 sys
      drwxrwxrwt.   7 root root 145 Dec  4 17:37 tmp
      drwxr-xr-x.  12 root root 144 Dec  4 17:37 usr
      drwxr-xr-x.  20 root root 262 Dec  4 17:37 var
      
      # 3. 查一下程序資料
      # ps -aux
      USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
      root           1  0.0  0.1  12128  3316 pts/0    Ss   08:30   0:00 /bin/bash
      root          26  0.0  0.1  47544  3512 pts/0    R+   08:41   0:00 ps -aux
      
      你可以發現幾個很有趣的現象~就是:
      • 因為 docker 無須開機,所以不需要 /boot 這個核心所在的目錄
      • 剛剛觀察過 /var/lib/docker,你會有發現 overlay 這個有趣的裝置!那用來作為虛擬根目錄
      • 因為是類似 chroot 的環境,所以你用 ps aux 時,只有執行的 bash 與當下的指令而已!
  2. 關閉 docker 與離開 docker 保留使用權的方式:
    1. 直接離開 bash 的情境:

      基本上,當你使用 exit 離開之後,這個 docker 就關閉了!並不會持續存在喔!

      [root34c3c830df4e /]# exit
      exit
      # 此時會回到 Host 的終端界面中,請開始在本機關查 docker 的 container 資料:
      
      # docker ps
      CONTAINER ID   IMAGE     COMMAND    CREATED    STATUS    PORTS     NAMES
      # 這會顯示目前啟動的 docker container 的資訊!
      
      # docker ps -a
      CONTAINER ID  IMAGE   COMMAND      CREATED         STATUS        PORTS  NAMES
      34c3c830df4e  centos  "/bin/bash"  14 minutes ago  Exited (0) 15 ...    cnetos1
      # -a 可以同時列出已經存在但是沒有啟用的 docker container 資料喔!
      
    2. 重新啟動已經建立過的 docker 容器:

      如果你只是想要暫時離開這個標準輸入 (standard input) 的狀態,可以使用底下的組合按鍵來暫時離開! 因為已經啟動過這個 container 了,會被記憶起來!所以,你不能再以『docker run -it --name centos1 centos』啟動, 否則會被判斷為失敗!如下所示:

      # docker run -it --name centos1 centos
      docker: Error response from daemon: Conflict. The container name "/cnetos1" is already 
        in use by container "34c3c830df4eec88918d184aec221bbaedca46ea3cddd331442697a9b42b9a69". 
        You have to remove (or rename) that container to be able to reuse that name.
      See 'docker run --help'.
      

      那怎辦?沒關係,可以透過底下的方式來啟動:

      # docker start 'container name'
      # docker start centos1
      # docker ps
      CONTAINER ID  IMAGE   COMMAND     CREATED     STATUS           NAMES
      34c3c830df4e  centos  "/bin/bash" 5 hours ago Up 5 seconds     centos1
      
    3. 連線到已經啟動的 docker 上,並且不關閉離開 docker 的方法:
      # 1. 連線的方法:
      # docker exec -it [name or id] [command]
      # docker exec -it centos1 bash
      
      # 2. 不要關閉 docker container 但是可以離開終端界面的組合按鈕:
      [ctrl] + p 之後 [ctrl] + q
      
      # docker ps
      CONTAINER ID IMAGE   COMMAND     CREATED     STATUS             NAMES
      34c3c830df4e centos  "/bin/bash" 5 hours ago Up About a minute  centos1
      
    4. 嘗試操作 docker 進行一些類似上課的練習:
      1. 再次取得 centos1 的終端機界面
      2. 嘗試使用 ifconfig 及 ip addr show 及 vim 等指令
      3. 嘗試使用 yum 安裝 net-tools vim-enhanced bash-completion iproute 等軟體
      4. 再次嘗試使用 ip addr show 以及 ifconfig 指令,看看輸出的情況
      5. 用 find 找出這個 docker 系統中,具有 suid 或 sgid 的檔案檔名,並列出權限。
  3. 關閉與刪除 docker 的方法:

    我們上面都在惡搞,所以搞錯了很多的 docker 容器資料!同時,也想要將容器刪除 (注意,是刪除容器,不是刪除 image 喔!)。 處理的方法也很簡單:

    # 1. 離開 docker container 之後,就觀察目前的 container 狀態:
    # docker ps
    CONTAINER ID   IMAGE     COMMAND       CREATED       STATUS          PORTS     NAMES
    34c3c830df4e   centos    "/bin/bash"   6 hours ago   Up 46 minutes             centos1
    
    # 2. 關閉容器:
    # docker stop [name or ID]
    # docker stop centos1
    # docker ps -a
    CONTAINER ID   IMAGE     COMMAND       CREATED       STATUS                    NAMES
    34c3c830df4e   centos    "/bin/bash"   6 hours ago   Exited (0) 6 seconds ago  centos1
    
    # 3. 刪除容器
    # docker rm [name or id]  <==可以填寫 "CONTAINER ID" 或 "NAMES" 的內容
    # docker rm centos1
    # docker rm 34c3c830df4e
    

    最後,當你重新 docker ps -a 時,就看不到任何的 container 名稱囉!

  4. 完成底下的實做:
    1. 啟動名為 ubuntu1 容器名稱,使用的是 ubuntu 映像檔。
    2. 進入 utuntu 之後,執行 ps 與 ps aux 及 top 等指令,說明為什麼程序數量這麼少?
    3. 不要停止 ubuntu1,但是離開 docker 的操作
    4. 觀察 docker 的活動情況
    5. 關閉 ubuntu1
    6. 刪除 ubuntu1

參考資料