• Docker容器内使用Docker——DinD与DooD


    DinD与DooD简介

    在部分场景中,我们需要在Docker容器内操作Docker镜像。如,容器内实现对资源的监控、服务的打包、自动化构建等操作,这些操作都需要能够与Docker服务端实现交互来实现的。

    在Docker容器内操作Docker有两种模式,分别为DinD(Docker in Docker):在Docker容器内部运行独立的Docker进程;DooD(Docker outside of Docker):运行在Docker容器外部的Docker,即在容器内部操作外部的Docker服务端。

    DinD与DooD对比:

    DinDDooD
    原理在Docker容器内部安装独立的Docker客户端与服务端,实现容器内独立的的Docker操作在Docker容器内部安装独立的Docker客户端,将外部Docker服务端的.sock文件挂载在容器内部,通过内部客户端与挂载的.sock文件实现Docker操作
    优点Docker服务端之间相互隔离,无干扰,缓存,适合需要独立隔离的环境能在容器内部实现与外部Docker服务端的交互,能够利用外部Docker外部服务端现有的镜像,缓存等资源
    缺点无法利用缓存,Docker容器销毁后环境镜像丢失容器内部的操作会对外部Docker造成影响。不同容器可以查看同一宿主机的Docker daemon守护进程,并且不同实例可以修改统一Docker daemon下的镜像,存在安全问题。多个实例之间存在环境的争抢问题。

    DinD与DooD的应用场景

    当我们需要在容器内实现对Docker外部资源的监控时,需要在容器内有一定的手段能够访问到外部的Docker服务端,实现与外部Docker服务端进行通讯,获取到Docker服务端到信息实现对资源的监控。如,常用的监控工具Prometheus,Docker监控管理面板Portainer这些都是可以通过DooD的方式实现内部容器监控外部Docker资源情况。
    在一些场景中,我们需要实现CI/CD等自动化集成等操作如Jenkins时,如果采用容器的方式部署,需要在如集成容器内部进行容器打包的情况下,可以采用DinD或者DooD的模式,实现容器内部的镜像打包登操作。

    DooD构建Docker镜像

    由于Docker对容器来运行Docker的做法做出一定限制,所以无法直接在Docker容器内按照Docker。如我们参考Docker安装文档的方式在容器内部安装Docker,只会安装Docker客户端。

    构建DooD镜像

    以Ubuntu:22.04镜像为基镜像,参考Docker引擎安装官方文档,构建DooD镜像。

    DooD镜像Dockerfile:

    FROM ubuntu:22.04
    
    RUN \
        # Add Docker's official GPG key:
        apt update && \
        apt install -y ca-certificates curl gnupg && \
        install -m 0755 -d /etc/apt/keyrings && \
        curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
        chmod a+r /etc/apt/keyrings/docker.gpg && \
        # Add the repository to Apt sources:
        echo \
            "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
            "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
            tee /etc/apt/sources.list.d/docker.list > /dev/null && \
        apt update && \
        # Install the Docker packages.
        apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    
    CMD [ "bash" ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    DooD镜像的构建命令:

    docker build . \
        --file ./Dockerfile \
        --tag "ubuntu:dood"
    
    • 1
    • 2
    • 3

    运行DooD镜像

    直接运行Docker

    打包后直接运行DooD镜像:

    docker run -it ubuntu:dood bash
    
    • 1

    调用Docker命令:

    root@17f49a00974d:/# docker version
    Client: Docker Engine - Community
     Version:           24.0.6
     API version:       1.43
     Go version:        go1.20.7
     Git commit:        ed223bc
     Built:             Mon Sep  4 12:31:44 2023
     OS/Arch:           linux/amd64
     Context:           default
    Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如上命令所示,调用docker version后。Docker客户端日志信息输出正常,但是服务端却显示Cannot connect to the Docker daemon at unix:///var/run/docker.sock.。我们平常在Docker客户端执行的命令实际上是Docker的客户端进程,实际上Docker如打包镜像,运行容器,拉取镜像登操作都是由Docker服务端操作的,Docker客户端实现通过命令来调用服务端。

    DooD模式运行Docker

    通过.sock后缀可以看到,Docker的客户端和服务端实际上是通过socket机制进行的通讯。在采用DooD模式的情况下,我们可以讲外部的Docker服务端socket文件挂载在内部容器中,实现在容器内部,借助外部socket进行通讯,完成Docker调用。

    DooD模式运行DooD镜像:

    docker run -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:dood bash
    
    • 1

    调用Docker命令:

    root@ff54c629edd0:/# docker version
    Client: Docker Engine - Community
     Version:           24.0.6
     API version:       1.41 (downgraded from 1.43)
     Go version:        go1.20.7
     Git commit:        ed223bc
     Built:             Mon Sep  4 12:31:44 2023
     OS/Arch:           linux/amd64
     Context:           default
    
    Server: Docker Desktop 4.17.0 (99724)
     Engine:
      Version:          20.10.23
      API version:      1.41 (minimum version 1.12)
      Go version:       go1.18.10
      Git commit:       6051f14
      Built:            Thu Jan 19 17:32:04 2023
      OS/Arch:          linux/amd64
      Experimental:     false
     containerd:
      Version:          1.6.18
      GitCommit:        2456e983eb9e37e47538f59ea18f2043c9a73640
     runc:
      Version:          1.1.4
      GitCommit:        v1.1.4-0-g5fd4c4d
     docker-init:
      Version:          0.19.0
      GitCommit:        de40ad0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    如上命令所示,通过挂载/var/run/docker.sock文件后启动DooD镜像,调用Docker命令不仅打印出容器内部Docker客户端的日志,也打印出了外部Docker服务端的日志。此时,我们在容器内部调用Docker命令,实际上就是交由外部的Docker服务端来进行操作,所以在容器内部的Docker操作会同步到外部宿主机中。

    DinD构建Docker镜像

    通过官方dind镜像使用dind模式的docker

    由于Docker的限制,不可以直接在Docker里面安装Docker服务端,但是Docker官方有提供构建好的DinD镜像docker:dind

    由于docker本质上是一种隔离技术,在Docker容器内部仍需要访问到外部的Linux资源。在DinD模式下运行Docker,也需要访问到外部Linux内核中的资源,有些资源需要root权限才可以访问。所以在启动DinD镜像时,需要添加--privileged参数,开启Docker容器到特权模式,否则容器内部无法访问特定资源。

    启动官方DinD镜像:

    docker run -it --privileged docker:dind bash
    
    • 1

    本地构建DinD镜像

    由于Docker本身只是一种服务隔离技术,并不是传统意义上的虚拟机,并不能完全的虚拟化模拟软硬件资源,在容器的运行中还是需要访问外部资源,且由于Docker底层采用UnionFS文件系统。所以无法通过安装的方式安装Docker服务端。Docker提供有二进制免安装文件,通过二进制免安装文件,可以在一定程度上不完全依赖外部的环境,实现Docker内部访问Docker资源。

    访问GitHub的docker镜像构建仓库阅读代码可知,Docker官方的dind镜像也是通过二进制文件实现的。

    Docker在使用时,需要依赖一些证书和库,所以我们可以参考Docker引擎安装官方文档添加必要的仓库但不执行后续的Docker安装操作,然后安装二进制运行依赖库即可。在ubuntu:22.04镜像中,只需要额外安装iptables库即可。

    下载解压二进制Docker文件

    Docker官方提供了Docker的二进制文件供无网络或免安装环境下使用,可在Docker官网提供的下载地址下载对应版本的Dokcer二进制包tgz文件。

    以docker-24.0.6.tgz为例,进行下载解压

    # Docker二进制文件下载
    wget https://download.docker.com/linux/static/stable/x86_64/docker-24.0.6.tgz
    
    # Docker二进制文件解压
    docker -xzvf docker-24.0.6.tgz
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • Docker软件分为客户端进程和服务端进程,Docker的二进制软件同样也有服务端进程dockerdcontainerd
      • dockerdcontainerd进程需要以守护进程的方式在后端运行
      • dockerd进程依赖于containerdrunc进程
        • 需要先以nohup command &的方式启动containerdrunc进程
        • 确保依赖库安装后,依赖进程启动后,以nohup command &方式启动dockerd进程
    • dockerd服务端守护进程启动后,即可通过docker二进制文件操作docker

    构建DinD镜像

    ubuntu:22.04镜像为基,构建dind镜像

    DinD镜像Dockerfile:

    FROM ubuntu:22.04
    
    COPY ./docker /data/docker
    
    WORKDIR /data/docker
    
    RUN \
        # Add Docker's official GPG key:
        apt update && \
        apt install -y ca-certificates curl gnupg && \
        install -m 0755 -d /etc/apt/keyrings && \
        curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg && \
        chmod a+r /etc/apt/keyrings/docker.gpg && \
        # Add the repository to Apt sources:
        echo \
            "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
            "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
            tee /etc/apt/sources.list.d/docker.list > /dev/null && \
        apt update && \
        apt install -y iptables
    
    # Add docker to environment
    ENV PATH="/data/docker:${PATH}"
    
    CMD [ "bash" ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    DinD镜像打包命令:

    docker build . \
        --file ./Dockerfile \
        --tag "ubuntu:dind"
    
    • 1
    • 2
    • 3

    运行DinD镜像

    DinD镜像的运行需要在特权模式下运行。同时,启动容器后,需要依次以守护进程的方式启动runccontainerddockerd等进程,才可以以DinD的模式访问Docker。

    特权模式启动dind镜像:

    docker run -it --privileged ubuntu:dind bash
    
    • 1

    启动守护进程:

    nohup runc &
    nohup containerd &
    nohup dockerd &
    
    • 1
    • 2
    • 3

    运行Docker命令:

    root@a09d8ec17a42:/data/docker# docker version
    Client:
     Version:           23.0.6
     API version:       1.42
     Go version:        go1.19.9
     Git commit:        ef23cbc
     Built:             Fri May  5 21:13:15 2023
     OS/Arch:           linux/amd64
     Context:           default
    
    Server: Docker Engine - Community
     Engine:
      Version:          23.0.6
      API version:      1.42 (minimum version 1.12)
      Go version:       go1.19.9
      Git commit:       9dbdbd4
      Built:            Fri May  5 21:14:22 2023
      OS/Arch:          linux/amd64
      Experimental:     false
     containerd:
      Version:          v1.6.21
      GitCommit:        3dce8eb055cbb6872793272b4f20ed16117344f8
     runc:
      Version:          1.1.7
      GitCommit:        v1.1.7-0-g860f061
     docker-init:
      Version:          0.19.0
      GitCommit:        de40ad0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    如上命令所示,我们即打印了Docker的客户端和服务端信息。同时,当我们拉取镜像操作时,也可以看到,外部的Docker并不会发生变化,即通过DinD模式成功运行Docker,实现与外部环境的隔离。

  • 相关阅读:
    nginx学习(4)Nginx 配置高可用集群(主从配置)
    揭开人工智能、机器学习和深度学习的神秘面纱
    Mac系统在idea中安装tomcat报错 error=13, Permission denied和error =1 Operation not...解决办法
    停止使用 Python 循环,这三种方法效果更棒
    网工内推 | Linux运维,六险二金,最高30K,IE认证优先
    c++ 学习之运算符重载 之 <<
    Linux内核启动---init进程
    Python子进程管理与进程信息获取
    [Linux 基础] make、Makefile自动化构建代码工具
    七、C语言函数定义详解
  • 原文地址:https://blog.csdn.net/qq_42482078/article/details/132861398