CI/CD
需求:项目通过分支、tags等触发不同环境的一键部署
触发运行-前提
-
首先git代码上传origin要带上编写好的gitlab-ci.yml
-
在gitlab的project->Settings->CI/CD->Runners->点击Expand->Shared runners->关闭Enable shared runners for this project, 否则无法会导致gitlab CI无法正常执行,提示The pipeline failed due to the user not being verified
-
Setting-Genneral-Visibility, project features, permissions,打开CI/CD按钮
-
Setting-CI/CD-Runners,点击enble for this project启动Available specific runners按钮
先上案例:.gitlab-ci.yml
default:
image: node:16.14.0
variables:
GIT_CLEAN_FLAGS: 'none'
USER: master
SSH_PORT: 58666
BASE_JAR_DIR: /data1/artifacts
BASE_ART_DIR: /data1/artifacts
BASE_LOG_DIR: /data1/logs
TEST_HOST_FRONTEND_01: 10.xx.xxx.12
PROD_HOST_FRONTEND_01: 10.xx.xxx.11
CI_DEBUG_TRACE: 'false'
LOG_DIR: '$BASE_LOG_DIR/$CI_PROJECT_NAME'
APP_DIR: '$BASE_ART_DIR/$CI_PROJECT_NAME'
VER_DIR: '$BASE_ART_DIR/$CI_PROJECT_NAME/$CI_COMMIT_SHORT_SHA'
WECOM_ROBOT_KEY: 'xxxx' #企微机器人key
stages:
- clean
- install
- build
- deploy
- notify
clean-job:
stage: clean
tags:
- titan
script:
- rm -rf node_modules
only:
variables:
- $FORCE_INSTALL == "true"
install-job:
stage: install
tags:
- titan
script:
- echo 'ooOoo install'
# - rm -rf .yarn/cache
# - ln -s /root/.yarn/berry/cache .yarn/cache
- yarn || yarn install
# refs && changes,并集
only:
refs:
- tags
- master
- test
- /^release\/.*/
- /^patch\/.*/
- /^hotfix\/.*/
# refs&changes取并集
changes:
- package.json
- yarn.lock
- .yarnrc.yml
- .gitlab-ci.yml
build-release-job:
stage: build
tags:
- titan
script:
- yarn build
only:
- test
- /^release\/.*/
- /^patch\/.*/
- /^hotfix\/.*/
build-prod-job:
stage: build
tags:
- titan
script:
- yarn build
only:
- tags
# .隐藏keys
# 跨服务器部署
.deploy-job: &deploy-job
script:
- ssh -p ${SSH_PORT} ${USER}@${DEPLOY_SERVER} "mkdir -p $VER_DIR"
- scp -P $SSH_PORT -r ./dist/www/* $USER@$DEPLOY_SERVER:$VER_DIR
- ssh -p ${SSH_PORT} ${USER}@${DEPLOY_SERVER} "rm $APP_DIR/current; ln -s $VER_DIR $APP_DIR/current"
deploy-uat-job:
stage: deploy
variables:
## 定义变量, 该job后面的步骤能使用
DEPLOY_SERVER: $TEST_HOST_FRONTEND_01
tags:
- titan
only:
- test
- /^release\/.*/
- /^patch\/.*/
- /^hotfix\/.*/
<<: *deploy-job
deploy-prod-job:
stage: deploy
variables:
DEPLOY_SERVER: $PROD_HOST_FRONTEND_01
tags:
- titan
only:
- master
script:
# 代码所在服务器部署
- mkdir -p $VER_DIR
- cp -r ./dist/www/* $VER_DIR
- rm -rf $APP_DIR/current
- ln -s $VER_DIR $APP_DIR/current
# 企微通知
notify-job-when-success:
stage: notify
script:
- |
curl "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${WECOM_ROBOT_KEY}" -H 'Content-Type:application/json' -d "
{
\"msgtype\": \"markdown\",
\"markdown\": {
\"content\": \"项目名称:${CI_PROJECT_NAME}\n>项目构建结果:<font color=\\\"info\\\">成功</font>\n>本次构建由:${GITLAB_USER_NAME} 触发\n>提交号:${CI_COMMIT_SHA}\n>提交日志:${CI_COMMIT_MESSAGE}\n>构建分支:${CI_COMMIT_REF_NAME}\n>流水线地址:[${CI_PIPELINE_URL}](${CI_PIPELINE_URL})\"
}
}"
tags:
- titan
only:
- tags
- master
- test
- /^release\/.*/
- /^patch\/.*/
- /^hotfix\/.*/
when: on_success
notify-job-when-fail:
stage: notify
script:
- echo 'fail'
- export USER_MOBILE=$(curl "http://account-prod.internal.biateam.com/services/internal/devops/get-mobile-by-email?email=${GITLAB_USER_EMAIL}")
- |
curl "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${WECOM_ROBOT_KEY}" -H 'Content-Type:application/json' -d "
{
\"msgtype\": \"text\",
\"text\": {
\"mentioned_mobile_list\":[\"${USER_MOBILE}\"]
}
}"
- |
curl "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${WECOM_ROBOT_KEY}" -H 'Content-Type:application/json' -d "
{
\"msgtype\": \"markdown\",
\"markdown\": {
\"content\": \"项目名称:${CI_PROJECT_NAME}\n>项目构建结果:<font color=\\\"warning\\\">失败</font>\n>本次构建由:${GITLAB_USER_NAME} 触发\n>提交号:${CI_COMMIT_SHA}\n>提交日志:${CI_COMMIT_MESSAGE}\n>构建分支:${CI_COMMIT_REF_NAME}\n>流水线地址:[${CI_PIPELINE_URL}](${CI_PIPELINE_URL})\"
}
}"
tags:
- titan
only:
- master
- test
- /^release\/.*/
- /^patch\/.*/
- /^hotfix\/.*/
when: on_failure
gitlab-ci.yml属性详解
- stages
定义pipeline的全部阶段(stage),阶段内的全部任务并行执行,全部任务成功开始下一阶段任务,任何阶段内的任意job失败都会导致pipeline失败,所有stage,job执行成功后pipeline会显示pass。如果未定义stages,则默认有build、test、deploy三个阶段,如果job中未定义stage属性,则默认为test
注意执行的顺序与编写先后顺序无关
- only & except
-
only: 定义在哪个分支或者tag上的修改触发执行;
-
except:定义哪个分支或者tag修改不触发任务的执行。
可以使用正则表达式指定,也可以指定关键字。only和except可同时使用。如果only和except在一个job配置中同时存在,则以only为准,跳过except(从上面示例中得出)。
此四个关键字可以和only、except一起使用:refs、variables、changes、kubernetes
使用only:refs和except:refs关键字来控制何时根据分支名称或管道类型向管道添加作业。
only的refs和changes同时使用时,是并集才触发
如下关键字:
关键字 描述
branches ## git分支
tags ## tag标签
api ## 当管道被第二个管道API触发时(不是触发器API)。
external ## 当使用除GitLab之外的CI服务时。
pipelines ## 对于多项目触发器,使用CI_JOB_TOKEN的API创建。
pushes ## 管道是由用户推送git触发的。
schedules ## 计划的pipelines
triggers ## 用于使用触发器令牌创建的管道
web ## 在GitLab UI中运行管道按钮
merge_requests ## 合并请求
-
tags
tags可以从允许运行此项目的所有Runners中选择特定的Runners来执行jobs。这里的tags是在注册Runner过程中,设置Runner的标签,只有Runner中的有这个标签,这个job才能运行 -
when: 用于执行失败时或者失败后的任务
可以设置为:
on_success:只有当前面的stages的所有工作成功时才执行。
on_failure:当前面stages中任意一个job失败后执行。
always:无论前面的job状态如何都执行。
manual:手动执行,Gitlab8.10开始引入,手动操作指令是不自动执行的特殊类型的job;它们必须要人为启动。手动操作指令可以从pipeline,build,environment和deployment视图中启动。
-
variables
job中可以使用关键字variables来定义job变量。当设置了job级别的关键字variables,他会覆盖全局YAML和顶部预定义的job变量,想要关闭全局变量可以在job中设置一个空数组。在job里面定义的variables可被后面的job使用 -
隐藏keys
Key 是以.开始的,GitLab CI 将不会处理它。你可以使用这个功能来忽略jobs。如:.deploy-job: &deploy-job,deploy-job将会被隐藏,不会执行 -
Anchors
yaml有个方便的功能称为“锚”,他可以轻松的在文档中复制内容,Anchors可用于复制或者继承属性,并且是使用隐藏keys来提供模板的完美示例。
&在anchor的名称(job_definition)前设置,<<表示将anchor的内容复制到此处,*包括命名的anchor(job_definition)。但是如果job内有相同的属性,将会覆盖掉anchor的内容
.job_template: &job_definition
script:
- echo "job template"
test1:
<<: *job_definition
test2:
<<: *job_definition
script:
- echo "test2"
- environment
解决部署环境管理的问题需要使用GitLab CI/CD关键词environment。使用它,开发者可以将一个作业设置为某一环境的部署作业,同一个环境的部署作业会被收集到一起,运行部署作业,或者停止作业都将触发一个钩子。开发者可以自定义执行相关业务逻辑。下图是一个部署环境的管理页面
- on_stop: 是用于定义一个在移除环境时触发的作业,它的值必须是一个同流水线,同环境的作业名称。表明在通过UI移除部署环境或者自动移除部署环境时 运行配置的作业。
- auto_stop_in: 配置项用于到期自动移除部署环境,如一天后,一周后
- action: 配置项是用于定义当期作业是部署环境的动作,有三个值,start 默认值),prepare,stop。
- start: 表明当期作业是创建一个部署环境
- prepare: 准备部署环境
- stop: 停止部署环境 on_stop选择的作业必须配置 action: stop
# 部署test环境,停止环境时运行clean_test_env作业
deploy_test_env:
script: echo 'deploy test env'
environment:
name: test
url: https://fizzz.blog.csdn.net/
on_stop: clean_test_env # clean_test_env需配置action:stop
# 部署dev环境,一周后自动停止
deploy_dev_env:
script: echo 'deploy test env'
environment:
name: test
url: https://fizzz.blog.csdn.net/
auto_stop_in: 1 week
# 停止test环境,停止环境的脚本需自行编写
clean_test_env:
script: echo 'stop deploy and clean test env'
when: manual
environment:
name: test
action: stop
- when: 用于执行失败时或者失败后的任务,值如下
- on_success: 只有当前面的stages的所有工作成功时才执行。
- on_failure: 当前面stages中任意一个job失败后执行。
- always: 无论前面的job状态如何都执行。
- manual: 手动执行,需要在gitlab的cicd-pipeline页面手动点击运行job。
- artifacts: 被用于在job作业成功后将制定列表里的文件或文件夹附加到job上,传递给下一个job.
job1
stage: build
image: node:16
script:
- mkdir -p ./artifacts
- echo "编译环境 ${ENV}"
- yarn build:${ENV}
- mv dist ./artifacts
# after_script:
# - mv dist ./artifacts
artifacts:
name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}"
paths:
- ./artifacts/*
expire_in: 2 day
job2
stage: deliver
image: docker:stable
before_script:
- mv ./artifacts/* ./ # 使用job1的artifacts目录
- ls -alh ./dist/*
script:
- export IMAGE=xxxx
- docker login xxx
- docker build -t IMAGE .
- docker push IMAGE
docker+k8s版本gitlab-ci.yml
定义参数:首先在gitlab页面上的-/settings/ci_cd->Variables定义HARBOR_PWD,KUBE_CONFIG两个全局常量,在gitlab-ci.yml文件中调用
variables:
REPOSITORY: hub.xxx.xxxx.cn
MODULE: module-web
MODULE_NAMESPACE: web-namespace
NAMESPACE_DEV: $MODULE_NAMESPACE
NAMESPACE_TEST: $MODULE_NAMESPACE
NAMESPACE_PROD: $MODULE_NAMESPACE
REPOSITORY_CONTEXT: 11336
REPOSITORY_PATH: $REPOSITORY/$MODULE_NAMESPACE/$MODULE
WECOM_ROBOT_KEY: "xxx" #企微机器人
stages:
- test
- install
- build
- deliver
- deploy
- notify
#before_script:
# - echo 'Coty DBEI'
# Cache downloaded dependencies and plugins between builds.
# To keep cache across branches add 'key: "$CI_JOB_NAME"'
#cache:
# key: module-web
# paths:
# - $(pwd)/.yarn-cache
# - node_modules/
######################
#### 基础环境定义 #####
######################
## 开发联调环境QA环境
.if_dev_test: &if_dev_test
# only:
# - develop
# - thin-jar-test
# - /^release.*$/
# - /^test.*$/
environment:
name: test
# variables定义test环境的变量
variables:
ENV: test
NAMESPACE: $NAMESPACE_TEST
DEPLOMENT: $MODULE-test
IMAGE_VERSION: $CI_PIPELINE_ID
.if_dev_test_batch: &if_dev_test_batch
only:
- develop
- thin-jar-test
- /^release.*$/
- /^test.*$/
## 生产环境
.if_prod: &if_prod
# only:
# refs:
# - tags
# variables:
# - $CI_COMMIT_TAG =~ /^v\d+.\d+.\d+-?.*$/
environment:
name: prod
variables:
ENV: prod
NAMESPACE: $NAMESPACE_PROD
DEPLOMENT: $MODULE
IMAGE_VERSION: $CI_COMMIT_REF_NAME
.if_prod_batch: &if_prod_batch
only:
refs:
- tags
variables:
- $CI_COMMIT_TAG =~ /^v\d+.\d+.\d+-?.*$/
######################
#### install:安装依赖 ######
######################
.install: &install
stage: install
image: node:16
script:
- yarn config set registry https://registry.npm.taobao.org
- yarn install --registry=https://registry.npm.taobao.org
# 使用缓存依赖包&上传缓存最新依赖包。pull-push作业在作业开始时下载缓存,并在作业结束时将更改上传到缓存
cache:
key: "xxx:node_modules"
paths:
- node_modules/
policy: pull-push
yarn:install for dev&test:
# 加载变量
<<: *install
<<: *if_dev_test
except:
- master
only:
refs:
- develop
- /^release.*$/
- /^test.*$/
changes:
- package.json
- yarn.lock
- .yarnrc.yml
- .gitlab-ci.yml
yarn:install for production:
<<: *install
<<: *if_prod
only:
refs:
- tags
variables:
- $CI_COMMIT_TAG =~ /^v\d+.\d+.\d+-?.*$/
changes:
- package.json
- yarn.lock
- .yarnrc.yml
- .gitlab-ci.yml
######################
#### build:打包 ######
######################
.build: &build
stage: build
image: node:16
# before_script:
# - yarn config set registry https://registry.npm.taobao.org
# - yarn install --registry=https://registry.npm.taobao.org
# - mkdir -p ./artifacts
script:
- mkdir -p ./artifacts
- echo "编译环境 ${ENV}"
- yarn build:${ENV}
- mv dist ./artifacts
# after_script:
# - mv dist ./artifacts
artifacts:
name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}"
paths:
- ./artifacts/*
expire_in: 2 day
# 使用缓存依赖
cache:
key: "xxx:node_modules"
paths:
- node_modules/
policy: pull
yarn:build for dev&test:
<<: *build
<<: *if_dev_test
<<: *if_dev_test_batch
except:
- master
# script:
# - 'yarn build:test'
yarn:build for production:
<<: *build
<<: *if_prod
<<: *if_prod_batch
# script:
# - 'yarn build:prod'
#############################
#### deliver:推送镜像阶段 ####
#############################
.deliver: &deliver
stage: deliver
image: docker:stable
before_script:
- mv ./artifacts/* ./
- ls -alh ./dist/*
script:
# 打包推送镜像
- export IMAGE=$REPOSITORY_PATH:$IMAGE_VERSION
- docker login $REPOSITORY -u 'robot$web-namespace' -p $HARBOR_PWD
- docker build -t ${IMAGE} .
- docker push ${IMAGE}
docker:push for dev&test:
<<: *deliver
<<: *if_dev_test
<<: *if_dev_test_batch
docker:push for production:
<<: *deliver
<<: *if_prod
<<: *if_prod_batch
##########################
#### deploy:部署到k8s ####
##########################
.deploy: &deploy
stage: deploy
image: hub.xxx.xxxx.cn/library/cli:v0.0.3
dependencies: []
before_script:
- /opt/bin/entry.sh
script:
- export IMAGE=$REPOSITORY_PATH:$IMAGE_VERSION;
# 设置上下文
- kubectl config use-context $REPOSITORY_CONTEXT
# 启动:更改镜像版本 Deploy可以在部署新版本数据时,成功启动一个pod,才会下线一个老版本的Pod
- kubectl -n $NAMESPACE set image deployment.apps/$DEPLOMENT $DEPLOMENT=$IMAGE
kube:deploy to dev&test:
<<: *deploy
<<: *if_dev_test
<<: *if_dev_test_batch
kube:deploy to production:
<<: *deploy
<<: *if_prod
<<: *if_prod_batch
when: manual
##############################
#### notify:企微通知发布信息 ####
##############################
notify-job-when-success:
stage: notify
image: hub.xxx.xxxx.cn/library/cli:v0.0.3
script:
- |
curl "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${WECOM_ROBOT_KEY}" -H 'Content-Type:application/json' -d "
{
\"msgtype\": \"markdown\",
\"markdown\": {
\"content\": \"项目构建结果:<font color=\\\"info\\\">成功</font>\n>本次构建由:${GITLAB_USER_NAME} 触发\n>项目名称:${CI_PROJECT_NAME}\n>提交号:${CI_COMMIT_SHA}\n>提交日志:${CI_COMMIT_MESSAGE}\n>构建分支:${CI_COMMIT_REF_NAME}\n>流水线地址:[${CI_PIPELINE_URL}](${CI_PIPELINE_URL})\"
}
}"
when: on_success
only:
- develop
- test
- prod
- thin-jar-test
- /^release.*$/
- /^test.*$/
- tags
notify-job-when-fail:
stage: notify
image: hub.xxx.xxxx.cn/library/cli:v0.0.3
script:
- export USER_MOBILE=$(curl "http://account-prod.internal.biateam.com/services/internal/devops/get-mobile-by-email?email=${GITLAB_USER_EMAIL}")
- |
curl "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${WECOM_ROBOT_KEY}" -H 'Content-Type:application/json' -d "
{
\"msgtype\": \"text\",
\"text\": {
\"mentioned_mobile_list\":[\"${USER_MOBILE}\"]
}
}"
when: on_failure
only:
- develop
- test
- prod
- thin-jar-test
- /^release.*$/
- /^test.*$/
- tags
docker+k8s粗略了解
初始化创建dockerfile文件、nginx.conf文件、deployment-dev.yaml和service-dev.yaml
-
docker: 容器,根据本地目录上Dockerfile文件和nginx.conf文件将dist包成一个容器,推送用于部署,其启动速度快,资源隔离,耗能低,减小系统开销。
-
Kubernetes: 用于对容器的部署、管理、修复、负载均衡和配置等用途,使得用户可以更加轻松地构建、部署和运行程序。其核心概念有:
-
创建Namespace: 命名空间,是一个虚拟集群,用于隔离和管理不同的资源。不同命名空间中的资源是相互独立的,同一命名空间中的资源可以直接访问。
-
部署pod: pod是 k8s中最小的部署单元,由一个或多个容器组成, Pod 提供容器运行时的环境,同时也提供了一些共享的资源,可使用kubectl命令增删改pod。可通过pod ip访问容器nginx
# 查看pod ip、节点信息 kubectl get pods -n test -o wide curl 10.xxx.xxx.79 curl 10.xxx.xxx.80
-
部署Deployment: 管理pod,创建、删除、更新pod,以确保 Pod 的副本数始终处于指定状态,用Deployment启动的容器,如果直接删除Pod则会在被删除后自动再次创建pod,只有这个Deployment被删除才能永久删除pod。
-
部署Service(测试环境): 用于提供对这组 Pod 的访问和负载均衡。Service 可以通过集群内部 IP、DNS 名称或外部 IP(NodePort 或 LoadBalancer)提供服务。如果不指定 service type,则创建的Service默认为ClusterIP,这种方式只能在Pod内部实现访问。指定type为NodePort类型,可以实现Pod外暴露访问。可通过Service IP访问Nginx,且在外部可通过ServiceNodePort访问Pod
kubectl get svc -n test -o wide curl 10.xxx.xxx.62:8888
-
部署Ingress(正式环境): 用于将外部流量路由到集群内部的服务。Ingress 可以基于 URL、域名或 HTTP 头将流量路由到不同的服务中。在外部使用Ingress Host访问Pod。
设置定时任务
如果想要定时任务执行Pipeline, 可以在gitlab的project->CI/CD->Schedules->New schedule中设置,其中时间间隔的设置采用的是corn语法
gitlab-runner常用命令
$ gitlab-runner list //查看各个runner状态
$ gitlab-runner stop //停止服务
$ gitlab-runner start //启动服务
$ gitlab-runner restart //重启服务
$ gitlab-runner status //查看服务状态
$ gitlab-runner verify //检查注册的runner是否可以连接,但不验证gitlab是否正在使用runner
$ gitlab-runner verify --delete --name project1_runner_for_sonar //删除runner, 这里的project1_runner_for_sonar是注册时填写的gitlab-ci description for this runnergitlab-runner unregister --url https://gitlab.com --token t0k3n //通过url和token注销一个runner
$ gitlab-runner unregister --url https://gitlab.com --token t0k3n //通过url和token注销一个runner
$ gitlab-runner unregister --name test-runner //通过runner名称注销(同名删除第一个)
$ gitlab-runner unregister --all-runners //注销所有runner
坑
- 同一个stage有多个job并行时,会出异步导致找不到文件问题
$ cp -r ./dist/redaviator/* $VER_DIR
cp: cannot stat './dist/redaviator/*': No such file or directory
...
# 解决:
1. 第一种,添加多个stage,作为同步使用
2. 第二种,在同一个job里写2个job的代码