docker 基础镜像的管理

本文讨论基础镜像的管理,只有一个问题:基础镜像之间会有依赖关系,被依赖的镜像变化时,依赖它的镜像都要重新 build。

我采用的是下面的方式,下面每一个名称都是一个目录,目录名就是镜像名,目录名是层级结构,以 – 分割,前面的被后面的依赖。

centos6
centos6-java7
centos6-java7-tomcat6
centos6-java7-tomcat7
centos6-java7-tomcat8
centos6-java8
centos6-java8-tomcat6
centos6-java8-tomcat7
centos6-java8-tomcat8
centos6-python2.6
centos6-python2.6-tornado
centos7
centos7-java7
centos7-java7-tomcat6
centos7-java7-tomcat7
centos7-java7-tomcat8
centos7-java8
centos7-java8-tomcat6
centos7-java8-tomcat7
centos7-java8-tomcat8
centos7-python2.6
centos7-python2.6-tornado

构建依赖通过字符串匹配判断,比如如果构建  centos6-python2.6,centos6-python2.6-tornado 也会被构建。

# python build.py -n centos6-python2.6
name:centos6-python2.6
names:[‘centos6-python2.6’, ‘centos6-python2.6-tornado’]
cmd:cd centos6-python2.6 && docker build –rm=true -t dockerhub.internal.nosa.me/centos6-python2.6 . && docker push dockerhub.internal.nosa.me/centos6-python2.6
build centos6-python2.6 succ
cmd:cd centos6-python2.6-tornado && docker build –rm=true -t dockerhub.internal.nosa.me/centos6-python2.6-tornado . && docker push dockerhub.internal.nosa.me/centos6-python2.6-tornado
build centos6-python2.6-tornado succ

build.py 脚本在 这里

构建 docker 镜像

构建 docker 镜像主要基于 git project 和 branch (还有 timeout ),输出的是可运行镜像。


我们基于 git 事件自动打包,当 project 有新的 push 或者 merge 事件时自动触发,我司使用 Gerrit,监控 git 事件
很简单:

ssh -p 29418 -i key_path user@git.xxx.com "gerrit stream-events"

比如获取到了下面的事件:

{
"type": "ref-updated",
    "submitter": {
    "name": "Zhou xxx",
    "email": "zhouxxx@xxx.com",
    "username": "zhouxxx"
},
"refUpdate": {
    "oldRev": "f612a09551f848c22cc6b758ab6844c9afd73b00",
    "newRev": "c6f8b2214afcaa83d11a109703c31227e9ac4068",
    "refName": "master",
    "project": "projectName"
}
}

匹配需要的 type 然后拿到 project 和 refName。


构建的整体思路是创建一个 Container,在 Container 里构建镜像,构建镜像需要一些配置文件,我们定义了一个 dockerfiles 项目来专门存放配置文件,目录结构如下:

dockerfiles/{project1,project2…}/{service1,service2…}/{Dockerfile,build_app_image,build_app.sh,…}
  1. 对于一个 project,它有哪些 service 根据 dockerfiles/project/ 下面的目录名称来获取,当 project 有事件发生
    ,它下面所有的 service 都会构建(构建之前先 pull dockerfiles,保持 dockerfiles 最新);

  2. service 需要一个构建应用程序包的镜像,包含必要的基础环境,比如 centos6-java6-maven 包含 java 和 maven。

  3. 构建应用程序包镜像 在 build_app_image 中定义;

  4. build_app.sh 是具体构建应用程序包的命令,可以约定一个基础目录统一存放 project,以便 build_app.sh 知道进入哪个目录打包;

  5. Dockerfile 用于打真正运行的镜像,FROM 后面指定运行基础镜像,比如 centos6-java8-tomcat8,运行镜像的格式定
    义为 service:branch,比如 image-service:release_20151128。

  6. 我们用的 Docker 调度系统是 swarm,调用 swarm api 创建基于构建应用程序包镜像的服务,并且 Container command (entrypoint)中要写相关命令,包括:

1). 在约定的统一目录拉取项目代码,用:

git clone -b release/RB_2015_11_01 --depth=1 ssh://git.nosa.me:29418/projectName

2). 并且拉取 dockerfiles 配置:

git clone -b release/master --depth=1 ssh://git.nosa.me:29418/dockerfiles

3). 执行 build_app.sh 打包;

4). 构建运行镜像:

docker build --rm=true -t dockerRegistry/image-service:release_20151128 .

docker push dockerRegistry/image-service:release_20151128

这两个命令在 Dockefile 的目录运行,这里会使用 build_app.sh 打成的包,比如 COPY webapp.war /。

这些命令合起来就是完整的构建命令,举个例子:

cd / && git clone -b master –depth=1 ssh://autodeploy@git.nosa.me:29418/wdstack && \
cd / && git clone -b master –depth=1 ssh://autodeploy@git.nosa.me:29418/dockerfiles && \
cd //wdstack && sh //dockerfiles/services/wdstack/wdstack-service/build_app.sh && \
cd //dockerfiles/services/wdstack/wdstack-service && \
docker build –rm=true -t hub.internal.nosa.me/wdstack-service:master . && \
docker push hub.internal.nosa.me/wdstack-service:master

build docker image in docker

我们在设计 docker 打包服务的时候遇到一个问题:如何在 docker 中 build docker image

这篇文章 中介绍使用

--privileged

参数可以在启动的 docker 中运行新的 docker,但是我发现 systemctl start docker 的时候报错:Failed to get D-Bus connection: No connection to service manager。

更简单的办法是把宿主机的 /var/run/docker.sock 挂载到 docker 的 /var/run/docker.sock,而且此时 docker 和宿主机共享 image,使得在 docker 中 build image 可以大大减小网络传输。

举个例子:

# docker run -t -v /var/run/docker.sock:/var/run/docker.sock -i 0d2e243b2977 /bin/bash
[root@b8bd7326f448 /]# mkdir test
[root@b8bd7326f448 /]# cd test
[root@b8bd7326f448 test]# cat Dockerfile
FROM 18573b5f8de7
ADD xxx /xxx
[root@b8bd7326f448 test]# touch xxx
[root@b8bd7326f448 test]# docker build .
Sending build context to Docker daemon  2.56 kB
Sending build context to Docker daemon
Step 0 : FROM 18573b5f8de7
—> 18573b5f8de7
Step 1 : ADD xxx /xxx
—> fa7718637b8a
Removing intermediate container 7d4615157279
Successfully built fa7718637b8a
[root@b8bd7326f448 test]# docker tag fa7718637b8a docker-in-docker-test
[root@b8bd7326f448 test]# docker push docker-in-docker-test
The push refers to a repository [10.19.26.15:5000/docker-in-docker-test] (len: 0)
fa7718637b8a: Image already exists
18573b5f8de7: Image successfully pushed
0f88a20aa712: Image successfully pushed
Digest:sha256:37eeddc4483dbad69136832dee304eb49cb186a1d80a20bb8a19431176340d9f

另外,我没找到如何在 Dockerfile 中定义「把宿主机的 /var/run/docker.sock 的挂载到 docker 的 /var/run/docker.sock」,但是如果使用 kubernetes,可以在 pod 配置文件中定义,比如:

apiVersion: v1
kind: Pod
metadata:
name: docker-in-docker
  labels:
name: docker-in-docker
spec:
containers:
  – name: docker-in-docker
    image: 10.19.26.15:5000/docker-in-docker
    ports:
    – containerPort: 80
      hostPort: 80
    volumeMounts:
      – mountPath: "/var/run/docker.sock"
        name: "docker-socket"
        volumes:
        – name: "docker-socket"
          hostPath:
          path: "/var/run/docker.sock"

参考链接

为什么叫应用基础平台

为什么我要建立这个分类?为什么不叫IAAS 或者PAAS,也为什么不叫做Docker 或者 Cloud foundry ?

因为我没想好怎么做,我知道现在运维最大的问题就是APP,如何管理APP是问题所在,这关系着开发和运维的合作方式,开发很痛苦,运维也很痛苦,我想改变这一点。

那么现在存在的工具,比如虚拟化,比如Docker ,比如其他的东西,他们每个都解决了什么问题?符合我的需求么?他们自己有什么问题?这些都是我思考的问题,我会一直思考的。

所以我把它叫做应用基础平台,我会把我的思考、思路、工具 和 方式 都写在这个分类里面,它代表着我对运维的思考,而不仅仅是某项技术。

所以我想对自己说:祝好。