如何在Docker中使用Docker
1. 典型適用場(chǎng)景
在 CI 中,通常會(huì)有一個(gè) CI Engine 負(fù)責(zé)解析流程,控制整個(gè)構(gòu)建過(guò)程,而將真正的構(gòu)建交給 Agent 去完成。例如,Jenkins 、GitLab 均是如此。
如下圖, 連接 CI Engine 的 Agent, 種類很多。這是為了滿足不同項(xiàng)目對(duì)構(gòu)建環(huán)境的要求。
同時(shí) Agent 是動(dòng)態(tài)的,構(gòu)建時(shí)才需要,構(gòu)建完成時(shí)即銷毀。CI 非常適合實(shí)踐容器、Serverless 等技術(shù),因此在生產(chǎn)過(guò)程中 Agent 經(jīng)常是容器化的。
那么問(wèn)題就來(lái)了?如果 CI Engine 也是容器化的,在容器中如何使用 Agent 容器去構(gòu)建呢?如果 Agent 已經(jīng)是容器化的,那么在 Agent 上如何構(gòu)建鏡像呢?這就是本篇將給出的回答,如何在 Docker 中使用 Docker。
2. 兩種使用模式
我們需要知道 Docker 以 C/S 模式工作,主要分為兩個(gè)部分,Docker CLI 和 Docker Daemon 。Docker CLI ,也就是客戶端,提供給用戶命令行操作 Docker,例如 docker create/images/ps 等。Docker Damon ,也就是守護(hù)進(jìn)程,負(fù)責(zé)接受用戶指令,維護(hù)容器的生命周期。
2.1 Docker in Docker
Docker in Docker ,以下簡(jiǎn)稱 DinD 。
如上圖,可以在 Container 中直接運(yùn)行一個(gè) Docker Daemon ,然后使用 Container 中的 Docker CLI 工具操作容器。
這種方式下,容器中的 Docker Daemon 完全獨(dú)立于外部,具有良好的隔離特性??雌饋?lái),Container 類似一個(gè) VM ,但 DinD 的作者自己也不是很推薦。
主要原因還是安全問(wèn)題。DinD 需要以特權(quán)模式啟動(dòng),這種嵌套會(huì)帶來(lái)潛在的安全風(fēng)險(xiǎn)。
這種方式下,響應(yīng)命令的容器嵌套于使用 docker 命令的容器。
2.2 Docker outside of Docker
Docker outside of Docker ,以下簡(jiǎn)稱 DooD 。
如上圖,Docker 以 C/S 模式工作,使用時(shí)用戶關(guān)注的是 C 端,而生命周期的管理在 S 端。
因此,只需要將 Container 的外部 Docker Daemon 服務(wù)掛載到 Container 。讓 Container 誤以為本地運(yùn)行了 Docker Daemon,使用 Docker CLI 命令操作時(shí),外部的 Docker Daemon 會(huì)響應(yīng)請(qǐng)求。
這種方式下,響應(yīng)命令的容器與使用 docker 命令的容器處于同一層級(jí)。
3. Docker 環(huán)境下的演示
3.1 DinD
- 運(yùn)行 DinD 容器
- $ docker run --privileged -e DOCKER_TLS_CERTDIR="" -d --name dockerd docker:dind
- d6414f2ff0076c42de19a8a1fe122481c1a72b3bd45fd490dbe1c427414b4139
- 運(yùn)行帶 CLI 的容器鏈接 DinD 容器
- $ docker run --rm -it --link dockerd:docker docker:latest sh
- 在 DinD 容器中,拉取鏡像
- # 拉取鏡像
- $ docker pull shaowenchen/devops-java-sample
- # 查看鏡像
- $ docker images
- REPOSITORY TAG IMAGE ID CREATED SIZE
- shaowenchen/devops-java-sample latest fa4651c24a18 6 weeks ago 122MB
使用起來(lái)和一個(gè)獨(dú)立的 Docker Daemon 環(huán)境一樣。
- 查看外部是否受影響
鍵入 exit 退出容器,通過(guò)主機(jī)上的 Docker Daemon
- $ docker images |grep fa4651c24a18
符合預(yù)期。DinD 使用的是獨(dú)立的 Docker Daemon,對(duì)外部的實(shí)例沒(méi)有直接影響。
3.2 DooD
- 運(yùn)行一個(gè)容器
- $ docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock alpine sh
- 安裝 curl
這里為了避免安裝 Docker CLI ,直接使用 curl 調(diào)用 Docker Daemon 的 API。
- $ apk update && apk add curl
- 拉取鏡像
- $ curl -XPOST --unix-socket /var/run/docker.sock http://localhost/images/create?fromImage=shaowenchen/docker-robotframework&tag=latest
- ...
- {"status":"Status: Downloaded newer image for shaowenchen/docker-robotframework"}
- 查看拉取的鏡像
鍵入 exit 退出容器,通過(guò)主機(jī)上的 Docker Daemon
- $ docker images |grep robotframework
- shaowenchen/docker-robotframework latest d99cfa7ee716 12 months ago 1.5GB
符合預(yù)期。DooD 方式直接使用的外部 Docker Daemon。
4. Kubernetes 環(huán)境下的演示
4.1 DinD
- 創(chuàng)建一個(gè) dind.yaml 文件,內(nèi)容如下:
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: dind
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: dind
- template:
- metadata:
- labels:
- app: dind
- spec:
- containers:
- - name: dockerd
- image: 'docker:dind'
- env:
- - name: DOCKER_TLS_CERTDIR
- value: ""
- securityContext:
- privileged: true
- - name: docker-cli
- image: 'docker:latest'
- env:
- - name: DOCKER_HOST
- value: 127.0.0.1
- command: ["/bin/sh"]
- args: ["-c", "sleep 86400;"]
- 創(chuàng)建 Deployment
- $ kubectl apply -f dind.yaml
- 查看創(chuàng)建的 Pod 名
- $ kubectl get pod |grep dind
- dind-5446ffbc8d-68q28 2/2 Running 0 12s
- 進(jìn)入 Pod
- $ kubectl exec -it dind-5446ffbc8d-68q28 -c docker-cli sh
- 測(cè)試是否使用獨(dú)立的 Docker Daemon
- $ docker pull nginx
- $ docker images
- REPOSITORY TAG IMAGE ID CREATED SIZE
- nginx latest daee903b4e43 3 days ago 133MB
符合預(yù)期,這里僅顯示了剛拉取的 Nginx 的鏡像,完全獨(dú)立于主機(jī)的 Docker Daemon。
4.2 DooD
- 創(chuàng)建一個(gè) dood.yaml 文件,內(nèi)容如下:
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: dood
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: dood
- template:
- metadata:
- labels:
- app: dood
- spec:
- containers:
- - image: docker:latest
- name: docker-cli
- securityContext:
- privileged: false
- command: ["/bin/sh"]
- args: ["-c", "sleep 86400;"]
- volumeMounts:
- - mountPath: /var/run/docker.sock
- name: volume-docker
- volumes:
- - hostPath:
- path: /var/run/docker.sock
- type: ""
- name: volume-docker
- 創(chuàng)建 Deployment
- $ kubectl apply -f dood.yaml
- 查看創(chuàng)建的 Pod 名
- $ kubectl get pod |grep dood
- dood-667d8bcfc6-d5fzf 1/1 Running 0 15s
- 進(jìn)入 Pod
- $ kubectl exec -it dood-667d8bcfc6-d5fzf -c docker-cli sh
- 測(cè)試是否使用的是主機(jī)的 Docker Daemon
- $ docker images |wc
- 69 482 8509
符合預(yù)期,這里 Docker 命令使用的就是外部的 Docker Daemon。
5. 參考
- https://medium.com/better-programming/about-var-run-docker-sock-3bfd276e12fd
- https://github.com/jpetazzo/dind































