最近挥霍青春、沉沦于学习科学文化知识,摸了些旁门左道,故而在此想做一些分享,同时也是小弟我第一次编写文章。以下都是一些仅代表个人的一些观点和心得,本人尽量使用比较通俗易懂的话语来阐述,希望能给各位少侠一些启发和帮助。若有解释不当的地方,还请各位大锅指点一二。
CI/CD介绍
对与Gitlab 提供的 CI/CD, 其称之为持续集成服务、通俗点就是自动化(打包、测试、部署 ...)
CI (持续构建) --> 代码提交后触发自动化的单元测试,代码预编译,构建镜像,上传镜像等.
CD (持续发布) --> 将构建好的程序发布到各种环境,如预发布环境,正式环境.
为什么要使用持续集成?
我们都知道,项目开发最基本的流程不过于 "开发" -> "打包" -> "测试" -> "部署",在一个完整项目的生命周期中,避免不了多次以上这样的流程。传统的模式可能会让我们觉得厌烦,原因不外乎于下面几点:
1、开发和打包都在同一台机器上运作,可能导致打包的过程中,影响开发的进度
2、一天天的都是手动打包、测试、部署、日复一日的工作使我们厌倦
3、每个开发者都在自己的机器打包项目,不同的环境配置可能会有各种千奇百怪的问题
除以上问题,当然还有其他不尽人意的缺点,就不一 一描述了,上面的问题足以让人脑壳深疼。在这个时候,我相信强大的持续集成方案一定能解决你对开发流程不满的地方
1、提供后台集成服务,开发、打包,互不影响
2、减少人工编译部署过程中的低级错误
3、解决不同环境下构建项目产生不一致的问题
还有各种好处。。。(具体还是根据大家不同的场景需求定制自己的自动化方案吧)
特别说明
本人使用的系统环境都是 ubuntu、使用docker,项目语言是node.js
不熟悉docker基础的仁兄们需要先看看docker的一些基础知识
个人建议使用两台以上的服务器, 一台开发+测试, 一台部署发布项目 (笔者认为,如果只使用一台服务器,实际上发挥不出 CI/CD 的威力)
对与网上现有的各大分享gitlab ci/cd 的文章,使用 shell 来搭建ci/cd服务的太多了,在此我就不介绍这种方案了。但是基于docker 方案的文章,也看了不少,对与我个人而言并不是太友好,有些说的比较高深,难以理解,有些过于简单,不够详细。所以决定写下这篇文章希望能和大家互相探讨。该文章主要是基于docker 的持续集成方案。
认识Gitlab-CI/CD流程
该小节适于初步认识gitlab持续集成服务的读者阅读、对于已经理解基本流程的可以直接跳过本小节(这一节已经不能教你什么了)
GitLab8.0之后,GitLab CI/CD 就已经集成在GitLab里了。给我几分钟,你会发现,其实流程很简单。
pipeline
每次代码提交就会触发一次pipeline。如上去所示,每一行就是一个集成服务,我们称之为流水线。一次pipeline可以看成一次构建任务。
stage
stage就是上述构建任务中的各个构建阶段。一般会包含:安装依赖,测试,编译,部署服务等多个阶段。
job
job表示构建工作,是每个stage构建阶段里具体执行的工作。
我们可以点击进去看看,你会发现整个流程就像工厂车间里面的任务一样,一个任务完成后接着执行下一个(或多个)任务,不难看出来,以上的任务顺序为 build ->test -> deploy。
(并且如果有其中一个任务失败了,我们可以选择后面的任务是否能继续下去,这对我们很有用,而这些任务都是我们可以预先设定好的,并按照我们的规则来进行)
通过上面这些概念上的东西,你应该对 Gitlab-CI/CD 的工作 流程有了初步的了解,但是这些任务都是交给谁来托管呢? 没错,它就是 GitLab runner。下面就开始我们的主题吧。
启动GitLab runner服务
1、首先要安装docker、已经安装的可跳过此步骤:
(附上docker 安装的官方文档 Get Docker CE for Ubuntu | Docker Documentation)
sudo apt-get update
sudo apt-get install docker-ce
复制代码
2、创建gitlab-runner容器:
sudo docker pull gitlab/gitlab-runner:latest
sudo docker stop gitlab-runner && docker rm gitlab-runner
复制代码
sudo docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
复制代码
3、注册runner(绑定gitlab项目):
首先我们需要打开自己gitlab的项目,打开一下红色指示的地方
注册runner:
sudo docker exec -it gitlab-runner gitlab-ci-multi-runner register -n \
--url 这里填写上图中的url \
--registration-token 这里填写上图中的token \
--executor docker \
--description "gitlab-runner in docker" \
--tag-list "ci-cd" \
--docker-privileged=false \
--docker-pull-policy="if-not-present" \
--docker-image "docker:latest" \
--docker-volumes /var/run/docker.sock:/var/run/docker.sock
复制代码
成功后如下图所示(该步骤有可能会出现网络异常的情况,可以尝试多次重试)
上面的命令将注册一个新的 Runner 来使用 Docker 所提供的特殊docker:latest镜像。这里使用的是官方提供的 Use Docker socket binding 模式,是将/var/run/docker.sock绑定装载到容器中,以便 docker 在该镜像的上下文中可用。(请注意,它正在使用 宿主机 本身的 Docker 守护进程,docker 命令产生的任何容器都将是 一开始我们创建gitlab-runner容器的兄弟,而不是所运行程序的子进程)有兴趣可自行阅读官方文档,看看具体参数的用法。
这里需要说明的是 docker-pull-policy,设置gitlab是否从远程拉去image, 如果iamge是本地的,需要配置该属性的值为 if-not-present,这样可以避免docker 镜像每次都pull
4、提升gitlab-runner用户权限:
由于runner执行过程中,是通过一个叫做 gitlab-runner 的用户来进行操作的,因为不是root用户,所以免不了会有权限问题,这里我们将其添加到docker组中,并验证gitlab-runner是否可以访问Docker
sudo usermod -aG docker gitlab-runner
sudo -u gitlab-runner -H docker info
复制代码
配置.gitlab-ci.yml
好了,通过上一节,我们已经成功注册runner了,是时候定制我们的持续集成服务了,在项目根目录中创建.gitlab-ci.yml文件。(附上官方的文档说明 配置gitlab-ci.yml规则)
先附上完整版的yml,里面均有注释,后面再针对特殊的地方做些解释
# 使用docker镜像
image: docker:latest
# 设置变量
variables:
# 镜像仓库地址
REGISTRY: registry.cn-shenzhen.aliyuncs.com
# 镜像版本
REGISTRY_IMAGE_TAG: registry.cn-shenzhen.aliyuncs.com/jieyufeng/gitlab-ci-cd:master
# 镜像启动后的容器名
CONTAINER_NAME: gitlab-ci-cd
stages:
- build
- test
- deploy
# ----------------构建-----------------
build:
stage: build
script:
# 停止并删除正在使用当前镜像的容器
- if [ "$(docker ps -a | grep $CONTAINER_NAME)" ]; then
- docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME
- fi
# 删除当前已存在的镜像
- if [ "$(docker images | grep $REGISTRY_IMAGE_TAG)" ]; then
- docker rmi $REGISTRY_IMAGE_TAG
- fi
# 登录镜像仓库
- docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $REGISTRY
# 构建新的镜像
- docker build -t $REGISTRY_IMAGE_TAG .
# 上传镜像
- docker push $REGISTRY_IMAGE_TAG
only:
- master
tags:
- ci-cd
# ----------------测试-----------------
test:
stage: test
script:
# 本地启动容器进行测试
- docker run -d --name $CONTAINER_NAME -p 3000:3000 $REGISTRY_IMAGE_TAG
when: on_success
only:
- master
tags:
- ci-cd
# ----------------部署-----------------
deploy:
# 切换ubuntu作为deploy任务的镜像
image: ubuntu:latest
stage: deploy
script:
# 给runner配置私钥
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
# 给runner配置ssh登录不验证HostKey
- '[[ -f /.dockerenv ]] && echo -e "Host *\\n\\tStrictHostKeyChecking no\\n\\n" > ~/.ssh/config'
# 使用ssh远程登录正式服务器,并拉取之前build上传好的镜像进行部署
- ssh root@$DEPLOY_HOST "
docker images;
docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $REGISTRY;
docker pull $REGISTRY_IMAGE_TAG;
docker run -d --name $CONTAINER_NAME -p 3000:3000 $REGISTRY_IMAGE_TAG;"
when: manual
allow_failure: false
only:
- master
tags:
- ci-cd
复制代码
部分说明:
1、这里会涉及一个概念,叫做docker in docker的概念,针对这个我个人也没能很好的解释是个什么东西,以免误导大家,因此提供官方的文档 docker socket binding 给大家参考
2、既然要使用自动化部署,那就免不了要使用ssh免密登录策略,此处就关于免密的就不做详细介绍了,可以参考该文档 ssh免密登录
3、试想一下,有些敏感的参数我们是不希望明文暴露在.gitlab-ci.yml 中的,比如密码、私钥等,那怎么办才好呢?对此,官方提供了很好的方案 GitLab CI/CD Variables,设置后,我们可以直接使用参数来代替铭感信息
如上图所示,我们需要在我们gitlab项目中设置对应的参数:
REGISTRY_USER: 登录你个人的docker镜像仓库用户名
REGISTRY_PASSWORD: 登录你个人的docker镜像仓库密码
DEPLOY_HOST: 你正式服务器的地址
SSH_PRIVATE_KEY: gitlab-runner所在的服务器的ssh私钥
如下图所示,我们在.gitlab-ci.yml只需要使用对应的参数即可,很高大上的有木有
对与不敏感的信息,我们也可以.gitlab-ci.yml设置参数 (其实就跟我们平常写代码,声明一个全局的参数是一个道理的)
4、各流程简单描述
build: 因为我们runner所在的服务器就是测试服务器,所以我们在构建镜像之前,需要先将使用了该镜像的容器给删除,并删除旧的镜像。删除后我们构建新的镜像,并登陆个人镜像仓库进行上传。(由于网络没办法翻墙,这里推荐使用 阿里云容器镜像服务,也可以使用你们自己搭载的个人镜像仓库。 如果你们可以翻墙,也可以使用gitlab配套的容器镜像仓库,如下图,没有翻墙请慎用,可能会导致上传镜像失败的情况)
test: 正如刚才我们说的 runner所在的服务器就是测试服务器,所以test的任务比较简单,就是使用docker启动我们刚才编译的镜像即可
deploy: 因为docker:latest 镜像中没有ssh服务,我们可以换一种思路,切换ubuntu:latest 镜像,大家都知道,使用ssh远程登录肯定免不了登录验证, 这里一定要先打通服务器之间的ssh免密登录,也就是上面的第2点说明,否则登录不成功,接着就是给runner配置私钥了。针对这个,官方也有方案文档 ssh-keys-when-using-the-docker-executor。需要注意的地方是,一般我们不希望每一次构建一次流水线就部署一次,因此我们可以在deploy中设置when: manual,它让我们可以通过手动来控制是否需要执行部署的操作
如图所示,我们可以根据前面的任务状态来确定是否需要进行部署任务,只需点一下红色标注的地方即可
总结
鸡汤:可能对于像我这样的新手一开始接触这个,会有很多很多困惑,也会遇到各种各样的问题。但是勇于尝试,总能成功的,毕竟罗马也不是一天建成的不是吗?
最后,由于这个Gitlab-CI/CD太过强大,还有很多高大上的方案本人也还在学习中,文章中出现的文档链接都很有用,都是官方文档的,每个人可能都会遇到各种不同的问题,本文只是做一个总结分享,并不一定能解决大家所遇到的问题,大家可以多阅读资料,互相探讨一下
附上本人简单的demo: gitlab-ci-cd