Docker 基礎
- 2020 年 9 月 20 日
- 筆記
Docker 基礎
一、簡介
1. 什麼是 Docker
docker是一個開源的應用容器引擎,基於go語言開發並遵循了apache2.0協議開源。docker本質就是宿主機的一個進程。docker是通過namespace實現資源隔離,通過cgroup實現資源限制,通過寫時複製技術(copy-on-write)實現了高效的文件操作(類似虛擬機的磁碟比如分配500g並不是實際佔用物理磁碟500g)
namespace的六項隔離
| namespace | 系統調用參數 | 隔離內容 |
|---|---|---|
| UTS | CLONE_NEWUTS | 主機名,域名 |
| IPC | CLONE_NEWWIPC | 訊號量,消息堆列和共享記憶體 |
| PID | CLONE_NEWPID | 進程編號 |
| NETWORK | CLONE_NEWNET | 網路設備、網路棧、埠等 |
| MOUNT | CLONE_NEWNS | 掛載點(文件系統) |
| USER | CLONE_NEWUSER | 用戶和用戶組(3.8以後的內核才支援) |
名字空間是 Linux 內核一個強大的特性。每個容器都有自己單獨的名字空間,運行在其中的應用都像是在獨立的作業系統中運行一樣。名字空間保證了容器之間彼此互不影響。
1 pid 名字空間
不同用戶的進程就是通過 pid 名字空間隔離開的,且不同名字空間中可以有相同 pid。所有的 LXC 進程在Docker 中的父進程為Docker進程,每個 LXC 進程具有不同的名字空間。同時由於允許嵌套,因此可以很方便的實現嵌套的 Docker 容器。
2 net 名字空間
有了 pid 名字空間, 每個名字空間中的 pid 能夠相互隔離,但是網路埠還是共享 host 的埠。網路隔離是通過 net 名字空間實現的, 每個 net 名字空間有獨立的 網路設備, IP 地址, 路由表, /proc/net 目錄。這樣每個容器的網路就能隔離開來。Docker 默認採用 veth 的方式,將容器中的虛擬網卡同 host 上的一 個Docker網橋 docker0 連接在一起。
3 ipc 名字空間
容器中進程交互還是採用了 Linux 常見的進程間交互方法(interprocess communication – IPC), 包括訊號量、消息隊列和共享記憶體等。然而同 VM 不同的是,容器的進程間交互實際上還是 host 上具有相同 pid 名字空間中的進程間交互,因此需要在 IPC 資源申請時加入名字空間資訊,每個 IPC 資源有一個唯一的 32位 id。
4 mnt 名字空間
類似 chroot,將一個進程放到一個特定的目錄執行。mnt 名字空間允許不同名字空間的進程看到的文件結構不同,這樣每個名字空間 中的進程所看到的文件目錄就被隔離開了。同 chroot 不同,每個名字空間中的容器在 /proc/mounts 的資訊只包含所在名字空間的 mount point。
5 uts 名字空間
UTS(「UNIX Time-sharing System」) 名字空間允許每個容器擁有獨立的 hostname 和 domain name, 使其在網路上可以被視作一個獨立的節點而非 主機上的一個進程。
6 user 名字空間
每個容器可以有不同的用戶和組 id, 也就是說可以在容器內用容器內部的用戶執行程式而非主機上的用戶。
control Group 控制組
特點
- cgroup的api以一個偽文件系統的實現方式,用戶的程式可以通過文件系統實現cgroup的組件管理;
- cgroup的組件管理操作單元可以細粒度到執行緒級別,另外用戶可以創建和銷毀cgroup,從而實現資源的分配和再利用;
- 所有資源管理的功能都以子系統的方式實現,介面統一子任務創建之初與其父任務處於同一個cgroup的控制組
功能
- 資源限制:可以對任務使用的資源總額進行限制;
- 優先順序分配:通過分配的cpu時間片數量以及磁碟IO頻寬大小,實際上相當於控制了任務運行優先順序;
- 資源統計:可以統計系統的資源使用量,如cpu時長,記憶體用量等;
- 任務控制:cgroup可以對任務執行掛起、恢復等操作;
2. 容器的優點
- 靈活:即使是最複雜的應用也可以集裝箱化。
- 輕量級:容器利用並共享主機內核。
- 可互換:您可以即時部署更新和升級。
- 攜帶型:您可以在本地構建,部署到雲,並在任何地方運行。
- 可擴展:您可以增加並自動分發容器副本。
- 可堆疊:您可以垂直和即時堆疊服務。
3. Docker 三個重要的概念
docker 是一個 C/S 架構模型。
1)image鏡像
docker鏡像就是一個只讀模板,比如,一個鏡像可以包含一個完整的centos,裡面僅安裝apache或用戶的其他應用,鏡像可以用來創建docker容器,另外docker提供了一個很簡單的機制來創建鏡像或者更新現有的鏡像,用戶甚至可以直接從其他人那裡下一個已經做好的鏡像來直接使用。
2)container容器
docker利用容器來運行應用,容器是從鏡像創建的運行實例,它可以被啟動,開始、停止、刪除、每個容器都是互相隔離的,保證安全的平台,可以吧容器看做是要給簡易版的linux環境(包括root用戶許可權、鏡像空間、用戶空間和網路空間等)和運行再其中的應用程式
3)repostory倉庫
倉庫是集中存儲鏡像文件的滄桑,registry是倉庫主從伺服器,實際上參考註冊伺服器上存放著多個倉庫,每個倉庫中又包含了多個鏡像,每個鏡像有不同的標籤(tag)
倉庫分為兩種,公有倉庫和私有倉庫,最大的公開倉庫是docker Hub,存放了數量龐大的鏡像供用戶下周,中國的docker pool,這裡倉庫的概念與Git類似,registry可以理解為github這樣的託管服務
二、Docker 架構
1. 看得見的架構
Docker使用了C/S體系架構。
- Docker客戶端與Docker守護進程(daemon)通訊,守護進程負責構建,運行和分發Docker容器。
- Docker客戶端和守護進程可以在同一個系統上運行,也可以將Docker客戶端連接到遠程Docker守護進程。
- Docker客戶端和守護進程使用REST API通過UNIX套接字或網路介面進行通訊。
2. 總體架構
用戶是使用Docker Client與Docker Daemon建立通訊,並發送請求給後者。
- 而Docker Daemon作為Docker架構中的主體部分,首先提供Server的功能使其可以接受Docker Client的請求;
- 而後Engine執行Docker內部的一系列工作,每一項工作都是以一個Job的形式的存在。Job的運行過程中,當需要容器鏡像時,則從Docker Registry中下載鏡像,並通過鏡像管理驅動graphdriver將下載鏡像以Graph的形式存儲;
- 當需要為Docker創建網路環境時,通過網路管理驅動networkdriver創建並配置Docker容器網路環境;
- 當需要限制Docker容器運行資源或執行用戶指令等操作時,則通過execdriver來完成。
- 而libcontainer是一項獨立的容器管理包,networkdriver以及execdriver都是通過libcontainer來實現具體對容器進行的操作。
- 當執行完運行容器的命令後,一個實際的Docker容器就處於運行狀態,該容器擁有獨立的文件系統,獨立並且安全的運行環境等。
三、各個模組的介紹
主要模組
1. Docker Client
docker client 是docker架構中用戶用來和docker daemon建立通訊的客戶端,用戶使用的可執行文件為docker,通過docker命令行工具可以發起眾多管理container的請求。
docker client可以通過以下三種方式和docker daemon建立通訊:tcp://host:port; unix:path_to_socket; fd://socketfd
docker client可通過設置命令行flag參數的形式設置安全傳輸層協議(TLS)的有關參數,保證傳輸的安全性。
docker client發送容器管理請求後,由docker daemon接受並處理請求,當docker client 接收到返回的請求相應並簡單處理後,docker client 一次完整的生命周期就結束了。當需要繼續發送容器管理請求時,用戶必須再次通過docker可以執行文件創建docker client。
2. Docker Daemon
docker daemon 是docker架構中一個常駐在後台的系統進程。功能是:接收處理docker client發送的請求。該守護進程在後台啟動一個server,server負載接受docker client發送的請求;接受請求後,server通過路由與分發調度,找到相應的handler來執行請求。
docker daemon啟動所使用的可執行文件也為docker,與docker client啟動所使用的可執行文件docker相同,在docker命令執行時,通過傳入的參數來判別docker daemon與docker client。
docker daemon的架構可以分為:docker server、engine、job。daemon
a)docker server
在docker架構中時專門服務於docker client的server,該server的功能時:接受並調度分發docker client發送的請求,架構圖如下:
在Docker的啟動過程中,通過包gorilla/mux(golang的類庫解析),創建了一個mux.Router,提供請求的路由功能。在Golang中,gorilla/mux是一個強大的URL路由器以及調度分發器。該mux.Router中添加了眾多的路由項,每一個路由項由HTTP請求方法(PUT、POST、GET或DELETE)、URL、Handler三部分組成。
若Docker Client通過HTTP的形式訪問Docker Daemon,創建完mux.Router之後,Docker將Server的監聽地址以及mux.Router作為參數,創建一個httpSrv=http.Server{},最終執行httpSrv.Serve()為請求服務。
在Server的服務過程中,Server在listener上接受Docker Client的訪問請求,並創建一個全新的goroutine來服務該請求。在goroutine中,首先讀取請求內容,然後做解析工作,接著找到相應的路由項,隨後調用相應的Handler來處理該請求,最後Handler處理完請求之後回復該請求。
需要注意的是:Docker Server的運行在Docker的啟動過程中,是靠一個名為」serveapi」的job的運行來完成的。原則上,Docker Server的運行是眾多job中的一個,但是為了強調Docker Server的重要性以及為後續job服務的重要特性,將該」serveapi」的job單獨抽離出來分析,理解為Docker Server。
b)engine
Engine是Docker架構中的運行引擎,同時也Docker運行的核心模組。它扮演Docker container存儲倉庫的角色,並且通過執行job的方式來操縱管理這些容器。
在Engine數據結構的設計與實現過程中,有一個handler對象。該handler對象存儲的都是關於眾多特定job的handler處理訪問。舉例說明,Engine的handler對象中有一項為:{「create」: daemon.ContainerCreate,},則說明當名為」create」的job在運行時,執行的是daemon.ContainerCreate的handler。
c)job
一個Job可以認為是Docker架構中Engine內部最基本的工作執行單元。Docker可以做的每一項工作,都可以抽象為一個job。例如:在容器內部運行一個進程,這是一個job;創建一個新的容器,這是一個job,從Internet上下載一個文檔,這是一個job;包括之前在Docker Server部分說過的,創建Server服務於HTTP的API,這也是一個job,等等。
Job的設計者,把Job設計得與Unix進程相仿。比如說:Job有一個名稱,有參數,有環境變數,有標準的輸入輸出,有錯誤處理,有返回狀態等。
3. Docker Registry
Docker Registry是一個存儲容器鏡像的倉庫。而容器鏡像是在容器被創建時,被載入用來初始化容器的文件架構與目錄。
在Docker的運行過程中,Docker Daemon會與Docker Registry通訊,並實現搜索鏡像、下載鏡像、上傳鏡像三個功能,這三個功能對應的job名稱分別為」search」,」pull」 與 「push」。
其中,在Docker架構中,Docker可以使用公有的Docker Registry,即大家熟知的Docker Hub,如此一來,Docker獲取容器鏡像文件時,必須通過互聯網訪問Docker Hub;同時Docker也允許用戶構建本地私有的Docker Registry,這樣可以保證容器鏡像的獲取在內網完成。
4. Graph
Graph在Docker架構中扮演已下載容器鏡像的保管者,以及已下載容器鏡像之間關係的記錄者。一方面,Graph存儲著本地具有版本資訊的文件系統鏡像,另一方面也通過GraphDB記錄著所有文件系統鏡像彼此之間的關係。Graph的架構如下:
其中,GraphDB是一個構建在SQLite之上的小型圖數據庫,實現了節點的命名以及節點之間關聯關係的記錄。它僅僅實現了大多數圖資料庫所擁有的一個小的子集,但是提供了簡單的介面表示節點之間的關係。
同時在Graph的本地目錄中,關於每一個的容器鏡像,具體存儲的資訊有:該容器鏡像的元數據,容器鏡像的大小資訊,以及該容器鏡像所代表的具體rootfs。
5. Driver
Driver是Docker架構中的驅動模組。通過Driver驅動,Docker可以實現對Docker容器執行環境的訂製。由於Docker運行的生命周期中,並非用戶所有的操作都是針對Docker容器的管理,另外還有關於Docker運行資訊的獲取,Graph的存儲與記錄等。因此,為了將Docker容器的管理從Docker Daemon內部業務邏輯中區分開來,設計了Driver層驅動來接管所有這部分請求。
在Docker Driver的實現中,可以分為以下三類驅動:graphdriver、networkdriver和execdriver。
a)graphdriver
graphdriver主要用於完成容器鏡像的管理,包括存儲與獲取。即當用戶需要下載指定的容器鏡像時,graphdriver將容器鏡像存儲在本地的指定目錄;同時當用戶需要使用指定的容器鏡像來創建容器的rootfs時,graphdriver從本地鏡像存儲目錄中獲取指定的容器鏡像。
在graphdriver的初始化過程之前,有4種文件系統或類文件系統在其內部註冊,它們分別是aufs、btrfs、vfs和devmapper。而Docker在初始化之時,通過獲取系統環境變數」DOCKER_DRIVER」來提取所使用driver的指定類型。而之後所有的graph操作,都使用該driver來執行。
graphdriver的架構如下:
b)networkdriver
networkdriver的用途是完成Docker容器網路環境的配置,其中包括Docker啟動時為Docker環境創建網橋;Docker容器創建時為其創建專屬虛擬網卡設備;以及為Docker容器分配IP、埠並與宿主機做埠映射,設置容器防火牆策略等。networkdriver的架構如下:
c)execdriver
execdriver作為Docker容器的執行驅動,負責創建容器運行命名空間,負責容器資源使用的統計與限制,負責容器內部進程的真正運行等。在execdriver的實現過程中,原先可以使用LXC驅動調用LXC的介面,來操縱容器的配置以及生命周期,而現在execdriver默認使用native驅動,不依賴於LXC。具體體現在Daemon啟動過程中載入的ExecDriverflag參數,該參數在配置文件已經被設為」native」。這可以認為是Docker在1.2版本上一個很大的改變,或者說Docker實現跨平台的一個先兆。execdriver架構如下:
6. libcontainer
libcontainer是Docker架構中一個使用Go語言設計實現的庫,設計初衷是希望該庫可以不依靠任何依賴,直接訪問內核中與容器相關的API。
正是由於libcontainer的存在,Docker可以直接調用libcontainer,而最終操縱容器的namespace、cgroups、apparmor、網路設備以及防火牆規則等。這一系列操作的完成都不需要依賴LXC或者其他包。libcontainer架構如下:
另外,libcontainer提供了一整套標準的介面來滿足上層對容器管理的需求。或者說,libcontainer屏蔽了Docker上層對容器的直接管理。又由於libcontainer使用Go這種跨平台的語言開發實現,且本身又可以被上層多種不同的程式語言訪問,因此很難說,未來的Docker就一定會緊緊地和Linux捆綁在一起。而於此同時,Microsoft在其著名雲計算平台Azure中,也添加了對Docker的支援,可見Docker的開放程度與業界的火熱度。
暫不談Docker,由於libcontainer的功能以及其本身與系統的松耦合特性,很有可能會在其他以容器為原型的平台出現,同時也很有可能催生出雲計算領域全新的項目。
7. Docker container
Docker container(Docker容器)是Docker架構中服務交付的最終體現形式。
Docker按照用戶的需求與指令,訂製相應的Docker容器:
用戶通過指定容器鏡像,使得Docker容器可以自定義rootfs等文件系統; 用戶通過指定計算資源的配額,使得Docker容器使用指定的計算資源; 用戶通過配置網路及其安全策略,使得Docker容器擁有獨立且安全的網路環境; 用戶通過指定運行的命令,使得Docker容器執行指定的工作。
四、Docker 安裝
- linux內核版本依賴
kernel version >= 3.8
可以使用如下命令查看
uname -a | awk '{split($3, arr, "-"); print arr[1]}'
# 關閉selinux
setenforce 0
sed -i'/^SELINUX=/c\SELINUX=disabled' /etc/selinux/config
# 關閉防火牆
systemctl stopfirewalld.service
systemctl disablefirewalld.service
systemctl stop iptables.service
systemctl disableiptables.service
# 卸載舊版本
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine
# 設置stable鏡像倉庫
yum-config-manager --add-repo //mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum makecache fast
# 安裝最新版本
yum -y install docker-ce
# 運行
systemctl enable docker
systemctl start docker
當要以非 root 用戶可以直接運行 docker 時,需要執行 usermod -aG docker $USER當要以非 root 用戶可以直接運行 docker 時,需要執行
五、常用命令
六、延伸資料
6.1 Docker安裝
《CentsOS下安裝Docker》
6.2 Docker常用命令
《Docker 命令大全》
6.3 Docker(UnionFS)
《深入分析Docker鏡像原理》
《創建自己的Docker基礎鏡像》
《Linux文件系統之aufs》
《Container內不需要OS,為何需要OS的基礎鏡像?》
《淺談linux中的根文件系統(rootfs的原理和介紹)》
6.4 Docker原理與實現
《Docker容器原理與實現》
《Docker 核心技術與實現原理》
《DOCKER基礎技術》
《Docker底層架構核心技術》
《Docker的概念及剖析原理和特點》


