Docker 常見疑難雜症解決方案

這裏主要是爲了記錄在使用 Docker 的時候遇到的問題及其處理解決方法。

1.Docker 遷移存儲目錄


默認情況系統會將 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 配置文件

# 3.改動docker啓動配置文件
$ sudo vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd --graph=/data/docker/

# 4.改動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 命令單純複製文件內容的話,就會出現屬性不一致的情況,同時還會有一定的安全問題。

2.Docker 設備空間不足

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 使用的空間擴展到整個文件系統。

# 查看系統的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

3.Docker 缺共享鏈接庫

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

4.Docker 容器文件損壞

對 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

5.Docker 容器優雅重啓

不停止服務器上面運行的容器,重啓 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
        }
    ]
}

6.Docker 容器無法刪除

找不到對應容器進程是最嚇人的

問題起因:今天遇到 docker 容器無法停止 / 終止 / 刪除,以爲這個容器可能又出現了 dockerd 守護進程託管的情況,但是通過ps -ef <container id>無法查到對應的運行進程。哎,後來開始開始查 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

7.Docker 容器中文異常

容器存在問題話,記得優先在官網查詢

問題起因:今天登陸之前部署的 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

8.Docker 容器網絡互通

瞭解 Docker 的四種網絡模型

問題起因:在本機部署 Nginx 容器想代理本機啓動的 Python 後端服務程序,但是對代碼服務如下的配置,結果訪問的時候一直提示 502 錯誤。

# 啓動Nginx服務
$ docker run -d -p 80:80 $PWD:/etc/nginx 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: <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
    }
    ...
}

當容器使用 host 網絡時,容器與宿主共用網絡,這樣就能在容器中訪問宿主機網絡,那麼容器的 localhost 就是宿主機的 localhost 了。

# 服務的啓動方式有所改變(沒有映射出來端口)
# 因爲本身與宿主機共用了網絡,宿主機暴露端口等同於容器中暴露端口
$ docker run -d -p 80:80 --network=host $PWD:/etc/nginx nginxx

9.Docker 容器總線錯誤

總線錯誤看到的時候還是挺嚇人了

問題起因:在 docker 容器中運行程序的時候,提示 bus error 錯誤。

# 總線報錯
$ inv app.user_op --name=zhangsan
Bus error (core dumped)

解決方法:原因是在 docker 運行的時候,shm 分區設置太小導致 share memory 不夠。不設置 –shm-size 參數時,docker 給容器默認分配的 shm 大小爲 64M,導致程序啓動時不足。

# 啓動docker的時候加上--shm-size參數(單位爲b,k,m或g)
$ docker run -it --rm --shm-size=200m pytorch/pytorch:latest

解決方法:還有一種情況就是容器內的磁盤空間不足,也會導致 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

10.Docker 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.

11.Docker 默認使用網段

啓動的容器網絡無法相互通信,很是奇怪!

問題起因:我們在使用 Docker 啓動服務的時候,發現有時候服務之前可以相互連通,而有時間啓動的多個服務之前卻出現了無法訪問的情況。究其原因,發現原來是因爲使用的內部私有地址網段不一致導致的。有點服務啓動到了 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"[]
        }
    }
}

12.Docker 服務啓動串臺

使用 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,所以啓動的時候被認爲是同一個,這就出現了上述的問題。如果需要深入瞭解的話,可以去看對應源代碼。

# 可以將目錄結構調整爲如下所示
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

13.Docker 命令調用報錯

在編寫腳本的時候常常會執行 docker 相關的命令,但是需要注意使用細節!

問題起因:CI 更新環境執行了一個腳本,但是腳本執行過程中報錯了,如下所示。通過對應的輸出信息,可以看到提示說正在執行的設備不是一個 tty。

隨即,查看了腳本發現報錯地方是執行了一個 exec 的 docker 命令,大致如下所示。很奇怪的是,手動執行或直接調腳本的時候,怎麼都是沒有問題的,但是等到 CI 調用的時候怎麼都是有問題。後來好好看下下面這個命令,注意到 -it 這個參數了。

# 腳本調用docker命令
docker exec -it <container_name> psql -Upostgres ......

我們可以一起看下 exec 命令的這兩個參數,自然就差不多理解了。

-i/-interactive #即使沒有附加也保持 STDIN 打開;如果你需要執行命令則需要開啓這個選項
-t/–tty #分配一個僞終端進行執行;一個連接用戶的終端與容器 stdin 和 stdout 的橋樑

解決方法:docker exec 的參數 -t 是指 Allocate a pseudo-TTY 的意思,而 CI 在執行 job 的時候並不是在 TTY 終端中執行,所以 -t 這個參數會報錯。

14.Docker 定時任務異常

在 Crontab 定時任務中也存在 Docker 命令執行異常的情況!

問題起因:今天發現了一個問題,就是在備份 Mysql 數據庫的時候,使用 docker 容器進行備份,然後使用 Crontab 定時任務來觸發備份。但是發現備份的 MySQL 數據庫居然是空的,但是手動執行對應命令切是好的,很奇怪。

# Crontab定時任務
0 */6 * * * \
    docker exec -it <container_name> sh -c \
        'exec mysqldump --all-databases -uroot -ppassword ......'

解決方法:後來發現是因爲執行的 docker 命令多個 -i 導致的。因爲 Crontab 命令執行的時候,並不是交互式的,所以需要把這個去掉纔可以。總結就是,如果你需要回顯的話則需要 -t 選項,如果需要交互式會話則需要 -i 選項。

-i/-interactive #即使沒有附加也保持 STDIN 打開;如果你需要執行命令則需要開啓這個選項
-t/–tty  #分配一個僞終端進行執行;一個連接用戶的終端與容器 stdin 和 stdout 的橋樑

15.Docker 變量使用引號

compose 裏邊環境變量帶不帶引號的問題!

問題起因:使用過 compose 的同學可能都遇到過,我們在編寫啓動配置文件的時候,添加環境變量的時候到底是使用單引號、雙引號還是不使用引號。時間長了,可能我們總是三者是一樣的,可以相互使用。但是,直到最後我們發現坑越來越多,越來越隱晦。

反正我是遇到過很多是因爲添加引號導致的服務啓動問題,後來得出的結論就是一律不適用引號。裸奔,體驗前所未有的爽快!直到現在看到了 Github 中對應的 issus 之後,才終於破案了。

# TESTVAR="test"
在Compose中進行引用TESTVAR變量,無法找到

# TESTVAR=test
在Compose中進行引用TESTVAR變量,可以找到

# docker run -it --rm -e TESTVAR="test" test:latest
後來發現docker本身其實已經正確地處理了引號的使用

解決方法:得到的結論就是,因爲 Compose 解析 yaml 配置文件,發現引號也進行了解釋包裝。這就導致原本的 TESTVAR="test" 被解析成了'TESTVAR="test"',所以我們在引用的時候就無法獲取到對應的值。現在解決方法就是,不管是我們直接在配置文件添加環境變量或者使用 env_file 配置文件,能不使用引號就不適用引號。

  1. Docker 刪除鏡像報錯

無法刪除鏡像,歸根到底還是有地方用到了!

問題起因:清理服器磁盤空間的時候,刪除某個鏡像的時候提示如下信息。提示需要強制刪除,但是發現及時執行了強制刪除依舊沒有效果。

# 刪除鏡像
$ 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=<image_id>)

# 根據TAG刪除鏡像
$ docker rmi -f c565xxxxc87f
bash
# 刪除懸空鏡像
$ docker rmi $(docker images --filter "dangling=true" -q --no-trunc)

17.Docker 普通用戶切換

切換 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 服務啓動時候需要的文件都配置到一個無權限的目錄,即可解決。

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;
}
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Oz7MaDc87heRUNf7z645zA