美文网首页web前端开发
在Docker容器中操作Docker (dind)

在Docker容器中操作Docker (dind)

作者: bysir | 来源:发表于2019-08-18 16:08 被阅读0次

    什么是DockerInDocker

    就是在Docker容器中再次运行一个Docker服务.

    在一个容器中操作Docker在CI工具中是很常见的, 如构建一个Docker镜像.

    但由于在容器中运行一个Docker服务会有各种问题, 如镜像文件存储, 嵌套的容器也并不容易维护, 后来便衍生出了另一种更实用的方案: 挂载主机上Docker服务的sock

    docker run -v /var/run/docker.sock:/var/run/docker.sock ...
    

    这样将不会遇到嵌套副作用,并且将在多个调用之间共享构建缓存。

    ps: 更多知识请阅读 docker 官方提及的这篇文章: do-not-use-docker-in-docker-for-ci

    我接下来要写的也是如何使用它, 并记录我的使用场景.

    需求

    我有一个需求是这样的:

    当某个镜像更新之后, 通知docker重新pull并部署. 简单的来说就是当容器更新, 就自动运行

    docker stack deploy --prune --resolve-image always -c ./stack.yaml myapp
    

    以实现更新部署.

    由于自己编写的程序也运行在Docker中, 而不是宿主机, 所有没办法直接执行以上命令, 这就需要Docker In Docker了.

    怎么做

    1. 查阅官方的docker镜像, 里面会说到注意事项与使用方法.

    简单来说 你只需要这样:

    docker run -it --rm  -v /var/run/docker.sock:/var/run/docker.sock docker "/bin/sh"
    

    然后docker ps就能看到宿主机上的所有容器.

    1. 运行你想要执行的命令

    如我的就是

    docker stack deploy --prune --resolve-image always -c ./stack.yaml myapp
    

    当然 这里的stack.yaml文件需要在构建这个容器时添加进来 或者 挂载进来, 这肯定难不倒你.

    With Golang

    如果你要将这段CMD在你的程序中运行也十分简单:

    func Run(name string, args ...string) (output string, err error) {
        cmd := exec.Command(name, args...)
        var out bytes.Buffer
        cmd.Stderr = &out
        cmd.Stdout = &out
        err = cmd.Run()
        output = out.String()
        if err != nil {
            return
        }
    
        return
    }
    
    func main(){
        _, err = Run("docker", "stack", "deploy", "--with-registry-auth", "--prune", "--resolve-image", "always", "-c", "./stack.yaml", "render")
    }
    

    写好程序之后你可以使用这个Dockfile构建你的镜像

    FROM docker
    
    COPY runner /
    
    ENTRYPOINT ["./runner"]
    

    而运行这个镜像的stack.yaml文件需要配置挂载

    version: '3'
    services:
      api:
        image: registry.cn-hangzhou.aliyuncs.com/xxxx:latest
        tty: true
        volumes:
          - /home/render/stack.yaml:/stack.yaml
          - /home/render/.docker:/root/.docker
          - /var/run/docker.sock:/var/run/docker.sock
        deploy:
          mode: global
          restart_policy:
            condition: on-failure
            delay: 5s
    

    你会看到我又挂载了.docker文件夹, 这个无关紧要, 在后面的疑难杂症会说到这个问题.

    疑难杂症

    --resolve-image always

    此参数是17.9版本之后新加的, 用于解决deploy不pull最新的镜像的问题. 详情看这个ISSUE:
    force docker deploy to pull new images

    没有权限pull

    私有仓库必须登录才有访问权限, 所以需要在宿主机上先login, 登录成功后会发现在 ~/.docker有新生成的配置文件
    , 其中存储了认证所需要的信息. 但在Docker容器中拿不到这个信息所以就会报错.

    解决办法是将配置文件挂载进容器

    volumes:
        - /home/yourusername/.docker:/root/.docker
    

    得到的客户端的Ip地址是 10.255.0.2

    问题描述:

    网络结构如下:

    客户端 -> 服务器上的Nginx容器 (反代)-> 应用程序

    在Nginx中配置了

    proxy_set_header HTTP_X_FORWARDED_FOR $remote_addr;
    

    在应用程序中得到"HTTP_X_FORWARDED_FOR"头 却是10.255.0.2, 而不是客户端真正的ip.

    解决办法:
    google : docker 10.255.0.2

    得到的信息挺多的, 大多数是Docker3年4年都没有close的ISSUE....

    当前可用的解决办法有

    相关文章

      网友评论

        本文标题:在Docker容器中操作Docker (dind)

        本文链接:https://www.haomeiwen.com/subject/opawjctx.html