点击在我的博客 xuxusheng.com 中查看,有更好的排版哦~

一、背景

当项目比较少,或者更新不频繁的时候(或者技术比较菜的时候),手动部署部署还能够接受,但是一旦部署次数频繁了,手动操作就是一件痛苦万分的事情了。

公司内部使用的是 jenkins ,从成熟稳定的角度来看,是非常符合要求的,但是针对个人项目,第一没有公司的项目那么复杂,第二在流程上也不需要考虑多人协作、测试等需求,这个时候 jenkins 就显得过于庞大了一点(奇丑无比)。

调研了一圈目前可以使用的主流(免费)CI 工具,最终决定采用 GitLab CI 来作为自己的博客和个人项目的 CI 平台。

二、简介

GitLab CIUI 非常清新,文档也非常全面,同时使用起来也非常简单,简直是程序员居家必备的好帮手。

GitLab CI 本身只是作为仓库里附加的一个功能,并不提供项目构建的环境,或者可以将 GitLab CI 理解成一个调度员,所做的事情就是决定何时触发构建任务,以及在哪个环境下执行构建任务等等。

三、初识 GitLab Runner

GitLab Runner 是一个开源项目,用于运行您的任务并将结果发送回 GitLab。它与 GitLab CI 一起使用,GitLab CI 是 GitLab 随附的开源持续集成服务,用于协调任务。

英文好的同学建议(英文不好的同学也建议)直接阅读官方文档:GitLab Runner

就我个人理解来看,GitLab Runner 其实就是将某台服务器,注册成为 GitLab CI 的任务执行单元,用于在 CI 过程中,执行相应的任务,类似于一个分布式系统的 masterslave 的角色。

Gitlab CIRunner 并没有什么严格的要求,可以是一台 linux 实体机,也可以是 Virtual Box 里的虚拟机,甚至可以是某个 Docker 容器。

针对不同的 Runner,可以在注册时划分不同的固有角色(可执行的任务不同,详情参见文档:Executors),以及打上不同的自定义标签,以便于 GitLab CI 在调度时灵活分配任务,多个 Runner 并行执行任务等等。

GitLab 针对免费用户,提供了每个月 2000 分钟的免费构建时间,在不超过这个时间时,可以直接使用 GitLab 提供的共享 Runner

但是使用别人的服务器来进行构建任务,总是会暴露一些敏感信息出去,所以自建 GitLab Runner,就是最佳选择了。

四、自建 GitLab Runner

前面提到了 GitLab Runner 其实就是一台执行任务的机器而已,GitLab 也提供了现成的脚本,将当前机器注册为 Runner

但是当我们期望在同一个系统下,部署不同种类的 Runner 来执行不同的任务,比如一个 Runner 专门执行构建任务,另一个 Runner 专门执行部署任务。

或者说我们期望注册一个 Runner,但是又不希望影响系统现有的环境时,直接执行脚本将宿主机注册为 Runner 的方式就不太合适了。

Virtual Box 创建多个虚拟机当然可以解决这个问题,但是创建多个虚拟机对资源的消耗问题也是显而易见的。

这个时候 Docker 就又出来拯救世界了。

早在调研 jenkins 的时候,就期望能够使用 docker 来部署 jenkins,然后在这个 jenkins 容器中,执行包含了 docker 命令的构建脚本,这就涉及到了在 docker 容器中执行 docker 命令的问题,解决起来真的是一把辛酸泪。

得益于 docker in docker (参见:https://hub.docker.com/_/docker?tab=description&page=1),这个坑总算是能够填了。

Gitlab CIDocker 的支持非常好,文档之类的东西非常全面,建议直接阅读官方文档即可:

实在不想看官方文档,也可以继续往下阅读。

1. 创建 Runner 容器

创建一个 gitlab-runner-docker 目录,然后新建一个 docker-compose.yml 文件,内容如下:

version: "3"
services:
  app:
    image: gitlab/gitlab-runner
    container_name: gitlab-runner-docker
    restart: always
    volumes:
      - ./config:/etc/gitlab-runner
      - /var/run/docker.sock:/var/run/docker.sock

gitlab-runner-docker 目录下执行 docker-compose up --build -d 命令,docker ps -a 即可看见刚才创建的容器,同时目录下会生成一个 config 目录,用于存放 Runner 的配置文件。

2.注册 Runner

执行命令 docker exec -it gitlab-runner-docker gitlab-runner register (或者进入容器内部执行 gitlab-runner register 也可以)

接下来会看到一系列的输入项,一步一步输入即可。

a) 输入 GitLab 的地址。

如果是使用的官方的 GitLab,就输入 https://gitlab.com,自建的 GitLab 就输入自己的IP或域名即可。

 Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
 https://gitlab.com
b) 输入 Token 来注册 Runner

GitLab 的仓库 Setting -> CI/CD 设置页面中,展开 Runners 部分,即可看到生成的 Token,复制粘贴即可。
在这里插入图片描述

 Please enter the gitlab-ci token for this runner
 xxxToken
c) 输入一段 Runner 的描述,之后可以在 GitLab 管理页面进行修改。
 Please enter the gitlab-ci description for this runner
 [hostame] my-runner
d) 输入 Runner 关联的标签,之后可以在 GitLab 管理页面进行修改。
 Please enter the gitlab-ci tags for this runner (comma separated):
 my-tag,another-tag
e) Enter the Runner executor:

GitLab Runner 内置了多种 executor,不同类型的 executor 的区别,可以参考文档:Executors

这里我们填写 docker

Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
 docker
f) If you chose Docker as your executor, you’ll be asked for the default image to be used for projects that do not define one in .gitlab-ci.yml:

如果选择了 executordocker,那么就需要选择一个默认的镜像。

最开始接触的时候,这里可能会有点难以理解,需要说明的是,我们刚才使用 docker 创建的这个 Runner 容器,并不是构建脚本直接执行的环境。

打个比方,在构建任务中,我们需要执行一段 js 脚本,那么我们就需要 nodejs 环境,也就是说需要创建一个 nodejs 容器,那么这个容器,会由刚才创建的 Runner 容器来进行创建。

也就是说我们实际上是在一个 Docker 容器中,再创建另一个容器,而再次创建出来的这个容器,才是构建任务实际运行的环境。

针对 GitLab CI 的每一阶段的构建任务,我们都可以指定不同的 Docker 镜像,当执行到某个任务时,Runner 就会根据这个任务指定的镜像创建出相应的容器,再在这个容器内执行任务。

而这一步需要指定的默认镜像,就是当 .gitlab-ci.yml 文件中没有指定任务需要的镜像时使用的。

Please enter the Docker image (eg. ruby:2.1):
alpine:latest

官方文档的例子中,默认镜像使用的是 alpine:latest,是个精简版的 linux 镜像,也就意味着啥工具都没有,每次执行构建任务前先装 git 等一系列工具显然不合理。

推荐使用 tico/docker 作为默认镜像,参见:https://hub.docker.com/r/tico/docker,这个镜像在官方 docker 镜像的基础上,加入了 curlphpgit 等等一系列常用的工具。

是的,没错,这个镜像是基于官方 docker 镜像的,这就意味着,我们仍然可以在这个镜像生成的容器中执行 docker 命令,这样在构建脚本中,就可以完美调用 docker 命令了。

3. 配置 Runner

进入 config 目录,会发现一个 config.toml 文件,里面是 gitlab-runner 相关的配置信息。

concurrent = 1
check_interval = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "home-runner-docker"
  url = "https://gitlab.com"
  token = "xxxxxxxxxxxxxxx"
  executor = "docker"
  [runners.docker]
    tls_verify = false
    image = "tico/docker"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache"]
    shm_size = 0
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]

当执行构建任务时如果出现以下报错,请将上面的配置文件中的 privileged 的值改为 true

参考:Use docker-in-docker with privileged mode
在这里插入图片描述

五、使用心得

以我的博客为例,首先是一个专门用来执行构建任务 Runner 容器。

当接收到博客的构建任务时,创建一个基于 tico/docker 镜像的容器,然后再在这个容器中执行构建脚本。

而构建脚本中,又调用 docker 命令创建了一个 nodejs 容器来进行打包编译,然后再将 build 之后生成的静态文件移入一个 nginx 镜像,作为最终部署使用的镜像并上传到阿里云容器服务。

接着 GitLab CI 会将部署任务发送至另一个专门用来执行部署任务的 Runner 容器,这个 Runner 容器会 ssh 登录上目标服务器,拉取最新的镜像并运行。

然后博客自动部署完毕啦,欢迎访问:
https://www.xuxusheng.com

贴出我的博客的 .gitlab-ci.yml 文件感受一下:

stages:
  - build
  - test
  - release

# 构建
build:
  stage: build
  tags:
    - xs1
    - docker
  image: tico/docker:latest
  services:
    - docker:dind
  variables:
    DOCKER_HOST: tcp://docker:2375/
    DOCKER_DRIVER: overlay2
    DOCKER_REPO: $docker_repo
  before_script:
    - echo 'before_script'
    - docker login --username=$docker_user -p $docker_pwd $docker_host
  script:
    - sh ./ci/build/build.sh
  only:
    - master

# 发布
release:
  stage: release
  tags:
    - xs1
    - ssh
  variables:
    DOCKER_REPO: $docker_repo
    PROJECT_NAME: $project_name
    SERVER: $deploy_server
    PORT: $port
  script:
    - sh ./ci/release/release.sh
  environment:
    name: production
  only:
    - master

构建过程欣赏:
在这里插入图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐