这里主要是为了记录在使用 Docker 的时候遇到的问题及其处理解决方法。
Docker疑难杂症汇总
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.停止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
# [方式一] 改动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/" ] }
# 使用mv命令 $ sudo mv /var/lib/docker /data/docker # 使用cp命令 $ sudo cp -arv /data/docker /data2/docker
# 查看物理磁盘空间 $ 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
# 查看基本信息 # 硬件驱动使用的是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
# 显示哪些容器目录具有最大的日志文件 $ 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
# /etc/docker/daemon.json { "live-restore": true, "storage-opt": [ "dm.basesize=20G" ] }
# 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
# 报错信息
No space left on device
# 查看系统的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
# 报错信息 OSError: [Errno 28] No space left on device
# 1.首先更加提示排除日志提示内容(检查磁盘空间是否足够) -> 整机的磁盘和inode数量足够 ➜ df -ih Filesystem Inodes IUsed IFree IUse% Mounted on /dev/root 233M 13M 221M 6% / devtmpfs 16M 744 16M 1% /dev tmpfs 16M 5.1K 16M 1% /run # 2.涉及挂载则排查mount-bind方式挂载问题(手动试试) -> 没有问题;排除mount-bind缺陷 ➜ fallocate -l 10G /cache/test # 3.排除存储提供方式(排除驱动的问题) -> ESSD;排除驱动问题 到底是本机磁盘存储?NAS存储?NSF挂载的呢? # 4.确认磁盘存储类型(考试文件系统问题) -> EXT4 ➜ fdisk -l # 5.排除代码问题、权限问题(排除常见问题) -> 无果 一些常见的问题已经基本排除,那么我们来考虑文件系统本身的问题 # 6.往文件系统异常的方向上靠(执行命令dmesg查看内核日志) -> 异常信息找到了 ➜ dmesg EXT4-fs warning (device sdd): ext4_dx_add_entry:2461: Directory (ino: 3145729) index full, reach max htree level :2 EXT4-fs warning (device sdd): ext4_dx_add_entry:2465: Large directory feature is not enabled on this filesystem # 7.验证问题并解决问题 -> 的确没有开启large_dir的选项 ➜ tune2fs -l Filesystem revision #: 1 (dynamic) Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum Filesystem flags: signed_directory_hash ➜ tune2fs -O large_dir /dev/sdd
Docker 命令需要对/tmp 目录下面有访问权限
# 提示错误信息 $ docker-compose --version error while loading shared libraries: libz.so.1: failed to map segment from shared object: Operation not permitted
# 重新挂载 $ sudo mount /tmp -o remount,exec
对 dockerd 的配置有可能会影响到系统稳定
# 操作容器遇到类似的错误 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
不停止服务器上面运行的容器,重启 dockerd 服务是多么好的一件事
# 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 } ] }
找不到对应容器进程是最吓人的
# 删除容器 $ sudo docker rm -f f8e8c3.. Error response from daemon: Conflict, cannot remove the default name of the container
# 删除容器文件 $ sudo rm -rf /var/lib/docker/containers/f8e8c3...65720 # 重启服务 $ sudo systemctl restart docker.service
容器存在问题话,记得优先在官网查询
# 查看容器支持的字符集 root@b18f56aa1e15:# locale -a C C.UTF-8 POSIX
# 临时解决 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
了解 Docker 的四种网络模型
# 启动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: <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
server { ... location /api { proxy_pass http://172.17.0.1:8080 } ... }
# 服务的启动方式有所改变(没有映射出来端口) # 因为本身与宿主机共用了网络,宿主机暴露端口等同于容器中暴露端口 $ docker run -d -p 80:80 --network=host $PWD:/etc/nginx nginxx
总线错误看到的时候还是挺吓人了
# 总线报错 $ inv app.user_op --name=zhangsan Bus error (core dumped)
# 问题原因 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'
# 磁盘空间不足 $ df -Th Filesystem Type Size Used Avail Use% Mounted on overlay overlay 1T 1T 0G 100% / shm tmpfs 64M 24K 64M 1% /dev/shm
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 ...
# 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.
启动的容器网络无法相互通信,很是奇怪!
# 查看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": [] } } }
使用 docker-compose 命令各自启动两组服务,发现服务会串台!
# 服务目录结构如下所示
A: /data1/app/docker-compose.yml
B: /data2/app/docker-compose.yml
# 可以将目录结构调整为如下所示
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 -f ./docker-compose.yml -p app1 up -d
在编写脚本的时候常常会执行 docker 相关的命令,但是需要注意使用细节!
Docker命令调用报错
# 脚本调用docker命令 docker exec -it <container_name> psql -Upostgres ......
Docker命令调用报错
在 Crontab 定时任务中也存在 Docker 命令执行异常的情况!
# Crontab定时任务 0 */6 * * * \ docker exec -it <container_name> sh -c \ 'exec mysqldump --all-databases -uroot -ppassword ......'
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 类型的,这是如果希望保持原样信息的话,可以使用 '/" 引起来将其变成字符串格式的。
无法删除镜像,归根到底还是有地方用到了!
# 删除镜像 $ 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
# 查询依赖 - image_id表示镜像名称 $ docker image inspect --format='{{.RepoTags}} {{.Id}} {{.Parent}}' $(docker image ls -q --filter since=<image_id>) # 根据TAG删除镜像 $ docker rmi -f c565xxxxc87f
# 删除悬空镜像 $ docker rmi $(docker images --filter "dangling=true" -q --no-trunc)
切换 Docker 启动用户的话,还是需要注意下权限问题的!
# 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)
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; }
Docker 服务在启动的时候,将地址绑定到 IPv6 地址上面了,提示报错信息!
# 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.
# 操作系统配置 $ cat /etc/sysctl.conf | grep ipv6 net.ipv6.conf.all.disable_ipv6=1
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:
# 修改配置 $ vim /etc/docker/daemon.json { "ipv6": false, "fixed-cidr-v6": "2001:db8:1::/64" } # 重启服务 $ systemctl reload docker
# 修改系统配置 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
Docker 服务在启动的时候,提示超时,被直接终止了!
$ 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
$ 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
如果发现服务都一切正常,但是无法无法访问的话,则多为网络问题!
# 部署服务架构 nginx(80) -> web1(8080) -> web2(8081) # 报错信息如下所示 nginx connect() failed (113: No route to host) while connecting to upstream
# 检查开放的端口 $ 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
新初始化的机器,无法获取私有仓库的镜像文件!
# 登录私有仓库 $ 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
# 登录私有仓库之后会在用户家目录下生成一个docker配置 # 其用来记录docker私有仓库的登录认证信息(是加密过的信息但不安全) => base64 $ cat .docker/config.json { "auths": { "docker.escapelife.site": { "auth": "d00u11Fu22B3355VG2xasE12w==" } } }
如何使使用 docker-compose 启动的容器服务 hang 住而不退出
➜ docker ps -a 4e6xxx9a4 app:latest "/xxx/…" 26 seconds ago Restarting (1) 2 seconds ago
# 类似原理 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
# 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"]
有些情况,内部规划的网段和可能和 Dockerd 默认的网段有冲突,导致异常出现!
➜ nc -v 172.16.100.12 8000 nc: connect to 172.16.100.12 port 8000 (tcp) failed: Connection refused
$ 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!
# 修改配置 $ 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
Docker 不使用默认网段
# 报错信息 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}] }
有些情况,我们服务器上面需要使用内部私有的容器镜像地址!
# 拉取/登陆私库时提示
$ 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 密码
解决 Docker 容器时间时区和宿主机不同步的问题!
# 容器内部时间(CST - 东八区 - 北京时间) [root@server ~]# date Fri Apr 27 22:49:47 CST 2022 # 容器外部时间(UTC - 格林尼治 - 标准时间) [root@server ~]# docker run --rm nginx date Fri Apr 27 14:49:51 UTC 2022
# 以 docker run 方式启动 $ docker run -d --name 'app' \ -v /etc/localtime:/etc/localtime \ escape/nginx:v1 # 以 Dockerfile 构建 ENV TimeZone=Asia/Shanghai RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 以 docker-compose 方式启动 environment: TZ: Asia/Shanghai
启动很多容器服务,导致存储和运行的容器目录(/var/lib/docker/)磁盘消耗很大!
# 查看容器的默认文件驱动 $ docker info | grep "Storage Driver" Storage Driver: overlay2 # 查看磁盘消耗大户 $ sudo du -sh /var/lib/docker/overlay2 900G /var/lib/docker/overlay2
Docker 容器导致磁盘不足
# 里面到底存储的啥 # diff文件夹占用100G+,merged文件夹占用200+G $ sudo ls -lh /var/lib/docker/overlay2/1e3137...9706bca6 drwxr-xr-x 10 root root 4096 8月 5 2021 diff/ # 大 -rw-r--r-- 1 root root 26 8月 5 2021 link -rw-r--r-- 1 root root 376 8月 5 2021 lower drwxr-xr-x 1 root root 4096 8月 5 2021 merged/ # 大 drwx------ 3 root root 4096 4月 24 12:22 work/ # link文件的内容对应了/var/lib/docker/overlay2/l的文件夹名称 # 保存了镜像层的短标识;用于解决mount参数中长字符超过页大小限制的问题
# 查看那个容器层占用了大量磁盘空间 $ sudo du -sh /var/lib/docker/overlay2/* | grep G | sort -rn 121G 1e3137...9706bca6 33G 9d50b3...ef3ae1b0 12G 462157...03ce3935 # 查找对应层所属于那个容器镜像 $ docker ps -q | \ xargs docker inspect --format '{{.State.Pid}}, {{.Id}}, {{.Name}}, {{.GraphDriver.Data.WorkDir}}' | \ grep xxx # 可以根据容器id和名称查看他的层数信息(从低到高) $ docker inspect xxx | grep -E "LowerDir|UpperDir|MergedDir|WorkDir"
# 在该目录下也存在一个同样名字容器的文件夹(xxx) $ ls -lh /var/lib/docker/image/overlay2/layerdb/mounts/ fff30d...bc6a0222 8078cf...73bda80b b801ce...cc78e234 # mount-id: 表示了我们刚才创建的容器的目录(/var/lib/docker/overlay2) $ ls -lh /var/lib/docker/image/overlay2/layerdb/mounts/ff30d...bc6a0222/ -rw-r--r-- 1 root root 69 8月 5 2021 init-id -rw-r--r-- 1 root root 64 8月 5 2021 mount-id -rw-r--r-- 1 root root 71 8月 5 2021 parent
启动很多容器服务,导致存储和运行的容器目录(/var/lib/docker/)磁盘消耗很大!
root@a06xxx9e799:/opt/app/config# rm config-docker-app.yml rm: cannot remove 'config-docker-app.yml': Device or resource busy
root@a06xxx9e799:/opt/app# cat /proc/mounts | grep "config" /dev/sda /opt/app/config/config-docker-app.yml ext4 rw,relatime,data=ordered 0 0
在使用容器添加环境变量的时候,加不加引号是有很大区别的!
version: "3" services: app: image: app:latest container_name: app ports: - 80:80 environment: - APP_CONFIG_EMAIL_LIST='["a", "b"]' networks: - app_network networks: app_network:
version: "3" services: app: image: app:latest container_name: app ports: - 80:80 environment: - APP_CONFIG_EMAIL_LIST=['a', 'b'] networks: - app_network networks: app_network:
$ python3 >>> from app.config import get_config >>> get_config('APP_CONFIG_EMAIL_LIST') ['a', 'b'] >>> type(get_config('APP_CONFIG_EMAIL_LIST')) <class 'list'>
需要分清楚 docker 镜像的导入命令组合,不要搞乱了!
# 报错信息 docker: Error response from daemon: No command specified
# 仓库操作 $ docker save app:0.1 -o app_0.1.tar $ docker load -i app_0.1.tar # 运行镜像 $ docker export -o app_0.1.tar a404c6c174a2 $ docker import app_0.1.tar
EACCES:日志权限问题
# supervisor服务启动报错 2022-10-29 15:08:44,427 INFO spawnerr: unknown error making dispatchers for 'nginx': EACCES 2022-10-29 15:08:44,427 INFO gave up: nginx entered FATAL state, too many start retries too quickly
[分析思路] 注意到提示信息中包含 EACCES,所以大概率判断是因为文件或目录权限不对导致的,这时就需要排除配置文件中关于文件和目录相关的配置是否有权限问题。后来发现是因为在容器中服务使用的是普通用户,但是输出日志文件所在的权限是 root 用户的。解决之后,再次 update,问题解决!
[分析思路] 但是我这里遇到的问题并不是普通文件,而是因为服务部署在类 K8S 的平台上面需要将日志输出到终端显示。发现输出到 /dev/fd/1 和 /dev/stdout 并不可以,总是提示上述问题,即便是修改了权限也无法正常使用。在下面这个链接上面看到,有人之前反馈这个问题。如果还是不行的话,就需要提前手动给下普通用户的权限。
# 启动前授权 chown app /dev/fd/1 chown app /dev/stdout chown app /dev/stderr
还是因为容器里面权限导致的问题
# Rust程序报错 OS can't spawn a new worker thread # Tomcat服务报错 Cannot find /usr/local/tomcat/bin/setclasspath.sh This file is needed to run this program
[分析思路] 其中关于 Rust 的报错,可以参考 Rust: OS can’t spawn a new worker thread 链接。可以看到是因为启动的时候,需要添加特权指令才能够正常使用。
[分析思路] 其中关于 Tomcat 的报错,可以参考 docker 运行 tomcat 报错 链接。可以看到,也是因为权限异常导致的问题,修复方式就是更新 runc 或者给容器加特权开关。
# docker --privileged --security-opt="seccomp=unconfined" # compose privileged: true
Operation not permitted
# 提示报错一 OpenBLAS blas thread init: pthread create failed for thread 1 of 16: Operation not permitted # 提示报错二 ls: cannot access '/etc/docker-entrypoint-initdb.d/': Operation not permitted
# compose privileged: true
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!