故事

程序員小張: 剛畢業(yè),參加工作1年左右,日常工作是CRUD

架構(gòu)師老李: 多個(gè)大型項(xiàng)目經(jīng)驗(yàn),精通各種屠龍寶術(shù);
有一天,小張碰到了老李,他想向老李請(qǐng)教有關(guān)Docker的知識(shí)。于是,小張走向老李并問道:“老李,我聽說你懂得很多關(guān)于Docker的知識(shí),請(qǐng)問你能否給我講講Docker的基本結(jié)構(gòu)和組件?”
老李微笑著點(diǎn)了點(diǎn)頭,開始向小張介紹Docker的基本結(jié)構(gòu)和組件。他告訴小張,Docker由三個(gè)主要概念構(gòu)成:鏡像、容器和倉庫。其中,鏡像是一個(gè)只讀的模板,容器則是基于這個(gè)模板創(chuàng)建的可運(yùn)行實(shí)例,而倉庫則是用于存儲(chǔ)鏡像的地方。
隨后,老李詳細(xì)地介紹了Docker的各個(gè)組件,包括Docker客戶端、Docker守護(hù)進(jìn)程、Docker鏡像以及Docker容器。他為小張講解了每個(gè)組件的作用以及它們之間如何相互配合來實(shí)現(xiàn)Docker的功能。
在老李的深入講解下,小張逐漸理解了Docker的基本結(jié)構(gòu)和組件,并感到非常興奮。他決定在未來的工作中深入研究Docker,并將其應(yīng)用于項(xiàng)目中,以提高團(tuán)隊(duì)的效率和質(zhì)量。
Docker是一個(gè)開源平臺(tái),用于快速開發(fā)、部署和運(yùn)行應(yīng)用程序。它由多個(gè)組件組成,以下是Docker的主要組件:
- Docker Daemon:它是Docker的核心組件,負(fù)責(zé)管理鏡像、容器、網(wǎng)絡(luò)和卷等資源,并將Docker API暴露給客戶端。
- Docker Client:它是與Docker Daemon通信的主要接口,可以通過命令行或API向Daemon發(fā)送請(qǐng)求。
- Docker鏡像(Docker Image):它是一個(gè)只讀的模板,它包含了所有用于運(yùn)行應(yīng)用程序所需要的代碼、庫文件、環(huán)境變量和配置文件等內(nèi)容。
- Docker容器(Docker Container):它是基于Docker鏡像創(chuàng)建的可運(yùn)行實(shí)例。每個(gè)容器都是一個(gè)獨(dú)立的、輕量級(jí)的操作系統(tǒng),它們之間相互隔離并且可以共享主機(jī)的內(nèi)核。
- Docker Registry:它是用于存儲(chǔ)和分發(fā)Docker鏡像的公共或私有倉庫。Docker Hub是最流行的公共Registry,而Docker Trusted Registry則是一種常見的私有Registry解決方案。
- Docker Compose:它是一個(gè)工具,用于定義和運(yùn)行多個(gè)容器的應(yīng)用程序。使用Docker Compose,可以通過一個(gè)簡(jiǎn)單的配置文件來描述應(yīng)用程序的各個(gè)組件,從而使它們可以在一個(gè)統(tǒng)一的環(huán)境中運(yùn)行。
- Docker Swarm:它是Docker的原生集群管理工具,用于協(xié)調(diào)和管理多個(gè)Docker節(jié)點(diǎn)。使用Docker Swarm,可以將多個(gè)Docker節(jié)點(diǎn)組成一個(gè)大型的虛擬集群,并在其中部署、管理和擴(kuò)展Docker容器。
這些組件共同構(gòu)成了Docker的核心功能,使得開發(fā)人員和系統(tǒng)管理員能夠更加便捷地開發(fā)、部署和管理應(yīng)用程序。
接下來,我們深入到docker內(nèi)部,分析和學(xué)習(xí)一下它的底層實(shí)現(xiàn)核心技術(shù),并對(duì)常見的操作進(jìn)行實(shí)踐操作。
容器vs虛擬機(jī)
容器是一種沙盒技術(shù),可以看成集裝箱,這樣應(yīng)用之間就有了邊界而不至于互相干擾,方便搬動(dòng);
程序運(yùn)行起來的計(jì)算機(jī)執(zhí)行環(huán)境的總和 就是進(jìn)程;
容器的核心功能: 通過約束和修改進(jìn)程的動(dòng)態(tài)表現(xiàn),創(chuàng)造出一個(gè)邊界;
制造約束: Cgroups技術(shù)
修改進(jìn)程視圖: Namespace技術(shù)
容器的本質(zhì):
int pid = clone(main_function, stack_size,CLONE_NEWPID|SIGCHLD,NULL);
多次調(diào)用clone方法可以創(chuàng)建多個(gè)pid的進(jìn)程N(yùn)ameSpace ,每個(gè)namespace中都會(huì)人為自己是第一號(hào)進(jìn)程,看不到宿主機(jī)的進(jìn)程空間也看不到其它的pid的進(jìn)程空間;
除了PID Namespace ,linux還提供了Mount (掛載點(diǎn)信息), UTS , IPC , Network (網(wǎng)絡(luò)設(shè)備和配置), User這些namespace,來對(duì)各種不同的進(jìn)程上下文進(jìn)行障眼法操作;
只能看到namespace所限定的資源,文件,設(shè)備,狀態(tài),配置 ;對(duì)宿主機(jī)和其它的不相關(guān)程序完全看不到;
所以,容器是一種限定了namespace的進(jìn)程而已;

旁路式的輔助和管理工作;
| 對(duì)比項(xiàng)目 | 虛擬機(jī) | docker容器 |
|---|---|---|
| 真實(shí)存在 | 真實(shí)存在,并運(yùn)行一個(gè)完整的GuestOs | 不真實(shí)存在,只是輔助作用 |
| 會(huì)帶來額外的資源消耗和占用 | 100-200M內(nèi)存,通過虛擬化軟件的攔截和處理 | 無消耗 |
| 內(nèi)核 | 多個(gè)虛擬機(jī)可以使用不同的內(nèi)核 | 共享操作系統(tǒng)內(nèi)核 |
敏捷和高性能 是容器相比于虛擬機(jī)最大的優(yōu)勢(shì);
缺點(diǎn): 容器隔離的不徹底
多個(gè)容器之間使用的還是同一個(gè)宿主機(jī)的操作系統(tǒng)內(nèi)核;
linux內(nèi)核中很多資源和對(duì)象不能被namespace化,比如時(shí)間;(基于虛擬化和獨(dú)立內(nèi)核技術(shù)的容器實(shí)現(xiàn)隔離)
容器的底層實(shí)現(xiàn)基礎(chǔ)
cgroups
容器對(duì)宿主機(jī)操作系統(tǒng)來說是一個(gè)普通進(jìn)程,普通進(jìn)程的資源限制如果設(shè)置,會(huì)擠占別的進(jìn)程的資源。
Linux Control Groups : 限制一個(gè)進(jìn)程組使用的資源上限,包括: CPU, 內(nèi)存,磁盤,網(wǎng)絡(luò)帶寬。對(duì)進(jìn)程進(jìn)行優(yōu)先級(jí)設(shè)置,審計(jì),對(duì)進(jìn)程掛起和恢復(fù)操作。
/sys/fs/cgoup
可以對(duì)資源進(jìn)行獨(dú)特的限制:
| blkio | 塊設(shè)備設(shè)定io限制 |
|---|---|
| cpuset | 進(jìn)程分配單獨(dú)的cpu和對(duì)應(yīng)的內(nèi)存節(jié)點(diǎn) |
| memory | 設(shè)定內(nèi)存使用限制 |
在docker run啟動(dòng)的時(shí)候可以傳遞這些資源限制參數(shù):
--cpu-period=100000 --cpu-quota=20000
缺點(diǎn): 容器中 linux的 /proc top 顯示的是宿主機(jī)的信息 lxcfs
namespace
進(jìn)程看到的經(jīng)過特殊處理的視圖。
nt 設(shè)備掛載點(diǎn)
network 網(wǎng)絡(luò)
user 用戶目錄
UTS host
IPC 進(jìn)程通信
rootfs
進(jìn)入容器之后,看到的文件系統(tǒng),即容器鏡像,它保持了應(yīng)用在不同環(huán)境下的一致性。
主要使用了下面兩種技術(shù)來實(shí)現(xiàn)。
| 技術(shù) | 操作效果 |
|---|---|
| mount Namespace | 對(duì)容器進(jìn)程視圖的改變,伴隨著掛載操作才能生效; |
| 容器中看到的是一個(gè)獨(dú)立的隔離環(huán)境,而不是繼承宿主機(jī)的文件系統(tǒng); | |
| chroot/pivot_root | 改變進(jìn)程的根目錄 |
rootfs只包含了操作系統(tǒng)的文件,但是不包含操作系統(tǒng)的內(nèi)核。
這個(gè)就是容器鏡像: 掛載在容器的根目錄上,用來為容器進(jìn)程提供隔離后的執(zhí)行環(huán)境的文件系統(tǒng),就是所謂的容器鏡像。
一致性:
應(yīng)用+操作系統(tǒng)的文件和目錄;
鏡像是打包操作系統(tǒng)的能力;打通了應(yīng)用在本地開發(fā)和遠(yuǎn)端執(zhí)行環(huán)境之間難以逾越的鴻溝;
容器鏡像將會(huì)成為未來軟件的主流發(fā)布方式。
分層+聯(lián)合文件系統(tǒng) union file system ; AUFS ;
目錄:
/var/lib/docker/aufs/diff/layerid
/var/lib/docker/aufs/mnt

層分成三個(gè)部分:
- 可讀寫層;(修改層)
- init層;(配置層) /etc/hosts /etc/resolv.conf等配置信息 只針對(duì)當(dāng)前容器有效,不能提交
- 只讀層;(操作系統(tǒng)本身)
docker基本操作
1 購買一個(gè)cvm
為了學(xué)習(xí)和實(shí)驗(yàn)的目的,先購買一個(gè)遠(yuǎn)程的linux機(jī)器。
| 條目 | 選擇 |
|---|---|
| 1.進(jìn)入購買頁面 騰訊云的輕量級(jí)別cvm | https://console.cloud.tencent.com/cvm/overview |
| 2.新建實(shí)例, | 選擇 競(jìng)價(jià)實(shí)例 最便宜 |
| 3.選擇區(qū)域 | 選擇離你最近的區(qū)域 |
| 4.選擇最低的配置 | S6.MEDIUM2 2C4G 哪個(gè)最便宜買哪個(gè) |
| 5.鏡像選擇 | TencentOS,最新版本 |
| 6.帶寬 | 選擇按照使用流量計(jì)費(fèi) , 帶寬可以選擇10Mbps |
| 7.安全組 | 默認(rèn)放行所有的請(qǐng)求和響應(yīng) 這里是測(cè)試目的 |
| 8.設(shè)置root賬號(hào)和密碼 | 自己設(shè)置 |
| 9.其他的免費(fèi)的開通即可 | 總價(jià)格大概是0.1元/小時(shí) 流量 0.8元/GB 流量基本用不上 |
購買成功頁面如下:

然后使用一個(gè)ssh工具,比如xshell或者finalshell 登錄上去;

登錄進(jìn)去之后,先確認(rèn)一下cpu和內(nèi)存是否對(duì)得上。
top
然后按 1

2 安裝最新版本docker
指令:
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io
#啟動(dòng)docker
sudo systemctl start docker.service
#確認(rèn)docker可以使用
docker search redis

3 docker helloworld
一個(gè)簡(jiǎn)單的python程序。
from flask import Flask
import socket
import os
app = Flask(__name__)
@app.route('/')
def hello():
html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname())
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
Flask
# 使用官方提供的Python開發(fā)鏡像作為基礎(chǔ)鏡像
FROM python:2.7-slim
# 將工作目錄切換為/app
WORKDIR /app
# 將當(dāng)前目錄下的所有內(nèi)容復(fù)制到/app下
ADD . /app
# 使用pip命令安裝這個(gè)應(yīng)用所需要的依賴
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# 允許外界訪問容器的80端口
EXPOSE 80
# 設(shè)置環(huán)境變量
ENV NAME World
# 設(shè)置容器進(jìn)程為:python app.py,即:這個(gè)Python應(yīng)用的啟動(dòng)命令
CMD ["python", "app.py"]
代碼放在我的gitlhub上。
制作鏡像:
# cd 到Dcokerfile所在的目錄
docker build -t p1:v1 .
# 運(yùn)行
docker run --name p1 -p 80:80 -it p1:v1
運(yùn)行效果:

Dockerfile的每個(gè)語句執(zhí)行后,都會(huì)生成一個(gè)對(duì)應(yīng)的鏡像層。
查看本地鏡像指令:
docker images
4 保存鏡像到倉庫

docker tag p:v1 carter880522hn/app:pythondemo
docker login
#輸入你的docker hub的賬號(hào)密碼,即可推送到你的私有倉庫 當(dāng)然你也可以使用其他公有云廠商的鏡像倉庫
docker push carter880522hn/app:pythondemo

5 docker commit 原理
也可以進(jìn)到正在運(yùn)行的鏡像,做一些修改,然后提交之后,推送到基礎(chǔ)鏡像。
docker ps
# 可以找到運(yùn)行的容器id
docker commit ContainerId 遠(yuǎn)程tag
按照分層邏輯。
鏡像分為三層:
- 只讀層,操作系統(tǒng);
- init層, hosts, sysctl.conf文件;
- 讀寫層,程序相關(guān)的層;
docker commit實(shí)際上是在容器運(yùn)行之后,把最上層的可讀寫層,加上原來容器的只讀層,打包成了一個(gè)新的鏡像,只讀層是宿主機(jī)共享,不占用額外空間。
6 docker exec 原理
這個(gè)命令是如何進(jìn)入到容器內(nèi)部的呢?
容器本質(zhì)上是宿主機(jī)創(chuàng)建的進(jìn)程,進(jìn)程的namespace在機(jī)器上是實(shí)實(shí)在在文件。
查看容器在宿主機(jī)上的進(jìn)程編號(hào):
docker inspect --format '{{.State.Pid}}' bc917451cee1
查看宿主機(jī)上的namespace文件。
ls -lh /proc/容器PID/ns

容器內(nèi)部的namespace實(shí)際上在宿主機(jī)上有對(duì)應(yīng)的文件進(jìn)行對(duì)應(yīng)。 所以,我們可以使用 exec 去控制容器的文件。
linux中一個(gè)進(jìn)程是可以選擇加入到某個(gè)進(jìn)程已有的namespace,從而達(dá)到進(jìn)入進(jìn)程所在容器的目的。
下面的參數(shù),啟動(dòng)容器的時(shí)候,可以進(jìn)入另外一個(gè)容器的network namesapce;
--net container:4ddf4638572d
7 volume原理
容器內(nèi)部的新建的文件,如何讓宿主機(jī)獲取到?
宿主機(jī)上的文件,容器內(nèi)部如何訪問?
答案就是Volume,即數(shù)據(jù)卷。
語法如下:
docker run -v /local:/container ...
rootfs的掛載過程:
- 容器被創(chuàng)建,開啟Mount Namespace ;
- 執(zhí)行chroot或者 pivot_root ;
volume,是在 1,2之間的時(shí)機(jī),把volune指定的宿主機(jī)和容器目錄對(duì)應(yīng)關(guān)系進(jìn)行綁定,從而完成掛載;
做這個(gè)掛載的時(shí)候,容器進(jìn)程已經(jīng)創(chuàng)建了,Mount Namespace已經(jīng)開啟了,這個(gè)掛載信息只在容器可見,在宿主機(jī)是看不見這個(gè)掛載點(diǎn)的,保證了容器的隔離性不被Volume打破。
利用的是linux的 bind mount機(jī)制。linux的文件系統(tǒng)節(jié)點(diǎn)叫做inode, 文件指針叫做dentry , bind mount實(shí)際修改的是dentry , 這樣容器內(nèi)部和宿主機(jī)對(duì)應(yīng)的目錄修改,就指向了同一個(gè)inode .

volume中的文件,不會(huì)寫到鏡像,但是如果你這個(gè)時(shí)候進(jìn)行docker commit 操作, 這個(gè)volume對(duì)應(yīng)的容器目錄會(huì)被提交。
docker 鏡像結(jié)構(gòu)圖:

容器運(yùn)行環(huán)境
在宿主機(jī)上,
應(yīng)用的靜態(tài)表現(xiàn)即
應(yīng)用的動(dòng)態(tài)表現(xiàn)即容器,是一個(gè)使用cgroups和namesace 限制隔離的進(jìn)程組。
| 維度 | 說明 |
|---|---|
| 應(yīng)用靜態(tài)表現(xiàn) | 各種鏡像,鏡像即位于 /var/lib/docker/aufs/mnt上的 rootfs ; |
| 應(yīng)用的動(dòng)態(tài)表現(xiàn) | 容器,是一個(gè)使用cgroups和namesace 限制隔離的進(jìn)程組。 |
| 容器編排 | 把用戶提交的鏡像運(yùn)行起來 |
| 擴(kuò)展生態(tài) | CI/CD、監(jiān)控、安全、網(wǎng)絡(luò)、存儲(chǔ) |
k8s
k8s: google和redhat公司聯(lián)合推出的開源項(xiàng)目
價(jià)值: 基于容器構(gòu)建分布式系統(tǒng)的基礎(chǔ)依賴;
k8s的架構(gòu):

解決的問題: 編排,管理,調(diào)度用戶提交的作業(yè)。 大規(guī)模集群中的各種任務(wù),實(shí)際上存在各種關(guān)系,對(duì)這些關(guān)系的處理才是作業(yè)編排和管理系統(tǒng)最困難的地方。
docker只是CRI的一種實(shí)現(xiàn)方式。
| 物理部署/虛機(jī)部署 | k8s部署 |
|---|---|
| 應(yīng)用 | pod |
| 訪問關(guān)系 直接維護(hù)配置文件 | service |
| 配置信息管理 通過文件 | configmap/secret |
| daemon 做日志收集,災(zāi)難恢復(fù),數(shù)據(jù)備份 每臺(tái)主機(jī)只運(yùn)行一個(gè) | daemonset |
| 定時(shí)任務(wù) | cronjob |
| 一次性任務(wù) | job |
| 兩臺(tái)nginx做負(fù)載均衡 |
keepalive做一個(gè)vip
部署兩個(gè)nginx | 一個(gè)deployment,一個(gè)service |
處理思路:
1.通過pod,job來描述你管理的應(yīng)用;
2.定義一些平臺(tái)級(jí)的服務(wù)對(duì)象來編排: service,secret,autoscaler ;

小結(jié)
本文從一個(gè)了解docker的故事出發(fā),詳細(xì)分析了docker的三大底層核心技術(shù),cgroups,namesapce,rootfs ; 并從實(shí)踐出發(fā),購買一個(gè)遠(yuǎn)程的linux機(jī)器,安裝docker, 運(yùn)行一個(gè)簡(jiǎn)單的python應(yīng)用,并結(jié)合底層核心技術(shù),講述了docker exec , docker commit ,volume的實(shí)現(xiàn)原理,然后簡(jiǎn)單介紹了k8s的架構(gòu)和解決的問題,一些核心概念的引出;

原創(chuàng)不易,關(guān)注誠可貴,轉(zhuǎn)發(fā)價(jià)更高!轉(zhuǎn)載請(qǐng)注明出處,讓我們互通有無,共同進(jìn)步,歡迎溝通交流。
我會(huì)持續(xù)分享Java軟件編程知識(shí)和程序員發(fā)展職業(yè)之路,歡迎關(guān)注!