24 個 Docker 常見問題處理技巧
VSole2023-02-20 13:55:00

1Docker 遷移存儲目錄
默認情況系統會將 Docker 容器存放在 /var/lib/docker 目錄下
- [問題起因] 今天通過監控系統,發現公司其中一臺服務器的磁盤快慢,隨即上去看了下,發現
/var/lib/docker這個目錄特別大。由上述原因,我們都知道,在/var/lib/docker中存儲的都是相關于容器的存儲,所以也不能隨便的將其刪除掉。 - 那就準備遷移
docker的存儲目錄吧,或者對/var設備進行擴容來達到相同的目的。更多關于dockerd的詳細參數,請點擊查看 官方文檔 地址。 - 但是需要注意的一點就是,盡量不要用軟鏈, 因為一些
docker容器編排系統不支持這樣做,比如我們所熟知的k8s就在內。
# 發現容器啟動不了了 ERROR:cannot create temporary directory! # 查看系統存儲情況 $ du -h --max-depth=1
- [解決方法 1] 添加軟鏈接
# 1.停止docker服務 $ sudo systemctl stop docker # 2.開始遷移目錄 $ sudo mv /var/lib/docker /data/ # 3.添加軟鏈接 $ sudo ln -s /data/docker /var/lib/docker # 4.啟動docker服務 $ sudo systemctl start docker
- [解決方法 2] 改動 docker 配置文件
# [方式一] 改動docker啟動配置文件
$ sudo vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd --graph=/data/docker/
# [方式二] 改動docker啟動配置文件
$ sudo vim /etc/docker/daemon.json
{
"live-restore": true,
"graph": [ "/data/docker/" ]
}
- [操作注意事項] 在遷移
docker目錄的時候注意使用的命令,要么使用mv命令直接移動,要么使用cp命令復制文件,但是需要注意同時復制文件權限和對應屬性,不然在使用的時候可能會存在權限問題。如果容器中,也是使用root用戶,則不會存在該問題,但是也是需要按照正確的操作來遷移目錄。
# 使用mv命令 $ sudo mv /var/lib/docker /data/docker # 使用cp命令 $ sudo cp -arv /data/docker /data2/docker
- 下圖中,就是因為啟動的容器使用的是普通用戶運行進程的,且在運行當中需要使用
/tmp目錄,結果提示沒有權限。在我們導入容器鏡像的時候,其實是會將容器啟動時需要的各個目錄的權限和屬性都賦予了。如果我們直接是cp命令單純復制文件內容的話,就會出現屬性不一致的情況,同時還會有一定的安全問題。
Docker遷移存儲目錄
2Docker 設備空間不足
Increase Docker container size from default 10GB on rhel7.
- [問題起因一] 容器在導入或者啟動的時候,如果提示磁盤空間不足的,那么多半是真的因為物理磁盤空間真的有問題導致的。如下所示,我們可以看到
/分區確實滿了。
# 查看物理磁盤空間 $ df -Th Filesystem Size Used Avail Use% Mounted on /dev/vda1 40G 40G 0G 100% / tmpfs 7.8G 0 7.8G 0% /dev/shm /dev/vdb1 493G 289G 179G 62% /mnt
- 如果發現真的是物理磁盤空間滿了的話,就需要查看到底是什么占據了如此大的空間,導致因為容器沒有空間無法啟動。其中,
docker自帶的命令就是一個很好的能夠幫助我們發現問題的工具。
# 查看基本信息 # 硬件驅動使用的是devicemapper,空間池為docker-252 # 磁盤可用容量僅剩16.78MB,可用供我們使用 $ docker info Containers: 1 Images: 28 Storage Driver: devicemapper Pool Name: docker-252:1-787932-pool Pool Blocksize: 65.54 kB Backing Filesystem: extfs Data file: /dev/loop0 Metadata file: /dev/loop1 Data Space Used: 1.225 GB Data Space Total: 107.4 GB Data Space Available: 16.78 MB Metadata Space Used: 2.073 MB Metadata Space Total: 2.147 GB
- [解決方法] 通過查看信息,我們知道正是因為
docker可用的磁盤空間不足,所以導致啟動的時候沒有足夠的空間進行加載啟動鏡像。解決的方法也很簡單,第一就是清理無效數據文件釋放磁盤空間(清除日志),第二就是修改docker數據的存放路徑(大分區)。
# 顯示哪些容器目錄具有最大的日志文件 $ du -d1 -h /var/lib/docker/containers | sort -h # 清除您選擇的容器日志文件的內容 $ cat /dev/null > /var/lib/docker/containers/container_id/container_log_name
- [問題起因二] 顯然我遇到的不是上一種情況,而是在啟動容器的時候,容器啟動之后不久就顯示是
unhealthy的狀態,通過如下日志發現,原來是復制配置文件啟動的時候,提示磁盤空間不足。 - 后面發現是因為
CentOS7的系統使用的docker容器默認的創建大小就是10G而已,然而我們使用的容器卻超過了這個限制,導致無法啟動時提示空間不足。
2019-08-16 11:11:15,816 INFO spawned: 'app-demo' with pid 835 2019-08-16 11:11:16,268 INFO exited: app (exit status 1; not expected) 2019-08-16 11:11:17,270 INFO gave up: app entered FATAL state, too many start retries too quickly cp: cannot create regular file '/etc/supervisor/conf.d/grpc-app-demo.conf': No space left on device cp: cannot create regular file '/etc/supervisor/conf.d/grpc-app-demo.conf': No space left on device cp: cannot create regular file '/etc/supervisor/conf.d/grpc-app-demo.conf': No space left on device cp: cannot create regular file '/etc/supervisor/conf.d/grpc-app-demo.conf': No space left on device
- [解決方法 1] 改動 docker 啟動配置文件
# /etc/docker/daemon.json
{
"live-restore": true,
"storage-opt": [ "dm.basesize=20G" ]
}
- [解決方法 2] 改動 systemctl 的 docker 啟動文件
# 1.stop the docker service $ sudo systemctl stop docker # 2.rm exised container $ sudo rm -rf /var/lib/docker # 2.edit your docker service file $ sudo vim /usr/lib/systemd/system/docker.service # 3.find the execution line ExecStart=/usr/bin/dockerd and change it to: ExecStart=/usr/bin/dockerd --storage-opt dm.basesize=20G # 4.start docker service again $ sudo systemctl start docker # 5.reload daemon $ sudo systemctl daemon-reload
- [問題起因三] 還有一種情況也會讓容器無法啟動,并提示磁盤空間不足,但是使用命令查看發現并不是因為物理磁盤真的不足導致的。而是,因為對于分區的
inode節點數滿了導致的。
# 報錯信息 No space left on device
- [解決方法] 因為
ext3文件系統使用inode table存儲inode信息,而xfs文件系統使用B+ tree來進行存儲。考慮到性能問題,默認情況下這個B+ tree只會使用前1TB空間,當這1TB空間被寫滿后,就會導致無法寫入inode信息,報磁盤空間不足的錯誤。我們可以在mount時,指定inode64即可將這個B+ tree使用的空間擴展到整個文件系統。 - Docker+K8s+Jenkins 主流技術全解視頻資料【干貨免費分享】
# 查看系統的inode節點使用情況 $ sudo df -i # 嘗試重新掛載 $ sudo mount -o remount -o noatime,nodiratime,inode64,nobarrier /dev/vda1
- [補充知識] 文件儲存在硬盤上,硬盤的最小存儲單位叫做 扇區(
Sector)。每個扇區儲存512字節(相當于0.5KB)。操作系統讀取硬盤的時候,不會一個個扇區地讀取,這樣效率太低,而是一次性連續讀取多個扇區,即一次性讀取一個塊(block)。這種由多個扇區組成的塊,是文件存取的最小單位。塊的大小,最常見的是4KB,即連續八個sector組成一個block塊。文件數據都儲存在塊中,那么很顯然,我們還必須找到一個地方儲存文件的元信息,比如文件的創建者、文件的創建日期、文件的大小等等。這種儲存文件元信息的區域就叫做索引節點(inode)。每一個文件都有對應的inode,里面包含了除了文件名以外的所有文件信息。 inode也會消耗硬盤空間,所以硬盤格式化的時候,操作系統自動將硬盤分成兩個區域。一個是數據區,存放文件數據;另一個是inode區(inode table),存放inode所包含的信息。每個inode節點的大小,一般是128字節或256字節。inode節點的總數,在格式化時就給定,一般是每1KB或每2KB就設置一個inode節點。
# 每個節點信息的內容 $ stat check_port_live.sh File: check_port_live.sh Size: 225 Blocks: 8 IO Block: 4096 regular file Device: 822h/2082d Inode: 99621663 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 1006/ escape) Gid: ( 1006/ escape) Access: 2019-07-29 14:59:59.498076903 +0800 Modify: 2019-07-29 14:59:59.498076903 +0800 Change: 2019-07-29 23:20:27.834866649 +0800 Birth: - # 磁盤的inode使用情況 $ df -i Filesystem Inodes IUsed IFree IUse% Mounted on udev 16478355 801 16477554 1% /dev tmpfs 16487639 2521 16485118 1% /run /dev/sdc2 244162560 4788436 239374124 2% / tmpfs 16487639 5 16487634 1% /dev/shm
3Docker 缺共享鏈接庫
Docker 命令需要對/tmp 目錄下面有訪問權限
- [問題起因] 給系統安裝完
compose之后,查看版本的時候,提示缺少一個名為libz.so.1的共享鏈接庫。第一反應就是,是不是系統少安裝那個軟件包導致的。隨即,搜索了一下,將相關的依賴包都給安裝了,卻還是提示同樣的問題。
# 提示錯誤信息 $ docker-compose --version error while loading shared libraries: libz.so.1: failed to map segment from shared object: Operation not permitted
- [解決方法] 后來發現,是因為系統中
docker沒有對/tmp目錄的訪問權限導致,需要重新將其掛載一次,就可以解決了。
# 重新掛載 $ sudo mount /tmp -o remount,exec
4Docker 容器文件損壞
對 dockerd 的配置有可能會影響到系統穩定
- [問題起因] 容器文件損壞,經常會導致容器無法操作。正常的
docker命令已經無法操控這臺容器了,無法關閉、重啟、刪除。正巧,前天就需要這個的問題,主要的原因是因為重新對docker的默認容器進行了重新的分配限制導致的。
# 操作容器遇到類似的錯誤 b'devicemapper: Error running deviceCreate (CreateSnapDeviceRaw) dm_task_run failed'
- [解決方法] 可以通過以下操作將容器刪除/重建。
# 1.關閉docker $ sudo systemctl stop docker # 2.刪除容器文件 $ sudo rm -rf /var/lib/docker/containers # 3.重新整理容器元數據 $ sudo thin_check /var/lib/docker/devicemapper/devicemapper/metadata $ sudo thin_check --clear-needs-check-flag /var/lib/docker/devicemapper/devicemapper/metadata # 4.重啟docker $ sudo systemctl start docker
5Docker 容器優雅重啟
不停止服務器上面運行的容器,重啟 dockerd 服務是多么好的一件事
- [問題起因] 默認情況下,當
Docker守護程序終止時,它會關閉正在運行的容器。從Docker-ce 1.12開始,可以在配置文件中添加live-restore參數,以便在守護程序變得不可用時容器保持運行。需要注意的是Windows平臺暫時還是不支持該參數的配置。
# Keep containers alive during daemon downtime
$ sudo vim /etc/docker/daemon.yaml
{
"live-restore": true
}
# 在守護進程停機期間保持容器存活
$ sudo dockerd --live-restore
# 只能使用reload重載
# 相當于發送SIGHUP信號量給dockerd守護進程
$ sudo systemctl reload docker
# 但是對應網絡的設置需要restart才能生效
$ sudo systemctl restart docker
- [解決方法] 可以通過以下操作將容器刪除/重建。
# /etc/docker/daemon.yaml
{
"registry-mirrors": ["https://vec0xydj.mirror.aliyuncs.com"], # 配置獲取官方鏡像的倉庫地址
"experimental": true, # 啟用實驗功能
"default-runtime": "nvidia", # 容器的默認OCI運行時(默認為runc)
"live-restore": true, # 重啟dockerd服務的時候容易不終止
"runtimes": { # 配置容器運行時
"nvidia": {
"path": "/usr/bin/nvidia-container-runtime",
"runtimeArgs": []
}
},
"default-address-pools": [ # 配置容器使用的子網地址池
{
"scope": "local",
"base":"172.17.0.0/12",
"size":24
}
]
}
$ vim /etc/docker/daemon.json
{
"default-address-pools" : [
{
"base" : "172.240.0.0/16",
"size" : 24
}
]
}
6Docker 容器無法刪除
找不到對應容器進程是最嚇人的
- [問題起因] 今天遇到
docker容器無法停止/終止/刪除,以為這個容器可能又出現了dockerd守護進程托管的情況,但是通過ps -ef無法查到對應的運行進程。哎,后來開始開始查supervisor以及Dockerfile中的進程,都沒有。這種情況的可能原因是容器啟動之后,主機因任何原因重新啟動并且沒有優雅地終止容器。剩下的文件現在阻止你重新生成舊名稱的新容器,因為系統認為舊容器仍然存在。
# 刪除容器 $ sudo docker rm -f f8e8c3.. Error response from daemon: Conflict, cannot remove the default name of the container
- [解決方法] 找到
/var/lib/docker/containers/下的對應容器的文件夾,將其刪除,然后重啟一下dockerd即可。我們會發現,之前無法刪除的容器沒有了。
# 刪除容器文件 $ sudo rm -rf /var/lib/docker/containers/f8e8c3...65720 # 重啟服務 $ sudo systemctl restart docker.service
7Docker 容器中文異常
容器存在問題話,記得優先在官網查詢
- [問題起因] 今天登陸之前部署的
MySQL數據庫查詢,發現使用SQL語句無法查詢中文字段,即使直接輸入中文都沒有辦法顯示。
# 查看容器支持的字符集 root@b18f56aa1e15:# locale -a C C.UTF-8 POSIX
- [解決方法]
Docker部署的MySQL系統使用的是POSIX字符集。然而POSIX字符集是不支持中文的,而C.UTF-8是支持中文的只要把系統中的環境LANG改為"C.UTF-8"格式即可解決問題。同理,在K8S進入pod不能輸入中文也可用此方法解決。
# 臨時解決 docker exec -it some-mysql env LANG=C.UTF-8 /bin/bash # 永久解決 docker run --name some-mysql \ -e MYSQL_ROOT_PASSWORD=my-secret-pw \ -d mysql:tag --character-set-server=utf8mb4 \ --collation-server=utf8mb4_unicode_ci
8Docker 容器網絡互通
了解 Docker 的四種網絡模型
- [問題起因] 在本機部署
Nginx容器想代理本機啟動的Python后端服務程序,但是對代碼服務如下的配置,結果訪問的時候一直提示502錯誤。
# 啟動Nginx服務
$ docker run -d -p 80:80 $PWD:/etc/nginx nginx
server {
...
location /api {
proxy_pass http://localhost:8080
}
...
}
- [解決方法] 后面發現是因為
nginx.conf配置文件中的localhost配置的有問題,由于Nginx是在容器中運行,所以localhost為容器中的localhost,而非本機的localhost,所以導致無法訪問。 - 可以將
nginx.conf中的localhost改為宿主機的IP地址,就可以解決502的錯誤。
# 查詢宿主機IP地址 => 172.17.0.1
$ ip addr show docker0
docker0: mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:d5:4c:f2:1e brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:d5ff:fe4c:f21e/64 scope link
valid_lft forever preferred_lft forever
server {
...
location /api {
proxy_pass http://172.17.0.1:8080
}
...
}
- 當容器使用
host網絡時,容器與宿主共用網絡,這樣就能在容器中訪問宿主機網絡,那么容器的localhost就是宿主機的localhost了。
# 服務的啟動方式有所改變(沒有映射出來端口) # 因為本身與宿主機共用了網絡,宿主機暴露端口等同于容器中暴露端口 $ docker run -d -p 80:80 --network=host $PWD:/etc/nginx nginxx
9Docker 容器總線錯誤
總線錯誤看到的時候還是挺嚇人了
- [問題起因] 在
docker容器中運行程序的時候,提示bus error錯誤。
# 總線報錯 $ inv app.user_op --name=zhangsan Bus error (core dumped)
- [解決方法] 原因是在
docker運行的時候,shm分區設置太小導致share memory不夠。不設置--shm-size參數時,docker給容器默認分配的shm大小為64M,導致程序啟動時不足。具體原因還是因為安裝pytorch包導致了,多進程跑任務的時候,docker容器分配的共享內存太小,導致torch要在tmpfs上面放模型數據用于子線程的 共享不足,就出現報錯了。
# 問題原因 root@18...35:/opt/app# df -TH Filesystem Type Size Used Avail Use% Mounted on overlay overlay 2.0T 221G 1.4T 3% / tmpfs tmpfs 68M 0 68M 0% /dev shm tmpfs 68M 41k 68M 1% /dev/shm # 啟動docker的時候加上--shm-size參數(單位為b,k,m或g) $ docker run -it --rm --shm-size=200m pytorch/pytorch:latest # 在docker-compose添加對應配置 $ shm_size: '2gb'
- [解決方法] 還有一種情況就是容器內的磁盤空間不足,也會導致
bus error這樣的報錯,所以如果出現了,清除多余文件和目錄或者分配一個大的磁盤空間,就可以解決了。
# 磁盤空間不足 $ df -Th Filesystem Type Size Used Avail Use% Mounted on overlay overlay 1T 1T 0G 100% / shm tmpfs 64M 24K 64M 1% /dev/shm
10Docker NFS 掛載報錯
NFS 掛載之后容器程序使用異常為內核版本太低導致的
- [問題起因] 我們將服務部署到
openshift集群中,啟動服務調用資源文件的時候,報錯信息如下所示。從報錯信息中,得知是在Python3程序執行read_file()讀取文件的內容,給文件加鎖的時候報錯了。但是奇怪的是,本地調試的時候發現服務都是可以正常運行的,文件加鎖也是沒問題的。后來發現,在openshift集群中使用的是NFS掛載的共享磁盤。
# 報錯信息 Traceback (most recent call last): ...... File "xxx/utils/storage.py", line 34, in xxx.utils.storage.LocalStorage.read_file OSError: [Errno 9] Bad file descriptor # 文件加鎖代碼 ... with open(self.mount(path), 'rb') as fileobj: fcntl.flock(fileobj, fcntl.LOCK_EX) data = fileobj.read() return data ...
- [解決方法] 從下面的信息得知,要在
Linux中使用flock()的話,就需要升級內核版本到2.6.11+才行。后來才發現,這實際上是由RedHat內核中的一個錯誤引起的,并在kernel-3.10.0-693.18.1.el7版本中得到修復。所以對于NFSv3和NFSv4服務而已,就需要升級Linux內核版本才能夠解決這個問題。
# https://t.codebug.vip/questions-930901.htm $ In Linux kernels up to 2.6.11, flock() does not lock files over NFS (i.e., the scope of locks was limited to the local system). [...] Since Linux 2.6.12, NFS clients support flock() locks by emulating them as byte-range locks on the entire file.
11Docker 使用默認網段
啟動的容器網絡無法相互通信,很是奇怪!
- [問題起因] 我們在使用
Docker啟動服務的時候,發現有時候服務之前可以相互連通,而有時啟動的多個服務之前卻出現了無法訪問的情況。究其原因,發現原來是因為使用的內部私有地址網段不一致導致的。有的服務啟動到了172.17 - 172.31的網段,有的服務跑到了192.169.0 - 192.168.224的網段,這樣導致服務啟動之后出現無法訪問的情況(默認情況下,有下面這個兩個網段可供其使用)。
Docker默認使用網段
- [解決方法] 上述問題的處理方式,就是手動指定
Docker服務的啟動網段,二選一就可以了。
# 查看docker容器配置
$ cat /etc/docker/daemon.json
{
"registry-mirrors": ["https://vec0xydj.mirror.aliyuncs.com"],
"default-address-pools":[{"base":"172.17.0.0/12", "size":24}],
"experimental": true,
"default-runtime": "nvidia",
"live-restore": true,
"runtimes": {
"nvidia": {
"path": "/usr/bin/nvidia-container-runtime",
"runtimeArgs": []
}
}
}
12Docker 服務啟動串臺
使用 docker-compose 命令各自啟動兩組服務,發現服務會串臺!
- [問題起因] 在兩個不同名稱的目錄目錄下面,使用
docker-compose來啟動服務,發現當A組服務啟動完畢之后,再啟動B組服務的時候,發現A組當中對應的一部分服務又重新啟動了一次,這就非常奇怪了!因為這個問題的存在會導致,A組服務和B組服務無法同時啟動。之前還以為是工具的Bug,后來請教了 “上峰”,才知道了原因,恍然大悟。
# 服務目錄結構如下所示 A: /data1/app/docker-compose.yml B: /data2/app/docker-compose.yml
- [解決方法] 發現
A和B兩組服務會串臺的原因,原來是docker-compose會給啟動的容器加label標簽,然后根據這些label標簽來識別和判斷對應的容器服務是由誰啟動的、誰來管理的,等等。而這里,我們需要關注的label變量是com.docker.compose.project,其對應的值是使用啟動配置文件的目錄的最底層子目錄名稱,即上面的app就是對應的值。我們可以發現,A和B兩組服務對應的值都是app,所以啟動的時候被認為是同一個,這就出現了上述的問題。如果需要深入了解的話,可以去看對應源代碼。
Docker服務啟動串臺
# 可以將目錄結構調整為如下所示 A: /data/app1/docker-compose.yml B: /data/app2/docker-compose.yml A: /data1/app-old/docker-compose.yml B: /data2/app-new/docker-compose.yml
- 或者使用
docker-compose命令提供的參數-p手動指定標簽,來規避該問題的發生。
# 指定項目項目名稱 $ docker-compose -f ./docker-compose.yml -p app1 up -d
13Docker 命令調用報錯
在編寫腳本的時候常常會執行 docker 相關的命令,但是需要注意使用細節!
- [問題起因]
CI更新環境執行了一個腳本,但是腳本執行過程中報錯了,如下所示。通過對應的輸出信息,可以看到提示說正在執行的設備不是一個tty。
Docker命令調用報錯
- 隨即,查看了腳本發現報錯地方是執行了一個
exec的docker命令,大致如下所示。很奇怪的是,手動執行或直接調腳本的時候,怎么都是沒有問題的,但是等到CI調用的時候怎么都是有問題。后來好好看下,下面這個命令,注意到-it這個參數了。
# 腳本調用docker命令 docker exec -it psql -Upostgres ......
- 我們可以一起看下
exec命令的這兩個參數,自然就差不多理解了。
編號
參數
解釋說明
1
即使沒有附加也保持 STDIN 打開;如果你需要執行命令則需要開啟這個選項 2
分配一個偽終端進行執行;一個連接用戶的終端與容器 stdin 和 stdout 的橋梁 |
- [解決方法]
docker exec的參數-t是指Allocate a pseudo-TTY的意思,而CI在執行job的時候并不是在TTY終端中執行,所以-t這個參數會報錯。同時在 『stackoverflow』也有人給出原因,可以自行查看。
Docker命令調用報錯
14Docker 定時任務異常
在 Crontab 定時任務中也存在 Docker 命令執行異常的情況!
- [問題起因] 今天發現了一個問題,就是在備份
Mysql數據庫的時候,使用docker容器進行備份,然后使用Crontab定時任務來觸發備份。但是發現備份的MySQL數據庫居然是空的,但是手動執行對應命令切是好的,很奇怪。
# Crontab定時任務 0 */6 * * * \ docker exec -it sh -c \ 'exec mysqldump --all-databases -uroot -ppassword ......'
- [解決方法] 后來發現是因為執行的
docker命令多個-i導致的。因為Crontab命令執行的時候,并不是交互式的,所以需要把這個去掉才可以。總結就是,如果你需要回顯的話則需要-t選項,如果需要交互式會話則需要-i選項。
編號
參數
解釋說明
1
即使沒有附加也保持 STDIN 打開;如果你需要執行命令則需要開啟這個選項 2
分配一個偽終端進行執行;一個連接用戶的終端與容器 stdin 和 stdout 的橋梁 |
15Docker 變量使用引號
compose 里邊環境變量帶不帶引號的問題!
- [問題起因] 使用過
compose的朋友可能都遇到過,在編寫啟服務啟動配置文件的時候,添加環境變量時到底是使用單引號、雙引號還是不使用引號的問題?時間長了,我們可能會將三者混用,認為其效果是一樣的。但是后來,發現的坑越來越多,才發現其越來越隱晦。 - 反正我是遇到過很多問題,都是因為添加引號導致的服務啟動異常的,后來得出的結論就是一律不使引號。裸奔,體驗前所未有的爽快!直到現在看到了
Github中對應的 issus 之后,才終于破案了。
# 在Compose中進行引用TEST_VAR變量,無法找到 TEST_VAR="test" # 在Compose中進行引用TEST_VAR變量,可以找到 TEST_VAR=test # 后來發現docker本身其實已經正確地處理了引號的使用 docker run -it --rm -e TEST_VAR="test" test:latest
- [解決方法] 得到的結論就是,因為
Compose解析yaml配置文件,發現引號也進行了解釋包裝。這就導致原本的TEST_VAR="test"被解析成了'TEST_VAR="test"',所以我們在引用的時候就無法獲取到對應的值。現在解決方法就是,不管是我們直接在配置文件添加環境變量或者使用env_file配置文件,能不使用引號就不適用引號。 - 需要注意的是環境變量配置的是日志格式的話(
2022-01-01),如果使用的是Python的yaml.load模塊的話,會被當做是date類型的,這是如果希望保持原樣信息的話,可以使用'/"引起來將其變成字符串格式的。
16Docker 刪除鏡像報錯
無法刪除鏡像,歸根到底還是有地方用到了!
- [問題起因] 清理服器磁盤空間的時候,刪除某個鏡像的時候提示如下信息。提示需要強制刪除,但是發現及時執行了強制刪除依舊沒有效果。
# 刪除鏡像 $ docker rmi 3ccxxxx2e862 Error response from daemon: conflict: unable to delete 3ccxxxx2e862 (cannot be forced) - image has dependent child images # 強制刪除 $ dcoker rmi -f 3ccxxxx2e862 Error response from daemon: conflict: unable to delete 3ccxxxx2e862 (cannot be forced) - image has dependent child images
- [解決方法] 后來才發現,出現這個原因主要是因為
TAG,即存在其他鏡像引用了這個鏡像。這里我們可以使用如下命令查看對應鏡像文件的依賴關系,然后根據對應TAG來刪除鏡像。
# 查詢依賴 - image_id表示鏡像名稱
$ docker image inspect --format='{{.RepoTags}} {{.Id}} {{.Parent}}' $(docker image ls -q --filter since=)
# 根據TAG刪除鏡像
$ docker rmi -f c565xxxxc87f
# 刪除懸空鏡像
$ docker rmi $(docker images --filter "dangling=true" -q --no-trunc)
17Docker 普通用戶切換
切換 Docker 啟動用戶的話,還是需要注意下權限問題的!
- [問題起因] 我們知道在
Docker容器里面使用root用戶的話,是不安全的,很容易出現越權的安全問題,所以一般情況下,我們都會使用普通用戶來代替root進行服務的啟動和管理的。今天給一個服務切換用戶的時候,發現Nginx服務一直無法啟動,提示如下權限問題。因為對應的配置文件也沒有配置var相關的目錄,無奈 ??♀ !?
# Nginx報錯信息 nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (13: Permission denied) 2020/11/12 15:25:47 [emerg] 23#23: mkdir() "/var/cache/nginx/client_temp" failed (13: Permission denied)
- [解決方法] 后來發現還是
nginx.conf配置文件,配置的有問題,需要將Nginx服務啟動時候需要的文件都配置到一個無權限的目錄,即可解決。
user www-data;
worker_processes 1;
error_log /data/logs/master_error.log warn;
pid /dev/shm/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
gzip on;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
client_body_temp_path /tmp/client_body;
fastcgi_temp_path /tmp/fastcgi_temp;
proxy_temp_path /tmp/proxy_temp;
scgi_temp_path /tmp/scgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
include /etc/nginx/conf.d/*.conf;
}
18Docker 綁定到 IPv6 上
Docker 服務在啟動的時候,將地址綁定到 IPv6 地址上面了,提示報錯信息!
- [問題起因] 物理機器更新了對應補丁之后,重啟了服務,導致原本可以正常啟動的
docker-compose服務提示如下報錯信息。不清楚是否修改了操作系統的相關配置,還是對應docker進行的其他方面的配置,比如修改/etc/docker/daemon.json或者docker的service啟動文件。
# Docker的報錯信息 docker run -p 80:80 nginx:alpine succeeds. Previously, this was failing with Error \ starting userland proxy: listen tcp6 [::]:80: socket: address family not supported by protocol.
- [解決方法] 通過如上所示的報錯信息,可以看到服務的啟動端口綁定到了
tcp6上面了,但是對應的socket發現系統本身并不支持。這時,我們一看下對應的操作系統ipv6的設置,發現系統禁用了,所有的ipv6地址。需要了解的朋友,可以參考 fix port forwarding with ipv6.disable=1 和 cannot start if ipv6 is disabled on host 這兩個issus來獲取更多信息。
# 操作系統配置 $ cat /etc/sysctl.conf | grep ipv6 net.ipv6.conf.all.disable_ipv6=1
- [方法一] 最為簡單的解決方法,就是在
docker-compose.yml文件中,手動指定將對應服務的端口綁定到ipv4上面,如下所示。
version: "3" services: app: restart: on-failure container_name: app_web image: app:latest ports: - "0.0.0.0:80:80/tcp" volumes: - "./app_web:/data" networks: - app_network networks: app_network:
- [方法二] 或者修改
/etc/docker/daemon.json文件,在配置中,阻止Docker錯誤的將端口映射到IPv6上,即可達到同樣的效果,且不用再次修改多個服務的啟動配置文件了。
# 修改配置
$ vim /etc/docker/daemon.json
{
"ipv6": false,
"fixed-cidr-v6": "2001:db8:1::/64"
}
# 重啟服務
$ systemctl reload docker
- [方法三]
Docker默認情況下會同時將端口映射于IPv4與IPv6兩者上,而且有的時候會出現只綁定到了IPv6,導致服務無法正常訪問的情況。現在通用的始終還是IPv4地址,因此最簡單的做法就是關閉IPv6地址。詳細的配置,可以參考 Port redirecting binding to IPv6 but not IPv4 interfaces 這個issus地址。
# 修改系統配置 echo '1' > /proc/sys/net/ipv6/conf/lo/disable_ipv6 echo '1' > /proc/sys/net/ipv6/conf/lo/disable_ipv6 echo '1' > /proc/sys/net/ipv6/conf/all/disable_ipv6 echo '1' > /proc/sys/net/ipv6/conf/default/disable_ipv6 # 重啟網絡 $ /etc/init.d/networking restart # 最后檢測是否已關閉IPv6 ip addr show | grep net6
1919. Docker 容器啟動超時
Docker 服務在啟動的時候,提示超時,被直接終止了!
- [問題起因] 使用
docker-compose啟動容器的時候,等待了很久的時候(大約2-3分鐘左右),之后提示如下信息。通過閱讀信息內容,可以看到是因為超時導致的,提示可以通過設置環境變量,加大超時的時間。
$ docker-compose up -d ERROR: for xxx UnixHTTPConnectionPool(host='localhost', port=None): Read timed out. (read timeout=70) ERROR: An HTTP request took too long to complete. Retry with --verbose to obtain debug information. If you encounter this issue regularly because of slow network conditions, consider setting COMPOSE_HTTP_TIMEOUT to a higher value (current value: 60).
- [解決方法] 按照提示設置的環境變量之后,再次啟動發現確實可以正常啟動了,但是還是能夠感覺到有些慢。
$ sudo vim /etc/profile export COMPOSE_HTTP_TIMEOUT=500 export DOCKER_CLIENT_TIMEOUT=500
- 排除了下啟動流程,因為容器啟動有映射目錄到容器里面且目錄大小比較大,所以懷疑是因為
i/o導致的。隨即使用iotop命令查看服務器目前的i/o情況,發現存在很多個rg命令,且都處于100%左右。查了下,發現是vscode遠程服務器啟動的搜索目錄結構的進程,西八,有些坑呀!
$ sudo iotop 4269 be/4 escape 15.64 K/s 0.00 B/s 0.00 % 98.36 % rg --files --hidden 4270 be/4 escape 28.15 K/s 0.00 B/s 0.00 % 97.46 % rg --files --hidden 4272 be/4 escape 31.27 K/s 0.00 B/s 0.00 % 97.39 % rg --files --hidden 4276 be/4 escape 34.40 K/s 0.00 B/s 0.00 % 96.98 % rg --files --hidden
20Docker 端口網絡限制
如果發現服務都一切正常,但是無法無法訪問的話,則多為網絡問題!
- [問題起因] 啟用服務之后,登錄跳轉發現直接
502報錯了。排除了配置等相關原因都沒有任何問題(做過相關測試),這就非常奇怪了!
# 部署服務架構 nginx(80) -> web1(8080) -> web2(8081) # 報錯信息如下所示 nginx connect() failed (113: No route to host) while connecting to upstream
- [解決方法] 根據錯誤信息可知,是因為沒有路由到指定的
host導致了,隨即看了下防火墻是開著的,看了日志發現被過濾掉了,西八!問題找到了,現在需要做的就是,要么添加防火墻規則,要么關閉防火墻。
# 檢查開放的端口 $ sudo firewall-cmd --permanent --zone=public --list-ports # 開啟需要路由的端口 $ sudo firewall-cmd --permanent --zone=public --add-port=8080/tcp $ sudo firewall-cmd --permanent --zone=public --add-port=8081/tcp # 配置立即生效 firewall-cmd --reload # 關閉防火墻 $ sudo systemctl stop firewalld.service # 禁用自啟動 $ sudo systemctl disable firewalld.service
21Docker 無法獲取鏡像
新初始化的機器,無法獲取私有倉庫的鏡像文件!
- [問題起因] 機器初始化之后,使用如下命令登錄私有
docker倉庫,發現提示無法獲取對應鏡像,但是在其他機器上面獲取該鏡像就可以執行成功,這就非常奇怪了!
# 登錄私有倉庫 $ echo '123456' | docker login -u escape --password-stdin docker.escapelife.site # 異常信息提示 $ sudo docker pull docker.escapelife.site/app:0.10 Error response from daemon: manifest for docker.escapelife.site/app:0.10 not found: manifest unknown: manifest unknown
- [解決方法] 太坑了,我還以為我發現某個隱藏的
bug了,可勁的排查,最后發現,原來是自己鏡像包名字寫錯了,應該寫成0.0.10的,自己卻寫成了0.10。這里,紀念一下,以后碰到上述報錯,那肯定是鏡像不存在的。
# 登錄私有倉庫之后會在用戶家目錄下生成一個docker配置
# 其用來記錄docker私有倉庫的登錄認證信息(是加密過的信息但不安全) => base64
$ cat .docker/config.json
{
"auths": {
"docker.escapelife.site": {
"auth": "d00u11Fu22B3355VG2xasE12w=="
}
}
}
22Docker 使容器不退出
如何使使用 docker-compose 啟動的容器服務 hang 住而不退出
- [問題起因] 有時候我們啟動的服務,因為某些問題(
bug)導致服務無法正常啟動,就會出現容器無限重啟(restart: on-failure)的情況,這時就很不利于排除問題。
? docker ps -a 4e6xxx9a4 app:latest "/xxx/…" 26 seconds ago Restarting (1) 2 seconds ago
- [解決方法] 這時我們就需要根據,服務構建使用命令來決定是用什么命令來
hang住服務。卡住的原理,就類似于使用/bin/bash進入容器是一樣的,這里我就不過多解釋了。
# 類似原理 docker run -it --rm --entrypoint=/bin/bash xxx/app:latest # 使用Command命令 tty: true command: tail -f /dev/null # 使用Entrypoint命令 tty: true entrypoint: tail -f /dev/null
- 同理,我們在使用
docker-compose或者k8s平臺部署服務的時候,也有時會因為啟動問題需要,使啟動的服務不直接退出,來手動調試和排查問題原因。所以,我這里記錄下其不同部署方式的,暫停方式。
# Compose version: "3" services: app: image: ubuntu:latest tty: true entrypoint: /usr/bin/tail command: "-f /dev/null" # K8S apiVersion: v1 kind: Pod metadata: name: ubuntu spec: containers: - name: ubuntu image: ubuntu:latest command: ["/bin/bash", "-c", "--"] args: ["while true; do sleep 30; done;"] # command: ["sleep"] # args: ["infinity"]
23Docker 不使用默認網段
有些情況,內部規劃的網段和可能和 Dockerd 默認的網段有沖突,導致異常出現!
- [問題起因] 今天在新機器上面,部署了一整套服務(多臺機器),服務部署完畢之后,通過前置
Nginx服務發現并不能訪問,后置機器開放的端口,發現發到對應端口的請求都沒有轉發出去。這就比較奇怪了,因為端口控制是已經開通了的,不應該出現不通的情況。
? nc -v 172.16.100.12 8000 nc: connect to 172.16.100.12 port 8000 (tcp) failed: Connection refused
- [解決方法] 發現服務器端口不通,我這里懷疑可能是
dockerd服務啟動導致的,所以我先將服務都停掉,直接在機器上面啟動了Python的服務端程序(Linux機器自帶Python2.7.x的版本),然后在前置Nginx服務發現,端口確實是通的。后來,排除發現是內部服務默認網段和dockerd服務啟動的默認網段是沖突的,導致重寫了機器的防火墻規則,導致出現上述異常的。
$ python -m SimpleHTTPServer 8000 Serving HTTP on 0.0.0.0 port 8000 ... ? nc -v 172.16.100.12 8000 Connection to 172.16.100.12 8000 port [tcp/*] succeeded!
- 既然問題已經知道了,現在需要做的就是非常簡單了:不適用默認網段!通過 『mirantis』 里面,我們可以選擇進行設置,然后重啟服務
dockerd服務,即可。
# 修改配置
$ sudo cat /etc/docker/daemon.json
{
"default-address-pools":[{"base":"192.168.100.0/20","size":24}]
}
# 重啟服務
$ sudo systemctl restart docker
# 啟動服務驗證是否生效
$ ip a
$ docker network inspect app | grep Subnet

- 這時,就到了考驗我們網絡的子網劃分的能力了:如何在給定的網段下面合理且高效的進行劃分呢?咳咳,確實難倒我了,這時我們可以再這個在線網站上面 JSON 在線解析 進行劃分,然后選定合理的
base和size就可以了。
# 報錯信息
Error response from daemon: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network
# 按照下圖我們可以對 pool 進行合理劃分
# 給定 10.210.200.0 + 255.255.255.0 的網段來劃分子網
$ sudo cat /etc/docker/daemon.json
{
"default-address-pools":[{"base":"10.210.200.0/24","size":28}]
}
- 其中,
base告訴我們劃分子網的網段是什么(從來開始),是從前兩位(/16)開始,還是第三位開始(/24)呢?而size則告訴我們劃分的每個子網有多少IP地址可以使用呢?從"10.210.200.0/24"我們可以知道,該網絡下面只有254個可用的IP地址(直接使用肯定不夠),然后我們需要給docker使用,劃分每個子網可用16個IP地址,所以子網就應該寫成28了。

Docker 不使用默認網段
24Docker 添加私有倉庫
有些情況,我們服務器上面需要使用內部私有的容器鏡像地址!
- [問題起因] 如果新機器上面需要使用私有倉庫的話,但是又沒有配置,再獲取鏡像的時候就會出現如下報錯信息。
# 拉取/登陸私庫時提示 $ docker pull 192.168.31.191:5000/nginx:latest x509: certificate signed by unknown authority
- [解決方法] 該問題的處理方式很簡單,如下所示,配置一下倉庫地址,重啟服務并登陸私有倉庫就可以了。
# 添加配置
$ sudo cat /etc/docker/daemon.json
{
"insecure-registries": ["192.168.31.191:5000"]
}
# 重啟docker
$ sudo systemctl restart docker
# 重新登錄即可
$ docker login 私庫地址 -u 用戶名 -p 密碼
VSole
網絡安全專家