docker筆記

docker概念

鏡像與容器

docker鏡像是一個統一文件系統,由一系列只讀層堆疊而成。容器則是在只讀層堆頂加上一個讀寫層。而當一個容器被分配給隔離的進程空間后即可轉為運行態。

thin pool的概念

傳統的存儲空間分配方式成為fat provisioning,要求多少空間就分配多少空間,即使只使用了其中很小一部分。而thin provisioning則是在需要的時候才實際分配空間。thin pool建立在thin provisioning的思想上,它分配的是虛擬的chunk,而不是物理的block。
當docker使用Device Mapper作為存儲驅動,可以配置成direct-lvm模式,這種情況下存儲使用一個塊設備作為thin pool,thin pool由存儲實際數據的data space和存儲指針的metadata space組成。

OCI

Open Container Initiative,開放容器倡議(標準)。主要有兩個標準文檔:容器運行時標準 (runtime spec)和 容器鏡像標準(image spec)。這兩個協議通過 OCI runtime filesytem bundle 的標準格式連接在一起,OCI 鏡像可以通過工具轉換成 bundle,然后 OCI 容器引擎能夠識別這個 bundle 來運行容器。

OCI bundle

所謂bundle是指一個根文件系統和一個config.json文件,runc可以用這兩個輸入建立一個容器。

OCI state

OCI定義了容器的5種狀態,creating, created, running, stopped, paused.

—————————————————————————————————————————————————————————————————————
|       | ---start--> |       | ---error,exit,crash,kill--> |       |
|created|             |running| ---pause---> |paused|       |       |
|       |             |       | <--resume--- |      |       |stopped|
|       | ------------------create failed-----------------> |       |
—————————————————————————————————————————————————————————————————————
repository和registry

repository存儲一種特定的鏡像,其中可以包含多個版本(tag),例如docker.io/busybox是一個repository。
registry則是存有大量repository的服務器,例如docker.io是官方的registry。

docker組件間的交互

dockerd通過監聽unix、tcp socket接收請求,對外的API接口是REST方式,可以用curl直接發送GET或者POST請求來模擬通過docker調用指令的效果,例如:
curl '127.0.0.1:2375/v1.37/images/create?fromImage=hello-world&tag=latest' -X POST
與docker pull等效。
dockerd與containerd之間通過grpc協議通信,containerd與containerd-shim之間使用ttrpc協議通信(早期版本似乎用過其他通信方式)。

docker配置

daemon啟動參數配置文件

可以在/etc/sysconfig/docker中的OPTIONS中添加參數(這是默認位置,具體以docker service的狀態信息中的environment file為準),或者在/etc/docker/daemon.json中添加。

倉庫配置文件

/etc/containers/registries.conf,雖然是新版本的推薦配置方法,但單獨在這里添加似乎不能正常使用。
其他方法:在/etc/docker/daemon.json中添加如 "insecure-registries": ["***"],此配置不影響首選倉庫,不指定倉庫的情況下依然搜索官方倉庫docker.io,需要另外配置registry-mirror來修改默認的鏡像倉庫。

存儲配置文件

/etc/sysconfig/docker-storage,但同樣可以在daemon.json中設置。

網絡代理配置文件

/etc/systemd/system/docker.service.d/* 初始狀態下需要手動創建該目錄,配置文件名無限制,例如proxy.conf。

# 內容示例
[Service]
Environment="HTTPS_PROXY=https://***"
Environment="HTTP_PROXY=http://****"
Environment="NO_PROXY=localhost,huawei.com,athuawei.com,huaweidevice.com,hw3static.com,euleros-obs"

docker daemon會根據其啟動環境中的HTTP_PROXY,HTTPS_PROXY,NO_PROXY變量來決定代理行為,注意daemon的啟動環境中的變量和linux系統環境變量是兩回事,需要單獨設置。

docker服務的啟動參數

systemctl show docker可以查看docker服務的各種信息,其中EnvironmentFile項目設置的文件包含有環境變量的設置,例如/etc/sysconfig/docker-storage中的DOCKER_STORAGE_OPTIONS,然后在ExecStart項目的啟動命令中引用這些變量。
修改服務環境變量后需要systemctl daemon-reload之后再重啟服務。

配置DeviceMapper的direct lvm模式

https://docs.docker.com/storage/storagedriver/device-mapper-driver/
注意配成direct lvm后如果要刪除邏輯卷,需要先取消邏輯卷的激活狀態:lvchange -an docker/thinpool
刪除邏輯卷后如果要再重建邏輯卷使用docker,需要刪掉docker原來的/var/lib/docker/devicemapper目錄,因為其中存有原來的文件系統數據,與新建的邏輯卷不匹配,文件系統校驗會失敗。

cgroup目錄

在/var/run/docker/libcontainerd/xxxxx/config.json(可以用管道轉接python -m json.tool再轉接less格式化顯示內容)中找到容器的cgroup目錄,docker run可以用--cgroup-parent參數設置目錄路徑。

docker服務無法啟動時查看容器配置

可以查看/var/lib/docker/container/[container_id]/config.v2.json

容器擴容

docker info中有Base Device Size項顯示容器的默認容量,可以通過在docker-storage或daemon.json中添加storage-opt dm.basesize=**G調整,注意daemon.json中的格式要求,以及daemon.json和/etc/sysconfig/docker之間可能存在的配置沖突。修改后需要重啟docker服務,并且原有的images不會發生變化,如需調整只能刪掉后重下載鏡像。另外縮小容量需要刪除/var/lib/docker目錄,否則重啟服務時會報錯。

docker存儲位置

docker info中的Docker Root Dir項目是docker存儲的根目錄。創建的容器存儲在containers/下,下載的鏡像信息記錄在image/目錄下,但實際的數據存在于${Storage_Driver}目錄下。例如使用devicemapper存儲驅動,在容器運行著的情況下在devicemapper/mnt/[device_name_ID]下面可以直接看到容器中的統一文件系統。其中的device_name_ID可以在docker inspect中的DeviceName項查到。

日志

docker服務日志

通過以下rsyslog配置修改docker服務日志輸出目的地

/etc/rsyslog.d/docker.conf

$FileCreateMode 0644
template(name="DockerLogFileName" type="list") {
   constant(value="/var/log/docker/")
   property(name="syslogtag" securepath="replace" \
            regex.expression="docker/\\(.*\\)\\[" regex.submatch="1")
   constant(value="/docker.log")
}
if $programname == 'dockerd' then \
  /var/log/docker/combined.log
if $programname == 'dockerd' then \
  if $syslogtag contains 'docker/' then \
    ?DockerLogFileName
  else
    /var/log/docker/no_tag/docker.log
$FileCreateMode 0600
容器業務日志

docker run啟動容器時可以用--log-driver指定日志記錄方式。
docker默認的logging driver是json-file,在該模式下日志會被記錄在/var/lib/docker/<containerID>/<containerID>-json.log中。
如果指定journald作為logging driver,日志會被記錄到linux系統日志中,可以用journalctl -u docker來篩選查看。
如果指定syslog作為logging driver,日志會根據/etc/rsyslog.conf的配置寫入指定的文件中。

shim日志

默認情況下不打開,需要docker daemon設置debug級別的日志等級才會輸出到messages中。

常用操作

導入導出

docker save 將image保存成tar包,并且保留鏡像的分層信息;docker export 將容器保存成tar包,不保留分層信息。
docker load 可以將save產生的包導入成鏡像,還原出分層信息;docker import 導入的tar包甚至不是一個鏡像,僅僅是一個文件系統。

容器內外的文件傳輸

docker cp host_file container_ID:container_file 將外部的文件復制到容器中,兩個參數反過來也可以將文件從容器中取出。相當于cp -r,可以復制整個文件夾。目標路徑是已存在的文件夾時,會將源拷貝到該文件夾下,如果要讓源文件夾和目標文件夾合并,需要把源文件夾寫成source_dir/.的形式。

關于exec的umask

docker exec執行指令的時候無視容器內的umask設置,固定使用自己的umask,官方版本是022。

exec的等效操作

利用nsenter進入后臺容器,先用docker inspect --format "{{.State.Pid}}" container_ID查找容器的pid,然后nsenter --target $PID --mount --uts --ipc --net --pid -- env --ignore-environment -- /bin/bash即可進入容器環境。

容器權限

docker run的--cap-add和--cap-drop用以賦予、取消容器權限,權限類型參考ttp://man7.org/linux/man-pages/man7/capabilities.7.html

shm共享內存

/dev/shm是從內存中映射出來的虛擬文件系統,對應一塊內存空間。默認情況下各個容器的/dev/shm是獨立的,但可以在docker run參數中設置--ipc指定共享另一個容器的shm(ipc命名空間隔離進程間通信,而共享內存正是進程間通信手段)。
此設置也可在inspect信息中查詢IpcMode來確認。

查找被占用的掛載路徑

find /proc/*/mountinfo | xargs grep [****]

從mnt路徑反向查容器

docker ps -a -q | xargs docker inspect -f "{{.Id}} {{.GraphDriver.Data.DeviceName}}" | grep *****

鏡像相關

鏡像的各種ID
  • 鏡像id碼
    manifest.json的config字段是鏡像id,這是對鏡像配置文件進行sha256計算得到的。鏡像配置文件是tar包解壓后manifest.json以外的另一個json文件,文件名就是這個id本身。鏡像載入后配置文件存放在環境的/var/lib/docker/image/[graph_driver]/imagedb/content/sha256下。
  • 鏡像tar包分層路徑碼
    鏡像tar包解壓出來的manifest.json中記錄了各層的路徑碼,也是解壓出來各個存有layer.tar的目錄的路徑名。這是對分層的config.json數據結構做256校驗得到的。
    image/v1/imagev1.go: CreateID()創建此路徑碼
    路徑碼按照從上到下依次為最底層到最新層。
  • 鏡像分層DiffID
    在代碼中被稱為DiffID的編號是對各分層文件layer.tar進行256校驗得到的,分層的diffid可以在鏡像配置文件中查看,也可以docker inspect鏡像查看各分層的sha256碼。
    diffid按照從上到下依次為最底層到最新層。
  • 鏡像分層ChainID
    chainID:docker內容尋址機制采用的索引ID,其值根據當前層diffID和祖先層的chainID算得:
    若該鏡像層是最底層,那么其chainID 和 diffID 相同
    否則,chainID=sha256(父層chainID+" "+本層diffID)
    layer/layer.go: CreateChainID()創建此ID,注意代碼中ChainID是包含sha256:前綴的字符串,計算新層ID時是把前綴一起帶進去計算256碼的。
    鏡像存在于本地的情況下/var/lib/docker/image/overlay2/layerdb/sha256下面各個分層的路徑名就是該層的chainID。
  • 鏡像分層parent
    父鏡像層的chainID
查詢第三方倉庫中的鏡像

docker search默認只訪問官方倉庫docker.io,要搜索第三方倉庫需要加倉庫地址,如docker search rnd-dockerhub.huawei.com/busybox

secure registry的認證

registry本質上是server端的一個容器,拉起該容器時需要制定使用的證書(crt)和私鑰(key),其中crt是根據key生成的,包含registry信息和公鑰。這兩個文件的規范獲取方式是由CA簽發,下游環境應該是自簽。
用戶端pull/push鏡像使用的證書需要放在/etc/docker/certs.d下面,以registry域名和端口作為目錄名,例如/etc/docker/certs.d/hub.myreg.com:2121/。目錄下應該有key,cert,crt三個文件。其中crt文件就是registry端使用的那個證書,需要手動復制到用戶端,用戶端使用該文件驗證registry的身份;key是client端的私鑰,cert是根據私鑰生成的證書,證書中包含client的個體信息和公鑰,此證書會提供給registry端用于驗證用戶身份。
參考文檔:https://docs.docker.com/registry/insecure/https://docs.docker.com/engine/security/certificates/
另外docker login進行的認證是有別于上述TLS安全上的另一種認證,用于限制特定的用戶訪問倉庫。

運行時目錄相關

檢查exec運行的任務

可以通過/var/run/docker/containerd/[container_id]下的stdin,stdout等文件來判斷有沒有exec任務

runc相關

用runc查看容器狀態

比docker ps更準確
runc --root /var/run/docker/runtime-runc/moby list
其中root參數可以ps查看容器shim進程的-runtime-root參數

健康檢查

健康檢查可以在dockerfile中用HEALTHCHECK配置,這樣做出來的鏡像自帶健康檢查,也可以在docker run的時候用--health-cmd參數添加。
dockerfile中相關指令:
  HEALTHCHECK [option] CMD *****
option可用選項:
  --interval=DURATION (default: 30s)
  --timeout=DURATION (default: 30s)
  --start-period=DURATION (default: 0s)
  --retries=N (default: 3)
run指令中的相關參數:
  --health-cmd      Command to run to check health
  --health-interval    Time between running the check
  --health-retries    Consecutive failures needed to report unhealthy
  --health-timeout    Maximum time to allow one check to run
  --health-start-period  Start period for the container to initialize before starting health-retries countdown
  --no-healthcheck    Disable any container-specified HEALTHCHECK
注意檢查CMD只應該返回0或者1,返回值規定:
  0: success - the container is healthy and ready for use
  1: unhealthy - the container is not working correctly
  2: reserved - do not use this exit code

容器網絡

ethtool -S veth*** 可以查看veth網卡另一端的設備號,然后可以在ip link list中查看是什么設備。

ip_forward功能

容器網絡需要系統開啟ip_forward功能
sysctl net.ipv4.ip_forward 檢查系統ip_forward是否開啟
如果沒有開啟,修改/etc/sysctl.conf配置文件中的net.ipv4.ip_forward參數,然后sysctl -p使之生效;向/etc/sysconfig/network添加FORWARD_IPV4=YES,然后重啟network服務。

ipv6支持

docker默認關閉ipv6功能,需要在daemon配置中添加"ipv6": true,如果添加后報"could not find an available, non-overlapping IPv6 address",需要進一步添加"fixed-cidr-v6": "****"。

docker代碼

client與daemon的交互

docker daemon端接收到client端發送的API請求后,處理入口代碼在api/server/router/下,和容器相關的請求在container下,和鏡像相關的請求在image下。

daemon與containerd的交互

以grpc方式交互,api定義在api/grpc/types/api.proto中,該文件生成了api.pb.go,是go語言層面上的代碼定義。

docker-toolkit

app image

docker_load使用-i參數指定的APP_IMAGE實際上就是原本的鏡像名,即docker build制作鏡像時用-t參數指定的命名(和tag),如果不知道原本的鏡像名,可以查看manifest內容,第一層元素就是鏡像名。

hash

docker_save會計算各個tar包的hash值保存在manifest中,docker_load會校驗tar包hash值是否和manifest的記錄匹配,不匹配則會報諸如Invalid base image tarball的信息。

其他

刪除docker邏輯卷

dmsetup remove docker-thinpool
dmsetup remove docker-thinpool_tdata
dmsetup remove docker-thinpool_tmeta
lvremove /dev/docker/thinpool
如果要重建thinpool,重建后可能需要激活 lvchange -ay /dev/[vg_name]/[lv_name]

核對IO高的可能性

/var/log/sa下面有記錄io情況的日志,但時間很稀疏。

用curl直接發http請求

例如curl -XGET --unix-socket /var/run/docker.sock http://localhost/images/json,相當于docker images請求
curl -XGET --unix-socket /var/run/docker.sock http://localhost/containers/json?all=true,相當于docker ps -a請求
docker的http api可以查閱https://docs.docker.com/engine/api/v1.40/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。