24 個常見的 Docker 疑難雜癥處理技巧
轉自:Escapelife 的博客
原文:https://tinyurl.com/2p89skum
這里主要是為了記錄在使用 Docker 的時候遇到的問題及其處理解決方法。
1Docker 遷移存儲目錄
默認情況系統(tǒng)會將 Docker 容器存放在 /var/lib/docker 目錄下
[問題起因]?今天通過監(jiān)控系統(tǒng),發(fā)現公司其中一臺服務器的磁盤快慢,隨即上去看了下,發(fā)現? /var/lib/docker?這個目錄特別大。由上述原因,我們都知道,在?/var/lib/docker?中存儲的都是相關于容器的存儲,所以也不能隨便的將其刪除掉。那就準備遷移? docker?的存儲目錄吧,或者對?/var?設備進行擴容來達到相同的目的。更多關于?dockerd?的詳細參數,請點擊查看?官方文檔?地址。但是需要注意的一點就是,盡量不要用軟鏈, 因為一些? docker?容器編排系統(tǒng)不支持這樣做,比如我們所熟知的?k8s?就在內。
#?發(fā)現容器啟動不了了
ERROR:cannot create temporary directory!
#?查看系統(tǒng)存儲情況
$?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?命令單純復制文件內容的話,就會出現屬性不一致的情況,同時還會有一定的安全問題。

2Docker 設備空間不足
Increase Docker container size from default 10GB on rhel7.
[問題起因一]?容器在導入或者啟動的時候,如果提示磁盤空間不足的,那么多半是真的因為物理磁盤空間真的有問題導致的。如下所示,我們可以看到? /?分區(qū)確實滿了。
#?查看物理磁盤空間
$?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
如果發(fā)現真的是物理磁盤空間滿了的話,就需要查看到底是什么占據了如此大的空間,導致因為容器沒有空間無法啟動。其中, docker?自帶的命令就是一個很好的能夠幫助我們發(fā)現問題的工具。
#?查看基本信息
#?硬件驅動使用的是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?數據的存放路徑(大分區(qū))。
#?顯示哪些容器目錄具有最大的日志文件
$?du?-d1?-h?/var/lib/docker/containers?|?sort?-h
#?清除您選擇的容器日志文件的內容
$?cat?/dev/null?>?/var/lib/docker/containers/container_id/container_log_name
[問題起因二]?顯然我遇到的不是上一種情況,而是在啟動容器的時候,容器啟動之后不久就顯示是? unhealthy?的狀態(tài),通過如下日志發(fā)現,原來是復制配置文件啟動的時候,提示磁盤空間不足。后面發(fā)現是因為? CentOS7?的系統(tǒng)使用的?docker?容器默認的創(chuàng)建大小就是?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
[問題起因三]?還有一種情況也會讓容器無法啟動,并提示磁盤空間不足,但是使用命令查看發(fā)現并不是因為物理磁盤真的不足導致的。而是,因為對于分區(qū)的? inode?節(jié)點數滿了導致的。
#?報錯信息
No?space?left?on?device
[解決方法]?因為? ext3?文件系統(tǒng)使用?inode table?存儲?inode?信息,而?xfs?文件系統(tǒng)使用?B+ tree?來進行存儲。考慮到性能問題,默認情況下這個?B+ tree?只會使用前?1TB?空間,當這?1TB?空間被寫滿后,就會導致無法寫入?inode?信息,報磁盤空間不足的錯誤。我們可以在?mount?時,指定?inode64?即可將這個?B+ tree?使用的空間擴展到整個文件系統(tǒng)。
#?查看系統(tǒng)的inode節(jié)點使用情況
$?sudo?df?-i
#?嘗試重新掛載
$?sudo?mount?-o?remount?-o?noatime,nodiratime,inode64,nobarrier?/dev/vda1
[補充知識]?文件儲存在硬盤上,硬盤的最小存儲單位叫做?扇區(qū)( Sector)。每個扇區(qū)儲存?512?字節(jié)(相當于0.5KB)。操作系統(tǒng)讀取硬盤的時候,不會一個個扇區(qū)地讀取,這樣效率太低,而是一次性連續(xù)讀取多個扇區(qū),即一次性讀取一個塊(block)。這種由多個扇區(qū)組成的塊,是文件存取的最小單位。塊的大小,最常見的是4KB,即連續(xù)八個?sector?組成一個?block?塊。文件數據都儲存在塊中,那么很顯然,我們還必須找到一個地方儲存文件的元信息,比如文件的創(chuàng)建者、文件的創(chuàng)建日期、文件的大小等等。這種儲存文件元信息的區(qū)域就叫做索引節(jié)點(inode)。每一個文件都有對應的?inode,里面包含了除了文件名以外的所有文件信息。inode?也會消耗硬盤空間,所以硬盤格式化的時候,操作系統(tǒng)自動將硬盤分成兩個區(qū)域。一個是數據區(qū),存放文件數據;另一個是?inode?區(qū)(inode table),存放?inode?所包含的信息。每個?inode?節(jié)點的大小,一般是?128?字節(jié)或?256?字節(jié)。inode?節(jié)點的總數,在格式化時就給定,一般是每1KB或每2KB就設置一個?inode?節(jié)點。
#?每個節(jié)點信息的內容
$?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 目錄下面有訪問權限
[問題起因]?給系統(tǒng)安裝完? compose?之后,查看版本的時候,提示缺少一個名為?libz.so.1?的共享鏈接庫。第一反應就是,是不是系統(tǒng)少安裝那個軟件包導致的。隨即,搜索了一下,將相關的依賴包都給安裝了,卻還是提示同樣的問題。
#?提示錯誤信息
$?docker-compose?--version
error?while?loading?shared?libraries:?libz.so.1:?failed?to?map?segment?from?shared?object:?Operation?not?permitted
[解決方法]?后來發(fā)現,是因為系統(tǒng)中? docker?沒有對?/tmp?目錄的訪問權限導致,需要重新將其掛載一次,就可以解決了。
#?重新掛載
$?sudo?mount?/tmp?-o?remount,exec
4Docker 容器文件損壞
對 dockerd 的配置有可能會影響到系統(tǒng)穩(wěn)定
[問題起因]?容器文件損壞,經常會導致容器無法操作。正常的? 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 容器優(yōu)雅重啟
不停止服務器上面運行的容器,重啟 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重載
#?相當于發(fā)送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?中的進程,都沒有。這種情況的可能原因是容器啟動之后,主機因任何原因重新啟動并且沒有優(yōu)雅地終止容器。剩下的文件現在阻止你重新生成舊名稱的新容器,因為系統(tǒng)認為舊容器仍然存在。
#?刪除容器
$?sudo?docker?rm?-f?f8e8c3..
Error?response?from?daemon:?Conflict,?cannot?remove?the?default?name?of?the?container
[解決方法]?找到? /var/lib/docker/containers/?下的對應容器的文件夾,將其刪除,然后重啟一下?dockerd?即可。我們會發(fā)現,之前無法刪除的容器沒有了。
#?刪除容器文件
$?sudo?rm?-rf?/var/lib/docker/containers/f8e8c3...65720
#?重啟服務
$?sudo?systemctl?restart?docker.service
7Docker 容器中文異常
容器存在問題話,記得優(yōu)先在官網查詢
[問題起因]?今天登陸之前部署的? MySQL?數據庫查詢,發(fā)現使用?SQL?語句無法查詢中文字段,即使直接輸入中文都沒有辦法顯示。
#?查看容器支持的字符集
root@b18f56aa1e15:#?locale?-a
C
C.UTF-8
POSIX
[解決方法]? Docker?部署的?MySQL?系統(tǒng)使用的是?POSIX?字符集。然而?POSIX?字符集是不支持中文的,而?C.UTF-8?是支持中文的只要把系統(tǒng)中的環(huán)境?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
????}
????...
}
[解決方法]?后面發(fā)現是因為? 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?分區(qū)設置太小導致?share memory?不夠。不設置?--shm-size?參數時,docker?給容器默認分配的?shm?大小為?64M,導致程序啟動時不足。具體原因還是因為安裝?pytorch?包導致了,多進程跑任務的時候,docker?容器分配的共享內存太小,導致?torch?要在?tmpfs?上面放模型數據用于子線程的 共享不足,就出現報錯了。
#?問題原因
[email protected]:/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?程序執(zhí)行?read_file()?讀取文件的內容,給文件加鎖的時候報錯了。但是奇怪的是,本地調試的時候發(fā)現服務都是可以正常運行的,文件加鎖也是沒問題的。后來發(fā)現,在?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+?才行。后來才發(fā)現,這實際上是由?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?啟動服務的時候,發(fā)現有時候服務之前可以相互連通,而有時啟動的多個服務之前卻出現了無法訪問的情況。究其原因,發(fā)現原來是因為使用的內部私有地址網段不一致導致的。有的服務啟動到了?172.17 - 172.31?的網段,有的服務跑到了?192.169.0 - 192.168.224?的網段,這樣導致服務啟動之后出現無法訪問的情況(默認情況下,有下面這個兩個網段可供其使用)。

[解決方法]?上述問題的處理方式,就是手動指定? 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 命令各自啟動兩組服務,發(fā)現服務會串臺!
[問題起因]?在兩個不同名稱的目錄目錄下面,使用? docker-compose?來啟動服務,發(fā)現當?A?組服務啟動完畢之后,再啟動?B?組服務的時候,發(fā)現?A?組當中對應的一部分服務又重新啟動了一次,這就非常奇怪了!因為這個問題的存在會導致,A?組服務和?B?組服務無法同時啟動。之前還以為是工具的?Bug,后來請教了?“上峰”,才知道了原因,恍然大悟。
#?服務目錄結構如下所示
A:?/data1/app/docker-compose.yml
B:?/data2/app/docker-compose.yml
[解決方法]?發(fā)現? A?和?B?兩組服務會串臺的原因,原來是?docker-compose?會給啟動的容器加?label?標簽,然后根據這些?label?標簽來識別和判斷對應的容器服務是由誰啟動的、誰來管理的,等等。而這里,我們需要關注的?label?變量是?com.docker.compose.project,其對應的值是使用啟動配置文件的目錄的最底層子目錄名稱,即上面的?app?就是對應的值。我們可以發(fā)現,?A?和?B?兩組服務對應的值都是?app,所以啟動的時候被認為是同一個,這就出現了上述的問題。如果需要深入了解的話,可以去看對應源代碼。

#?可以將目錄結構調整為如下所示
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?手動指定標簽,來規(guī)避該問題的發(fā)生。
#?指定項目項目名稱
$?docker-compose?-f?./docker-compose.yml?-p?app1?up?-d
13Docker 命令調用報錯
在編寫腳本的時候常常會執(zhí)行 docker 相關的命令,但是需要注意使用細節(jié)!
[問題起因]? CI?更新環(huán)境執(zhí)行了一個腳本,但是腳本執(zhí)行過程中報錯了,如下所示。通過對應的輸出信息,可以看到提示說正在執(zhí)行的設備不是一個?tty。

隨即,查看了腳本發(fā)現報錯地方是執(zhí)行了一個? exec?的?docker?命令,大致如下所示。很奇怪的是,手動執(zhí)行或直接調腳本的時候,怎么都是沒有問題的,但是等到?CI?調用的時候怎么都是有問題。后來好好看下,下面這個命令,注意到?-it?這個參數了。
#?腳本調用docker命令
docker?exec?-it??psql?-Upostgres?......
我們可以一起看下? exec?命令的這兩個參數,自然就差不多理解了。
| 編號 | 參數 | 解釋說明 |
|---|---|---|
| 1 | -i/-interactive | 即使沒有附加也保持 STDIN 打開;如果你需要執(zhí)行命令則需要開啟這個選項 |
| 2 | -t/–tty | 分配一個偽終端進行執(zhí)行;一個連接用戶的終端與容器 stdin 和 stdout 的橋梁 |
[解決方法]? docker exec?的參數?-t?是指?Allocate a pseudo-TTY?的意思,而?CI?在執(zhí)行?job?的時候并不是在?TTY?終端中執(zhí)行,所以?-t?這個參數會報錯。同時在 『stackoverflow』也有人給出原因,可以自行查看。

14Docker 定時任務異常
在 Crontab 定時任務中也存在 Docker 命令執(zhí)行異常的情況!
[問題起因]?今天發(fā)現了一個問題,就是在備份? Mysql?數據庫的時候,使用?docker?容器進行備份,然后使用?Crontab?定時任務來觸發(fā)備份。但是發(fā)現備份的?MySQL?數據庫居然是空的,但是手動執(zhí)行對應命令切是好的,很奇怪。
#?Crontab定時任務
0?*/6?*?*?*?\
????docker?exec?-it??sh?-c?\
????????'exec?mysqldump?--all-databases?-uroot?-ppassword?......'
[解決方法]?后來發(fā)現是因為執(zhí)行的? docker?命令多個?-i?導致的。因為?Crontab?命令執(zhí)行的時候,并不是交互式的,所以需要把這個去掉才可以??偨Y就是,如果你需要回顯的話則需要?-t?選項,如果需要交互式會話則需要?-i?選項。
| 編號 | 參數 | 解釋說明 |
|---|---|---|
| 1 | -i/-interactive | 即使沒有附加也保持 STDIN 打開;如果你需要執(zhí)行命令則需要開啟這個選項 |
| 2 | -t/–tty | 分配一個偽終端進行執(zhí)行;一個連接用戶的終端與容器 stdin 和 stdout 的橋梁 |
15Docker 變量使用引號
compose 里邊環(huán)境變量帶不帶引號的問題!
[問題起因]?使用過? compose?的朋友可能都遇到過,在編寫啟服務啟動配置文件的時候,添加環(huán)境變量時到底是使用單引號、雙引號還是不使用引號的問題?時間長了,我們可能會將三者混用,認為其效果是一樣的。但是后來,發(fā)現的坑越來越多,才發(fā)現其越來越隱晦。反正我是遇到過很多問題,都是因為添加引號導致的服務啟動異常的,后來得出的結論就是一律不使引號。裸奔,體驗前所未有的爽快!直到現在看到了? Github?中對應的 issus 之后,才終于破案了。
#?在Compose中進行引用TEST_VAR變量,無法找到
TEST_VAR="test"
#?在Compose中進行引用TEST_VAR變量,可以找到
TEST_VAR=test
#?后來發(fā)現docker本身其實已經正確地處理了引號的使用
docker?run?-it?--rm?-e?TEST_VAR="test"?test:latest
[解決方法]?得到的結論就是,因為? Compose?解析?yaml?配置文件,發(fā)現引號也進行了解釋包裝。這就導致原本的?TEST_VAR="test"?被解析成了?'TEST_VAR="test"',所以我們在引用的時候就無法獲取到對應的值?,F在解決方法就是,不管是我們直接在配置文件添加環(huán)境變量或者使用?env_file?配置文件,能不使用引號就不適用引號。需要注意的是環(huán)境變量配置的是日志格式的話( 2022-01-01),如果使用的是?Python?的?yaml.load?模塊的話,會被當做是?date?類型的,這是如果希望保持原樣信息的話,可以使用?'/"?引起來將其變成字符串格式的。
16Docker 刪除鏡像報錯
無法刪除鏡像,歸根到底還是有地方用到了!
[問題起因]?清理服器磁盤空間的時候,刪除某個鏡像的時候提示如下信息。提示需要強制刪除,但是發(fā)現及時執(zhí)行了強制刪除依舊沒有效果。
#?刪除鏡像
$?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
[解決方法]?后來才發(fā)現,出現這個原因主要是因為? 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?進行服務的啟動和管理的。今天給一個服務切換用戶的時候,發(fā)現?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)
[解決方法]?后來發(fā)現還是? 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?服務提示如下報錯信息。不清楚是否修改了操作系統(tǒng)的相關配置,還是對應?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?發(fā)現系統(tǒng)本身并不支持。這時,我們一看下對應的操作系統(tǒng)?ipv6?的設置,發(fā)現系統(tǒng)禁用了,所有的?ipv6?地址。需要了解的朋友,可以參考 fix port forwarding with ipv6.disable=1 和 cannot start if ipv6 is disabled on host 這兩個?issus?來獲取更多信息。
#?操作系統(tǒng)配置
$?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,導致服務無法正常訪問的情況?,F在通用的始終還是?IPv4?地址,因此最簡單的做法就是關閉?IPv6?地址。詳細的配置,可以參考 Port redirecting binding to IPv6 but not IPv4 interfaces 這個?issus?地址。
#?修改系統(tǒng)配置
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?分鐘左右),之后提示如下信息。通過閱讀信息內容,可以看到是因為超時導致的,提示可以通過設置環(huán)境變量,加大超時的時間。
$?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).
[解決方法]?按照提示設置的環(huán)境變量之后,再次啟動發(fā)現確實可以正常啟動了,但是還是能夠感覺到有些慢。
$?sudo?vim?/etc/profile
export?COMPOSE_HTTP_TIMEOUT=500
export?DOCKER_CLIENT_TIMEOUT=500
排除了下啟動流程,因為容器啟動有映射目錄到容器里面且目錄大小比較大,所以懷疑是因為? i/o?導致的。隨即使用?iotop?命令查看服務器目前的?i/o?情況,發(fā)現存在很多個?rg?命令,且都處于?100%?左右。查了下,發(fā)現是?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 端口網絡限制
如果發(fā)現服務都一切正常,但是無法無法訪問的話,則多為網絡問題!
[問題起因]?啟用服務之后,登錄跳轉發(fā)現直接? 502?報錯了。排除了配置等相關原因都沒有任何問題(做過相關測試),這就非常奇怪了!
#?部署服務架構
nginx(80)?->?web1(8080)
??????????->?web2(8081)
#?報錯信息如下所示
nginx?connect()?failed?(113:?No?route?to?host)?while?connecting?to?upstream
[解決方法]?根據錯誤信息可知,是因為沒有路由到指定的? host?導致了,隨即看了下防火墻是開著的,看了日志發(fā)現被過濾掉了,西八!問題找到了,現在需要做的就是,要么添加防火墻規(guī)則,要么關閉防火墻。
#?檢查開放的端口
$?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?倉庫,發(fā)現提示無法獲取對應鏡像,但是在其他機器上面獲取該鏡像就可以執(zhí)行成功,這就非常奇怪了!
#?登錄私有倉庫
$?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
[解決方法]?太坑了,我還以為我發(fā)現某個隱藏的? bug?了,可勁的排查,最后發(fā)現,原來是自己鏡像包名字寫錯了,應該寫成?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 不使用默認網段
有些情況,內部規(guī)劃的網段和可能和 Dockerd 默認的網段有沖突,導致異常出現!
[問題起因]?今天在新機器上面,部署了一整套服務(多臺機器),服務部署完畢之后,通過前置? Nginx?服務發(fā)現并不能訪問,后置機器開放的端口,發(fā)現發(fā)到對應端口的請求都沒有轉發(fā)出去。這就比較奇怪了,因為端口控制是已經開通了的,不應該出現不通的情況。
??nc?-v?172.16.100.12?8000
nc:?connect?to?172.16.100.12?port?8000?(tcp)?failed:?Connection?refused
[解決方法]?發(fā)現服務器端口不通,我這里懷疑可能是? dockerd?服務啟動導致的,所以我先將服務都停掉,直接在機器上面啟動了?Python?的服務端程序(Linux?機器自帶?Python2.7.x?的版本),然后在前置?Nginx?服務發(fā)現,端口確實是通的。后來,排除發(fā)現是內部服務默認網段和?dockerd?服務啟動的默認網段是沖突的,導致重寫了機器的防火墻規(guī)則,導致出現上述異常的。
$?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?了。

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?密碼//blog.csdn.net/yugemengjing/article/detai
有收獲,點個在看?



