docker
我們先來了解下docker的原理,如何才能制造出一個真正隔離的軟件運行環(huán)境.
namespace
docker在創(chuàng)建容器進程的時候可以指定一組namespace參數(shù)撒会,這樣容器就只能看到當前namespace所限定的資源迈窟,文件,設(shè)備涡拘,網(wǎng)絡(luò)。用戶豆励,配置信息,而對于宿主機和其他不相關(guān)的程序就看不到了袱蚓,PID namespace讓進程只看到當前namespace內(nèi)的進程枚碗,Mount namespace讓進程只看到當前namespace內(nèi)的掛載點信息逾一,Network namespace讓進程只看到當前namespace內(nèi)的網(wǎng)卡和配置信息,
docker利用namespace機制隔離出一個軟件執(zhí)行環(huán)境,我們可以用下圖來將docker和虛擬機技術(shù)做一個對比
一個centOS的KVM啟動起來后,即使什么不做也需要消耗200M的內(nèi)存,而且用戶進程運行在虛擬機里,對宿主機操作系統(tǒng)的調(diào)用不可避免會受到虛擬化軟件的攔截,而容器化的應(yīng)用依然是宿主機上的一個普通進程.
cgroup
全名 linux control group肮雨,用來限制一個進程組能夠使用的資源上限遵堵,如CPU,內(nèi)存怨规,網(wǎng)絡(luò)等陌宿,另外Cgroup還能夠?qū)M程設(shè)置優(yōu)先級和將進程掛起和恢復(fù),cgroup對用戶暴露的接口是一個文件系統(tǒng)波丰,/sys/fs/cgroup下 這個目錄下面有 cpuset,memery等文件壳坪,每一個可以被管理的資源都會有一個文件,如何對一個進程設(shè)置資源訪問上限呢掰烟?在/sys/fs/cgroup目錄下新建一個文件夾爽蝴,系統(tǒng)會默認創(chuàng)建上面一系列文件,然后docker容器啟動后纫骑,將進程ID寫入taskid文件中蝎亚,在根據(jù)docker啟動時候傳人的參數(shù)修改對應(yīng)的資源文件
$ docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash
Linux Cgroup的設(shè)計還是比較易用的,簡單理解就是一個子系統(tǒng)目錄加上一組資源限制文件,而對docker等linux容器項目而言,只需要在每個子系統(tǒng)下面,為每個容器創(chuàng)建一個控制組也就是一個文件夾,讓后在容器啟動后寫入進程PID.
文件系統(tǒng)
即使開啟了Mount Namespace,容器進程看到的文件系統(tǒng)還是和宿主機一樣的,mount namespace修改的時容器進程對掛載點的認知,他對容器進程視圖的改變,一定伴隨掛載操作才生效,但是作為一個普通用戶,我們希望在容器內(nèi)看到的文件系統(tǒng)就是一個獨立的隔離環(huán)境,而不是繼承自宿主機上的文件系統(tǒng).
我們可以在容器啟動后馬上掛載他的整個根目錄,這個掛載對宿主機不可見,然后通過chroot來更改change root file system更改進程的根目錄到掛載的位置,一般會通過chroot掛載一個完整的linux的文件系統(tǒng),但是不包括linux內(nèi)核先馆,這樣當我們交付一個docker鏡像的時候不僅包含需要運行的程序還包括這個程序依賴運行的這個環(huán)境颖对,因為我們打包了整個依賴的linux文件系統(tǒng),對一個應(yīng)用來說磨隘,操作系統(tǒng)才是他所依賴的最完整的依賴庫缤底,
這個掛載在容器根目錄下,用來為容器進程提供隔離后的執(zhí)行環(huán)境的文件系統(tǒng),就是所謂的容器鏡像,它還有一個專業(yè)的名詞rootFS
由于rootfs里面打包的不僅僅是用戶程序,而是整個系統(tǒng)的文件目錄,也就是是應(yīng)用和應(yīng)用依賴的類庫和環(huán)境變量都在里面.
增量層
docker在鏡像的設(shè)計中引入層的概念顾患,也就是用戶在制作docker鏡像中的每一次修改都是在原來的rootfs上新增一層roofs,之后通過一種聯(lián)合文件系統(tǒng)union fs的技術(shù)進行合并,合并的過程中如果兩個rootfs中有相同的文件則會用最外層的文件覆蓋原來的文件來進行去重操作个唧,舉個例子江解,我們從鏡像中心pull一個mysql的鏡像到本地,當我們通過這個鏡像創(chuàng)建一個容器的時候徙歼,就在這個鏡像原有的層上新加了一個增roofs,這個文件系統(tǒng)只保留增量修改犁河,包括文件的新增刪除,修改魄梯,這個增量層會借助union fs和原有層一起掛載到同一個目錄桨螺,這個增加的層可以讀寫,原有的其他層只能讀酿秸,這樣保證了所有對docker鏡像的操作都是增量灭翔,之后用戶可以commit這個鏡像將對這個鏡像的修改生成一個新的鏡像,新的鏡像就包含了原有的層和新增的層辣苏,只有最原始的層才是一個完整的linux fs, 那么既然只讀層不允許修改肝箱,那么我怎么刪除只讀層的文件呢,這個時候只需要在讀寫層也就是最外層生成一個whiteout文件來遮擋原來的文件就可以了稀蟋,
$ docker image inspect ubuntu:latest
...
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:f49017d4d5ce9c0f544c...",
"sha256:8f2b771487e9d6354080...",
"sha256:ccd4d61916aaa2159429...",
"sha256:c01d74f99de40e097c73...",
"sha256:268a067217b5fe78e000..."
]
}
dockerfile
可以通過docfile生成一個鏡像煌张,docfile里面可以指定 from的原始鏡像,以及自定義操作例如拷貝文件等退客,容器啟動命令等
例如下面的dockerfile,FROM原語指定了原始鏡像是python:2.7-slim,避免了先從一個原始的ubantu鏡像,然后通過apt-get install按照python
WORDIR 切換工作目錄
ADD拷貝文件
RUN 在容器里執(zhí)行shell
CMD 指定容器進程,相當于 docker rum python app.py
# 使用官方提供的 Python 開發(fā)鏡像作為基礎(chǔ)鏡像
FROM python:2.7-slim
# 將工作目錄切換為 /app
WORKDIR /app
# 將當前目錄下的所有內(nèi)容復(fù)制到 /app 下
ADD . /app
# 使用 pip 命令安裝這個應(yīng)用所需要的依賴
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# 允許外界訪問容器的 80 端口
EXPOSE 80
# 設(shè)置環(huán)境變量
ENV NAME World
# 設(shè)置容器進程為:python app.py骏融,即:這個 Python 應(yīng)用的啟動命令
CMD ["python", "app.py"]
容器網(wǎng)絡(luò)
從之前的章節(jié)中我們可以看到,容器只是一個被隔離的進程,這個進程只能看到他自己network namespace下的網(wǎng)絡(luò)棧,所謂的網(wǎng)絡(luò)棧包括了
網(wǎng)卡(network interface),回環(huán)設(shè)備(loopback device),路由表(route table),iptables規(guī)則,這些已經(jīng)可以構(gòu)成這個 進程和外界進行網(wǎng)絡(luò)通訊的基本環(huán)境.
容器作為宿主機內(nèi)的一個進程,在不設(shè)置network namespace的情況下,可以使用宿主機的網(wǎng)絡(luò)棧.
$ docker run –d –net=host --name nginx-host nginx
以上例子,就是容器采用宿主機的網(wǎng)絡(luò),當容器啟動后監(jiān)聽的是宿主機上的80端口
采用宿主機網(wǎng)絡(luò)可以為容器提供最好的網(wǎng)絡(luò)性能,但是我們希望容器有一個完全被隔離的環(huán)境,包括網(wǎng)絡(luò)環(huán)境,她應(yīng)該有自己的IP地址和端口,這樣就不會和其他進程有端口沖突,例如我運行4個ngnx容器進程,我希望他們是完全一樣的,向運行在4臺宿主機里面一樣,都監(jiān)聽80端口.所以我們希望容器進程運行在自己的network namespace內(nèi).
我們現(xiàn)在把容器看成,一臺主機,兩臺主機之間想要通信,就需要拉一條網(wǎng)線,多個主機之間想要通信,就需要一個交換機用網(wǎng)線把他們連起來,在linux上,能夠啟到虛擬交換作用的設(shè)備就是網(wǎng)橋(Bridge),他是一個工作在數(shù)據(jù)鏈路層的設(shè)備,我們可以把它當成一個二層交換機,而二層設(shè)備主要靠學習MAC地址對應(yīng)的端口,并將數(shù)據(jù)包轉(zhuǎn)發(fā)到對應(yīng)的端口上去.
docker在安裝的時候會在宿主機上創(chuàng)建一個叫docker0的網(wǎng)橋,而容器可用通過Veth Pair的虛擬設(shè)備,連接到這個網(wǎng)橋,Veth Pair這種設(shè)備在創(chuàng)建的時候會有兩張?zhí)摂M網(wǎng)卡,從一個網(wǎng)卡里面發(fā)出的數(shù)據(jù)包會到達另外一個網(wǎng)卡里,并且這兩個網(wǎng)卡可以跨network namespace,這樣Veth Pair可以理解為連接不同namespace的網(wǎng)線.
下圖是我啟動了兩個nginx容器,分別為nginx-1 ,nginx-2,我們用ifconfig查看宿主機上的網(wǎng)卡信息
$ docker run –d --name nginx-1 nginx
$ docker run –d --name nginx-2 nginx
被插在網(wǎng)橋上的虛擬網(wǎng)卡,不會調(diào)用網(wǎng)絡(luò)協(xié)議中棧處理數(shù)據(jù)包,只會像一個端口一樣,將數(shù)據(jù)包交給網(wǎng)橋,由網(wǎng)橋進行轉(zhuǎn)發(fā).
下面我們分析下一個宿主機內(nèi)的兩個容器之間的網(wǎng)絡(luò)怎么通訊?
當我們在容器nginx-1內(nèi)去ping一下nginx-2的ip,默認下是通的
- 1.宿主機內(nèi)的兩個主機通過二層網(wǎng)絡(luò)相通,nginx-1會先發(fā)一個ARP包來獲取nginx-2的MAC地址
- 2.nginx-1只能看到他自己network namespace內(nèi)的網(wǎng)卡 nginx-1-eth-0 ,所以數(shù)據(jù)包從這個網(wǎng)卡發(fā)出,但是這個網(wǎng)卡是一個Veth Pair設(shè)備,這個設(shè)備的另外一端在宿主機上默認的namespace內(nèi),并且是插在網(wǎng)橋docker-0上
- 3.由于Veth Pair設(shè)備的作用,宿主機的虛擬網(wǎng)卡收到數(shù)據(jù)包后,直接交給網(wǎng)橋docker-0進行轉(zhuǎn)發(fā),而docker-0會把數(shù)據(jù)包廣播給插在這個網(wǎng)橋上的其他虛擬網(wǎng)卡
- 4.這樣數(shù)據(jù)就會被廣播到宿主機上的另外一個虛擬網(wǎng)卡上了,這個虛擬網(wǎng)卡也是一個Veth Pair,他的另外一端是nginx-2容器的namespace內(nèi)的虛擬網(wǎng)卡,這個網(wǎng)卡將自己MAC地址回復(fù)
- 5.這樣nginx-1的namespace內(nèi)的虛擬網(wǎng)卡就獲取到了nginx-2的namespace內(nèi)的網(wǎng)卡的MAC地址了,就可以組裝數(shù)據(jù)包將請求發(fā)給nginx-2了
- 6.同樣數(shù)據(jù)包先根據(jù)Veth Pair設(shè)備到達宿主機namespace內(nèi)的網(wǎng)卡,然后交給docker-0進行轉(zhuǎn)發(fā),由于此時docker-0網(wǎng)橋已經(jīng)學習到了nginx-2的mac地址對應(yīng)的端口了,只需要查CAM表查一下記錄,轉(zhuǎn)發(fā)到另外一塊宿主機的虛擬網(wǎng)卡,然后到達nginx-2的namespace內(nèi)的網(wǎng)卡
以上就是同一個宿主機內(nèi)的不同docker容器通過Veth Pair設(shè)備和docker-0網(wǎng)橋通信的流程,與此類似,容器和其他宿主機進行通信,docker-0網(wǎng)橋在轉(zhuǎn)發(fā)的時候會根據(jù)宿主機的路由規(guī)則,將數(shù)據(jù)轉(zhuǎn)發(fā)給宿主機上的eth-0網(wǎng)卡,然后在由宿主機上德etho-進行轉(zhuǎn)發(fā).
那么容器怎么和其他宿主機內(nèi)的網(wǎng)絡(luò)通信呢?
在docker默認配置下,一臺宿主機內(nèi)的docker-0網(wǎng)橋和另外一個宿主機內(nèi)的docker-0網(wǎng)橋沒有任何關(guān)聯(lián),它們之間沒辦法相互關(guān)聯(lián),所以連在不同網(wǎng)橋上的容器沒有辦法進行連通.我們可以通過軟件的方式,創(chuàng)建一整個集群共用的網(wǎng)橋,集群內(nèi)所有的容器都連到這個網(wǎng)橋上,這個就是覆蓋網(wǎng)絡(luò)(overlay),為了實現(xiàn)這個網(wǎng)橋,我們需要了解FLANNEL技術(shù),以及他的量種實現(xiàn):UDP,VXLAN
UDP的模式是在宿主機內(nèi)增加一個軟件進程,通過TUN設(shè)備,將數(shù)據(jù)包發(fā)給上層應(yīng)用,然后在由應(yīng)用層判斷如何進行轉(zhuǎn)發(fā),目前UDP模式已經(jīng)被淘汰,主要因為性能太低,涉及太多次數(shù)據(jù)包從內(nèi)核態(tài)到用戶態(tài)的轉(zhuǎn)發(fā).
VXLAN是linux內(nèi)核本身就支持的一種網(wǎng)絡(luò)虛擬化技術(shù),VXLAN維護一個虛擬的局域網(wǎng),使在這個LAN以內(nèi)的容器可以相互通信.
從上圖可以看出,為了能讓二層網(wǎng)絡(luò)能夠打通,VXLAN需要在宿主機上設(shè)置一個特殊的設(shè)備作為隧道的兩端,這個設(shè)備就是VTEP (Vxlan Tunnel Endpoin ) 虛擬隧道端點.而VTEP的作用和上面UDP的應(yīng)用程序類似,他會拆包和封包,只不過他在數(shù)據(jù)鏈路層而不是涉及用戶太和內(nèi)核態(tài)的轉(zhuǎn)換.
kubernetes
k8s是一個做容器編排和調(diào)度的工具,kubernets的最小調(diào)度單元是POD萌狂,一個POD可以管理一組同生命周期的容器绎谦,k8s提供一個restful的客戶端api供用戶使用,所以會有一個APIserver來接受請求粥脚,通過etcd作為數(shù)據(jù)庫來存儲請求中得CRUD操作窃肠,而其他模塊例如控制器中的調(diào)度單元,會掃描數(shù)據(jù)庫中的記錄刷允,如果有新的POD還沒有分配物理節(jié)點冤留,則會執(zhí)行調(diào)度動作,如果發(fā)現(xiàn)新增了副本數(shù)量树灶,就會增加POD副本纤怒,如果修改了POD相關(guān)配置就去執(zhí)行,而每一個節(jié)點上面都會允許一個kube-proxy用來接收外部請求后轉(zhuǎn)發(fā)天通,而docker采用的插拔容器泊窘,可以使用docker引擎,也可以用其他的引擎。
下面我們分析下k8s下相關(guān)的概念
1.POD POD是kubernate的最小可操作單元,如果我們把容器看成一個特殊的進程,那POD就是一個有相同生命周期的進程組,POD里的所有容器共享一個Network namespace,并且可以共享一個Volume,那么kubernate怎么實現(xiàn)呢? k8s會在容器創(chuàng)建的時候先創(chuàng)建一個中間容器infra,然后其他的容器和這個容器共享namespace來實現(xiàn).這樣一個POD內(nèi)的多個容器可以通過localhost進行通信,一個POD只有一個IP地址,POD的生命周期和infra相關(guān),和容器A和B無關(guān).
2.MASTER Kubernatis集群中的master節(jié)點,可以部署在物理機或者虛擬機上,master節(jié)點負責維護集群的狀態(tài),以及對外提供API服務(wù),master節(jié)點上部署以下3種組件
2.1 API SERVER,作為kubernatis系統(tǒng)的入口,封裝了核心對象的增刪改查操作以及配置操作,以RESTFUL api的方式以及命令行的方式kubectl將接口暴露給外部客戶和內(nèi)部組件使用,維護的對象會被持久化到etcd中.
2.2 Schedule 調(diào)度器:為新建的POD選擇NODE,也就是分配機器,這個調(diào)度器也是可以插拔的,可以換成其他調(diào)度器,調(diào)度器可以考慮根據(jù)NODE的負載以及物理位置等參數(shù)設(shè)計調(diào)度算法.
2.3 Controller 控制器:所有資源的自動化控制中心,是一個大總管,例如Node Controller 用于管理NODE對象,接收NODE節(jié)點的使用情況,和NODE生命周期的管理即:創(chuàng)建和銷毀,以及心跳檢查等,Replication Controller ,副本控制器,監(jiān)測業(yè)務(wù)集群中POD數(shù)量是否符合預(yù)期,如果不夠會創(chuàng)建,多余會銷毀,當管理員修改了預(yù)期的值后,該進程就會進行響應(yīng).
3.NODE 節(jié)點是kubernatis集群中,相對于master而言的工作主機,這些主機可以說物理機,也可以是虛擬機,POD容器都運行在這些工作節(jié)點上,每個Node上都運行著一個叫kublet的進程,用來啟動和管理POD,NODE被master管理,一個NODE宕機后,master會將這臺node上部署的POD在其他NODE上重新創(chuàng)建并部署起來,NODE節(jié)點上部署有以下組件.
3.1 kubelet,作為一個單獨進程運行在NODE節(jié)點上,主要功能是:容器的管理,鏡像的管理,數(shù)據(jù)卷的管理,同時kubelet也提供restful接口服務(wù),當master節(jié)點的控制器可以下發(fā)請求到node上的kubelet進程,進行某個POD的創(chuàng)建,啟動容器和銷毀,并監(jiān)聽容器運行狀態(tài)匯報給master.
3.2 kube-proxy,負責為POD創(chuàng)建代理對象,用來實現(xiàn)訪問POD提供服務(wù)的網(wǎng)絡(luò)請求的路由和轉(zhuǎn)發(fā),該proxy可以提供一定的負載均衡和SDN的功能
3.3 容器環(huán)境,可以是docker也可以是其他容器技術(shù)
4.etcd作為一個鍵值數(shù)據(jù)庫,存儲集群的元數(shù)據(jù),以及POD的配置信息,并具有消息訂閱功能,所以,當用戶修改了POD預(yù)期副本數(shù)量,Replication Controller就可以感知到, etcs采用raft協(xié)議來保證集群環(huán)境下的數(shù)據(jù)一致性,并提供restful的api來供客戶端使用,kubernatis中的網(wǎng)絡(luò)配置信息,以及操作記錄和各個對象的狀態(tài)都存儲在etcd中,多個組件間的交互都需要用到etcd,所以etcs對整個k8s集群特別重要,所以etcd必須保證高可用.
Devops
Devops用來保證,開發(fā),運維,測試之間的高效溝通和協(xié)作,是軟件發(fā)布更加簡單和便捷.我們可以簡單的將Devops思想抽象成兩個產(chǎn)品,一個產(chǎn)品用來做項目管理,一個產(chǎn)品用來做軟件發(fā)布和集成.
我們可以嘗試簡單思考下一個項目管理的系統(tǒng)需要提供哪些功能:
1.支持看板視圖,讓團隊可以快速做信息同步,面板信息應(yīng)該包括:項目描述,項目進度以及碰到的問題,而項目進度可以根據(jù)時間列出:需求立項,需求評審,設(shè)計評審,技術(shù)方案評審,開發(fā),測試,發(fā)布.
2.項目迭代管理,提供各種維度報表,用來分析和預(yù)估后期項目周期.
3.代碼關(guān)聯(lián),可以將需求在gitlab上關(guān)聯(lián)代碼
而一個軟件發(fā)布的系統(tǒng)應(yīng)該提供以下功能
1.代碼管理,代碼提交可以關(guān)聯(lián)對應(yīng)的需求,支持代碼在線review
2.持續(xù)集成,支持每日定時,或者代碼提交自動觸發(fā)構(gòu)建動作
3.支持從gitlab上拉取最新的代碼,然后在特定環(huán)境下打包,然后根據(jù)dockerfile生成鏡像并提交
4.支持鏡像管理
5.支持自動化單元測試,接口測試,性能測試
6.持續(xù)部署,支持根據(jù)生成的鏡像自動部署測試環(huán)境和開發(fā)環(huán)境,使開發(fā)環(huán)境可以隨時訪問
7.灰度發(fā)布,可以灰度發(fā)布到生產(chǎn)環(huán)境和預(yù)發(fā)布環(huán)境
我們?nèi)绾卫胟8s做到自動化打包和發(fā)布?
- 1.創(chuàng)建pileline 指定項目名稱和對應(yīng)的tag,以及依賴工程,一個pipeline指一個完整的項目生命周期(開發(fā)提交代碼到代碼倉庫,打包,部署到開發(fā)環(huán)境,自動化測試,部署到測試環(huán)境,部署到生產(chǎn)環(huán)境)
- 2.根據(jù)項目名稱和tag去gitlab上拉取最新的代碼(利用java里的Runtime執(zhí)行shell腳本)
- 3.利用maven進行打包,這個時候可以為maven創(chuàng)建一個單獨的workspace(shell腳本)
- 4.根據(jù)預(yù)先寫好的docfile,拷貝maven打的包生成鏡像,并上傳鏡像 (shell腳本)
- 5.通過k8s的api在測試環(huán)境發(fā)布升級
- 6.通過灰度等方案發(fā)布到生產(chǎn)環(huán)境
藍綠發(fā)布
一般在反向代理層,例如nginx,的配置文件做一個軟連接,分別指向兩套環(huán)境,我們將這兩套環(huán)境分別稱為生產(chǎn)環(huán)境和預(yù)發(fā)布環(huán)境,正常情況下配置文件的軟連接指向生成環(huán)境,這個時候流量都會進入到生成環(huán)境的集群,發(fā)布的時候先發(fā)布預(yù)發(fā)布環(huán)境的機器,由于沒有流量進入,所以機器重啟沒有太大影響,機器發(fā)布完成后,可以通過其他入口,例如內(nèi)部修改host或者其他流量入口進行內(nèi)部測試,如果內(nèi)部測試通過,就可以修改軟連接指向預(yù)發(fā)布環(huán)境的集群,然后在進行驗證當外部流量進來后,是否正常,如果不正澈姹可以直接在把流量切回去,如果正常就可以升級生成環(huán)境的機器,然后在把流量切到生產(chǎn)環(huán)境
灰度發(fā)布
灰度發(fā)布可以在發(fā)布前,將一部分比例的機器的流量切走,然后進行軟件升級,升級完成后把流量切回來,然后進行驗證,驗證有問題在把流量掐掉,沒有問題可以慢慢按比例對外,這種方式需要新老版本的服務(wù)同時對外提供服務(wù),有些情況下我們把通過白名單或者固定用戶的形式開發(fā)服務(wù)也叫灰度.