徹夜怒肝!Docker 常見疑難雜癥解決方案已擼完,快要裂開了。。。
作者: Escape
鏈接: https://escapelife.github.io/posts/43a2bb9b.html
這里主要是為了記錄在使用 Docker 的時(shí)候遇到的問題及其處理解決方法。
1.Docker 遷移存儲(chǔ)目錄
默認(rèn)情況系統(tǒng)會(huì)將 Docker 容器存放在/var/lib/docker 目錄下
問題起因:今天通過監(jiān)控系統(tǒng),發(fā)現(xiàn)公司其中一臺(tái)服務(wù)器的磁盤快慢,隨即上去看了下,發(fā)現(xiàn) /var/lib/docker 這個(gè)目錄特別大。由上述原因,我們都知道,在 /var/lib/docker 中存儲(chǔ)的都是相關(guān)于容器的存儲(chǔ),所以也不能隨便的將其刪除掉。
那就準(zhǔn)備遷移 docker 的存儲(chǔ)目錄吧,或者對 /var 設(shè)備進(jìn)行擴(kuò)容來達(dá)到相同的目的。更多關(guān)于 dockerd 的詳細(xì)參數(shù),請點(diǎn)擊查看 官方文檔 地址。
但是需要注意的一點(diǎn)就是,盡量不要用軟鏈, 因?yàn)橐恍?docker 容器編排系統(tǒng)不支持這樣做,比如我們所熟知的 k8s 就在內(nèi)。
# 發(fā)現(xiàn)容器啟動(dòng)不了了
ERROR:cannot create temporary directory!
# 查看系統(tǒng)存儲(chǔ)情況
$ du -h --max-depth=1
解決方法1:添加軟鏈接
# 1.停止docker服務(wù)
$ sudo systemctl stop docker
# 2.開始遷移目錄
$ sudo mv /var/lib/docker /data/
# 3.添加軟鏈接
# sudo ln -s /data/docker /var/lib/docker
# 4.啟動(dòng)docker服務(wù)
$ sudo systemctl start docker
解決方法2:改動(dòng) docker 配置文件
# 3.改動(dòng)docker啟動(dòng)配置文件
$ sudo vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd --graph=/data/docker/
# 4.改動(dòng)docker啟動(dòng)配置文件
$ sudo vim /etc/docker/daemon.json
{
"live-restore": true,
"graph": [ "/data/docker/" ]
}
操作注意事項(xiàng):在遷移 docker 目錄的時(shí)候注意使用的命令,要么使用 mv 命令直接移動(dòng),要么使用 cp 命令復(fù)制文件,但是需要注意同時(shí)復(fù)制文件權(quán)限和對應(yīng)屬性,不然在使用的時(shí)候可能會(huì)存在權(quán)限問題。如果容器中,也是使用 root 用戶,則不會(huì)存在該問題,但是也是需要按照正確的操作來遷移目錄。
# 使用mv命令
$ sudo mv /var/lib/docker /data/docker
# 使用cp命令
$ sudo cp -arv /data/docker /data2/docker
下圖中,就是因?yàn)閱?dòng)的容器使用的是普通用戶運(yùn)行進(jìn)程的,且在運(yùn)行當(dāng)中需要使用 /tmp 目錄,結(jié)果提示沒有權(quán)限。在我們導(dǎo)入容器鏡像的時(shí)候,其實(shí)是會(huì)將容器啟動(dòng)時(shí)需要的各個(gè)目錄的權(quán)限和屬性都賦予了。如果我們直接是 cp 命令單純復(fù)制文件內(nèi)容的話,就會(huì)出現(xiàn)屬性不一致的情況,同時(shí)還會(huì)有一定的安全問題。

Increase Docker container size from default 10GB on rhel7.
問題起因一:容器在導(dǎo)入或者啟動(dòng)的時(shí)候,如果提示磁盤空間不足的,那么多半是真的因?yàn)槲锢泶疟P空間真的有問題導(dǎo)致的。如下所示,我們可以看到 / 分區(qū)確實(shí)滿了。
# 查看物理磁盤空間
$ 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ā)現(xiàn)真的是物理磁盤空間滿了的話,就需要查看到底是什么占據(jù)了如此大的空間,導(dǎo)致因?yàn)槿萜鳑]有空間無法啟動(dòng)。其中,docker 自帶的命令就是一個(gè)很好的能夠幫助我們發(fā)現(xiàn)問題的工具。
# 查看基本信息
# 硬件驅(qū)動(dòng)使用的是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
解決方法:通過查看信息,我們知道正是因?yàn)?docker 可用的磁盤空間不足,所以導(dǎo)致啟動(dòng)的時(shí)候沒有足夠的空間進(jìn)行加載啟動(dòng)鏡像。解決的方法也很簡單,第一就是清理無效數(shù)據(jù)文件釋放磁盤空間(清除日志),第二就是修改 docker 數(shù)據(jù)的存放路徑(大分區(qū))。
# 顯示哪些容器目錄具有最大的日志文件
$ du -d1 -h /var/lib/docker/containers | sort -h
# 清除您選擇的容器日志文件的內(nèi)容
$ cat /dev/null > /var/lib/docker/containers/container_id/container_log_name
問題起因二:顯然我遇到的不是上一種情況,而是在啟動(dòng)容器的時(shí)候,容器啟動(dòng)之后不久就顯示是 unhealthy 的狀態(tài),通過如下日志發(fā)現(xiàn),原來是復(fù)制配置文件啟動(dòng)的時(shí)候,提示磁盤空間不足。
后面發(fā)現(xiàn)是因?yàn)?CentOS7 的系統(tǒng)使用的 docker 容器默認(rèn)的創(chuàng)建大小就是 10G 而已,然而我們使用的容器卻超過了這個(gè)限制,導(dǎo)致無法啟動(dòng)時(shí)提示空間不足。
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:改動(dòng) docker 啟動(dòng)配置文件
# /etc/docker/daemon.json
{
"live-restore": true,
"storage-opt": [ "dm.basesize=20G" ]
}
解決方法2:改動(dòng) systemctl 的 docker 啟動(dòng)文件
# 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
問題起因三:還有一種情況也會(huì)讓容器無法啟動(dòng),并提示磁盤空間不足,但是使用命令查看發(fā)現(xiàn)并不是因?yàn)槲锢泶疟P真的不足導(dǎo)致的。而是,因?yàn)閷τ诜謪^(qū)的 inode 節(jié)點(diǎn)數(shù)滿了導(dǎo)致的。
# 報(bào)錯(cuò)信息
No space left on device
解決方法:因?yàn)?ext3 文件系統(tǒng)使用 inode table 存儲(chǔ) inode 信息,而 xfs 文件系統(tǒng)使用 B+ tree 來進(jìn)行存儲(chǔ)。考慮到性能問題,默認(rèn)情況下這個(gè) B+ tree 只會(huì)使用前 1TB 空間,當(dāng)這 1TB 空間被寫滿后,就會(huì)導(dǎo)致無法寫入 inode 信息,報(bào)磁盤空間不足的錯(cuò)誤。我們可以在 mount 時(shí),指定 inode64 即可將這個(gè) B+ tree 使用的空間擴(kuò)展到整個(gè)文件系統(tǒng)。
# 查看系統(tǒng)的inode節(jié)點(diǎn)使用情況
$ sudo df -i
# 嘗試重新掛載
$ sudo mount -o remount -o noatime,nodiratime,inode64,nobarrier /dev/vda1
補(bǔ)充知識(shí):文件儲(chǔ)存在硬盤上,硬盤的最小存儲(chǔ)單位叫做“扇區(qū)”(Sector)。每個(gè)扇區(qū)儲(chǔ)存 512 字節(jié)(相當(dāng)于0.5KB)。操作系統(tǒng)讀取硬盤的時(shí)候,不會(huì)一個(gè)個(gè)扇區(qū)地讀取,這樣效率太低,而是一次性連續(xù)讀取多個(gè)扇區(qū),即一次性讀取一個(gè)“塊”(block)。這種由多個(gè)扇區(qū)組成的”塊”,是文件存取的最小單位。”塊”的大小,最常見的是4KB,即連續(xù)八個(gè) sector 組成一個(gè) block 塊。文件數(shù)據(jù)都儲(chǔ)存在”塊”中,那么很顯然,我們還必須找到一個(gè)地方儲(chǔ)存文件的元信息,比如文件的創(chuàng)建者、文件的創(chuàng)建日期、文件的大小等等。這種儲(chǔ)存文件元信息的區(qū)域就叫做“索引節(jié)點(diǎn)”(inode)。每一個(gè)文件都有對應(yīng)的 inode,里面包含了除了文件名以外的所有文件信息。
inode 也會(huì)消耗硬盤空間,所以硬盤格式化的時(shí)候,操作系統(tǒng)自動(dòng)將硬盤分成兩個(gè)區(qū)域。一個(gè)是數(shù)據(jù)區(qū),存放文件數(shù)據(jù);另一個(gè)是 inode 區(qū)(inode table),存放 inode 所包含的信息。每個(gè) inode 節(jié)點(diǎn)的大小,一般是 128 字節(jié)或 256 字節(jié)。inode 節(jié)點(diǎn)的總數(shù),在格式化時(shí)就給定,一般是每1KB或每2KB就設(shè)置一個(gè) inode 節(jié)點(diǎn)。
# 每個(gè)節(jié)點(diǎn)信息的內(nèi)容
$ 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
3.Docker 缺共享鏈接庫
Docker 命令需要對/tmp 目錄下面有訪問權(quán)限
問題起因:給系統(tǒng)安裝完 compose 之后,查看版本的時(shí)候,提示缺少一個(gè)名為 libz.so.1 的共享鏈接庫。第一反應(yīng)就是,是不是系統(tǒng)少安裝那個(gè)軟件包導(dǎo)致的。隨即,搜索了一下,將相關(guān)的依賴包都給安裝了,卻還是提示同樣的問題。
# 提示錯(cuò)誤信息
$ docker-compose --version
error while loading shared libraries: libz.so.1: failed to map segment from shared object: Operation not permitted
解決方法:后來發(fā)現(xiàn),是因?yàn)橄到y(tǒng)中 docker 沒有對 /tmp 目錄的訪問權(quán)限導(dǎo)致,需要重新將其掛載一次,就可以解決了。
# 重新掛載
$ sudo mount /tmp -o remount,exec
4.Docker 容器文件損壞
對 dockerd 的配置有可能會(huì)影響到系統(tǒng)穩(wěn)定
問題起因:容器文件損壞,經(jīng)常會(huì)導(dǎo)致容器無法操作。正常的 docker 命令已經(jīng)無法操控這臺(tái)容器了,無法關(guān)閉、重啟、刪除。正巧,前天就需要這個(gè)的問題,主要的原因是因?yàn)橹匦聦?docker 的默認(rèn)容器進(jìn)行了重新的分配限制導(dǎo)致的。
# 操作容器遇到類似的錯(cuò)誤
b'devicemapper: Error running deviceCreate (CreateSnapDeviceRaw) dm_task_run failed'
解決方法:可以通過以下操作將容器刪除/重建。
# 1.關(guān)閉docker
$ sudo systemctl stop docker
# 2.刪除容器文件
$ sudo rm -rf /var/lib/docker/containers
# 3.重新整理容器元數(shù)據(jù)
$ 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
5.Docker 容器優(yōu)雅重啟
不停止服務(wù)器上面運(yùn)行的容器,重啟 dockerd 服務(wù)是多么好的一件事
問題起因:默認(rèn)情況下,當(dāng) Docker 守護(hù)程序終止時(shí),它會(huì)關(guān)閉正在運(yùn)行的容器。從 Docker-ce 1.12 開始,可以在配置文件中添加 live-restore 參數(shù),以便在守護(hù)程序變得不可用時(shí)容器保持運(yùn)行。需要注意的是 Windows 平臺(tái)暫時(shí)還是不支持該參數(shù)的配置。
# Keep containers alive during daemon downtime
$ sudo vim /etc/docker/daemon.yaml
{
"live-restore": true
}
# 在守護(hù)進(jìn)程停機(jī)期間保持容器存活
$ sudo dockerd --live-restore
# 只能使用reload重載
# 相當(dāng)于發(fā)送SIGHUP信號(hào)量給dockerd守護(hù)進(jìn)程
$ sudo systemctl reload docker
# 但是對應(yīng)網(wǎng)絡(luò)的設(shè)置需要restart才能生效
$ sudo systemctl restart docker
解決方法:可以通過以下操作將容器刪除/重建。
# /etc/docker/daemon.yaml
{
"registry-mirrors": ["https://vec0xydj.mirror.aliyuncs.com"], # 配置獲取官方鏡像的倉庫地址
"experimental": true, # 啟用實(shí)驗(yàn)功能
"default-runtime": "nvidia", # 容器的默認(rèn)OCI運(yùn)行時(shí)(默認(rèn)為runc)
"live-restore": true, # 重啟dockerd服務(wù)的時(shí)候容易不終止
"runtimes": { # 配置容器運(yùn)行時(shí)
"nvidia": {
"path": "/usr/bin/nvidia-container-runtime",
"runtimeArgs": []
}
},
"default-address-pools": [ # 配置容器使用的子網(wǎng)地址池
{
"scope": "local",
"base":"172.17.0.0/12",
"size":24
}
]
}
6.Docker 容器無法刪除
找不到對應(yīng)容器進(jìn)程是最嚇人的
問題起因:今天遇到 docker 容器無法停止/終止/刪除,以為這個(gè)容器可能又出現(xiàn)了 dockerd 守護(hù)進(jìn)程托管的情況,但是通過ps -ef <container id>無法查到對應(yīng)的運(yùn)行進(jìn)程。哎,后來開始開始查 supervisor 以及 Dockerfile 中的進(jìn)程,都沒有。這種情況的可能原因是容器啟動(dòng)之后,之后,主機(jī)因任何原因重新啟動(dòng)并且沒有優(yōu)雅地終止容器。剩下的文件現(xiàn)在阻止你重新生成舊名稱的新容器,因?yàn)橄到y(tǒng)認(rèn)為舊容器仍然存在。
# 刪除容器
$ sudo docker rm -f f8e8c3..
Error response from daemon: Conflict, cannot remove the default name of the container
解決方法:找到 /var/lib/docker/containers/ 下的對應(yīng)容器的文件夾,將其刪除,然后重啟一下 dockerd 即可。我們會(huì)發(fā)現(xiàn),之前無法刪除的容器沒有了。
# 刪除容器文件
$ sudo rm -rf /var/lib/docker/containers/f8e8c3...65720
# 重啟服務(wù)
$ sudo systemctl restart docker.service
7.Docker 容器中文異常
容器存在問題話,記得優(yōu)先在官網(wǎng)查詢
問題起因:今天登陸之前部署的 MySQL 數(shù)據(jù)庫查詢,發(fā)現(xiàn)使用 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 進(jìn)入 pod 不能輸入中文也可用此方法解決。
# 臨時(shí)解決
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
8.Docker 容器網(wǎng)絡(luò)互通
了解 Docker 的四種網(wǎng)絡(luò)模型
問題起因:在本機(jī)部署 Nginx 容器想代理本機(jī)啟動(dòng)的 Python 后端服務(wù)程序,但是對代碼服務(wù)如下的配置,結(jié)果訪問的時(shí)候一直提示 502 錯(cuò)誤。
# 啟動(dòng)Nginx服務(wù)
$ docker run -d -p 80:80 $PWD:/etc/nginx nginx
nginx
server {
...
location /api {
proxy_pass http://localhost:8080
}
...
}
解決方法:后面發(fā)現(xiàn)是因?yàn)?nginx.conf 配置文件中的 localhost 配置的有問題,由于 Nginx 是在容器中運(yùn)行,所以 localhost 為容器中的 localhost,而非本機(jī)的 localhost,所以導(dǎo)致無法訪問。
可以將 nginx.conf 中的 localhost 改為宿主機(jī)的 IP 地址,就可以解決 502 的錯(cuò)誤。
# 查詢宿主機(jī)IP地址 => 172.17.0.1
$ ip addr show docker0
docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> 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
nginx
server {
...
location /api {
proxy_pass http://172.17.0.1:8080
}
...
}
當(dāng)容器使用 host 網(wǎng)絡(luò)時(shí),容器與宿主共用網(wǎng)絡(luò),這樣就能在容器中訪問宿主機(jī)網(wǎng)絡(luò),那么容器的 localhost 就是宿主機(jī)的 localhost 了。
# 服務(wù)的啟動(dòng)方式有所改變(沒有映射出來端口)
# 因?yàn)楸旧砼c宿主機(jī)共用了網(wǎng)絡(luò),宿主機(jī)暴露端口等同于容器中暴露端口
$ docker run -d -p 80:80 --network=host $PWD:/etc/nginx nginxx
9.Docker 容器總線錯(cuò)誤
總線錯(cuò)誤看到的時(shí)候還是挺嚇人了
問題起因:在 docker 容器中運(yùn)行程序的時(shí)候,提示 bus error 錯(cuò)誤。
# 總線報(bào)錯(cuò)
$ inv app.user_op --name=zhangsan
Bus error (core dumped)
解決方法:原因是在 docker 運(yùn)行的時(shí)候,shm 分區(qū)設(shè)置太小導(dǎo)致 share memory 不夠。不設(shè)置 –shm-size 參數(shù)時(shí),docker 給容器默認(rèn)分配的 shm 大小為 64M,導(dǎo)致程序啟動(dòng)時(shí)不足。
# 啟動(dòng)docker的時(shí)候加上--shm-size參數(shù)(單位為b,k,m或g)
$ docker run -it --rm --shm-size=200m pytorch/pytorch:latest
解決方法:還有一種情況就是容器內(nèi)的磁盤空間不足,也會(huì)導(dǎo)致 bus error 的報(bào)錯(cuò),所以清除多余文件或者目錄,就可以解決了。
# 磁盤空間不足
$ df -Th
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 1T 1T 0G 100% /
shm tmpfs 64M 24K 64M 1% /dev/shm
10.Docker NFS 掛載報(bào)錯(cuò)
總線錯(cuò)誤看到的時(shí)候還是挺嚇人了
問題起因:我們將服務(wù)部署到 openshift 集群中,啟動(dòng)服務(wù)調(diào)用資源文件的時(shí)候,報(bào)錯(cuò)信息如下所示。從報(bào)錯(cuò)信息中,得知是在 Python3 程序執(zhí)行 read_file() 讀取文件的內(nèi)容,給文件加鎖的時(shí)候報(bào)錯(cuò)了。但是奇怪的是,本地調(diào)試的時(shí)候發(fā)現(xiàn)服務(wù)都是可以正常運(yùn)行的,文件加鎖也是沒問題的。后來發(fā)現(xiàn),在 openshift 集群中使用的是 NFS 掛 載的共享磁盤。
# 報(bào)錯(cuò)信息
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() 的話,就需要升級(jí)內(nèi)核版本到 2.6.11+ 才行。后來才發(fā)現(xiàn),這實(shí)際上是由 RedHat 內(nèi)核中的一個(gè)錯(cuò)誤引起的,并在 kernel-3.10.0-693.18.1.el7 版本中得到修復(fù)。所以對于 NFSv3 和 NFSv4 服務(wù)而已,就需要升級(jí) Linux 內(nèi)核版本才能夠解決這個(gè)問題。
# 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.
11.Docker 默認(rèn)使用網(wǎng)段
啟動(dòng)的容器網(wǎng)絡(luò)無法相互通信,很是奇怪!
問題起因:我們在使用 Docker 啟動(dòng)服務(wù)的時(shí)候,發(fā)現(xiàn)有時(shí)候服務(wù)之前可以相互連通,而有時(shí)間啟動(dòng)的多個(gè)服務(wù)之前卻出現(xiàn)了無法訪問的情況。究其原因,發(fā)現(xiàn)原來是因?yàn)槭褂玫膬?nèi)部私有地址網(wǎng)段不一致導(dǎo)致的。有點(diǎn)服務(wù)啟動(dòng)到了 172.17 - 172.31 的網(wǎng)段,有的服務(wù)跑到了 192.169.0 - 192.168.224 的網(wǎng)段,這樣導(dǎo)致服務(wù)啟動(dòng)之后出現(xiàn)無法訪問的情況。
解決方法:上述問題的處理方式,就是手動(dòng)指定 Docker 服務(wù)的啟動(dòng)網(wǎng)段,就可以了。
# 查看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": []
}
}
}
12.Docker 服務(wù)啟動(dòng)串臺(tái)
使用 docker-compose 命令各自啟動(dòng)兩組服務(wù),發(fā)現(xiàn)服務(wù)會(huì)串臺(tái)!
問題起因:在兩個(gè)不同名稱的目錄目錄下面,使用 docker-compose 來啟動(dòng)服務(wù),發(fā)現(xiàn)當(dāng) A 組服務(wù)啟動(dòng)完畢之后,再啟動(dòng) B 組服務(wù)的時(shí)候,發(fā)現(xiàn) A 組當(dāng)中對應(yīng)的一部分服務(wù)又重新啟動(dòng)了一次,這就非常奇怪了!因?yàn)檫@個(gè)問題的存在會(huì)導(dǎo)致,A 組服務(wù)和 B 組服務(wù)無法同時(shí)啟動(dòng)。之前還以為是工具的 Bug,后來請教了“上峰”,才知道了原因,恍然大悟。
# 服務(wù)目錄結(jié)構(gòu)如下所示
A: /data1/app/docker-compose.yml
B: /data2/app/docker-compose.yml
解決方法:發(fā)現(xiàn) A 和 B 兩組服務(wù)會(huì)串臺(tái)的原因,原來是 docker-compose 會(huì)給啟動(dòng)的容器加 label 標(biāo)簽,然后根據(jù)這些 label 標(biāo)簽來識(shí)別和判斷對應(yīng)的容器服務(wù)是由誰啟動(dòng)的、誰來管理的,等等。而這里,我們需要關(guān)注的 label 變量是 com.docker.compose.project,其對應(yīng)的值是使用啟動(dòng)配置文件的目錄的最底層子目錄名稱,即上面的 app 就是對應(yīng)的值。我們可以發(fā)現(xiàn), A 和 B 兩組服務(wù)對應(yīng)的值都是 app,所以啟動(dòng)的時(shí)候被認(rèn)為是同一個(gè),這就出現(xiàn)了上述的問題。如果需要深入了解的話,可以去看對應(yīng)源代碼。
# 可以將目錄結(jié)構(gòu)調(diào)整為如下所示
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 命令提供的參數(shù) -p 來規(guī)避該問題的發(fā)生。
# 指定項(xiàng)目項(xiàng)目名稱
$ docker-compose -f ./docker-compose.yml -p app1 up -d
13.Docker 命令調(diào)用報(bào)錯(cuò)
在編寫腳本的時(shí)候常常會(huì)執(zhí)行 docker 相關(guān)的命令,但是需要注意使用細(xì)節(jié)!
問題起因:CI 更新環(huán)境執(zhí)行了一個(gè)腳本,但是腳本執(zhí)行過程中報(bào)錯(cuò)了,如下所示。通過對應(yīng)的輸出信息,可以看到提示說正在執(zhí)行的設(shè)備不是一個(gè) tty。

# 腳本調(diào)用docker命令
docker exec -it <container_name> psql -Upostgres ......
我們可以一起看下 exec 命令的這兩個(gè)參數(shù),自然就差不多理解了。
-i/-interactive #即使沒有附加也保持 STDIN 打開;如果你需要執(zhí)行命令則需要開啟這個(gè)選項(xiàng)
-t/–tty #分配一個(gè)偽終端進(jìn)行執(zhí)行;一個(gè)連接用戶的終端與容器 stdin 和 stdout 的橋梁
解決方法:docker exec 的參數(shù) -t 是指 Allocate a pseudo-TTY 的意思,而 CI 在執(zhí)行 job 的時(shí)候并不是在 TTY 終端中執(zhí)行,所以 -t 這個(gè)參數(shù)會(huì)報(bào)錯(cuò)。
14.Docker 定時(shí)任務(wù)異常
在 Crontab 定時(shí)任務(wù)中也存在 Docker 命令執(zhí)行異常的情況!
問題起因:今天發(fā)現(xiàn)了一個(gè)問題,就是在備份 Mysql 數(shù)據(jù)庫的時(shí)候,使用 docker 容器進(jìn)行備份,然后使用 Crontab 定時(shí)任務(wù)來觸發(fā)備份。但是發(fā)現(xiàn)備份的 MySQL 數(shù)據(jù)庫居然是空的,但是手動(dòng)執(zhí)行對應(yīng)命令切是好的,很奇怪。
# Crontab定時(shí)任務(wù)
0 */6 * * * \
docker exec -it <container_name> sh -c \
'exec mysqldump --all-databases -uroot -ppassword ......'
解決方法:后來發(fā)現(xiàn)是因?yàn)閳?zhí)行的 docker 命令多個(gè) -i 導(dǎo)致的。因?yàn)?Crontab 命令執(zhí)行的時(shí)候,并不是交互式的,所以需要把這個(gè)去掉才可以??偨Y(jié)就是,如果你需要回顯的話則需要 -t 選項(xiàng),如果需要交互式會(huì)話則需要 -i 選項(xiàng)。
-i/-interactive #即使沒有附加也保持 STDIN 打開;如果你需要執(zhí)行命令則需要開啟這個(gè)選項(xiàng)
-t/–tty #分配一個(gè)偽終端進(jìn)行執(zhí)行;一個(gè)連接用戶的終端與容器 stdin 和 stdout 的橋梁
15.Docker 變量使用引號(hào)
compose 里邊環(huán)境變量帶不帶引號(hào)的問題!
問題起因:使用過 compose 的同學(xué)可能都遇到過,我們在編寫啟動(dòng)配置文件的時(shí)候,添加環(huán)境變量的時(shí)候到底是使用單引號(hào)、雙引號(hào)還是不使用引號(hào)。時(shí)間長了,可能我們總是三者是一樣的,可以相互使用。但是,直到最后我們發(fā)現(xiàn)坑越來越多,越來越隱晦。
反正我是遇到過很多是因?yàn)樘砑右?hào)導(dǎo)致的服務(wù)啟動(dòng)問題,后來得出的結(jié)論就是一律不適用引號(hào)。裸奔,體驗(yàn)前所未有的爽快!直到現(xiàn)在看到了 Github 中對應(yīng)的 issus 之后,才終于破案了。
# TESTVAR="test"
在Compose中進(jìn)行引用TESTVAR變量,無法找到
# TESTVAR=test
在Compose中進(jìn)行引用TESTVAR變量,可以找到
# docker run -it --rm -e TESTVAR="test" test:latest
后來發(fā)現(xiàn)docker本身其實(shí)已經(jīng)正確地處理了引號(hào)的使用
解決方法:得到的結(jié)論就是,因?yàn)?Compose 解析 yaml 配置文件,發(fā)現(xiàn)引號(hào)也進(jìn)行了解釋包裝。這就導(dǎo)致原本的 TESTVAR="test" 被解析成了 'TESTVAR="test"',所以我們在引用的時(shí)候就無法獲取到對應(yīng)的值?,F(xiàn)在解決方法就是,不管是我們直接在配置文件添加環(huán)境變量或者使用 env_file 配置文件,能不使用引號(hào)就不適用引號(hào)。
16. Docker 刪除鏡像報(bào)錯(cuò)
無法刪除鏡像,歸根到底還是有地方用到了!
問題起因:清理服器磁盤空間的時(shí)候,刪除某個(gè)鏡像的時(shí)候提示如下信息。提示需要強(qiáng)制刪除,但是發(fā)現(xiàn)及時(shí)執(zhí)行了強(qiáng)制刪除依舊沒有效果。
# 刪除鏡像
$ docker rmi 3ccxxxx2e862
Error response from daemon: conflict: unable to delete 3ccxxxx2e862 (cannot be forced) - image has dependent child images
# 強(qiáng)制刪除
$ dcoker rmi -f 3ccxxxx2e862
Error response from daemon: conflict: unable to delete 3ccxxxx2e862 (cannot be forced) - image has dependent child images
解決方法:后來才發(fā)現(xiàn),出現(xiàn)這個(gè)原因主要是因?yàn)?TAG,即存在其他鏡像引用了這個(gè)鏡像。這里我們可以使用如下命令查看對應(yīng)鏡像文件的依賴關(guān)系,然后根據(jù)對應(yīng) TAG 來刪除鏡像。
# 查詢依賴 - image_id表示鏡像名稱
$ docker image inspect --format='{{.RepoTags}} {{.Id}} {{.Parent}}' $(docker image ls -q --filter since=<image_id>)
# 根據(jù)TAG刪除鏡像
$ docker rmi -f c565xxxxc87f
bash
# 刪除懸空鏡像
$ docker rmi $(docker images --filter "dangling=true" -q --no-trunc)
17.Docker 普通用戶切換
切換 Docker 啟動(dòng)用戶的話,還是需要注意下權(quán)限問題的!
問題起因:我們都知道在 Docker 容器里面使用 root 用戶的話,是不安全的,很容易出現(xiàn)越權(quán)的安全問題,所以一般情況下,我們都會(huì)使用普通用戶來代替 root 進(jìn)行服務(wù)的啟動(dòng)和管理的。今天給一個(gè)服務(wù)切換用戶的時(shí)候,發(fā)現(xiàn) Nginx 服務(wù)一直無法啟動(dòng),提示如下權(quán)限問題。因?yàn)閷?yīng)的配置文件也沒有配置 var 相關(guān)的目錄,無奈 ???♀ !?
# Nginx報(bào)錯(cuò)信息
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ā)現(xiàn)還是 nginx.conf 配置文件,配置的有問題,需要將 Nginx 服務(wù)啟動(dòng)時(shí)候需要的文件都配置到一個(gè)無權(quán)限的目錄,即可解決。
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;
}





