DOCKER概覽
docker服務端是個daemon進程,管理docker容器并與客戶端交互。服務端監聽/var/run/docker.sock這個套接字,提供命令行交互和RESTful API兩種方式。
Docker的daemon進程需要用root運行,docker用戶組有root用戶相同的權限,所以是種安全隱患。另外,默認通信方式是非認證的模式,所以也是不安全的,但是可以設置TLS認證。
鏡像的概念不是Docker發明的,甚至可以參照理解windows系統的ghost盜版方式。
Docker鏡像是基于Union文件系統的一種分層結構,可以由一步一步的構建出來。可以參照理解純凈版的ghost windows(基礎鏡像),和安裝了office等軟件的ghost windows(FROM基礎鏡像)
鏡像也可以理解為容器的“源代碼”,后面會稍微展開一些。
上面說了鏡像是容器的源代碼,那么Registry就可以理解為代碼庫。
Registry官方提供一個公共庫,是收費的。企業也可以搭建自己的私有Registry。
容器就涉及Docker大名鼎鼎的集裝箱概念了,容器就是集裝箱,鏡像是貨物。容器不關心具體鏡像是什么,容器會按照相同的方式去管理鏡像,如創建、啟動、關閉、重啟、銷毀。
容器可以包含一個或多個進程,但是當前的主流做法(微服務)是單容器單進程。
Docker并不是憑空冒出來的,依賴于原來OS的一些功能。但是Docker也希望反向作用于容器標準化,于是有了libcontainer作為容器管理的抽象,后續好真正實現跨平臺。
Docker底層依賴linux內核的namespace、cgroups、Union File System(可選:Device Manager、AUFS、vfs、btrfs),主要用于資源的隔離和控制,如文件系統、進程、網絡等。所以Docker需要較新的內核版本,推薦是3.8以上。
Docker采用寫時復制(https://en.wikipedia.org/wiki/Copy-on-write)技術,通過分層的文件系統保證高效率和很少占用空間。
Docker可以日志化容器中的標準輸入輸出,STDOUT/STDERR/STDIN,也支持偽tty終端,提供一個交互shell,可以用來定位和調試。
Docker底層依賴的設計思路可以參考公司內李國柱的博客:
http://3ms.huawei.com/hi/blog/11100_1899017.html?h=h
細節可參照陳皓的系列博客:
http://coolshell.cn/articles/17010.html
http://coolshell.cn/articles/17029.html
http://coolshell.cn/articles/17049.html
http://coolshell.cn/articles/17061.html
http://coolshell.cn/articles/17200.html
Docker命令大概分四類,參考:
http://www.infoq.com/cn/articles/docker-command-line-quest
其中大部分和LINUX命令相同的名字的命令,基本也是相似的語義。
info命令用于查看docker基本信息,會返回所有容器的鏡像、執行和存儲驅動、基本配置。容器保存在/var/lib/docker/containers 。
inspect命令可以查看容器的最詳細信息。
images命令查看所有的鏡像。鏡像保存在宿主機的/var/lib/docker目錄。
run命令是運行容器,-i參數保證STDIN開啟,-t命令啟動偽tty,兩個參數可以保證啟動容器后的交互。run指定的鏡像,會先找本地,找不到會找倉庫,找到一次就緩存到本地,整個過程類似maven的管理。
--name會給容器指定一個名字,這個很有用,會被很多地方引用,所以必須是唯一的,如果有重名,必須先rm掉舊的容器。-d表示daemon,創建一個長期運行的容器。-- rm表示一次性運行的容器,運行完容器的進程會自動刪除容器。-h會指定容器的主機名,不指定的話默認是容器ID。--volumes-from指定從某個容器掛載所有的卷。
attach可以重新附著一個run的容器會話。
logs命令查看容器日志,-f參數使用和linux的tail –f類似。-t為日志加時間戳。
build構建鏡像、history可以查看這個構建的過程。
exec命令在容器內啟動新的進程,-d可以設置為daemon進程。
Docker鏡像就是文件系統一層一層疊加出來的。
最底層是一個引導文件系統bootfs,類似linux的引導文件系統。docker用戶不會和引導文件系統有交互,因為在容器啟動后,就會被移到內存中,引導文件系統會被unmount,以便留出更多內存供initrd(boot loader initialized RAM disk)磁盤鏡像使用。
引導文件系統上層是rootfs,rootfs是基于內存的文件系統,所有操作都在內存中完成;也沒有實際的存儲設備,所以不需要設備驅動程序的參與。基于以上原因,linux在啟動階段使用rootfs文件系統,當磁盤驅動程序和磁盤文件系統成功加載后,linux系統會將系統根目錄從rootfs切換到磁盤文件系統。
linux引導過程中,root文件系統先按只讀方式加載,引導完成,包括校驗完成,會被切換為讀寫模式。但是Docker中,root文件系統永遠都是只讀模式,Docker用union mount技術又會在root文件系統之上加載其他的文件系統,這些也是只讀的。
union mount會一次同時加載多個文件系統,但是從外看,只有一個文件系統。union mount把各層文件疊加到一起,這個看起來的最終的文件系統會包含所有底層的文件和目錄。這個外觀型的文件系統,就是鏡像。
所以,容易理解,鏡像也是可以一層一層疊加的,下層的鏡像就是上層的parent image,最底層的鏡像是base image。
當容器啟動時,Docker在鏡像的最頂層加載一個讀寫文件系統,在Docker中運行的程序就是在這個讀寫層執行的。剛啟動時,這個讀寫層是空的,發生變化時,變化的部分會體現在這一層,這就是所謂的寫時復制。
實際上,容器就是這個讀寫層+下面的鏡像層+配置。
鏡像的版本管理貌似是參考了git的思路。
Docker Registry管理鏡像,包含了鏡像、層、和一些關于鏡像的元數據。
Docker默認用的是官方的Registry,也就是Docker Hub,這個部分也是開源的,所以企業也可以搭建自己的私有Registry。Registry也可以運行在Docker中~
Docker Hub中有兩種類型的倉庫,user repository和top-level repository。用戶倉庫的鏡像是Docker用戶創建。頂級倉庫是Docker內部人員管理,一般是由Docker合作廠家提供,如操作系統發行商。用戶基于頂層的這些基礎鏡像來構建自己的鏡像。
用戶倉庫的命名由用戶名和倉庫名兩部分組成,如user/repo,頂層倉庫只包含倉庫名。
同一倉庫中的鏡像,可以通過加tag區分。相同鏡像ID可以有多個TAG。
docker run命令從鏡像啟動一個容器時,如果鏡像不在本地,docker默認會從Docker Hub下載鏡像,如果沒有顯示指定tag,會默認下載latest標簽的鏡像。也可以通過docker pull命令,預先加載到本地。
docker search命令,查找所有Docker Hub上公共的可用鏡像。docker push命令將鏡像推送到docker hub。
Docker Hub還有個比較有意思的功能是“Trusted Build”,將Docker Hub關聯GitHub上的Dockerfile文件,每次提交代碼,都會觸發一次構建,并創建新鏡像。
https://docs.docker.com/engine/reference/builder/
這里邊講的“構建”,并不是說從零構建,這會復雜的多。一般說的構建鏡像就是基于基礎鏡像去構建新鏡像。從零構建可以參考.
https://docs.docker.com/engine/userguide/eng-image/baseimages/
構建鏡像有兩種方式:
1. 單步命令:docker commit
2. 批量方式:Dockerfile文件 + docker build命令
可以類比于java中的單個編譯和ant腳本批量編譯。commit命令一般不推薦使用,因為Dockerfile功能更強,也更靈活。
上面說Registry類似git倉庫,其實commit命令其實類似git提交代碼。下載鏡像、基于它構建、構建鏡像的過程,其實可以類比于下載代碼,基于代碼修改,提交代碼的過程。
commit命令需要指定docker容器的ID(通過docker ps –l –q命令看剛創建的容器ID),一個目標倉庫和鏡像名。commit命令提交的只是差異部分,并不是全量提交,這也是分層的好處。
Dockerfile實際上是定義了一種DSL,提供了一種批量構建鏡像的方式。參考
Dockerfile所在的目錄就是構建環境,被成文build context。Docker會在構建鏡像時將這個目錄和目錄中的文件和子目錄上傳到Docker守護進程。Docker守護進程可以直接訪問構建所需的任何代碼、文件或其他的數據。
Dockerfile由一系列的指令和參數組成,每條指令,如FROM,都是大寫字母。指令后面要加一個參數。Dockerfile中的指令從上到下按順序執行。
每條指令都會創建一個新的鏡像,并自動提交。相當于基于父鏡像,每執行一條指令,就docker commit一次,然后新構建的鏡像作為下一條指令的父鏡像。直到所有指令執行完。
官方給了幾個dockerfile的實例,舉一個如下:
EXPOSE指定對外暴漏的接口,在實際宿主機上,還要做端口映射。方法是使用docker run命令的-p參數,可以隨機分配,或指定端口映射,設置可以指定某個特定的宿主機的映射方式。也可以使用-P參數,直接暴露所有EXPOSE指令指定的端口。
RUN是最基本的運行指令,會在當前鏡像中運行指定的指令。每條RUN都會創建一個新的鏡像層,如果成功,就會提交鏡像,然后繼續執行下一條。
FROM會引入基礎鏡像,所有命令基于此開始構建。
# 開頭是注釋。
docker build命令,會執行dockerfile,并返回最后一個成功的鏡像。
build命令推薦指定鏡像名和標簽,沒指定tag會默認打上latest標簽。也可以打多個標簽:
$ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
build命令執行過程中,如上面所說,每一步都是獨立的構建,每一步都會有一個鏡像ID:
所以,在執行某一個指令失敗時,會返回最后一個成功的鏡像。可以在這個鏡像的基礎上,手工執行出錯的指令,進行調試。而之前構建出來的鏡像也會被作為緩存,再次構建不是從頭開始,而是從失敗的位置開始構建。如果需要關閉緩存,在build命令后加—no-cache參數。
構建的過程,也可以通過docker history命令回溯。
CMD命令指定容器啟動時要運行的命令;對應的,上面說的RUN是鏡像構建時要運行的命令。參數格式和RUN命令相同,都支持數組形式參數:
CMD ["executable","param1","param2"] (exec form, this is the preferred form)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)
CMD在一個dockerfile中只有一條,即使配置了多條,也只有最后一條生效。
要注意的是,啟動命令可以被docker run命令指定的啟動命令覆蓋。
ENTRYPOINT和CMD非常類似,區別在于不能被覆蓋。docker run命令指定的任何參數都會被當作參數再次船體給ENTRYPOINT指令中指定的命令。
ENV命令,顧名思義,設置構建鏡像的環境變量。從構建的鏡像啟動容器,環境變量就會生效,且持久生效。對應的docker run命令-e指定的環境變量,只會在運行時有效。
容器啟動的工作目錄。CMD和ENTRYPOINT都會從這個目錄下執行。可以設置多次,運行不同指令,如:
WORKDIR /a RUN xxx WORKDIR b CMD xxx run命令的-w可以覆蓋工作目錄
指定鏡像以什么用戶運行。run命令的-u選項可以覆蓋
向容器添加卷。卷是docker設計的一種共享方式,可以繞過unionfs,在不同容器間持久化共享數據。可以用來存放調試代碼、日志管理、數據共享等用途。
卷有幾個特性:
1.卷在容器間共享和重用
2.對卷的修改是即時生效的
3.卷的修改不會對更新鏡像產生影響
4.直到所有引用的容器都被刪除,卷才會被刪除(要注意刪除容器,卷被誤刪)
5.卷這個參數不是必須的,容器可以選擇不共享
可以通過數組參數,指定多個卷。
提交或創建鏡像時,鏡像中不包含卷。
ADD把構建目錄下的文件和目錄復制到鏡像中。文件源支持正常路徑和URL模式。目的路徑如果不存在,會創建全路徑。
ADD有個奇葩的隱藏功能,會根據地址參數末尾的字符來判斷是文件還是目錄,比如以/結尾,就認為是目錄。
另一個奇葩的隱藏功能,如果將gzip等標準壓縮格式作為源,ADD會自動解壓。
COPY和ADD基本一樣,只是不會自動解壓。個人認為docker這種ADD/COPY,CMD/ENTRYPOINT的設計非常奇葩。這個命令創建的文件和目錄的UID和GID都會設為0。拷貝的內容包含文件系統的元數據。
這是個鉤子,當鏡像作為父鏡像構建時,鉤子會被調用。
ONBUILD后面可以加任何構建指令,插入的指令可以認為是緊跟在FROM之后執行的。
可以結合這個命令和構建緩存的功能,制作構建模板。
docker支持兩種網絡驅動,bridge 和overlay ,默認是網橋模式。
如果不特意指定,守護進程管理所有容器的網絡連接,都是通過docker0這個虛擬網橋:
ubuntu@ip-172-31-36-118:~$ ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:47:bc:3a:eb
inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:47ff:febc:3aeb/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:9001 Metric:1
RX packets:17 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1100 (1.1 KB) TX bytes:648 (648.0 B)
docker0接口有符合RFC1918的私有IP地址,范圍是172.16~172.30,上例中的網關地址是172.17.0.1,也是所有docker容器的網關地址。
docker創建一個容器就會創建一組互聯的網絡接口,一端是容器的eth0,一段是docker0,這樣Docker相當于管理了一個虛擬子網。
默認容器和宿主機網絡是不通的,除非指定打開端口。
docker容器重啟,就會改變IP地址。這對需要互聯的容器很不方便,所以docker提供了link功能。link可以把一個或多個docker容器連接起來,互相通信。
https://docs.docker.com/engine/userguide/networking/work-with-networks/#linking-containers-in-user-defined-networks
link需要引用容器的名字,所以上文也強調過名字的重要性。
docker run –link選項,可以創建兩個容器間的父子連接,有兩個參數,一個是要連接的容器名,另一個參數是連接后容器的別名。別名可以讓使用者不關注底層容器的名字。
因為是父子連接,所以不是雙向的,但是可以運行兩個命令來達到雙向的目的:
$ docker run --net=isolated_nw -itd --name=container4 --link container5:c5 busybox
01b5df970834b77a9eadbaff39051f237957bd35c4c56f11193e0594cfd5117c
$ docker run --net=isolated_nw -itd --name=container5 --link container4:c4 busybox
72eccf2208336f31e9e33ba327734125af00d1e1d2657878e2ee8154fbb23c7a
$ docker attach container4
/ # ping -w 4 c5
PING c5 (172.25.0.5): 56 data bytes
64 bytes from 172.25.0.5: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.0.5: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=3 ttl=64 time=0.097 ms
--- c5 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
/ # ping -w 4 container5
PING container5 (172.25.0.5): 56 data bytes
64 bytes from 172.25.0.5: seq=0 ttl=64 time=0.070 ms
64 bytes from 172.25.0.5: seq=1 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=2 ttl=64 time=0.080 ms
64 bytes from 172.25.0.5: seq=3 ttl=64 time=0.097 ms
--- container5 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.070/0.081/0.097 ms
$ docker attach container5
/ # ping -w 4 c4
PING c4 (172.25.0.4): 56 data bytes
64 bytes from 172.25.0.4: seq=0 ttl=64 time=0.065 ms
64 bytes from 172.25.0.4: seq=1 ttl=64 time=0.070 ms
64 bytes from 172.25.0.4: seq=2 ttl=64 time=0.067 ms
64 bytes from 172.25.0.4: seq=3 ttl=64 time=0.082 ms
--- c4 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.065/0.070/0.082 ms
/ # ping -w 4 container4
PING container4 (172.25.0.4): 56 data bytes
64 bytes from 172.25.0.4: seq=0 ttl=64 time=0.065 ms
64 bytes from 172.25.0.4: seq=1 ttl=64 time=0.070 ms
64 bytes from 172.25.0.4: seq=2 ttl=64 time=0.067 ms
64 bytes from 172.25.0.4: seq=3 ttl=64 time=0.082 ms
--- container4 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.065/0.070/0.082 ms
父容器可以直接訪問自容器的任意公開端口,而且只有配置--link連接的容器才能訪問。容器端口甚至不需要對宿主機開放。
連接只能運行在同個宿主機中,不能運行在不同宿主機。
配置link,會在父容器的dns配置文件,如/etc/hosts里寫入IP,重啟后,這個IP也會隨著刷新。不過docker技術變化很快,這種底層的技術也可能變化很快。
Docker生態有三種API:Registry API、Docker Hub API、Docker Remote API,前兩個是與倉庫交互,最后一個是與docker daemon進程交互。
REST API支持證書認證。
基本接口和docker命令都能一一對應,如/images/json返回結果和docker images命令非常類似。還可以控制容器,如/containers/start
容器編排有兩大類,一種是簡單的把容器連起來,如Fig;一種是更強大的容器編排,包括服務發現,動態伸縮,調度等,如Consul(服務發現),kubernetes、mesos(集群調度和管理),swarm。
每個話題都很大,這里不展開。
描述問題一般有個約定,要給出背景信息:
docker info和docker version的輸出
uname –a命令的輸出
要描述你想要什么,希望它如何工作,嘗試了什么。
提問前可以先搜索,參考提問的藝術。
轉載請注明出處:華為云博客 https://portal.hwclouds.com/blogs
Docker
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。