CD和CI是分开的,CD更追求上线的速度。比如在高峰期想要启动更多的实例进行运行。传统的容器化部署解决了迅速启动和维护大量副本的问题,即规模化问题。但是却带来了IO上的问题,因为每个镜像可能是因版本的需要从Docker仓库拉取的,这就带来了大量的网络IO,部署的时候并没有想象中迅速,即docker启动是很快,但是拉取却很慢。
需要以IO问题作为切入点。以java项目为例:
mvn package是打包的步骤,一般是必须要运行的一个步骤,如果这个过程完全运行在容器中,因为每个容器都是干净的,所以每次都要下载大量jar包。
这个问题,我们可以有两种解决方案,
第一是放弃容器环境,直接用实体机器的mvn环境打包,因为实体机的本地仓库会积累所以可以减少之后的构建。具体到gitlab-ci就是使用shell模式的runner,将一台装有maven的实体宿主机器作为runner运行。例如这样的配置
# .gitlab-ci.yml job1: stage: build # 根据tag指定跑脚本的runner tags: - shell-mvn-runner script: - mvn package -Dmaven.test.skip=true -B第二是将maven容器的本地仓库映射出来,类似于docker run -v的作用将外面的目录映射到里面,使每次可以重用下好的jar包。具体配置方式是在/etc/gitlab-runner/config.toml
[runners.docker] host = "" hostname = "" image = "ruby:2.1" privileged = false disable_cache = true volumes = ["/path/to/bind/from/host:/path/to/bind/in/container:rw"]这样配置后,该runner就相当于docker run的-v参数将外面的目录挂载进去了。 参考链接https://docs.gitlab.com/runner/configuration/advanced-configuration.html#volumes-in-the-runners-docker-section
在传统的方式上一般
第一步要写个Dockerfile,将jar包考到一个java的镜像(如java:8)然后将镜像的CMD改成java -jar ..。第二步 docker build -t xxx .将镜像构建出来。第三步 docker login && docker push将镜像上传到私有仓库。第四步将这个镜像在部署环境(如k8s)拉取并部署。在gitlab-ci时前三步一般是在dind(docker in docker)的环境中完成的,这样的话步骤二每次需要拉取一个java:8镜像,步骤三每次需要上传一个镜像(虽然上传时会有已有文件层不在传输,但是还是有很多校验过程)。
针对这个过程的IO我能想到的有以下几种优化:
1 不使用dind,而是直接用shell runner,在这台宿主机上安装docker环境。这样拉取就会因为累积而减少。主要优化的步骤二,但是shell模式会产生很多垃圾。多个仓库不好隔离(可以得到别人的镜像)。2 不以镜像作为部署的最小单位,我们部署的其实是jar包,没有必要非得打包成镜像-上传-拉取-部署。我们只需要将jar包保存到一个pv上,然后直接到第四步,在java的镜像上将jar包目录映射进去,直接运行java -jar xxx就可以了。以前的yml
# .gitlab-ci.yml image: docker:latest services: - docker:dind variables: DOCKER_DRIVER: overlay stages: - build - package - deploy maven-build: image: cr.d.xiaomi.net/wuyao/mimvn:v1 stage: build script: - mvn package -Dmaven.test.skip=true -B artifacts: paths: - target/*.jar docker-build: stage: package script: - wget http://v10-staging.git.n.xiaomi.com/wuyao/Dockerfiles/raw/master/mvn-spring-boot/Dockerfile - docker build -t cr.d.xiaomi.net/wuyao/demo . - docker login -u wuyao -p $DOCKER_TOKEN cr.d.xiaomi.net - docker push cr.d.xiaomi.net/wuyao/demo deploy: stage: deploy image: cr.d.xiaomi.net/wuyao/kubectl:v1.7.6 script: - kubectl config set-credentials wuyao --token $TOKEN - kubectl config set-cluster c3-k8s --server https://c3-k8s.cc.d.xiaomi.net - kubectl config set-context wuyao@c3-k8s/wuyao --user wuyao --cluster c3-k8s --namespace wuyao - kubectl config use-context wuyao@c3-k8s/wuyao - sh -c kubectl delete -f http://v10-staging.git.n.xiaomi.com/wuyao/Dockerfiles/raw/master/mvn-spring-boot/deployment.yaml - kubectl create -f http://v10-staging.git.n.xiaomi.com/wuyao/Dockerfiles/raw/master/mvn-spring-boot/deployment.yaml # .gitlab-ci.yml stages: - build - deploy job1: stage: build tags: - shell script: - mvn package -Dmaven.test.skip=true -B - mkdir -p /opt/nfs/wuyao/demo/$CI_COMMIT_SHA - cp ./target/*.jar /opt/nfs/wuyao/demo/$CI_COMMIT_SHA/ job2: stage: deploy tags: - docker image: cr.d.xiaomi.net/wuyao/kubectl:v1.7.6 script: - kubectl config set-credentials wuyao --token $TOKEN - kubectl config set-cluster c3-k8s --server https://c3-k8s.cc.d.xiaomi.net - kubectl config set-context wuyao@c3-k8s/wuyao --user wuyao --cluster c3-k8s --namespace wuyao - kubectl config use-context wuyao@c3-k8s/wuyao - apk --no-cache add openssl wget - wget http://v10-staging.git.n.xiaomi.com/wuyao/Dockerfiles/raw/master/mvn-spring-boot/deployment.yaml - sed -i "s%JARPATH%$CI_PROJECT_PATH/$CI_COMMIT_SHA%g" deployment.yaml - sh -c "kubectl delete -f deployment.yaml" - kubectl create -f deployment.yaml例如这个配置,分为构建和部署,构建阶段用shell-runner运行mavn package。然后将jar包考到pv上(这里我用的nfs挂载)。然后部署阶段用docker-runner使用kubectl镜像,按照写好的k8s的yaml文件模板简单替换字符后,进行创建。