DevOps学习 -- 使用 GitLab、GiLab-Runner

DevOps学习 -- 使用 GitLab、GiLab-Runner

下图为我们的自动化部署流程

用户推送代码到 GitLab,GitLab 将配置好的代码更新事件(流水线)发送到 GitLab-Runner或其他CI/DI软件,CI/DI软件完成自动部署。

一、安装配置部署 GitLab

安装 GitLab

  • 1.获取 GitLab,官方地址:https://packages.gitlab.com/gitlab/gitlab-ce,因为我使用的是 Centos8,所以我下载了 wget --content-disposition https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/8/gitlab-ce-15.3.2-ce.0.el8.x86_64.rpm/download.rpm

  • 2.安装 GitLab,使用命令 sudo yum install gitlab-ce-15.3.2-ce.0.el8.x86_64.rpm,如果报缺少包,则缺哪个安哪个。

  • 3.执行gitlab-ctl reconfigure,重新配置 GitLab。

  • 4.查看登陆密码(24小时后失效),使用命令cat /etc/gitlab/initial_root_password

  • 5.在网页上打开GitLab,端口号默认为80,用户名默认为:root

  • 6.修改密码:右上角->Prefrences->Password

  • 7.修改语言:右上角->Prefrences->Prefrences->Localization->Language

新建一个项目,并拉取

  • 1.首页点击新建项目
  • 2.填写相关信息,点击创建
  • 3.克隆项目(需要替换域名为当前gitlab所在主机的IP)
  • 4.使用 git clone http://gitlab.example.com/gitlab-instance-7a3f9f21/xlogin.git 拉取项目

二、安装配置 GitLab-Runner

前面我们讲过,GitLab-Runner 用于接收 GitLab 的更新事件,然后运行我们事先配置好的事件流程(流水线),如 build->test->deploy等。由此可以看出 GitLab-Runner 只是一个执行器,它接收来自 GitLab 特定格式的元数据,然后按照配置好的执行策略(配置文件)来运行;这个配置文件默认为项目根目录下的.gitlab-ci.yml文件。

  • 1.安装 GitLab-Runner,yum install gitlab-runner
  • 2.配置 GitLab-Runner:sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner,指定了gitlab-runner的用户名和工作目录
  • 3.启动 GitLab-Runner:gitlab-runner start
  • 4.将 GitLab-Runner 注册到 GitLab 上:gitlab-runner register。如下图:

Enter an executor:表示通过什么方式运行流水线中配置的 script,如果在本机运行编译部署等任务,则这里填写 shell;其他选项这里暂不做讨论。
注册令牌获取方式:菜单 -> 管理员 -> 管理中心 -> 概览 -> Runner -> 注册一个实例Runner

  • 5.修改配置文件,配置文件地址:/etc/gitlab-runner/config.toml

添加 clone_url 项(不配置通过git拉取的时候会报错)

  • 6.重新启动 gitlab-runner restart
  • 7.补充:如果需要在这台机器上使用 maven、gradle 等进行构建,那么需要安装这些软件。如果在当前机器上进行构建 git 是必须安装的。

三、配置流水线

如前所述,流水线是我们配置自动化部署的关键,用户将代码推送到仓库时,GitLab会自动的调用流水线,GitLab 会将流水线中配置的行为交由 GitLab-Runner 来执行。

--- variables: MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true" MAVEN_CLI_OPTS: "--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true" image: maven:3.3.9-jdk-8 cache: paths: - ".m2/repository" verify: stage: test script: - 'mvn $MAVEN_CLI_OPTS verify' except: variables: - "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH" deploy: stage: deploy script: - if [ ! -f /etc/gitlab/ci_settings.xml ]; then echo "ci_settings 文件不存在,具体请看介绍: https://docs.gitlab.com/ee/user/packages/maven_repository/index.html#create-maven-packages-with-gitlab-cicd "; fi - 'echo "部署到远程仓库"' - 'mvn $MAVEN_CLI_OPTS deploy -s /etc/gitlab/ci_settings.xml' - 'mvn package' - 'echo "部署到远程机器"' - 'python3 /root/deploy.py ${CI_PROJECT_DIR}/target/*.jar ${CI_PROJECT_TITLE} /root/config.yml' - 'echo "部署成功"' only: variables: - "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"

分为验证和部署两个阶段,阶段一验证代码,阶段二分别部署代码到远程 maven 仓库和远程机器

  • 3.现在当用户对目标仓库进行 push 的时候,GitLab 会自动运行流水线,首先将事件发送给 GitLab-Runner,然后 GitLab-Runner 拉取最新仓库并运行配置的流水线任务。
  • 4.配置文件中可以使用一些预定义的 CI 环境变量,可查阅:https://docs.gitlab.com/ee/ci/variables/predefined_variables.html

部署到远程 maven 仓库

这一步主要体现在 mvn $MAVEN_CLI_OPTS deploy -s /etc/gitlab/ci_settings.xml,这里使用了 maven 插件,相应的 pom.xml 文件为:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <distributionManagement> <repository> <id>repo</id> <url>http://123.123.132.123:8081/repository/maven-releases/</url> </repository> <snapshotRepository> <id>repo</id> <url>http://123.123.132.123:8081/repository/maven-snapshots/</url> </snapshotRepository> </distributionManagement> </project>

上面的 project.distributionManagement.repository.id 和 ci_settings.xml 文件中的 servers.server.id 一一对应。ci_setttings.xml 文件和 .m2/settings.xml 文件类似,属于 maven 的配置文件。在 ci_settings.xml 文件中,我们配置了远程 maven 仓库的账号密码,由此 maven 可以将打包后的结果 push 到远程 maven 仓库。
ci_settings.xml文件内容为:

<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"> <servers> <server> <id>repo</id> <username>admin</username> <password>123456</password> </server> </servers> </settings>

部署到远程机(并运行)

前面用到的部署脚本deploy.py运行环境为:至少python3.8、PyYAML包,代码如下:

import yaml import os import sys class Executor: def __init__(self, argv: list): self.file = argv[3] self.argv = argv def _read_yml(self, file, mode='r', encoding='utf8') -> dict: """ 读取 yml 文件 :param file: 文件名 :param mode: 打开文件模式,默认为 'r' :param encoding: 编码格式 :return: dict """ with open(file, mode, encoding=encoding) as f: res = yaml.full_load(f) return res def _validate(self, data: dict) -> bool: """ 验证数据 :param data: 数据 :return: bool """ params = self.argv if len(params) < 3: return False self.jar_path = params[1] self.project_name = params[2] return True def _run_command(self,command: str) -> list: """ 运行本地命令 :param command: 命令 :return: 结果 """ with os.popen(command) as f: strs = f.readlines() return strs def _run_remote_command(self, data: dict) -> None: """ 运行远程命令 :param data: 包含url、identity_file、command、username :return: None """ ip = str(data.get("url")) identity_file = str(data.get("identity_file")) username = str(data.get("username")) command = str(data.get("command")) if len(ip) == 0 | len(identity_file) == 0 | len(command) == 0 | len(username) == 0: print("远程命令参数出错,【ip】:【%s】【identity_file】:【%s】【command】:【%s】" % (ip, identity_file, command)) return remote_command = f"ssh -i {identity_file} {username}@{ip} '{command}'" print("开始执行远程命令:%s" % remote_command) strs = self._run_command(remote_command) print("远程命令执行结果:【%s】" % strs) print("远程命令【%s】执行结束." % remote_command) def _ssh(self, data: dict) -> None: """ mode = ssh :param data: 数据 :return: None """ print("模式:【ssh】") has_command = str(data.get("has_command")) run_command = str(data.get("command")) if (has_command == "True") & len(run_command) > 0: self._run_remote_command(data) else: print("命令未执行,请检查参数.") def _scp(self, data: dict) -> None: """ mode = scp :param data: 数据 :return: None """ print("模式:【scp】") identity_file = data.get("identity_file") jar_path = self.jar_path username = data.get("username") spliter = os.sep project_name = self.project_name ip = data.get("url") base_dir = data.get("base_dir") has_command = str(data.get("has_command")) run_command = str(data.get("command")) success = False command = f"scp -i {identity_file} {jar_path} {username}@{ip}:{base_dir}{spliter}{project_name}{spliter} " print("开始执行命令: %s" % command) strs = self._run_command(command) if len(strs) == 0: success = True print("命令执行成功") else: print("命令【%s】执行失败" % command) print("错误信息: %s" % strs) if success & (has_command == "True") & len(run_command) > 0: self._run_remote_command(data) def _sftp(self, data: dict) -> None: """ mode = sftp :param data: 数据 :return: None """ print("模式:【sftp】") print(data) def execute(self) -> None: """ 开始执行 :return: None """ data = self._read_yml(self.file) if not self._validate(data): print("配置文件:%s 格式错误" % self.file) deploy = data.get('deploy') # 找到当前项目需要部署机器 needs = dict() for one_instance in deploy: ins = deploy.get(one_instance) if self.project_name == ins.get("project_name"): needs[one_instance] = ins print("%s 将被部署到 %s 台机器" % (self.project_name, str(len(needs)))) for one_instance in needs: ins = needs.get(one_instance) print("%s 部署到 %s" % (self.project_name, one_instance)) mode = ins.get('mode') # 通过反射方式调用函数 mode = "_" + mode # 检查是否有 mode 函数 或者 属性 if hasattr(self, mode): getattr(self, mode)(ins) else: print("未知模式:%s: %s" % (one_instance, mode)) if __name__ == "__main__": argv = sys.argv executor = Executor(argv) executor.execute()

部署脚本的配置文件config.yml如下:

deploy: ins1: ## 项目名称,脚本里用项目名称来匹配远程机,一个项目可配置多个远程机,从而部署到多个环境 project_name: test url: 123.123.123.123 username: root ## 私钥文件地址 identity_file: /root/xxxx.pem ## 模式 -- 只实现了 ssh 和 scp 模式,ssh 仅运行命令,scp 将jar包部署到远程机 mode: scp ## jar文件最终路径为 {base.dir}/{project_name}/ base_dir: /box/ ## 是否运行命令 has_command: True ## 运行命令 command: sh /box/test/start.sh

至此,配置完毕。

四、常见问题

GitLab、Git-Runner 运行卡顿?高 IO?

添加 swap 分区

  • 创建连续空间:dd if=/dev/zero of=/data/swap bs=512 count=8388616
  • 创建 swap 分区:mkswap /data/swap
  • 启动 swap 分区:swapon /data/swap
  • 在 /etc/fstab 文件中记录文件的名字,使系统重启后,swap 仍然有效 echo “/data/swap swap swap defaults 0 0” >> /etc/fstab
  • 查看 swap 分区:cat /proc/swaps

之后可以选择重新加载 gitlab:sudo gitlab-ctl reconfigure

GitLab-Runner 运行权限问题

通过 gitlab-runner 运行脚本使用的是前面配置的 gitlab-runner 用户角色,因为 gitlab-runner 在部署的时候使用了私钥文件,所以 gitlab-runner 用户角色必须有私钥文件的读权限。

posted @   zolmk  阅读(309)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
点击右上角即可分享
微信分享提示