在Docker容器中操作Docker

什么是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 = exec.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....

当前可用的解决办法有

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章