前言
背景
传统的前端部署往往都要经历:
- 本地代码更新
- 本地打包项目
- 清空服务器相应目录
- 上传项目包至相应目录
这些都是机械重复的步骤,而且容易出错,容易丢失源码。
对于这一过程我们往往可以通过 CI/CD 方法进行优化:
- CI(Continuous Integration):持续集成。
- CD(Continuous Deployment):持续交付,持续部署。
优点
让我们的前端项目构建部署等能够通过自动化的工具自动进行,我们只需要关注代码实现 前端的 CI/CD 可以帮助团队提高代码质量,缩短交付周期,提升团队效率,减少人为错误等。
服务器
构建自动化部署首先我们需要有一个自己的服务器。2 核 4G,这是最低要求的配置,否则跑不动 gitlab,我这里买了阿里云的服务器作为演示。
安装 gitlab 相关依赖
1. 相关环境、依赖安装
yum -y install policycoreutils openssh-server openssh-clients postfix
2. 设置 postfix 开机启动,且完成启动
systemctl enable postfix && systemctl start postfix
3. 关闭防火墙或者防火墙增加白名单
systemctl stop firewalld.service
4. 下载 gitlab-ce
wget --content-disposition packages.gitlab.com/gitlab/gitl...
最新的 gitlab-ce 文件会比较大,有一个多 G,这里推荐使用清华源的地址下载,速度会比较快。阿里云的下载速度是 1024/8 * 带宽大小,比如 1M 的带宽,下载速度最大就是 128k/s,实际的下载速度肯定还要比这个小。
5. 安装 gitlab-ce
rpm -i gitlab-ce-14.9.2-ce.0.el7.x86_64.rpm
安装的 gitlab-ce 版本一定要和下载的一致。
看到 gitlab 图标出来,就说明我们安装成功了。
6. 配置 GitLab
安装好了之后,我们还需要修改端口号和地址:
vim /etc/gitlab/gitlab.rb
找到 external_url 和 nginx['listen_port'],将它们的值改为服务器的公网 ip + 端口和端口。
external_url '120.79.55.27:8000' nginx['listen_port'] = 8000
注意: 8000 端口一定要在安全组中对外开放。
7. 重置及启动
修改完配置之后需要重置配置:
gitlab-ctl reconfigure
配置成功之后,告诉了我们登录密码存储在/etc/gitlab/initial_root_password 文件中。
启动:
gitlab-ctl start
重启之后,就可以访问 gitlab 地址了,浏览器打开 120.79.55.27:8000
账号为 root,密码我们需要去/etc/gitlab/initial_root_password 查看:
然后就可以登录了。
GitLab Runner 安装
GitLab Runner 是一个用于执行 GitLab CI/CD 作业的开源项目。它是一个独立的二进制文件,可以在各种操作系统上运行,包括 Linux、Windows 和 macOS。
GitLab Runner 的主要功能是接收来自 GitLab 服务器的 CI/CD 作业请求,并根据作业的定义执行相应的任务。它可以在不同的执行环境中运行作业,例如容器、虚拟机或物理机。
1. 添加 gitlab 官方库
bash
# For Debian/Ubuntu/Mint
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
# For RHEL/CentOS/Fedora
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash
2. 安装
sudo yum install gitlab-ci-multi-runner
3. 注册
gitlab-runner register
需要按照步骤输入:
- 输入 gitlab 的服务 URL,这个是 gitlab 的地址 http://120.79.55.27:8000/
- 输入 gitlab-ci 的 Toekn
gitlab-ci 的 Token 在项目主页 -> Sttings -> CI/CD -> Runners Expand:
- 关于集成服务中对于这个 runner 的描述
- 给这个 gitlab-runner 输入一个标记,这个 tag 非常重要,在后续的使用过程中需要使用这个 tag 来指定 gitlab-runner
- 选择执行器,gitlab-runner 实现了很多执行器,可用在不同场景中运行构建,详情可见 GitLab Runner Executors,这里选用 Shell 模式
这样 GitLabRunner 就安装好了,刷新 gitlab 页面看到新增的一个 Runner 下一步就是把项目集成到 gitlab-ci 中,开始持续集成了。
如果刷新页面发现 gitlab-runner 显示未激活可以本地运行:
javascript
sudo gitlab-runner verify
sudo gitlab-runner restart
推送代码,完成自动化部署
在 gitlab 新建一个项目,将本地代码推送上去。
安装一个静态服务器,用来展示 web 页面的
yum install httpd
检查安装服务的版本
httpd -v
安装完后的 apache 会在服务器下新增/var/www/html/目录, 这个目录就是存放网站资源的位置。
最后我们只需要在每次部署的时候把生产的单页面拷贝到这个页面下,就能在浏览器上通过对应的 IP+路径 来访问 Web 页面了。
httpd 服务默认端口是 80,所以需要在阿里云安全组里将 80 端口放开,也可以修改默认端口,在 /etc/httpd/conf/httpd.conf 文件中修改:
编写.gitlab-ci.yml 配置文件
在.gitlab-ci.yml 文件中,可以定义:
- 要运行的脚本。
- 要包含的其他配置文件和模板。
- 依赖项和缓存。
- 要按顺序运行的命令和要并行运行的命令。
- 将应用程序部署到的位置。
- 无论您是想自动运行脚本还是手动触发它们中的任何一个。
脚本(Script)被分组到作业(Job)中,并且作业作为更大的流水线(Pipeline)的一部分运行。可以将多个独立作业分组到按定义顺序运行的阶段。 CI/CD 配置至少需要一项非隐藏的作业。
当将 .gitlab-ci.yml 文件添加到仓库时,gitlab 会检测到它,并且 GitLab Runner 会运行作业中定义的脚本。
以下是一个简单的.gitlab-ci.yml 文件内容:
javascript
stages:
- build
- test
build-code-job:
stage: build
script:
- echo "Check the ruby version, then build some Ruby project files:"
- ruby -v
- rake
test-code-job1:
stage: test
script:
- echo "If the files are built successfully, test some files with one command:"
- rake test1
test-code-job2:
stage: test
script:
- echo "If the files are built successfully, test other files with a different command:"
- rake test2
在这个例子中,build 阶段的 build-code-job 作业首先运行。它输出作业使用的 Ruby 版本,然后运行 rake 来构建项目文件。如果此作业成功完成,则 test 阶段中的两个 test-code-job 作业将并行启动并对文件运行测试。
示例中的完整流水线由三个作业组成,分为两个阶段,build 和 test。每次将更改推送到项目中的任何分支时,流水线都会运行。
GitLab CI/CD 不仅会执行作业,还会显示执行期间发生的情况,就像在终端中看到的一样:
gitlab-ci.yml 配置的特定关键字
在了解了 YML 文件的基本内容后,接下来需要了解的就是 gitlab-ci 独特的配置关键字,这些关键字将在.gitlab-ci.yml 中使用,并用来控制一个 pipeline 具体的运作过程。
gitlab 提供了很多配置关键字,这里介绍最基础和常用的几个,其它的关键字可以查看官网了解.gitlab-ci.yml 关键字参考:
全局关键字
- default
可以为某些关键字设置全局默认值。 未定义一个或多个所列关键字的作业使用在 default: 部分中定义的值。
以下关键字可以具有自定义默认值:
- after_script
- artifacts
- before_script
- cache
- hooks
- image
- interruptible
- retry
- services
- tags
- timeout
javascript
default:
image: ruby:3.0
rspec:
script: bundle exec rspec
rspec 2.7:
image: ruby:2.7
script: bundle exec rspec
示例将 ruby:3.0 镜像设置为流水线中所有作业的默认镜像。 rspec 2.7 作业不使用默认值,因为它使用特定于作业的 image: 部分覆盖了默认值:
注意:
- 创建流水线时,每个默认值都会复制到所有未定义该关键字的作业。
- 如果作业已经配置了其中一个关键字,则作业中的配置优先,不会被默认替换。
- 使用 inherit:default 控制作业中默认关键字的继承。
- stages
使用 stages 来定义包含作业组的阶段。stages 是为流水线全局定义的。在作业中使用 stage 来定义作业属于哪个阶段。
如果 .gitlab-ci.yml 文件中没有定义 stages,那么默认的流水线阶段是:
bash
.pre
build
test
deploy
.post
stages 项的顺序定义了作业的执行顺序:
- 同一阶段的作业并行运行。
- 下一阶段的作业在上一阶段的作业成功完成后运行。
例如:
markdown
stages:
- build
- test
- deploy
build 中的所有作业并行执行。
如果 build 中的所有作业都成功,test 作业将并行执行。
如果 test 中的所有作业都成功,deploy 作业将并行执行。
如果 deploy 中的所有作业都成功,则流水线被标记为 passed。
如果任何作业失败,流水线将被标记为 failed 并且后续阶段的作业不会启动。当前阶段的作业不会停止并继续运行。
如果作业未指定 stage,则作业被分配到 test 阶段。
如果定义了一个阶段,但没有作业使用它,则该阶段在流水线中不可见。
- workflow
使用 workflow: 来确定是否创建流水线。在顶层定义此关键字,使用单个 rules: 关键字,类似于在作业中定义的 rules:。
可以在 workflow: 中使用 name 来定义流水线的名称。
javascript
variables:
PROJECT1_PIPELINE_NAME: 'Default pipeline name'
workflow:
name: '$PROJECT1_PIPELINE_NAME'
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: never
- if: '$CI_PIPELINE_SOURCE == "push"'
when: never
- when: always
作业关键字
- script
使用 script 指定 runner 要执行的命令。
javascript
job1:
script: "bundle exec rspec"
job2:
script:
- uname -a
- bundle exec rspec
- stage
使用 stage 定义作业在哪个 stage 中运行。同一个 stage 中的作业可以并行执行。
如果没有定义 stage,则作业默认使用 test 阶段。
javascript
stages:
- build
- test
- deploy
job1:
stage: build
script:
- echo "This job compiles code."
job2:
stage: test
script:
- echo "This job tests the compiled code. It runs when the build stage completes."
job3:
script:
- echo "This job also runs in the test stage".
job4:
stage: deploy
script:
- echo "This job deploys the code. It runs when the test stage completes."
environment: production
注意:
- 如果作业在不同的 runner 上运行,则它们可以并行运行。
- 如果您只有一个 runner,如果 runner 的 concurrent 设置大于 1,作业可以并行运行。
.pre和.post
使用 .pre 阶段在流水线开始时运行作业。.pre 始终是流水线的第一阶段。用户定义的阶段在 .pre 之后执行。 您不必在 stages 中定义 .pre。
使用 .post 阶段使作业在流水线的末尾运行。.post 始终是流水线的最后阶段。用户定义的阶段在 .post 之前执行。 你不必在 stages 中定义 .post。
如果流水线仅包含 .pre 或 .post 阶段的作业,则它不会运行。 在不同的阶段必须至少有一项其他作业。
javascript
stages:
- build
- test
job1:
stage: build
script:
- echo "This job runs in the build stage."
first-job:
stage: .pre
script:
- echo "This job runs in the .pre stage, before all other stages."
last-job:
stage: .post
script:
- echo "This job runs in the .post stage, after all other stages."
job2:
stage: test
script:
- echo "This job runs in the test stage."
- rules
使用 rules 来包含或排除流水线中的作业。
创建流水线时会评估规则,并按顺序评估,直到第一次匹配。找到匹配项后,该作业将包含在流水线中或从流水线中排除,具体取决于配置。
rules 替换了 only/except,并且它们不能在同一个作业中一起使用。如果您将一个作业配置为使用两个关键字,则系统会返回一个 key may not used with rules 错误。
rules 接受以下规则:
- if
- changes
- exists
- allow_failure
- variables
- when
javascript
job:
script: echo "Hello, Rules!"
rules:
- if: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME != $CI_DEFAULT_BRANCH
when: never
- if: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^feature/
when: manual
allow_failure: true
- if: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME
注意:
如果规则匹配并且没有定义 when,则规则使用为作业定义的 when,如果未定义,则默认为 on_success。
=~ 和 !~ 表达式右侧的变量被认为是正则表达式。
- only / except
only 和 except 没有被积极开发。rules 是控制何时向流水线添加作业的首选关键字。
可以使用 only 和 except 来控制何时向流水线添加作业。
- 使用 only 来定义作业何时运行。
- 使用 except 定义作业 ** 不** 运行的时间。
javascript
job1:
script: echo
only:
- main
- /^issue-.*$/
- merge_requests
job2:
script: echo
except:
- main
- /^stable-branch.*$/
- schedules
- tags
使用 tags 从项目可用的所有 runner 列表中选择一个特定的 runner。
注册 runner 时,可以指定 runner 的标签,例如 ruby、postgres 或 development。要获取并运行作业,必须为 runner 分配作业中列出的每个标签。
javascript
job:
tags:
- ruby
- postgres
在此示例中,只有同时具有 ruby 和 postgres 标签的 runner 才能运行该作业。
- when
使用 when 配置作业运行的条件。如果未在作业中定义,则默认值为 when: on_success。
可以接受以下值:
- on_success (默认):仅当早期阶段没有作业失败或具有 allow_failure: true 时才运行作业。
- on_failure:仅当早期阶段至少有一个作业失败时才运行作业。早期阶段具有 allow_failure: true 的作业始终被认为是成功的。
- never:无论早期阶段的作业状态如何,都不要运行作业。只能在 rules 部分或 workflow: Rules 中使用。
- always:无论早期阶段的作业状态如何,都运行作业,也可以在 workflow:rules 中使用。
- manual:仅在手动触发时运行作业。
- delayed:延迟作业的执行指定的持续时间。
javascript
stages:
- build
- cleanup_build
- test
- deploy
- cleanup
build_job:
stage: build
script:
- make build
cleanup_build_job:
stage: cleanup_build
script:
- cleanup build when failed
when: on_failure
test_job:
stage: test
script:
- make test
deploy_job:
stage: deploy
script:
- make deploy
when: manual
environment: production
cleanup_job:
stage: cleanup
script:
- cleanup after jobs
when: always
在这个例子中,脚本:
- 只有当 build_job 失败时才执行 cleanup_build_job。
- 无论成功或失败,始终将 cleanup_job 作为流水线的最后一步执行。
- 在 GitLab UI 中手动运行时执行 deploy_job。
- variables
使用 variables 为作业定义自定义变量。
variables 可以作为全局和工作关键字。可以在全局级别使用它,也可以在作业级别使用它。
如果将 variables 定义为全局关键字,它的行为类似于所有作业的默认变量。创建流水线时,每个变量都会复制到每个作业配置。 如果作业已经定义了该变量,则作业级别变量优先。
javascript
variables:
DEPLOY_SITE: "https://example.com/"
deploy_job:
stage: deploy
script:
- deploy-script --url $DEPLOY_SITE --path "/"
environment: production
deploy_review_job:
stage: deploy
variables:
REVIEW_PATH: "/review"
script:
- deploy-review-script --url $DEPLOY_SITE --path $REVIEW_PATH
environment: production
注意:
- 名称只能使用数字、字母和下划线 (_)。
- 值必须是字符串。
对于项目中一些重要信息,比如密码、密钥等可以作为变量存储在gitlab中:
预设变量
预设变量,这些都是GitLab CI/CD自动注入到当前环境的。很多是只读的变量,不允许用户修改。这些变量都是使用 以CI开头,大写_的方式组成。如 预设变量 CI_COMMIT_AUTHOR,它是用于表明提交的作者。
更多预设变量信息可以查看官网文档预设变量。
完成一个简单的.gitlab-ci.yml 配置文件
根据上面介绍的.gitlab-ci.yml 的语法和关键字,我们可以写一个简单的.gitlab-ci.yml 文件,在项目根目录创建:
ruby
stages: # 分段
- deploy
deploy:
stage: deploy
tags:
- deploy
script:
- sshpass -p $PASSWORD scp ./index.html $CUSTOM_USERNAME@$CUSTOM_IP:/var/www/html
- $PASSWORD 是服务器的登录密码
- $CUSTOM_USERNAME 是用户名
- $CUSTOM_IP 是服务器公网 ip
安装 sshpass:
yum install sshpass
代码推上去自动部署如果报错: Host key verification failed. lost connection, 需要将/etc/ssh/ssh_config 文件里的 StrictHostKeyChecking 设置为 no。
最新的代码推送上去后,可以看到运行如下:
需要重启一下 http 服务:
service httpd start
最后输入部署的 IP 看看我们部署的网页:
已经把项目代码部署上去了,这样一个简单的 gitlab cicd 就搭建成功了。