1 Docker 簡介
1.1 Docker 由來
Docker 是基於 Go 語言開發的一個容器引擎,Docker是應用程序與系統之間的隔離層。通常應用程序對安裝的系統環境會有各種嚴格要求,當服務器很多時部署時系統環境的配置工作是非常繁瑣的。Docker讓應用程序不必再關心主機環境,各個應用安裝在Docker鏡像裏,Docker引擎負責運行包裹了應用程序的docker鏡像。
Docker的理念是讓開發人員可以簡單地把應用程序及依賴裝載到容器中,然後輕鬆地部署到任何地方,Docker具有如下特性。
Docker容器是輕量級的虛擬技術,佔用更少系統資源。
使用 Docker容器,不同團隊(如開發、測試,運維)之間更容易合作。
可以在任何地方部署 Docker 容器,比如在任何物理和虛擬機上,甚至在雲上。
由於Docker容器非常輕量級,因此可擴展性很強。
1.2 Docker 基本組成
鏡像(image):
Docker 鏡像就好比是一個目標,可以通過這個目標來創建容器服務,可以簡單的理解爲編程語言中的類。
容器(container):
Docker 利用容器技術,獨立運行一個或者一組應用,容器是通過鏡像來創建的,在容器中可執行啓動、停止、刪除等基本命令,最終服務運行或者項目運行就是在容器中的,可理解爲是類的實例。
倉庫(repository):
倉庫就是存放鏡像的地方!倉庫分爲公有倉庫和私有倉庫,類似Git。一般我們用的時候都是用國內docker鏡像來加速。
1.3 VM 跟 Docker
虛擬機:
傳統的虛擬機需要模擬整臺機器包括硬件,每臺虛擬機都需要有自己的操作系統,虛擬機一旦被開啓,預分配給他的資源將全部被佔用。每一個虛擬機包括應用,必要的二進制和庫,以及一個完整的用戶操作系統。
Docker:
容器技術是和我們的宿主機共享硬件資源及操作系統可以實現資源的動態分配。容器包含應用和其所有的依賴包,但是與其他容器共享內核。容器在宿主機操作系統中,在用戶空間以分離的進程運行。
1.4 Docker 跟 DevOps
DevOps 是一組過程、方法與系統的統稱,用於促進開發(應用程序/軟件工程)、技術運營和質量保障(QA)部門之間的溝通、協作與整合。
DevOps 是兩個傳統角色 Dev(Development) 和 Ops(Operations) 的結合,Dev 負責開發,Ops 負責部署上線,但 Ops 對 Dev 開發的應用缺少足夠的瞭解,而 Dev 來負責上線,很多服務軟件不知如何部署運行,二者中間有一道明顯的鴻溝,DevOps 就是爲了彌補這道鴻溝。DevOps 要做的事,是偏 Ops 的;但是做這個事的人,是偏 Dev 的, 說白了就是要有一個瞭解 Dev 的人能把 Ops 的事幹了。而Docker 是適合 DevOps 的。
1.5 Docker 跟 k8s
k8s 的全稱是 kubernetes,它是基於容器的集羣管理平臺,是管理應用的全生命週期的一個工具,從創建應用、應用的部署、應用提供服務、擴容縮容應用、應用更新、都非常的方便,而且可以做到故障自愈,例如一個服務器掛了,可以自動將這個服務器上的服務調度到另外一個主機上進行運行,無需進行人工干涉。k8s 依託於Google自家的強大實踐應用,目前市場佔有率已經超過Docker自帶的Swarm了。
如果你有很多 Docker 容器要啓動、維護、監控,那就上k8s吧!
1.6 hello world
docker run hello-world
的大致流程圖如下:
2 Docker 常見指令
官方文檔:
https://docs.docker.com/engine/reference/commandline/build/
3 Docker 運行原理
Docker 只提供一個運行環境,他跟 VM 不一樣,是不需要運行一個獨立的 OS,容器中的系統內核跟宿主機的內核是公用的。docker容器本質上是宿主機的進程。對 Docker 項目來說,它最核心的原理實際上就是爲待創建的用戶進程做如下操作:
啓用 Linux Namespace 配置。
設置指定的 Cgroups 參數。
切換進程的根目錄(Change Root),優先使用 pivot_root 系統調用,如果系統不支持,纔會使用 chroot。
3.1 namespace 進程隔離
Linux Namespaces 機制提供一種進程資源隔離
方案。PID、IPC、Network 等系統資源不再是全局性的,而是屬於某個特定的Namespace。每個namespace下的資源對於其他 namespace 下的資源都是透明,不可見的。系統中可以同時存在兩個進程號爲0、1、2的進程,由於屬於不同的namespace,所以它們之間並不衝突。
PS:Linux 內核提拱了6種 namespace 隔離的系統調用,如下圖所示。
3.2 CGroup 分配資源
Docker 通過 Cgroup 來控制容器使用的資源配額,一旦超過這個配額就發出OOM。配額主要包括 CPU、內存、磁盤三大方面, 基本覆蓋了常見的資源配額和使用量控制。
Cgroup 是 Control Groups 的縮寫,是Linux 內核
提供的一種可以限制、記錄、隔離進程組所使用的物理資源(如 CPU、內存、磁盤 IO 等等)的機制,被 LXC(Linux container)、Docker 等很多項目用於實現進程資源控制。Cgroup 本身是提供將進程進行分組化管理的功能和接口的基礎結構,I/O 或內存的分配控制等具體的資源管理是通過該功能來實現的,這些具體的資源 管理功能稱爲 Cgroup 子系統。
3.3 chroot 跟 pivot_root 文件系統
chroot(change root file system)命令的功能是 改變進程的根目錄到指定的位置。比如我們現在有一個$HOME/test
目錄,想要把它作爲一個 /bin/bash
進程的根目錄。
首先,創建一個 HOME/test/{bin,lib64,lib} 把bash命令拷貝到test目錄對應的bin路徑下 cp -v /bin/{bash,ls} $HOME/test/bin 把bash命令需要的所有so文件,也拷貝到test目錄對應的lib路徑下 執行chroot命令,告訴操作系統,我們將使用HOME/test /bin/bash
ls /
返回的都是
$HOME/test
目錄下面的內容,Docker就是這樣實現容器根目錄的。爲了能夠讓容器的這個根目錄看起來更
真實
,一般在容器的根目錄下掛載一個完整操作系統的文件系統,比如Ubuntu16.04的ISO。這樣在容器啓動之後,容器裏執行
ls /
查看到的就是Ubuntu 16.04的所有目錄和文件。
容器鏡像
。更專業的名字叫作:rootfs(根文件系統)。所以一個最常見的 rootfs 會包括如下所示的一些目錄和文件:
$ ls /
bin dev etc home lib lib64 mnt opt proc root run sbin sys tmp usr var
3.4 一致性
打包操作系統
的能力,這個最基礎的依賴環境也終於變成了應用沙盒的一部分。這就賦予了容器所謂的一致性:
無論在本地、雲端,還是在一臺任何地方的機器上,用戶只需要解壓打包好的容器鏡像,那麼這個應用運行所需要的完整的執行環境就被重現出來了。
3.5 UnionFS 聯合文件系統
$ tree
.
├── fruits
│ ├── apple
│ └── tomato
└── vegetables
├── carrots
└── tomato
$ mkdir mnt
$ sudo mount -t aufs -o dirs=./fruits:./vegetables none ./mnt
$ tree ./mnt
./mnt
├── apple
├── carrots
└── tomato
$ echo mnt > ./mnt/apple
$ cat ./mnt/apple
mnt
$ cat ./fruits/apple
mnt
$ echo mnt_carrots > ./mnt/carrots
$ cat ./vegetables/carrots
old
$ cat ./fruits/carrots
mnt_carrots
在mount aufs命令時候,沒有對 vegetables 跟 fruits 設置權限,默認命令行上第一個的目錄是可讀可寫的,後面的全都是隻讀的。有重複的文件名,在mount命令行上,越往前的被操作的優先級越高。
3.6 layer 分層
-
rw 表示可寫可讀read-write。 -
ro 表示read-only,如果你不指權限,那麼除了第一個外,ro是默認值,對於ro分支,其永遠不會收到寫操作,也不會收到查找whiteout的操作。 -
rr 表示 real-read-only,與read-only不同的是,rr 標記的是天生就是隻讀的分支,這樣,AUFS可以提高性能,比如不再設置inotify來檢查文件變動通知。
$ tree
.
├── fruits
│ ├── apple
│ └── tomato
├── test #目錄爲空
└── vegetables
├── carrots
└── tomato
$ mkdir mnt
$ mount -t aufs -o dirs=./test=rw:./fruits=ro:./vegetables=ro none ./mnt
$ ls ./mnt/
apple carrots tomato
.wh.apple
,你就會發現 ./mnt/apple 這個文件就消失了,跟執行了 rm ./mnt/apple 是一樣的結果:
$ touch ./test/.wh.apple
$ ls ./mnt
carrots tomato
/var/lib/docker/aufs/diff
目錄下,然後通過查詢
/sys/fs/aufs
查看被聯合掛載在一起的各個層的信息,多個基礎層最終被聯合掛載在
/var/lib/docker/aufs/mnt
裏面,這裏面存儲的就是一個成品。
3.6.1 只讀層
docker image inspect ubuntu:latest
會發現容器的rootfs最下面的四層,對應的正是ubuntu:latest鏡像的四層。它們的掛載方式都是隻讀的(ro+wh),都以增量的方式分別包含了Ubuntu操作系統的一部分,四層聯合起來組成了一個成品。
3.6.2 可讀寫層
3.6.3 init 層
-init
結尾的層,夾在只讀層和讀寫層之間。Init層是Docker項目單獨生成的一個內部層,專門用來存放 /etc/hosts 等信息。
4 Docker 網絡
docker network ls
會發現它會自動創建三個網絡。
[root@server1 ~]$ docker network ls
NETWORK ID NAME DRIVER SCOPE
0147b8d16c64 bridge bridge local
2da931af3f0b host host local
63d31338bcd9 none null local
4.1 Host 模式
4.2 Container 模式
4.3 None 模式
4.4 Bridge 模式
-
在主機上創建一對虛擬網卡 veth pair 設備。veth設備總是成對出現的,它們組成了一個數據的通道,數據從一個設備進入,就會從另一個設備出來。因此veth設備常用來連接兩個網絡設備。 -
Docker 將 veth pair 設備的一端放在新創建的容器中,並命名爲eth0。另一端放在主機中,以veth65f9 這樣類似的名字命名,並將這個網絡設備加入到docker0網橋中,可以通過brctl show命令查看。 -
從 docker0 子網中分配一個IP給容器使用,並設置 docker0 的IP地址爲容器的默認網關。
-
容器訪問外部
假設主機網卡爲eth0,IP地址10.10.101.105/24,網關10.10.101.254。從主機上一個IP爲172.17.0.1/16 的容器中ping百度(180.76.3.151)。首先IP包從容器發往自己的默認網關 docker0,包到達docker0後,會查詢主機的路由表,發現包應該從主機的 eth0 發往主機的網關10.10.105.254/24。接着包會轉發給eth0,並從eth0發出去。這時Iptable規則就會起作用,將源地址換爲 eth0 的地址。這樣,在外界看來,這個包就是從10.10.101.105上發出來的,Docker容器對外是不可見的。
-
外部訪問容器
創建容器並將容器的80端口映射到主機的80端口。當我們對主機 eth0 收到的目的端口爲80的訪問時候,Iptable規則會進行DNAT轉換,將流量發往172.17.0.2:80,也就是我們上面創建的Docker容器。所以,外界只需訪問10.10.101.105:80就可以訪問到容器中的服務。
4.5 --link
docker run -d -P --name linux03 --link linux02 linux
docker exec -it linux03 ping linux02 可ping通。
docker exec -it linux02 ping linux03 不可ping通。
172.17.0.3 linux03 12ft4tesa # 跟Windows的host文件一樣,只是做了地址綁定
4.6 自建Bridge
docker run -d -P --name linux01 LinuxSelf
docker run -d -P --name linux01 --net bridge LinuxSelf
# --driver bridge 網絡模式定義爲 :橋接
# --subnet 192.168.0.0/16 定義子網 ,範圍爲:192.168.0.2 ~ 192.168.255.255
# --gateway 192.168.0.1 子網網關設爲: 192.168.0.1
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
docker run -d -P --name linux-net-01 --net mynet LinuxSelf
docker run -d -P --name linux-net-02 --net mynet LinuxSelf
docker exec -it linux-net-01 ping linux-net-02的IP # 結果OK
docker exec -it linux-net-01 ping linux-net-02 # 結果OK
5 可視化界面
5.1 Portainer
5.2 DockerUI
5.3 Shipyard
有道無術,術可成;有術無道,止於術
歡迎大家關注Java之道公衆號
好文章,我在看❤️
本文分享自微信公衆號 - Hollis(hollischuang)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。