首页

    gitlab CI/CD优化

    标签:gitlab-CI-CD,docker

    我的配置

    docker image cache

    docker-in-docker

    docker-in-docker(dind)顾名思义,就是在docker里面运行docker.由于要在docker里控制里面的docker,所以要开启privileged mode

    /etc/gitlab-runner/config.toml配置

    [[runners]]
      ...
      executor = "docker"
      [runners.docker]
        ...
        privileged = true
        volumes = ["/cache"]
      [runners.cache]
    

    另外,.gitlab-ci.yml需要配置services为dind

    image: docker:stable
    services:
      - docker:dind
    

    dind会保证每次创建的job都是一个干净的环境,之前执行job所build的镜像都不会保存.比如,我的配置中,build stage里的$CACHE_IMAGE_URL,$BUILD_IMAGE_URL,node:8.11.2-alpine都不会缓存在docker executor,当然,更不可能在gitlab runner的host上了。下次有job执行时,只能重新从registry拉取,无法利用docker缓存
    这里能优化的地方,只有创建一个tag为cache的镜像,在构建docker镜像时,先拉取tag为cache的镜像,在build的时候,使用--cache-from参数,像这样

    docker pull your_image:cache || true
    docker build --cache-from your_image:cache -t your_image:your_tag -t your_image:cache .
    docker push your_image:cache
    docker push your_image:your_tag
    

    Docker socket binding

    如果觉得docker in docker每次都需要pull,浪费时间,可以使用docker socket bind.

    /etc/gitlab-runner/config.toml配置

    [[runners]]
      ...
      executor = "docker"
      [runners.docker]
        ...
        privileged = false
        volumes = ["/cache","/var/run/docker.sock:/var/run/docker.sock"]
      [runners.cache]
    

    .gitlab-ci.yml不需要配置services

    docker socket bind和gitlab runner host共用docker daemon,所以build的镜像都会缓存在host,这样job执行时不用每次都去registry pull

    nodejs module cache

    我的博客在gitlab ci构建的时候,需要nodejs编译.而编译需要一些nodejs package,我们当然不想每次构建的时候,都npm install重新下载包.这就需要将node_modules目录缓存起来.

    shell executor

    如果gitlab runner executors设置的是shell

    The Shell executor is a simple executor that allows you to execute builds locally to the machine that the Runner is installed

    构建的时候,相当于在装有gitlab runner的机器上执行.gitlab-ci.ymlscript.npm install后,node_modules目录会在gitlab runner的工作目录里.使用gitlab CI配置里的cache,就可以在不同的job里使用缓存的node_modules目录.

    docker executor

    docker executor的话,就不能使用缓存在gitlab runner机器上的node_modules目录,因为每次构建都会创建新的容器

    The Docker executs when used with GitLab CI, connects to Docker Engine and runs each build in a separate and isolated container

    怎么办?好在docker layer是可以缓存的

    After building the image, all layers are in the Docker cache.
    In Docker 1.10 and higher, only RUN, COPY, and ADD instructions create layers. Other instructions create temporary intermediate images, and no longer directly increase the size of the build.

    npm install和后面的命令分开,不用&&拼接命令,对npm install单独一行RUN指令。

    FROM node:alpine
    WORKDIR /blog
    
    ADD package.json /tmp/package.json
    RUN cd /tmp && npm install --only=production \
        && cp -a /tmp/node_modules /blog
    
    ADD . /blog
    RUN ./node_modules/.bin/gulp --gulpfile gulpfile-prod.js \
        && mv html/index.html . \
        && rm -r node_modules src package* gulpfile* /tmp
    

    这样在构建的时候,node_modules目录会成为一层layer,被缓存起来.

    图片标题

    注意如果用的是dind,需要和上面docker-in-docker executor那的--cache-from那段代码一起使用才能缓存node_modules目录,因为dind不会在docker executor缓存镜像

    multi-stage builds

    multi-stage builds可以让我们在构建docker镜像时,使用不同的基础镜像
    比如我的博客需要nodejs环境编译,但是部署,上线不需要nodejs环境,需要openresty.Dockerfile可以这样写

    FROM node:alpine as node
    #编译博客
    ...
    
    FROM openresty:alpine
    COPY --from=node src dst 
    

    copy指令将nodejs编译好的目录拷贝到openresty

    OverlayFS driver

    如果使用的是docker executor的docker in docker mode,storage-driver用的是vfs,可以使用更好的storage-driver,参见

    apk add --virtual

    alpine安装的时候,可以将部署,上线时不需要的包安装到一个目录,最后再删除这个目录

    apk add --no-cache --virtual .build-deps package...
    apk del .build-deps
    

    参考

    Building Docker images with GitLab CI/CD
    Best practices for writing Dockerfiles


    不定期更新