專題 - Docker 容器基礎實做
上次更新日期 2021/01/03
除了虛擬機器 (VM) 之外,其實目前還有很多輕量級的虛擬化技術存在,例如所謂的容器 (container) 技術! 在 Linux 上面有個名為 docker 的容器技術,可以讓我們很輕鬆的就完成許多的軟體佈署,而且不會浪費很多的系統資源, 相當有趣!
容器 (container) 與 Docker
- 什麼是 container (容器):
- 根據維基百科 (wiki) 對於 container 的說明,在容器出現之前,我們稱為『作業系統層虛擬化 (Operating system-level virtualization)』,基本上,就是在核心層級執行一組被獨立出來的程式,這個程式會被獨自隔離,且這個程式雖然可以取得系統的所有資源, 然而這個程式只能看到屬於他自己的部份而已。一般來說,這種虛擬化也被稱為容器化技術 (Containerization)。[1]
- 承上,這一隻被隔離的軟體實體,就被稱為是一個容器 (container),或者是虛擬專用伺服器 (virtual private servers)。 對於每個程序的擁有者與執行者來說,他們使用的伺服器感覺好像就是獨自運作一般 (似乎與 Host 本身無關的感覺)。
- 事實上,最早的容器起源,可以思考的就是 chroot 這個機制! 我們可以將所有的系統資源掛載到某個目錄,然後將他變成根目錄 (chroot) 來處理的意思!回想一下 Linux 課程, 當你使用 chroot 去救援系統時,事實上,你使用的核心以及系統資源 (/sys, /proc, /dev 等等重要資源),都還是屬於原有的系統的! 這就是容器!
- 因為容器使用了原有系統的資源,因此所需要的獨立的資源相對小很多! 與目前主流的 VM 不一樣。因為 VM 是一個『獨立的作業系統』,而 container 只是在原有系統核心下的一個被隔離的程序! 兩者本質差很多。因此,VM 可以模擬不同的作業系統 (在 Windows 模擬 Linux,或在 Linux 模擬 Windows),但是 container 只能運作與母系統相同的作業系統環境 (因為共用核心資源!)
- 完成底下的實做:
- 開啟 VM,並且進入救援模式的流程:
- 透過各種方法,啟動你的一個 VM 系統
- 注意看出該系統的埠口號碼,然後直接使用 remote-viewer 去做 spice 連線
- 連線取得終端機之後,進行 [ctrl]+[alt]+[del] 重新開機
- 重新開機過程中,在選單模式輸入 rd.break 進入救援模式中
- 尚未掛載系統資源的 chroot 實做:
- 在救援模式中查閱 /sysroot 的裝置以及相關的掛載資訊
- 將 /sysroot 重新掛載為可讀寫
- 使用 chroot /sysroot 變更成為根目錄系統
- 使用 df 或 fdisk -l 或 gdisk -l 等等,能不能取得你的裝置分割表?
- 掛載系統資源之後,才進行 chroot 的實做!
- 退出 chroot,使用 mount --bind 的方式,掛載 /dev, /proc, /sys.. 到 /sysroot 的相關目錄
- 再次 chroot /sysroot,再次使用 df, fdisk -l, gdisk -l 等等,確認裝置存在否?
- 思考一下 chroot 的相關資源是這次的救援系統?還是你原本的系統?
- 離開 chroot 後,重新開機,再正確的進入系統中。
- 開啟 VM,並且進入救援模式的流程:
- 什麼是 docker:
- Docker 是一個開放原始碼的專案,這個專案就是以 Linux container 為基本的開發環境。 主要緣起於 2013 年 dotCloud 公司的內部專案,後來太熱門,因此 dotCloud 公司甚至改名為 Docker Inc。 後來在 0.9 版本之後,更以 Google 開發的 Go 程式語言進行函式庫的開發,在 Linux 核心上面玩弄容器技術 (Linux Container, LXC)。
- Docker 的商標圖示:
- Docker 的角色與 VM 角色的差異:
- 如上圖所示,右側的 VM 啟動時,需要完整的底層作業系統執行 (Guest Operating System), 如果你只是想要執行 VM 上面的一個軟體 (App A),還是得要執行完整的 VM 才行,因此消耗系統資源比較多。至於左側的 Docker 容器, 因為與 Host Operation System 幾乎是共用核心與資源,因此只需要直接執行 App A 而已,無須 Guest Operating System 的支援, 因此資源損耗比較少,啟動的速度與佈署的速度都比較快速![2]
- 你可以把 docker 容器理解為一個沙箱 (sandbox),每個容器內可以執行一個應用程式,而不同的容器都是互相獨立隔離的。不過, 容器之間可以透過某些機制來進行溝通。容器的啟動與關閉相當快速,就好像你在作業系統當中執行一個簡單的應用程式一般。 另外,很多時候,一個容器內,大部分也只啟動一個應用程式而已。
- Docker 的名詞與概念:
- Docker 的運作,基本上包含了三個基本概念:
- 映像檔 (image)
- 容器 (container)
- 倉庫 (repository)
- 映像檔, Image:
映像檔就是一個唯讀的模板。一個映像檔可以包含一個完整的 CentOS 作業系統環境,裡面只安裝了 Apache 或者是使用者需要的其他應用程式。事實上,你可以將他想成是一個 VM 的原始磁碟這樣的概念即可。映像檔主要的目的就是在產生 Docker 容器。 - Docker 容器 (container)
容器就是從映像檔執行起來的軟體實體。容器可以被啟動、開始、停止、刪除等等。每個容器都是獨立且互相隔離的。 你可以想像成,每個容器就是由映像檔快照出來的、被執行的系統的存在。且映像檔 (image) 是唯讀的, 容器則是透過某些特殊的檔案系統,讓它可以寫入,且不影響原本的映像檔。 - 倉庫 (repository)
倉庫就是放置 image 的所在,倉庫有公開的有私有的,例如 github 或者是 docker 官網所提供的映像檔, 那就是公開的倉庫。如果你想要有自己公司內部,或者是自己測試用的內部倉庫,那就自己幹一個,也就是私有的倉庫了。 - 基本上,上面這三個 docker 的元件,可以使用底下的圖示來簡易的了解: 雖然圖上的倉庫使用了 Registry,根據大眾的說法,你可以將 Registry 與 Repository 在 docker 軟體上,視為相同的東西來使用即可。 我們這個專案的目的在於取得可以使用的 Container 元件,所以,我們所進行的任何動作,都應該是在 Docker_host 那部機器上而已, 只要安裝好 Docker daemon ,然後下載適當的 Images 之後,就可以啟用 Container ,接下來只要連結到 container 上, 我們就可以快速的架構好所需要的服務囉!
- Docker 的運作,基本上包含了三個基本概念:
Docker 安裝啟用以及 Image 處理
- Docker 的安裝與啟動
- 現在的 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
- 啟動 docker 的方法也很簡單,直接啟動 docker 這個服務即可:
# systemctl start docker # systemctl enable docker
很快的,這樣就安裝好了,而且也啟動了 docker!接下來,就可以開始處理 docker 的各項任務了! - 接下來開始觀察一下,當你安裝並成功啟動 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 會主動的加入一些防火牆機制!特別注意這個情況喔!
- 現在的 docker:
- 處理 docker image
- 前面提到,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: 是否允許使用者驗證映像檔來源與內容
- 下載映像檔到本地端:
# 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 存在了! - 另外,由於我們目前僅有註冊一個倉庫在 docker.io 當中,所以,上述的 image 下載,也可以略過 repository 的說明,
直接 pull 即可。例如:
# docker pull centos
- 前面提到,docker 最重要的其實就是那個類似 VM 磁碟的 image。目前你的系統是否擁有 image 呢?
可以使用底下的指令來觀察:
- 完成底下的實做:
- 搜尋 ubuntu 這個映像檔檔名,並找到官方提供的版本
- 下載 ubuntu 映像檔到本地端
- 查詢目前系統有的映像檔資料
- 查詢 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
- 目前我們沒有要使用 httpd,請移除這個映像檔 (可以使用 docker rmi httpd 來處理)
- 最終,請找出 /var/lib/docker 底下容量最大的目錄,並比對 images 的容量,大概就能知道 image 是放在系統的哪個地方了。
使用 docker 容器
- 開始 docker 容器的啟用與簡易操作:
- 不要忘記,透過執行類似映像檔的快照行為,就可以啟動一個 docker 容器! 這個容器可以視為是一個軟體的實體~只是他快照到 docker 映像檔而已。
- 容器的啟動:容器的啟動直接透過 docker run 來處理!語法是:
# docker run -it --name container_name image_name
上述指令中:- -i:讓容器的標準輸入 (standard input) 維持在啟動的狀態
- -t:讓 Docker 配置一個虛擬終端機,並綁定容器到標準輸入的狀態!因此, -it 通常是合在一起執行。
- --name:配置一個名稱給容器,未來比較好記憶與操作!
- image_name:就是映像檔的名稱,要注意,image_name 最好一定放在最後面喔!
- 實際執行的流程:
# 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 與當下的指令而已!
- 關閉 docker 與離開 docker 保留使用權的方式:
- 直接離開 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 資料喔!
- 重新啟動已經建立過的 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
- 連線到已經啟動的 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
- 嘗試操作 docker 進行一些類似上課的練習:
- 再次取得 centos1 的終端機界面
- 嘗試使用 ifconfig 及 ip addr show 及 vim 等指令
- 嘗試使用 yum 安裝 net-tools vim-enhanced bash-completion iproute 等軟體
- 再次嘗試使用 ip addr show 以及 ifconfig 指令,看看輸出的情況
- 用 find 找出這個 docker 系統中,具有 suid 或 sgid 的檔案檔名,並列出權限。
- 直接離開 bash 的情境:
- 關閉與刪除 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 名稱囉!
- 完成底下的實做:
- 啟動名為 ubuntu1 容器名稱,使用的是 ubuntu 映像檔。
- 進入 utuntu 之後,執行 ps 與 ps aux 及 top 等指令,說明為什麼程序數量這麼少?
- 不要停止 ubuntu1,但是離開 docker 的操作
- 觀察 docker 的活動情況
- 關閉 ubuntu1
- 刪除 ubuntu1
參考資料
- ps01:維基百科之作業系統層虛擬化:https://en.wikipedia.org/wiki/OS-level_virtualisation
- ps02:docker 入門到實戰(值得慢慢看):https://philipzheng.gitbooks.io/docker_practice/content/
- https://www.cloudsavvyit.com/490/what-does-docker-do-and-when-should-you-use-it/
- https://itnext.io/getting-started-with-docker-facts-you-should-know-d000e5815598
- https://docs.docker.com/engine/install/centos/
- 清輝的 Docker 基本教學