长期支持版本

    社区创新版本

      安装配置

      本章节主要介绍和开源容器Docker安装相关的重要配置。

      注意事项

      • Docker容器的安装需要使用root权限。
      • docker-engine rpm包与containerd rpm包、runc rpm包、podman rpm包不能同时安装。因为docker-engine rpm包中已经包含Docker运行所需的所有组件,其中包括containerd、runc、docker二进制,且containerd、runc和podman rpm包也分别提供了对应的二进制,所以重复安装时会出现软件包冲突。

      基本安装配置

      配置daemon参数

      可以通过在/etc/docker/daemon.json文件中添加配置项自定义配置参数,相关配置项以及如何使用可以通过dockerd --help查看。配置示例如下:

      cat /etc/docker/daemon.json 
      {        
          "debug": true,        
          "storage-driver": "overlay2",        
          "storage-opts": ["overlay2.override_kernel_check=true"] 
      }
      

      daemon运行目录配置

      用户需要明白重新指定各种运行目录和文件(包括--graph、--exec-root等),可能会存在目录冲突,或文件属性变换,对应用的正常使用造成影响。

      须知: 用户指定的目录或文件应为docker专用,避免冲突导致的文件属性变化带来安全问题。

      • 以--graph为例,当我们使用/new/path/作为daemon新的Root Dir时,如果/new/path/下已经存在文件,且目录或文件名与docker需要使用的目录或文件名冲突(例如: containers、hooks、tmp等目录)时,docker可能会更新原有目录或文件的属性,包括属主、权限等为自己的属主和权限。

      须知: 从docker-17.05开始,--graph参数被标记为Deprecated,用新的参数--data-root替代。

      daemon自带网络配置

      • Docker daemon使用--bip参数指定docker0网桥的网段之后,如果在下一次重启的时候去掉--bip参数,docker0网桥会沿用上一次的--bip配置,即使重启之前已经删除docker0网桥。原因是docker会保存网络配置并在下一次重启的时候默认恢复上一次配置。
      • Docker network create 并发创建网络的时候,可以创建具有相同名字的两个网络。原因是docker network是通过id来区分的,name只是个便于识别的别名而已,不保证唯一性。
      • Docker在桥接bridge网络模式下,Docker容器是通过宿主机上的NAT模式,建立与宿主机之外世界的通信。Docker Daemon在启动一个容器时,每在宿主机上映射一个端口都会启动一个docker-proxy进程来实现访问代理。建议用户在使用这种userland-proxy时,只映射必须的端口,减少docker-proxy进行端口映射所消耗的资源。

      daemon-umask配置

      容器主进程和exec进程的默认umask为0022,为了满足安全性需求,避免容器受到攻击,修改runc的实现,将默认umask修改为0027。修改后others群组将无法访问新建文件或目录。

      docker启动容器时的默认umask值为0027,可以在dockerd启动时,使用--exec-opt native.umask=normal参数将容器启动时的umask修改为0022。

      须知: 如果docker create/run也配置了native.umask参数,则以docker create/run中的配置为准。

      详细的配置见docker createdocker run章节的参数说明。

      daemon启动时间

      Docker服务由systemd管理,systemd对各个服务的启动时间有限制,如果指定时间内docker服务未能成功启动,则可能由以下原因导致:

      • 如果使用devicemapper且为第一次启动,docker daemon需要对该设备做文件系统初始化操作,而该操作会进行大量磁盘IO操作,在磁盘性能不佳或存在大量IO竞争时,很可能会导致docker daemon启动超时。devicemapper设备只需要初始化一次,后续docker daemon启动时不再需要重复初始化。
      • 如果当前系统资源占用太高,导致系统卡顿,系统所有的操作都会变慢,也可能会出现docker服务启动超时的情况。
      • daemon重启过程中,需要遍历并读取docker工作目录下每一个容器的配置文件、容器init层和可写层的配置,如果当前系统存在过多容器(包含created和exited的容器),并且磁盘读写性能受限,也会出现daemon遍历文件过久导致docker服务启动超时的情况。

      出现服务启动超时情况,建议对以下两种情况进行排查调整:

      • 容器编排层定期清理不需要的容器,尤其是exited的容器。
      • 结合解决方案的性能要求场景,调整编排层的清理周期和docker服务的启动时间。

      关联组件journald

      重启systemd-journald后需要重启docker daemon。journald通过pipe获取docker daemon的日志,如果journald服务重启,会导致该pipe被关闭,docker的日志写入操作便会触发SIGPIPE信号,该错误信号会导致docker daemon crash。由于忽略该信号影响严重,可能导致后续docker daemon的日志无法记录,因此建议用户在重启journald服务或者journald 异常后主动去重启docker daemon,保证docker日志能够被正常记录,避免daemon crash导致的状态异常。

      关联组件firewalld

      需要在重启或拉起firewalld之后重启docker服务,保证docker服务在firewalld之后启动。

      • firewalld服务启动会清空当前系统的iptables规则,所以在启动docker daemon过程中,重启firewalld可能会导致docker服务插入iptables规则失败,从而导致docker服务启动失败。
      • docker服务启动后重启firewalld服务,或者状态发生了变化(从启动到停止,或者从停止到启动),会导致docker的iptables规则被删除,创建带端口映射的容器失败。

      关联组件iptables

      docker使用--icc=false选项时,可以限制容器之间互通,但若os自带某些规则,可以造成限制容器之间互通失效,例如:

      Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) 
      ... 
      0     0 ACCEPT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0 
      ... 
      0     0 DROP       all  --  docker0 docker0  0.0.0.0/0            0.0.0.0/0
      ...
      

      在Chain FORWARD中,DROP上面多出了一条ACCEPT icmp的规则,造成加了--icc=false后,容器之间也能ping通,但容器之间如果使用udp/tcp协议,对端仍然是不可达的。

      因此,在容器os中使用docker,如果需要使用--icc=false选项时,建议先在host上清理一下iptables相关的规则。

      关联组件audit

      docker支持配置audit,但不是强制的。例如:

      -w /var/lib/docker -k docker 
      -w /etc/docker -k docker 
      -w /usr/lib/systemd/system/docker.service -k docker 
      -w /usr/lib/systemd/system/docker.socket -k docker 
      -w /etc/sysconfig/docker -k docker 
      -w /usr/bin/docker-containerd -k docker 
      -w /usr/bin/docker-runc -k docker 
      -w /etc/docker/daemon.json -k docker
      

      配置docker的audit,好处在于可以记录更多信息便于审计,但从安全角度来看,它对防攻击并没有实质性的作用。另一方面,audit配置会导致严重的效率问题,可能导致系统卡顿,生产环境中请谨慎使用。

      下面以“-w /var/lib/docker -k docker”为例,演示docker audit的配置:

      [root@localhost signal]# cat /etc/audit/rules.d/audit.rules | grep docker -w /var/lib/docker/  -k docker 
      [root@localhost signal]# auditctl -R /etc/audit/rules.d/audit.rules | grep docker 
      [root@localhost signal]# auditctl -l | grep docker -w /var/lib/docker/ -p rwxa -k docker
      

      说明: -p [r|w|x|a] 和-w一起使用,观察用户对这个目录的读、写、执行或者属性变化(如时间戳变化)。这样的话,在/var/lib/docker目录下的任何文件、目录操作,都会打印日志到audit.log中,从而会有太多的日志往audit.log中记录,会严重地影响auditd, 比如内存、cpu占用等,进而影响os的运行。例如:每次执行"ls /var/lib/docker/containers"都会有类似如下日志记录到/var/log/audit/audit.log中。

      type=SYSCALL msg=audit(1517656451.457:8097): arch=c000003e syscall=257 success=yes exit=3 a0=ffffffffffffff9c a1=1b955b0 a2=90800 a3=0 items=1 ppid=17821 pid=1925 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts6 ses=4 comm="ls" exe="/usr/bin/ls" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="docker"type=CWD msg=audit(1517656451.457:8097):  cwd="/root"type=PATH msg=audit(1517656451.457:8097): item=0 name="/var/lib/docker/containers" inode=1049112 dev=fd:00 mode=040700 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:container_var_lib_t:s0 objtype=NORMAL 
      

      安全配置seccomp

      在做容器网络性能测试时发现,Docker相对于原生内核namespace性能有所下降,经分析开启seccomp后,系统调用(如:sendto)不会通过system_call_fastpath进行,而是调用tracesys,这会带来性能大幅下降。因此,建议在有高性能要求的业务的容器场景下关闭seccomp,示例如下:

      docker run -itd --security-opt seccomp=unconfined busybox:latest
      

      禁止修改docker-daemon的私有目录

      不允许对Docker用的根目录(默认/var/lib/docker)和运行时目录(默认/run/docker)以及其文件作任何修改,包括在该目录下删除文件,添加文件,对目录或者文件做软/硬链接,修改文件的属性/权限,修改文件的内容等,如果确实需要做修改,后果自负。

      普通用户大量部署容器场景下的配置注意事项

      普通用户在OS主机上能创建的进程数的上限,例如:可以在系统中创建配置文件“/etc/security/limits.d/20-nproc.conf”限制;类似的,普通用户在容器里也能创建的进程数的上限,由容器镜像中“/etc/security/limits.d/20-nproc.conf”文件对应的值决定,如下所示:

      cat /etc/security/limits.conf 
      *       soft    nproc   4096
      

      当普通用户大量部署容器,导致容器内进程过多资源不够出现报错时,需要把容器镜像“/etc/security/limits.d/20-nproc.conf”文件中如上所示的4096配置值加大。

      可配置的最大值请参考内核的最大能力,如下:

      [root@localhost ~]# sysctl -a | grep pid_max 
      kernel.pid_max = 32768
      

      存储驱动配置

      本发行版docker支持overlay2和devicemapper两种存储驱动。由于overlay2较devicemapper而言,拥有更好的性能,建议用户在生产环境中优先考虑。

      配置overlay2存储驱动

      配置方法

      docker默认为使用overlay2存储驱动,也可以通过如下两种方式显式指定。

      • 编辑/etc/docker/daemon.json,通过storage-driver字段显式指定。

        cat /etc/docker/daemon.json
        {
            "storage-driver": "overlay2"
        }
        
      • 编辑/etc/sysconfig/docker-storage,通过docker daemon启动参数显式指定。

        cat /etc/sysconfig/docker-storage 
        DOCKER_STORAGE_OPTIONS="--storage-driver=overlay2"
        

      注意事项

      • 部分容器生命周期管理的操作会报找不到相应的rootfs或者相关的可执行文件。

      • 如果容器的健康检查配置的是执行容器内的可执行文件,也会报错,导致容器的健康检查失败。

      • 如果将overlay2作为graphdriver,在容器中第一次修改镜像中的文件时,若该文件的大小大于系统剩余的空间,修改将会失败。因为即使修改很小,也要把这个文件完整的拷贝到上层,剩余空间不足导致失败。

      • overlay2文件系统相比普通文件系统天然存在一些行为差异,归纳如下:

        • 内核版本

          overlay2只兼容原生4.0以上内核,建议配合使用ext4文件系统。

        • Copy-UP性能问题

          修改lower层文件会触发文件复制到upper层,其中数据块复制和fsync比较耗时。

        • rename目录问题

          • 只有源路径和目标路径都在merged层时,才允许rename系统调用,否则rename系统调用会报错-EXDEV。

          • 内核4.10引入了redirect dir特性来修复rename问题,对应内核选项为CONFIG_OVERLAY_FS_REDIRECT_DIR。

            在使用overlay2场景下,对文件系统目录进行重命名时,如果系统配置文件/sys/module/overlay/parameters/redirect_dir中配置的特性开关为关闭状态,则会导致使用失败;如果用户要使用相关特性,需要用户手动设置/sys/module/overlay/parameters/redirect_dir为“Y”。

        • Hard link break问题

          • 当lower层目录中有多个硬链接,在merged层写入数据会触发Copy-UP,导致硬链接断开。
          • 内核4.13引入了index feature来修复这个问题,对应内核选项为 CONFIG_OVERLAY_FS_INDEX。注意这个选项没有前向兼容性,不支持热升级。
        • st_dev和st_ino变化

          触发Copy-UP之后,用户只能看到merged层中的新文件,inode会变化。虽然attr和xattr可以复制,但st_dev和st_ino具有唯一性,不可复制。这会导致stat和ls查看 到相应的变化。

        • fd变化

          Copy-UP之前,以只读模式打开文件得到描述符fd1,Copy-UP之后,打开同名文件得到文件描述符fd2, 二者实际指向不同的文件。向fd2写入的数据不会在fd1中体现。

      异常场景

      容器使用配置了overlay2存储驱动的过程中,可能出现挂载点被覆盖的异常情况。例如

      异常场景-挂载点被覆盖

      挂载关系:在问题容器的挂载点的下面,存在一个/var/lib/docker/overlay2的挂载点:

      [root@localhost ~]# mount -l | grep overlay 
      overlay on /var/lib/docker/overlay2/844fd3bca8e616572935808061f009d106a8748dfd29a0a4025645457fa21785/merged type overlay (rw,relatime,seclabel,lowerdir=/var/lib/docker/overlay2/l/JL5PZQLNDCIBU3ZOG3LPPDBHIJ:/var/lib/docker/overlay2/l/ELRPYU4JJG4FDPRLZJCZZE4UO6,upperdir=/var/lib/docker/overlay2/844fd3bca8e616572935808061f009d106a8748dfd29a0a4025645457fa21785/diff,workdir=/var/lib/docker/overlay2/844fd3bca8e616572935808061f009d106a8748dfd29a0a4025645457fa21785/work) 
      /dev/mapper/dm-root on /var/lib/docker/overlay2 type ext4 (rw,relatime,seclabel,data=ordered)
      

      执行部分docker命令会遇到错误,比如:

      [root@localhost ~]# docker rm 1348136d32
      docker rm: Error response from daemon: driver "overlay2" failed to remove root filesystem for 1348136d32: error while removing /var/lib/docker/overlay2/844fd3bca8e616572935808061f009d106a8748dfd29a0a4025645457fa21785: invalid argument
      

      此时,在主机侧可以发现对应容器的rootfs找不到,但这并不意味着rootfs丢失,只是被/var/lib/docker/overlay2挂载点覆盖,业务仍然可以正常运行,不受影响。修复方案可以参考如下:

      • 修复方案一

        1. 确定当前docker所使用graphdriver:

          docker info | grep "Storage Driver"
          
        2. 查询当前的挂载点:

          Devicemapper: mount -l | grep devicemapper 
          Overlay2: mount -l | grep overlay2
          

          输出格式为: A on B type C (D)

          • A:块设备名称或overlay
          • B:挂载点
          • C:文件系统类型
          • D:挂载属性
        3. 从下往上逐一umount这些挂载点B。

        4. 然后全部docker restart这些容器,或者删除所有容器。

        5. 重启docker。

          systemctl restart docker
          
      • 修复方案二

        1. 业务迁移
        2. 节点重启

      配置devicemapper存储驱动

      用户如果需要使用devicemapper存储驱动,可以通过如下两种方式显式指定。

      • 编辑/etc/docker/daemon.json,通过storage-driver字段显式指定。

        cat /etc/docker/daemon.json
        {
            "storage-driver": "devicemapper"
        }
        
      • 编辑/etc/sysconfig/docker-storage,通过docker daemon启动参数显式指定。

        cat /etc/sysconfig/docker-storage 
        DOCKER_STORAGE_OPTIONS="--storage-driver=devicemapper"
        

      注意事项

      • 使用devicemapper必须使用devicemapper+direct-lvm的方式,配置的方法可以参考 https://docs.docker.com/engine/userguide/storagedriver/device-mapper-driver/#configure-direct-lvm-mode-for-production

      • 配置devicemapper时,如果系统上没有足够的空间给thinpool做自动扩容,请禁止自动扩容功能。

      • 禁止把/etc/lvm/profile/docker-thinpool.profile中如下两个值都改成100。

        activation {   
          thin_pool_autoextend_threshold=80   
          thin_pool_autoextend_percent=20 
        }
        
      • 使用devicemapper时推荐加上--storage-opt dm.use_deferred_deletion=true --storage-opt dm.use_deferred_removal=true。

      • 使用devicemapper时,容器文件系统推荐使用ext4,需要在docker daemon的配置参数中加 上--storage-opt dm.fs=ext4。

      • 当graphdriver为devicemapper时,如果metadata文件损坏且不可恢复,需要人工介入恢复。禁止直接操作或篡改daemon存储devicemapper的元数据。

      • 使用devicemapper lvm时,异常掉电导致的devicemapper thinpool损坏,无法保证thinpool损坏后可以修复,也不能保证数据的完整性,需重建thinpool。

      docker daemon开启了user namespace特性,切换devicemapper存储池时的注意事项

      • 一般启动容器时,deviceset-metadata文件为:/var/lib/docker/devicemapper/metadata/deviceset-metadata。
      • 使用了user namespace场景下,deviceset-metadata文件使用的是:/var/lib/docker/{userNSUID.GID}/devicemapper/metadata/deviceset-metadata。
      • 使用devicemapper存储驱动,容器在user namespace场景和普通场景之间切换时,需要将对应deviceset-metadata文件中的BaseDeviceUUID内容清空;针对thinpool扩容或者重建的场景下,也同样的需要将对应deviceset-metadata文件中的BaseDeviceUUID内容清空,否则docker服务会重启失败。

      强制退出docker相关后台进程的影响

      信号量残留

      使用devicemapper作为graphdriver时,强制退出强制退出可能导致信号量残留。docker在操作dm的过程中会创建信号量,如果在释放信号量前,daemon被强制退出,可能导致该信号量无法释放,一次强制退出最多泄露一个信号量,泄露概率低。而linux系统有信号量上限限制,当信号量泄露次数达到上限值时将无法创建新的信号量,进而导致docker daemon启动失败。排查方法如下:

      1. 首先查看系统上残留的信号量

        $ ipcs 
        ------ Message Queues -------- 
        key        msqid      owner      perms      used-bytes   messages 
        ------ Shared Memory Segments -------- 
        key        shmid      owner      perms      bytes      nattch     status 
        ------ Semaphore Arrays -------- 
        key        semid      owner      perms      nsems 
        0x0d4d3358 238977024  root       600        1 
        0x0d4d0ec9 270172161  root       600        1 
        0x0d4dc02e 281640962  root       600        1
        
      2. 接着用dmsetup查看devicemapper创建的信号量,该信号量集合是上一步中查看到的系统信号量的子集

        dmsetup udevcookies 
        
      3. 最后查看内核信号量设置上限,第四个值就是当前系统的信号量使用上限

        # cat /proc/sys/kernel/sem 
        250     32000   32      128
        

        如果步骤1中残留的信号量数量与步骤3中看到的信号量上限相等,则是达到上限,此时docker daemon无法正常启动。可以使用下述命令增加信号量使用上限值来让docker恢复启动

        echo 250 32000  32  1024 > /proc/sys/kernel/sem
        

        也可以手动清理devicemapper残留的信号量(下面是清理一分钟以前申请的dm相关信号量)

        # dmsetup udevcomplete_all 1 
        This operation will destroy all semaphores older than 1 minutes with keys that have a prefix 3405 (0xd4d). 
        Do you really want to continue? [y/n]: y 
        0 semaphores with keys prefixed by 3405 (0xd4d) destroyed. 0 skipped.
        

      网卡残留

      使用bridge模式启动容器的过程中,强制退出daemon可能导致网卡残留。使用bridge网络模式,当docker创建容器时,会先在host上创建一对veth,然后再把该网卡信息存到数据库中,如果在创建完成,存到docker的数据库之前,daemon被强制退出,那么该网卡无法被docker关联,下次启动也无法删除(docker本身会清理自己数据库中不用的网卡),从而造成网卡残留。

      重启容器失败

      容器hook耗时较长,且启动阶段遇到containerd被强制退出,再次执行容器start操作可能失败。容器启动阶段遇到containerd被强制退出,docker start操作直接返回错误;containerd被重新拉起后,上次启动可能仍处于runc create执行阶段(执行用户自定义hook,可能耗时较长),此时再次下发docker start命令启动该容器,可能提示以下错误:

      Error response from daemon: oci runtime error: container with id exists: xxxxxx
      

      该错误是由runc create一个已经存在(创建中)的容器导致,等第一次start对应的runc操作结束后再次执行docker start便可以成功。

      由于hook的执行不受docker控制,这种场景下尝试回收该容器有可能导致containerd进程启动卡死(执行未知hook程序),且问题的风险可控(短期影响当前容器的创建):

      • 问题出现后等待第一次操作结束可以再次成功启动该容器。
      • 一般是在容器启动失败后创建新的容器,不复用已经失败的容器。

      综上,该问题暂时作为场景约束。

      服务无法正常重启

      短时间内频繁重启docker服务导致该服务无法正常重启。docker系统服务由systemd负责监控,如果docker服务在10s内重启次数超过5次,systemd服务就会监控到该异常行为,因此会禁止docker服务启动。只有等到下一个10s周期开始后,docker服务才能响应重启命令正常重启。

      系统掉电影响

      主机意外掉电或系统panic等场景下,由于docker daemon的状态无法及时刷新到磁盘,导致重启后docker daemon状态不正常,可能出现的问题有(包括但不限于):

      • 掉电前创建的容器,重启后docker ps -a看不到,该问题是因为该容器的状态文件没有刷新到磁盘,从而导致重启后daemon无法获取到该容器的状态(镜像、卷、网络等也可能会有类似问题)。
      • 掉电前某个文件正处于写入状态,尚未完全写入,重启后daemon重新加载该文件发现文件格式不正常或内容不完整,导致重启加载出错。
      • 针对掉电时会破坏docker DB的情况,在重启节点时会清理data-root下面的db文件。因此重启前创建的如下信息在重启后会被删除:
        • network,用docker network创建的资源会在重启后清除。

        • volume,用 docker volume创建的资源会在重启后删除。

        • 构建缓存,构建缓存信息会在重启后删除。

        • containerd保存的元数据,由于启动容器会重建containerd元数据,重启节点会清理containerd中保存的元数据。

          说明: 用户若选择采用手动清理恢复环境的方式,可通过配置环境变量“DISABLE_CRASH_FILES_DELETE=true”屏蔽daemon掉电重启时db文件清理功能。

      文档捉虫

      “有虫”文档片段

      问题描述

      提交类型 issue

      有点复杂...

      找人问问吧。

      PR

      小问题,全程线上修改...

      一键搞定!

      问题类型
      规范和低错类

      ● 错别字或拼写错误;标点符号使用错误;

      ● 链接错误、空单元格、格式错误;

      ● 英文中包含中文字符;

      ● 界面和描述不一致,但不影响操作;

      ● 表述不通顺,但不影响理解;

      ● 版本号不匹配:如软件包名称、界面版本号;

      易用性

      ● 关键步骤错误或缺失,无法指导用户完成任务;

      ● 缺少必要的前提条件、注意事项等;

      ● 图形、表格、文字等晦涩难懂;

      ● 逻辑不清晰,该分类、分项、分步骤的没有给出;

      正确性

      ● 技术原理、功能、规格等描述和软件不一致,存在错误;

      ● 原理图、架构图等存在错误;

      ● 命令、命令参数等错误;

      ● 代码片段错误;

      ● 命令无法完成对应功能;

      ● 界面错误,无法指导操作;

      风险提示

      ● 对重要数据或系统存在风险的操作,缺少安全提示;

      内容合规

      ● 违反法律法规,涉及政治、领土主权等敏感词;

      ● 内容侵权;

      您对文档的总体满意度

      非常不满意
      非常满意
      提交
      根据您的反馈,会自动生成issue模板。您只需点击按钮,创建issue即可。
      文档捉虫
      编组 3备份