docker

跟着公司用了一段時間的 docker,一直感覺不明不白的。今天抽了點空從頭梳理了一遍,過程記錄如下。

概念

使用 docker 基礎的概念有三個:鏡像容器倉庫

鏡像功能上和我們裝系統時使用的 ghost 鏡像類似,可以看成一個系統環境的導出文件。

而容器就是用某個鏡像創建的系統。

比如我刻了一張 XX 特製 XP 系統盤,這張光盤就是一個鏡像,我拿着這個鏡像在 N 臺服務器上裝系統就等同於啓動了多個容器。容器的本質是進程,因此這裏有一點區別就是容器的啓動成本比真實的操作系統要低得多。

倉庫就是一個集中存放各種鏡像的,可以類比於 github,他們在概念設計上也很類似。比如都有地址和標籤的概念。通常地址用於標記項目,標籤用於標記版本。

倉庫也分爲公開和私有。官方的 dockerhub 是公開的源,如果你要部署閉源項目,需要自己搭建私有源,類似自己搞個 gitlab。

開始使用

安裝 dockerd

容易想象,因爲 docker 需要管理本地的很多鏡像文件,又需要管理衆多的容器進程,因此在部署端一定存在一個很可能名爲 dockerd 的進程。

確實如此,使用 yum 安裝好 docker-ce 後就可以添加服務並啓動它:

$ sudo systemctl enable docker
$ sudo systemctl start docker

這個管理進程使用的是 unix socket 來進行通信,而非 tcp。這個 unix socket 有訪問權限控制,只接受 root 和 docker 組訪問,因此你跑服務的用戶需要進組

$ sudo usermod -aG docker $USER

關於倉庫

如果是實驗性質的使用 docker,可以在 dockerhub 註冊個賬號使用公共倉庫。否則就需要通過 docker-registry 搭建私有倉庫。以下全部基於 dockerhub。

拉取鏡像

管理鏡像就像管理 git 項目。基礎命令是 docker image COMMAND,參考: docker image

一開始要使用的是 pull,用來從 dockerhub 拉取一個基礎鏡像。用法是

docker image pull [OPTIONS] NAME[:TAG|[@DIGEST](https://my.oschina.net/u/3392911)]

OPTIONS 可以暫時忽略,重點是後面的 鏡像唯一標識

NAME 的完整格式是包含倉庫地址和用戶的,比如 localhost:5000/test/hello/ 分隔的三塊分別是 倉庫地址用戶名項目名。可以看到,跟 git 地址很像:https://github.com/iqiyi/dpvs.git

TAG 或 DIGEST 是一個特殊版本的標誌,TAG 通常是可讀性更好的名字,如 1.0.0。而 DIGEST 更加精準。這個用法跟 git 也是一樣的。

有時這個長長的唯一標識還可以簡寫爲只包含項目名,這時默認使用 dockerhub 倉庫,tag 爲 lastest。比如對於 dockerhub.com/library/nginx 這個鏡像,你可以使用 docker image pull nginx 來拉取。

創建容器

當你有了一個鏡像以後就可以用它創建容器了,注意容器是持久化的一個概念,它的狀態有 也有 。就像你 ghost 了 N 臺電腦,這些電腦是有實體的,可以開機也可以關機。因爲之前提到過容器的本質是進程,可能會讓人誤以爲容器停下以後就被自動銷燬了,其實沒有。

創建一個新的容器使用 docker container COMMAND 子命令,因爲我們剛纔 pull 的是一個 nginx 鏡像,所以要讓他正確的跑起來的話需要加兩個參數:

docker container run --name mynginx -d -p 80:80 nginx

-d 的意思很好猜,就是守護進程。-p 是做了一個端口映射。因爲宿主機上的端口空間和容器的端口空間是獨立的,所以需要配置這個轉發(其實容器裏的一切都是獨立的)。因此宿主機的 80 端口只能被 dockerd 監聽,然後轉發給容器。這裏的 -p 80:80 就是把宿主機的 80 轉給了該容器。這時候在宿主機使用 curl -i http://localhost/ 就可以看到 nginx 的歡迎頁面了。

啓動容器時使用 --name 是個好習慣。

端口映射的意義在於,多個容器可以各自監聽自己端口空間裏的同一個端口。用 nginx 來說明比較不直觀,因爲通常我們只會起一個 nginx,他對 80 是獨佔的。那麼用 *GI 類的後臺服務來說明吧:我們在製作映像的時候,可以指定其監聽某個端口,比如 5000。那麼不管你用這個鏡像起幾個容器,他們監聽的都是自己的 5000 端口,然後我們只需要在 docker 端配置上端口轉發,就可以對外提供多個端口的服務了,比如我們讓 dockerd 監聽 5001~5009 之間的 9 個端口,分別轉發給 9 個容器。這樣我們就把部署相關的某些配置從項目代碼裏面拿了出來,這有利於項目的標準化交付。在使用 docker 之前,我部署 wsgi 服務的時候,通常每個實例都會有一個不同的 "instance.py" 文件,裏面描述了端口、線程等信息,這種文件其實是不太好維護的。

當然這些配置信息雖然拿出來了,依然需要在外層進行維護,但這個任務做起來要簡單的多,而且還有 compose 這個工具,compose 的說明在下面獨立成節。

可以看到 dockerd 對容器的管理在概念上非常類似 supervisord。做這樣的類比有助於幫助理解。

容器管理

容器管理使用 docker container 的剩餘子命令來完成,參考:docker container

仍然要強調的是,容器有狀態。stop 一個容器並沒有刪除他,如果你給 docker container ls 命令加上 -a 參數,就可以看到那些已經被停掉的容器。

因爲容器的運行環境完全是獨立的,有時候我們想 debug 的時候就需要進入容器裏面去,這個需求可以通過 docker container exec -ti fc588f356589 bash/sh 來實現,他的意思是在某個運行中的容器內執行某個命令,這裏我們選擇開一個 shell,並通過 -ti 參數與當前終端連接起來。

數據持久化

容器運行於完全獨立的文件系統有時候也是個麻煩,應用難免產生一些數據,我們希望這些數據可以存放在宿主機上而不是容器裏,比如日誌。

首先,把數據保存在容器的文件系統裏是個不好的想法,剛纔跑起來的 nginx 日誌其實被重定向到了標準輸出:

lrwxrwxrwx 1 root root   11 Nov  4 18:41 access.log -> /dev/stdout
lrwxrwxrwx 1 root root   11 Nov  4 18:41 error.log -> /dev/stderr

這些日誌被 dockerd 存下來了,可以通過 docker container logs CONTAINER_ID 來查看,我們的目標是將其寫入某個宿主文件,比如 /var/log/nginx/xx.log

就像剛纔把宿主機端口映射到容器裏一樣,我們也可以把宿主機的文件系統的一部分(目錄)映射進容器裏。只要在創建容器時傳入掛載選項即可:

--mount /var/log/nginx:/var/log/nginx

對於另一些靜態內容,如果你希望容器對其只讀,可以使用

--mount /var/log/nginx:/var/log/nginx:ro

在較早版本,比如 17 以前,可以使用 -v 代替 --mount 做同樣的事情,-v 是 --volume 的縮寫。這種文件系統的直接映射叫做 bind mount,是 docker 早期的一種方式。

新版本更鼓勵使用另一種名爲 Volume 的方式,數據卷是一種被 docker 管理的對象,他們存放於 docker 的數據目錄(var/lib/docker/)下,即 /var/lib/docker/volumes。在這種模式下,給一個 container 分配數據卷只需要(只能)指定該卷的名稱,路徑是固定的。如果要修改這個路徑就需要修改 docker 的數據目錄。

--mount source=ng-log,target=/var/log/nginx

--mount 的標準用法下參數是逗號分隔的鍵值對。這裏 source 就是宿主機的數據卷名稱,日誌的路徑是 /var/lib/docker/volumes/ng-log/_data/access.log

注意數據卷的數據不能被直接修改。

docker 還有一種存放數據的方法,這裏不使用持久化這個術語是因爲這次數據不再落盤了。tmpfs 用於提供一個基於內存的掛載點給容器使用。容器停止時便被銷燬。

--mount type=tmpfs,destination=/var/log/nginx

Dockerfile

以上是關於使用鏡像,Dockerfile 則是關於製作鏡像。

Dockerfile 是一個文本的描述文件,描述了從一個基礎鏡像開始做哪些步驟可以得到一個新的鏡像。也因此,鏡像是分層的。比如基於 nginx 鏡像,我給他添加一個配置文件就得到一個新的,結合了我的業務的鏡像。

這個 Dockerfile 一般可以放在工程的最外層目錄,或者接近最外層的目錄(按你的習慣)。以配置 nginx 靜態服務爲例,工程目錄爲:

$ ll

-rw-rw-r-- 1 xion xion    86 11月 27 16:08 Dockerfile
-rw-rw-r-- 1 xion xion 13972 11月 27 16:01 hello.html

其中 Dockerfile 內容爲:

FROM nginx

ADD hello.html /usr/share/nginx/html/

然後執行 docker build -t mynginx .,-t 是加個標籤的意思,支持 repo:tag 的格式,我只指定了名稱,那麼 tag 默認就是 latest。記得指定名稱是個好習慣~

這時查看鏡像列表就會發現本地多了一個鏡像:

$ docker images
REPOSITORY          TAG                 IMAGE ID
mynginx             latest              70710e43a5a2        
nginx               latest              40960efd7b8f        

把它跑起來就能訪問 hello.html 了。

Compose

以上的部分已經涵蓋了 docker 的基礎使用。docker 還提供了一些更高級的面向集羣的部署管理工具,但也要求和 docker 更高的集成度,部署配置使用一個 docker-compose.yml 文件來描述。使用這套部署系統部署的應用稱爲 Dockerized

非必須,是否使用看信仰程度吧。如果你已經有一套現成的部署邏輯和服務發現系統,可以不用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章