在Docker容器中操作Docker (dind)
什么是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了.
怎么做
- 查阅官方的docker镜像, 里面会说到注意事项与使用方法.
简单来说 你只需要这样:
docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock docker "/bin/sh"
然后docker ps
就能看到宿主机上的所有容器.
- 运行你想要执行的命令
如我的就是
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....
- Original ip is not passed to containers
- Document how to get real remote client ip for service running in container
当前可用的解决办法有