精通-Gitlab12-全-
精通 Gitlab12(全)
原文:
annas-archive.org/md5/f3ad0f06b1b00f432ef450fc3e3fa136译者:飞龙
序言
GitLab 是一款提升团队工作流程的工具,能够支持 DevOps 生命周期的各个环节。最初,它只是一个源代码管理工具,但如今,GitLab 能够从管理初步想法、构建和测试源代码,到开发和生产的各个环节提供帮助。
你将学习如何利用 GitLab 中的所有功能,通过整合开发过程的各个阶段来提升业务。通过在本地或云端创建一个平台,你将受益于降低摩擦、提高协作,并通过更高效的运营推动竞争优势。
本书面向谁
本书适合开发人员和 DevOps 专业人员,他们希望掌握 GitLab 中的软件开发工作流,并通过将团队部署到 GitLab 来提升生产力,无论是本地安装还是基于云的基础设施。
本书内容
第一章,介绍 GitLab 架构,简要介绍了公司及其产品的创造者,并对 GitLab 及其组件进行了高层次的概述。
第二章,安装 GitLab,展示了如何通过几种不同的方法安装和配置 GitLab。可以从零开始,也可以使用 Omnibus 安装程序。特别关注了容器化解决方案中的 Docker 和 Kubernetes。最后,以 DigitalOcean 基础设施为例,展示了云安装的过程。
第三章,通过 UI 配置 GitLab,解释了安装后可以在 GitLab Web UI 中配置的选项。本章还涵盖了这些实例级选项所在的管理页面。
第四章,通过终端配置 GitLab,讲解了配置 GitLab 的不同方式。首先是通过 GitLab 提供的 Omnibus 包安装程序进行配置,该程序自动化了大部分安装工作。接下来,本章继续介绍源代码安装的配置过程,还涵盖了 Docker 容器的配置和 Kubernetes 安装的管理。
第五章,从 GitHub 导入项目到 GitLab,通过动手实验展示了从 GitHub 迁移的过程。首先,探讨了应该修改的 GitHub 项目设置。然后,展示了在 GitLab 中准备导入所需的设置,最后讲解了执行导入的步骤。
第六章,从 CVS 迁移,首先比较了 CVS 和 Git 这两种根本不同的系统。接着提供了迁移准备的指导,并讨论了实际转换的过程,以及清理不再需要的遗留物。
第七章,从 SVN 迁移,首先解释了 SVN 和 Git 之间的微妙及不那么微妙的区别。读者将学习如何通过两种不同的方法进行迁移:使用 SubGit 进行镜像和使用 svn2git 工具。
第八章,从 TFS 迁移仓库,首先处理了 TFS 和 Git 之间的差异。随后,展示了如何通过使用 git-tfs 工具将 TFS 项目的信息迁移到 Git 中。
第九章,GitLab 愿景 - 一体化工具链,解释了 GitLab 提供整个 DevOps 工具链给开发者的愿景,回顾了 XP 的起源和敏捷宣言。同时也探讨了 DevOps 范式的兴起,并总结了 GitLab 提供的工具链。
第十章,创建你的产品,验证它,并打包它,展示了 GitLab 的产品愿景及其工作流,重点围绕提供一个完整的工具链来创建产品的理念。本章专注于不同的阶段,并通过实例解释相关概念。
第十一章,发布和配置阶段,讨论了 GitLab 的一个大特点:能够通过不同、易于设计的阶段提供完整的生产之旅。通过这种方式,你可以创建不同的环境,并最终自动化整个产品的流水线。
第十二章,使用 Prometheus 监控,介绍了如何通过使用内置的 Prometheus 功能和默认脚本语言来监控你的 GitLab 环境。本章的第二部分解释了可用的不同安全性测试。
第十三章,将 GitLab 与 CI/CD 工具集成,解释了虽然 GitLab 旨在提供一个完整的工具链,但在实际使用中总是需要进行集成。本章讲解了一些开箱即用的大型集成,并以一个关于 webhooks 如何提供一种通用方式来从 GitLab 获取信息的部分作结。
第十四章,为 GitLab 持续集成设置项目,介绍了 GitLab CI 概念,这些概念存在于应用服务器上,并可以针对每个项目进行精细调整和自定义。本章的第二部分主要集中在如何准备你的项目以使用这些 CI 概念,并为其设置 runner。
第十五章,安装和配置 GitLab Runners,解释了 GitLab runners 的工作原理,包括如何安装它们。接下来的步骤是创建一个示例项目,并使用 shell 执行器来构建它。
第十六章,使用 GitLab 运行器与 Docker 或 Kubernetes,检查基于 Docker 的运行器架构以及使用 Kubernetes API 的运行器,使用与前面章节相同的示例。
第十七章,自动扩展 GitLab CI 运行器,展示了使用自动扩展的运行器架构。根据需求,所需的运行器数量会增加或减少。所示的示例使用 VirtualBox 和 亚马逊网络服务(AWS)来部署实例。
第十八章,监控 CI 指标,处理监控特定的 GitLab 运行器。通过实验室,我们展示了如何在运行器内部启用监控。介绍之后,具体的功能和系统指标将被解释。
第十九章,使用水平扩展创建基础 HA 架构,展示了不同组件如何互动。其次,展示了数据库的准备工作,以及多个一体化的应用服务器。最后,解释了该 高可用性(HA)设置中仓库的共享文件系统和 Redis 缓存的配置。我们将使用 Terraform 和 Ansible 创建演示环境。
第二十章,管理混合 HA 环境,在早期的水平 HA 架构基础上进行扩展,但复杂性继续增加。主要区别在于,应用服务器将几个组件合并,而这些组件现在被拆分到新的层次中。
第二十一章,使您的环境完全分布式,建立在前面的章节基础上。完全分布式架构旨在通过再次将组件拆分为新层次来提高容错能力。现在有了一个 SSH 节点和多个 sidekiq 层。
第二十二章,使用 Geo 创建 GitLab 的分布式只读副本,从 GEO 产品的解释开始,GEO 是企业版许可证的一部分。使用本书第五章《扩展服务器基础设施(高可用性设置)》中相同的工具,我们将解释如何设置 GEO 以在两个不同的地理位置之间创建复制。
为了最大化从本书中获得收益
为了最大限度地从本书中获得收益,您应该有一台 Linux 或 macOS 机器,具备互联网连接,并且拥有亚马逊 AWS、Google 和微软 Azure 账户。这些都是运行示例所必需的。
阅读本书需要一些基本的 IT 知识。您需要具备以下方面的经验:
-
Linux
-
Shell 脚本
-
Ruby 和 JavaScript 的基础编程技能
-
基本的 Docker 容器知识
-
基本的使用 Terraform 创建基础设施即代码的知识
-
基本的 Ansible 知识
下载示例代码文件
你可以从你的账户中下载本书的示例代码文件,访问www.packt.com。如果你在其他地方购买了这本书,可以访问www.packt.com/support并注册以便直接通过电子邮件获取文件。
你可以通过以下步骤下载代码文件:
-
登录或注册到www.packt.com。
-
选择支持标签。
-
点击代码下载和勘误表。
-
在搜索框中输入书名,并按照屏幕上的指示操作。
下载文件后,请确保使用最新版本的工具解压或提取文件夹:
-
WinRAR/7-Zip for Windows
-
Zipeg/iZip/UnRarX for Mac
-
7-Zip/PeaZip for Linux
本书的代码包也托管在 GitHub 上,地址是github.com/PacktPublishing/Mastering-GitLab-12。如果代码有更新,它将在现有的 GitHub 仓库中更新。
我们还提供来自我们丰富的书籍和视频目录中的其他代码包,您可以在github.com/PacktPublishing/找到它们。快去看看吧!
代码实战
访问以下链接查看代码的执行情况:
下载彩色图片
我们还提供了一份 PDF 文件,其中包含本书中使用的截图/图表的彩色图像。你可以在这里下载:static.packt-cdn.com/downloads/9781789531282_ColorImages.pdf。
使用的约定
本书中使用了多种文本约定。
CodeInText:表示文本中的代码词、数据库表名、文件夹名称、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名。例如:“让我们继续在/usr/local/www目录下安装网页文档。”
代码块的格式如下:
server {
listen 8080;
server_name localhost;
当我们希望引起你注意代码块中的特定部分时,相关行或项目会以粗体显示:
server {
listen 8080;
server_name localhost;
任何命令行输入或输出都按如下格式书写:
$mkdir /usr/local/www
$chmod 755 /usr/local/www
$cd /usr/local/www
粗体:表示一个新术语、一个重要的词或在屏幕上看到的单词。例如,菜单或对话框中的单词在文本中会以这种方式出现。比如:“你可以通过点击靠近 Logo 部分的选择文件按钮来完成此操作。”
警告或重要的注意事项会以这种方式显示。
小贴士和技巧以这种方式显示。
与我们联系
我们欢迎读者的反馈。
一般反馈:如果你对本书的任何方面有疑问,请在邮件主题中提到书名,并通过customercare@packtpub.com联系我们。
勘误:虽然我们已尽最大努力确保内容的准确性,但错误仍然会发生。如果您在本书中发现任何错误,我们将不胜感激,若您能将其报告给我们。请访问 www.packt.com/submit-errata,选择您的书籍,点击“勘误提交表单”链接,并填写相关详情。
盗版:如果您在互联网上发现我们作品的任何非法复制品,烦请提供相关位置或网站名称。请通过 copyright@packt.com 与我们联系,并附上该材料的链接。
如果您有兴趣成为作者:如果您在某个领域有专业知识,且有兴趣撰写或为书籍贡献内容,请访问 authors.packtpub.com。
评价
请留下评价。在您阅读并使用本书之后,何不在您购买书籍的网站上留下评论呢?潜在读者可以看到您的公正意见,从而做出购买决定,我们在 Packt 也可以了解您对我们产品的看法,而我们的作者也可以看到您对他们书籍的反馈。谢谢!
想了解更多有关 Packt 的信息,请访问 packt.com。
第一部分:在本地或云端安装和设置 GitLab
本节将帮助你深入了解 GitLab 部署选项和 GitLab 组件架构,使你能够在本地和云端安装并配置 GitLab。
本节包括以下章节:
-
第一章,介绍 GitLab 架构
-
第二章,安装 GitLab
-
第三章,使用 Web UI 配置 GitLab
-
第四章,从终端配置 GitLab
第一章:介绍 GitLab 架构
理解 GitLab 项目的背景有助于我们理解在 GitLab 工作流设计中所做的选择。GitLab 项目最初是一个小型的开源项目,后来发展成了一个拥有 400 人和数千名志愿者的组织。当前,它有两个版本:一个是免费的 社区版(CE),另一个是带有专有许可证的 企业版(EE)。企业版提供多个支持层级。虽然它是专有许可证,但该版本的源代码是公开的,可以从 GitLab 获取。
要掌握 GitLab,必须对其各个组件有扎实的了解。在本章中,我们将探讨 GitLab 安装的基本组件,特别关注 GitLab 持续集成(CI)及其附带的运行器。由于不同组件可以分布在不同的服务器或云服务提供商上,我们还将提供这些提供商的概述,并介绍 GitLab 如何看待它们。
在本章中,我们将涵盖以下主题:
-
GitLab 的起源
-
GitLab CE 或 EE
-
GitLab 的核心组件
-
GitLab CI
-
GitLab 运行器
-
云原生
技术要求
要跟随本章的操作步骤,请下载包含示例的 Git 仓库,网址为 GitHub:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter01。你还需要安装 Homebrew:brew.sh/。
GitLab 的起源
故事始于 2011 年,当时来自乌克兰的网页程序员 Dimitri Zaporozhets 面临一个常见问题。他想要使用 Git 来进行版本管理,并通过 GitHub 进行协作,但在公司中无法使用。他需要一个既不妨碍他开发代码又易于使用的工具。像许多开发者一样,他遇到了被迫使用的协作工具的问题。为了解决这些问题,他用 Ruby on Rails 创建了一个侧项目:GitLab。在与同事 Valery Sizov 的合作下,他在常规工作之余开发了这个项目。
在这一举措后,该项目迅速发展壮大:
| 日期 | 事实 |
|---|---|
| 2011 | GitLab 的未来 CEO Sytze Sybrandij 对 GitLab 项目和代码印象深刻,并邀请 Zaporozhets 尝试通过 about.gitlab.com/ 将其商业化。 |
| 2012 | GitLab 通过 Hacker News 向更广泛的受众发布 (news.ycombinator.com/item?id=4428278)。 |
| 2013 | Dimitri Zaporozhets 决定全职投入到 GitLab,并加入公司。 |
| 2015 | GitLab 成为 Y Combinator 的一部分,并获得了当年的风险资本资助。 |
| 2018 | GitLab 获得了 1 亿美元的风险投资,并估值为 10 亿美元。 |
| 2019 | GitLab 公司拥有超过 600 名员工。 |
GitLab 的最初想法是通过提供支持服务来从开源技术中赚钱。然而,发生的情况是,公司开始仅仅通过顾问来升级 GitLab,然后停止服务合同。很明显,100% 开源的路线并不具有竞争力。因此,他们选择了开放核心模式。在开放核心下,公司以开源许可证发布核心软件系统。软件的不同版本则以商业许可证销售,并包含更多功能。
因此,GitLab 被分为两个版本:一个是开源版本,一个是企业版本。
探索 GitLab 版本 – CE 和 EE
GitLab 软件的核心被称为CE。它以 MIT 许可证发布,这是麻省理工学院创建的一种宽松的自由软件许可证。您可以修改该软件并在您的创作中使用它。
任何进入 CE 的功能都不会被删除,或者移至封闭源版本。当 GitLab EE 在 2013 年创建时,其核心是 GitLab CE,但它具有额外的企业功能,如轻量级目录访问协议(LDAP)组。这些功能本身并不是开源的,但如果公司认为它们是核心功能,可以将它们添加到核心版本中。这个想法是,企业应该尽可能多地为解决问题和创建新功能做出贡献。
2016 年,GitLab EE 产品被分为三个层级:Starter、Premium 和 Ultimate。每个层级比前一个层级贵大约五倍,并包含更多功能和支持选项,如下表所示:
| 版本 | 功能(简要列表) |
|---|
| Starter | 核心 GitLab CE 的所有功能:
-
CI/CD
-
项目问题板
-
Mattermost 集成
-
时间跟踪
-
GitLab 页面
|
| Premium | 更多企业功能,如下所示:
-
Maven 和 NPM 仓库功能
-
受保护环境
-
燃尽图
-
多个 LDAP 服务器和 Active Directory 支持
|
| Ultimate | 所有选项,包括以下内容:
-
所有安全扫描工具
-
史诗
-
免费的访客用户
-
Web IDE 的 Web 终端
|
GitLab 有很多功能,但我们首先要集中讨论基本的构建模块。
GitLab 的核心系统组件
GitLab 不是一个单体应用程序。它试图遵循 Unix 的哲学,即软件模块应该只做一件特定的事情,并做好这件事。GitLab 的组件虽然不像 Unix 的awk和sed那样小巧精致,但每个组件都有一个明确的目标。您可以在以下图表中找到这些组件的高级概览:

GitLab 最初是一个纯 Ruby on Rails 应用程序,但后来一些组件使用 Go 重新设计。Ruby on Rails 是一个基于 Ruby 编程语言构建的开发框架。它实现了模型-视图-控制器模式,并提供了连接不同数据库的方法(例如,ActiveRecord)。它提倡“约定优于配置”和 不要重复自己(DRY)编程。它非常适合快速开发,同时又具备高性能和许多功能。
让我们深入了解这些组件,以便理解它们的角色。
NGINX
Unicorn Web 组件不能直接使用,因为它没有提供处理客户端的所有功能。默认捆绑的反向代理是 NGINX。也可以使用 Apache 作为 GitLab 的前端,但推荐使用 NGINX。有很多可以安装在 Unicorn 前端的 Web 服务器,但最终基本上可以分为两种类型,分别如下:
-
基于进程的(分叉或多线程)
-
异步
NGINX 和 lighttpd 可能是两个最著名的异步服务器。Apache 毋庸置疑是事实上的标准进程型服务器。这两种服务器的最大区别在于它们如何处理可扩展性。对于进程型服务器,每一个新连接都需要一个线程,而像 NGINX 这样的事件驱动异步服务器只需要几个线程(或理论上只需一个)。对于轻量级工作负载,这没有太大区别,但当连接数增多时,尤其是在内存方面,你会看到很大的差异。当处理成千上万的并发连接时,NGINX 使用的内存仍然保持在几兆字节左右,而 Apache 则可能使用几百兆字节,或者根本无法工作。这就是为什么 NGINX 是更好的选择。
你可以在许多平台上运行 NGINX,并且安装过程非常简单,正如你在接下来的章节中看到的,你将亲自尝试!
安装 NGINX
NGINX 是大多数包管理仓库的一部分,包括 yum 和 apt。在基于 apt 的发行版(如 Debian 和 Ubuntu)上安装 NGINX 需要以下命令:
sudo apt-get update
sudo apt-get install nginx
在 macOS 上,我们可以使用 brew 执行一个快速的一行命令:
brew install nginx
当然,也可以从源代码安装。记得先安装依赖项,包括 PCRE、zlib 和 OpenSSL。你可以在以下网站找到源代码:nginx.org/en/download.html。
在启动 NGINX 之前,你需要提供一个配置文件,使其能够连接到 Unicorn Web 组件。这两个服务器之间的接口是本地 Unix 域套接字。
请创建一个名为 nginx.config 的文件,并添加以下内容:
events{}
上述部分是必需的,你可以指定 NGINX 如何处理连接。对于这个例子,我们接受默认设置;这就是为什么该部分为空的原因。
下一部分是一个 HTTP 块。你可以定义多个,并让设置继承,但在这里,我们定义 HTTP 请求应该重定向到 upstream gitlab-app,即 Unicorn。你还可以看到接口是 Unix 套接字:
http {
# Tell nginx there is a unicorn waiting
upstream gitlab-app {
server unix:/var/www/gitlab-app/tmp/sockets/unicorn.sock fail_timeout=0;
}
所以,我们已经定义了 NGINX 如何连接到后端,即 GitLab。在前端,我们希望接受来自 HTTP 客户端的请求。这是通过 server 块来处理的:
server {
listen 8080;
server_name localhost;
这个块中的下一个指令处理磁盘上不存在的路径。它将请求转发到应用程序:
try_files $uri/index.html $uri @gitlab-app;
以下是 gitlab-app 的定义,它修改请求头以通过 Unix 套接字将请求代理到上游 Unicorn 服务器:
location @gitlab-app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://gitlab-app;
}
不要忘记关闭服务器和 HTTP 块:
}
}
现在我们有了配置文件,你可以通过以下方式运行这个 NGINX:
nginx -c /path/to/nginx.config
该命令不应返回任何输出,且 NGINX 服务器会在后台运行。你可以通过检查进程列表自己验证这一点:
$ ps -ax|grep nginx |grep -v grep
33310 ?? 0:00.00 nginx: master process nginx -c /Users/joostevertse/nginx.config
33312 ?? 0:00.00 nginx: worker process
现在你已经有了一个运行中的 NGINX 服务器,如果你在浏览器中访问 http://localhost:8080,你应该会收到一个 502 错误。这是因为还没有 Unicorn 服务器在 Unix 套接字上监听。我们将在下一部分展示如何运行 Unicorn。
Unicorn
Unicorn 是一个为处理高性能客户端的应用程序设计的 HTTP 服务器,它能够在低延迟和足够带宽的连接上运行。它利用了 Linux 类系统内核中的特性。它被称为 Rack HTTP 服务器,因为它实现了用于 Rack 应用程序的 HTTP。Rack 实际上是一个 Ruby 实现的最小接口,用于处理 Web 请求,你可以在代码中使用它。
你可以在 rack.github.io 找到该项目。
Unicorn 作为守护进程在 Unix 系统中运行,使用 Ruby 和 C 编程语言编写。使用 Ruby 意味着它还可以运行 Ruby on Rails 应用程序,例如 GitLab。
现在你已经掌握了 Unicorn 的基本概念,我们可以安装它,并将之前安装的 NGINX 连接到它。
安装 Unicorn
如果你已经安装了 Ruby,可以按照以下步骤轻松安装 Unicorn:
- 第一步是安装 Ruby on Rails:
$gem install rails
- 下一步是安装 Unicorn 服务器二进制文件:
$gem install unicorn
- 让我们继续在
/usr/local/www安装 Web 文档。我们从创建目录开始:
$mkdir /usr/local/www
$chmod 755 /usr/local/www
$cd /usr/local/www
- 我们现在可以创建我们的 Rails 应用程序,并通过 Unicorn 提供服务:
$rails new gitlab-app
- 在一大堆输出后,可能看起来有点吓人,但这实际上是一个为你预先配置好的应用程序。让我们配置 Unicorn 来提供服务。我们可以在这里获取默认的配置文件:
cd gitlab-app/config
wget https://raw.github.com/defunkt/unicorn/master/examples/unicorn.conf.rb
- 我们需要修改一些内容。让我们从为基本应用程序文件夹创建一个变量开始:
APP_PATH = "/var/www/gitlab-app"
- 现在我们可以更改以下路径:
working_directory APP_PATH
stderr_path APP_PATH + "/log/unicorn.stderr.log"
stdout_path APP_PATH + "/log/unicorn.stderr.log"
pid APP_PATH + "/tmp/pid/unicorn.pid"
- Unicorn 可以监听端口和/或套接字。我们将使用 Unix 套接字进行监听,因为这是最短的路径:
#change the path to the socket (defined also in nginx config)
listen app_path + '/tmp/sockets/unicorn.sock', backlog: 64
- 我们可以通过以下命令启动 Unicorn:
unicorn -c config/unicorn.rb
如果 NGINX 也提前启动了,我们现在可以将浏览器指向 http://localhost:8080。
现在,也许你遇到了错误,想要了解发生了什么问题。了解如何在出现问题时调试 NGINX 和 Unicorn 可能是必要的。下一节将介绍这一内容。
调试 Unicorn
可能是安装 Unicorn 时产生了错误,或者你遇到了性能问题,怀疑是 Unicorn 未正常工作所致。
有几种方法可以找到问题的根源。日志文件可以为你指明方向。
Unicorn 日志中的超时
以下输出是 Unicorn 工作者超时在 unicorn_stderr.log 中的表现。这不一定是坏事,它只是意味着新的工作进程被启动了:
[2015-06-05T10:58:08.660325 #56227] ERROR -- : worker=10 PID:53009 timeout (61s > 60s), killing
[2015-06-05T10:58:08.699360 #56227] ERROR -- : reaped #<Process::Status: pid 53009 SIGKILL (signal 9)> worker=10
[2015-06-05T10:58:08.708141 #62538] INFO -- : worker=10 spawned pid=62538
[2015-06-05T10:58:08.708824 #62538] INFO -- : worker=10 ready
可能是 Unicorn 可用的工作进程不足以响应当前的请求。NGINX 会缓存大量请求,所以我们必须检查交接套接字,看看 Unicorn 是否能跟得上。为此,可以使用一个方便的脚本,地址为:github.com/jahio/unicorn-status。
它可以通过以下命令调用:
$ ruby unicorn_status.rb /var/opt/gitlab/gitlab-rails/sockets/gitlab.socket 10
Running infinite loop. Use CTRL+C to exit.
------------------------------------------
Active Requests Queued Requests
20 11
这里的第一个参数是 unicorn_status.rb 脚本,第二个是要连接的套接字 ../.socket,最后一个参数是轮询间隔(10)。
Unicorn 进程消失
在 Linux 上,有一种机制叫做 Out-of-Memory (OOM) Killer,当系统内存不足且没有交换内存时,它会释放内存。如果 Unicorn 占用了过多内存,它可能会被杀死。
使用 dmesg | egrep -i 'killed process' 搜索 OOM 事件:
[102335.3134488] Killed process 5567 (ruby) total-vm:13423004kB, anon-rss:554088kB
其他类型的错误或 100% CPU 占用
调试 Unicorn 进程的终极方法是对其运行 strace:
-
运行
sudo gdb -p (PID)以附加到 Unicorn 进程。 -
在 GDB 控制台中运行
call (void) rb_backtrace()并在/var/log/gitlab/unicorn/unicorn_stderr.log中找到生成的 Ruby 回溯信息:
from /opt/gitlab/embedded/lib/ruby/gems/2.4.0/gems/bundler-1.16.2/lib/bundler/cli/exec.rb:28:in `run'
from /opt/gitlab/embedded/lib/ruby/gems/2.4.0/gems/bundler-1.16.2/lib/bundler/cli/exec.rb:74:in `kernel_load'
from /opt/gitlab/embedded/lib/ruby/gems/2.4.0/gems/bundler-1.16.2/lib/bundler/cli/exec.rb:74:in `load'
from /opt/gitlab/embedded/bin/unicorn:23:in `<top (required)>'<br/> from /opt/gitlab/embedded/bin/unicorn:23:in `load
from /opt/gitlab/embedded/lib/ruby/gems/2.4.0/gems/unicorn-5.1.0/bin/unicorn:126:in `<top (required)>'
from /opt/gitlab/embedded/lib/ruby/gems/2.4.0/gems/unicorn-5.1.0/lib/unicorn/http_server.rb:132:in `start'
from /opt/gitlab/embedded/lib/ruby/gems/2.4.0/gems/unicorn-5.1.0/lib/unicorn/http_server.rb:508:in `spawn_missing_workers'
from /opt/gitlab/embedded/lib/ruby/gems/2.4.0/gems/unicorn-5.1.0/lib/unicorn/http_server.rb:678:in `worker_loop'
from /opt/gitlab/embedded/lib/ruby/gems/2.4.0/gems/unicorn-5.1.0/lib/unicorn/http_server.rb:678:in `select'
- 完成后,使用
detach和exit退出 GDB。
Sidekiq
Sidekiq 是一个后台作业处理框架。它通过在后台执行任务来帮助你扩展应用程序。欲了解更多有关 Sidekiq 的信息,请访问以下网站:github.com/mperham/sidekiq/wiki。
每个 Sidekiq 服务器进程从 Redis 队列中拉取任务并处理它们。像你的 Web 进程一样,Sidekiq 启动 Rails,这样你的任务和工作进程就可以使用完整的 Rails API,包括 ActiveRecord。服务器将实例化工作进程,并使用给定的参数调用 perform。其余部分由你的代码决定。
安装 Sidekiq
使用 Sidekiq 非常简单。它可以作为 gem 安装:
$ sudo gem install sidekiq
Password:
Fetching: redis-4.0.2.gem (100%)
Successfully installed redis-4.0.2
Fetching: connection_pool-2.2.2.gem (100%)
.,,,
Done installing documentation for redis, connection_pool, rack, rack-protection, sidekiq after 7 seconds
5 gems installed
如你所见,某些依赖项也已安装,例如 Redis、连接池、Rack 和 Rack 保护。
调试 Sidekiq
与 Unicorn 一样,有多种方法可以调试 Sidekiq 处理。最简单的方法是以管理员身份登录 GitLab 并查看日志,特别是查看后台作业页面上的队列和作业,如以下截图所示:

有时,你会遇到问题,并在 Linux 服务器上发现一些情况。
Sidekiq 进程消失
如前所述,在 Unicorn 部分中,OOM Killer 可能会在 Sidekiq 使用过多内存时终止它。
使用 dmesg | egrep -i 'killed process' 查找 OOM 事件:
[102335.3134488] Killed process 8887 (ruby) total-vm:13523004kB, anon-rss:5540458kB
Sidekiq 进程似乎什么也不做
如果 Sidekiq 没有执行任何工作并且大部分时间看起来卡住了,这意味着程序在等待某些东西。一种常见的等待情况是当你正在进行远程网络调用时。如果你认为这可能是原因,你可以通过发送 TTIN 信号让 Sidekiq 进程转储堆栈跟踪到日志中。
这是在 /var/log/gitlab/sidekiq/current 中的日志文件中,Sidekiq 工作者的样子:
{"severity":"INFO","time":"2019-06 23T19:00:14.493Z","class":"RemoteMirrorNotificationWorker","retry":3,"queue":"remote_mirror_notification","jid":"69eb806bfb66b82315bcb249","created_at":"2019-06-23T19:00:14.461Z","correlation_id":"toX0HnYW0s9","enqueued_at":"2019-06-23T19:00:14.461Z","pid":471,"message":"RemoteMirrorNotificationWorker JID-69eb806bfb66b82315bcb249: done: 0.03 sec","job_status":"done","duration":0.03,"completed_at":"2019-06-23T19:00:14.493Z"}
从 GitLab 12.0 开始,Sidekiq 的默认输出日志格式是 JSON,这使得通过像 logstash 这样的工具读取日志文件变得更容易,因为它更加结构化。
其他类型的错误或 100% CPU 占用
调试 Sidekiq 进程的终极方法是通过 GDB 让它转储堆栈跟踪信息:
-
运行
sudo gdb -p (PID)来附加到 Sidekiq 工作进程。 -
在 GDB 控制台中运行
call (void) rb_backtrace(),并在/var/log/gitlab/sidekiq/current中查找生成的 Ruby 堆栈跟踪:
2018-09-21_19:55:03.48430 from /opt/gitlab/embedded/lib/ruby/gems/2.4.0/gems/redis-3.3.5/lib/redis/connection/ruby.rb:83:in `_read_from_socket'
2018-09-21_19:55:03.48431 from /opt/gitlab/embedded/lib/ruby/gems/2.4.0/gems/redis-3.3.5/lib/redis/connection/ruby.rb:87:in `rescue in _read_from_socket'
2018-09-21_19:55:03.48432 from /opt/gitlab/embedded/lib/ruby/gems/2.4.0/gems/redis-3.3.5/lib/redis/connection/ruby.rb:87:in `select'
-
堆栈跟踪信息非常难以阅读,但这个进程在被跟踪时正在进行网络操作,我们可以看到一个 (
_read_from _socket)。你可以查看源代码来确认它正在做什么(源代码中提到有行号)。 -
完成后,使用
detach离开 GDB 并退出。
你也可以使用其他跟踪工具来检查循环进程的行为。例如,在 Linux 中,strace -p <pid> 允许你查看进程正在执行的系统调用。
GitLab Shell
这个组件用于通过 SSH 提供对 Git 仓库的访问。实际上,对于通过 git-http 协议的推送,它也会代替 Rails 应用程序被调用。它本质上是一个围绕 Git 客户端的小 Ruby 封装。Git 通过 SSH 使用预定义的命令,这些命令可以在 GitLab 服务器上执行。为了进行授权,它会调用 GitLab API。在 GitLab 5.0 之前,这个功能由 Gitolite 提供,并由 Perl 编程语言驱动。
该项目的源代码可以在此找到:gitlab.com/gitlab-org/gitlab-shell。你可以看到以下页面:

您可以将其安装在本地,但它仅在与其他 GitLab 组件一起部署时才真正有用。当您完成安装(有关如何安装,请参见 第二章,安装 GitLab),下一部分将描述遇到问题时的调试方法。
调试 GitLab Shell
在全包安装中,GitLab Shell 的日志文件可以在以下位置找到:
/var/log/gitlab/gitlab-shell/gitlab-shell.log
作为替代,源代码安装时可以在以下位置找到:
/home/git/gitlab-shell/gitlab-shell.log
您通常会发现的是与 GitLab Shell 基本操作相关的日志行:
-
Git 命令(如
git push和git pull)。 -
授权调用 GitLab Rails API 以检查是否允许连接
-
执行预接收钩子
-
请求的操作
-
接收后操作
-
任何自定义的接收后操作
这里,我们列出了日志文件中的一些行:
bash-4.1$ tail gitlab-shell.log
time="2018-09-26T08:59:53+02:00" level=info msg="executing git command" command="gitaly-upload-pack unix:/var/opt/gitlab/gitaly/gitaly.socket {\"repository\":{\"storage_name\":\"default\",\"relative_path\":\"xxx/xxx.git\",\"git_object_directory\":\"\",\"git_alternate_object_directories\":[],\"gl_repository\":\"xxx\"},\"gl_repository\":\"project-xx\",\"gl_id\":\"key-xx\",\"gl_username\":\"xxxxxx\"}" pid=18855 user="user with key key-xx"
time="2018-09-26T08:59:53+02:00" level=info msg="finished HTTP request" duration=0.228132057
method=POST pid=18890 url="http://127.0.0.1:8080/api/v4/internal/allowed"
time="2018-09-26T08:59:54+02:00" level=info msg="finished HTTP request" duration=0.030036933 method=POST pid=18890 url="http://127.0.0.1:8080/api/v4/internal/pre_receive"
time="2018-09-26T08:59:54+02:00" level=info msg="finished HTTP request" duration=0.094035804 method=POST pid=18979 url="http://127.0.0.1:8080/api/v4/internal/post_receive"
查找错误的一种方法是寻找某些模式,例如failed,如下所示。这个特定错误指向 Unicorn 的 500 错误,检查用户是否有正确的授权来调用 GitLab API。
如果您在 Unicorn 日志(production.log)中搜索 HTTP 500 错误,应该会显示此错误:
bash-4.1$ grep -i failed gitlab-shell.log
time="2018-09-26T08:05:52+02:00" level=error msg="API call failed" body="{\"message\":\"500 Internal Server Error\"}" code=500 method=POST pid=1587 url="http://127.0.0.1:8080/api/v4/internal/allowed"
time="2018-09-26T08:45:13+02:00" level=error msg="API call failed" body="{\"message\":\"500 Internal Server Error\"}" code=500 method=POST pid=24813 url="http://127.0.0.1:8080/api/v4/internal/allowed"
Redis
Redis 是一个缓存工具和 HTTP 会话存储,允许您将网站的缓存数据和会话信息保存到外部位置。这意味着您的网站不必每次都进行计算;相反,它可以从缓存中检索数据并更快地加载网站。即使应用程序崩溃,用户会话也会保存在内存中。Redis 是一个快速的缓存工具,因为它首先使用内存。它有几个有用的优点:
-
一切都存储在一个地方,因此您只需刷新一个缓存。
-
它比 Memcache 更快。使用大型商店的网站时,可以明显感受到这一点。
-
会话保存在内存中,而不是保存在数据库中。
-
后端变得更快。
Redis 不仅仅是一个缓存,它还是一个数据结构存储。它基本上是一个数据库,应该从概念上将其视为一个数据库。在其操作和数据处理方式方面,它与 NoSQL 数据库更为相似。
安装 Redis
Redis 可通过所有主要的软件包管理系统获得。以下是不同平台上安装它的命令:
- 适用于安装了 Homebrew 的 Mac:
brew install redis
- 适用于 Linux Ubuntu 或其他基于 APT 的发行版:
apt-get install redis
- 适用于 Red Hat 或其他基于 YUM 的发行版:
yum install redis
然而,安装 Redis 的首选方式是通过从源代码编译。这种方式可以轻松保持更新。除了 GCC 编译器和标准 C 库外,它没有其他特别的依赖项。您可以在 download.redis.io/redis-stable.tar.gz 找到最新的稳定版本。
安装和编译它就像输入以下命令一样简单:
curl http://download.redis.io/redis-stable.tar.gz | tar xvz
cd redis-stable
make
成功完成后,你可以选择进行下一步操作,即执行 make test 来对已编译的源代码进行测试。
若要将二进制文件安装到一个有用的位置,请使用以下命令:
sudo make install
关于已编译结构的进一步解释,请前往 src 目录。你将找到以下信息:
-
redis-server:Redis 服务器程序 -
redis-sentinel:用于监控 Redis 集群的程序 -
redis-cli:用于控制 Redis 的命令行程序 -
redis-benchmark:用于测量 Redis 性能的程序 -
redis-check-aof和redis-check-dump:用于处理数据损坏的工具
现在一切都准备就绪,我们可以启动服务器了。
在 macOS 上通过 brew 安装时,使用以下命令:
brew services start redis
在其他平台上,当从源代码构建时,你可以直接通过运行 redis-server 命令启动 Redis 服务器。在一个新的 shell 窗口中,输入以下命令:
redis-server
按下 Enter 后,你将看到服务器启动:

你可以通过执行以下命令来测试你的 Redis 实例是否正常工作:
$ redis-cli ping
当 Redis 正常运行时,将会有响应:
PONG
如果收到 PONG,那么一切正常。
Redis 中的基础数据操作
让我们通过操作这些数据结构来探索 Redis 的一些基础。再次启动 redis-cli 命令行工具,不带 ping 参数,它将连接到本地的 Redis 服务器:
$redis-cli
127.0.0.1:6379>
将 Redis 视为一个简单的哈希数据库并不公平,尽管它提供的五种数据结构实际上都由键和值组成。我们来总结一下这五种数据结构:
- 字符串:你可以使用
set命令将一个值写入 Redis。在简单字符串的情况下,你只需按如下所示将值保存到数据存储中。设置字符串值后,你可以通过发出get命令再次检索该值:
$ redis-cli
127.0.0.1:6379> set mykind "Human"
OK
127.0.0.1:6379> get mykind
"Human"
127.0.0.1:6379>
- 哈希:与字符串类似,你可以使用
set命令将任意数量的值设置为一个键。一般来说,Redis 将值视为字节数组,并不关心它们是什么。这使得 Redis 在表示对象时非常方便。同样,通过get命令,你可以检索这些值。GitLab 使用这种类型来存储用户的 web 会话信息:
$ redis-cli
127.0.0.1:6379> set programs:tron '{"name": "tron","kind": "program"}'
OK
127.0.0.1:6379> get programs:tron
"{\"name\": \"tron\",\"kind\": \"program\"}"
- 列表:Redis 中的列表类型实现为链表。你可以通过
rpush(右推,向列表尾部添加项)或lpush(左推,向列表头部添加项)快速地向列表中添加项。另一方面,通过索引访问项的速度就较慢,因为它需要搜索链表。然而,对于队列机制来说,这仍然是一个不错的解决方案。
$ redis-cli
127.0.0.1:6379> rpush specieslist human computer cyborg
(integer) 3
127.0.0.1:6379> rpop specieslist
"cyborg"
127.0.0.1:6379> rpop specieslist
"computer"
127.0.0.1:6379> rpop specieslist
"human"
127.0.0.1:6379> rpop specieslist
(nil)
- 集合:另一种数据类型是集合。你可以使用
sadd命令向集合中添加成员。别忘了,这些集合是无序的,因此,如果你使用smembers查询成员,返回的顺序通常会与输入时不同:
$ redis-cli
127.0.0.1:6379> sadd speciesset human computer cyborg
(integer) 3
127.0.0.1:6379> smembers speciesset
1) "computer"
2) "human"
3) "cyborg"
- 有序集合:幸运的是,也有一个有序集合。它几乎一样,但一个不同之处是,你需要为条目添加一个分数,这将自动排序,如下所示:
127.0.0.1:6379> zadd speciessortedset 1 human
(integer) 1
127.0.0.1:6379> zadd speciessortedset 2 computer
(integer) 1
127.0.0.1:6379> zadd speciessortedset 3 cyborg
(integer) 1
127.0.0.1:6379> zrange speciessortedset 0 -1
1) "human"
2) "computer"
3) "cyborg"
Gitaly
在 GitLab 的早期版本中,所有 Git 操作都依赖于使用本地磁盘或网络共享。Gitaly 是一个旨在消除对网络文件系统(NFS)依赖的项目。Gitaly 提供了一个基于远程过程调用(RPCs)的系统,供 GitLab 访问 Git 仓库,而不是调用文件系统服务。它是用 Go 编写的,并使用gRPC 远程过程调用(gRPC),这是 Google 的跨平台 RPC 框架。从 2017 年初开始,它一直在稳步开发,自 GitLab 11.4 以来,它可以替代对共享 NFS 文件系统的需求。
你可以在以下截图中查看 Gitaly 及其在 GitLab 架构中的位置:

在小型安装中,它与所有其他组件运行在同一台服务器上。在大型集群环境中,你可以设置专用的 Gitaly 服务器,Gitaly 客户端可以使用这些服务器,如下所示:
-
独角兽
-
Sidekiq
-
gitlab-workhorse -
gitlab-shell -
Elasticsearch 索引器
-
Gitaly 作为客户端
该项目的源代码可以在这里找到:gitlab.com/gitlab-org/gitaly.
调试 Gitaly
你可以使用可用于 Golang 的调试工具。但首先,你可以查看日志文件。
对于源代码安装,请使用此方法:
/home/git/gitaly/
对于 Omnibus 安装,请使用此方法:
/var/log/gitlab/gitaly/current
以下是一个日志行的示例:
2018-09-26_13:23:40.57373 lrv162w2 gitaly: time="2018-09-26T13:23:40Z" level=info msg="finished streaming call" grpc.code=OK grpc.method=SSHUploadPack grpc.request.glRepository=project111111 grpc.request.repoPath=namespace/project-bl.git grpc.request.repoStorage=default grpc.request.topLevelGroup=hb-backend grpc.service=gitaly.SSHService grpc.time_ms=150 peer.address=@ span.kind=server system=grpc
你可以看到日志级别是info,这是一个捕获 Git SSH 命令的日志事件(method=SSHUploadPack)。它在服务器上启动了一个 Git pack命令,这意味着它重新排列并压缩了一个仓库中的数据。
要生成更详细的日志,你可以在配置文件中将日志级别设置为调试。它通过Tom 的明显最小语言(TOML)配置文件进行配置。此文件在前面提到的 Gitaly 源代码库中有文档说明。
对于源代码安装,请查看此处:
/home/git/gitaly/config.toml
你可以更改以下部分并更改级别:
# # Optional: Set log level to only log entries with that severity or above
# # One of, in order: debug, info, warn, error, fatal, panic
# # Defaults to "info"
# level = "warn"
对于 Omnibus 安装,可以将以下指令添加到gitlab.rb中,以影响 Gitaly 的监控级别。设置为debug以启用调试级别日志:
gitaly['log_directory'] = "/var/log/gitlab/gitaly"
gitaly['logging_level'] = "debug"
GitLab Workhorse
GitLab Workhorse 是一个复杂的反向代理,位于 GitLab 前面。最初为了解决处理git-http请求的问题,它作为一个周末项目开始,名称为gitlab-git-httpserver。该功能之前由gitlab-grack(gitlab.com/gitlab-org/gitlab-grack)提供。主要的 Web 应用服务器 Unicorn 并不特别适合处理这些可能需要很长时间才能完成的请求。直接在 Unicorn 中处理这些请求实际上会抵消 Unicorn 可以提供的优势:快速和可扩展的 HTTP 请求。
Workhorse 是用 Golang 创建的,由 GitLab 开发人员 Jacob Vosmaer 构思。你可以在about.gitlab.com/2016/04/12/a-brief-history-of-gitlab-workhorse/阅读关于它的创建过程。
尽管最初设计用于处理 Git HTTP 协议,GitLab Workhorse 逐渐增加了许多功能,例如:
-
某些静态文件,如 JavaScript 和 CSS 文件,会直接提供服务。
-
它可以拦截 Rails 关于打开文件的请求。Workhorse 将打开文件并在响应正文中发送内容。
-
它可以拦截对 Git 大文件存储 (LFS) 的调用,并在准备好文件并上传到指定位置后插入一个临时路径。Git LFS 是一个功能,允许将大文件存储在 GitLab 项目空间之外。
-
它可以控制 Rails 的 WebSocket 连接,例如终端输出。
Workhorse 位于 NGINX 后面,负责处理请求路由和 SSL 终止。
调试 GitLab Workhorse
由于 workhorse 是用 Golang 编写的应用程序,你可以使用该语言的方法来调试程序。
它还支持使用 Sentry 进行远程错误日志记录。要启用此功能,请设置GITLAB_WORKHORSE_SENTRY_DSN环境变量。
对于 Omnibus 安装
以下内容在文件(/etc/gitlab/gitlab.rb)中定义:
gitlab_workhorse['env'] = {'GITLAB_WORKHORSE_SENTRY_DSN' => 'https://foobar'}
对于源代码安装
以下环境变量可以在文件(/etc/default/gitlab)中设置:
export GITLAB_WORKHORSE_SENTRY_DSN='https://foobar'
当然,首先要查看的是此组件生成的日志文件。在基于 Omnibus 的 GitLab 安装中,你可以在/var/log/gitlab/gitlab-workhorse中找到它们。
以下是默认日志文件的摘录:
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.1 Safari/605.1.15" 0.478
2018-08-16_20:26:43.42795 localhost:8080 @ - - [2018/08/16:20:26:43 +0000] "GET /root/mastering-gitlab-12.git/info/refs?service=git-upload-pack HTTP/1.1" 401 26 "" "git/2.15.2 (Apple Git-101.1)" 0.066
2018-08-16_20:26:50.60861 localhost:8080 @ - - [2018/08/16:20:26:50 +0000] "POST /root/mastering-gitlab-12.git/git-upload-pack HTTP/1.1" 200 329 "" "git/2.15.2 (Apple Git-101.1)" 0.249
在前面的日志文件中,例如,你会看到git-http操作,例如git-upload-pack。
数据库
GitLab 有两种数据库可用:PostgreSQL 和 MySQL/MariaDB。由于 GitLab 作为一个产品的快速迭代发展主要集中在 PostgreSQL 上,因此不推荐使用后者,因为 MySQL 上没有许多优化。此外,使用 MySQL 时不能使用零停机时间方法,也没有如子组和 GEO 等功能,这些将在本书后面解释。
如前所述,Ruby on Rails 使用所谓的 MVC 方法。MVC 是一个广为人知的架构模式,由 Trygve Reenskaug 在 Smalltalk 语言中开发。后来它被改进为适用于 web 应用程序(Model 2)。MVC 中的模型由 ActiveRecord 库实现,这是 Ruby on Rails 的一部分。
数据模型的权威来源可以在此处找到:gitlab.com/gitlab-org/gitlab-ee/blob/master/db/schema.rb。它是自动生成的,代表了当前数据库的状态。
Omnibus 包中包含的默认 PostgreSQL 数据库可以处理最多 10,000 用户的工作负载。此外,如果你希望使用冷备份设置来创建一个灾难恢复(DR)计划,你可以使用特定的故障切换机制。
一个常用的技术是创建一个冷备数据库(PostgreSQL 数据库 2)在另一个站点,如下图所示:

当你希望扩展或增加应用服务器的数量时,也需要扩展数据库。数据库扩展有三个重要方面。首先,你需要尽可能高效地扩展数据库客户端连接。为此,你可以使用 PgBouncer,它是一个轻量级的连接池管理器。
其次,你需要有多个数据库实例,其中一个是主节点,并将数据从主节点复制到从节点。在前述的灾难恢复(DR)情况下,这是通过 PostgreSQL 内建的基本复制机制完成的。在当前情况下,使用了一个特定的工具 repmgr,这是一种用于 PostgreSQL 集群化和处理故障切换的工具。
最后,像Consul这样的服务发现工具可以用来检测每个节点的 PostgreSQL 状态,并更新决定连接哪个 Postgres 实例的 PGbouncer 服务设置。
生成的架构如下所示:

如你所见,设置 GitLab 数据库有不同的方式。前面图中突出的架构将在本书扩展服务器基础设施(高可用性设置)章节中作为构建高可用性环境的示例。
GitLab CI
GitLab CI 是一个帮助执行软件组件持续集成(CI)的功能。当多个开发人员使用版本控制系统协同工作时,如果某个开发者的更改破坏了整个产品,就可能会出现问题。确保这种情况发生的概率较低,或者至少能尽早发现的最佳方法是更频繁地使用集成测试,因此称为持续集成。
GitLab CI 于 2013 年作为一个独立项目推出,但后来被集成进了主 GitLab 包中。结合 GitLab Runner 软件,这一功能在开发者中非常受欢迎,也是推动业务发展的重要因素。它还使 GitLab 能够将其产品打造为一个不仅可以进行 CI,还能够进行持续交付,甚至覆盖到生产环境的解决方案。目前 GitLab 的产品愿景是成为一个完整的 DevOps 生命周期产品,从创意到生产。
Forrester 在The Forrester Wave: Continuous Integration Tools, Q3 2017中将 GitLab 评为持续集成(CI)领域的领导者。如下图所示:

反馈是极限编程(XP)运动中一个重要的方面,也是 GitLab CI 的一个关键元素。它也作为开发者之间沟通的一种方式。
Pipeline 和作业
Pipeline 和构建作业是现代持续集成/持续交付(CI/CD)系统的基本构件。在 GitLab 中,启动一个 pipeline 非常简单。你只需将一个.gitlab-ci.yml文件添加到你的项目中,然后在每次提交/推送到代码库时,pipeline 便会启动。每个项目都有一个 pipeline 概览;你可以在左侧菜单栏中的 CI/CD 下找到它:

或者,你可以通过访问 Pipeline 的作业页面查看所有作业,如下图所示:

你可以通过点击作业的状态(例如,失败或成功)查看作业的日志。你可以调试为什么某些作业失败,并准确查看发生了什么:

使用 pipeline 和作业进行 CI/CD 的重要性不容忽视。在本节中,你看到了 GitLab 中 pipeline 的基本界面,但在接下来的几章中,将对此进行更详细的讨论(使用 GitLab CI 和 CI Runner部分)。
GitLab Runners
GitLab Runners 最初由 Kamil Trzciński 于 2015 年开发。现在它们已成为 GitLab 最受欢迎的功能之一。
初始的 GitLab-CI-Runner 是一个用 Ruby 编写的非常简单的应用程序,但在相当基础的设置中运行良好。你可以将它看作是一个裸跑者可能是什么样子的参考实现。
旧版 runner 的问题
旧版 runner 的主要问题是它每次只能运行一个并发作业。如果你想运行更多作业,你只能设置一台新服务器或创建一个额外的用户来构建作业。
其次,它始终在服务器 Shell 中运行项目。这使得使用不同版本的 Ruby 或其他依赖项测试项目变得非常困难。它不是无状态的,这意味着你有一个被污染的构建环境。因此,构建结果并不可靠。如今,每次都拥有一个无状态且干净的构建环境至关重要。
旧版 Runner 的另一个缺点是它只能在基于 Linux 的平台上运行。为了让它在 macOS 上运行,GitLab 用户平台之一,你需要进行额外的修改。对于微软 Windows 的支持更是不可想象。
最后,曾经有一些繁重的管理负担。服务器很难扩展,因为设置一个新服务器需要很长时间,因为你需要处理依赖关系,才能构建项目。
更新后的 Runner 是一个二进制文件,可以放置在任何类型的机器上。它非常容易设置为服务,并且能够与多个项目和多个 GitLab CI 协调器一起工作。它还支持 Docker,使得以不同版本设置构建环境变得非常简单。
切换到 Go
Go(或 Golang)是一种新的编程语言(不到 10 年)。它已经被一些令人印象深刻的公司广泛使用,如 Docker (docker.com)、Google、Kubernetes (kubernetes.io) 和 Prometheus (prometheus.io)。Go 是一个多用途工具,可以帮助你在低级别接近操作系统的层面编程,也可以在像 Java 这样的高级语言中编程。它非常适合用来创建系统软件。该语言由 R. Griesemer、R. Pike 和 K. Thompson 在 2009 年为 Google 创建。后者因共同创造了第一版 Unix 实现和 B 编程语言而非常著名。Go 语言最重要的特点是它能够编译出一个不依赖任何库的二进制文件,适用于多个操作系统,如 Linux、macOS、BSD 和 Windows。这也意味着它可以在不同的处理器架构上运行(i386、amd64、ARM 和 PowerPC)。
以下是 Go 的一些优点:
-
非常优秀的标准库(还有很多可选的库可以在其他地方找到)。
-
在 Go 中开发和测试非常快速。
-
文化/社区倾向于选择简单的解决方案而非复杂的解决方案(这很好)。
-
很酷的工具,如 Gofmt、竞争检测器和
go vet。 -
为并发设计——例如,你可以使用 goroutine 和 channels。
-
类型安全——可以避免许多运行时错误和错误的类型定义。
-
垃圾回收——虽然使用 C 的程序员知道如何清理内存,但这仍然是有帮助的。
-
闭包或匿名函数——启用使用函数式编程原则(高阶函数)。
所有这些特点使 Go 成为 GitLab Runners 的完美选择。使用 Go,你可以创建一个相对较小的二进制文件,能够在多个平台上运行。它包含了运行项目所需的一切。
在 GitLab 环境中,作业由 Runner 执行。它们按照 .gitlab-ci.yml 文件中定义的方式运行。Runner 本身可以运行在 虚拟机(VM)上,例如 VmWare(VM)、VPS、笔记本电脑、Docker 容器或 Kubernetes 集群中。通信是单向的,从 Runner 到 GitLab,主要通过 HTTP API 进行,因此该路径必须对 Runner 可访问。
.yml 文件定义了你的 CI/CD 流水线有哪些阶段,以及每个阶段要做什么。通常包括构建、测试和部署阶段。
GitLab 在其手册中提到 无聊 作为减少复杂性的一种有价值的方式;参见 about.gitlab.com/handbook/values/#efficiency。
项目可以在 gitlab.com/gitlab-org/gitlab-runner 找到:

执行器可以特定于某个项目,或者它可以服务于 GitLab 中的多个项目。如果它服务于所有项目,它被称为共享执行器。GitLab Runners 实现了许多 执行器,可以在不同场景下用于你的构建:
-
Shell 执行器:执行器简单地执行一个 shell。构建的依赖项需要手动安装。
-
基于 Docker 的执行器:执行器从容器中运行。这使得创建干净的构建变得更容易,因为依赖管理被转移到容器镜像中。它还使得创建需要互相依赖的服务的构建环境变得更容易,例如 PostgreSQL。
-
自动扩展 Docker SSH:Docker 机器使用 Docker 引擎创建实例以运行 Docker 容器。
-
Kubernetes:GitLab Runner 可以使用 Kubernetes 在 Kubernetes 集群上运行构建。
在过去的几年中,Runners 发展了很多。GitLab 本身将它们视为其套件中最重要的组件之一。本节为你提供了更多关于这一流行工具发展的见解。
云原生
在 2016 年末和 2017 年初,GitLab 社区就是否从云迁回裸金属服务器展开了公开辩论,讨论是否对 GitLab.com 更具成本效益。当时,用于存储仓库的文件系统是 Ceph。这个分布式文件系统的性能不足以支撑 GitLab.com。他们向社区征求建议,收到了许多有类似经历的人的反馈。最终,决定继续留在云端(about.gitlab.com/2017/03/02/why-we-are-not-leaving-the-cloud/)。GitLab 反而决定专注于创建一个解决方案,重点不再是文件系统层面,而是确保 Git 输入/输出(I/O)行为在应用层面得到更好的管理。这也可以看作是 Gitaly 组件的诞生。Sid Sijbrandij 强调,GitLab 作为一家软件公司,而非基础设施公司,这一点非常重要。
2018 年 8 月,GitLab 将其基于云的服务,GitLab.com,从 Azure 迁移到 Google Cloud Platform(GCP)。根据 CEO Sid Sijbrandij 的说法,迁移至 GCP 的主要原因如下:
“作为一个公共云,Google 比其他云服务提供商有更多的经验,因为他们基本上是为自己打造了一套云 [...] 你可以在网络方面看到这一点,他们的网络质量超越了其他所有人。它更可靠,延迟更低,而且真的非常令人印象深刻,我们很高兴开始在上面托管 GitLab.com。”
看起来这次调整是值得的;用户反馈称,GitLab.com明显变得更快了。另一个很可能会带来进一步加速的转变是使用 Kubernetes 作为容器编排器。这是他们战略中一个重要的部分,旨在将更多功能融入到 GitLab 中,除了自动扩展 GitLab 运行器之外。GitLab 自己的高可用工具 GEO 被用来将数据从一个云同步到另一个云。运行在 Google 架构上的 GitLab 也可以利用对象存储来实现一些特定功能,例如 Git LFS。
总结
在这一章中,我们了解了 GitLab 背后的人和组织。从一开始,我们展示了该项目多年来的发展历程。我们详细讲解了 GitLab 的核心组件以及如何安装它们。对于某些组件,我们还介绍了调试安装的方法。
我们还简要介绍了 GitLab CI 以及与其交互的客户端程序,例如 GitLab Runner。我们展示了这个功能为何如此重要,以及 IT 行业如何看待它。
在下一章中,我们将介绍如何在不同系统上安装和配置 GitLab。如果你是该产品的新用户,准备好惊叹吧!
问题
-
GitLab 最初是由谁和何时开发的?
-
GitLab 是如何获得资金的?
-
列举所有在 GitLab 软件中使用的编程语言。
-
GitLab 使用了哪些许可证?
-
为什么他们使用这些许可证?
-
列举 GitLab 的核心组件。
-
GitLab 有多少个办公室?
-
Redis 中存储了什么?
-
Gitaly 替代了什么?
-
GitLab 在 2018 年选择了哪个云服务作为重点?
进一步阅读
-
Sidekiq—源代码和文档:
github.com/mperham/sidekiq -
Ruby on Rails:
rubyonrails.org -
Unicorn:
thorstenball.com/blog/2014/11/20/unicorn-unix-magic-tricks/ -
云原生编程与 Golang 由 Mina Andrawos、Martin Helmich 编著:
www.packtpub.com/in/application-development/cloud-native-programming-golang -
Nginx HTTP 服务器 - 第四版 由 Clement Nedelcu、Martin Fjordvald 编著:
www.packtpub.com/in/virtualization-and-cloud/nginx-http-server-fourth-edition -
精通 Redis 由 Jeremy Nelson 编著:
www.packtpub.com/in/big-data-and-business-intelligence/mastering-redis -
PostgreSQL 管理手册, 9.5/9.6 版 由 Simon Riggs、Gianni Ciolli、Gabriele Bartolini 编著:
www.packtpub.com/in/big-data-and-business-intelligence/postgresql-administration-cookbook-9596-edition
第二章:安装 GitLab
在本章中,我们将讨论几种安装 GitLab 的方法。我们将从推荐的安装方式开始,即使用 omnibus 安装器在你自己的机器上安装 GitLab。其次,我们将展示如何从 GitLab 的源文件进行完整安装。所有这些操作将在 Debian 平台上进行。然后,我们将展示如何使用 Kubernetes 编排器这一更现代的应用运行方式。最后,我们将展示如何使用云平台 DigitalOcean 进行安装。DigitalOcean 提供了预定义的 GitLab 镜像,这些镜像使用 omnibus 安装器进行了内部配置。
本章将覆盖以下内容:
-
使用 omnibus 包安装
-
从源文件运行
-
使用 Docker 安装 GitLab
-
使用 Kubernetes 部署 GitLab
-
在 DigitalOcean 上创建 Droplet
技术要求
对于管理 omnibus 安装,有一个名为 gitlab.rb 的中央配置文件。你需要创建它或复制一个示例文件。你可以在 gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template 找到一个模板。升级后该模板不会自动更新。在本章的许多部分,我将引用并讨论此文件的内容。
要跟随本章的指令,请下载在 GitHub 上提供的 Git 仓库:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter02。
虽然 GitLab 可以安装在多种平台上,但在本章中我们选择了 Debian 9 来展示如何进行安装。你可以从 debian.org 下载 Debian。
我们还将需要以下工具:
-
Helm:
helm.sh
安装要求
对于所有类型的安装,都涉及到防火墙/流量问题。Linux 的基本防火墙软件是 iptables,它与 Linux 内核紧密相关。配置 iptables 是相当复杂的,关于这方面有其他书籍。幸运的是,有许多用户友好的程序可供选择,这些程序能够通过与 iptables 交互 来帮助你管理系统防火墙。
Linux UFW(简单防火墙)是一个前端工具。如果你的系统上安装了它,请按照这些指示打开防火墙。
在配置 GitLab 之前,你需要确保防火墙规则足够宽松,以允许 web 流量。
执行以下命令查看当前活动防火墙的状态:
$sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
正如你所见,当前规则允许 SSH 流量通过,但其他服务的访问被限制。由于 GitLab 是一个 Web 应用程序,我们应该允许 HTTP 访问。如果你的 GitLab 服务器有与之关联的域名,GitLab 也可以从 Let's Encrypt 项目请求并启用免费的 TLS/SSL(传输层安全性/安全套接字层)证书来保护你的安装。在这种情况下,我们还需要允许 HTTPS 访问。
由于 HTTP 和 HTTPS 的协议到端口的映射可以在 /etc/services 文件中找到,我们可以按名称允许该流量。如果你还没有启用 OpenSSH 流量,现在也应该允许该流量:
$sudo ufw allow http
$sudo ufw allow https
$sudo ufw allow OpenSSH
如果你再次检查 ufw status 命令,你应该会看到至少这两项服务的访问已被配置:
$sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
80 ALLOW Anywhere
443 ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
80 (v6) ALLOW Anywhere (v6)
443 (v6) ALLOW Anywhere (v6)
上述输出表明,一旦我们配置好应用程序,GitLab 的 Web 界面将可以访问。
使用 omnibus 包安装 GitLab
安装 GitLab 有几种方法。最好的方法是使用 omnibus 安装程序,这是一种基于 Chef 的配置包。安装程序实际上是来自 Chef 项目的一个分支,地址是 github.com/chef/omnibus。之所以这是最好的安装方式,是因为它为你处理了大量的样板工作。GitLab 安装涉及很多细节,很容易出错。通过 Chef omnibus 自动化这一过程,消除了许多复杂性和潜在的错误。安装程序可以用来在多个平台上安装 GitLab:
-
Ubuntu
-
Debian
-
CentOS(任何 Red Hat 衍生版)
-
OpenSUSE
-
Raspbian
我们将在下面“运行安装程序”部分使用 Debian 作为示例。
Omnibus 结构
全局而言,omnibus 包由以下组成:
-
项目定义
-
单独的软件定义
-
GitLab 配置模板
-
Chef 组件,例如 cookbook 和属性
-
管理服务的 Runit 配方
-
测试
-
最后但同样重要的是,
gitlab-ctl命令
项目定义
此文件包含元数据,并描述项目的详细信息,以及项目中包含的依赖项。你可以在 omnibus 源代码的 config/projects/gitlab.rb 中找到它。
单独的软件定义
在 config/software/ 文件夹中找到,它包含了所有属于 omnibus 安装的软件。例如,如果你想使用 PostgreSQL(关系数据库),你将找到其配置、许可证、依赖项,以及如何构建或获取该软件的说明。有时,可能需要一个补丁,补丁也会被包含进去。
GitLab 配置模板
所有配置指令都从 /etc/gitlab/gitlab.rb 文件中读取,该文件应放置在目标系统上,供 omnibus 应用。你可以使用该文件来调整很多设置。指定设置的标准方法是使用以下内容:
component['settings'] = $value eg. gitlab_rails['webhook_timeout'] = 10
Chef 组件
有几个Chef食谱是 GitLab omnibus 的一部分,是否执行这些食谱取决于你指定的配置。
Runit 配方
GitLab 选择了runit(wiki.archlinux.org/index.php/Runit)作为进程管理器,负责管理通过 omnibus-gitlab 包安装的所有服务。在安装时,它会确定使用的是哪个初始化系统,并确保在启动时适当地调用它。它管理服务的停止、启动、重新加载和启用。
测试
omnibus-gitlab 仓库使用 ChefSpec(行为驱动测试框架)来测试其食谱。测试可能会检查应该存在的文件以及运行命令后的状态。例如,这些测试通常只有在你更改 omnibus-gitlab 安装程序的源代码时才会有意义(gitlab.com/gitlab-org/omnibus-gitlab/)。你可以在 spec 文件夹中找到这些测试。
gitlab-ctl 命令
这是使用 omnibus-gitlab 包时最重要的命令。它在运行安装程序后可用。该工具可以用来管理一般事务,如启动/停止/重新加载所有 omnibus-gitlab 提供的服务,但它在应用 gitlab.rb 配置文件中的更改时也提供了重要功能。不要忘记使用以下命令应用更改:
gitlab-ctl reconfigure
主要命令如下:
-
help(命令帮助) -
cleanse(删除所有数据并重置状态) -
show-config(显示将要创建的配置) -
uninstall(停止所有进程并移除管理进程服务)
服务管理命令如下:
-
hup(发送服务或所有挂起信号) -
kill(发送服务或所有终止信号) -
start/restart/stop(发送服务或所有命令) -
status(测试并报告指定服务或所有服务的状态) -
tail(查看所有服务的日志)
使用 omnibus-gitlab 包进行升级
通常,如果你在现有安装上部署该包,它将自动升级已安装的组件。对于 GitLab 12,PostgreSQL 数据库将自动升级到版本 10.7,除非你创建一个名为 /etc/gitlab/disable-postgresql-upgrade 的文件。升级时,请始终阅读发行说明中的特别说明。对于版本 12,相关说明请见这里:docs.gitlab.com/omnibus/update/gitlab_12_changes.html。
运行安装程序
以下将展示如何在 Debian Linux 上运行 omnibus-gitlab 安装程序。在运行安装包之前,我们需要准备一些事情:
- 我们需要设置国际化设置并安装一些软件包(curl、openssh-server 和默认的 SSL 根证书):
sudo apt-get update
export LANGUAGE=en_US.UTF-8
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
sudo locale-gen en_US.UTF-8
sudo apt-get install -y curl openssh-server ca-certificates
- 在使用 GitLab 时,配置电子邮件通知也是很重要的。通常,这通过 Postfix 完成,但您也可以使用其他解决方案并将 GitLab 指向它(外部 SMTP(简单邮件传输协议)):
sudo apt-get install -y postfix
-
最佳选项是在被询问时选择 Internet Site,并使用您的外部主机名作为邮件名称。其余选项,接受默认设置即可。
-
添加 GitLab 包仓库并安装该包。
-
使用以下
curl命令,您可以安装 GitLab 包仓库,并通过下载一个包来启动安装:
curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
- 下一步是执行包安装步骤。您可以将
EXTERNAL_URL变量设置为您的新 GitLab 实例的 URL。如果您指定一个 https:// 的 URL,安装程序将尝试使用 Let's Encrypt 来生成证书。此服务免费使用(letsencrypt.org/),但需要有效的主机名(将进行验证)和可以从互联网访问的端口 80。您还可以指定一个普通的 http:// URL,此时不使用 Let's Encrypt。安装命令如下:
sudo EXTERNAL_URL="http://gitlab.example.com" apt-get install gitlab-ee
浏览到外部 URL 并登录
如果您是第一次使用它,您将看到一个密码重置表单。您可以为初始管理员账户指定密码,密码保存后,您将被引导到登录界面。使用刚刚选择的管理员凭据登录。
在接下来的章节中,包含了有关如何配置 GitLab 和后续操作的详细信息和说明。
从源代码运行
在从源代码安装时,请确保您已查看适用于您的平台的最新安装指南,特别是您想要的 GitLab 版本(例如,12-0)。本书中的说明最终会过时。此外,如果遇到问题,您可以尝试在 GitLab 论坛上找到答案:forum.gitlab.com/c/troubleshooting。如果问题是 GitLab 的 Bug 或不期望的行为,您可以在 gitlab.com/gitlab-org/gitlab-ce/issues 上开一个问题。以下部分将展示适用于 Debian 版本的具体安装说明。
操作系统 – Debian 9
在这里,您将找到关于如何在基于 Debian 的 Linux 上安装 GitLab 的说明。Debian 是最古老的 Linux 发行版之一,约在 25 年前创建。它背后的基金会始终坚持一个原则,即只包含开源 GPL(通用公共许可证)软件。其使用的包管理系统 apt 结合优秀的包维护者,确保了多年来的良好质量。他们使用的过程来决定哪些组件应当包含,创造了一个非常干净的产品。
Debian 成为了一种基础发行版,其他的则从它分支并进行了扩展。在 2016 年,约有 125 种基于 Debian 的发行版。
以下安装指令是为 Debian 操作系统创建并测试的。如果要在 Red Hat Enterprise Linux (RHEL) 或其姊妹操作系统 Community Enterprise Operating System (CentOS) 上安装,建议使用 omnibus 包。
以下指令适用于大多数人。许多人会遇到权限问题,因为他们改变了目录的位置或以不同的用户身份运行服务。
首先,我们将开始解释在安装 GitLab 之前需要安装的基本软件包。然后,我们会讲解所需编程语言的安装。
一旦这些步骤成功完成,我们将通过准备 SQL 数据库和 GitLab 内存数据库继续安装。
最后,我们将开始安装 GitLab 应用程序组件。
在安装过程中,你需要编辑多个配置文件。确保你有一个可用的编辑器。最常用的是 vim(人们对此有不同看法),你可以像这样安装:
sudo apt-get install -y vim
sudo update-alternatives --set editor /usr/bin/vim.basic
所需的基本软件包
首先,将区域设置为你喜欢的语言(我使用英文 UTF-8,即 8 位 Unicode 转换格式)。这些设置默认在我的 Debian 系统中是不存在的:
export LANGUAGE=en_US.UTF-8
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
sudo locale-gen en_US.UTF-8
然后,使用以下命令安装所需的软件:
sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libre2-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate rsync python-docutils pkg-config cmake wget
确保你安装了正确版本的 Git。使用以下命令安装 Git:
sudo apt-get install -y git-core
确保 Git 版本为 2.9.5 或更高版本:
git --version
安装邮件服务器,但不要使用 Exim。使用 Postfix 更加合理:
sudo apt-get install -y postfix
然后,选择 Internet Site 并按 Enter 键确认主机名。
所需的编程语言
GitLab 需要几个编程语言才能正常运行。你需要安装它们,以便使用所有功能。
Ruby
由于 GitLab 主要是用 Ruby 编写的,我们需要安装这个语言。如果操作系统中存在旧版 Ruby 1.8,请移除:
sudo apt-get remove ruby1.8
下载最新版本的 Ruby,检查签名并进行编译:
$ wget https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.3.tar.gz
$ shasum ruby-2.6.3.tar.gz 2347ed6ca5490a104ebd5684d2b9b5eefa6cd33c ruby-2.6.3.tar.gz
$ tar xvzf ruby-2.6.3.tar.gz
..
$ cd ruby-2.6.3
$ ./configure --disable-install-rdoc
$ make
$ sudo make install
安装完成后,检查版本:
$ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
接着,安装 Bundler Gem:
$ sudo gem install bundler --no-document --version '< 2'
Fetching: bundler-1.17.3.gem (100%)
Successfully installed bundler-1.17.3
1 gem installed
现在,Ruby 的基础安装已经完成。
Go
GitLab 的新部分是用 Go 编写的(有时称为 Golang)。这些部分自 GitLab 8.0 版本以来就已存在,因此我们也需要这个语言编译器,以便运行新版 GitLab。最好从这里下载 Go 的最新版本:golang.org。下载后,确保校验和正确(对于 linux-amd64 页面,Go 11.10 的校验和是 aefaa228b68641e266d1f23f1d95dba33f17552ba132878b65bb798ffa37e6d0)。我们将其安装在/usr/local/bin 位置:
$ wget https://dl.google.com/go/go1.11.10.linux-amd64.tar.gz
$ shasum -a256 go1.11.10.linux-amd64.tar.gz
aefaa228b68641e266d1f23f1d95dba33f17552ba132878b65bb798ffa37e6d0 go1.11.10.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.11.10.linux-amd64.tar.gz
$ sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
$ rm go1.11.10.linux-amd64.tar.gz
$ go version
go version go1.11.10 linux/amd64
目前,Go 支持八种不同的硬件指令集,因此你有一些选择。你可以在 Go 下载页面找到适用于除 64 位 Linux 之外平台的下载:golang.org/dl/。
Node.js
GitLab 使用 Node.js 来编译 JavaScript,Yarn 用于管理 JavaScript 组件的依赖。由于这些工具发展迅速(有定期的新版本发布),你应该查看当前的要求,访问 about.gitlab.com/。截至 2019 年 4 月,支持的 Node.js 版本应该是 ≥ 8.10.0,Yarn 版本应该是 ≥ v1.10.0。
由于 Linux 发行版中的版本通常滞后,建议从源代码安装。以下代码块展示了如何进行安装:
$ curl --location https://deb.nodesource.com/setup_12.x | sudo bash -
$ sudo apt-get install -y nodejs
$ node -v
v12.6.0
$ curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - OK
$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee \
/etc/apt/sources.list.d/yarn.list
$ sudo apt-get update
$ sudo apt-get install yarn ...
$ yarn -v
1.17.3
你可以在 yarnpkg.com/en/docs 查找关于 Yarn 的更多信息,Node.js 的文档可以在 nodejs.org/en/docs/ 找到。
系统用户:
为 GitLab 创建一个没有登录 shell 的 Git 用户,并在 GECOs 字段提供一个常见名称(GECOS = 旧 Unix 打印机的年龄标记):
$ sudo adduser --disabled-login --gecos 'GitLab user' git
Adding user `git' ...
Adding new group `git' (1001) ...
Adding new user `git' (1001) with group `git' ...
Creating home directory `/home/git' ...
Copying files from `/etc/skel' ...
结果是添加了一个名为 git 的用户,创建了一个名为 git 的组,建立了主目录,并从 /etc/skel 复制了一些模板文件到主目录。
SQL 数据库:
你确实应该使用 PostgreSQL 数据库,正如在 第一章《介绍 GitLab 架构》中所解释的那样。对于 MySQL(另一种 SQL 数据库),请参考 MySQL 设置指南。
使用以下命令安装数据库软件包:
$ sudo apt-get install -y postgresql postgresql-client libpq-dev postgresql-contrib
Setting up postgresql (9.6+181+deb9u2) ...
Setting up postgresql-client (9.6+181+deb9u2) ...
Setting up postgresql-contrib-9.6 (9.6.10-0+deb9u1) ...
Setting up postgresql-contrib (9.6+181+deb9u2) ...
Processing triggers for systemd (232-25+deb9u4) ...
Processing triggers for libc-bin (2.24-11+deb9u3) ...
启动数据库引擎:
$ sudo service postgresql start
为 GitLab 创建一个数据库用户:
$ sudo -u postgres psql -d template1 -c "CREATE USER git CREATEDB;"
CREATE ROLE
创建 pg_trgm 扩展(GitLab 8.6+ 所需):
$ sudo -u postgres psql -d template1 -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
CREATE EXTENSION
创建 GitLab 生产数据库并授予该数据库所有权限:
$ sudo -u postgres psql -d template1 -c "CREATE DATABASE gitlabhq_production OWNER git;"
CREATE DATABASE
尝试使用新用户连接到新数据库:
$ sudo -u git -H psql -d gitlabhq_production
Postgresql (9.4.22) Type “help” for help.
gitlabhq_production=>
通过在数据库控制台粘贴或键入此内容来检查是否启用了 pg_trgm 扩展:
SELECT true AS enabled
FROM pg_available_extensions
WHERE name = 'pg_trgm'
AND installed_version IS NOT NULL;
如果扩展已启用,将产生以下输出:
enabled
-------
t
(1 row)
现在,我们设置数据库密码:
gitlabhq_production=> \password git
Enter new password: <type a password>
Enter it again: <type again this password>
gitlabhq_production=> \q
使用 \q 退出数据库控制台。保存这个密码以备后用,在配置 GitLab 安装时使用。
在 PostgreSQL 主配置文件中创建条目:
$ vi /etc/postgresql/9.6/main/postgresql.conf
将监听地址更改为 *,或在现在显示为 localhost 时更改 IP 并取消注释:
listen_addresses = '*'
在 PostgreSQL 主机文件中创建条目:
$ sudo vi /etc/postgresql/9.6/main/pg_hba.conf
添加如下行:
host gitlabhq_production git <ip of gitlab server>/32 md5
保存主机文件后,重启数据库实例以使设置生效:
$ sudo service postgresql restart
数据库现在已为 GitLab 准备就绪。
Redis 内存数据库:
在上一章中,我们讨论了 Redis 及其工作原理。
安装 GitLab 至少需要 v2.8 版本的 Redis,可以通过 apt 在 Debian 上轻松安装:
$ sudo apt-get install redis-server
配置 Redis 使用套接字:
$ sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.orig
通过将 port 设置为 0,禁用 Redis 监听 传输控制协议(TCP):
$ sudo sed 's/^port .*/port 0/' /etc/redis/redis.conf.orig | sudo tee /etc/redis/redis.conf
在 Debian 和类似发行版上启用 Redis 套接字的默认路径:
$ sudo echo 'unixsocket /var/run/redis/redis.sock' | sudo tee -a /etc/redis/redis.conf
授予 Redis 组所有成员对套接字的权限:
$ sudo echo 'unixsocketperm 770' | sudo tee -a /etc/redis/redis.conf
创建包含套接字的目录(如果存在则无妨):
$ sudo mkdir /var/run/redis
$ sudo chown redis:redis /var/run/redis
$ sudo chmod 755 /var/run/redis
如果适用,请持久化包含套接字的目录:
echo 'd /var/run/redis 0755 redis redis 10d -' | sudo tee -a /etc/tmpfiles.d/redis.conf
fi
激活对 redis.conf 的更改:
$ sudo service redis-server restart
将 Git 添加到 Redis 组:
$ sudo usermod -aG redis git
我们现在有了一个可以与 GitLab 一起使用的功能正常的 Redis 服务器。
GitLab
我们将在 git 用户的主目录中安装 GitLab:
$ cd /home/git
克隆源代码:
$ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 12-0-stable gitlab
Cloning into 'gitlab'...
remote: Enumerating objects: 1234071, done.
remote: Counting objects: 100% (1234071/1234071), done.
remote: Compressing objects: 100% (369844/369844), done.
remote: Total 1234071 (delta 937064), reused 1101079 (delta 849256)
Receiving objects: 100% (1234071/1234071), 529.69 MiB | 5.58 MiB/s, done.
Resolving deltas: 100% (937064/937064), done.
转到 GitLab 安装文件夹:
$ cd /home/git/gitlab
复制示例 GitLab 配置:
$ sudo -u git -H cp config/gitlab.yml.example config/gitlab.yml
更新 GitLab 配置文件并按照文件顶部的说明操作:
$ sudo -u git -H editor config/gitlab.yml
复制示例秘密文件:
$ sudo -u git -H cp config/secrets.yml.example config/secrets.yml
$ sudo -u git -H chmod 0600 config/secrets.yml
确保 GitLab 可以写入 log/ 和 tmp/ 目录:
$ sudo chown -R git log/
$ sudo chown -R git tmp/
$ sudo chmod -R u+rwX,go-w log/
$ sudo chmod -R u+rwX tmp/
确保 GitLab 可以写入 tmp/pids/ 和 tmp/sockets/ 目录:
$ sudo chmod -R u+rwX tmp/pids/
$ sudo chmod -R u+rwX tmp/sockets/
创建 public/uploads/ 目录:
$ sudo -u git -H mkdir public/uploads/
现在,由于 public/uploads 中的文件由 GitLab-Workhorse 提供服务,请确保只有 GitLab 用户可以访问 public/uploads/ 目录:
$ sudo chmod 0700 public/uploads
更改存储 CI 作业跟踪的目录的权限:
$ sudo chmod -R u+rwX builds/
更改存储 CI 产物的目录权限:
$ sudo chmod -R u+rwX shared/artifacts/
更改存储 GitLab 页面文件的目录权限:
$ sudo chmod -R ug+rwX shared/pages/
复制示例 Unicorn 配置:
$ sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
查找核心数量:
$ nproc
如果预计会有高负载实例,请启用集群模式。将工作线程数设置为至少与核心数量相同。例如,对于一个 2GB 内存的服务器,将工作线程数更改为 3:
$ sudo -u git -H editor config/unicorn.rb
复制示例 Rack 攻击配置:
$ sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb
需要为 Web 编辑器配置 Git 用户 autocrlf 的 Git 全局设置:
$ sudo -u git -H git config --global core.autocrlf input
禁用 git gc –auto,因为 GitLab 已经在需要时运行 git gc:
$ sudo -u git -H git config --global gc.auto 0
启用包文件位图:
$ sudo -u git -H git config --global repack.writeBitmaps true
启用推送选项:
$ sudo -u git -H git config --global receive.advertisePushOptions true
配置 Redis 连接设置:
$ sudo -u git -H cp config/resque.yml.example config/resque.yml
如果你没有使用默认的 Debian 配置,请更改 Redis 套接字路径:
$ sudo -u git -H editor config/resque.yml
通过复制 PostgreSQL 的模板到 database.yml 来配置 GitLab 数据库设置:
$ sudo -u git cp config/database.yml.postgresql config/database.yml
现在,更新 config/database.yml:
$ sudo -u git -H editor config/database.yml
至少需要更改以下几行:
password: "<your secure password>"
host: <your postgres host>
"<your secure password" 是你在本章 SQL 数据库 部分中创建的密码!主机是你的 PostgreSQL 数据库服务器的主机名或 IP 地址。
使 config/database.yml 文件只对 Git 可读:
$ sudo -u git -H chmod o-rwx config/database.yml
安装 RubyGems(预计会有大量输出):
$sudo -u git -H bundle install --deployment --without development test mysql aws kerberos
...
核心 GitLab 应用程序现在已安装在系统中。我们还需要其他组件,如 GitLab Shell、GitLab Workhorse 和 Gitaly。这些将在接下来的章节中解释。
安装 GitLab Shell
GitLab Shell 是专门为 GitLab 开发的 SSH 访问和仓库管理软件。你可以按如下方式安装:
$ sudo -u git -H bundle exec rake gitlab:shell:install REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production SKIP_STORAGE_VALIDATION=true
默认情况下,gitlab-shell 配置是从你的主 GitLab 配置生成的。你可以按如下方式查看(并修改)gitlab-shell 配置:
$ sudo -u git -H editor /home/git/gitlab-shell/config.yml
启动服务将在稍后执行。
安装 GitLab-Workhorse
GitLab-Workhorse 使用 GNU(Gnu’s Not Unix)make。以下命令将把 GitLab-Workhorse 安装到 /home/git/gitlab-workhorse,这是推荐的安装位置:
$ sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
安装 Gitaly
使用 Git 拉取 Gitaly 源代码并用 Go 编译:
$ sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly,/home/git/repositories]" RAILS_ENV=production
限制 Gitaly 套接字访问:
$ sudo chmod 0700 /home/git/gitlab/tmp/sockets/private
$ sudo chown git /home/git/gitlab/tmp/sockets/private
如果你使用的是非默认设置,需更新 config.toml:
$ cd /home/git/gitaly
$ sudo -u git -H editor config.toml
确保 Gitaly 已启动:
$ sudo -u git bash -c "/home/git/gitlab/bin/daemon_with_pidfile /home/git/gitlab/tmp/pids//gitaly.pid /home/git/gitaly/gitaly /home/git/gitaly/config.toml >> /home/git/gitlab/log/gitaly.log 2>&1 &"
查看/home/git/gitlab/log/gitaly.log中的错误,并检查 Gitaly 进程是否在ps -ax进程列表中。它应该在运行。
初始化数据库并激活高级功能
使用以下命令初始化数据库并激活高级功能:
$ cd /home/git/gitlab
$ sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production force=yes
完成后,你将看到以下内容:
‘Administrator account created:'
你可以继续安装并最终启动 GitLab,然后第一个访问登录页面的人将被要求提供一个新的管理员密码。这可能不是你想要的,因此有一个命令可以在启动之前设置这个密码。你需要提供密码、电子邮件和变量来覆盖数据库检查以使其工作(回答提示中的是):
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=yourpassword GITLAB_ROOT_EMAIL=youremail@gmail.com DISABLE_DATABASE_ENVIRONMENT_CHECK=1
因此,如果你没有设置密码(并且它仍然是默认密码),请在安装完成并第一次登录服务器之前,不要将 GitLab 暴露到公共互联网。
准备系统的最后步骤
在我们启动 GitLab 应用程序之前,还有一些操作需要完成。
备份你的密钥文件(GitLab 存储加密密钥的地方):
sudo cp config/secrets.yml /to/somewhere/safe
安装 System V 初始化脚本:
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
启动时激活 GitLab:
sudo update-rc.d gitlab defaults 21
确保日志文件频繁轮换(以节省磁盘空间):
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
检查 GitLab 及其环境是否正确设置:
$ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
你将获得如下输出:
System information
System: Debian 9.8
Current User: git
Using RVM: no
Ruby Version: 2.5.5p157
Gem Version: 2.7.6.2
Bundler Version:1.17.3
Rake Version: 12.3.2
Redis Version: 3.2.6
Git Version: 2.11.0
..
系统上的所有配置都已设置好以运行 GitLab,并确保在重启后能继续运行。
准备提供服务
我们几乎准备好启动 GitLab 了。首先,我们需要准备前端来提供内容。
编译 GetText PO 文件
使用以下命令编译 GetText PO(便携式对象)文件。这将处理不同语言中的字符串值(你将看到类似的输出):
$sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
Created app.js in /home/git/gitlab/app/assets/javascripts/locale/ja
Created app.js in /home/git/gitlab/app/assets/javascripts/locale/eo
Created app.js in /home/git/gitlab/app/assets/javascripts/locale/zh_HK
Created app.js in /home/git/gitlab/app/assets/javascripts/locale/fil_PH
Created app.js in /home/git/gitlab/app/assets/javascripts/locale/ar_SA
Created app.js in /home/git/gitlab/app/assets/javascripts/locale/en
..
编译资产
使用以下命令通过 Yarn 编译资产(将收到类似的输出):
$sudo -u git -H yarn install --production --pure-lockfile
yarn install v1.15.2
[1/5] Validating package.json...
[2/5] Resolving packages...
...
Done in 48.37s.
最后,使用以下命令编译最后的资产(类似输出):
$sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
warning Resolution field "ts-jest@24.0.0" is incompatible with requested version "ts-jest@²³.10.5"
`yarn:check` finished in 4.2137985 seconds
Created app.js in /home/git/gitlab/app/assets/javascripts/locale/ja
...
启动你的 GitLab 实例
使用以下命令启动你的 GitLab 实例:
sudo service gitlab start
或使用以下命令:
sudo /etc/init.d/gitlab restart
如果成功,你应该在检索状态时看到类似以下内容的输出:
root@93d7eb73dce6:/home/git/gitlab# sudo service gitlab status
Starting GitLab Unicorn
Starting GitLab Sidekiq
Starting GitLab Workhorse
Gitaly is already running with pid 2877, not restarting
The GitLab Unicorn web server with pid 5240 is running.
The GitLab Sidekiq job dispatcher with pid 5312 is running.
The GitLab Workhorse with pid 5290 is running.
Gitaly with pid 2877 is running.
GitLab and all its components are up and running.
主要应用程序正在运行,因此现在我们需要将 NGINX 放在前面作为反向代理。
NGINX
该组件在 GitLab 架构中的角色在第一章中有很好的描述,介绍 GitLab 架构。它作为反向代理,缓冲来自客户端的 HTTP 请求,然后将其发送到 Unicorn 应用程序服务器。Debian 自带的默认 NGINX 版本过旧,无法与 GitLab 一起使用。因此,我们必须安装一个较新的版本(> 1.12.1)。
添加公共仓库密钥和 NGINX 的apt仓库 URL,以便我们可以通过apt-get安装 NGINX 包:
#key
$ cd /tmp/ && wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key
#repo
$ sudo bash -c 'echo "deb http://nginx.org/packages/mainline/debian/ stretch nginx" > /etc/apt/sources.list.d/nginx.list'
$ sudo apt-get update
现在,安装最新版本的 NGINX:
$ sudo apt-get install -y nginx
将我们 GitLab 安装文件夹中的 GitLab 自定义 NGINX 配置文件复制到 NGINX 配置文件夹:
$ cd /home/gitlab/gitlab;sudo cp lib/support/nginx/gitlab /etc/nginx/conf.d/gitlab.conf
如有需要,请更改设置(例如,将server_name YOUR_SERVER_FQDN行更改为 GitLab 应用服务器的 DNS 名称):
$ sudo editor /etc/nginx/conf.d/gitlab.conf
删除默认的 NGINX 配置文件:
$ sudo rm -f /etc/nginx/conf.d/default*
重启 NGINX 以激活配置:
sudo service nginx restart
如果出现错误,请查看/var/log/nginx/gitlab_error.log。现在,你应该能找到正在运行的 GitLab。
通过网页浏览器访问你的新 GitLab 应用服务器进行首次登录。如果你在Run gitlab:setup时没有创建密码,你将看到一个表单来为管理员账户设置密码。默认的username = 'root'可以稍后更改。你现在可以设置密码并重新登录,开始工作!
安装完成!
从 Docker 使用它
未来属于容器。这个观点已经讨论多年,现在几乎已经成为事实。在容器中运行应用程序有许多优势。它需要的操作系统开销要小得多,因为容器共享底层操作系统的容量。GitLab 通过 Docker Hub 提供 GitLab Docker 镜像,Docker Hub 是互联网中官方 Docker 镜像的中央注册库。
GitLab CE 和 EE 都可用,分别称为gitlab/gitlab-ce和gitlab/gitlab-ee。GitLab Docker 镜像是功能完整的 GitLab 镜像,它们在一个容器中运行所有服务。
容器可以在不同的环境中运行,但让我们从以下开始:
-
直接在 Docker 引擎中运行镜像。
-
使用
docker-compose运行 GitLab。
你确实需要 Docker 软件。查看官方安装文档(tuleap-documentation.readthedocs.io/en/latest/developer-guide/quick-start/install-docker.html)了解如何安装。
Docker 在 Windows 上不受官方支持。你可能会遇到卷权限和其他未知问题。请自行承担风险,并在论坛的互联网中继聊天(IRC)中寻求帮助。
直接运行镜像
在运行镜像之前,请确保有一个目录用于存储配置、日志和数据(或准备好丢失数据)。通常,我们会在主文件夹中创建目录,但更好的做法是使用文件系统层次标准(FHS),这是一个社区支持的标准,规定了文件的存放位置。/src看起来很适合存储容器数据(请参见tldp.org/LDP/Linux-Filesystem-Hierarchy/html/srv.html)。GitLab 在他们的示例中也使用了这个约定。
GitLab 容器使用宿主机挂载的卷来存储持久化数据:
| 本地目录 | 容器位置 | 用途 |
|---|---|---|
/srv/gitlab/data |
/var/opt/gitlab/data |
用于存储应用数据。 |
/srv/gitlab/logs |
/var/log/gitlab |
用于存储日志。 |
/srv/gitlab/config |
/etc/gitlab |
用于存储 GitLab 配置文件。 |
如果你想使用其他本地目录也是可以的,但容器的位置对于 GitLab 正常运行是必需的。
现在,运行gitlab-ce镜像:
sudo docker run \
--hostname gitlab.joustie.nl \
--publish 443:443 --publish 80:80 --publish 22:22 \
--name gitlab \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
以这种方式运行会让它在前台运行,你将能看到控制台输出。你可以添加 --detach 使镜像在后台运行。
从 --publish(或者简写 -p)开始,会使访问 SSH、HTTP 和 HTTPS 所需的端口可用。所有 GitLab 数据将作为 /srv/gitlab/ 的子目录存储。
添加 --restart always \ 作为选项会使容器在系统重启后自动启动。
如果你使用的是 SELinux,并且不希望它引起权限问题,你可以在你的卷后加上 'Z'(--volume /srv/gitlab/data:/var/opt/gitlab:Z)。Docker 将会执行一个 shell 命令:chcon -Rt svirt_sandbox_file_t 用于该位置。
你可以使用以下命令检查容器是否正在运行:
docker ps
你应该能看到一份正在运行的容器列表,其中包括一个名为gitlab的容器。在这个示例中,你现在可以通过gitlab.joustie.nl访问这个容器.
启动后配置 GitLab
由于 GitLab 提供的容器使用的是官方的 omnibus 包,因此所有配置操作都集中在 gitlab.rb 文件上。
容器内的软件是通过 omnibus GitLab 安装配置的,这意味着 /etc/gitlab/gitlab.rb 在容器内被使用。你可以通过进入容器并使用 shell 编辑该文件:
sudo docker exec -it gitlab /bin/bash
另一种方法是直接在 Docker 命令中编辑 gitlab.rb 文件:
sudo docker exec -it gitlab vi /etc/gitlab/gitlab.rb
你还需要在 gitlab.rb 文件中将 external_url 设置为有效的值,以确保 GitLab 中的仓库链接能够正确工作。在那里,你还可以检查其他设置,比如启用 HTTPS,以及一个非常重要的 SMTP 服务器,用于邮件发送。Docker 镜像中不包含 SMTP 服务器。
当你完成所需的更改后,你需要重新启动容器以重新配置 GitLab(每次重启时都会这样做):
sudo docker restart gitlab
使用配置设置作为输入启动容器
你可以通过在docker run命令中添加GITLAB_OMNIBUS_CONFIG环境变量来启动 GitLab 容器并让其在启动时自行配置。
在其中加入你希望的任何 gitlab.rb 设置,它们将在容器启动过程中加载,优先于内部的 gitlab.rb 文件。以下是 omnibus-gitlab 模板中的一些示例,你可以将其作为参数添加到 docker:
--env GITLAB_OMNIBUS_CONFIG="external_url '
external_url 'GENERATED_EXTERNAL_URL' \
gitlab_rails['smtp_enable'] = true \
gitlab_rails['smtp_address'] = "smtp.server" \
gitlab_rails['smtp_port'] = 465"
gitlab_rails['gitlab_shell_ssh_port'] = 2222
下面是一个示例,它在启动容器时设置外部 URL 并设置 SMTP 服务器地址:
sudo docker run --detach \
--hostname gitlab.joustie.nl \
--env GITLAB_OMNIBUS_CONFIG="external_url 'http://gitlab.joustie.nl'; gitlab_rails['smtp_address'] = "smtp.server" " \
--publish 443:443 --publish 80:80 --publish 22:22 \
--name gitlab \
--restart always \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
你可以添加更多环境变量,相关文档可以参考:docs.gitlab.com/ee/administration/environment_variables.html。
容器可能需要一些时间才能投入运行。启动并配置后,可以通过浏览器访问 GitLab,地址为https://localhost。
第一次看到 GitLab 登录页面时,需要设置管理员密码。选择并提交密码后,您可以使用该密码登录。
升级 GitLab
即使在容器中,升级 GitLab 有时也是必要的。简单的方法如下:
- 停止当前活动的容器:
sudo docker stop gitlab (or sha)
- 移除现有实例:
sudo docker rm gitlab (or sha)
- 拉取新的镜像:
sudo docker pull gitlab/gitlab-ce:latest
- 按照之前的方式重新创建容器:
sudo docker run --detach \
--hostname gitlab.joustie.nl \
--publish 443:443 --publish 80:80 --publish 22:22 \
--name gitlab \
--restart always \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
当容器重新启动时,它将重新配置并更新自身(它将执行gitlab-ctl reconfigure)。
在不同的 IP 地址上运行 GitLab CE
使用相同的--publish机制,您不仅可以指定端口,还可以指定 Docker 使用的 IP 地址。
要在 IP 地址 192.168.1.1 上运行最新的 GitLab CE,请使用以下命令:
sudo docker run --detach \
--hostname gitlab.joustie.nl \
--publish 192.168.1.1:443:443 \
--publish 192.168.1.1:80:80 \
--publish 192.168.1.1:22:22 \
--name gitlab \
--restart always \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
现在,GitLab 可通过 http://192.168.1.1 和 https://192.168.1.1 访问。在 使用 Docker Compose 安装 GitLab 部分可以找到一个使用不同端口的 docker-compose.yml 示例。
调试容器
有时,容器的行为可能与您预期的不符。如何调试此问题?
首先,您可以检查容器日志:
sudo docker logs gitlab
进入运行中的容器:
sudo docker exec -it gitlab /bin/bash
现在您拥有 GitLab 容器的 root 访问权限,您可以查看容器的状态,仿佛您在运行 omnibus-gitlab 的虚拟机中一样。
使用 Docker Compose 安装 GitLab
Docker Compose 用于将多个容器作为一个服务运行。通过使用此工具,您可以轻松管理基于 Docker 的 GitLab 安装。它可用于配置、安装和升级服务。它是基于 Python 的,可以从 docs.docker.com/compose/install/ 安装。
如果您已安装 Docker Compose,或系统中已有它,您可以构建您的服务。
创建一个 docker-compose.yml 文件(或下载一个示例):
web:
image: 'gitlab/gitlab-ce:latest'
restart: always
hostname: 'gitlab.joustie.nl'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://gitlab.joustie.nl'
ports:
- '80:8080'
- '443:4443'
- '22:2222'
volumes:
- '/srv/gitlab/config:/etc/gitlab'
- '/srv/gitlab/logs:/var/log/gitlab'
- '/srv/gitlab/data:/var/opt/gitlab'
检查端口设置。这与使用 --publish 80:9090 或 -p 2224:22 配合纯 docker 使用时相同,而不是使用 docker-compose。
确保您位于与 docker-compose.yml 文件相同的目录,并运行以下命令:
docker-compose up -d
GitLab 将在启动时运行 omnibus-gitlab 配置重新配置,以设置 GitLab。要在启动时添加更多配置设置,请按照之前提到的说明将指令添加到 GITLAB_OMNIBUS_CONFIG 变量中。
使用 Docker Compose 更新 GitLab
我们已经看到多种方式来运行 Docker 容器。您可以单独运行它们(纯 Docker),或者创建可以协同工作的容器集(Docker Compose)。下一步是编排容器。
使用 Kubernetes 部署 GitLab
经过数年的不确定性后,谷歌的 Kubernetes 成为领先的容器编排工具。每个主要云供应商都已集成其 API。但这并不意味着它在所有地方的表现都是一样的。由于该产品发展迅速,你可能会注意到不同的表现。
在 Kubernetes 集群上部署 GitLab 最快速的方法是使用 Helm charts。通过 Helm,避免了管理集群中每个单独资源的麻烦,Helm 将这些资源打包成一个应用模型:chart。它就像一个包管理系统,应用程序在其中注册,关于如何安装、配置和升级该应用程序的信息都包含在该包中。
Helm 包含一个名为 Tiller 的服务器,它位于 Kubernetes 集群中,以及与 Tiller 服务器通信的命令行客户端 Helm。
GitLab Runner Helm chart
使用这个 chart,你可以创建可扩展的 GitLab Runner。它将使用 Kubernetes 执行器。当接收到来自 GitLab CI 的新任务时,会在指定的命名空间中创建一个新的 Pod。
首先,添加 Helm 仓库:
helm repo add gitlab https://charts.gitlab.io helm init
在你启动这个 Runner 之前,你需要创建一个 .yml 配置文件(我们将其命名为 values.yml)。你可以参考 gitlab.com/charts/gitlab-runner/blob/master/values.yaml 中的模板文件,模板中已对所有设置进行了解释。
最基本的配置如下:
gitlabUrl: https://gitlab.home.joustie.nl/
runnerRegistrationToken: "dE47NAgHgnFRpdd23RiDJ9JOSzBH40mxqLa1B42Ds5eb94ZWebhPydPt9n"
在配置完 values.yml 后,你可以开始部署。
部署 GitLab Runner 到 Kubernetes
使用以下命令启动部署(将 yournamespace 替换为你喜欢的命名空间):
helm install --namespace yournamespace --name gitlab-runner -f values.yml gitlab/gitlab-runner
几分钟后,你应该能在 GitLab 管理页面的 Runners 部分看到你的 Runner。
GitLab Helm chart
这是在云原生环境中安装 GitLab 的官方推荐方法。该 Helm chart 包含了所有必要的组件,帮助你快速启动,并且可以轻松扩展部署。这个特定的 chart 是在 Kubernetes 集群中运行 GitLab 的最佳方式。
默认部署包含以下内容:
-
核心 GitLab 组件:
-
Unicorn: 预分叉的 Ruby on Rails Web 服务器
-
GitLab Shell: Git 服务器上的 Ruby 封装,支持 Git-over-SSH
-
GitLab Workhorse: 智能反向代理,处理大规模 HTTP 请求
-
Registry: GitLab 容器注册表
-
Sidekiq: GitLab 的后端服务,处理合并请求、电子邮件和其他异步任务
-
Gitaly: Git 操作的存储层抽象
-
-
额外的可选依赖项:
-
Redis: 缓存键值存储,数据库多工具,可以加速处理
-
Minio: 一个兼容 Amazon S3 接口的对象存储服务器
-
-
附加材料:
-
使用 Kubernetes 执行器的自动扩展、非特权 GitLab Runner:如果你通过 Kubernetes 运行 GitLab,那么专用的 GitLab Runner 是设计的一部分。
-
通过 Let's Encrypt 自动配置 SSL:当你向 Kubernetes 提供管理员账户和域名时,内置的 Let's Encrypt 自动化功能可以为你设置 SSL。
-
与 GitLab Docker 镜像一样,GitLab chart 是为核心产品完成的功能,部署只需要几分钟。使用 Helm chart 部署 GitLab 根据硬件或服务位置的不同,通常需要 5 到 10 分钟。也可以将某些组件部署在 Kubernetes 集群外部;这通常是生产环境中的做法。最好将应用状态保持在集群外部。
部署 GitLab 到 Kubernetes 的要求如下:
-
你需要 Helm 版本 >2.9 和
kubectl>1.8(与你的集群版本相差约 1 个小版本)。 -
使用版本 >1.8 且至少包含 6 个 vCPU 和 16 GB RAM 的 Kubernetes 集群。
-
集群可以是基于 Google GKE、Amazon EKS 或 Microsoft AKS 的集群,或者使用 Minikube 等本地集群。
-
你应该能够轻松配置你的域名的通配符 DNS 条目(例如
*.example.com)和外部 IP。 -
你可以连接并登录到集群。
-
已配置并初始化的 Helm Tiller 正在运行。
为确保 Helm 已配置并初始化,运行以下命令:
$ helm repo add gitlab https://charts.gitlab.io/
$ helm repo update
将 GitLab 部署到 Kubernetes
部署 GitLab 需要以下三个参数:
-
global.host.domain:应指向你的通配符 DNS 域名 -
global.hosts.externalIP:集群的外部 IP 地址 -
certmanager-issues.email:用于颁发证书的电子邮件地址(Let's Encrypt)
因此,当你只有这些参数时,直接运行命令:
$ helm upgrade --install gitlab gitlab/gitlab \
--timeout 600 \
--set global.hosts.domain=home.joustie.nl \
--set global.hosts.externalIP=<your external ip> \
--set certmanager-issuer.email=admin@joustie.nl
你也可以使用 values.yml 文件运行部署,和 GitLab Runners chart 一样。你可以在 gitlab.com/charts/gitlab/tree/master/examples 找到示例。
监控部署
在运行 helm upgrade --install 命令后,可能需要几分钟才能返回输出。它的输出应类似如下:
Release "gitlab" does not exist. Installing it now.
NAME: gitlab
LAST DEPLOYED: Wed Jan 2 12:31:31 2019
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/PersistentVolumeClaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
gitlab-minio Bound pvc-fc207fb5-0e81-11e9-b9ef-025000000001 10Gi RWO hostpath 9s
gitlab-postgresql Bound pvc-fc2158a3-0e81-11e9-b9ef-025000000001 8Gi RWO hostpath 9s
gitlab-prometheus-server Bound pvc-fc2240b5-0e81-11e9-b9ef-025000000001 8Gi RWO hostpath 9s
gitlab-redis Bound pvc-fc236cfb-0e81-11e9-b9ef-025000000001 5Gi RWO hostpath 9s
之后(或在另一会话中部署时),你可以运行 helm status gitlab 命令查看部署信息。
初始登录
如果一切顺利,你可以通过将 GitLab 添加到你的通配符 DNS 名称中找到你的安装,例如我们之前的示例,gitlab.home.joustie.nl。
我们尚未为 GitLab 中的初始管理员用户指定根密码。在 Kubernetes 部署过程中,会自动生成一个随机密码。你可以通过以下命令从终端获取此密码(将 name 替换为你的部署名称;对于我们来说,它是 gitlab):
kubectl get secret <name>-gitlab-initial-root-password -ojsonpath={.data.password} | base64 --decode ; echo
外发邮件
如果您未指定,默认情况下没有启用外发邮件。您需要通过指定一些设置来启用它。如果您立即设置选项,以下是 install 命令:
$ helm upgrade --install gitlab gitlab/gitlab \
--timeout 600 \
--set global.hosts.domain=home.joustie.nl \
--set global.hosts.externalIP=<your external ip> \
--set certmanager-issuer.email=admin@joustie.nl \
--set global.smtp.enabled=true \
--set global.smtp.address=smtp.xs4all.nl \
--set global.smtp.port=25
此外,确保没有防火墙阻止流量通过。Google Kubernetes Engine(GKE)上的集群默认会阻止 SMTP 端口。
使用 Helm 图表更新 GitLab
一旦安装了 GitLab 图表,配置更改和图表更新应通过 Helm 升级进行。
如果您想升级 GitLab 或更改设置,请按照以下步骤进行:
#update the chart
helm repo add gitlab https://charts.gitlab.io/
helm repo update
#get the current values
helm get values gitlab > gitlab.yaml
编辑 gitlab.yaml 文件,查看这里的可能值:docs.gitlab.com/charts/installation/command-line-options.html。
保存并应用设置文件:
helm upgrade gitlab gitlab/gitlab -f gitlab.yaml
该命令应该会返回大量输出,但应提到以下内容:
STATUS: DEPLOYED
使用 Helm 图表卸载 GitLab
要卸载 GitLab 图表,请运行以下命令:
helm delete gitlab
之后,您可以运行 helm status 查看操作是否已完成。
在 DigitalOcean 上创建 Droplets
DigitalOcean 是一家源自纽约的云服务提供商,多年来一直是开发者的宠儿。它提供 API、集成以及实惠的定价,帮助您运行应用程序工作负载和虚拟机(VM)。
有两种方式可以在 DigitalOcean 上安装 GitLab。您可以自己创建虚拟机(Droplets)并使用 omnibus 安装程序进行配置,或者自己从源代码安装。更好的方式是使用站点上已提供的预定义 GitLab Droplet 镜像。在创建 Droplet 时,您可以指定此镜像。
登录到 DigitalOcean 后,您可以访问 Droplets 页面并创建新的 Droplet:

确定 Droplet 的选项:

登录后,系统会提示您设置一些选项:

Droplet 已准备好。系统将重启并重新配置。如果登录失败,请通过 SSH 以 root 用户身份登录到您的 droplet 并执行以下命令:
tail -100 /var/log/gitlab_set_pass.log
看看这个:
Could not create the default administrator account:
–> Password is too short (minimum is 8 characters)
如果这个页面可见,我们需要尝试使用 omnibus 包中的一种方法重新设置密码。请将以下行添加到/etc/gitlab/gitlab.rb文件中:
gitlab_rails[‘initial_root_password'] = ‘nonstandardpassword'
然后,执行以下命令以重新填充数据库(它是空的,所以这无关紧要)并重置管理员密码:
gitlab-rake gitlab:setup
经过一段时间后,您应该会收到以下输出:
Administrator account created:
login: root password: You'll be prompted to create one on your first visit.
如果您访问新 GitLab 实例的 URL,您可以按照如下所示设置密码:

享受您的 GitLab!
总结
在本章节中,我们讨论了安装 GitLab 的不同方法。Linux 平台是我们选择的操作系统,并为其提供了相关的指导和示例。我们从最适合大多数组织的安装方式开始,即使用 omnibus 包。
也可以从头开始安装 GitLab 并从源代码运行它。
也可以从 Docker 容器中运行 GitLab。我们还展示了如何更新基于 Docker 的 GitLab 安装,并举了一个使用 Docker Compose 创建多容器安装的例子。最后,我们谈到了扩展时,您可能希望部署并管理多个容器。我们展示了如何使用 Kubernetes 作为编排工具来实现这一目标。
在下一章中,我们将深入探讨初始安装后配置 GitLab 的过程。
问题
-
安装 GitLab 的推荐方式是什么?
-
至少,您需要在防火墙上开放哪些端口?
-
在哪些平台上可以使用 omnibus 包安装 GitLab?
-
在基于 omnibus 的安装中,您使用的基本管理命令是什么?
-
在基于源代码的 GitLab 安装中,最低需要什么版本的 Git?
-
在基于源代码的 GitLab 安装中,您需要启用哪个 PostgreSQL 扩展?
-
官方 GitLab CE Docker 镜像的名称是什么?
-
根据 Linux 文件系统层次结构 (LFH),网站特定数据的位置在哪里?
-
运行 Docker Compose 需要安装什么编程语言?
-
部署 GitLab 组件到 Kubernetes 的推荐方式是什么?
进一步阅读
-
学习 Docker - Docker 18.x 基础 由 Gabriel N. Schenker 编写:
www.packtpub.com/in/networking-and-servers/learn-docker-fundamentals-docker-18x -
在 Kubernetes 上开发和操作微服务 由 Martin Helmich 编写:
www.packtpub.com/in/virtualization-and-cloud/develop-and-operate-microservices-kubernetes-video -
GitLab 安装文档:
about.gitlab.com/install/
第三章:使用 Web UI 配置 GitLab
在上一章安装 GitLab 后,你可能已经有了一个运行中的实例。那么,如何管理它呢?你需要了解如何配置软件。本章将解释如何在不同类型的 GitLab 安装中进行配置。
本章将涵盖以下主题:
-
配置 GitLab 实例级别的设置
-
配置 GitLab 组级别的设置
-
配置 GitLab 项目级别的设置
技术要求
若要管理 omnibus 安装,你需要使用一个名为 gitlab.rb 的中央配置文件。你需要自己创建该文件或复制一个示例配置文件。此配置文件的模板可以在gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template找到。请注意,它在升级后不会自动更新。我们将在本章引用并讨论该文件的部分内容。
为了跟随本章的说明,请下载本书的 GitHub 仓库,并获取其中的示例,下载链接:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter03。
配置 GitLab 实例级别的设置
当你以管理员身份登录 GitLab 时,你会在菜单的右上方看到一个工具图标:

当你点击时,管理员页面将加载,你可以在此访问实例级别的设置。基本页面提供了活动项目、用户和组的概览:

在左侧,有几个全局管理选项。我们来逐一了解它们。
菜单选项
选项是分组的,有些项目甚至可以展开:
-
仪表盘:仪表盘可以让你了解 GitLab 实例中的项目、用户和组的数量。你还可以在此界面创建新的项目、用户或组。仪表盘还显示了其他有趣的统计信息,让你概览当前活动的功能和已安装的组件。如果你启用了 GitLab 实例信息的暴露,它还会提示你是否需要将实例升级到 GitLab 的新版本。
-
项目:在项目面板中,你可以搜索项目并创建新项目。对于搜索选项,提供了一些可用的过滤器。
-
用户:用户面板提供与项目面板相同的功能,即可以通过高级筛选搜索用户并创建新用户。
-
组:组面板在功能上与之前的面板相同,但没有复杂的过滤器。
-
作业:作业面板提供了有关持续集成/持续部署(CI/CD)作业的信息,这些作业可能是待处理的、正在运行的或已完成的。
-
运行器:在此管理页面的这一部分,可以查看有关 CI/CD GitLab 运行器的选项和视图。
-
Gitaly 服务器:默认情况下,只有一个 Gitaly 服务器,并且会在此显示。然而,根据你的设置,可能会有更多服务器。
监控
监控部分提供了管理 GitLab 实例所需的有趣信息。
-
系统信息:CPU、内存及其他指标。
-
后台作业:GitLab 将 Sidekiq 统计信息 gem 集成到应用程序中,可以在此查看。
-
日志:在此部分,你可以查看来自最重要的 GitLab 日志文件(如 unicorn、gitlab-shell、Sidekiq 等)的 2,000 行信息。
-
健康检查:这是系统管理员非常感兴趣的页面。在这里,你将找到一些端点,可以提供有关正在运行的 GitLab 实例健康状况的洞察。此处还存在一个令牌,如果你的监控软件希望抓取该页面,它需要将令牌作为参数发送。
-
请求配置文件:这对于开发人员或测试人员来说很有趣。在这里,你可以向 GitLab 发送请求头,以便进行请求分析。
-
审计日志:如果你拥有企业许可证,可以在这里找到审计事件并进行筛选。
消息
你的 GitLab 实例具有一个功能,允许你向所有用户发送消息。如果你想通知用户有关系统范围的事件,如升级和计划停机,这些广播会很有用。以下是管理员页面,你可以在侧边菜单中找到:

在你安排了新消息后,它也可以在以后重新使用:

系统钩子
GitLab 可以在系统级别执行 HTTP POST 请求并对多个事件进行处理。
当你创建新项目或用户时,会触发一个标准事件。此外,它也可以发送其他类型的事件。只需添加目标 URL 和(可选的)密钥令牌:

当你配置好系统钩子后,页面会有一个下拉列表,可以通过调用 URL 来测试其是否工作。
插件
在此页面上,你还可以配置已安装的插件。这基本上是触发本地安装的程序,而不是调用带参数的 URL。
它要求你将插件代码放置在 /opt/gitlab/embedded/service/gitlab-rails/plugins 中,并且必须以特定方式编写。安装后,插件可以作为钩子运行。
应用程序
在此管理页面的这一部分,你可以注册第三方应用程序,以便使用 GitLab 作为 OAuth 授权提供者。
开放授权(OAuth)是一种开放的授权标准。用户可以允许程序或网站访问他们存储在其他网站上的私人数据,而无需透露用户名和密码。
要注册应用,你需要提供一个名称、回调 URL,并设置一些选项。
受信任意味着基于已验证的资源拥有者凭证交换令牌。随后,在使用该应用时,用户授权步骤将被跳过。
还有一些其他范围被定义,允许特定应用执行各种操作。它们如下:
-
API:授予对 API 的完全读写权限,包括所有组和项目。
-
read_user:授予通过/userAPI 端点对经过身份验证的用户的个人资料的只读访问权限,其中包括用户名、公共电子邮件和全名。还授予对/users下只读 API 端点的访问权限。 -
sudo:当以管理员身份进行身份验证时,授予执行 API 操作的权限,可以作为系统中的任何用户执行操作。 -
read_repository:授予通过 HTTP 使用 Git(而非使用 API)对私有项目中的存储库的只读访问权限。 -
openid:授予使用 OpenID Connect 进行 GitLab 身份验证的权限。它还授予对用户个人资料和组成员身份的只读访问权限。
如果需要,你也可以撤销注册。GitLab 使用 doorkeeper-gem,可以在 github.com/doorkeeper-gem/doorkeeper 找到,以提供此功能。
滥用报告
在 GitLab 网页界面的多个地方,作为用户,你可以举报和报告滥用行为。你可以在以下部分找到举报按钮:
-
评论
-
问题和合并请求
-
用户的个人资料页面(参见以下截图):

当你点击举报滥用按钮时,以下表单将出现:

如果你点击发送报告按钮,管理员将收到通知,他或她将在管理页面的滥用报告部分找到该报告:

许可证
如果你正在使用 GitLab 企业版,这是你管理许可证的地方。你可以上传文件或插入适当的许可证密钥。你还可以浏览 GitLab 购买新的许可证。
Kubernetes
在此部分,你可以添加一个实例范围的 Kubernetes 集群,用于创建部署环境。我们将在第十一章中讨论此选项,发布和配置阶段。
推送规则
在此部分,你可以定义各种规则,允许或禁止 Git 推送:

Geo
如果你拥有企业许可证并且想配置 GitLab 实例的副本,这就是你需要使用的地方。我们将在第二十二章《使用 Geo 创建 GitLab 的分布式只读副本》中讨论 Geo。
部署密钥
在这一部分,你可以注册 SSH 密钥,这些密钥被称为全局共享部署密钥。它们允许在整个 GitLab 安装中的任何仓库上配置只读或读写(如果启用)访问权限。当管理员在此处注册它们时,你可以在你的项目中分配它们,如下图所示:

这个功能可以被远程 CI/CD 服务器用来检出代码。
服务模板
项目服务允许你将 GitLab 与其他第三方应用程序集成。它们类似于你在其他系统中找到的插件。它们在将功能添加到 GitLab 时提供了很大的灵活性。在服务模板部分,你可以编辑预定义模板的信息。然后,仓库所有者在启用服务集成时需要配置更少的信息。
外观
你可以在外观设置页面定义一些 GitLab 实例的外观设置:

例如,如果你想在首页向用户展示一个漂亮的标志,你可以通过上传新标志来更改它。你可以通过点击“选择文件”按钮来上传新的标志,按钮位于“标志”部分附近。完成后,登出并重新登录,你将被重定向到首页:

设置
本节包含很多详细选项,我们将在这里逐一讨论。
一般
该部分管理面板处理的是那些不容易归入某个群组的设置。
可见性和访问控制
在这里,你可以定义用户的默认授权和权限。有三个基本选项。例如,你可以将项目设为私密,这意味着只有你或你授权的人可以看到你创建的新项目。内部级别意味着只有登录用户才能看到你的项目(只读)。公开是访问级别中最广泛的,允许任何人看到你的项目(但不能对其进行写操作)。相同的授权也可以设置用于代码片段和群组。你还可以确定可以导入哪些源。
有很多导入模块可供选择,但也许你希望限制这些模块。另一个导入选项是允许项目导出。有几个功能可以帮助用户实现这一点。如果这不是你想要支持的功能,你可以在这里禁用它。GitLab 支持两种 Git 客户端访问协议(SSH 和 HTTP)。如果你愿意,你也可以在这一部分禁用它们。最后,你可以控制哪些类型的 SSH 密钥可以用于 Git SSH。如果 RSA 太不安全,直接禁用它。
帐户与限制
在此部分,您可以设置会话过期时间(默认为 20 分钟)、项目限制和最大附件大小。默认项目限制设置为 100,000,而存储库的最大大小默认没有设置。附件大小为 10MB。您还可以启用 Gravatars 功能,这意味着上传到 gravatar.com 的图像可以作为您的个人图标。GitLab 可以充当 OAuth 后端,并且您可以允许普通用户通过它注册新应用。
您还可以配置新用户的处理方式。可以将他们定义为外部用户。这意味着新用户只能访问他们被授权访问的特定项目,不能创建新项目。可选地,您可以通过正则表达式为该规则定义例外。最后一个选项是启用提示,以便让用户知道他们尚未上传公共 SSH 密钥。
差异限制
在此部分,您可以设置差异补丁的最大大小,超出该大小将无法在正常的差异视图中显示。如果达到此大小,将显示指向 blob 视图的链接。
注册限制
在默认安装中,GitLab 登录页面上提供了一个表单,供没有帐户的用户注册。您可以在此处禁用注册页面,并编辑白名单和黑名单,提前拒绝某些域的访问。您还可以指定是否在注册程序后发送电子邮件。
注册完成后,将向用户显示一些文本。
登录限制
这些设置涵盖了以下限制:
-
启用或禁用使用密码登录 Web 界面(如果没有密码,则需要使用如 Google 等第三方认证提供者)
-
启用或禁用 Git 通过 HTTPS 的密码验证(如果没有启用,则需要使用个人访问令牌)
-
启用或禁用两步验证(包括一个宽限期,用于确定用户在配置过程中可以等待的时间)
可以使用硬件令牌设备作为第二因素,如下图所示(此功能仅在 Chrome 中有效):

您还可以选择使用代码生成器应用程序,例如 Google 身份验证:

您还可以在此处设置首页 URL,未登录的用户将被重定向到该页面。同样,您可以定义注销后的路径,用户注销后将被引导到该路径。
最后,您还可以设置一个标准的登录文本,以便在登录页面上显示。
服务条款和隐私政策
如果您为一个使用严格条款的组织使用 GitLab,您可以确保用户接受您在此处输入的政策,这将以漂亮的 Markdown 格式呈现。
外部认证
对于某些安装,可能至关重要的是让外部系统对 GitLab 中的访问政策产生更大影响。为此,你可以指定一个外部授权服务来检查用户的信息和分配给项目的分类标签。基于该查询,可能会或不会授予访问权限。如果启用此功能,所有使用跨项目数据的页面将不再工作(例如片段和活动)。
Web 终端
这是一个近期的功能,你可以通过 Web 浏览器访问终端(作为管道的一部分)。在这里,你可以为此终端会话设置超时时间。将其设置为零意味着会话将无限期进行。
Web IDE
Web IDE 是一个全新的功能,仍在全面开发中。这本书实际上就是在它上面编写的,而且它的表现优于大多数 Web 编辑器。你可以从设置页面控制的主要选项是功能切换,它允许客户端 JavaScript 项目使用 CodeSandbox 启用实时预览功能。
获取更多有关项目访问的信息,请访问:codesandbox.io。
集成
推动 GitLab 作为产品发展的方式之一是提供多种与其他产品集成的方法。这也与 Unix 和开源哲学一致,即创建小而可互操作的工具。从技术层面来说,完成这一点有三种方法:
-
使用 Webhooks(事件机制、异步等。欲了解更多信息,请访问:
gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/web_hooks/web_hooks.md)。 -
使用 GitLab API(主动从 GitLab 获取信息)。
-
使用项目集成(从 GitLab 中的仓库运行)。在设置中,有四个可选项:Elasticsearch、PlantUML、Snowplow 和查看第三方服务的能力。
Elasticsearch
仅在企业版中提供的此搜索引擎集成功能非常强大,但它是一个独立的话题。它为 GitLab 提供了全文搜索选项,让你能够在源代码库中搜索文本。你需要安装搜索程序,该程序在另一台服务器上提供 HTTP Web 接口,并指定连接设置(URL):

你还可以限制将被索引的内容:

另一个选项是连接到你在 Amazon 云中运行的 Elasticsearch 实例。如果你已设置,可以在此指定连接设置:

你可以在此查看更多信息:elastic.co。
PlantUML
在这里,你可以定义 PlantUML 的 URL(这是一个 API 集成)。
第三方服务
此设置使您可以选择退出第三方提供的服务。这种提供的一个例子是获取免费的 Google Cloud 积分,以便您可以使用 Google 的 Kubernetes 平台。
Snowplow
再次,Snowplow 是企业功能。一些公司希望在 GitLab 中跟踪自定义事件。使用 Snowplow,您可以使用这个大数据平台来收集和分析数据。如果启用此集成,必须提供收集器 URI、站点 ID 和 Cookie 域。
存储库
在这里,您将找到适用于所有存储库的通用选项。
存储库镜像
使用 GitLab,您可以创建存储库镜像。这意味着,在初始同步之后,内容(以及可能的元数据)会与远程源保持更新。同步作业将自动触发,并在 15 分钟后超时。此设置确定用户是否可以设置镜像。如果禁用,只有管理员可以执行此任务。
存储库存储
最重要的设置处理存储使用的方式。不使用名称的文件夹结构,可以使用散列值作为项目目录名称。这样,在移动项目时,文件夹不会在操作系统级别上移动 - 而是在数据库中改变对散列的引用。保持基于散列的引用树和搜索比通过名称遍历文件夹树快得多。
其次,您可以指定新项目存储的替代位置。如果有多个位置,它们将以没有特定顺序的方式轮换。显示在选择器中的位置取决于在gitlab.rb(适用于独立包安装)或gitlab.yml(基于源的安装)中定义的存储路径。
这里也可以找到存储电路断路器的设置。这用于处理 GitLab 使用的底层存储的故障。当使用网络文件系统如Network File System(NFS)时,可能会出现锁定问题,最终使整个系统无限期地挂起。
存储库维护
Git 具有称为fsck的特殊完整性检查功能。就像文件系统一致性检查(fsck)用于文件系统一样,它可以验证结构并告诉我们是否受到威胁。选择 Git 函数的名称是因为 Git 最初是作为文件系统构建的。文件系统被分类为图模型,Git 将其实现为树和 blob 对象。由于所有项目都有校验和,fsck 可以验证对象及其关系的完整性。此图在以下图表中描述:

仓库中文件之间的变化会被保存为增量,并打包成 pack 文件,然后进行压缩。这同样适用于仓库层次结构中的其他对象。简而言之,这意味着图形模型已经增强,使得 Git 操作在您的计算机上更快、更高效,并节省磁盘空间。有时,在执行commit --amend或git rebase之后,像提交这样的对象变得无法访问(没有父 SHA)。所有前述的使用场景都可以成为 Git 垃圾回收功能的候选项:git gc(git-scm.com/docs/git-gc)。建议定期对您的 Git 仓库运行此功能。
在 GitLab 设置中,您可以控制 Git 在 GitLab 服务器上使用的两种方式,以维护仓库层次结构。第一个选项是,您可以启用周期性的仓库检查,使用git fsck。让 GitLab 执行此操作可确保您能够发现并可能修复那些通常难以发现的磁盘损坏问题,尤其是当您处理所有文件时。
GitLab 还可以执行清理工作。它应该定期运行,以防止仓库损坏。不幸的是,它有时也会产生误报。
第二个选项是,您可以控制服务器上清理工作(housekeeping)的方式,以提高 Git 仓库的效率和速度。您可以启用并控制何时在 GitLab 服务器上执行git gc。另一个用于git pack操作的选项是允许它们使用位图索引。这可能会导致克隆速度更快(但会占用更多磁盘空间)。清理工作参数包括在执行git repack(增量)、git repack(完整)和git gc之前,仓库被推送的次数。
模板
从 GitLab 11.x 开始,您可以定义一个特殊目录,为 GitLab 提供模板。您也可以创建您自己的模板。
自定义项目模板设置允许您指定哪个组是默认组,以便您可以提供模板。
CI/CD
本页包含使用 GitLab 进行持续集成(CI)和持续部署(CD)的若干配置选项。
Auto DevOps 设置
从 GitLab 11.3 开始,Auto DevOps 会自动为所有项目启用。当项目的构建过程被触发并创建了管道时,如果管道失败,Auto DevOps 功能将会被禁用。如果项目中存在替代的 .gitlab-ci.yml 文件,则会使用该文件。您可以在此处覆盖默认的 Auto DevOps 设置并禁用它们:

共享 Runner 设置
CI/CD 的关键组成部分之一是 runners。在这一部分,你可以为所有新项目启用共享 runners,这意味着任何共享 runner 都有可能构建你的代码。这其中有安全方面的考虑,因为某些共享 runner 可能没有使用无状态机制。它们可能在运行你的任务时,不清理生成的工件,导致新构建任务的产生。这可能不是你想要的结果,你的数据有可能会被其他构建项目所泄露。
因此,另一个选项是为共享 runners 设置一些警告文本,以便你确保共享 runners 处于可控状态:

当 runners 构建工件时,结果可以在管道完成后上传并查看。你可能希望为上传到 GitLab CI 的 ZIP 文件设定一个合并工件的大小限制。
你还可以指定构建后工件保持可用的时间。这由默认的工件过期时间控制:

当一个工件达到这个过期时间时,它会在定期的过期任务中被删除。正如我们在下面的截图中看到的,它每小时运行一次:

容器注册表
GitLab 还可以作为容器注册表使用。这里的意思是,它可以存储你在工作站上创建的镜像,或者在 GitLab runner 管道中创建的镜像。在管理员区域的这一部分,你可以指定授权令牌的有效期。默认情况下是五分钟。
报告
大多数人都能轻松识别或解释“报告”这个术语。我个人不理解为什么 GitLab 会将某些选项归类到这个主题下。在我看来,第一个选项,即垃圾邮件和反机器人保护,应该属于设置中的安全或隐私部分。
垃圾邮件和反机器人保护
GitLab 能够使用 reCAPTCHA 和 Akismet 来处理滥用流量。如果启用此设置,你需要一个站点密钥和一个私密密钥,这两个密钥可以在 www.google.com/recaptcha 上生成。通过这种方式,你为防止垃圾邮件发送者自动创建用户设置了另一道防线。如果你注册,你必须通过回答特定问题来证明你是一个人类用户.
类似地,Akismet 可以帮助你保护 GitLab 中的 Issues,避免被垃圾邮件攻击。随着博客和评论功能的出现,涌现出一种新的垃圾邮件技术,称为评论垃圾邮件。垃圾邮件发送者试图通过大量的评论来影响读者。Akismet 被设立为一种额外的检查(GitLab 称之为 API),以防止自动化评论(GitLab 中的 Issues)。
作为额外的安全措施,你可以选择限制来自多个 IP 地址的同时登录。你甚至可以通过设置 IP 过期时间来限制用户可以连接的最大 IP 数量。
滥用报告
在这里,您可以设置接收滥用报告的电子邮件地址。GitLab 的各个部分都可以创建滥用报告。
错误报告和日志记录
一般来说,GitLab 运行的所有服务都有许多日志文件。在此部分,您可以为客户端(也称为浏览器)指定日志记录和报告。GitLab 前端的 JavaScript 应用程序运行在您的浏览器中,并可以选择使用 Sentry (sentry.io/welcome/)。通过使用它,您可以监控并主动捕捉用户的错误并采取相应的措施。
指标和配置分析
GitLab 系统的管理员或所有者通常希望了解他们系统的性能——不仅仅是操作系统级别,还包括功能方面。例如,了解某些操作完成所需的时间非常重要。这些指标在 GitLab 中是可以获得的,但您需要进行一些配置才能使这些数据可用。有多种方式可以从 GitLab 获取这些数据,在此您可以指定信息来源的详细信息。
指标 – InfluxDB
第一个可以作为指标后端存储的系统是 InfluxDB。这是一种特殊的数据库,专门用于存储时间序列数据。在您可以使用此功能之前,您需要在单独的服务器上设置此数据库。由于其负载较重,无法在同一台 GitLab 机器上运行。有关如何配置的说明,请参见docs.gitlab.com/ee/administration/monitoring/performance/influxdb_configuration.html。启用 InfluxDB 指标时,它会设置一个用户数据报协议 (UDP)流向 InfluxDB 主机,传输各种事件信息。您可以微调连接池大小、连接超时时间等,以确保不会生成过多的数据。更改此设置后,您需要重新启动。
指标 – Prometheus
自 2018 年起,GitLab 的首选时间序列数据库是 Prometheus。您可以在这里启用 GitLab 的抓取端点。更改后,您需要重新启动 GitLab。Prometheus 服务器已经捆绑在omnibus-gitlab中。
配置分析 – 性能条
有时,您可能希望了解 GitLab 在浏览器中运行时的性能,或者您想查看 GitLab 请求的哪个部分最耗时。进入配置分析-性能条,您可以为特定的组启用该功能。启用性能条后,您需要按下P + B 键盘快捷键以实际显示它:

使用统计信息
在此设置部分,您可以启用或禁用 GitLab 默认收集的两组信息。您还可以选择仅让实例管理员查看这些信息。这些信息类型如下:
-
版本检查(当有新版本可用时,您将在概述中看到“尽快更新”)
-
使用情况 ping(用于改进 GitLab 及其用户体验)
-
可以将数据共享给公众或仅限管理员
-
勾选预览负载按钮以查看此 ping 中共享的其他数据
假名化数据收集
启用后,此选项将确保 GitLab 将匿名化的信息写入逗号分隔值文件。此文件随后会上传到您在配置文件中指定的 S3 存储桶(gitlab.rb用于 omnibus-gitlab 安装,gitlab.yml用于基于源的安装)。
网络
在此部分,您可以控制一些影响网络性能和通信的选项。
性能优化
在这里,您可以禁用写入授权密钥文件,并让 GitLab 直接从数据库读取授权密钥,而不是通过文件读取。这有助于加速 Git SSH 的授权阶段。
用户和 IP 速率限制
在这里,您可以对 Web 和 API 请求进行流量控制。您还可以选择区分身份验证请求和匿名请求。
外发请求
默认情况下,系统中创建的 webhooks 不允许超出本地 IP 子网。通过此选项,您可以允许 webhooks 外发。
Geo
在此部分,您可以设置一些 Geo 偏好设置,例如与二级实例的通信超时后认为连接丢失的时间。另一个可用的设置允许您列出可以连接并假装是二级实例的 IP 和网络。
偏好设置
这些设置是通用的,与不同的主题相关。
邮件
在电子邮件部分,您可以使电子邮件直接来自创建问题或合并请求的发起者。您还可以控制的第二个选项是 GitLab 是否以 HTML 格式发送电子邮件。
帮助页面
您还可以自定义 GitLab 帮助页面的展示方式。您可以选择提供一些自定义文本,这些文本将在帮助页面的顶部显示:

以下是标准帮助页面的截图:

让我们通过添加This is a help page.来进行一些更改:

结果如下:

页面
如果您使用 GitLab Pages 功能,您可以指定页面的最大大小。如果您希望页面大小无限制,可以将其设置为零。您还可以允许用户在您为其提供页面之前证明他们拥有该域名:

实时功能
GitLab Web 界面提供了轮询实时事件的选项,就像您在合并请求上按下合并按钮时一样。您可以在这里设置一个乘数,以便使轮询更少(或更多,具体取决于您的需求)。
Gitaly
关于 Gitaly,你可以在这里控制一些时限。如果 Gitaly 运行缓慢,你会希望它对某些请求设置超时,因为否则操作可能会导致整个 GitLab 实例崩溃。由于 Gitaly 是与仓库的接口,可以把它想象成文件系统或节点没有响应。发生锁定或网络问题阻止数据传输时,可能会发生非常严重的情况。最好知道何时“切断”连接。
本地化
这是设置中一个很大的部分,因为软件产品有许多本地化设置。此屏幕中唯一显示的设置是“每周的默认第一天”:

这取决于你的地理位置。
配置 GitLab 组级别设置
管理员区域只对具有管理员角色的用户开放,但其他角色也可以配置设置。如果你作为用户被授予了添加组的权限,你就可以更改自己创建的组的设置。如果你从顶级菜单进入“你的组”,你可以从左侧菜单访问设置。
在这里,你会看到一个子菜单,里面列出了可以配置的项目。它看起来和管理员区域的 UI 非常相似,但当然,它仅限于当前的组:

在 GitLab 12.0 中,群组设置的“常规”面板新增了一个有趣的功能:按 IP 地址限制访问。这个功能是企业版的,通过它,你可以确保某些 IP 地址无法访问组的内容。在下方的截图中,你可以看到192.168.1.0/24是唯一允许查看该组内容的 IP 地址范围:

例如,你不希望从 VPN 下载软件。
配置 GitLab 项目级别设置
在前面的部分,我们看到可以在管理员区域和组级别调整设置。还可以为单个项目设置选项。如果你浏览到某个项目,你会在左侧看到一个“设置”菜单:

常规
“常规”菜单提供了一些在其他地方找不到的特定设置。让我们来看看其中最重要的设置。
命名、主题、头像
在“常规”设置下,你可以找到

可见性、项目功能、权限
这些设置中非常重要的一部分是“可见性、项目功能、权限”部分。你可以启用或禁用某些功能,并确定谁可以执行什么操作:

合并请求
对于单个项目,你可以定义合并请求的行为。例如,你可以定义 GitLab 如何在服务器上执行合并。对于每个执行的合并请求,服务器上会有一个 Git 会话,运行的是你工作站上相同的 Git 二进制文件。例如,你可以指定服务器端从不做合并提交:

在 GitLab 12.0 版本中,加入了合并列车的概念。如果启用此功能,所有的合并请求必须按顺序通过,而你的合并将成为这个列车的一部分。只有整个合并成功时,才能成功执行,这种用例在不同团队共同开发同一产品的企业环境中很常见。在未来的版本中,此功能将默认启用。
总结
在本章中,我们讨论了如何通过 Web 界面配置现有的 GitLab 应用实例。GitLab 的管理页面让你对实例有很大的控制权。在浏览完这些页面后,我们解释了可以管理的各种项目。
在下一章中,我们将通过常规终端界面(而非 Web 浏览器)来配置 GitLab。
问题
-
用什么图标表示管理部分?
-
在管理员仪表盘上突出显示的三个项目是什么?
-
上传的 logo 图像最大大小是多少?
-
可以配置哪种度量指标后端?
-
哪个产品用于启用实时预览功能?
-
哪款 UML 工具可以与 GitLab 集成?
-
使用哪种机制防止网络存储挂起 GitLab?
-
哪个过程可以帮助保持仓库的完整性?
-
默认启用的是哪项 CI/CD 功能?
-
在 GitLab Pages 中需要设置什么值才能启用无限制大小?
进一步阅读
-
Kubernetes 入门 - 第三版,作者:Jesse White 和 Jonathan Baier:
www.packtpub.com/in/virtualization-and-cloud/getting-started-kubernetes-third-edition -
在 Kubernetes 上开发和操作微服务,作者:Martin Helmich:
www.packtpub.com/virtualization-and-cloud/develop-and-operate-microservices-kubernetes-video -
Docker 厨房秘籍 - 第二版,作者:Neependra K Khare,Jeeva S. Chelladhurai,Ken Cochrane:
www.packtpub.com/in/virtualization-and-cloud/docker-cookbook-second-edition
第四章:从终端配置 GitLab
安装 GitLab 后,你可能已经有了一个正在运行的实例。那么,如何管理它呢?在前一章中,我们向你展示了可以通过 Web 界面管理的选项。但还有许多只能通过服务器上的配置文件设置的选项。你需要知道如何在常规终端中配置软件。本章将解释如何为不同类型的 GitLab 安装实现这一点。
本章将涵盖以下主题:
-
从终端配置 omnibus 安装
-
配置源安装
-
重新配置 GitLab Docker 容器
-
在 Kubernetes 环境中更改 GitLab
技术要求
对于管理 omnibus 安装,有一个名为gitlab.rb的中央配置文件。你需要创建它或复制一个示例。可以找到模板,位置是gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template。升级后该模板不会更新。本章将引用并讨论文件中的部分内容。
若要跟随本章中的说明,请从 GitHub 下载可用的 Git 存储库:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter04。
你还需要以下内容:
-
Docker:
www.docker.com
从终端配置 omnibus 和 GitLab 安装
你可以在gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template找到gitlab.rb的模板。升级后该模板不会更新。
你还可以使用 omnibus 或 GitLab 为你的 GitLab 安装实现高可用性(HA)。
在gitlab.rb中有一部分可以定义你正在配置的 GitLab 实例的角色。如果没有默认定义的角色,omnibus 将配置你的服务器来托管 GitLab 的所有核心组件。
例如,如果你的实例将作为 Redis 主节点运行,并且运行着 Redis sentinel 代理,请添加以下代码行:
roles ['redis_sentinel_role', 'redis_master_role']
可用的角色如下:
-
redis_sentinel_role:仅启用 sentinel 服务。 -
redis_master_role:启用 Redis 和监控,并允许你配置主密码。 -
redis_slave_role:启用 Redis 服务和监控。 -
geo_primary_role:配置用于复制的数据库,并将应用服务器设置为geo primary。 -
geo_secondary_role:为数据库准备复制,并将应用设置为secondary geo。 -
postgres_role:启动机器上的postgresql、repmgr和consul服务。仅设置这些组件。 -
pgbouncer_role:此角色添加 PgBouncer 软件用于数据库负载均衡功能。
编辑后,您必须运行gitlab-ctl reconfigure来应用设置。
配置源安装
需要更改的主要配置文件是gitlab.yml,通常位于/home/git/gitlab/config。它遵循.yml标准,并且这有几个含义。首先,缩进非常重要。如果您只是复制并粘贴配置,您会发现可能会搞乱文件内容。gitlab.yml文件中另一个重要特性是使用锚点和别名(&base)来指定不同的配置目标。实际上,这意味着所有环境的主要配置都在config文件中指定(production: &base)。
在production: &base目标下,指定了其他环境,并且它们引用&base但会覆盖某些键值对。GitLab 如何知道应该使用哪个环境的信息?这由安装和启动 GitLab 时使用的RAILS_ENV变量决定。
我们在本书的代码库中放置了一个示例配置文件(github.com/PacktPublishing/Mastering-GitLab-12/blob/master/Chapter04/config_gitlab.yml.example)。
配置文件有几个部分,我们将逐一讲解。
GitLab 应用设置
该配置文件部分主要用于定义整个 GitLab 应用的全局设置。
您将遇到的第一个设置主要是针对 Web 服务器组件(Unicorn)。您可以指定用于主机名的 FQDN、监听的端口,以及是否使用 HTTPS:
host: localhost
port: 80
https: false
如果您想使用 HTTPS,可以将端口设置为443,并将 HTTPS 设置为 true。如果您的设置中有不同的ssh_host,您也可以指定它(如果您希望 Git-SSH 在同一服务器上运行,则无需指定此项):
ssh_host: ssh.host_example.com
此外,如果您想使用相对 URL(例如/mygitlab/):
relative_url_root: /gitlab
您还可以更改关于使用哪个操作系统用户来运行 Web 服务器进程的选项:
user: git
下一个设置是关于日期和时间的,您可以指定整个应用程序使用的时区:
time_zone: 'UTC'
下一个设置包处理电子邮件。您可以完全禁用 GitLab 使用电子邮件,或指定发件人、主题等内容:
email_enabled: true
email_from: example@example.com
email_display_name: GitLab
email_reply_to: noreply@example.com
email_subject_suffix: ''
下一个设置决定是否允许新用户创建小组(请小心,因为现有用户保留此权限)。
default_can_create_group: false
如果您希望旧用户也失去此权限,可以使用 Rails 控制台来实现:
irb(main):012:0> @users.each do |u|
irb(main):013:1* u.can_create_group= false
irb(main):014:1> u.save
irb(main):015:1> end
下一个选项允许您更改用户名。如果您使用的是其他系统,如轻量级目录访问协议(LDAP)进行账户管理,一般不推荐更改用户名:
username_changing_enabled: false
下一组选项决定了 GitLab 使用的样式;我建议您尝试所有选项(1-10):
# default_theme: 1 # default: 1
一个可以非常高效地自动化开发工作流的好功能是通过向默认项目分支提交代码来自动关闭问题。您可以使用以下模式使自动化生效。如果匹配,将会关闭。由于该模式相当复杂,我们将使用提供的代码:
issue_closing_pattern: '\b((?:[Cc]los(?:e[sd]?|ing)|\b[Ff]ix(?:e[sd]|ing)?|\b[Rr]esolv(?:e[sd]?|ing)|\b[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)'
比如,我们可以使用以下提交信息:
Fix #122 and Closes groupx/bestproject#1000.
Also important for #10 and fixes #11, #229 and #188
and https://gitlab.example.com/group/mything/issues/9999.
在前面的示例中,#122、#1000、#11、#229、#188 和 #9999 将被自动关闭。只有 #10 不会受到影响,因为它不匹配模式。
在 GitLab 中,每个项目都有一些默认功能,如在项目空间中创建问题或维基的可能性。您可以选择默认启用或禁用它们:
default_projects_features:
issues: true
merge_requests: true
wiki: true
snippets: true
builds: true
container_registry: true
GitLab 的 CI/CD 组件在网络边界上严重依赖 webhooks 作为其主要的事件机制。在慢速网络上,您可能需要增加它们的超时值,以使它们更努力地尝试:
webhook_timeout: 10
您可以下载项目内容,这些内容会被打包成一个 ZIP 文件。创建这个 ZIP 文件需要一些临时磁盘空间,您可以在这里定义,仓库下载目录(相对于 Rails 目录的路径):
repository_downloads_path: shared/cache/archive/
GitLab 还可以利用电子邮件客户端,从 IMAP 服务器获取邮件,并解析邮件内容中的问题和合并请求 ID。如果您正确设置,您可以允许用户回复通知邮件,结果将被添加到问题或合并请求中。第一部分是启用或禁用此功能:
incoming_email:
enabled: false
第二部分是您定义用于接收电子邮件的地址,并指出其中哪部分是变量(每个合并请求或问题编号都是变量):
address: "gitlab-incoming+%{key}@gmail.com"
其他设置主要涉及与凭据和连接设置一起使用的 IMAP 服务器:
user: "gitlab-incoming@gmail.com"
password: "[REDACTED]"
host: "imap.gmail.com"
port: 993
ssl: true
start_tls: false
mailbox: "inbox"
idle_timeout: 60
这些设置一起启用了您的 GitLab 实例的来邮功能。
这部分涵盖了配置文件中的一些通用设置,接下来的部分是关于存储不同种类文件的内容。
存储大文件
GitLab CI/CD 组件可以构建您的软件,并生成构建工件。这些工件会从 GitLab 运行器发送回 GitLab 服务器。您可以通过 web UI 下载它们。默认情况下,这些工件存储在 GitLab 服务器的 shared/artifacts 中。由于这些文件可能变得非常大且数量众多,您也可以将它们存储在其他地方,GitLab 服务器可以在需要时取回它们,或者重定向请求。您可以设置 Amazon S3 等对象存储作为一个大桶来存储文件。
默认情况下,GitLab 将构建产物存储在 GitLab 安装服务器上的本地共享路径shared/artifact:
artifacts:
enabled: true
path: shared/artifacts
在合并请求期间比较操作时,diffs文件通常会在启用时保存在数据库中。但当设置为true时,GitLab 将它们保存在共享路径中:
external_diffs:
enabled: true
storage_path: shared/external-diffs
与构建产物和diffs的处理方式相同,GitLab 可以将 Git 大文件存储 (LFS)对象存储在不同的位置,即项目仓库之外。然后,在项目仓库中插入指向该位置的引用作为替代。这是一个 Git 客户端扩展,服务器端的相关实现已经在 GitLab 中完成。(更多关于 Git LFS 的信息可以在git-lfs.github.com/找到。)可以启用并指定路径:
lfs:
enabled: true
storage_path: shared/lfs-objects
另一个可能的对象存储用途是存储uploads,如附件和头像。我们可以在 GitLab 中启用此功能,以节省空间并更高效地使用存储:
uploads:
base_dir: uploads/-/system
存储选项不仅仅局限于本地。你还可以将文件作为对象存储到其他地方。
使用对象存储
存储构建产物的更高效方式是利用对象存储。目前,它可以用于存储artifacts、lfs、uploads和合并请求的external_diffs。诀窍是添加一个object_store:部分,它可以包含多个选项:
object_store:
enabled: true
remote_directory: artifacts
直接上传避免了在传输过程中保存文件,直接上传到 AWS 或其他选择的对象存储提供商:
direct_upload: true
在remote_directory之后的下一个设置可以在文件首先保存在 GitLab 时,限制上传的构建产物:
background_upload: false
如果将proxy_download设置为false,下载时将直接跳转到对象存储,而不是通过代理连接:
proxy_download: false
还有一些与连接到对象存储相关的特定设置。这些设置与提供商类型和该提供商的特定属性有关。凭证和区域是大多数提供商需要定义的设置。AWS 签名版本用于创建签名 URL,你可以选择 v2 或 v4。端点对于 AWS 是固定的,但对于其他提供商可能不同。路径样式指的是文件的解析方式。如果设置为true,它将是host/bucket_name/object,但如果设置为false,它将是bucket_name.host/object。
连接部分下的所有设置将如下所示:
connection:
provider: AWS
aws_access_key_id: AWS_ACCESS_KEY_ID
aws_secret_access_key: AWS_SECRET_ACCESS_KEY
region: us-east-1
aws_signature_version: 4
endpoint: 'https://s3.amazonaws.com'
path_style: true
到目前为止,我们已经看到了大文件存储的不同选项,这些大文件可以是 CI 作业或仓库的一部分。
如你所见,存储不同类型文件有很多选项。你还可以使用 GitLab 页面将某些文件发布为网页内容,这就是下一节的主题。
GitLab 页面
GitLab 页面是 CI/CD 的扩展,能够自动构建并将 web 内容部署到 web 服务器。你可以直接从一个仓库发布静态网站。有几个选项需要考虑。要使用此功能,还需要决定是否启用访问控制,以及页面存储的路径。
该扩展使用 GitLab 页面守护进程,守护进程是用 Go 编写的,并从共享位置提供内容。如果你在同一服务器上运行它,最好将其运行在 80 或 443 端口上,以防需要为 GitLab 服务器添加 IP。也可以在单独的服务器上运行,但需要通过网络从 GitLab 服务器导出路径到主机,并在该主机上运行 GitLab 页面守护进程。
更多信息请访问gitlab.com/gitlab-org/gitlab-pages。
关于 GitLab 页面的一些初始设置包括完全启用或禁用它,启用或禁用 access_control,以及你可以设置共享页面的路径:
pages:
enabled: false
access_control: false
path: shared/pages
以下设置决定了 GitLab 页面服务器的属性:
host: example.com
port: 80
https: false
artifacts_server: true
external_http: ["1.1.1.1:80", "[2001::1]:80"]
external_https: ["1.1.1.1:443", "[2001::1]:443"]
最后一个设置定义了管理员访问的套接字位置:
admin:
address: unix:/home/git/gitlab/tmp/sockets/private/pages-admin.socket # TCP connections are supported too (e.g. tcp://host:port)
GitLab 页面设置完成。
Mattermost
有许多 GitLab 集成可用,但其中一个非常有趣的是 Mattermost,这是一个类似 Slack 的聊天协作工具。它在 2017 年被 GitLab 收购,现在甚至集成在 Omnibus GitLab 安装中。你可以启用 ChatOps 操作与此工具配合使用。在设置中,你可以在 GitLab 中启用特定按钮:
mattermost:
enabled: false
host: 'https://mattermost.example.com'
启用此功能后,你可以在 GitLab 服务设置中使用添加按钮。
Gravatar
以下代码块定义了使用头像的设置,默认情况下为 gravatar.com。Gravatar 是一种全球公认的头像。头像是一个会在网站间跟随你的图像,Gravatar 服务起源于 WordPress,最初用于博客。你可以在此处设置 HTTP 和 HTTPS URL:
gravatar:
plain_url: “http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon”
ssl_url: “https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon”
另一个示例是将其设置为指向 Office 365 URL(你必须先登录 Office 365 才能使用它):
gravatar: plain_url: “http://outlook.office365.com/owa/service.svc/s/GetPersonaPhoto?email=%{email}&size=HR120x120”
ssl_url: “http://outlook.office365.com/owa/service.svc/s/GetPersonaPhoto?email=%{email}&size=HR120x120”
另一个提供这些服务的服务是Libreavatar。你可以在wiki.libravatar.org/api/找到更多信息。
Sidekiq
GitLab 的 Sidekiq 组件负责后台任务,你可以在第一章《介绍 GitLab 架构》中找到更多信息,介绍 GitLab 架构。大部分配置是在 Sidekiq 自身的配置文件中完成的,但你可以在 gitlab.yml 文件中定义一些选项。
例如,你可以在这里定义 log_format 和将在 Sidekiq 中运行的 cron 作业:
## Sidekiq
sidekiq:
log_format: default # (json is also supported)
在 Sidekiq 背景处理过程中,还集成了一个作业调度器,默认情况下运行一些作业。调度的格式就像 Unix 系统上的 cron。最重要的作业如下所示:
-
stuck_ci_jobs_worker:将卡住的作业设置为失败状态。 -
pipeline_schedule_worker:执行计划触发器。 -
expire_build_artifacts_worker:删除过期的构建工件。 -
repository_check_worker:定期在所有仓库上运行git fsck。 -
ci_archive_traces_cron_worker:存档尚未存档的实时跟踪记录。 -
admin_email_worker:发送管理员电子邮件。 -
repository_archive_cache_worker:删除过时的仓库存档。 -
pages_domain_verification_cron_worker:验证自定义 GitLab 页面域名。 -
schedule_migrate_external_diffs_worker:定期将diffs从数据库迁移到外部存储。
对于 GitLab 企业版 (EE),提供了几个额外的作业,主要是处理 Geo 同步和 LDAP 同步的作业。
GitLab 注册表
GitLab 可以作为完整的 Docker 容器注册表使用。你可以在以下部分为其设置选项。
第一个选项基本上是开关:
enabled: true
下一个选项是主机名,它将被设置:
host: registry.example.com
你还可以定义它将监听的网络端口:
port: 5005
你可以定义一个 GitLab 自身将连接的内部地址:
api_url: http://localhost:5000/
GitLab 注册表使用一个密钥对,rootcertbundle,这里是私钥的位置:
key: config/registry.key
设置用于存储的路径:
path: shared/registry
设置证书的发行者名称:
issuer: gitlab-issuer
更多选项请参见 docs.gitlab.com/ee/administration/container_registry.html。
GitLab CI 设置
GitLab CI 曾是一个独立的软件组件,但现在已经牢固地集成到 GitLab 后端。
gitlab.yml 中有三个选项需要配置:
-
all_broken_builds:仅在构建失败时发送电子邮件。 -
add_pusher:还将推送最新版本的用户添加到收件人列表中。 -
builds_path:存储构建跟踪记录的位置。
gitlab_ci:
all_broken_builds: true
add_pusher: true
builds_path: builds/
GitLab CI 的进一步配置通过你在上一章中看到的 Web 界面完成。
身份验证设置
GitLab 提供了多个身份验证提供程序。主要的本地身份验证提供程序是 LDAP 接口(用于 Active Directory、OpenLDAP)。
第一部分是启用该功能:
ldap:
enabled: false
下一部分是服务器的声明:
servers:
main:
label: 'LDAP'
host: '_your_ldap_server'
port: 389
uid: 'sAMAccountName'
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
如你所见,我们定义了一个 label 并设置了 host 和 port。我们还提供了 uid 属性供使用,以及 password 和 bind_dn(用于连接 LDAP 的对象或用户)。
你可以设置 SSL 选项以增强安全性:
encryption: 'start_tls'
verify_certificates: true
ssl_version: 'TLSv1_1'
接下来是一些与登录操作和超时处理相关的设置:
timeout: 10
active_directory: true
allow_username_or_email_login: false
block_auto_created_users: false
GitLab 需要知道它能从 LDAP 树中找到哪些用户,并且如何将其与其他对象或人员区分开来。你可以设置 LDAP 基础和过滤器:
base: 'ou=People,dc=gitlab,dc=example' or 'DC=mydomain,DC=com'
user_filter: '(&(objectclass=user)(|(samaccountname=momo)(samaccountname=toto)))'
在下一部分,你可以确定希望在 GitLab 中使用哪些 LDAP 属性。它将 LDAP 对象映射到 GitLab 对象:
username: ['uid', 'userid', 'sAMAccountName']
email: ['mail', 'email', 'userPrincipalName']
name: 'cn'
first_name: 'givenName'
last_name: 'sn'
lowercase_usernames: false
如果你拥有企业许可证,你有权定义多个 LDAP 服务器,这在企业环境中是常见的。你可以为我们在前面的代码块中填写的设置块添加一个主标签,并创建一个带有新标签的新块:
label:
host:
除了 LDAP 外,你还可以通过启用 OAuth 的云服务提供商进行用户认证。设置的第一部分处理一些常规选项,例如启用该功能、如何处理来自这些提供商的信息以及该机制的其他行为方面。配置文件中的注释非常详细,说明了有哪些可用选项。
本节的第二部分允许你定义一个 OmniAuth 提供商。这里提到了几个示例,如 GitHub、Facebook 和 Auth0(auth0.com/)(一个身份管理平台)。同样,配置文件提供了关于如何配置这些的详细解释。
在该配置文件部分的末尾,有两个设置,我认为它们不是认证设置,但它们很重要。
你可以定义共享文件存储路径。GitLab 在一些操作中使用共享文件存储:
shared:
/mnt/gitlab
在这里,你定义 Gitaly 服务的设置。如果你是从源代码运行它,应该说明你是从哪里运行的:
gitaly:
client_path: /home/git/gitaly/bin
token:
高级设置
我不确定为什么在gitlab.yml中会有这样的高级部分,但第一部分选项涉及到存储库设置。这里主要是指定存储库路径,如下所示:
storages:
default:
path: /home/git/repositories/
gitaly_address: unix:/home/git/gitlab/tmp/sockets/private/gitaly.socket #
gitaly_token: 'special token'
始终会有一个default条目,你可以在此处指定path和gitaly_address(也以tcp://形式)。你可以在这里覆盖全局的gitaly_token。
GitLab 有一个备份或恢复功能。它以 rake 任务的形式存在。例如,你可以像这样调用备份任务:
sudo -u git -H bundle exec rake gitlab:backup:create
前面的命令有一些可以通过配置文件设置的选项。再次强调,你可以使用云存储文件。GitLab 还导入了 AWS、Google、OpenStack Swift、Rackspace 和阿里云的云驱动程序。本地驱动程序也可用。以下是gitlab.yml中的一个示例条目:
## Backup settings
backup:
path: "tmp/backups"
GitLab shell 是提供 Git-SSH 连接的主要组件,当从源代码运行时,你必须指定它安装的路径。还设置了钩子路径,当像 git push 这样的事件触发时,它们可以执行(你可以创建自己的钩子):
gitlab_shell:
path: /home/git/gitlab-shell/
hooks_path: /home/git/gitlab-shell/hooks/
你还必须定义包含密钥的文件。它在 GitLab shell 连接到 rails 后端时用于验证尝试执行 Git-SSH 操作的用户的访问权限:
# File that contains the secret key for verifying access for gitlab-shell.
# Default is '.gitlab_shell_secret' relative to Rails.root (i.e. root of the GitLab app).
secret_file: /home/git/gitlab/.gitlab_shell_secret
在下面的代码块中,你可以看到 GitLab Workhorse 用来访问 rails 应用程序的密钥定义。
workhorse:
secret_file: /home/git/gitlab/.gitlab_workhorse_secret
它在文件.gitlab_workhorse_secret中定义,如下所示TJEf6HQcgkBjcLGVdZ4h6Y2PB89X1RGs/RsJ7FIfg6s=。
你可以选择切换到另一个 Git 版本:
## Git settings
git:
bin_path: /usr/bin/git
webpack程序用于编译和服务前端资产,如层叠样式表(CSS),仅限开发使用:
## Webpack settings
webpack:
dev_server:
enabled: true
host: localhost
port: 3001
内置的 Prometheus 端点还会收集关于 Unicorn 性能的数据。它将采样 Unicorn Unix 套接字以获取这些数据。在这里,你可以定义采样率。你还可以定义一个 IP 白名单,允许其连接到度量端点:
# Built in monitoring settings
monitoring:
unicorn_sampler_interval: 10
ip_whitelist:
- 127.0.0.0/8
另一个内置的度量指标提供程序是sidekiq的 Prometheus 导出器,可以从这里控制:
sidekiq_exporter:
enabled: true
address: localhost
port: 3808
如你所见,本节中处理的主题非常不同,我不确定它们是否应该归类为高级设置。gitlab.yml 文件中还有一个最终部分,叫做额外自定义。这里最重要的部分是rack_attack。
Rack Attack
当前互联网安全中的一个重要部分是内置的限流机制,用于抵御拒绝服务攻击。GitLab 使用 Rack Attack Gem,可以监控来自单个 IP 的请求数量。你可以在这里禁用它,并设置白名单和一些阈值:
rack_attack:
git_basic_auth:
enabled: true
ip_whitelist: ["127.0.0.1"]
你可以限制 Git HTTP 认证尝试的次数:
maxretry: 10
60 秒后,认证尝试计数器将被重置:
findtime: 60
你还可以禁止一个 IP 地址一个小时(3,600 秒);例如,在进行过多的认证尝试后:
bantime: 3600
我们已经到达了gitlab.yml文件的末尾。重启 GitLab 后,更改将生效。还有其他方式运行 GitLab。在下一节中,我们将查看如何在 Docker 容器中配置实例。
重新配置 GitLab Docker 容器
GitLab 官方容器使用omnibus-gitlab包,因此所有配置都通过唯一的配置文件/etc/gitlab/gitlab.rb进行管理。
一旦容器启动,你可以通过启动一个 Bash 会话连接到容器:
docker exec -it gitlab /bin/bash
现在你可以编辑 Docker 容器内的/etc/gitlab/gitlab.rb。然后,执行gitlab-ctl reconfigure来应用更改。
配置容器的第二种方式是通过环境变量GITLAB_OMNIBUS_CONFIG启动容器。该变量可以包含一个gitlab.rb文件的内容。这些设置不会持久化到实际文件中。
你可以使用以下代码:
sudo docker run \
--hostname gitlab.joustie.nl \
--env GITLAB_OMNIBUS_CONFIG="external_url 'http://gitlab.joustie.nl/'; gitlab_rails['lfs_enabled'] = true;" \
--publish 443:444 --publish 80:81 --publish 22:2222 \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
容器启动过程将始终运行gitlab-ctl reconfigure。这意味着你在容器启动时指定的 omnibus 模板设置需要每次启动容器时明确设置;否则,容器会重新配置,你会丢失这些设置。
在 Kubernetes 环境中更改 GitLab
由于 Kubernetes 上的 GitLab 是通过 Helm charts 配置的,因此你需要通过 Helm charts 配置所有设置。
选项可以在这里找到:gitlab.com/charts/gitlab/blob/master/doc/installation/command-line-options.md。
对于每组选项,有些是必需的,而那些不需要的选项会有默认值。
在接下来的部分,我们将选择一些选项来更改 Kubernetes 环境中的配置。
基本配置
这里有两个必需的设置:global.hosts.domain 和 global.hosts.externalIP。它们应该指向要为 GitLab 提供服务的主机和 IP 地址。以下是另一些有趣的设置:
-
global.psql.host -
global.psql.password.secret -
global.psql.password.key
这些包含指向外部 Postgres 实例的信息。
其他选项可以在命令行选项 URL 中找到,地址为 gitlab.com/charts/gitlab/blob/master/doc/installation/command-line-options.md。
配置 TLS
这些选项控制激活的 GitLab 的 TLS 配置。没有必需的选项,但如果你不想使用 Let's Encrypt,请将 global.ingress.configureCertmanager 设置为 false。
配置外发邮件
对于外发邮件,有几个选项,例如传入邮件服务器的主机——例如:
global.smtp.address (e.g. smtp.joustie.nl) with global.smtp.authentication="" (disable smtp authentication)
要设置此配置并启用它,你可以使用以下命令行:
helm upgrade gitlab gitlab/gitlab \
--timeout 600 \
--set global.hosts.domain=kicks-ass.net \
--set global.hosts.externalIP=82.161.132.207 \
--set certmanager-issuer.email=joustie@gmail.com \
--set global.smtp.enabled=true \
--set global.smtp.address=smtp.joustie.nl \
--set global.smtp.authentication=""
如果你的 SMTP 服务器需要身份验证,你需要将凭证添加为 –set 选项,并将密码作为密钥部署,具体如下:
kubectl create secret generic smtp-password --from-literal=password=yourpasswordhere
在你执行 helm upgrade 命令后,unicorn 和 sidekiq 组件将在 Kubernetes 集群中自动重启。
其他设置
还有许多其他设置,你可以在 gitlab.com/charts/gitlab/blob/master/doc/installation/command-line-options.md 查看它们。
以下是一些示例:
-
传入邮件配置:GitLab 还可以处理服务台功能的传入邮件。
-
GitLab shell:你可以为 GitLab shell 提供设置。
-
RBAC 设置:你可以描述 Kubernetes 基于角色的访问控制(RBAC)。
-
高级 nginx ingress 配置:更改集群中默认的 ingress nginx 设置。
-
高级集群内 Redis 配置:更改 Redis 集群的设置。
-
高级注册表配置:更改作为微服务运行的 Docker 注册表的设置。
-
高级 MinIO 配置:更改 MinIO 对象存储微服务的设置。
-
高级 GitLab 配置:更改 GitLab 中的高级设置。
总结
在本章中,我们讨论了配置现有 GitLab 应用实例。如前几章所见,选项会根据你选择的安装路径有所不同。Omnibus GitLab 安装相对容易配置。只需更改 /etc/gitlab/gitlab.rb 中的设置并运行 gitlab-ctl reconfigure。从源代码安装更改设置需要更多注意,因为 GitLab 组件之间的依赖关系不像 Omnibus GitLab 那样由系统管理。配置运行在 Docker 容器中的 GitLab 实例与管理 Omnibus GitLab 安装相同。
在下一章中,我们将探索在 GitHub 和 GitLab 中应该更改的设置,以了解导入和运行项目的过程。
问题
-
在
gitlab.rb中选择postgres_role时启用哪些服务? -
gitlab.yml通常在哪里找到? -
用于接收邮件功能的协议是什么?
-
LFS 代表什么?
-
哪个开源聊天工具与 GitLab 紧密集成?
-
支持 LDAP 组需要什么许可证?
-
GitLab 提供了什么机制来限制 Web 请求的频率?
-
除了 Helm,你还需要什么工具来配置 Kubernetes?
进一步阅读
-
Kubernetes 入门 - 第三版 由 Jesse White 和 Jonathan Baier 编写:
www.packtpub.com/virtualization-and-cloud/getting-started-kubernetes-third-edition -
在 Kubernetes 上开发和运营微服务 [视频] 由 Martin Helmich 编写:
www.packtpub.com/virtualization-and-cloud/develop-and-operate-microservices-kubernetes-video -
Docker 厨房秘籍 - 第二版 由 Jeeva S. Chelladhurai、Ken Cochrane、Neependra Khare 编写:
www.packtpub.com/virtualization-and-cloud/docker-cookbook-second-edition
第二部分:从不同位置迁移数据
根据读者当前使用的平台,解释和演示了成功迁移到 GitLab 的路径。
本节包括以下章节:
-
第五章,从 GitHub 导入项目到 GitLab
-
第六章,从 CVS 迁移
-
第七章,从 SVN 切换
-
第八章,从 TFS 迁移仓库
第五章:从 GitHub 导入项目到 GitLab
在第一部分中,我们解释了 GitLab 架构以及如何以多种方式安装和配置 GitLab。在本书的这一部分,我们将探讨从第三方迁移源项目到 GitLab 的几种方法。我们将从 GitHub 开始,它与 GitLab 非常相似,也是 GitLab 开发的灵感来源。经过多年的发展,这两个产品仍然共享相同的基本功能,但在额外功能上有所不同。有几种方法可以从 GitHub 导入项目。
在本章中,将涵盖以下内容:
-
在 GitLab 中使用 GitHub 集成功能(也称为 GitHub 导入器)
-
使用 GitHub token——为第三方应用集成在 GitHub 中创建该 token
-
使用 GitLab rake 任务——你需要访问一个可以运行 rake 任务的 GitLab 实例,并且需要拥有管理员权限
对于每种方法,我们将展示你在 GitHub 和 GitLab 中需要配置和准备的内容。最后,我们将针对每种方法执行导入操作。
技术要求
为了管理 omnibus 安装,你需要使用一个名为 gitlab.rb 的中央配置文件。你需要创建它或复制一个示例文件。幸运的是,已经提供了一个模板,你可以在 gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template 找到该模板。它在升级后不会更新。稍后在本章中,我将引用并讨论该文件的部分内容。
为了跟随本章的说明,请下载 GitHub 仓库以及 GitHub 仓库中的示例,下载地址为 github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter05。
你还需要一个 GitHub 账户,以及一个在云端或本地的 GitLab 账户。
关于用户匹配,你需要确保与仓库相关联的 GitHub 用户具备以下条件:
-
一个使用 OAuth 登录的 GitLab 账户,可以通过 GitHub 图标进行登录
-
一个 GitLab 账户,其电子邮件地址与 GitHub 中的公开电子邮件地址相同
使用 GitHub 集成功能
要从 GitHub 导出内容,必须设置适当的授权。这可以通过使用 GitHub 集成和 OAuth 注册来完成,GitLab 会与 GitHub 进行身份验证,以便访问用户的项目列表。
将 GitHub 上的作者和任务分配人映射到 GitLab 上的最佳效果,是使用 GitLab 中提供的 GitHub 集成功能,而不是仅使用 GitHub 的个人访问 token。
此集成涵盖了我们刚刚提到的所有导入项,并尽力保持所有引用的完整性。例如,导入程序将尝试查找 GitHub 问题和拉取请求的作者和受指派者。这意味着必须在您的 GitLab 实例中启用 GitLab 集成功能。
准备 GitHub 进行导出
要将 GitLab 注册为可以连接到 GitHub 的应用程序,您需要登录到 GitHub 帐户。为此,请访问github.com/settings/applications并将 GitLab 注册为 OAuth 应用程序:

使用以下数据填写表格:
-
应用名称(您的应用程序名称)
-
首页 URL(应用程序的完整 URL)
-
应用描述(一般描述)
-
授权回调 URL(最重要的设置,认证后发送的 URL):

注册成功后,会显示一个概览,包含两个非常重要的信息:客户端 ID 和客户端密钥,以及有多少用户通过 OAuth 链接连接了 GitHub 仓库。稍后您将需要这些客户端 ID 和客户端密钥来配置 GitLab 进行集成。
注册成功后,将出现以下屏幕:

现在,您已经配置了 GitHub,可以继续进行准备 GitLab 导入部分。
准备 GitLab 进行导入
为了完成 GitHub 集成的设置,我们需要配置 GitLab 实例,以便我们可以将其作为注册的 OAuth 应用程序连接到 GitHub。我们通过将omniauth_provider添加到 GitLab 配置来实现此操作。还记得客户端 ID 和客户端密钥吗?我们将在这里用到它们。
对于 GitLab omnibus 安装,我们需要在/etc/gitlab/gitlab.rb中创建如下的部分。app_id是客户端 ID,而app_secret是客户端密钥。提供的名称不重要。保存文件后,您需要使用gitlab-ctl reconfigure:
gitlab_rails['omniauth_providers'] = [
{
"name" => "github",
"app_id" => "dd1c6d6aed110b2cce8e",
"app_secret" => "f6ddd6059c694ecfc1a96f962fa20b6c3f7c8c4a",
"args" => { "scope" => "user:email" }
}
]
对于 GitHub Enterprise,您还可以指定您的 GitHub 实例的 URL:
"url" => "https://github.example.com/",
对于源代码安装的 GitLab,我们编辑config.yml文件,如下所示,并在保存后重新启动 GitLab:
- { name: 'github',
app_id: 'dd1c6d6aed110b2cce8e',
app_secret: 'f6ddd6059c694ecfc1a96f962fa20b6c3f7c8c4a',
url: "https://github.com/",
verify_ssl: true,
args: { scope: 'user:email' } }
请参阅github.com/gitlabhq/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template获取更多有关在gitlab.rb文件中可以设置的配置项的信息。还可以查看第二章,安装 GitLab,了解更多相关内容。
执行导入
按照以下步骤学习如何运行导入:
- 在 GitLab 中,选择创建一个新项目,并使用导入项目标签找到 GitHub 图标:

- 点击 GitHub 图标后,您将看到一个页面,在该页面上可以点击列出您的 GitHub 仓库:

- 启用 GitHub 集成后,下一步将是授权 GitLab 访问您的项目:

- 如果授权成功,将显示可迁移的项目列表。选择要迁移的项目并点击“导入”:

确保在您的命名空间中没有已经存在同名的项目,否则您将收到 422 错误,如下图所示:

导入完成后,您将收到一条消息:

项目已成功导入!
使用 GitHub 令牌
当使用 GitHub 令牌进行导入时,在 GitLab 中无需进行特殊配置——您可以立即输入,正如稍后所看到的那样。
准备 GitHub 进行导出
个人令牌可以作为您 GitHub 账户的委托授权。让我们开始:
- 您可以通过
github.com/settings/tokens创建一个:

- 确保设置有意义的令牌描述,并只选择仓库作用域。我们希望 GitLab 导入器访问以下对象:

- 令牌创建后,请务必将令牌记录在某个地方,因为它将不会再次显示,丢失令牌意味着您需要重新创建它:

现在,您拥有一个 GitHub 令牌,可以选择在 GitLab 网页界面中直接使用它。
运行导入
让我们开始并学习如何准备 GitLab 进行导入:
- 在 GitLab 中,创建一个新项目,并使用导入标签找到 GitHub 图标,如下图所示:

- 如果禁用了 GitHub 集成,下一屏将不会有列出 GitHub 仓库的选项。要选择要迁移的项目,您必须输入您在
github.com/上创建的个人令牌:

- 如果授权成功,将显示可迁移的项目列表。选择要迁移的项目并点击“导入”:

- 导入完成后,您将收到一条消息:

项目已经成功使用令牌导入。
使用 GitLab rake 任务
为了检索和导入 GitHub 仓库,您需要一个 GitHub 个人访问令牌,正如之前演示的那样。
准备 GitLab 进行导入
对于使用 rake 任务导入项目,在 GitLab 中预先配置的唯一选项是:Sidekiq 资源的数量。你会发现,对于大型项目,导入所有数据可能需要相当长的时间。为了加速这个过程,可以为以下队列分配更多的 Sidekiq 工作者:
-
github_importer -
github_importer_advance_stage
对于 GitLab Omnibus 安装,这些队列是默认 Sidekiq 进程的一部分。为 Sidekiq 提供更多线程意味着将有更多的工作者:
# sidekiq['concurrency'] = 25
查看 github.com/gitlabhq/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template,了解可以在 gitlab.rb 文件中设置的更多配置信息。此外,关于此主题,请参见 第二章,安装 GitLab。
运行导入
GitLab 的 rake 任务可以一次导入一个项目或多个项目。它是从命令行调用的,因此你需要管理员权限才能访问运行 GitLab 应用程序的机器。
当将 GitHub 仓库作为 rake 任务的第四个参数时,你可以直接导入它。
要导入一个特定的 GitHub 项目(这里命名为 joustie/github_repo),请执行以下操作:
- Omnibus 安装:
sudo gitlab-rake "import:github[<personal_access_token>,<gitlab user>,<namespace/project>,<source_namespace/github_repo>]"
- 从源代码安装:
bundle exec rake "import:github[<personal_access_token>,<gitlab user>,<namespace/project>,<source_namespace/github_repo>]" RAILS_ENV=production
要从你可用的 GitHub 项目列表中导入一个项目,请执行以下操作:
- Omnibus 安装:
sudo gitlab-rake "import:github[<personal_access_token>,<gitlab user>,<namespace/project>]"
- 从源代码安装:
bundle exec rake "import:github[<personal_access_token>,<gitlab user>,<namespace/project>]" RAILS_ENV=production
也可以指定子组,例如 <groupname/groupname/project>。
对于我的示例项目,你将看到以下结果:

GitHub 导入器使用你的 GitHub 个人访问令牌获取项目列表。当你想指定某个特定项目时,可以使用项目 ID:

导入器之后会要求确认:

rake 任务将同步运行并执行导入操作。导入操作完成后,会提供一个时间总结:

登录到 GitLab 实例后,你将找到导入的项目。
总结
我们在本章开始时解释了可以从 GitHub 迁移到 GitLab 的内容和可能性。关于数据迁移有三种选择,具体可以使用哪种方式取决于你的 GitLab 实例的配置。推荐的方法是使用 GitHub 导入器。这种机制提供了最用户友好且完整的迁移路径。
GitHub 和 GitLab 非常相似,但在下一章中,我们将看看 CVS 和 Git 作为版本控制工具的对比,以及如何迁移它。
问题
-
GitLab 中 pull request 的等价物是什么?
-
将 GitHub 项目导入 GitLab 有哪些方法?
-
显示作者信息需要什么?
-
用于连接到 GitHub 的认证机制是什么?
-
连接到 GitHub 需要的那个秘密是什么?
-
导入尽可能多的 GitHub 内容所需的 token 范围是什么?
-
处理导入的两个队列名称是什么?
-
在 GitLab 中可以在哪里找到 GitHub 的导入按钮?
-
从 GitHub 导入的 rake 任务名称是什么?
-
是否可以只导入一个特定的项目?
进一步阅读
- GitHub Essentials – 第二版,作者:Achilleas Pipinellis:
www.packtpub.com/web-development/github-essentials-second-edition
第六章:从 CVS 迁移
长时间以来,并行版本控制系统(CVS)是版本控制软件的标准。它本质上是一个为软件设计的客户端-服务器型版本控制系统。CVS 由阿姆斯特丹大学的 Dick Grune 于 1986 年编写,最初是一个名为RCS的 Shell 脚本集合。RCS 只能对单独的文件进行版本管理,因此这对版本控制系统来说是一个巨大的进步。1989 年,Brian Berliner 用 C 语言实现了一个版本,并进一步开发;它是一个开源软件,并以 GNU 通用公共许可证(GPL)发布。
在 2000 年代,版本控制的重心转向了 Subversion 以及像 Git 这样的分布式版本控制软件。
在本章中,我们将对比这两种版本控制系统。之后,我们将准备并执行从 CVS 到 Git 的迁移。
本章将涵盖以下主题:
-
CVS 与 Git
-
从 CVS 迁移到 Git 的准备工作
-
执行转换
-
迁移后的清理工作
技术要求
为了跟随本章中的说明,请下载本书的 GitHub 仓库及示例:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter06。
为了能够跟随本章内容,其他要求如下:
-
CVS 二进制文件:
www.nongnu.org/cvs/ -
npm/Node.js 二进制文件用于创建 JavaScript 示例:
nodejs.org -
cvs-fast-export二进制文件:www.catb.org/~esr/cvs-fast-export -
cvs2git二进制文件:www.mcs.anl.gov/~jacob/cvs2svn/cvs2git.html
CVS 与 Git
集中化与去中心化——这是 CVS(集中式)与 Git(分布式设计)之间最大的区别。在 CVS 中,所有开发者都从一个集中式仓库拉取代码,从而形成单点故障。Git 使用去中心化模型,每个开发者都有一个完整的代码仓库本地可用。通过使用推送和拉取请求,分布式仓库之间共享代码。
以下图示展示了在共享和分布式仓库中使用推送和拉取:

使用 Git 时,在网络协议和系统设置方面有不同的选项。特别是,你可以选择通过 SSH 或 HTTP 与远程服务通信。通过使用 SSH,你可以生成 SSH 密钥并设置它们以供 Git 使用。之后,你可以使用 Git 从 SSH 位置克隆/推送或拉取代码。你还可以使用 HTTP 协议,通常需要基本的 HTTP 认证,认证后 Git 操作可以进行。一些 Git 客户端内置缓存机制,帮助你避免每次都输入 HTTP 密码。
在这种情况下,发布工作是通过将更改提交到中央代码库来完成的。CVS 使用一种特定的协议,叫做 pserver,默认情况下是未加密并以纯文本形式传输。另一种选择是使用 远程 shell (rsh) 在远程服务器上。为了提高安全性,你还可以通过 SSH 隧道传输 pserver 协议,或使用 SSH 作为 rsh 可执行文件。
CVS 系统中使用的授权模型非常简单。如果你想进行写操作,你需要提交访问权限给仓库。没有机制可以接受未授权人员的补丁。现代 Git 服务器提供的所有合并请求功能必须由提交者自己执行。因此,从开发工程的角度来看,Git 提供了更复杂的工作流,适用于多人参与的软件项目。
除了基础设施的差异外,你的本地 CVS/Git 客户端之间也有几个差异。
文件集与变更集
CVS 和 Git 在元层次上表示更改的方式不同。CVS 使用文件集,因此更改按文件记录。而 Git 则使用变更集,因此更改是针对 整个 仓库记录的。这样做的优势在于你可以轻松地恢复更改;然而,这也意味着几乎不可能进行部分检出(如果你想这么做的话)。
Git 分支
使用 Git 时,创建新分支非常简单且成本低——它们只是指向特定提交的指针(SHA-1 ID)。你可以随时创建它们,这种方式应该是这样的,特别是在敏捷开发和多个开发人员协作的情况下。
以下图示了 Git 如何使用分支:

注意前面提到的哈希值(98ce9、22ca2 和 f23ae)——这些是唯一标识提交的 SHA 值。
CVS 也使用分支,但由于它是基于文件的,新分支会在一个新目录中复制所有文件,这样效率低得多。以下图示了这种情况:

分支和文件结构的处理方式是这两个系统之间最根本的区别之一。
创建代码库
使用 CVS 创建代码库时,你必须设置一个 CVSROOT 位置,用于存储版本控制数据。然后,你需要将项目导入该位置,并在其他地方创建一个工作副本。使用 Git 则更简单且更具逻辑性,因为你只需在现有目录中执行一次 git init && git add . && git commit,只要创建了一个包含仓库元数据的 .git 目录。
原子操作
CVS 的基本概念是,它操作的是文件,而不是快照。这意味着当更改处理并且操作被中断时,整个仓库处于不一致状态。这与 Git 不同,Git 中要么更改成功,要么在更改未完成的情况下失败。
对象命名或引用版本
在 Git 中,每个对象都有一个唯一的 ID,也就是一个 SHA-1 ID。这个 ID 使得将来引用变得更加方便(你也可以使用简短版本,如果你愿意)。在 CVS 中,每个文件都有自己的版本号。这个数字也反映了文件被修改的次数。在 CVS 中,要引用整个项目的更改,你需要使用标签。
关键字替换
CVS 支持在源代码中替换某些关键字。例如,$Author$、$CVSHeader$ 和 $Id$。开发者们常常使用这个功能(一些开发者现在仍在使用)。要使用这个机制,你需要在文本或源文件中插入这个关键字——我们称之为一个特殊变量。然后,你会将这个文件提交到你的代码库。通过这样做,CVS 会在提交过程中将这个变量替换为 CVS 上下文中的值。很多人都会在他们的 C 源代码中使用 $Id$ 字符串。如果你希望完全禁用这个行为,可以在命令行中指定 -ko。
举个例子,假设我在源文件中添加一个关键字,如下所示:
{
"name": "cvsproject",
"version": "1.0.0",
"description": "something",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "$Author$",
"license": "ISC"
}
然后,我会提交这个更改:
$ cvs commit -m "Added author keyword"
cvs commit: Examining .
cvs commit: Examining images
/Users/joostevertse/cvsroot/cvsproject/cvsproject/package.json,v <-- package.json
new revision: 1.3; previous revision: 1.2
提交后,关键字已被我的用户名替换:
"author": "$Author: joostevertse $",
Git 只支持一组非常有限的关键字,这是因为更改是按仓库进行的,而不是按文件进行的。此外,Git 避免在切换到另一个分支或回滚到另一个时间点时修改那些没有变化的文件。
二进制文件
Git 和 CVS 对待二进制文件的方式不同。在 CVS 中,处理二进制文件更加困难,因为你必须明确标记它们为二进制文件。如果不这样做,你可能会因为不想要的换行符转换或关键字替换而导致文件损坏。另一方面,Git 能够自动检测你的文件是否为二进制文件。它使用与 GNU diffutils 相同的机制。
让我们从源代码创建一个简单的二进制文件并添加一个保留关键字。然后,我们将使用 GCC 编译并运行它:
$ cat hello.c
#include <stdio.h>
int main()
{
// printf() displays the string inside quotation
printf("Hello, World!$Author$");
return 0;
}
$ gcc hello.c -o hello
$ ./hello
Hello, World!$Author$
所以,让我们把这个二进制文件复制到我们之前使用过的 CVS 项目中:
$ cp hello /Users/joostevertse/cvs/cvsproject/cvsproject/
$ cd $HOME/cvs/cvsproject/cvsproject
$ cvs add hello
cvs add: scheduling file `hello' for addition
cvs add: use `cvs commit' to add this file permanently
$ cvs commit -m "binary with keywords inside"
cvs commit: Examining .
cvs commit: Examining images
/Users/joostevertse/cvsroot/cvsproject/cvsproject/hello,v <-- hello
initial revision: 1.1
$ ./hello
Segmentation fault: 11
发生了什么?嗯,关键字替换将作者名称添加到了二进制文件中!它把文件损坏了。如果你运行 cat 命令,你会在文件中找到以下内容:
1ɉE???H??]??%?L??AS?%q?h?????Hello, World!$Author: joostevertse $P44{4
幸运的是,你可以通过 cvs admin 修复这个问题:
$ cvs admin -kb hello
$ cvs update -A hello
$ cvs commit -m "make it binary" hello
$ ./hello
Hello, World!$Author$
修改提交
Git 一个常用的特性是可以修改已提交的内容。这是可能的,因为在 Git 中,创建和发布提交是有区别的。这不会给用户带来不便,而 CVS 就会有这个问题。如果你在 Git 中提交信息写错了,你可以使用 git commit --amend 来修正它。虽然在 CVS 中技术上也是可能的,但实施起来会很困难:
$ echo "This is the last line FOR REAL" >> README.md
$ git log
commit 499beb6dd81ee62e90b05ee8e9aa3ccced7a4fd2 (HEAD -> new-readme)
Author: Joost Evertse <joustie@gmail.com>
Date: Thu Dec 6 21:18:32 2018 +0100
A new line was added
假设我忘记在 README.md 文件(我仓库中的一个文本文件)中添加内容。以下代码展示了如何将其添加到最后一次提交中:
$ echo "This is the last line FOR REAL" >> README.md
$ git add README.md
$ git commit -m "A new line was added FOR REAL" --amend
$ git log
commit 9c527dfe0ac2ce04b6cd1be6085bac00c7f31e6c (HEAD -> new-readme)
Author: Joost Evertse <joustie@gmail.com>
Date: Thu Dec 6 21:18:32 2018 +0100
A new line was added FOR REAL
这样做可以向提交中添加内容,但这只会反映在你的本地 Git 副本中。如果你想让其他人看到你的更改,仍然需要将更改推送到远程服务器。
中央仓库
CVS 作为一个集中式系统只有一个来源地。Git 是一个分布式版本控制系统,这意味着每个开发者都有自己的仓库副本。他们有一个私人仓库,并可以将更改推送到公共仓库,或者从其他远程仓库合并更改。在大型组织中,通常会有一个中央地方来聚合项目。使用 Git 时,不需要一个唯一的中央地方来存储更改。每个开发者都可以拥有自己的仓库(或者更好的方式是有多个仓库,即一个用于开发的私有仓库和一个用于发布已准备好的部分的公共裸仓库),并且可以以对称的方式从其他仓库拉取/获取更改。
在 Git 世界中,没有像 CVS 服务器那样的单一事实来源。可以有多个事实,并且由于如此容易从其他人那里集成更改,这种方式是可行的。
附带工具集
Git 提供了许多工具供你使用(例如 Git bisect),这使得工作方式更加高效。
检测文件重命名
CVS 不支持重命名文件,且在发生重命名时,恢复项目状态非常困难。Git 使用启发式重命名检测,分析文件内容或文件名是否相似。你也可以配置该检测,以便复制文件。
提交后再合并
使用本地仓库时,Git 的一个副作用是它改变了系统中提交的处理方式。使用 CVS 时,你需要先处理冲突(如果别人先修改了内容,你在拉取并更新工作目录时需要解决这些问题)。解决完这些问题后,你可以将更改提交到 CVS 仓库。这种方式叫做合并前提交。Git 完全不同,因为提交始终发生在本地仓库。提交前合并策略意味着你在提交后再合并更改,也可以让其他开发者合并并解决问题。如果有很多合并,区分不同提交之间的更改会变得更加困难,但为了保持线性的历史,也可以通过使用 Git 的rebase机制(git pull --rebase)模拟 CVS 行为,将更改应用到更新后的状态上。
最后的一个大区别是在人与人之间的协作方式。一些开发者只需要原始软件的只读副本,但他们需要匿名的只读访问权限。CVS 和 Git 都可以满足这一需求。如果人们想要向项目贡献某些东西,情况就不同了。使用 CVS,一种做法(例如在 Linux 内核中)是通过电子邮件发送补丁。这通常由那些只改变少量代码的人完成。而在 Git 中,实际上非常容易在现有的上游版本上执行你的更改,然后使用git format-patch生成一个电子邮件。对于较大的贡献,拉取请求的功能和易用性变得很重要,而这正是 Git 所设计的用途。所有这一切都发生在快照范式的背后。
准备从 CVS 迁移到 Git
在下一节中,我们将介绍两种可以帮助我们迁移仓库的工具。
使用cvs-fast-export准备转换
这个迁移工具是由 Eric S. Raymond 创建的,他是一个非常著名的作家/开发者,写过关于开源软件的著名文章《大教堂与集市》。他也是 fetchmail(一个早期的开源 POP3 客户端)的作者。让我们开始吧。
对于这个工具cvs-fast-export,我将从头开始创建一个 CVS 项目,然后进行转换。让我们开始吧:
- 创建一个 CVS 根目录。这是 CVS 数据库将要存放的地方:
$ cvs -d ~/cvsroot init
- 将一些 CVS 相关的环境变量添加到你的环境中:
$ echo "export CVSROOT=~/cvsroot; export CVSEDITOR=vim" >> ~/.bash_profile
$ source ~/.bash_profile
- 例如,我们将创建一个空的 JavaScript 项目并将其添加到 CVS 中。首先,创建一个项目:
$ mkdir cvs
$ cd cvs
$ mkdir cvsproject
$ cd cvsproject/
imac:cvsproject joostevertse$ npm init --yes
- 现在,让我们将项目添加到 CVS 中:
$ cd ..
$ cvs import -m "Example javascript project" cvsproject Joost start
cvs import: Importing /Users/joostevertse/cvsroot/cvsproject/cvsproject
N cvsproject/cvsproject/package.json
No conflicts created by this import
- 现在,项目已经存在于 CVSROOT 中:
$ ls ~/cvsroot
CVSROOT cvsproject
- 现在,我们需要完全清除
~/cvs/cvsproject,因为我们将把它检出为一个 CVS 工作目录:
$ rm -rf cvsproject/
- 下一步是从 CVS 中检出项目,这将创建一个 CVS 工作副本:
$ cvs checkout cvsproject
cvs checkout: Updating cvsproject
cvs checkout: Updating cvsproject/cvsproject
U cvsproject/cvsproject/package.json
- 查看目录会发现一个 CVS 目录结构:
$ tree
.
├── CVS
│ ├── Entries
│ ├── Repository
│ └── Root
└── cvsproject
├── CVS
│ ├── Entries
│ ├── Repository
│ └── Root
└── package.json
3 directories, 7 files
- 让我们创建一个图像目录并将其添加到仓库中:
$ mkdir images
$ cvs add images
Directory /Users/joostevertse/cvsroot/cvsproject/cvsproject/images added to the repository
- 使用
cvs status命令,我们可以查看哪些文件已发生更改:
$ cvs status
cvs status: Examining .
===================================================================
File: package.json Status: Locally Modified
Working revision: 1.1.1.1 2018-12-04 23:01:21 +0100
Repository revision: 1.1.1.1 /Users/joostevertse/cvsroot/cvsproject/cvsproject/package.json,v
Commit Identifier: xmczkBNhTpkbWw2B
Sticky Tag: (none)
Sticky Date: (none)
Sticky Options: (none)
cvs status: Examining images
- 让我们将这些更改提交到我们的 CVS 仓库:
$ cvs commit -m "changed description"
cvs commit: Examining .
cvs commit: Examining images
/Users/joostevertse/cvsroot/cvsproject/cvsproject/package.json,v <-- package.json
new revision: 1.2; previous revision: 1.1
- 使用
cvs log命令,我们可以更详细地查看有哪些更改。
$ cvs log
cvs log: Logging .
RCS file: /Users/joostevertse/cvsroot/cvsproject/cvsproject/package.json,v
Working file: package.json
head: 1.2
branch:
locks: strict
access list:
symbolic names:
start: 1.1.1.1
Joost: 1.1.1
keyword substitution: kv
total revisions: 3; selected revisions: 3
...
现在,我们有了一个准备好迁移的 CVS 仓库,使用的是cvs-fast-export。
使用cvs2git准备转换
现在,让我们看一下第二个工具,cvs2git。对于这个转换,我使用了一个我以前用过的项目的副本(itsmyparty: github.com/Joustie/itsmyparty),如下所示:
$ git clone git@gitlab.com:joustie/itsmyparty_gitlab.git
Cloning into 'itsmyparty_gitlab'...
imac:git joostevertse$ cd itsmyparty_gitlab
imac:itsmyparty_gitlab joostevertse$ tree
.
├── attendees.rb
├── attendees.yml
├── atttendees.yml
├── itsmyparty.rb
....
1 directory, 12 files
然后,我将源代码导入到 CVS 仓库中(我首先删除了.git目录):
$ cvs import -m "dir structure" cvsexample joost start
N cvsexample/machines.yml
N cvsexample/itsmyparty.rb
N cvsexample/presence.yml
N cvsexample/atttendees.yml
.....
No conflicts created by this import
如你所见,这次导入没有产生任何冲突,这很棒。我们现在已经准备好运行转换。
运行转换
有几种方法可以将 CVS 仓库中的数据转换成 Git 仓库。在转换仓库之后,你需要将这个新的 Git 仓库推送到 GitLab 服务器。
使用cvs-fast-export转换数据
执行以下步骤以使用cvs-fast-export转换数据:
- 首先,从 Eric Raymond 的网站下载源文件:
www.catb.org/~esr/cvs-fast-export:
$ wget http://www.catb.org/~esr/cvs-fast-export/cvs-fast-export-1.44.tar.gz
$ tar xvzf cvs-fast-export-1.44.tar.gz
$ cd cvs-fast-export
- 然后,构建软件:
$ make cvs-fast-export
$ cp cvs-fast-export /usr/local/bin; chmod +x /usr/local/bin/cvs-fast-export
- 进入您的
cvsroot目录,并使用find命令将工具与其连接,并将结果输出到文件:
imac:cvs-fast-export-1.44 joostevertse$ cd $HOME/cvsroot
imac:cvsroot joostevertse$ find . |cvs-fast-export -A ~/authormap >../cv.fi
- 然后,初始化一个空的 Git 存储库:
$ cd $HOME/git
$ mkdir cvsproject
$ cd cvsproject
$ git init
Initialized empty Git repository in /Users/joostevertse/git/cvsproject/.git/
- 现在,是时候使用
git fast-import将来自旧 CVS 项目的信息导入到我们空的 Git 存储库中了:
$ cat ../../cv.fi |git fast-import
Unpacking objects: 100% (19/19), done.
/usr/local/Cellar/git/2.15.1_1/libexec/git-core/git-fast-import statistics:
...
- 现在,所有的元信息都已转换,剩下的唯一任务是从 Git 中检出代码:
$ git checkout
$ git status
On branch master
nothing to commit, working tree clean
$ ls
hello package.json
现在,CVS 项目已作为 Git 存储库可用。
使用 cvs2git 转换数据
让我们来看看如何使用cvs2git转换数据:
- 第一步是将 CVS 数据结构迁移到可以通过
git fast-import导入的格式:
$ ./cvs2git --blobfile=/tmp/git-blob.dat \
--dumpfile=/tmp/git-dump.dat "--username=Firstname Lastname" ~/cvsroot
Writing temporary files to '/var/folders/sf/rdjwj43j4kx63xxng1gkgd040000gn/T/cvs2git-ElawtZ
...
- 第一个步骤(
CollectRevsPass)遍历了所有版本文件并分析了它们的修订。接下来的 14 个步骤将转换各种数据并进行迁移:
----- pass 2 (CleanMetadataPass) -----
Converting metadata to UTF8...
Done
...
- 现在,工具将显示导入的统计信息:
cvs2svn Statistics:
------------------
Total CVS Files: 50
Total CVS Revisions: 50
Total CVS Branches: 0
.....
- 下一步是创建一个骨架 Git 存储库,我们可以将转换后的数据导入其中:
$ mkdir itwasmyparty
$ cd itwasmyparty/
$ git init
Initialized empty Git repository in /Users/joostevertse/git/itwasmyparty/.git/
- 然后,我们开始导入,首先导入 blobs(
git-blob.dat):
$ git fast-import --export-marks=/tmp/git-marks.dat < /tmp/git-blob.dat
Unpacking objects: 100% (48/48), done.
git-fast-import statistics:
....
- 接着,我们导入 Git 元数据(
git-dump.dat):
$ git fast-import --import-marks=/tmp/git-marks.dat < /tmp/git-dump.dat
Unpacking objects: 100% (23/23), done.
git-fast-import statistics:
....
- 现在,我们只需检出数据:
$ git checkout
$ git status
On branch master
nothing to commit, working tree clean
现在,您已经有了迁移后的 CVS 存储库。
迁移后的清理
通常,在某种迁移之后,您需要进行清理。清理过程在这种情况下的方式取决于您的使用场景。如果这是一次性迁移,您应该将旧的 CVSROOT 打包成一个 tarball 并放入 DVD 中。您也可以选择让存储库系统共存,甚至从 Git 进行 CVS 存储库的更新。也有方法可以创建双向通信(准备好进行一些 shell 脚本编写)。
如果您已经将 CVS 存储库迁移到 Git,接下来必须做的事情是添加一个指向 GitLab 服务器的远程仓库。这将是创建拉取请求(或者在 GitLab 中称为合并请求)的平台。
在这里,您需要在 GitLab 服务器上创建一个空项目,然后添加远程仓库,如下所示:
git remote add gitlab url-to-gitlab-repo
git push gitlab master
您的旧 CVS 存储库将已经被转换并推送到您的 GitLab 服务器。
总结
在本章中,我们讨论了 CVS 的起源。我们以多种方式将 CVS 与 Git 进行了比较。两者之间的基本区别在于,CVS 是一个集中式版本控制系统,而 Git 则是分布式的。随后,我们设置了一个基本的 CVS 项目,并准备将其迁移到 GitLab。接着,我们制作了一个现有项目的副本,用于第二个工具。最后,我们看到了两种不同的转换方式来迁移存储库。
在下一章中,我们将看看许多人认为是 CVS 的增强版继任者:Subversion。它已经整合了 Git 中也存在的一些功能。
问题
-
CVS 和 Git 之间最大的区别是什么?
-
与 CVS 一起使用的网络协议叫什么?
-
CVS 使用变更集。(正确 | 错误)
-
Git 如何实现版本号管理?
-
如何在 Git 中轻松修正提交信息中的错字?
-
在 CVS 中可以重命名文件吗?
-
初始化新 CVS 数据库的命令是什么?
-
初始化一个新的 Git 项目的命令是什么?
-
谁创建了
cvs-fast-export工具? -
如何将迁移的代码库导入到 GitLab?
进一步阅读
-
cvs-fast-export:www.catb.org/~esr/cvs-fast-export -
Git 版本控制宝典 - 第二版 由 Kenneth Geisshirt、Emanuele Zattin、Rasmus Voss 和 Aske Olsson 编写:
www.packtpub.com/in/application-development/git-version-control-cookbook-second-edition
第七章:从 SVN 切换
CVS 的不足之处促使了其他系统的发展,其中最著名的一个就是 Apache Subversion (SVN) 。除了它带来的改进外,还特别确保了它与 CVS 的高度兼容性。它也采用了开源许可证(Apache 许可证,而非 GNU)。它由 Collabnet Inc. 于 2000 年启动,但在 2009 年转为 Apache 项目。你可以在 subversion.apache.org 找到它。
在本章中,我们将覆盖以下主题:
-
SVN 和 Git 之间的区别
-
使用 GIT 镜像 SVN
-
使用 svn2git 一次性迁移
技术要求
为了跟随本章中的说明,请下载本书的 GitHub 仓库以及来自 github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter07 的示例。
本章的其他要求如下:
-
Git:
git-scm.org -
Git LFS:
git-lfs.github.com -
SubGit:
subgit.com -
svn2git:
github.com/nirvdrum/svn2git
SVN 和 Git 之间的区别
与 CVS 相似,Git 和 SVN 之间最大的区别是,SVN 遵循集中式架构,而 Git 使用分布式网络。SVN 有一个服务器,作为客户端,你与它进行通信。与此不同,Git 可以有多个本地副本,其中一个副本可以存放在中央服务器上。这个架构如下所示:

在 Git 中,有多种关于协议和网络设置的选择。最重要的是,你可以选择通过 SSH 或 HTTP 与远程通信。使用 SSH 时,你将 Git 命令封装起来,可能使用证书认证,而使用 HTTP 时,你通过 WebDAV 和基本的 HTTP 认证来实现 Git 操作。
SVN 有一个抽象的网络层,这意味着客户端表现出的行为相同,无论它们操作的是哪种服务器。
有几种不同的服务器选项,具体如下:
-
svnserver 服务器:这个设置起来很简单,不需要服务器上的系统账户,比 WebDAV(通过 HTTP 协议扩展文件系统访问)更快。一个大的缺点是,默认情况下通过这个服务器进行的通信是不加密的。
-
通过 SSH 使用 svnserver:它具备我们之前提到的所有优点,但通过 SSH 加密保护。它的工作原理是通过 SSH 会话隧道化 svnserver 的流量。
-
带有 dav_svn 模块的 Apache Web 服务器:在这种情况下,所有网络层的流量都是通过 HTTP 进行的。著名的 Apache Web 服务器支持 WebDAV 操作,并且有一个专门的 Apache 模块处理 SVN 流量(
dav_svn)。由于 HTTP 协议的开销,它的速度明显较慢,但有一个额外的好处,那就是可以通过网页浏览器浏览仓库。
除了它们在基础设施上的差异外,你的本地 SVN/Git 客户端之间还有一些其他差异。
安全性和访问控制
使用 SVN 时,你可以设置服务器以授予或拒绝用户权限。你甚至可以基于路径定义更细粒度的访问规则。所有这些都在一个中央位置进行配置。而使用 Git 时,默认情况下客户端上没有访问控制。你的 Git 服务器实现或运行 Git 的中央仓库必须为你提供访问控制。GitLab 就具备这个功能。
在 SVN 的世界里,仓库的变更历史是集中保存在服务器上的,要更改它,你需要获得对这个中央位置的访问权限。
由于 Git 是一个分布式版本控制系统,每个开发人员都可以更改其本地仓库历史的任何部分。尽管强烈不建议推送更改过的历史,但这是可能的。如果其他开发人员依赖于特定的更改,这可能会造成混乱。
对于 Git 用户来说,仓库的完整历史会保存在本地,并从远程更新或向远程更新,因此始终存在本地副本。
定期备份对于 Git 和 SVN 都是明智的做法。即使有中央服务器和几个分布式副本,你仍然需要保持同步以确保数据的可用性。
空间要求和引用
相比 Git,SVN 能做的一件事是,你可以检出仓库的部分内容。而在 Git 中,仓库只能作为一个整体进行克隆。
在 Git 中,每个对象都有一个唯一的 ID,即 SHA-1;例如,921103db8259eb9de72f42db8b939895f5651422。
这样更容易引用。你也可以使用缩短版本(921103d):
$ git rev-parse --short 921103db8259eb9de72f42db8b939895f5651422
921103d
在 SVN 中,一个文件总是最新版本。要引用文件的更改,你需要使用 修订版本。该修订版本指向整个仓库。
SVN 工作目录包含每个文件的两个副本,这也是 Git 仓库通常比 SVN 小的原因 —— Git 仓库只包含一个副本。一个克隆的 Git 仓库目录包含一个小型索引文件,每个被跟踪的文件大约有 100 字节的索引数据。
如果一个项目包含大量文件,SVN 和 Git 之间的大小差异可能会变得相当大!大多数人没有意识到的一点是,SVN 可以跟踪空目录,而 Git 则不能!Git 只跟踪文件内容,因此空目录不会显示出来!下图展示了 SVN 如何处理更改:

另一方面,Git 使用图形模型,具体如下:

如我们所见,SVN 和 Git 在大小和引用方式上的差异非常大!
分支
Git 和 SVN 都支持使用分支。然而,在 SVN 中,分支是可能的工作流和样式的一部分,而在 Git 中,分支的使用是命令面板和标准工作方式的一部分。
SVN 的一个主要缺点是分支和合并的方式。如果你的仓库很大,这个过程可能会耗费很长时间。如果你在 SVN 中创建一个新的分支,你会在仓库内创建一个完全新的目录,这意味着结构中存在重复。当分支准备好或不再需要时,你将其提交回主干。
Git 和 1.5 之前的 SVN 版本之间的一个重大技术差异是,Git 默认使用三方合并,而 SVN 使用的是双方合并。它无法执行三方合并,因为它不存储合并信息。Git 通过使用其图形数据库,可以检查代码库共享的共同状态,然后从分叉点开始合并,这技术上就是三方合并。在 SVN 的最新版本中,这也被纳入其中,因为合并后会保存有关分支和合并的元信息。不幸的是,基本问题仍然存在——一个分支是一个完整的副本(而不是引用)。
同时,主干可能已经有了更改。你的版本不会包含开发者分支中的更改。这意味着你可能会遇到冲突的更改、文件或在你的分支中缺失的结构。
开发者如此喜欢 Git 的主要原因是它强大的分支模型。与 SVN 中的多次重复相比,Git 只创建指向特定提交的引用,这样就减少了重复,也减少了空间和 I/O 的浪费。通过创建、删除或更改分支来实例化引用不会影响提交。想尝试一下吗?快速修复一个 Bug?只需创建一个分支,编辑文件,然后将提交推送到中央仓库,再删除分支。轻松创建!
使用 SVN 和 Git 处理二进制文件
速度通常被认为是 Git 相较于 SVN 的主要优势。然而,在处理二进制文件时,情况并非完全如此。如果开发者每次都检出完整的仓库,并且仓库中包含不断变化的二进制文件,那么你就会失去这种速度优势。
在 SVN 中,只有工作树和最新的更改会被检出到本地系统。当对二进制文件进行大量更改时,SVN 检出这些文件所需的时间比 Git 少。
当然,Git 也有处理二进制文件的变通方法,最著名的就是 Git LFS(git-lfs.github.com/)。这是 GitHub 开发的一个解决方案,它是 Git 的一个扩展。通过它,你可以在仓库中存储一个指针,而不是一个大的二进制文件。不过,每次开发者操作都会导致大量历史数据的变更,这会使操作变得更慢。
GitLab 也支持 Git LFS 操作。根据你或管理员配置 GitLab 的方式,LFS 上传的数据可以存储在 GitLab 服务器本身,也可以存储在连接到你 GitLab 服务器的共享服务器存储上。或者,它可以存储在与 S3 兼容的服务中。
你可以在多个平台上安装 Git LFS(github.com/git-lfs/git-lfs/wiki/Installation)。主要的要求是你已安装 Git 版本 1.8.2 或更高版本。
让我们尝试使用 git-lfs:
- 我们将在 macOS 上尝试,并通过
brew安装:
$ brew install git-lfs
==> Downloading https://homebrew.bintray.com/bottles/git-lfs-2.6.1.high_sierra.bottle.tar.gz
==> Downloading from https://akamai.bintray.com/0d/0daf04ca0a32e208be0e6df07c42a1ab049a3e50c962b04ea650a626a97920bb?__gda__=exp=1545082825~hmac=321540978a32b9bda7e114cc68cdddb1c772d02d8c93ed919a0d04bff4075377&respo
######################################################################## 100.0%
==> Pouring git-lfs-2.6.1.high_sierra.bottle.tar.gz
==> Caveats
- 更新你的 Git 配置以完成安装:
$ git lfs install
$ git lfs install --system
- 当你安装了 Git LFS 后,你需要为本地仓库启用该功能:
$ git lfs install
Updated git hooks.
Git LFS initialized.
- 告诉 Git 哪些文件你认为是大文件。完成后,将
.gitattributes文件添加到提交中:
$ git lfs track "*.dmg"
Tracking "*.dmg"
$ git add .gitattributes
- 下一步是添加并提交你的更改:
$ git add .
$ git commit -m "Testing lfs"
[master eb9ed7c] Testing lfs
2 files changed, 4 insertions(+)
create mode 100644 .gitattributes
create mode 100644 OpenRA-release-20180923.dmg
- 现在,当你将仓库推送到远程服务器时,你会注意到不同的行为。Git LFS 正在处理部分上传:
$ git push
Locking support detected on remote "origin". Consider enabling it with:
$ git config lfs.https://gitlab.com/joustie/itsmyparty_gitlab.git/info/lfs.locksverify true
Uploading LFS objects: 100% (1/1), 35 MB | 979 KB/s, done
Counting objects: 4, done.
Delta compression using up to 16 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 487 bytes | 0 bytes/s, done.
Total 4 (delta 1), reused 0 (delta 0)
To gitlab.com:joustie/itsmyparty_gitlab.git
6b64bcc..eb9ed7c master -> master
所以,即使 SVN 处理文件更快,使用 Git LFS 时,你依然可以获得相同的优势。
如果你将 Git LFS 与 SVN 进行基本比较,那么 SVN 在处理二进制文件时速度更快。如果你使用 Git LFS 进行 Git 操作,它们的表现大致相同。
镜像 SVN 和 GIT
为了用 Git 镜像 SVN,我们将使用 SubGit 工具(subgit.com/),该工具由 TMate 软件维护和销售。你可以下载适用于你操作系统的版本,或者选择基本版,这是一个跨平台的 Java 二进制文件。如果你解压下载的包,你可以在 bin 目录中找到 SubGit 工具。
SubGit 应该已在你的 Git 服务器上设置。它会扫描你指定的远程 SVN 仓库的设置,然后下载 SVN 修订并将其转换为 Git 提交。SubGit 会保持两个仓库的同步。当用户向 Git 推送新提交时,SubGit 会转换并尝试更新 SVN。它还会在 SVN 中出现新修订时立即获取。SVN 和 Git 用户会看到彼此的提交,仿佛他们都在同一个版本控制系统中工作。SubGit 确保不同系统之间不会发生冲突,并保持镜像的完整性。
在镜像模式下运行 SubGit 需要你在 TMate 软件上注册该软件。对于开源、学术和初创项目,注册是免费的:
- 首先,在 GitLab 中创建一个空的项目:

- 然后,在运行 GitLab 实例的机器上打开终端,并创建以下环境变量。
SVN_PROJECT_URL应包含你想要复制/转换/镜像的 SVN 项目的链接:
$ export GIT_REPO_PATH=$HOME/git/pdf.git
$ export SVN_PROJECT_URL=svn://svn.riscos.info/pdf/trunk/
- 确保你在机器上配置了 Java:
$ apt-get install openjdk-8-jdk
Reading package lists... Done
Building dependency tree
Reading state information... Done
...
...
Setting up openjdk-8-jdk:amd64 (8u191-b12-0ubuntu0.16.04.1)
...
- 现在,我们可以开始第一次运行 SubGit 工具,它将配置所有内容,以便镜像或一次性迁移成功(我已将
subgit二进制文件从软件包复制到/opt/subgit/bin):
$ /opt/subgit/bin/subgit configure --layout auto $SVN_PROJECT_URL $GIT_REPO_PATH
SubGit version 3.3.5 ('Bobique') build #4042
Configuring writable Git mirror of remote Subversion repository:
Subversion repository URL : svn://svn.riscos.info/pdf
Git repository location : /var/opt/gitlab/git-data/repositories/root/pdf.git
Git repository is served by GitLab, hooks will be installed into 'custom_hooks' directory.
Peg location detected: r35 trunk
Fetching SVN history... Done.
Growing trees... Done.
Project origin detected: r1 trunk
Building branches layouts... Done.
Combing beards... Done.
Generating SVN to Git mapping... Done.
CONFIGURATION SUCCESSFUL
要完成 SubGit 的安装并持续运行,请执行以下操作:
- 如有需要,请在以下文件中调整 Subversion 到 Git 的分支映射:
/var/opt/gitlab/git-data/repositories/root/pdf.git/subgit/config
- 在默认的 SubGit 密码文件中定义至少一个 Subversion 凭证,如下所示:
/var/opt/gitlab/git-data/repositories/root/pdf.git/subgit/passwd
或者,您可以在以下根目录的[auth]部分配置 SSH 或 SSL 凭证:
/var/opt/gitlab/git-data/repositories/root/pdf.git/subgit/config
- 可选地,将自定义作者映射添加到以下文件中的
authors.txt文件:
/var/opt/gitlab/git-data/repositories/root/pdf.git/subgit/authors.txt
- 运行
subgit install命令:
$ subgit install /var/opt/gitlab/git-data/repositories/root/pdf.git
如果查询进程列表,您将看到 SubGit 守护进程正在运行:
$ ps ax |grep subgit |grep -v grep
17314 ? Ssl 0:00 /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java -noverify -client -Djava.awt.headless=true -Djna.nosys=true -cp /var/opt/gitlab/git-data/repositories/root/pdf.git/subgit/lib/subgit-3.3.5_4042_fat.jar org.tmatesoft.translator.SubGitDaemon test --svn /var/opt/gitlab/git-data/repositories/root/pdf.git --limit 1544992584093
如果在通过 SubGit 安装完成后直接查看 GitLab 中的项目,您会发现仍然看不见任何内容。这是因为用户界面缓存的原因。到目前为止,我们已在文件系统上执行了操作,需要刷新 Redis 缓存。
您可以在 GitLab 服务器上使用以下命令进行此操作:
$ gitlab-rake cache:clear
现在,导入的项目应该可见:

现在,我们有了两个同步的源代码仓库。有时,您只需要单向转换,这就是我们将在下一部分介绍的内容。
不需要同步,只需转换
您还可以使用 SubGit 工具进行一次性迁移。对此不需要许可证,而且它是免费的。只需下载工具并运行即可。
因此,不要使用启用同步的install,只需使用import作为参数:
$ /opt/subgit/bin/subgit import $GIT_REPO_PATH
SubGit version 3.3.5 ('Bobique') build #4042
Translating Subversion revisions to Git commits...
Subversion revisions translated: 35.
Total time: 9 seconds.
IMPORT SUCCESSFUL
完成此操作后,您可以刷新缓存,以便在 Web 界面中看到更改反映出来。
相比于同步,一次性转换是一个简单的操作,但也有其他工具可以实现这一点。
使用 svn2git 进行一次性迁移
正如您在 SubGit 中看到的那样,可以在 SVN 和 Git 之间创建一个同步解决方案。实际上,大多数时候,当您想迁移到一个新系统时,您会希望一次性完成。这减少了出错的可能性,也更容易推理。因此,当您进行这样的硬性迁移时,让您的开发人员使用新的仓库。设置同步在长期来看并不能帮助您迁移。与 SubGit 相比,您可以使用自己的工作站进行转换。
您可以使用像 svn2git 这样的工具(github.com/nirvdrum/svn2git)进行一次性转换。如果您已经安装了 Ruby 和 Git,可以将其作为 Ruby Gem 安装:
$ sudo gem install svn2git
在基于 Debian 的 Linux 发行版上,您可以安装本地软件包:
$ sudo apt-get install git-core git-svn ruby
如果您希望项目中的作者正确显示,可以确保在转换过程中正确地将 SVN 中的作者映射到 Git。这取决于您是否创建了作者文件。如果选择不创建,则不会进行映射。在某些情况下,这可能会成为一个问题,而有些用户则完全不在乎。如果您希望映射用户,请确保映射 SVN 仓库中的每个作者。没有正确映射将导致转换失败,您需要重新开始。
使用以下命令,您将获得仓库中所有作者的列表。在 SVN 源仓库中运行以下命令。我是在我的工作站上执行的:
$ svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
cgransden
peter
在这种情况下,只有两个作者。使用以下输出创建 authors.txt 文件,并逐行映射作者:
cgransden = cgransden <cgransden@gitlab.joustie.nl>
peter = peter <peter@gitlab.joustie.nl>
我们正在转换的仓库具有合理的默认结构。它有 trunk、branches 和 tags。如果您的 SVN 仓库更复杂,您需要使用更多选项。您可以在之前提到的 svn2git 文档的主页上找到这些选项,或者使用 svn2git --help。
确保在一个空目录中运行 svn2git 转换命令。
svn2git 命令行的默认格式是 https://svn.example.com/path/to/repo --authors /path/to/authors.txt。
在我的示例中,我们没有更改作者,所以我们将忽略他们。如果您的 SVN 仓库受到用户名和密码保护,您还可以添加 --username 'password' 和 --password 'password' 选项:
$ svn2git svn://svn.riscos.info/pdf
Initialized empty Git repository in /Users/joostevertse/svn/pdf.git/.git/
r1 = 154856522ddf7c81f34dc80b11a41b963dcc2c13 (refs/remotes/svn/trunk)
A !PDF/!sprites22,ff9
A !PDF/Documents/Help.html,faf
A !PDF/Documents/Licences/Copying
A !PDF/Documents/Licences/BSDLicence
A !PDF/Documents/Licences/README
...
下一步是通过将迁移后的仓库推送到 GitLab 完成转换。最好在 GitLab 中创建一个空项目,并获取项目的 HTTP 或 SSH 地址。然后,您可以将其作为远程仓库添加到本地仓库并推送。这将包含所有的提交和分支。
转换完成后,您可以通过在本地创建一个新的远程仓库并推送仓库,将项目导入 GitLab:
$ git push --all origin
Counting objects: 1009, done.
Delta compression using up to 16 threads.
Compressing objects: 100% (414/414), done.
Writing objects: 100% (1009/1009), 1.39 MiB | 0 bytes/s, done.
Total 1009 (delta 591), reused 1009 (delta 591)
remote: Resolving deltas: 100% (591/591), done.
To https://gitlab.joustie.nl:8443/root/pdf.git
* [new branch] master -> master
如果有标签,别忘了推送它们:
$ git push --tags origin
Everything up-to-date
这将通过 svn2git 完成转换,svn2git 是您可以用来一次性将 SVN 迁移到 Git 的第二个工具。
总结
在本章中,我们首先追溯了 SVN 的起源以及它为何流行。随后,我们在版本控制系统的相关方面(如架构、分支方法和如何处理二进制文件)上,对 SVN 和 Git 进行了比较。
本章的第二部分讲解了如何将 SVN 项目迁移到 Git。我们讨论的第一个工具是 SubGit。它不仅能将 SVN 项目迁移到 Git,还可以作为代理让两个仓库共存。我们谈到的第二个工具是 svn2git,它能够一次性完成迁移。这些工具之间的另一个显著区别是,SubGit 安装在您的 GitLab 服务器上,而 svn2git 可以在您的工作站上运行。
在下一章,我们将介绍另一种类型的源代码控制系统。这个系统由微软创建,并且不是开源的。
问题
-
SVN 项目的主页是什么?
-
SVN 和 Git 之间最大的区别是什么?
-
列举运行 SVN 服务器的三种不同方式。
-
在 SVN 中,项目的历史记录保存在哪里?
-
Git 使用 SHA,而 SVN 使用什么?
-
在 1.5 版本之前,SVN 执行的是什么类型的合并?
-
Git LFS 需要什么版本的 Git?
-
列举 GitLab 实现 LFS 作为存储后端的两种方式。
-
SubGit 支持哪些两种迁移机制?
-
当你使用 svn2git 时,迁移到 GitLab 的最后一步是什么?
进一步阅读
-
SubGit:
subgit.com/ -
svn2git:
github.com/nirvdrum/svn2git -
SVN 红皮书:
svnbook.red-bean.com/ -
SVN 文档:
subversion.apache.org/docs/
第八章:从 TFS 迁移仓库
团队基金会服务器(TFS)是微软应用生命周期管理(ALM)解决方案的协作平台和基础,提供代码版本管理(包括包管理(NuGet、Maven 等))、工作项管理、丰富的报告和仪表板功能、自动化构建与发布管理、以及测试管理。通过与如 Visual Studio 等开发工具的广泛集成,TFS 用于与设计、构建(持续集成)、测试和部署(持续交付)软件的过程进行沟通与协作,最终目的是提高生产力和团队产出,改善质量,并深入了解应用生命周期。
在本章中,我们将比较 TFS 与 Git,然后我们将把一个老旧的 TFVC(Team Foundation Version Control)格式的 TFS 仓库迁移到 Git。请注意,现如今微软已经统一使用 Git 进行版本管理,因此 TFVC 不再广泛使用。
在本章中,我们将涵盖以下主题:
-
TFS 与 Git
-
git-tfs 工具
技术要求
要跟随本章的指示,请从github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter08下载本书的 GitHub 仓库和示例。
本章的其他要求如下:
-
Git:
git-scm.org -
Azure DevOps 账户:
azure.microsoft.com/nl-nl/services/devops/ -
Git-tfs:
github.com/git-tfs/git-tfs -
GitLab 账户:(
gitlab.com或本地)
TFS 与 Git
微软创建 TFS 作为一个产品,旨在帮助团队开发软件。
它具有以下功能:
-
源代码管理
-
需求管理
-
实验室管理
-
报告
-
项目管理(适用于敏捷软件和瀑布式开发)
-
自动化构建
-
测试
-
发布管理功能
该解决方案旨在帮助产品的整个生命周期(ALM)。它可以与多个集成开发环境(IDEs)合作,但与微软自家的 Visual Studio 配合使用时效果最佳,现如今它已经是跨平台的。
它已经存在一段时间了,但目前的重点是让客户使用 TFS 的云版本:Azure DevOps。你可以获得本地 TFS 的所有功能,但它的可扩展性更强。
对于源代码管理,支持两种不同类型的仓库。最初,默认的是 Team Foundation Version Control (TFVC),它类似于 SVN,属于集中式 版本控制系统 (VCS)。另一种选择是 Git。在过去几年中,Git 已成为微软的默认版本控制系统。你可能找不到一个不使用 Git 的团队(可能除了负责 TFS 的团队)。
如果你想将 TFS Git 仓库迁移到 GitLab,这是非常简单的。例如,你可以将仓库迁移到一个新的本地 GitLab 实例:
cd existing_repo
git remote rename origin old-origin
git remote add origin https://gitlab.example.com/me/newprojectingitlab.git
git push -u origin --all
git push -u origin --tags
迁移 TFVC 项目需要更多的努力,我稍后会进行解释。首先,让我解释一下 TFVC 和 Git 之间的区别。我们从最重要的一点开始:它们是集中式的还是分布式的。
集中式或分布式
TFVC 的基本设计是一个集中式仓库,位于服务器上,授权的客户端连接到它以交换信息。Git 的分布式特性与此完全相反,默认情况下没有中央权限,可以自由地与他人推送和拉取变更。当然,你可以创建一个中央服务器,存放一个 Git 仓库的副本,大家都认为这是最新的版本。这种差异可以在下图中看到。Git 仓库可以存在于本地 TFS 或 Azure 实例中,而 TFVC 仓库只能存在于一个中央位置:

处理变更
你可以使用的最佳工具无疑是 Visual Studio。让我们讨论一些关于它如何处理文件变更的要点。
分支和合并能力
与 Git 中的分支相比,Git 的分支是仓库范围的,而 TFVC 的分支是路径范围的,且不如 Git 分支轻量。一般来说,团队成员会为他们正在处理的每个分支创建额外的工作区。变更是与分支独立的,因此为了避免集成时的混乱,你需要尽可能多地进行前向集成。TFVC 的标准合并使用的是双向合并(无基准)。
将创建分支的标准设定得高一些,只有在需要代码或发布隔离时才创建分支。在 TFVC 中使用分支更消耗资源。随着功能分支数量的增加,存储需求和分支层次结构的可视化模糊性也会增加。在下面的截图中,作为开发者,你可以看到无法合并,因为存在冲突:

在 Git 中,分支仅仅是指向提交的指针。Git 中的默认差异比较使用三方合并,这也有助于开发者进行合并。
历史
由于 TFVC 的集中式特点,当你没有连接时,有些信息是不可用的。文件历史不会被复制到客户端的开发机器上,只能在你在线连接服务器时查看。这些信息可以通过 Visual Studio 和网页门户查看。通过项目项的右键菜单,你可以注释文件,以查看谁修改了某一行代码,以及他们修改的时间。以下截图展示了在网页门户中可以查看的相关信息:

在 Git 中,文件历史记录会被复制到客户端的开发机器上。当没有连接到 TFS 服务器时,你仍然可以查看文件历史。你可以在 Visual Studio 和网页门户中查看,也可以在你使用的特定 Git 服务器(如 GitLab)中查看。通过使用 Git 的命令选项,你可以查看谁修改了文件的哪些行,以及为什么进行修改。这可以成为识别代码变更的有用工具,类似于 TFVC 中的注释功能。请看以下git blame命令:
$ git blame BSDLicence
e2cc9eb2 (peter 2010-01-08 20:35:51 +0000 1) The files
e2cc9eb2 (peter 2010-01-08 20:35:51 +0000 2)
e2cc9eb2 (peter 2010-01-08 20:35:51 +0000 3) !PDF.Res
e2cc9eb2 (peter 2010-01-08 20:35:51 +0000 4) !PDF.Sprites
e2cc9eb2 (peter 2010-01-08 20:35:51 +0000 5) !PDF.Sprites22
e2cc9eb2 (peter 2010-01-08 20:35:51 +0000 6) !PDF.Messages
e2cc9eb2 (peter 2010-01-08 20:35:51 +0000 7) !PDF.Documents.Help/html
e2cc9eb2 (peter 2010-01-08 20:35:51 +0000 8) !PDF.Documents.Help/txt
我们可以看到peter修改了这个文件。
可追溯性
对于某些使用场景,良好的可追溯性至关重要。想象一下像银行和保险公司这样的企业,它们由于法规的要求必须遵守严格的政策。由于 TFVC 是一个集中式的存储库类型,它的可追溯性开箱即用就相当好。所有的变更操作都记录在服务器上,且仅由授权的人员执行。问题、缺陷、看板和待办事项之间有直接的链接。
在 Git 中,也可以设置相同的系统,但你需要确保你的集中式存储库强制执行一些额外的检查,并处理实体之间的链接(例如,在 GitLab 中进行问题跟踪)。默认情况下,任何人都可以修改 Git 存储库的历史记录,因此开箱即用时,Git 的可追溯性并不好。
文件处理
Git 和 TFVC 之间的另一个大区别是在处理变更时。这个区别与 TFVC 使用集中式存储库,而 Git 本质上是分布式的这一事实相关。TFVC 在其中央服务器上保持项目中所有文件的单一根路径。在这种上下文下,可以在文件级别应用权限或锁定中央服务器上的文件。默认情况下,Git 或基本的 GitLab 配置无法实现这一点,因为没有中央位置可以强制执行这些权限。你可以在某个地方锁定文件,但开发人员可以继续在本地开发并修改代码。
另一方面,在 Git 中,你可以在一个 GitLab 项目中拥有多个存储库,并且可以在分支级别或存储库级别进行保护。你还可以定义多个远程仓库来推送代码。这意味着你可以将代码存储在 GitLab 中,也可以存储在 Windows Azure 上。以下截图展示了你可以用来在 TFVC 中锁定文件的菜单:

在 TFVC 中,所有文件都位于一个路径下并且受到控制,而在 Git 中,可以有多个分支和分布在不同远程服务器上的文件。要强制实施集中式控制是很困难的,甚至是不可能的。
git-tfs 工具
有几种方法可以将数据从 TFVC 迁移到 Git。最简单的方法是在 TFS 本身进行迁移。你可以使用他们自己提供的导入/导出工具,访问docs.microsoft.com/en-us/azure/devops/repos/git/import-from-tfvc?view=azure-devops&tabs=new-nav&viewFallbackFrom=vsts网站。
还有另一个可以用于迁移的工具。就像 git-svn 一样,可以在 TFS 和 Git 之间建立一个双向网关。它可以将 TFS 提交放入 Git 仓库,并允许你将更改推送回 TFS。
这些工具的存在是因为微软多年前内部切换到了 Git,并且他们已经为 Git 代码库贡献了大量源代码。这就是为什么如今在 Azure 等平台上创建新仓库时默认使用 Git 格式的原因。
准备迁移
在本节中,我们将演示如何迁移位于 Azure DevOps 上的 TFVC 项目。首先,我们将在 Azure DevOps 中找到我们的项目。你可以通过导航到你的组织页面来完成此操作:dev.azure.com。组织的设置超出了本书的范围,但如果你是小公司或开源项目,这非常简单且免费。你可以在 Azure 页面阅读更多内容,网址是:azure.microsoft.com/nl-nl/services/devops/。
git-tfs 迁移工具的项目页面可以在 GitHub 上找到(github.com/git-tfs/git-tfs)。在安装 git-tfs 之前,你需要考虑一些先决条件:
-
你需要一台 Windows 机器来进行安装。
-
必须安装 Git for Windows。
可以从github.com/git-tfs/git-tfs/releases下载 git-tfs 二进制文件。或者,你也可以通过 chocolatey(chocolatey.org/)进行托管安装。使用这样的包管理器安装可以为你处理必要的细节。当然,你也可以自己构建该包,因为源代码也可以获得。对于这些示例,我们将使用我已经设置好的基本 Windows 机器。我们开始吧:
- 使用 chocolatey 安装 Git 非常容易:

- 下一步是使用
gittfs包安装 git-tfs:

- 为了进行迁移,我们需要在 GitLab 实例中创建一个新项目,以便为我们的 Azure DevOps 项目提供一个目标位置:

- 现在,我们将使用 git-tfs 工具克隆我们在 Azure DevOps 中创建的测试项目。它将弹出一个认证窗口:

- 它会自动搜索变更集并将其转换为 Git:

- 为了将这个本地 Git 仓库复制到我们的 GitLab 项目,我们需要创建一个远程条目:
c:\git\MovingToGitlab>git remote add origin https://gitlab.joustie.nl:8443/root/movingtogitlab.git
- 然后,我们将使用
push -u将其推送到 GitLab。Git for Windows 会呈现一个登录界面,你可以在该界面通过 gitlab.joustie.nl 进行身份验证:

- 当迁移完成后,你可以登录到你的 GitLab 项目。在这里,你将看到推送的代码:

- 你可以使用
git log查看 Git 中的提交历史:

- 如果你将其与 TFVC 中的历史记录进行对比,你会发现它们是一样的:

通过使用 git-tfs 工具,迁移你现有的 TFVC 到 Git 是相对容易的。
总结
本章解释了 TFS 是什么,以及它在 Microsoft 产品系列中的地位。首先,我们比较了 TFVC 和 Git 在架构上的差异,以及它们处理分支和合并的方式。我们还考察了它们如何处理历史记录和变更的可追溯性。
从这里,我们了解到有多种方法可以从 TFS 迁移到 Git。例如,你可以直接从服务器本身导出。也有一个工具可以用来在 TFS 和 Git 仓库之间创建镜像,这个工具叫做 git-tfs。这个工具存在的最合理原因是因为,现如今,Git 在很大程度上依赖于 Git 仓库,并需要将 TFVC 项目内部转换为 Git 格式。
本章结束了本书关于将数据从其他系统迁移到 GitLab 的部分。在下一部分,我们将广泛讨论如何连接到 GitLab 的方法。
问题
-
TFS 在 Microsoft 的 ALM 套件中用于什么?
-
TFVC 和 Git 之间最大的区别是什么?
-
TFS 是 Azure 中的哪个产品的一部分?
-
如何将 TFS Git 仓库迁移到 GitLab?
-
在 TFVC 中,分支是如何作用域化的?
-
TFVC 的历史记录保存在什么地方?
-
哪个工具类似于 git-tfs?
-
在 Windows 上,哪个工具使得安装 Git 变得容易?
进一步阅读
-
《Microsoft Team Foundation Server 2015 Cookbook》,Tarun Arora 著:
www.packtpub.com/networking-and-servers/microsoft-team-foundation-server-2015-cookbook -
《Implementing DevOps with Microsoft Azure》,Mitesh Soni 著:
www.packtpub.com/networking-and-servers/implementing-devops-microsoft-azure -
TFS 网站:
visualstudio.microsoft.com/tfs/ -
Git-tfs:
github.com/git-tfs/git-tfs
第三部分:实现 GitLab DevOps 工作流
在本节中,您将理解并能够实现 GitLab 在工作流中提供的所有功能,或者选择工作流中所需的部分。
本节包含以下章节:
-
第九章,GitLab 视图 - 一个应用中的完整工具链
-
第十章,创建您的产品,验证并打包它
-
第十一章,发布和配置阶段
-
第十二章,使用 Prometheus 进行监控
-
第十三章,将 GitLab 与 CI/CD 工具集成
第九章:GitLab 视角 - 一个应用程序中的完整工具链
本章旨在提供更多关于 GitLab 背景的深入了解。该产品实际上是为了帮助解决敏捷运动所面临的一些问题而创建的。我们将讨论开发方法论的历史以及敏捷作为主流软件开发方式的崛起。敏捷方法论逐渐渗透到传统运维部门,进而引发了 DevOps 运动。最后,我们将总结一些 DevOps 工作方式中使用的工具。
本章将涵盖以下主题:
-
敏捷宣言
-
极限编程(XP)
-
DevOps 运动
-
工具链
技术要求
若要跟随本章的说明操作,请从 GitHub 下载包含示例的 Git 仓库:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter09。
敏捷宣言
上世纪 90 年代,一些人对将软件开发与建筑工程进行对比的经典工程思维感到不满。与其努力保持需求的稳定,防止需求蔓延或范围膨胀,他们寻找了一种不依赖于需求稳定性的过程。这些人提出了许多不同的想法,作为回应,这些方法通常被称为轻量级方法。所有这些方法与精益制造方法共同构成了敏捷运动,并随着时间的推移变得非常流行。
那么,Agile(敏捷)究竟是什么?如今为什么大家都在谈论它?对某些人来说,它是一种生活方式和看待事物的方式。在 IT 领域,根据谷歌的定义,它是一种项目管理方法,特别用于软件开发,特点是将任务分解为短期工作阶段,并频繁重新评估和调整计划。
官方上,敏捷不是一种方法,而是一个集合术语。敏捷源于多种开发产品(主要是软件)的方法。例如,XP、Scrum、动态系统开发方法(DSDM)、适应性软件开发和 Crystal 等。这些方法具有一个共同的特点,那就是它们都旨在减少产品和软件开发中的官僚主义,并接受变革。在 80 年代和 90 年代,这些独立的方法由各种专家发展,最终在 2001 年制定了敏捷宣言。这些方法主要是为了避免传统瀑布式方法应用中出现的问题。
所有来自不同敏捷领域的大咖们最终决定非正式地聚在一起,讨论如何帮助 IT 改进。敏捷宣言就是在这次 17 名软件开发人员的会议中制定的。会议从 2001 年 2 月 11 日到 2 月 13 日在犹他州雪鸟山的 The Lodge 举行。敏捷这个名字也是在这里确定的。听说当时“轻量级方法”也在讨论中,但最终选择了敏捷这个名字。
初始模型 – 瀑布模型
当人们谈论瀑布模型的起源时,常常说 W. W. Royce 在 1970 年通过论文《Managing the Development of Large Software Systems》(www-scf.usc.edu/~csci201/lectures/Lecture11/royce1970.pdf)提出了这一模型。Royce 本人实际上信奉迭代的软件开发方法,甚至没有使用“瀑布”这个术语。Royce 将瀑布模型描述为一种他认为过于极端的方法,甚至是注定会失败的提议:“我相信这个概念,但上述描述的实现方式风险过高,容易失败,”Royce 写道。
在 1970 年,Royce 认为瀑布方法必须视为一个初步概念,因为他觉得该方法存在一些错误。他发表了一篇文章,探讨了如何将这一初步概念发展成一个重复的模型。在这个增强的模型中,每个阶段之间有了更多的反馈,这与我们现在常见的方法更为相似。对 Royce 来说令人恼火的是,只有初步方法受到了关注,而他对这种方法的批评大部分被忽视了。
Royce 的意图是将该模型从论文中的初步概念转变为一个迭代模型;然而,原始方法仍被广泛使用并被理想化。然而,反对这一模型的人认为它过于基础,缺乏实际的应用价值。下图展示了瀑布模型:

瀑布模型包括以下几个阶段:
-
定义研究/分析(需求/分析):在此阶段,唯一的目标是寻找需求。会进行一些研究以明确软件的目的。
-
基本设计(设计):在此阶段,第一阶段的成果变得更加清晰。客户的愿望清单被写在纸上,并且程序的用户界面已经开始考虑。一般来说,在此阶段,会记录未来系统必须完成的功能。
-
技术设计/详细设计(设计):此时可以使用基本设计构建一个原型或最简程序。在这个阶段,会考虑如何从技术上实现所需的功能。选项已经被分组为模块、功能或程序。
-
构建/实施(编码):在构建阶段,为程序编写实际的源代码。
-
测试(Testing):在测试阶段,检查软件是否按照设计正确构建。在此阶段,也可能会出现已经在早期阶段做出的错误。在理论模型中,这不应该发生。
-
集成(Testing):现在系统应该已经准备好并经过测试。然而,它还应该能够与其他软件或硬件配合工作。为此,有专门的测试来确保集成工作正常。
-
管理与维护(Maintenance):为了确保系统按照规格继续运行和功能,必须进行维护。
总结来说,瀑布模型由不同的阶段组成。每个阶段有自己的级别,并决定执行顺序。最高级别的阶段首先执行,然后是后续较低级别的阶段。这等同于瀑布的自然效应,因此得名。
为了减轻原始方法的缺点,开发了几种增强形式。
罗伊斯模型
罗伊斯模型描述了另一种瀑布模型,它可以返回到之前的阶段。通常,在某个阶段会发现前一个阶段出了问题(这通常会在测试阶段显现)。然后,应该能够轻松回到之前的阶段。99%的情况下,设计需要进行修改——没有人是完美的,因此这个模型更现实。然而,罗伊斯仍然强调良好文档对于正确阶段过渡的重要性。
下图展示了罗伊斯模型:

刺身模型
刺身模型,由彼得·德格雷斯(Peter Degrace)设计,是书中讨论的模型之一,书名为 《邪恶问题,正义解决方案:现代软件工程范式目录》。该模型假设与瀑布模型相同的阶段,但这些阶段可以重叠(甚至多次重叠)。这种工作方式意味着更少的资源浪费。在下图中,您可以看到各个阶段是如何重叠的。关键在于,阶段之间没有硬性结束或关卡。您可以将当前时间作为一个例子。图中的另一个方面是,与瀑布模型不同,该模型还包含了提前时间。这意味着,即使分析还没有完成,也可以开始设计。这也意味着,您可以在设计阶段回到分析阶段。
下图展示了刺身模型:

V 模型
V 模型展示了软件开发中的阶段过渡,这为质量保证提供了可能性。在每个阶段过渡时,开发人员与客户会就设计等方面达成质量协议。V 形状表明,在 V 形的底部,在实际实施后,对所解决问题的理解会不断加深,并且最初的想法和需求会与现实进行对比。
下图展示了 V 模型:

这里呈现的工作方式符合经典的工程方法。在 1970 年代和 1980 年代的长时间里,人们认为软件工程应该像建造摩天大楼一样进行。摩天大楼的建造首先需要制定详细的建筑计划,建筑工人才能进行建设阶段。为了这样创建架构和计划,你需要非常清楚地理解需要建造的内容。这里重要的是,前期的需求被清晰列出,并且在过程中不会发生变化。然而,如果现实世界中的工程项目需求像软件一样迅速变化,什么也建不成。
DSDM
DSDM 源于线性软件开发方法学 (SDM*)模型,起源于 1990 年代。它是对线性方法中常见问题的回答——在这些方法中,要开发的功能通常在整体完成时才可用。如果交付的功能可以拆分成子功能,那么这些子功能可以单独交付。我们称之为增量系统开发或逐步开发。最初,你需要找出哪些功能可以拆分出去。这意味着瀑布模型的前两个阶段已经完成,但只有当部分功能明确时,才能逐步并行开发。
DSDM 不仅仅是在开发阶段应用迭代。分析之后的所有阶段都是迭代的。此外,阶段的划分看起来稍有不同。与线性开发模型相比,它还可以返回到先前的阶段。
DSDM 中的各个阶段结构如下:
-
可行性研究
-
商业分析
-
功能模型迭代
-
设计与构建迭代
-
实施
时间盒管理是 DSDM 中控制每个阶段的核心实践之一,它与 MoSCoW 方法中的更好优先级管理结合使用。我们将在接下来的部分进行解释。
时间盒管理
为了确保项目能及时为组织提供服务,采用时间框架。例如,优先级最高的功能应该始终在时间框架内交付。如果有时间和空间,低优先级的功能将有余地。时间框架是一个交付中间产品的时间间隔。在项目过程中,具体功能会进一步细化。由于不断增长的洞察力和变化的情况,功能的规格也可能发生变化。
准备一个时间框架,其中定义了必须拥有、应该拥有和(可能的)可以拥有的功能。这样的布局允许你在不影响时间框架最终时间的情况下创造操作空间。换句话说,在洞察变化或紧急情况发生时,你将能够重新调整优先级。这可能会以牺牲应该拥有和可以拥有的系统需求为代价。通过应用 MoSCoW,你可以明确这些选择。
使用这种技术,你可以始终保持对最高优先级功能需求的关注,监控时间和预算,并且在洞察发生变化时仍然能够采取行动。运用这种技术可以让你优先考虑那些能为公司带来最大效益的系统需求,同时降低那些来自于可能永远不会发生的情况的需求的优先级。这也使得系统设计更简洁——这有助于提高其可维护性。
基于 MoSCoW 分类的需求和愿望分类还有助于用户可视化项目在组织内的支持情况。现在有一些作者认为,你应该将项目视为微型项目的集群。每个微型项目是具有一定优先级的需求或愿望。在许多项目中,当人们面临变化时,来自利益相关者的支持开始崩溃。如果一个项目包含了许多必须拥有的功能,那么这种风险将比大部分由应该拥有的功能组成的项目要小。
MoSCoW
通常情况下,一个项目会因为用户组织提出过多的需求而受阻。然而,提供反馈给客户的开发策略可以防止开发组织的过度需求。DSDM 通过将功能需求和愿望分为若干类别,并为每个功能指明优先级,从而解决了这一问题。
DSDM 使用 MoSCoW 规则来确定需求和愿望的优先级。
MoSCoW 代表:
-
必须拥有:这一类别优先级最高,确保交付,并且是信息系统的核心。
-
应该拥有:一个必要的需求,但可以采取(临时)变通方法。
-
可以拥有:一个具有明确附加值的需求,但没有它,系统仍然是可用的。
-
想要拥有(但这次不能拥有):这个需求可以被忽略,尽管它并不意味着不相关;在下一个增量中,它可以变成必须拥有。
现在,DSDM 已不再广泛使用。过去十年里,其他方法变得更受欢迎,特别是像 Scrum 这样的敏捷方法,接下来我们将讨论它。
Scrum
本书中将要讨论的下一个轻量级模型是 Scrum,截止到 2018 年,它已经被广泛应用于 IT 领域之外。Scrum 不像 DSDM 那样是一个具体的方法,更准确的说它是一个框架。它借用了体育团队(特别是橄榄球)的范式,在这个范式中,一群人一起合作达成共同的目标。在橄榄球比赛中,Scrum 是将球投入比赛的一种方式。Scrum 小组由五到八名球员组成,他们作为一个整体协同工作。在 IT 领域,这就是一群人通过紧密合作与协调来创造商业价值。
在橄榄球比赛中,每个球员都有独特的角色;他们既参与进攻又参与防守,作为一个团队协作将球带到对方场地。这可以与 IT 中的一种情况进行对比,Scrum 团队的成功程度取决于团队内不同学科成员如何共同工作和相互协调。
橄榄球的类比源于 1986 年《哈佛商业评论》上的一篇文章,新产品开发游戏,作者竹内和野中在文章中引入了 Scrum 这一术语,并将其应用于产品开发。他们认为这种方法能带来更高的速度和灵活性,并且基于多种行业的案例研究,尤其是汽车行业。
在 1990 年代初,Ken Schwaber 和 Jeff Sutherland 开始在他们的公司中使用 Scrum 技术,并最终在 1995 年,在一次软件设计会议上介绍了 Scrum 框架。
Scrum 列出了以下价值观:
-
承诺:团队成员必须全身心地投入到项目中,而不是兼职工作。
-
专注:团队成员应当专注于每个迭代冲刺中需要完成的任务。
-
开放性(透明性):团队成员必须保持相互之间的信息流通,及时告知进展和潜在问题。
-
尊重:团队成员必须尊重背景和专业不同的人,并相信彼此的良好意图。
-
勇气:团队成员必须有勇气表达观点、提问并提出新的解决方案。
Scrum 适用于跨学科的团队,成员倾向于在同一个房间工作,以便于讨论和交流。团队由一名 Scrum Master(敏捷教练)监督,其角色是促进团队协作。产品负责人是客户或客户的代表。他或她指定所需的结果,通常以用户故事的形式呈现。这些用户故事会保存在一个列表中,称为产品积压或工作存量。产品负责人根据优先级对工作存量进行排序。最重要的用户故事排在最上面。
在 Scrum 中,你在 Sprint 或迭代中工作。通常这些持续时间从一周到一个月不等,其中以两周为最常见。Sprint 是有时间限制的。换句话说,Sprint 的持续时间和结束时间在事先就确定好了。在 Sprint 开始时,确定并记录该 Sprint 的用户故事,并将其列入 Sprint 待办事项列表。
Sprint 提供尽可能具体的结果。这意味着软件开发将提供可用的代码,包括集成、测试和文档,以便客户或最终用户能够理解。
在一个 Sprint 结束时,会进行 Sprint 回顾,展示成果给产品负责人。此外,团队内部还会进行评估。
Crystal 方法
Crystal 方法是一种轻量级方法,其特点包括强调人而非流程和产品、快速沟通(最好在同一个房间内共同工作)、快速交付产品、频繁且自动化的测试,以及定期评估。
与其他一些软件开发流程不同,Crystal 不是一种软件开发方法,而是一系列方法和流程的集合。这个集合被称为 Crystal 家族。Crystal 是由 Alistair Cockburn 发明并描述的。Crystal 家族中的每个成员都以颜色表示方法的重量,原则是:颜色越深,方法越重。方法的颜色是根据项目的大小和严重性来选择的。大小由参与项目的人数决定,严重性由选择该方法可能造成的系统性损害风险决定。这些颜色像真实的水晶一样,从浅到深排序。Crystal clear 是最小和最轻的,其次是黄色、橙色、橙色 web、红色、深红色、蓝色、紫色等。
尽管 Crystal 方法之间有所不同,但它们也有一些相似之处,这也是为什么它们被称为一个家族。它们有三个共同的优先事项:安全性、效率和可用性。此外,它们还具有共同的特征,其中最重要的三个是频繁交付(中间)产品、改进的反馈和良好的沟通。
我们已经讨论过基于团队的产品开发方法,并具有一定的流程步骤,现在讨论的是 Crystal 方法,一种轻量级的敏捷方法,专注于团队内部的人。还有一些更为激进的方法,不仅专注于人和流程,还专注于工具和技术质量。我们将在下一节中讨论这些 XP 方法。
XP
敏捷运动中最重要的子文化之一就是 XP。XP 的主要创始人有 Kent Beck、Ken Auer、Ward Cunningham、Martin Fowler 和 Ron Jeffries。他们在 1996 年的克莱斯勒综合补偿(C3)系统项目中开发了 XP。如今,它非常流行,这不仅体现在软件开发的文化中,也体现在其工作方式和所使用的工具集中。我们认为,GitLab 在许多方面正是 XP 当初设想的工具。实际上,它的影响深远,以至于我们决定为其专门开设一整章,解释它与 GitLab 的相关性。
XP 的名字来源于一系列被证明有效的开发原则(即所谓的最佳实践)被极限化的应用。XP 的最大优势来源于 12 条软件开发最佳实践的应用。这些最佳实践分为四个组别:细致的反馈、持续的过程、共享的知识和开发人员的福祉。
细粒度反馈
XP 中的一个重要原则是使用反馈机制,并尽量将反馈循环保持在最小。这个过程从规划阶段开始,因为在这个阶段得到客户的反馈可以有效地减少时间浪费。
XP 中的细粒度反馈组包括四个实践:规划游戏、结对编程、测试驱动开发(TDD)和全员参与。我们将在接下来的章节中讨论每个实践的反馈循环。
规划游戏
规划在每个迭代的开始进行,包含发布规划和迭代规划两个部分。
发布规划决定了哪些功能将在何时的发布版本中实现。开发人员和用户都会参与。
发布规划包含以下三个阶段:
-
探索阶段:在这里,用户列出新系统最重要的需求。这是通过用户故事的形式进行的。
-
承诺阶段:在这里,决定哪些用户故事会包含在下一个发布版本中,并确定发布的时间。
-
指导阶段:在这里,计划可以被修改,新的故事可以被加入,其他的则可以移除。
迭代规划是将纳入即将到来的 Sprint 发布计划中的用户故事分解为开发人员的任务。没有用户参与,只有开发人员。
迭代规划还包括三个阶段,具体如下:
-
探索阶段:在这里,故事被转化为任务,然后写在称为任务卡的卡片上。
-
承诺阶段:在这里,估算实现任务所需的时间,并将任务分配给开发人员(结对)。
-
指导阶段:在这里,任务被执行,并且结果与用户故事的原始时间表进行比较。
这种规划的目的是确保产品能够交付。重点不在于交付精确的数据,而在于交付产品。
发布规划
在这个规划游戏的过程中,客户和开发人员试图找出下一次软件发布将包括什么内容以及何时发布。重点是创建用户故事。
游戏的这一部分由以下三个阶段组成:
-
探索阶段:这是收集需求并估算实现这些需求所需时间的过程。此阶段的活动包括:
-
编写用户故事:在此,用户提出一个问题或需求;在咨询过程中,开发人员会尽量充分理解这个问题。在此基础上,编写用户故事。这是由用户完成的,表明他们对系统的期望。开发人员需要避免干预。
-
估算用户故事:开发人员估算完成此任务所需的时间。此时,开发人员还可以指定短期的检查(称为 spikes),以调查问题或解决方案方向的某些部分。这些 spikes 用于获得更好的时间估算,并在问题和/或解决方案对每个人都清楚时被抛弃。
-
拆分用户故事:在此阶段,故事必须完全清晰,所有歧义必须消除,才能开始迭代规划。如果开发人员由于不清楚而无法给出时间估计,故事必须被拆分。如果用户已经描述了所有的需求,他们可以继续进入决策阶段,这就是所谓的承诺阶段。
-
-
承诺阶段:在此阶段,我们将找出成本、收益以及它们对进度的影响。我们根据排序方式创建四个不同的列表,具体如下:
-
按价值排序:用户按他们认为重要的顺序排列用户故事。他们会创建以下三种堆栈:
-
关键:没有这些故事,系统无法工作或没有价值。
-
重要:对公司而言重要的用户故事。
-
可有可无:用户故事中实现的较不重要的特性。
-
-
按风险排序:在此,开发人员估算风险并相应地对故事进行排序。所有用户故事的值将加在一起,得到一个累计的风险值,可能是低(0-1)、中(2-4)或高(5-6)。以下是一个例子:
-
完整性(我们是否有关于表格的所有细节?)
-
-
-
完整(0)
-
不完整(1)
-
未知(2)
-
-
脆弱性(变更是否可能?):
-
-
-
低(0)
-
中等(1)
-
高(2)
-
-
难度(实现起来有多难?):
-
-
-
简单(0)
-
标准(1)
-
难(2)
-
-
-
-
确定开发速度(velocity):在此,开发人员确定他们能够以多快的速度执行项目,并根据此对项目进行排序。
-
范围:在这里,确定将在下一次发布中实现哪些用户故事。这是最终排序。基于此,确定发布日期。排序应该根据用户的价值(业务价值)进行。
-
- 引导阶段:在此阶段,开发者可以与用户一起引导流程。换句话说,他们仍然可以做出一些改变,不管是某个个别用户的故事,还是另一个特定故事的重要性。
迭代规划
根据团队的速度,可以确定每次迭代团队能完成多少个故事点。迭代可以持续一到三周。这里的重点是创建任务并对其进行优先级排序。迭代也具有与早期发布规划相同的阶段,如下所示:
-
探索阶段:在迭代规划的研究阶段,用户故事被划分为任务,并估算每个任务的完成时间。此阶段的主要活动包括以下内容:
-
将用户故事转化为任务,并将其写在任务卡片上。
-
添加或拆分任务;换句话说,如果开发者无法准确估算任务的持续时间,因为任务太大或太小,就需要进行调整。
-
任务估算,即对任务执行的估算。
-
-
承诺阶段:在迭代规划的分配阶段,以下任务会在开发者之间进行分配:
-
开发者(程序员)接受任务:每个开发者接受一个任务,并对其负责。
-
开发者给出时间表:因为开发者现在已经负责该任务,他们是最能给出时间估算的人。
-
确定有效工作时间,列出开发者或程序员在一次迭代期间可以工作的小时数。(例如,在一个 40 小时的工作周中,如果有五个小时用于会议,那么有效工作时间就是 35 小时。)
-
平衡:一旦所有任务分配完毕,将比较每个开发者分配的小时数与他们实际可用的小时数(这也称为负载系数)。然后可以重新分配任务,确保每个开发者的工作量大致相同。如果某个开发者工作量过多,需要调整任务。
-
-
引导阶段:在迭代规划的执行阶段,任务的执行将在此阶段进行。这个阶段有点像游戏元素,但建议按照以下步骤进行:
-
获取任务卡片:在此,开发者会获得一张卡片,上面描述了他们注册的某个任务。
-
寻找合作伙伴:开发者寻找一个合作伙伴共同开发该任务。
-
设计任务:如有必要,在此阶段将进行(简短的)设计。
-
编写单元测试:在编写任何代码之前,所有的测试都必须准备好。最好是自动化测试,因为这些测试通常需要在每次源代码提交时进行。
-
编写代码:在这里,程序员或开发人员编写程序。
-
测试程序:执行单元测试。
-
重构:在这一步骤中,应用重构规则,开发人员确保代码符合标准。
-
进行功能和集成测试:在单元测试之后,所有可能的其他测试都要运行,例如集成测试、回归测试等。此步骤中代码必须进行调整,直到测试成功。
-
结对编程
XP 指出,最终,所有的工作都围绕代码展开,软件开发中也是如此。所以,如果让开发人员一起审查代码是一件好事,那么软件开发应该始终以对的方式进行。换句话说,结对编程可以定义为两个人共同使用一台电脑进行工作。结对编程常被认为是 XP 中最极端和有争议的部分,因为它被认为效率较低。然而,研究表明,同行评审和代码检查是对抗漏洞最强大的武器—这些远比系统测试更为强大。这些技术通常只在稀疏情况下使用,并且通常会在程序员和管理者之间引起强烈反对,后者担心工作时间的增加。
通过强制执行所有软件开发都以对的形式进行,且对的组成会定期变化,从而产生一种集体的责任感,同时同行评审和代码检查成为软件开发过程中的自然一部分。最终交付的系统不再是由一堆用代码串起来的代码片段组成,这些代码片段难以维护。
这种工作方式还有一个好处:总会有至少两个人完全理解每一段代码。知识的传递给新同事更加自然,且工作中的持续培训也得以进行。
结对编程的一些好处包括:
-
更高质量的代码:像大声朗读代码和讨论背后的思维过程等活动有助于他人理解代码的复杂性,同时也给开发人员机会澄清细节,并防止做出不可逆的选择。
-
团队内部更好的知识共享:这在其中一位开发人员还不熟悉软件组件时,与熟悉该组件的开发人员合作特别有用。
-
改进知识传递:通过开发人员从经验丰富的团队成员那里自动学习新技术和技能来帮助实现这一目标。
-
更少的管理开销:这得益于更少的个人控制,因为开发人员是以两人或更多人的团队合作的。
-
持续专注:如果成对编程中的一个成员因任何原因被打断,成对编程尤其有帮助。
那么,成对编程有缺点吗?目前,成对编程的成本和收益尚不明确,但初步研究表明,当一对人一起工作时,任务的持续时间平均增加了 15%。这种成本是否能够通过更高的代码质量来证明其合理性,仍然存在争议。
测试驱动开发
在测试驱动开发(TDD)中,测试是在编程之前进行的。TDD 依赖于这样的前提:如果测试做得好,测试代码应该在编写任何代码(功能)之前就写好。
在 XP 中,自动单元测试的编写占据了重要位置,因为编写单元测试是在实际编程开始之前进行的。在 TDD 中,程序员首先编写一个或两个测试,编写一部分程序,增加一个新的测试用例,不断修改程序,直到新的测试通过,再设计新的测试,依此类推。
这一过程的优势在于,程序员必须思考程序应该具备的功能和需要考虑的异常;他们先考虑程序应该做什么,再考虑程序如何运作。换句话说,测试捕获了所需的功能。因此,每个程序必须仅具有足够的功能,以使得测试通过。如果所有测试都通过,程序就满足先前定义的要求,这些要求是通过已编写且成功的单元测试来定义的。
当需要重构时,已经编写好的单元测试就成了保证,确保更改不会导致程序运行中出现不良副作用。
如果需要为程序扩展新功能,首先要做的就是编写新的单元测试,定义要编写的新功能。只有当新旧单元测试都通过时,这个新功能才算实现。
如果在功能测试的后期发现了一个 bug,首先要做的是编写一个单元测试,揭示这个 bug。因此,bug 不是程序的错误,而是测试不充分的结果。
总结来说,使用 TDD 进行的软件开发项目包括以下几点:
-
从一个描述程序某个功能的单元测试开始
-
运行测试,最终应该会失败,因为没有代码
-
利用最少量的代码使得测试通过
-
重写代码,使其更简洁
-
通过更多的测试来重复这个过程
使用这种方法,尽管开始时需要更多时间,但随着时间的推移,缺陷率应该会下降。大多数团队报告称,一旦项目进入后期阶段,前期的测试成本就会得到回报,而且在项目的最后阶段工作会更加高效。通过这种方式开发的代码往往质量更高,因为为了确保测试有效,你必须编写高度内聚而低耦合的代码。这使得处理相同行为的代码保持在同一个类中,并且将模块尽可能隔离,且与其他代码的接口清晰。
这种方法听起来可能很简单,但在实际操作中却相当困难,因为开发人员可能会忘记运行测试。然而,这个问题可以通过在 CI/CD 环境中设置一个带有预配置测试的项目模板来轻松解决,在每次提交或推送时都会运行测试。如果有些软件工程师容易过度测试,最好事先约定测试的数量及其深度。例如,像访问器这类简单的构造不需要测试。另一方面,要小心不要过度简化测试,比如创建了测试却没有断言。
团队文化和协议对于测试非常重要。如果有些团队成员不认同,你将面临冲突。同时,确保任何测试模板、自动化工具或测试套件都得到充分支持,否则你的测试将无法正常运行。(这也意味着,应该有多人了解这些工具。)
本质上,TDD(测试驱动开发)可以真正提高软件质量。特别是当所有测试都通过持续过程自动化时,这一点尤其如此,接下来的章节将进一步讨论这一点。
持续过程
持续过程是一组旨在持续运行且没有中断的过程。同时也不需要批处理,因为批处理通常会减慢 XP 的整体进度。
持续集成
如果集成和集成测试很重要,那么代码应尽可能频繁地进行集成——最好是一天多次。这将防止你的团队在本地使用不同的副本,并鼓励他们一起工作。任何集成问题也会立刻显现出来。GitLab CI 就是为此而创建的,且在第一章中介绍了GitLab 架构概述。
使用持续集成(CI)的一个关键原因是防止集成问题,如果开发人员长时间独立工作,可能会发生这种问题。想象一下集成地狱的现象,在发布前的最后一刻,开发人员合并了一大块代码,结果引发了冲突。
在 XP 世界中,持续集成始终与 TDD 相结合。在运行集成测试之前,通过本地彻底测试代码会有所帮助,最好是通过单元测试进行。在本地环境中测试代码的这种方式有助于在破坏他人代码之前发现错误。请注意,您还可以使用功能切换来隐藏尚未完成的功能,这会禁用代码中的某些行为。
在某些情况下,构建服务器用于软件质量保证(QA)过程的其他部分,包括运行额外的安全测试、测量性能,甚至生成文档。这种将责任转移到构建服务器的行为意味着许多传统上在开发后完成的 QA 工作现在可以在开发过程中执行,同时还能获得即时反馈的好处。这种反馈循环是推动软件产品持续开发过程的重要驱动因素之一,另一个是自动化。
自动化进一步扩展到创建持续交付(CD),通过使软件部署成为自动化的一部分。为了使这一切成为可能并快速运行而没有问题,主干或分支中的代码应始终处于可部署状态。
构建软件产品的每个可以自动化的元素都有资格成为 CI 过程的一部分,尤其是在特别复杂的情况下。自动化这些阶段是 CI/CD 存在的原因之一。
重构
与传统开发方法不同的一项重要技术是重构,这是在不影响任何可见功能的情况下,通过小而精确的步骤连续重写程序代码。简而言之,重构不会增加功能,但简化了设计。通过定期执行重写步骤,总体效果往往令人惊讶。
与此同时,已经发现并记录了大约 70 条重写规则。它们具有诸如引入空对象、用查询替换临时变量和用多态替换条件等名称。成功应用重构的先决条件是有可用的单元测试,可以在每个重写步骤后自动进行,以确保功能未发生变化。例如,对于 Smalltalk,现在有一个重构浏览器,可以自动应用重写规则,用户无需过多担心准确性。重构经常用于准备实施扩展或功能变更。
重构并不意味着重写代码、修复错误或更改用户界面。重构的另一个危险是,在缺乏良好的自动化测试的情况下,可能会引入回归错误。
在使用这种技术一段时间并积累经验后,团队报告说代码长度有了显著改善,重复度减少,耦合和内聚性更好,环路复杂度降低。对于新接触你软件的人来说,这让学习变得更容易。对于团队来说,它有助于集体思考项目的整体设计,并理解为什么做出某些决策。通常,这也依赖于引入某些可重用的组件和模块。
短期迭代
软件定期以有限的版本交付给客户进行评审;如果短期迭代有效,那么你应该使其非常短。我们说的是秒、分钟或小时,而不是周、月或年。XP 的平均迭代周期为两周,尽管根据 extremeprogramming.org 的说法,它可以从一周到三周不等。
XP 循环包括六个阶段:探索、规划、迭代发布、生产、维护和死亡。
对于敏捷项目,迭代是一个特定的时间段,期间进行开发。这被称为时间框定。这个时间段因项目而异,通常在一到四周之间,并且通常为每个项目定义。典型的敏捷方法是项目由多个迭代组成,开始时有一个简短的规划阶段,结束时有一个收尾阶段。
迭代通常按工作周分类,工作周从星期一开始,到星期五结束。一段时间后,迭代的固定期限使得评估项目所需时间变得更加容易。
Scrum 方法中的迭代时间框被称为 Sprint,当然,这个名字来自于橄榄球。在 XP 中,它们被称为每周周期。对大多数人来说,迭代意味着重复或多次重复;但在敏捷开发的语境中,它指的是一个重复的过程。
每个人都拥有代码
请注意,每个开发者对程序代码的所有部分拥有平等的权利。如果设计良好,就让它成为每个人日常工作的组成部分,并在需要时逐步改进设计。如果架构如此重要,就让每个人都参与到开发中。这一概念鼓励每个人贡献并承担责任。
共享理解
小组中的价值观大多与感知有关。要作为一个小组高效且有效,你必须在某些方面达成一致,并共享共同的价值观和理解。
编程标准
要达成共享的理解,你需要制定一些规则。有一些编程标准是大家都知道并使用的。如果源代码格式一致,真的很有帮助。这样,大家都可以阅读并修改它。在代码文件中讲相同的语言。这也有助于确保代码库的集体所有权。
简单设计
如果代码属于一个小组中的所有开发者,并且每个人都可以修改一切,那么他们就应该能够做到这一点。保持设计尽可能简单。XP 大量使用保持简洁明了(KISS)原则。换句话说,系统要易于修改,设计应该尽可能简单。然而,这说起来容易做起来难。
传统的开发方法学会了提前思考,并始终考虑可能在未来实现的功能,但这些方法假设变更的成本会呈指数增长。因此,XP 总是试图选择最简单的设计来实现必须实现的功能。理想情况下,任何未来的扩展都可以通过 XP 实现,而不会产生通常的额外成本。事实证明,在实现经过深思熟虑的设计时,它往往并不反映当前的需求。这可能是因为在分析和设计过程中忽视了某些细节,或因为需求发生了变化。在 XP 中,设计并不是优先的,而是紧随代码之后。
系统隐喻
所有团队成员,包括开发者、用户或客户,应对系统有一个共同的看法(称为隐喻);每个人都必须能够用简单的语言描述系统。命名规范的使用也应有助于这一点。
正如我们所讨论的,最终的关键点是,在使用 XP 时,人的因素仍然是最重要的。例如,大家是否在讨论相同的内容?他们是否都认为设定了正确的优先级?他们是否能够创建出既有效又易于理解的软件?这一人类因素也是 DevOps 背后的驱动力之一,它是敏捷思维从软件开发到 IT 运维的延伸。
DevOps 运动
DevOps 这个术语起源于比利时,大约在上个十年末期,由所谓的 DevOps 日活动催生。这些活动旨在将开发和管理操作的 IT 专家汇聚一堂。最初,DevOps 团队被定义为一个多学科团队,负责一个服务的管理和持续交付。像亚马逊和谷歌这样的公司就使用这种团队,他们每天都会发布数十个变更。
这种工作方式在大组织中尚未标准化——ITIL 和 PRINCE2 仍然占主导地位,信息技术(IT)部门正在拼命尝试提供有价值的服务。这些服务的提供方式在当前的环境中难以维持,因为 IT 仍然常常被视为一个成本项目。造成这种情况的原因有:
-
将组织作为一组独立的孤岛来组织
-
专注于创建流程过剩(制定太多固定的规则)
-
没有定义清晰的关键绩效指标(KPI)来衡量绩效。
这些孤立的技术在这些领域中是不兼容的,而对于成功的业务与 IT 的对齐,需要一个统一的链条。
此外,我们现在看到客户越来越多地要求快速交付新功能。这包括快速解决事件、简化沟通渠道以及在 IT 组织中的卓越质量要求。使用传统的组织方式,在组织中,流程、工作方法、态度、行为以及所需的绩效和结果并未得到充分实现。爱因斯坦被引用的一句名言是:“疯狂的定义是一次又一次地重复同样的事情,并期待不同的结果”,似乎越来越适用于 IT。因此,现在是彻底重新考虑其组织架构的时候了。
以下图表说明了 DevOps 流程:

运动的历史
DevOps 这个术语在许多地方仍然造成了许多混淆。作为一种运动,它仍然很年轻,但它主要基于常识和过去的经验。DevOps 团队的出现是公司为了应对市场变化而做出的努力。新的 DevOps 方法旨在更快、更频繁地向客户发布更高质量的软件。DevOps 的简要时间线如下:
-
2007:在比利时政府数据中心迁移期间,帕特里克·德博亚斯对开发人员和系统管理员之间的许多冲突感到沮丧。这让他开始思考。
-
2008:在多伦多的敏捷大会上,软件开发者安德鲁·沙弗准备就要进行关于敏捷基础设施的演讲。他决定跳过,因为他认为没有听众,但德博亚斯确实打算参加。后来,德博亚斯找到沙弗进行了一场广泛的走廊谈话。根据他们的交谈,他们成立了敏捷系统管理组。
-
2009:两名 Flickr 员工约翰·奥尔斯波和保罗·哈蒙德提出测试、构建和部署响应迅速的新软件,以使运营和开发集成和透明化。第一届 DevOps 大会在比利时根特举行。会议于 10 月 30 日举行,吸引了大量开发者、系统管理员、专家等。会议结束后,持续的讨论转移到了 Twitter 上。为了创建一个令人难忘的标签,德博亚斯将其缩短为#DevOps。
-
2010:这是在美国举办的第一届 DevOps 大会,由约翰·威利斯(著名书籍《凤凰项目》的作者)及 DevOps 的早期倡导者共同组织。这一事件很快成为一个全球系列的由社区组织的会议,推动着 DevOps 社区的发展。
-
2011:DevOps 社区开始使用开源工具,如 Vagrant,这些工具可以利用 Chef 和 Puppet 等技术。
-
2012:演示文稿开发领域迅速增长,并开始集中于创新。现在,许多国家突然出现了各种 DevOps 日活动。
-
2014:全球一些最大公司开始在其组织中使用 DevOps 方法,包括 LEGO 和 Nordstrom。
今天,全球许多企业都在拥抱 DevOps;无论是小型、大型还是私人企业,都从 DevOps 中受益。DevOps 可以为任何企业带来长期的最佳结果,并有助于其成功。
然而,组织不可能迅速切换到 DevOps——改变组织中的流程可能会对其文化产生重大影响,并且需要时间。找出你在这段旅程中可能处于何种阶段的一个好方法是使用成熟度模型。当使用模型来表示现实时,你可以开始简化问题,而不是被可用的解决方案和工具数量所困扰。如果你知道自己处于成熟度模型的哪个位置,你就可以确定你想要到达的位置,然后规划你的旅程。
四象限模型
原始的成熟度模型是卡内基梅隆大学(CMU)发明的能力成熟度模型(CMM)。这个模型在完全利用时有些笨重,因此更简化、更轻量化的版本更为合适。其中一个版本是 Brian Dawson 提出的四象限模型(techbeacon.com/devops/how-map-your-devops-journey)。该模型来源于实际的 DevOps 转型,提供了一种灵活的评估成熟度的方式。
在四象限模型中,x轴上的值由软件开发周期中的不同阶段组成。你可以在其中识别出软件开发生命周期(SDLC):
-
定义
-
计划
-
代码
-
构建
-
整合
-
测试
-
发布
-
部署
-
运营
你可以看到,这与 GitLab 提出的 DevOps 生命周期阶段有相当大的重叠。
这个周期分为两部分:Agile 上游(包括定义、规划、编码和构建)和Agile 下游(包括集成、测试、发布、实施以及持续部署和持续交付等方法)。
在y轴上,表示的是组织中 Agile 和 DevOps 实践的采用程度。在低端是团队级别,然后是工作组级别,最后是企业级别。在原始的 CMMI 模型中,通常有不同的成熟度级别。下图展示了四象限模型:

Agile 上游意味着在软件生命周期中,产品的开发侧采用敏捷方法。Agile 下游则涉及 SDLC 中的部署和运营侧。
每个团队都必须努力实施四象限模型,因为它能够帮助他们更快地创新、提高生产力、应对市场变化、获得竞争优势,并提升员工满意度和留存率。
另一个衡量成熟度的方法是查看能力。例如,你在某些方面有多强?
四个能力层级
测试能力的一种方法是使用 Mike Kavis 在《福布斯》论文中提到的模型。他描述了一个基于 Noel Burch 在 1970 年代使用的 四个学习阶段 的模型。
基本思想是,个人在获得一项新技能时,会经历以下四个阶段:
-
阶段 1:无意识的不熟练:一个人可能没有意识到自己需要某项技能。改变或成长的第一步是认识到这种不足,并承认这项技能的存在。
-
阶段 2:有意识的不熟练:逐渐地,缺乏某项必要技能变得显而易见。这个过程通过犯错来学习。
-
阶段 3:有意识的熟练:经过几次迭代或尝试后,个人获得了该项技能,并能够有意识地运用它。虽然并非每次都能成功,但要做到这一点需要付出相当的努力。
-
阶段 4:无意识的熟练:这项技能变得如此自然或合乎逻辑,以至于可以在不自觉的情况下应用。甚至可以教给别人。
Davis 声称,这一模型可以应用于那些试图理解 DevOps 概念的组织。虽然它没有经过科学验证,但做以下比较还是有价值的:
-
阶段 1:一无所知:组织最初似乎对变革有抵触情绪。DevOps 这个术语被描述为一种炒作,且不适用。这通常意味着人们并不真正理解 DevOps 的含义。人们依然陷入老旧的思维模式,认为开发应该接管运维,或者反之。
-
阶段 2:认知:最终意识到某些事情必须改变。在这一阶段,会出现错误。例如,引入了自动化,但开发部门仍然认为自己负责编写一切。此时,出现了一个新的部门(DevOps 部门),开发人员只为运营创建自动化。这些开发人员对网络、 安全合规或其他运营问题了解甚少。如果运营部门的部门被转化为“DevOps 工程师”,也会出现类似的问题。由于缺乏工程知识,可能会出现未经测试且难以管理的 shell 脚本。然而,在这个阶段,组织仍在学习,如果能够管理好不可避免的成长痛点,最终会进入下一个阶段。
-
阶段 3:成熟阶段:在吸取了失败的教训后,组织的管理层开始关注并认识到改变人员和流程的附加价值。在上一阶段,目标是整合开发和运维的孤岛。现在,通过反复试验成功地实现了这一目标,合作扩展到包括法律部门、合规性和审计。生产力的第一个迹象开始显现,出现了专门的平台、框架或模板,用于从创意到生产部署标准化的企业应用程序。平台开始具备所有内建的功能,如合规性和质量控制。
-
阶段 4:100%以业务为驱动:在这一阶段,组织中的多个业务单元每天进行多次部署,并能够通过平台轻松改进流程并分享他们的知识。在最优化的形式中,业务单元完全控制并已成为一个跨学科的团队,可以与专门的平台专家进行咨询和协作。
当然,这些模型是相当理论化的,但它们可以帮助组织在变革过程中。幸运的是,已经有一些工具出现,帮助组织弥合不同成熟度阶段之间的差距,接下来的章节中将讨论这些工具。
工具链
尽管我们已经了解到,DevOps 不仅仅是工具,但在企业中,仍有许多常用的工具,如以下几种:
-
源代码仓库:计算机源代码已成为非常宝贵的资产。它通常存储在具有先进版本管理功能的仓库中。该仓库管理着被检查的多个版本的代码,因此开发人员可以在彼此的项目上进行协作。这个概念并不新鲜,已经存在了 30 年,但它是持续集成的重要组成部分,因为源代码存储在这里。常见的源代码仓库工具包括以下几种:
-
客户端上的 Git
-
GitLab
-
GitHub
-
Subversion
-
TFS
-
CVS
-
所有这些仓库工具将在第四章,从终端配置 GitLab,第五章,将项目从 GitHub 导入到 GitLab,第六章,从 CVS 迁移,以及第七章,从 SVN 切换中详细说明。
-
构建服务器:过去,软件的构建是在单个开发者的工作站上完成的,但在持续集成(CI)流水线中,使用专门的构建服务器将源代码库中的源代码编译成可执行文件。现代的构建服务器不仅负责构建,还提供先进的测试功能。常见的工具包括以下几种:
-
GitLab Runners:GitLab 的构建工具。
-
Jenkins:一个来自 Hudson 项目的分支,是一个持续集成(CI)平台。该平台主要用于反复执行和监控构建任务,以及应用程序的自动构建和测试。许多免费提供的插件使得扩展 Jenkins 的功能变得非常简单。该软件仅作为分布式服务提供,用于云端,并与 GitHub(作为源代码仓库)紧密集成。
-
-
配置管理:对于 CI/CD,你需要控制其所在的环境。为此,有配置管理工具来描述和自动化你基础设施的大部分部分。常用的工具包括以下:
-
Puppet:一种可以控制大量服务器的管理软件。这涉及到配置文件(服务器设置)和已安装软件(软件包)的管理。它使用声明性语言,并且学习曲线较陡峭。
-
Chef:另一款配置管理软件,Chef 支持的平台略少于 Puppet,并且不是声明性语言。Chef 使用纯 Ruby 代码来指示你希望在服务器上执行的操作。你可以更自由地创建自己的程序数据结构和函数。GitLab 用它来管理 Omnibus 包。
-
-
虚拟基础设施:运行软件的基础设施一直都是虚拟的,操作系统已经是几层抽象。在云中,虚拟基础设施是一个额外的抽象层,代表整个机器(例如网络、节点和存储)。还有一个编排层来管理基础设施。这使得上下扩展变得容易,并能有效利用所有资源。第一个真正的大规模虚拟基础设施作为服务提供的是 Amazon Web Services(AWS)。其他主要的科技公司随后也推出了 Google Cloud 和 Microsoft Azure。这些基础设施可以使用它们自己的编排工具进行管理,但也提供可以被配置管理工具使用的 API,包括以下:
-
Ansible
-
Puppet
-
Chef
-
Google Cloud
-
政府通常对其数据有特殊要求,这被称为数据主权,因此专门为政府提供的云服务应运而生。根据 Gartner 的说法,这些云服务可能会成为下一个遗留系统,就像政府基础设施被一对一地迁移到云端一样,但没有解构成弹性、高效且具有成本效益的云组件。
并不是每个人都能在公共云上运行他们的软件和数据,即使他们有特殊协议。例如,如果你运行的是私有云或混合云,实际上你就是在自己的数据中心使用互联网上存在的抽象技术。即使没有 Amazon 或 Azure 的弹性,自己应用云技术也可以非常有益。配套的自动化工具使得与现有系统的集成变得更容易,而且管理系统所需的人力也大大减少。也有私有云,例如,VMware 有 vCloud。扩展现有的 VMware 基础设施以创建类似云的环境是相当简单的。
-
测试自动化:测试的目的是确保对产品的信心。当产品在管道中达到部署阶段时,它应在到达该点之前自动进行某些缺陷测试。市场上有多种工具可以执行各种测试,并且可以与许多其他管道产品完美集成;其中包括:
-
Selenium:Selenium 是一个应用程序,允许你自动化浏览器操作。你可以根据目标来使用它。你可以自动化重复的管理任务,但 Selenium 也用于浏览器测试。
-
黄瓜(Cucumber):这是一款用于行为驱动开发(BDD)的测试工具。BDD 的主要目标是让人们能够更好地沟通,缩小技术人员与业务人员之间的差距。你可以用人类可读的格式编写测试。
-
Apache JMeter:JMeter 是一个开源工具,用于进行负载、性能和压力测试。它是一个简单但高效的应用程序,通过不同类型的脚本准确显示测试结果。这些脚本用于 HTTP 网站并提供模拟的测试环境。除了应用程序,JMeter 还适用于检查 Web 服务和各种数据库。
-
你还可以利用 GitLab Runners 并编写你自己的测试。
-
-
管道编排(Pipeline orchestration):管道是指从代码提交到版本控制系统后,将其从初步开发推向生产环境的一系列自动化步骤。它的理念基于制造业装配线的概念。
在 CI/CD 过程中,为了管理各项事务,引入了管道编排工具。这些工具包括:
-
-
Kubernetes:Kubernetes 本质上是一个用于大规模部署和管理容器的平台。Kubernetes 是希腊语,意为舵手或飞行员,这是该项目的第二个名称,最初是在 Google 的大型会议室中作为“Project Seven of Nine”诞生的。Project Seven of Nine 是 Borg 的外部版本,Borg 是驱动 Google 服务的任务调度器,其操作在很长一段时间内一直是 Google 的机密。
-
作为 Docker API 的扩展,使用 Swarm 进行的编排在几年前也变得非常流行。它可以轻松地将松散的 Docker 容器组合成一个管理虚拟 Docker 引擎。这使得从头开始大规模运行容器工作负载变得非常简单。
-
Mesos/Marathon Apache:Mesos 是一个分布式内核,是 DC/OS 的核心。它抽象了 CPU、内存、存储和其他计算资源。它提供资源管理 API,适用于数据中心和云环境的规划。它能够扩展到 10,000 个节点,因此非常适合大型生产集群。它支持与 Marathon 的容器编排。
-
所有前面提到的工具都可以与 GitLab 集成,你可以在管道的各个部分使用 GitLab。你可以使用 Runner 进行测试、构建或部署产品,并且可以利用 Kubernetes 来编排工作负载。
GitLab 中使用哪一部分管道由你决定,但它可以在 DevOps 生命周期的所有阶段为你提供支持,如下所示:

我们现在已经解释了 DevOps 中 CD 管道的基本设置。GitLab 提供了几乎 100%的所有阶段,但也可以与现有组件进行集成。
总结
本章旨在提供关于 GitLab 起源和发展的更多背景知识。一个工具不是凭空出现的。在 1990 年代,全球不同地区的人们达成了同样的结论:线性软件开发并不是所有项目的正确方法。经过 10 年的发展,最终 DevOps 解决方案进入了运维部门。DevOps 是一种工作方式和文化,并配有工具,GitLab 正是为此构建的。在下一章中,我们将看到 GitLab 如何为更好的 DevOps 体验做出贡献。
问题
-
什么是 SDLC?
-
在犹他州的敏捷宣言会议上有多少参与者?
-
瀑布模型第一次出现是什么时候?
-
XP 编程起源于哪里?
-
MoSCoW 是什么意思?
-
第一次 DevOps Days 在哪里以及何时举行?
-
什么是敏捷上游?
-
请列举两种配置管理工具。
进一步阅读
-
敏捷成熟度模型:
info.thoughtworks.com/rs/thoughtworks2/images/agile_maturity_model.pdf -
DevOps 成熟度模型
:
techbeacon.com/devops/how-map-your-devops-journey -
什么是 DevOps?
radar.oreilly.com/2012/06/what-is-devops.html -
《敏捷开发者手册》,作者:Paul Flewelling
:
www.packtpub.com/web-development/agile-developers-handbook -
DevOps:持续交付、集成与部署,由斯里查兰·瓦达帕利编著:
www.packtpub.com/virtualization-and-cloud/devops-continuous-delivery-integration-and-deployment-devops -
实用 DevOps,由乔阿基姆·维罗纳编著:
www.packtpub.com/in/networking-and-servers/practical-devops -
邪恶问题,正义解决方案:现代软件工程范式目录,由德格雷斯、彼得和斯塔尔、莱斯利·胡莱特编著,pp. 116, 117, 127. 经普伦蒂斯·霍尔出版公司许可再版,新泽西州恩格尔伍德悬崖,1990 年。
-
大型系统开发管理:概念与技术,由 W. W. 罗伊斯著,载于:第九届国际软件工程会议。ACM。1970 年,p. 328-38。
第十章:创建你的产品、验证并打包
在本章中,我们将尝试将上一章的理论与其在 GitLab 中的实现进行比较。GitLab 的诞生源于需要一个具有高级功能的协作平台,它已经自然地发展成现在的形式。它是在敏捷性理念的指导下构建的。我们将展示一个用例,其中一家小公司希望开发一款软件产品,我们将使用 GitLab 将这个创意转化为产品。
在本章中,我们将涵盖以下主题:
-
GitLab 工作流
-
管理你的创意
-
规划你的功能
-
创建它
-
验证你的产品
-
打包以供使用
技术要求
要跟随本章中的指令,请从 GitHub 下载 Git 仓库和示例:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter10。
如果你想尝试这些示例,你将需要一个亚马逊账户。
你还需要AWS 命令行界面(AWS CLI):docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html
GitLab 工作流
记住我们在上一章中介绍了 GitLab 视角下的 DevOps 流水线。此截图展示了 GitLab 的各个阶段:

在本章中,我们将展示整个流水线的多个方面,并尝试在我们的示例项目中使用它。第一阶段定义为管理,作为第一个阶段可能听起来有点奇怪,但它是一个贯穿整个流水线的持续过程,GitLab 提供了相应的工具。接下来的阶段是计划,在这一阶段,你需要进行细化、优先级排序并设定时间表。然后进入创建阶段,在这一阶段中执行任务以产生解决方案。创建完产品后,你需要在验证阶段测试其各个方面。验证产品后,你将进行打包以供部署。
为了更详细地解释 GitLab 工作流,我们将展示一个将贯穿全章的用例,展示 GitLab 中的各种功能。对于某些功能,你将需要最全面的 GitLab 许可证。
假设有一家公司叫做Event Horizon,他们想要建立一个管理活动(面向人类)的解决方案。例如,你可以使用他们的解决方案来安排派对的邀请。
我们介绍了 User1,他是一名后端工程师,负责为该解决方案创建后端。然后我们还有 User2,他目前是该产品的产品负责人。他们都属于公司的 IT 部门。接着是 User3,他属于市场营销部门。
User1 和 User2 都已成为 GitLab 中 IT 小组的成员。User3 是市场营销小组的成员,但仅具有 IT 组的报告者权限。
让我们帮助他们(最小化地)创建这个产品,并展示他们如何使用 GitLab 来完成这一过程。
在用户和开发人员都参与的会议中(在 XP 中的发布规划,或者在使用 Scrum 时的 Sprint 0),决定了以下需求:
-
我们希望构建一个帮助组织活动的应用程序。
-
需要通过电子邮件进行沟通。
-
我们提前创建了一个邀请者名单。
-
邀请者可以互动地表示是否会参加。
-
非功能性需求:文档非常重要。
-
非功能性需求:我们希望尽可能实现自动化。
-
非功能性需求:所使用的工具应增强协作。
-
非功能性需求:使用的代码应至少经过另一个人的审查。
在会议结束时,需求被优先级排序,开发人员与客户不再讨论,转而讨论 GitLab 作为产品的可能性。这个阶段是下一部分的主题。
DevOps 阶段——管理你的想法
我们学会了不再事先创建大型设计,而是采用迭代开发的方式。然而,即使是使用敏捷方法论的项目,在开始编码之前也有一些需要考虑的事项。在 DevOps 生命周期的这一阶段称为管理,它包括了整个生命周期。这是敏捷迭代的开始,也是结束,处于两者之间。你将从一开始就管理你的解决方案,这个过程永不停歇。管理的一部分是了解你的进展情况。你可以在 GitLab 中分析这一点,正如下一部分所展示的那样。
周期分析
在通过敏捷方法开发软件时,最重要的度量指标之一是周期时间。这个关键绩效指标(KPI)可以最好地描述为从开始工作到任务完成所经过的总时间。或者在 IT 领域,它指的是将一个想法实现到生产环境所需要的时间。
在敏捷项目管理之前,曾使用微软项目等工具来跟踪时间。如今,估算不再被输入到项目中,而是通过故事点来计算其他因素,如风险和速度,这些用于提前规划几个星期的工作。
在 GitLab 中,有一个地方可以测量周期时间:周期分析。这意味着你需要使用 GitLab 的项目管理功能,而且它在所有版本中都可以使用。以下图片是 GitLab CE 项目的周期分析示例。请注意,一个想法进入生产环境大约需要一个月的时间(这与其实际发布周期相符):

我们可以在项目的后期使用周期分析来检查 GitLab 的使用情况以及开发团队的开发速度。
DevOps 阶段——规划你的特性
GitLab 所设想的 DevOps 生命周期的下一个阶段是计划。在这个阶段,我们将探索所有为编码和构建解决方案做准备的步骤。在 GitLab 中,有几个工具可以帮助你完成这个阶段,无论你偏好哪种开发方法(敏捷或瀑布式)。从哪里开始?我们总是从创建一个问题开始。我们将在下一节中讨论这个问题:
问题
什么是问题?其实它可以是很多东西。它可能是一个新的商业想法,一个技术问题,或者是求助的呼声。问题就像是开始一个新的讨论,它是一种表达思考的方式。随着其他人的参与,它应该会发生变化。它是 GitLab 中的原子性的、基本的、最初的事物,其他一切都是由此派生的。通常,最好先查看现有问题列表,看看你的问题或需求是否已经是现有问题的一部分。
GitLab 作为一个产品,促进了这一讨论,并提供了管理讨论的方式以及验证假设的流程。
一个问题有多个属性和概念,我们将一一查看。
内容
当然,问题有内容,那么它是如何构成的呢?问题有一个标题和描述。还可以通过 markdown 格式在问题中插入任务。作为内容的一部分,还包括随附的评论和通过问题关联并可查看的事件/活动。在内容中,你还可以插入快速操作,这将在本章后面讨论。
问题的状态
问题有几种状态。当然,它可以是打开的或关闭的。另一个状态方面是问题的机密性。如果有敏感信息,问题可以标记为机密。这样问题的可见性就限制为拥有至少报告者权限的组成员。
元数据
每个问题都有一个作者,这些信息会显著地显示出来,同时还会显示当前的指派人。其他元信息包括一些熟知的项目管理数据,如:里程碑、截止日期和权重。
让我们基于我们的示例来澄清这一点。Event Horizon公司即将开始第一次 Scrum 迭代。他们想使用 GitLab 的问题来进行项目管理。
User1 创建了一个问题,询问我们将使用哪种技术。这可以是第一个问题的一个例子,如下图所示:

你会看到有一个定义了任务的列表。
User2 创建了一个关于项目应使用哪种文档风格的问题,如下图所示:

这两个问题都是有效的问题,可以用来发起讨论,我们将看到当一个问题发展到下一个层次——讨论时会怎样。我们将在下一节中讨论这个问题。
讨论
这是 GitLab 中非常重要的部分,是问题发现的下一步。你可以在问题的上下文中通过评论的形式给出反馈,也可以在以下内容中进行:
-
史诗(Epics)
-
合并请求
-
代码片段
-
提交(Commits)
-
提交差异(Commit diffs)
也可以创建线程式讨论,或将评论转化为线程讨论。你可以使用 Markdown 格式化你的文本,并使用快速操作(如本章中“快速操作”部分所述)。默认的评论形式如下所示:

如果你已设置 GitLab 接收电子邮件,你可以回复作为评论通知邮件发送的电子邮件。回复这些邮件会创建新的评论或讨论项。在以下截图中,你将看到“事件视界”Web 应用程序中的一个问题讨论:

如前所述,也可以向一个史诗(Epic)添加讨论,我们将在后面讨论这一点。以下示例是添加任何讨论到史诗的截图:

通过这些讨论,想法得以发展,对它的理解也得以深化。
里程碑
里程碑在项目中可以有不同的功能,但在 GitLab 中它们用于标示在实现一个共同目标时所处的位置,该目标由问题和合并请求定义。
里程碑可以用来标记敏捷迭代或冲刺的开始和结束。将里程碑命名为你的冲刺名称是非常实用的,然后你可以将问题关联到里程碑上,以便添加工作。
在 GitLab 中,它们作为一个发布周期使用。例如,当从版本 11.8 升级到 11.9 时,所有包含在该版本中的工作将由一个里程碑表示,并且该里程碑将标记为 11.9。每项工作都通过一个问题来表示,例如,你需要解决的问题或讨论。所有这些问题共同推动一个大型的里程碑。
对于“事件视界”项目,定义了三个里程碑:mvp 1、MVP3 和 mvp2。你可以如下所见:

如果我们打开一个里程碑,它具有以下特点:

项目里程碑只能在项目上下文中与问题和合并请求链接。你可以通过访问“问题”(Issues)然后选择“里程碑”(Milestones)查看里程碑列表。组里程碑可以在组上下文中与问题和合并请求链接,这意味着你可以将其链接到多个属于该组的项目。你可以通过组中的“问题”和“里程碑”链接找到这些里程碑。通过仪表板的里程碑列表或通过顶部导航链接“里程碑”(Milestones),可以查看所有里程碑的概览。
史诗(Epics)
如果您使用的是 GitLab EE,您可以创建史诗。史诗是一个广泛的用户故事,但仍然需要被拆解成一组更小的用户故事。通常,史诗描述的是一个定义好的功能或产品特性,但在团队能够承诺在一个冲刺(sprint)内实现之前,还需要进行更详细的工作。它像是问题中的一个主题。
对于大型组织,特别是处理长期项目管理程序的组织,还可以使用多级史诗(epic),以便您能够链接并协调各项工作。在创建史诗时,表单中有一个按钮(+)可以使用。
史诗与问题非常相似,您拥有相同的编辑功能和状态。相同的格式化方式也适用,快捷操作也一样。对于史诗来说,更为重要的可能是设置到期日期的选项。大型项目通常跨多个业务领域,且更依赖于日期,因此我们可以想象,这些字段在这里比在敏捷驱动的小型项目中更为合适。
从史诗中,您可以导航到关联的问题。同时,当问题变得过大并且被错误地表示为史诗时,也可以将问题提升为史诗。
对于 Event Horizon 项目,产品负责人 User3 可以创建一个史诗,以追踪两个不同项目的进展以及之前创建的问题。下图展示了这些项目的进度追踪:

使用史诗来将问题分组的功能对于保持对多个讨论、问题和项目的控制非常必要,这些讨论、问题和项目共同朝着解决方案迈进。
时间追踪
通过此功能,您可以追踪在问题和合并请求上花费了多少时间。您还可以追踪预估时间,以了解自己的进展情况。此功能是 GitLab Core 的一部分。
在下图中,我们可以看到,在 eventmanager 项目中,某个问题的时间已经被预估和使用:

在问题或合并请求的正文中,以及在评论中,您可以使用快捷操作输入该问题的预估时间和已用时间。此操作仅限团队成员执行。
如下图所示,您可以使用 /estimate,后跟时间单位。如果某个任务需要五天四小时,您可以在评论中写 /estimate 5d 4h,然后按下评论按钮。每个问题只能有一个预估时间。您也可以通过使用 /remove_estimate 来删除预估时间。以下是参考截图:

同样,你可以记录在问题上花费的时间。项目成员可以使用快速/spent操作在问题和合并请求中添加时间,累计专门用于某个问题的总时间。例如,如果已经花费了两个小时,你可以发出/spent 2h,它将在右侧面板显示。你甚至可以通过使用负数来删除花费的时间,例如,/spend -1h。它不会低于 0,但会重置该数字。你也可以使用/remote_time_spent一次性移除所有已花费的时间。
快速操作
GitLab 中一个非常方便的功能是快速操作。你可以使用带有特定关键字的/来触发对问题、史诗、合并请求和提交的命令,就像使用 IRC 聊天一样。这比使用 GitLab 的网页按钮或其他控件要快。记得将每个命令放在单独的行上,否则它将被解析错误。一旦它们被解析并执行,它们将从文本中被移除,其他人无法看到它们。当然,执行的操作会有审计日志。
一些快速操作的示例如下:
-
/todo:从评论中添加一个待办事项。 -
/done:标记待办事项为已完成。 -
/close:与点击关闭按钮相同 -
/assign me或@其他人 -
/milestone %milestone:从评论中设置一个里程碑。 -
/estimate <1w 3d 1h 10m>:添加时间估算 -
/due <in 1 day>:设置截止日期 -
/approve:批准合并请求 -
提交消息的特殊快速操作:
/tag v1.5– 立即为提交添加一个自定义消息标签。
你可以在此处找到所有快速操作:docs.gitlab.com/ee/user/project/quick_actions.html
项目问题看板
GitLab 中隐藏着一个非常实用的项目管理功能,它被称为项目问题看板。它可以通过可视化来实现工作流,并帮助进行规划和组织。它并不强制要求某种工作方式,你需要自己组织并形成工作流程。看板强烈依赖于标签,这些标签用于将问题分组到不同的列表中。
如果你进入左侧导航栏中的“问题”部分,你会找到“看板”选项。默认情况下,你会看到一个名为“开发”的看板,提供了基本的流程,如“打开”,“待办”,“进行中”和“已关闭”。你可以将问题从一个流程拖动到另一个流程,表示状态发生了变化。例如,如果一个问题已经完成,你可以将其拖动到“已关闭”。这样,问题会自动获得“已关闭”标签。以下截图是 eventmanager 项目问题看板的示例:

如果你使用 GitLab 企业版,甚至可以拥有多个问题看板,这可以意味着几种情况:
-
一个看板可以由多个团队共享:团队问题看板。
-
每个团队可以拥有自己的看板。
-
根据看板的范围(里程碑、标签、分配人和权重),项目有几种不同的视图。
在下图中,你可以看到如何通过点击下拉列表中的“开发”来创建新的问题板。默认的板如下所示:

当你点击“创建新板”时,会弹出一个表单,要求你输入新板的名称和范围。与标签一样,它只是问题的一个属性,用于对问题进行分组或聚合。我们选择专注于里程碑,并选择里程碑 MVP3,这个里程碑是来自第 3 次冲刺的最小可行产品,如下所示:

创建后,你将看到一个问题板,其中包含已分配到里程碑的所有问题。你可以通过点击添加问题(这会将问题与里程碑关联),如以下示例所示:

问题板可以作为团队的信息展示板,它按照你想要的方式组织问题,并且可以作为团队会议的讨论主题。
待办事项
对个人用户有用的一个规划功能是待办事项。通知邮件可能会堆积在你的收件箱里并变得杂乱。一个易于使用的待办事项列表,可以按时间顺序查看任务,更加聚焦。你可以在导航栏顶部找到它们,如下所示:

当你点击待办事项时,你将看到一个待办事项列表,并可以选择按不同字段进行排序,如以下示例所示:

以下项可能会触发待办事项:
-
一个问题或合并请求被分配给你。
-
在问题、合并请求、提交或史诗中被
@提及。 -
在你的项目 CI/CD 管道中的某个失败任务被标记为不允许失败。
-
当在自动化管道中,合并请求出现冲突时。
DevOps 阶段 - 创建它
现在,项目已经通过创建问题、里程碑等进行了规划,接下来是时候真正开始构建了。下一个阶段是创建,GitLab 提供了多种工具来帮助你实现这一目标。在开始构建之前,你应该确保项目和团队结构适合合作。
项目和小组
Event Horizon 公司在 GitLab 中创建了两个小组。一个小组叫做 IT,另一个叫做 Marketing,如下图所示:

在此上下文中,我们创建了两个独立的项目。第一个叫做 eventmanager,它将包含技术解决方案的源代码。如果这是你的第一个项目,你可以点击“新建项目”,或者点击顶部导航栏的 + 图标来创建该项目。创建新项目的表单如下所示:

项目的标题是eventmanager,它也会出现在 URL 中。仔细查看 URL 的第一部分。这是命名空间,默认情况下是你自己的命名空间,但我们希望这个项目位于 it 组中。当你这么做时,你会注意到可见性会自动设置为私密。这是因为it组将其作为默认设置。
最后,我们希望这个项目能附带一个 README 文件,因此我们需要选择自动创建它。不要担心项目的外观,我们将在迭代开发中进行改进。
接下来,有一个名为 eventmanager-documentation 的第二个项目,它将包含文档内容。我们将使用 GitLab Pages 作为技术来构建我们的用户文档,并选择 Pages/Plain HTML 作为模板。如下图所示:

你需要输入项目名称和项目标识符,并根据以下示例确定可见性:

如果你点击“项目”,你将看到两个项目的列表,如下所示的示例:

现在我们的项目和组结构已经准备好,让我们处理其他促进合作的功能。
代码片段
如果你曾作为开发者使用过 GitHub,你应该见过gists。这些是可以用于多个目的的小段代码。它们通常会出现在多个代码仓库中,但也可以作为 gists 进行重用。在 GitLab 中,这个概念被称为snippets(代码片段)。它们也可以用作讨论示例,等等。下面的示例是一个新的代码片段截图:

你可以在个人和项目级别创建代码片段。它们可以设置为公开,甚至有一个选项可以将它们嵌入到你自己的网站中,如下所示的示例:

代码片段可以用来共享信息,使用它们可以帮助实现平台的需求,从而增强协作性。
Web IDE
从 GitLab 10.4 版本开始,GitLab 提供了增强版的 Web 编辑器,使你能够在网页上在线编辑代码。它提供了许多不同的功能,包括对最常用语言(PHP、Ruby、Shell、Python、Java、C)和标记语言(XML、Markdown、HTML)的语法高亮显示。它基于 Monaco 编辑器,你可以在这里找到它:microsoft.github.io/monaco-editor/。
Event Horizon 公司也可以在 Web IDE 中开发他们的软件。当然,它支持 Ruby,这是 GitLab 主要编写的语言。
在以下截图中,你将看到在 Web IDE 中显示的 eventmanager 项目的文件:

使用此工具还将增强 Event Horizon 内部的协作。
Wiki
虽然我们为我们的新软件产品创建了一个单独的项目来提供文档,但 GitLab 默认也提供了一个创建文档的系统。那就是 Wiki 功能。每个项目都可以轻松启用此功能,之后你将拥有一个完整的 Wiki 系统。
当你通过项目中的左侧导航栏进入 Wiki 时,你将看到如下界面:

如果你创建了一个新的 Wiki,你需要提供标题、所使用的标记语言,然后你就可以开始添加内容。由于所有信息本身都保存在附带的 Git 仓库中,你可以提供提交信息,因为保存页面就相当于创建了一个新的提交,如下图所示:

一旦创建,你下次点击 Wiki 时将看到该页面。你现在可以创建更多页面(甚至是页面层次结构,如果你愿意),并且你可以查看页面历史(所有内容都有版本控制),如下例所示:

如你所见,Wiki 是另一个增强员工协作的功能。
受保护的分支
因为在 Git 中有可能重写整个历史,GitLab 内置了一些机制来帮助减轻这一风险。你可以使用受保护的分支。
受保护的分支具有以下特点:
-
在 GitLab 11.9 之前,你无法创建受保护的分支,只有Maintainers能创建(从 11.9 起,开发人员也可以创建)。
-
只有
Maintainers才能直接推送到受保护的分支。 -
禁止使用强制推送到受保护的分支。
-
受保护的分支不能删除。
接受更改到受保护分支的唯一方法是使用合并请求。默认情况下,主分支是受保护的分支。以下示例是受保护分支的截图:

使用受保护的分支强制要求进行代码审查,这是eventmanager项目的要求。
合并请求
所以,eventmanager的第一个版本几乎准备好了。User1 已经将最新的更改上传到分支 MVP1,并准备创建一个合并请求,如下图所示:

你可以通过在描述字段中指定Closes #2, #3来查看快速操作的使用情况。
如果你向下滚动,你将看到一个部分,展示当你接受此合并请求时,哪些更改被引入到主分支。它会显示我们示例中的更改数量(6),哪些文件被更改,以及更改的内容。如下截图所示:

你可以看到 User3 已被添加为合并请求的审批人,因此他或她必须审查这些更改,如下图所示:

在屏幕底部,你可以看到此合并请求将合并哪些分支,并且可以指定删除源分支和/或合并更改(这意味着所有更改都将放置在一个提交中)。如以下示例所示:

如果 User3 登录,他将看到一个待办事项,要求审核此合并请求。举个例子,假设 User3 进行了一次审核并留下了一些评论。让我们看一下index.erb文件的部分内容,该文件用于事件管理应用程序中显示邀请某人参加活动的消息。你可以在代码示例中找到该文件(github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter09/eventmanager)。以下是审阅者认为有问题的代码片段:
<p>You are invited to my event on December 15!</p>
User3 认为文本You are invited to my event on December 15!过于具体,建议简化文本。他希望将文本更改为You are invited to my event!。他可以在审核评论中使用快速操作添加建议,格式如下:

保存评论后,它会显示如下示例:

User3 现在可以完成他的审核了。User1 会收到通知,表示合并请求已被审核。当他登录并导航到合并请求中的审核评论时,他将发现 User3 提出的建议,并看到一个按钮可以应用该建议。评论将自动标记为已解决,如下所示:

User3 还有一条关于attendees.yml文件中某个条目的评论,如下代码所示:
attendees:
1:
attending: ''
email: someone@joustie.nl
guid: 3e8cb800-51ec-4d5d-ac7a-ca37ebe06389
name: Someone
10:
attending: 'NO'
email: anotherone@joustie.nl
guid: 1070d08f-c5ea-4247-95b1-ee7a3ca4a342
name: Anotherone
11:
attending: 'NO'
email: organiser@joustie.nl
guid: c7d49f48-2f05-468d-9d2e-9609e8311f3c
name: Organiser
在attendees.yml中没有任何带有YES的示例条目,因此 User3 建议更改一个条目,如下截图所示:

不幸的是,参加冲刺结束时演示的客户并没有被告知可以选择No的选项。同时,也没有定义实际的测试。因此,User1 决定将此评论作为问题推送到下一轮产品迭代中,示例如下:

当所有讨论点都已解决后,User3 可以批准合并请求,如下截图所示:

合并请求功能是使用 GitLab 的最重要原因之一。你可以在项目中合并代码或文本,并进行协作。
DevOps 阶段 – 验证你的产品
DevOps 生命周期的下一个阶段是验证。在创建阶段构建产品后,你需要验证产品是否符合要求,是否安全,以及整体质量是否合格。这一切都可以通过使用持续集成(CI)功能在 GitLab 中完成。在接下来的部分,我们将讨论 CI 管道中的若干组件。
代码质量报告
一个验证代码质量的不错功能是 GitLab 中的 CI/CD 质量扫描。它利用开源且免费的 Code Climate 引擎(codeclimate.com/)。它被嵌入在一个特殊的 Docker 容器中,可以在你的 GitLab 运行器内运行。以下代码是运行此类扫描的.gitlab-ci.yml文件示例:
code_quality:
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run
--env SOURCE_CODE="$PWD"
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
paths: [gl-code-quality-report.json]
当任务运行时,将生成以下日志:
Running with gitlab-runner 11.7.0 (8bb608ff)
on test-runner 97YoGmXL
...
下载 Docker 容器后,它将开始扫描代码。扫描完成后,将生成如下报告:
Uploading artifacts...
gl-code-quality-report.json: found 1 matching files
Uploading artifacts to coordinator... ok id=403 responseStatus=201 Created token=ngLDxFmF
Job succeeded
要查看报告,可以从右侧下载工件,那里有相关链接。以下示例显示了 Job 工件标签的截图:

如果你用编辑器打开报告并进行适当的格式化,它将显示警告或高风险/严重问题。在 eventmanager 项目的案例中,发现了一个未使用的变量,如下所示:
"type": "Issue",
"check_name": "Rubocop/Lint/UnusedBlockArgument",
"description": "Unused block argument - `letter`. If it's necessary, use `_` or `_letter` as an argument name to indicate that it won't be used.",
"categories": [
"Style"
],
"remediation_points": 50000,
"location": {
"path": "addUID.rb",
...
请注意 "remediation_points": 50000, 这一条目,它为问题评分。这个分数根据不同类别的发现而有所不同,可以用来比较多个扫描的总分,展示总质量的进展(或下降)。
以下是报告中提到的代码块:
yaml_hash['attendees'].each do |*letter*, hash|
p hash['name']
p hash['email']
p hash['attending']
p hash['guid'] = SecureRandom.uuid if p hash['guid'] == nil
end
你可以看到,在第一行中有一个letter变量,它在循环中未被使用。如果我们将letter改为_letter,测试应该不再报告此为警告。未使用的变量会被报告为警告,你可以通过加下划线来抑制警告。
在下一次 CI 管道运行后,代码质量扫描将显示未发现任何问题,报告将是空的。
你也可以在合并请求中使用代码质量报告。它可以在合并前运行,并比较修复点。如果有可能存在严重问题,任务会显示为失败并标记为红色。现在我们已经验证了应用程序的最小质量,我们还希望验证应用程序或网站是否对用户可用。我们可以构建审阅版本来进行手动测试。这是我们接下来的部分。
审阅应用
您的软件产品的最终验证是运行所有可用的测试,甚至模拟生产环境。在 GitLab 中,可以通过使用 GitLab CI 和 GitLab runners 来实现这一点。为了使其生效,我们将展示一个使用 eventmanager 文档网站的示例。该项目的一个要求是,当然,创建文档。我们还为此创建了一个单独的项目,叫做 eventmanager-documentation。已经存在网页,因此我们来自动化审查过程(这也是一个要求:更多的自动化)。我们将使用已启用作为 Web 服务器的 Amazon S3 存储桶(您可以在此处找到关于如何配置它们的信息:docs.aws.amazon.com/AmazonS3/latest/dev/HowDoIWebsiteConfiguration.html)。
要将文件上传到 S3 存储桶,您需要通过 AWS 进行身份验证。我们可以为项目添加包含所需 AWS 凭证的机密变量。您可以在项目 | 设置 | CI/CD | 环境变量下找到它们,如下图所示:

当 AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY 变量存在时,您可以在 GitLab CI/CD 管道中执行 aws-cli 操作。用于部署审查应用程序的 .gitlab-ci.yml 文件如下所示:
review apps:
variables:
S3_BUCKET_NAME: "joustie-1"
image: python:latest
environment: review
script:
- pip install awscli
- aws s3 cp public/*.html s3://$S3_BUCKET_NAME/
该作业称为审查应用程序,正如第一行所看到的。然后,声明了一个变量,即 S3 存储桶名称。接下来是所使用的 Docker 镜像名称,在这种情况下,它是一个安装了 Python 的镜像,因此我们可以利用在脚本部分安装的基于 Python 的 AWS CLI 工具。最后一行包含将文件复制到 S3 存储桶的 AWS CLI 命令,并通过我们设置的环境变量进行身份验证。
保存此文件后,作业将运行,如果一切顺利,HTML 文件将上传到您的存储桶。如果您查看作业日志,您将看到如下内容。
- 首先,它获取容器:
Pulling docker image python:latest ...
- 然后它克隆了仓库:
Checking out 5625d409 as master...
它安装了 Amazon CLI 工具:
$ pip install awscli
Collecting awscli
- 然后,它将 HTML 文件上传到 S3 存储桶:
$ aws s3 cp public/*.html s3://$S3_BUCKET_NAME/ Completed 617 Bytes/617 Bytes (2.1 KiB/s) with 1 file(s) remaining
当作业成功时,GitLab 中将创建审查环境。您可以点击审查链接,如下所示:

下一页将显示审查环境中的部署列表,如下所示:

您可以通过编辑手动将 URL 添加到环境中,如下所示:

添加 URL 到环境后,将显示一个查看部署的按钮,如下所示:

如果您点击查看部署,您将进入审查应用程序,如下所示:

从 GitLab 12.0 版本开始,在管道视图中,“查看应用程序”旁边将有一个“审核按钮”。点击后,它将运行审核应用程序,并在角落显示一个小表单,你可以在其中输入反馈。这些反馈将直接插入到问题中。

因此,为了验证我们的项目,我们使用了事件管理器代码的代码质量报告。我们发现,最初有一个警告,提示某个变量没有被使用。我们推荐了修复方案,并再次运行管道,警告消失了。
我们展示了在文档项目中使用审核应用程序的示例。通过使用此审核应用程序,我们可以验证最初的需求是否得到了满足。
在你的管道中实现的两种自动化方法将极大增强你的敏捷性和 DevOps 能力。当你发现错误时,可以修正它们并再次运行管道。
DevOps 阶段 – 为使用打包
GitLab 在其产品中使用 Docker 容器有多个用途。为了存储为项目自定义构建的容器镜像,GitLab 还为其添加了 Docker 容器注册表功能。通过使用此功能,你无需将镜像存储在一个可能不够安全的远程位置。容器注册表是下一节的主题。
GitLab 容器注册表
如果你在 GitLab 中启用了容器注册表功能,你可以存储在 CI/CD 流程中构建的 Docker 镜像。要启用此 GitLab 功能,你需要重新配置 GitLab 实例并启用注册表功能。 启用后,每个项目的左侧菜单中会显示一个注册表菜单项。如果你点击它,你可以查看注册表的内容和如何使用它的说明,如下图所示:

要将镜像存储在其中以备后用,你需要编辑项目根目录中的 CI/CD 配置文件。以下是事件管理器项目的示例:
build:
image: docker:stable
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
stage: build
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $CI_REGISTRY/it/eventmanager:latest .
- docker push $CI_REGISTRY/it/eventmanager:latest
总结该文件,它定义了一个构建任务。在第二行,定义了要使用的镜像为docker:stable。因为这个镜像将构建一个 Docker 容器,所以它使用了 docker:dind 服务,这是一个启用 Docker in Docker 功能的服务。然后,在变量部分,定义了两个 Docker 变量:连接到的 Docker 主机(即 Docker 引擎,它将自己构建一个容器)和要使用的 Docker 存储驱动程序,默认是 overlay2。定义的阶段是构建。接着,在脚本部分,给出了实际的脚本,它实际上是一个基本的 Docker 构建,前面是登录 GitLab Docker 注册表,后面是将构建的镜像推送到该位置。你可以看到以 $CI_REGISTRY 开头的变量。这些是 GitLab 中可用的预定义变量,可以用来利用一次性令牌。
当你在 Web IDE 中保存此文件或将其推送到 GitLab 时,新管道将立即运行。如果没有运行,或者显示没有可用的运行器,请检查你的运行器是否处于特权模式下。它需要这个模式来运行 docker:dind。
当镜像完成后,它会带着最新标签推送到 GitLab 注册表。如果成功,任务会成功并显示为绿色,正如以下代码所示:
$ docker push $CI_REGISTRY/it/eventmanager:latest
The push refers to repository [registry.joustie.nl:5005/it/eventmanager]
4fef5b78d890: Preparing
2cf71380877f: Preparing
...
latest: digest: sha256:346123861d2c745902e3e2aae3101c0fb3c17414467c74204119f17c2c0cfc9c size: 1786
Job succeeded
之后,你可以通过导航到项目的注册表页面来验证容器是否存在,如下所示:

注册表是从 CI/CD 管道存储镜像工件的一种安全方式。在 DevOps 管道中,它是包阶段的一部分。
总结
本章尝试通过一个示例解释 GitLab 流程。通过事件管理器示例展示模型的第一阶段,能够说明为什么 GitLab 是从敏捷革命中诞生的工具。从构想到实施,每个步骤都可以自动化,并且非常可定制。在下一章中,我们将继续探讨管道,通过查看发布和配置阶段。
问题
-
DevOps 循环的第一阶段是什么?
-
GitLab 中最重要的信息单元是什么?
-
为什么可以将评论转换为讨论?
-
如何为问题提供四天的估算?
-
如何在 GitLab 中强制执行审查机制?
-
GitLab Runner 运行代码质量扫描需要什么设置?
-
如何启用链接到已部署环境的功能?
-
为了在项目中启用注册表链接,你需要做什么?
深入阅读
-
全面的 Ruby 编程 由 Jordan Hudgens 编著:
www.packtpub.com/application-development/comprehensive-ruby-programming -
学习 Docker - Docker 18.x 基础 由 Gabriel N. Schenker 编著:
www.packtpub.com/networking-and-servers/learn-docker-fundamentals-docker-18x -
AWS 自动化秘籍 由 Nikit Swaraj 编著:
www.packtpub.com/virtualization-and-cloud/aws-automation-cookbook -
有效的 DevOps 与 AWS - 第二版 由 Yogesh Raheja、Giuseppe Borgese 和 Nathaniel Felsen 编著:
www.packtpub.com/virtualization-and-cloud/effective-devops-aws-second-edition
第十一章:发布和配置阶段
在本章中,我们将进一步推进自动化。我们将首先展示如何在测试后将您的代码部署到预发布环境,并最终部署到生产环境。这是 DevOps 转型中的一个基本概念。本章的最后,我们将解释 Auto DevOps,它是一种将部署完全自动化的方式,通过集成测试、安全扫描,甚至性能测试,将代码部署到 Kubernetes 集群。这被 GitLab 视为最优的 DevOps 路径。
在本章中,我们将涵盖以下主题:
-
在 Amazon Web Services 上进行的持续部署
-
使用 Google Cloud 中的 Kubernetes 集群进行 Auto DevOps
技术要求
为了跟随本章中的指示,请下载本书的 GitHub 仓库以及示例:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter11。
在Chapter11目录下,您将找到两个应用程序,它们用于本章中的示例。
要运行某些自动化脚本,您需要一个Amazon Web Services(AWS)账户,您可以在此创建:aws.amazon.com/free/。
要了解如何在Google Kubernetes Engine(GKE)上创建集群,您还需要一个可以与 Google Cloud 一起使用的 Google 账户,您可以在此创建:cloud.google.com/products/search/apply/。
持续部署
如第九章所述,持续部署是GitLab 愿景——一个应用中的完整工具链的延伸,它是持续集成的扩展,旨在最小化周期时间;也就是开发团队生产一行新代码并将其部署到生产环境的时间。我们将通过将eventmanager Ruby 代码部署到 Amazon Elastic Beanstalk 来演示这一实践,Elastic Beanstalk 是一种用于部署和扩展使用多种语言在不同平台上开发的 Web 应用程序和服务的服务。
还有一个选项是创建运行 Puma Web 服务器的 Ruby 环境。让我们登录 AWS web 控制台(console.aws.amazon.com/console/),并点击“服务”标签。我们可以使用搜索选项在 Amazon Cloud 的广泛服务中查找某些内容:

在“查找服务”小部件中输入beanstalk,然后点击它找到的链接。选择创建一个新环境:

环境将被部署。当完成后,您可以在仪表板中查看:

我们将使用 CI/CD 作为持续部署策略。首先,我们将创建一个 .gitlab-ci.yml 文件,包含不同的阶段(暂存和生产)。该文件的第一部分将定义在不同阶段使用的变量。在这里,我们将定义一个 S3_BUCKET_NAME,该变量将用于复制部署包,REGION 用于指定服务的托管位置,最后是 APPNAME,它对应于 Amazon Beanstalk 中的应用名称:
variables:
S3_BUCKET_NAME: "elasticbeanstalk-eu-west-1-513361393569"
REGION: "eu-west-1"
APPNAME: "eventmanager"
我们需要在推进到下一阶段之前运行我们的测试。在以下代码中,第二行指定了一个安装了 Ruby 的 Docker 镜像来运行我们的测试。在脚本部分,运行默认的rspec测试:
test:
image: ruby:latest
stage: test
script: "bundle install;rspec"
这个步骤将使用 dpl 工具(github.com/travis-ci/dpl)与 Amazon Beanstalk 进行交互并部署到暂存环境:
deploy_staging:
stage: deploy
script:
- echo "Deploy to staging server"
image: ruby:latest
environment:
name: staging
url: http://staging.gbnfcg9st9.eu-west-1.elasticbeanstalk.com
script:
- echo "Deploying to staging"
- gem install dpl
- dpl --provider=elasticbeanstalk --access-key-id=$AWS_ACCESS_KEY_ID --secret-access-key=$AWS_SECRET_ACCESS_KEY --app=$APPNAME --env=$CI_ENVIRONMENT_NAME --region=$REGION 13 --bucket_name=$S3_BUCKET_NAME
only:
- cd
如你所见,我们还需要从某处获取 AWS 凭证($AWS_ACCESS 变量)。我们可以在 eventmanager 项目的设置中定义这些变量,放在 CI/CD 环境变量部分。现在,我们将使用 GitLab 11.8 版本以来提供的 多项目管道 功能。我们将定义一个 桥接作业,该作业将在 eventmanager-documentation 项目中运行默认管道:
deploy_documentation:
stage: deploy
variables:
ENVIRONMENT: staging
trigger: marketing/eventmanager-documentation
我们还想部署到生产环境。为此,我们为其创建了一个名为 production 的独立 Beanstalk 实例。但如果我们希望最后一步保持为手动触发的步骤呢?那么我们需要定义一个带有 when:manual 的控制结构,这意味着该步骤必须手动启动:
deploy to production:
stage: deploy
environment: production
when: manual
image: ruby:latest
script:
- echo "Deploying to production"
- gem install dpl
- dpl --provider=elasticbeanstalk --access-key-id=$AWS_ACCESS_KEY_ID --secret-access-key=$AWS_SECRET_ACCESS_KEY --app=$APPNAME --env=$CI_ENVIRONMENT_NAME --region=$REGION 10 --bucket_name=$S3_BUCKET_NAME
only:
- cd
如果你保存这个 .gitlab-ci.yml 文件(并进行提交和推送),它将开始部署。如果你点击管道,你将看到作业概览:

在前面的截图中,你可以看到各个阶段。首先是测试阶段,运行 rspec 测试。deploy_documentation 作业触发下游管道(eventmanager-documentation)。同时,部署到暂存区域也开始了。让我们点击开始的作业:

通过查看部署到暂存区域的日志文件,你将看到以下代码:
Running with gitlab-runner 11.9.2 (fa86510e)
on Computer1 8REjeNy3
Using Docker executor with image ruby:latest ...
Pulling docker image ruby:latest ...
Using docker image sha256:f39c31795d257be1b6344eefdc324180a90ffb9b82a52d171982703dd26f549c for ruby:latest ...
Running on runner-8REjeNy3-project-3-concurrent-0 via Joosts-iMac-Pro.fritz.box...
Reinitialized existing Git repository in /builds/it/eventmanager/.git/
Clean repository
Fetching changes...
fatal: remote origin already exists.
Checking out 2dbf81c9 as cd...
...
Skipping Git submodules setup
$ echo "Deploying to staging"
Deploying to staging
容器安装了 dpl 依赖:
$ gem install dpl
Successfully installed dpl-1.10.8
1 gem installed
然后它运行部署:
dpl --provider=elasticbeanstalk --access-key-id=$AWS_ACCESS_KEY_ID --secret-access-key=$AWS_SECRET_ACCESS_KEY --app=$APPNAME --env=$CI_ENVIRONMENT_NAME --region=$REGION --bucket_name=$S3_BUCKET_NAME
dpl 工具仅在 Git 仓库为干净状态时运行:
Preparing deploy
Cleaning up git repository with `git stash --all`. If you need build artifacts for deployment, set `deploy.skip_cleanup: true`. See https://docs.travis-ci.com/user/deployment#Uploading-Files-and-skip_cleanup.
No local changes to save
Deploying application
No stash found.
Job succeeded
除了显示应用已部署的消息外,没有关于上传的反馈。你可以通过访问在 AWS 控制台中创建的 Beanstalk 环境中提到的 URL 来验证此部署。
你可以从图示中手动触发部署到生产环境的作业,这就是我们看到播放按钮的地方。
在 GitLab 中,你可以前往操作 | 环境 | 生产查看部署情况。你也可以回滚到先前的发布版本:

在这一部分,我们向你展示了如何使用 GitLab CI 和 GitLab Runner 实现一个部署管道。你只需使用 Shell 脚本创建它们,并通过使用多项目管道和高级语法让它们变得更加复杂。
Auto DevOps
默认情况下,Auto DevOps 在每个项目中都是开启的。它本质上是一个非常复杂的.gitlab-ci.yml文件,概述了从创建阶段开始的整个 DevOps 管道。
这符合 GitLab 的愿景,即提供一个应用程序来协作完成应用程序整个 DevOps 生命周期的工作。
配置 Auto DevOps
如前所述,Auto DevOps 默认在每个项目中启用,但如果你想禁用它或配置不同的设置,你需要进入设置,方法是前往设置 | CI/CD | Auto DevOps:

如你所见,你需要配置一个 Kubernetes 集群才能让这一切正常工作。
你可以在这里管理的另一个设置是部署策略。默认设置是管道部署到生产环境。这可能不是你企业所需的策略,你也可以使用增量发布策略。
你还应该注意到,部署管道在最后一步(生产)之前是完全自动化的。你可以选择将其作为手动步骤。
要控制管道中的各个步骤,你可以查看左侧的操作菜单。以下是可用操作的截图:

当第一段代码推送到代码库时,一个 Auto DevOps 管道将被创建。对于eventmanager项目,它如下所示:

让我们评估一下前面截图中的 Auto DevOps 管道的每个步骤。
构建步骤
主要思路是在构建阶段,你准备好以打包的方式运行你的代码——通过 Dockerfile 构建的 Docker 容器——通过 Heroku 构建包。
对于eventmanager应用,user1创建了以下 Dockerfile:
FROM ruby:2
COPY . /var/www/ruby
WORKDIR /var/www/ruby
RUN bundle install
CMD ["ruby","eventmanager.rb"]
EXPOSE 5000/tcp
如你所见,从前面代码的第一行,它拉取了一个基本的支持 Ruby 的 Debian Linux 镜像。它将所有源代码复制到一个目录并进入该目录。然后,运行 bundle install,这会安装所有必要的 Ruby 依赖项。最后,使用 CMD 命令启动eventmanager应用并将端口5000暴露给外部。必须暴露端口5000,因为用于部署到 Kubernetes 的默认 Helm 图表假设此端口用于运行应用。部署后,它将与端口80或443连接。
以下代码是构建阶段开始时的日志:
Running with gitlab-runner 11.8.0 (4745a6f3)
on runner-gitlab-runner-7fd79f558b-2wx96 _drEv8rS
Using Kubernetes namespace: gitlab-managed-apps
Using Kubernetes executor with image registry.gitlab.com/gitlab-org/cluster-integration/auto-build-image/master:stable ...
Waiting for pod gitlab-managed-apps/runner-drev8rs-project-3-concurrent-0fvjtb to be running, status is Pending
...
它运行 Kubernetes 执行器并等待 Pod 可用。
当发生这种情况时,构建脚本会运行。首先,尝试登录到此项目的 GitLab 注册表(我们随后需要将构建推送到那里):
$ /build/build.sh
Logging to GitLab Container Registry with CI credentials...
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Login Succeeded
...
然后,Docker 镜像的构建将开始:
Building Dockerfile-based application...
Sending build context to Docker daemon 113.7kB
Step 1/6 : FROM ruby:2-alpine
2-alpine: Pulling from library/ruby
...
构建好的容器镜像被推送到 eventmanager 项目的 Docker 注册表:
Successfully built 5bd173d74f67
Successfully tagged
...
Job succeeded
当 Docker 容器镜像存储在注册表中时,后续阶段将使用该镜像并将其拉取。这就结束了构建步骤。下一步是运行代码质量扫描。
代码质量扫描
在管道的这一阶段,使用 GitLab runner 扫描您的代码质量。您可以在第十章,创建您的产品、验证它并打包它中找到更多信息和示例。
容器扫描
下一阶段仍然是测试阶段的一部分。在这一阶段,将从您的镜像中实例化一个容器实例,并使用 clair (github.com/coreos/clair) 扫描漏洞。
它将登录到 eventmanager 的 Docker 注册表:
Running with gitlab-runner 11.8.0 (4745a6f3)
...
$ container_scanning
Logging to GitLab Container Registry with CI credentials...
...
然后,它将尝试获取一个带有扫描工具的容器:
Unable to find image 'arminc/clair-db:latest' locally
latest: Pulling from arminc/clair-db
...
当容器运行时,它将启动扫描:
2019/04/28 14:08:08 [0;32m[INFO] >Start clair-scanner
2019/04/28 14:08:37 [0;32m[INFO] > Server listening on port 9279
2019/04/28 14:08:37 [0;32m[INFO] >Analyzing 9cab74319993fe94abc345fa8933c789f4482b9644f9cb1d9758d31575ed1367
-----------------------------------------------------------+
| STATUS | CVE SEVERITY | PACKAGE NAME | PACKAGE VERSION | CVE DESCRIPTION |
| Unapproved | High CVE-2018-6551 | glibc | 2.24-11+deb9u4 | The malloc implementation in the GNU C Library (aka |
...
默认的 Ruby 镜像使用的是 Debian 镜像,显然有很多已知的漏洞。
如果您将 Dockerfile 使用的基础镜像切换到 ruby:2-alpine,您将获得一个更基础的 Linux 容器,应该不会出现所有这些错误。您将需要重新启动管道(在列表中点击重试):
Waiting for clair daemon to start
...
contains NO unapproved vulnerabilities
当没有发现漏洞时,报告将作为构件上传,作业将成功:
Uploading artifacts...
gl-container-scanning-report.json: found 1 matching files
..
Job succeeded
创建的 Docker 镜像现在将被扫描已知漏洞。当您想要将任何发现的内容列入白名单时,可以将它们添加到一个名为 clair-whitelist.yml 的文件中,并将其添加到您的代码库中。
依赖扫描
这一阶段的管道扫描您的代码,以查找软件依赖项中的已知安全漏洞。例如,如果您依赖的第三方库有已知的安全问题,这个问题会被发现。下一章节将详细解释这一点。
当扫描完成时,您将知道您在代码中使用的依赖项是否安全。了解依赖项使用的许可证也很有用,因为这可能会带来许多后果。我们将在下一节中解释这一点。
许可证管理
组织常常忽视的一个问题是如何管理您的知识产权(IP)。有不同的开源许可证,例如被分类为宽松型的许可证,如 X11、Apache 和 BSD 许可证。您还可以找到强制分享型的许可证,如 GPL,它们更具限制性,可能要求您分享衍生作品。通过使用许可证扫描器,您可以确保不使用对您的知识产权有负面影响的依赖项。
当作业运行时,您将看到以下输出:
Running with gitlab-runner 11.8.0 (4745a6f3)
on runner-gitlab-runner-7fd79f558b-2wx96 _drEv8rS
Using Kubernetes namespace: gitlab-managed-apps
Using Kubernetes executor with image registry.gitlab.com/gitlab-org/security-products/license-management:$CI_SERVER_VERSION_MAJOR-$CI_SERVER_VERSION_MINOR-stable ...
该扫描从一个容器中运行,但该容器运行代码并解析您的项目:
$ license_management
mesg: ttyname failed: Inappropriate ioctl for device
Added development to the ignored groups
Added test to the ignored groups
Fetching gem metadata from https://rubygems.org/.........
这将分析并上传报告构件:
Running license_finder in /it/eventmanager
LicenseFinder::Bundler: is active
Uploading artifacts...
gl-license-management-report.json: found 1 matching files
Uploading artifacts to coordinator... ok id=478 responseStatus=201 Created token=HjYg-s1y
Job succeeded
结果也可以从合并请求小部件中查看。
如你所见,在流水线中加入许可证检查非常有用。最好尽早知道你是否使用了影响软件分发方式的依赖项。在流水线的同一个并行步骤中,还会进行静态安全测试。
静态应用安全测试(SAST)
这一部分的流水线扫描你的代码,以查找已知的安全问题,这被称为静态应用安全测试。这个内容将在下一章中更详细地解释。测试阶段的最终并行步骤是运行由你的代码定义的实际测试。
最终测试步骤
在这个流水线中,启动了一个特定的容器,用于克隆源代码:
Running with gitlab-runner 11.8.0 (4745a6f3)
on runner-gitlab-runner-7fd79f558b-2wx96 _drEv8rS
Using Kubernetes namespace: gitlab-managed-apps
Using Kubernetes executor with image gliderlabs/herokuish:latest ...
它将尝试检测使用的语言。在我们的案例中,它找到了 Ruby 应用程序,这是正确的:
$ setup_test_db
$ cp -R . /tmp/app
$ /bin/herokuish buildpack test
-----> Ruby app detected
-----> Setting up Test for Ruby/Rack
-----> Using Ruby version: ruby-2.5.3
-----> Installing dependencies using bundler 1.15.2
安装必要的依赖项后,它将运行 rake 测试任务:
-----> Running test: bundle exec rspec
...
Finished in 0.03197 seconds (files took 0.22688 seconds to load)
3 examples, 0 failures
在我们的案例中,未检测到错误。你可以检查 tests 文件夹中运行的测试。
当然,运行的测试是你自己编写的,所以你决定它们的价值。测试完成后,下一阶段是部署到生产环境,相关内容将在下一节中介绍。
生产环境
默认的 Auto DevOps 流水线将在完成测试阶段后将代码部署到生产环境。你可以设置多个环境变量,以控制副本 pods 的自动扩展。此阶段的繁重工作由 auto-deploy-app Helm 图表完成。你还可以通过将自定义图表添加到项目中的 .chart 目录或设置 AUTO_DEVOPS_CHART 和 AUTO_DEVOPS_CHART_REPOSITORY 环境变量,结合图表 URL 来提供你自己的图表。它将创建以下内容:
-
一个部署令牌
-
一个针对你应用程序配置的 Prometheus 监控实例
让我们通过日志文件运行以下代码:
Running with gitlab-runner 11.8.0 (4745a6f3)
on runner-gitlab-runner-7fd79f558b-2wx96 _drEv8rS
Using Kubernetes namespace: gitlab-managed-apps
Using Kubernetes executor with image alpine:latest ...
这会检查前一个作业的工件,并对 Kubernetes 域进行检查。它将安装最小化 Helm 执行所需的依赖项:
Checking out 08222854 as master...
Skipping Git submodules setup
Downloading artifacts for code_quality (477)...
Downloading artifacts from coordinator... ok id=477 responseStatus=200 OK token=zxQGxCFW
Downloading artifacts for license_management (478)...
Downloading artifacts from coordinator... ok id=478 responseStatus=200 OK token=HjYg-s1y
Downloading artifacts for container_scanning (481)...
Downloading artifacts from coordinator... ok id=481 responseStatus=200 OK token=hErz9aWj
$ # Auto DevOps variables and functions # collapsed multi-line command
$ check_kube_domain
$ install_dependencies
下一步是下载所需的图表(auto-deploy-app 图表或自定义图表):
$ download_chart
接下来,我们需要确保已定义命名空间(通常是你使用的 Kubernetes 集群名称):
$ ensure_namespace
现在,是时候初始化 Tiller(Helm 服务器)了:
initialize_tiller
在这里,创建了一个用于访问注册表的密钥:
create_secret
最后,部署可以开始:
$ deploy secret "production-secret"
deleted secret/production-secret replaced
Deploying new release...
Release "production" has been upgraded.
Happy Helming! ...
部署后,你将看到关于应用程序运行 URL 的反馈。该名称是通过将命名空间附加到项目名称和集群运行的域通配符来创建的:
NOTES:
Application should be accessible at: http://it-eventmanager.kubernetes.joustie.nl
Waiting for deployment "production" rollout to finish: 0 of 1 updated replicas are available...
deployment "production" successfully rolled out
$ delete canary
$ delete rollout
$ persist_environment_url
Uploading artifacts...
environment_url.txt: found 1 matching files
Uploading artifacts to coordinator... ok id=482 responseStatus=201 Created token=koT8yujj
Job succeeded
如果你已配置 kubectl 使用你的 GKE 集群的上下文,在命令行中,你可以验证是否已进行部署:
Joosts-iMac-Pro:Part3 joostevertse$ kubectl get pods --all-namespaces
Pods 列表应该显示已启动的 pods:
NAME READY STATUS RESTARTS AGE
eventmanager production-6b9db68f6f-hrwzv 1/1 Running 0 11h
eventmanager production-postgres-5b5cf56747-xngbk 1/1 Running 0 11h
默认情况下,也会启动一个 postgres 实例,如果需要,你可以对安装进行微调以使用它。你可以在这里找到更多信息:docs.gitlab.com/ee/topics/autodevops/#postgresql-database-support。列表中还有其他 Pod,它们都是部署的一部分:
certmanager-cert-manager-6c8cd9f9bf-8kbf8 1/1 Running 0 11h
ingress-nginx-ingress-controller-ff666c548-n2s84 1/1 Running 0 11h
ingress-nginx-ingress-default-backend-677b99f864-bnk8c 1/1 Running 0 11h
runner-gitlab-runner-7fd79f558b-2wx96 1/1 Running 0 11h
tiller-deploy-6586b57bcb-t6zql 1/1 Running 0 11h
eventmanager 应用程序可以通过访问 http://it-eventmanager.kubernetes.joustie.nl 来查看:

现在,我们有一个正在运行的应用程序,正在进行测试和监控。接下来的最后一步是对生产环境进行性能检查。我们可以再次使用 Kubernetes 集群为其创建一个测试容器,并对其进行性能测试,这也是下一节的主题。
性能
在性能作业的日志文件中,你可以看到 Kubernetes 再次被用来启动一个实例以供使用:
Running with gitlab-runner 11.8.0 (4745a6f3)
on runner-gitlab-runner-7fd79f558b-2wx96 _drEv8rS
Using Kubernetes namespace: gitlab-managed-apps
该作业连接到GitLab.com,并验证它应该拉取的镜像版本。它使用 sitespeed.io 容器来完成此任务(hub.docker.com/r/sitespeedio/sitespeed.io/):
$ performance
Connecting to gitlab.com (35.231.145.151:443)
index.js 100% |********************************| 1614 0:00:00 ETA
Unable to find image 'sitespeedio/sitespeed.io:6.3.1' locally
6.3.1: Pulling from sitespeedio/sitespeed.io
...
容器内部,它尝试使用 Chrome 和 Firefox 测量浏览器性能:
Google Chrome 63.0.3239.132
Mozilla Firefox 54.0.1
[2019-04-25 21:42:41] INFO: Versions OS: linux 4.14.91+ nodejs: v8.9.4 sitespeed.io: 6.3.1 browsertime: 2.1.4 coach: 1.2.0
它将进行若干次后续运行:
[2019-04-25 21:42:41] INFO: Starting chrome for analysing http://it-eventmanager.kubernetes.joustie.nl 3 time(s)
[2019-04-25 21:42:41] INFO: Testing url http://it-eventmanager.kubernetes.joustie.nl run 1
[2019-04-25 21:42:51] INFO: Testing url http://it-eventmanager.kubernetes.joustie.nl run 2
[2019-04-25 21:43:00] INFO: Testing url http://it-eventmanager.kubernetes.joustie.nl run 3
[2019-04-25 21:43:09] INFO: 2 requests, 586 bytes, backEndTime: 39ms (±1.87ms), firstPaint: 119ms (±2.43ms), firstVisualChange: 0ms (±0.00ms), DOMContentLoaded: 103ms (±3.06ms), Load: 104ms (±3.06ms), speedIndex: 0 (±0.00), visualComplete85: 0ms (±0.00ms), lastVisualChange: 0ms (±0.00ms), rumSpeedIndex: 119 (±2.25) (3 runs)
[2019-04-25 21:43:13] INFO: HTML stored in /sitespeed.io/sitespeed-results
[2019-04-25 21:43:13] INFO: Finished analyzing http://it-eventmanager.kubernetes.joustie.nl
...
结果将保存为 HTML 报告工件以及 JSON 文件:
Uploading artifacts...
performance.json: found 1 matching files
sitespeed-results/: found 64 matching files
Uploading artifacts to coordinator... ok id=483 responseStatus=201 Created token=w-R8qzFw
Job succeeded
以下代码是 JSON 文件的一部分:
[
{
"subject": "/",
"metrics": [
{
"name": "Transfer Size (KB)",
"value": "0.6",
"desiredSize": "smaller"
...
也提供了一个漂亮的 HTML 报告,这是工件的一部分。
Auto DevOps 是一个非常实用的概念。如果你的应用程序遵循标准且不太复杂,它可以为你提供完整的管道。如果你需要更多的定制化,你可以使用模板并根据需求进行微调。
总结
本章展示了 GitLab 的潜力及其在软件产品运营阶段中扮演的角色。你可以开发解决方案,进行测试,并最终在一个环境中运行它。在这个过程中,你可以尽可能地实现自动化。如果使用 Auto DevOps 功能,你将实现 DevOps 概念的全部潜力,这是目前非常抢手的技能。
在下一章中,我们将查看监控和安全阶段,这些是 DevOps 管道中的最后阶段。
问题
-
你在哪个文件中定义部署?
-
dpl工具是什么? -
GitLab 对 Auto DevOps 的愿景是什么?
-
在构建阶段结束时(使用 Dockerfile),构建工件存储在哪里?
-
Auto DevOps 中使用的容器扫描器名称是什么?
-
Auto DevOps 中使用的部署 Helm 图表名称是什么?
-
生产部署时会部署多少个 Pod?
-
性能容器的名称是什么?
进一步阅读
-
高级基础设施渗透测试,作者:Chiheb Chebbi:
www.packtpub.com/networking-and-servers/advanced-infrastructure-penetration-testing -
学习 Docker - Docker 18.x 基础,作者:Gabriel N. Schenker:
www.packtpub.com/in/networking-and-servers/learn-docker-fundamentals-docker-18x -
AWS 自动化手册,作者:Nikit Swaraj:
www.packtpub.com/virtualization-and-cloud/aws-automation-cookbook -
在 Azure 上动手实践 Kubernetes,作者:Gunther Lenz 和 Shivakumar Gopalakrishnan:
www.packtpub.com/virtualization-and-cloud/hands-kubernetes-azure
第十二章:使用 Prometheus 进行监控
在本章中,我们将探讨如何使用 Prometheus 时间序列进行监控,同时还将运行一些自动化的安全测试。GitLab 中内置的安全测试仅在使用 GitLab Ultimate 许可证的本地部署或使用 gitlab.com 上的 Gold 订阅时可用。
在本章中,我们将覆盖以下主题:
-
配置 Prometheus
-
自定义监控
-
静态安全漏洞分析
-
动态应用安全测试(DAST)
-
依赖性检查
技术要求
为了能够管理 Omnibus 安装,需要一个中央配置文件,名为 gitlab.rb。你需要创建这个文件或复制一个示例。可以在 gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template 找到模板。此文件在升级后不会更新。在本章中,我们将引用并讨论此文件的元素。
为了跟随本章的指示,请下载包含示例的 Git 仓库,地址位于 GitHub:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter12.
你还需要安装 Python 来创建 Python 示例导出器。
配置 Prometheus
Prometheus 是一个开源监控系统,灵感来源于 Google 的生产监控系统 Borgmon。自 2012 年推出以来,该项目已经建立了一个活跃的社区,并被许多公司使用。它是一个 TSDB(时间序列数据库)的例子。这意味着时间在所有度量中都是一个刻意设定的 X 轴。每一个新的条目都是插入,而不是数据行的更新。
这一点最好通过表格来呈现:
| 时间(x) | 关键 | 值 |
|---|---|---|
| 12.01.33 | ping-latency | 0.234556 |
| 12.03.33 | ping-latency | 0.223344 |
这种方法的两个主要优势如下:
-
可扩展性:这些数据库经过调优,能够非常高效地摄取数据。
-
可用性:它们提供各种工具和功能,例如数据保留和持续查询。
而大多数监控系统侧重于通过健康检查来衡量外部系统行为,Prometheus 强调通过从软件本身请求度量来衡量内部系统行为。使用 Prometheus,你可以动态地设置监控,这使其成为了解分布式环境(例如 Kubernetes 集群上运行的软件)的一种有价值的工具。
Prometheus 项目提供了客户端库,便于从软件中导出指标。这些库使您能够将监控功能集成到您的软件中。可以说,它为您的应用程序内部的指标打开了一个端口。
您还可以使用客户端库中的函数和对象,暴露应用程序内部的指标。
另一种方法是运行一些独立的小型程序,收集 Prometheus 服务器将抓取的数据。GitHub 上的 Prometheus 组织提供了一些官方推荐的导出器:github.com/prometheus。
GitLab 同时集成了这两种方法。启用后,它可以暴露 GitLab 本身的指标,但全包(Omnibus)包也可以提供额外的导出器实例,能够监控多个组件。监控架构如下所示(如您所见,功能运行在 GitLab 应用程序服务器上):

您可以在图像中看到几个导出器;让我们逐个查看它们:
- (GitLab)指标导出器:GitLab 已将客户端功能集成到自身,并且可以在启用设置的情况下暴露指标。您可以在管理员 | 设置 | 指标与分析 | 指标 Prometheus 中找到此功能,如下图所示:

通过允许您在 http://{your gitlab url}}/-/metrics?token=something 查看导出器的输出,这个方法得以实现。健康检查页面中提供了一个示例,通过 监控 | 健康检查,如下图所示:

确保在请求中附加 token,否则您将看不到任何数据。访问该 URL 时,示例输出如下所示:

- Redis 导出器:这是一个外部程序,您可以在
github.com/oliver006/redis_exporter/blob/master/README.md#whats-exported找到它。它是一个 Go 二进制文件,暴露关于 Redis 的指标,Redis 是 GitLab 用于存储后台作业队列、会话状态和 UI 缓存的内存数据库和缓存。
与 Grafana 配合使用时,可以快速设置仪表盘,如下图所示:

-
Postgres 导出器:这是一个 Go 二进制文件和外部项目,网址为:
github.com/wrouesnel/postgres_exporter。 -
GitLab 监控器:这个导出器有些不同,它是用 Ruby 编写的,并通过 Sinatra Web 服务器 gem 来暴露指标。项目页面为:
gitlab.com/gitlab-org/gitlab-monitor。
当你查询 GitLab 监控时,可以提供不同的参数,这些参数表示你想抓取的指标种类;它们包括以下内容:
-
-
-
数据库:提供关于表格、行和 CI 构建的信息
-
Git:提供关于 Git 拉取的信息
-
进程:提供关于 CPU、进程数量、sidekiq 统计信息等的信息
-
-
-
Node Exporter:这可能是 Prometheus 中最知名的导出器之一。它包含了应用节点的许多基本指标。同样,这个导出器是用 Go 编写的,并可以在
github.com/prometheus/node_exporter找到。 -
要在 GitLab 应用服务器上启用内置的 Prometheus 服务器,编辑
/etc/gitlab/gitlab.rb文件。搜索prometheus['enable'],取消注释并设置为true。还有一些其他选项,但仅启用它就能让你得到一个工作的实例。
你应该修改的gitlab.rb文件部分如下所示:
################################################################################
## Prometheus
##! Docs: https://docs.gitlab.com/ce/administration/monitoring/prometheus/
###############################################################################
prometheus['enable'] = true
# prometheus['monitor_kubernetes'] = true
# prometheus['username'] = 'gitlab-prometheus'
# prometheus['uid'] = nil
# prometheus['gid'] = nil
修改gitlab.rb后,你应该运行重新配置以激活这些更改。你将看到消息,说明某些导出器和 Prometheus 服务器已经启动。
你可以通过访问prometheus['listen_address']中配置的地址来进入 Prometheus 控制台。你将看到一个查询界面。如果点击下拉列表,你应该能看到一系列可以查询的指标,如下图所示:

一旦你选择了一个指标,它将显示它在数据库中记录的所有值(包括从 Prometheus 导出器主动抓取的数据)。在这种情况下,选择的视图模式可能是控制台。你还可以通过点击“图表”查看数据的图形表示,如下图所示:

使用外部 Prometheus 主机
如果你没有使用 Omnibus 包来管理 GitLab,或者坚持使用外部 Prometheus 服务器,那么情况会有所不同。你需要注意的是,Prometheus 使用的默认安全模型相当简单;它假设任何人都可以查看存储的时间序列数据,并且服务器不提供身份验证、授权或加密功能。如果你需要这些功能,应该在 Prometheus 服务器前准备一个反向代理来帮助。更多关于这方面的信息,可以在prometheus.io/docs/operating/security/找到。
这种情况的监控架构如下图所示(如你所见,某些功能运行在单独的服务器上):

现在我们必须确保我们配置了一个准备好从中抓取数据的外部 Prometheus 主机。正如我们之前提到的,Prometheus 是一个单独的 Go 二进制文件。要指定要加载的配置文件,请使用--config.file标志。此配置文件必须具有 YAML 格式。下面显示了一个单个 Prometheus 服务器如何监视内嵌在 GitLab 中的 GitLab 指标导出程序的prometheus.yml示例:
- job_name: 'git-metrics'
params:
token: [ gitlab_health_check_access_token ]
metrics_path: /-/metrics
scrape_interval: 5s
scheme: https
tls_config:
insecure_skip_verify: true
file_sd_configs:
- files:
- /etc/prometheus/sd/gitlab_metrics_exporter_sd.yml
gitlab_metrics_exporter_sd.yml文件包含以下代码:
- targets: ['gitlab.joustie.nl']
labels:
app: gitlab
如果您将这两个文件放在/tmp或其他位置,并作为 Docker 容器运行 Prometheus(如下例所示),则应该已准备好进行外部 Prometheus 操作。当然,如果愿意,您也可以从源代码安装并在其他地方的专用服务器上运行它。
docker run -it --name my-prometheus \
-v /tmp:/etc/prometheus \
--publish 9090:9090 \
prom/prometheus
您现在知道 Prometheus 如何在 GitLab 应用程序服务器本身以及在单独的服务器上运行。
启用外部仪表板链接
自 GitLab 12.0 以来,还可以在 GitLab 内部启用链接到外部仪表板的选项。
-
转到设置 | 操作并导航到外部仪表板
-
插入位置到您的外部仪表板并点击保存更改:

自定义监控
有几种方法可以创建自定义监控脚本,这些脚本将向您的 Prometheus 服务器提供时间序列数据。正如前面在设置 Prometheus部分中提到的那样,有许多客户端库可用,例如github.com/prometheus/client_python。
在以下截图中,您可以看到前述项目不是很大,但在 GitHub 上有星标。

要使用此库,请使用 pip(用于模块的 Python 包管理器)使用以下代码进行安装:
$ pip install prometheus_client
Collecting prometheus_client
matplotlib 1.3.1 requires nose, which is not installed.
matplotlib 1.3.1 requires tornado, which is not installed.
Installing collected packages: prometheus-client
Successfully installed prometheus-client-0.6.0
您还可以通过从 Python 解释器或通过文件运行以下代码创建一个简单的导出程序:
from prometheus_client import start_http_server, Summary
import random
import time
# Create a metric to track time spent and requests made.
REQUEST_TIME = Summary('request_processing_seconds', 'Time spent processing request')
# Decorate function with metric.
@REQUEST_TIME.time()
def process_request(t):
"""A dummy function that takes some time."""
time.sleep(t)
if __name__ == '__main__':
# Start up the server to expose the metrics.
start_http_server(8000)
# Generate some requests.
while True:
process_request(random.random())
导出程序将在localhost的 8000 端口上启动,并在调用时显示以下页面:

您可以通过将以下代码添加到prometheus.yml并使用gitlab-ctl restart prometheus在 Omnibus 安装的 GitLab 应用程序服务器上重新启动 Prometheus,或者您可以在外部安装的 Prometheus 上使用service prometheus restart将此导出程序添加到您的 Prometheus 服务器中:
job_name: 'python_gitlab'
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
static_configs:-
targets: ['localhost:8000']
您现在可以选择修改自己的 Python 应用程序以报告指标,或者您可以创建 Python 代码来从系统中收集指标。例如,您可能希望解析某个日志文件以查找特定模式并累积相关指标。
安全漏洞的静态分析
静态应用安全测试(SAST)用于分析源代码或二进制文件,检测安全漏洞或弱点。当自动化进行时,它有助于使你的 DevOps 方法学更像 DevSecOps,其中安全测试和意识成为 DevOps 生命周期的一部分。
GitLab 在其 Ultimate 许可证模式下,提供自动化测试,作为应用程序开发的一部分。
目前,以下语言和框架是受支持的:
| 语言/框架 | 扫描工具 |
|---|---|
| .NET | Security Code Scan |
| C/C++ | Flawfinder |
| Go | gosec |
| Groovy(Gradle 和 Grail) | find-sec-bugs |
| Java(Maven 和 Gradle) | find-sec-bugs |
| JavaScript | ESLint 安全插件 |
| Node.js | NodeJsScan |
| PHP | phpcs-security-audit |
| Python | bandit |
| Ruby on Rails | brakeman |
| Scala(sbt) | find-sec-bugs |
| Typescript | TSLint 配置安全 |
首先,你需要一个带有 Docker-in-Docker 执行器的 GitLab Runner。
这是一个普通的 Docker 执行器,但它以特权模式运行。这意味着它可以运行自己的 Docker 守护进程,从而能够自行运行容器。
你可以通过更改 GitLab Runner 配置文件(config.toml),确保它有 privileged = true,来启用此功能。更改后,重新启动 Runner,如下所示:
[[runners]]
executor = "docker"
[runners.docker]
privileged = true
其次,你需要在 GitLab 项目文件夹中添加一个特定的 .gitlab-ci.yml 文件,以实现实际的耦合,如下片段所示:
sast:
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run
--env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}"
--volume "$PWD:/code"
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
artifacts:
reports:
sast: gl-sast-report.json
作为示例,我们从 github.com/CSPF-Founder/JavaVulnerableLab 下载了以下代码到我们的项目中。我们添加了 .gitlab-ci.yml 文件来运行扫描。当代码被推送时,工作流开始并为扫描做准备:
[0KRunning with gitlab-runner 11.7.0 (8bb608ff)
[0;m[0K on Joosts-MBP.fritz.box gGEycKK-
[0;m[0KUsing Docker executor with image docker:stable ...
[0;m[0KStarting service docker:stable-dind ...
[0;m[0KPulling docker image docker:stable-dind ...
[0;m[0KUsing docker image sha256:5b626cc3459ad077146e8aac1fbe25f7099d71c6765efd6552b9209ca7ea4dc1 for docker:stable-dind ...
[0;m[0KWaiting for services to be up and running...
[0;m[0KPulling docker image docker:stable ...
[0;m[0KUsing docker image sha256:73d492654a095a2f91078b2dfacd0cfe1a1fe25412fac54b4eb2f5a9609ad418 for docker:stable ...
[0;msection_start:1550847640:prepare_script
[0KRunning on runner-gGEycKK--project-1-concurrent-0 via Joosts-MBP.fritz.box...
section_end:1550847642:prepare_script
[0Ksection_start:1550847642:get_sources
在下一阶段,将克隆包含待扫描代码的仓库,如下所示:
[0K[32;1mCloning repository...[0;m
Cloning into '/builds/mastering_gitlab/JavaVulnerableLab'...
[32;1mChecking out 157b6e94 as master...[0;m
[32;1mSkipping Git submodules setup[0;m
section_end:1550847644:get_sources
[0Ksection_start:1550847644:restore_cache
[0Ksection_end:1550847646:restore_cache
[0Ksection_start:1550847646:download_artifacts
[0Ksection_end:1550847647:download_artifacts
[0Ksection_start:1550847647:build_script
[0K[32;1m$ export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')[0;m
[32;1m$ docker run --env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}" --volume "$PWD:/code" --volume /var/run/docker.sock:/var/run/docker.sock "registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code[0;m
在下一步中,执行将尝试获取特定的 Docker 镜像进行扫描。如果在本地找不到,它将尝试从 gitlab.org 获取,如下所示:
Unable to find image 'registry.gitlab.com/gitlab-org/security-products/sast:11-7-stable' locally
11-7-stable: Pulling from gitlab-org/security-products/sast
3f0edbe59eaa: Pulling fs layer
3f0edbe59eaa: Download complete
3f0edbe59eaa: Pull complete
Digest: sha256:d31cbb2bfd200b60543ef99fa03638c2335a52597e0966b7347f896dbe4e78e7
Status: Downloaded newer image for registry.gitlab.com/gitlab-org/security-products/sast:11-7-stable
成功下载镜像后,它将开始扫描,如下所示:
2019/02/22 15:00:52 Copy project directory to containers
2019/02/22 15:00:52 [bandit] Detect project using plugin
2019/02/22 15:00:52 [bandit] Project not compatible
2019/02/22 15:00:52 [brakeman] Detect project using plugin
2019/02/22 15:00:52 [brakeman] Project not compatible
2019/02/22 15:00:52 [gosec] Detect project using plugin
2019/02/22 15:00:52 [gosec] Project not compatible
2019/02/22 15:00:52 [find-sec-bugs] Detect project using plugin
2019/02/22 15:00:52 [find-sec-bugs] Project is compatible
2019/02/22 15:00:52 [find-sec-bugs] Starting analyzer...
10 分钟后,结果应如下所示:
Downloaded from central: https://repo.maven.apache.org/maven2/com/google/collections/google-collections/1.0/google-collections-1.0.jar (640 kB at 882 kB/s)
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 15 source files to /tmp/app/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.988 s
[INFO] Finished at: 2019-02-22T15:24:25Z
[INFO] ------------------------------------------------------------------------
扫描将报告哪些插件或模块可以使用(换句话说,它检查项目兼容性),如下所示:
Warnings generated: 49
2019/02/22 15:24:33 [find-sec-bugs-gradle] Detect project using plugin
2019/02/22 15:24:33 [find-sec-bugs-gradle] Project not compatible
2019/02/22 15:24:33 [find-sec-bugs-sbt] Detect project using plugin
2019/02/22 15:24:33 [find-sec-bugs-sbt] Project not compatible
2019/02/22 15:24:33 [find-sec-bugs-groovy] Detect project using plugin
2019/02/22 15:24:33 [find-sec-bugs-groovy] Project not compatible
2019/02/22 15:24:33 [flawfinder] Detect project using plugin
2019/02/22 15:24:33 [flawfinder] Project not compatible
2019/02/22 15:24:33 [phpcs-security-audit] Detect project using plugin
2019/02/22 15:24:33 [phpcs-security-audit] Project not compatible
2019/02/22 15:24:33 [security-code-scan] Detect project using plugin
2019/02/22 15:24:33 [security-code-scan] Project not compatible
2019/02/22 15:24:33 [nodejs-scan] Detect project using plugin
2019/02/22 15:24:33 [nodejs-scan] Project not compatible
现在你应该能看到报告中的发现,如下例所示(这并不是完全的):
+--------------------------------------------------------------------------------------+
| Severity | Tool | Location |
+--------------------------------------------------------------------------------------+
| High | Find Security Bugs | src/main/java/org/cysecurity/cspf/jvl/controller/LoginValidator.java:64 |
| |
| HTTP cookie formed from untrusted input |
+--------------------------------------------------------------------------------------+
| High | Find Security Bugs | src/main/java/org/cysecurity/cspf/jvl/controller/AddPage.java:45 |
| |
| Relative path traversal in servlet |
+--------------------------------------------------------------------------------------+
如下片段所示,你可以看到发现了很多安全问题:
Uploading artifacts...
gl-sast-report.json: found 1 matching files
Uploading artifacts to coordinator... ok id=4 responseStatus=201 Created token=Sy_pRf1e
Job succeeded
扫描最终通过上传报告完成。
本质上,SAST 试图分析你的代码,并根据哪些代码可以被扫描来应用插件。它会查找代码中的安全隐患。扫描在 GitLab 提供的一个特殊容器中进行。扫描完成后,会生成报告。
动态应用安全测试
动态应用安全测试(DAST)运行像你的应用程序扫描的 PEN 测试。
该测试使用 OWASP ZAProxy (github.com/zaproxy/zaproxy) 扫描您的 web 应用程序中运行的实例。它执行的是被动扫描,这意味着它仅通过探索链接来发现您的应用程序,无法发现动态创建的链接,也不会主动攻击您的应用程序。
在 GitLab 12.0 之前,这个扫描也使用了 Docker-in-Docker 机制,但现在它只是检索并运行一个容器和测试。这意味着镜像被缓存到 GitLab 运行器上,第一次检索镜像后,安全测试将更快地运行。
与 SAST 一样,您通过 .gitlab-ci.yml 文件来控制扫描方式,具体如下:
dast:
image: registry.gitlab.com/gitlab-org/security-products/zaproxy
variables:
website: "https://blog.joustie.nl"
allow_failure: true
script:
- mkdir /zap/wrk/
- /zap/zap-baseline.py -J gl-dast-report.json -t $website || true
- cp /zap/wrk/gl-dast-report.json .
artifacts:
reports:
dast: gl-dast-report.json
当您将代码推送到代码库时,DAST 扫描将开始准备,如下所示。首先,它会尝试查找 ZAProxy Docker 容器并拉取它。
Running with gitlab-runner 11.7.0 (8bb608ff)
on host gGEycKK-
Using Docker executor with image registry.gitlab.com/gitlab-org/security-products/zaproxy ...
Pulling docker image registry.gitlab.com/gitlab-org/security-products/zaproxy ...
Using docker image sha256:cd12d3ce5fc66ef0c6b2cf0e6b745876b666aed7f9e859451eaef884b92cefa7 for registry.gitlab.com/gitlab-org/security-products/zaproxy ...
扫描将按如下方式开始:
Running on runner-gGEycKK--project-2-concurrent-0 via Joosts-MBP.fritz.box...
Fetching changes...
Removing zap.out
HEAD is now at 6024894 Update .gitlab-ci.yml
From http://192.168.178.82/root/unsecure
6024894..e6b26fe master -> origin/master
Checking out e6b26fe5 as master...
Skipping Git submodules setup
$ mkdir /zap/wrk/
$ /zap/zap-baseline.py -J gl-dast-report.json -t $website || true
2019-02-22 15:50:26,650 Params: ['zap-x.sh', '-daemon', '-port', '40096', '-host', '0.0.0.0', '-config', 'api.disablekey=true', '-config', 'api.addrs.addr.name=.*', '-config', 'api.addrs.addr.regex=true', '-config', 'spider.maxDuration=1', '-addonupdate', '-addoninstall', 'pscanrulesBeta']
Feb 22, 2019 3:50:34 PM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
它会尝试扫描整个网站,如下例所示:
Total of 251 URLs
PASS: Cookie No HttpOnly Flag [10010]
PASS: Cookie Without Secure Flag [10011]
PASS: Incomplete or No Cache-control and Pragma HTTP Header Set [10015]
PASS: Content-Type Header Missing [10019]
PASS: Information Disclosure - Debug Error Messages [10023]
PASS: Information Disclosure - Sensitive Information in URL [10024]
PASS: Information Disclosure - Sensitive Information in HTTP Referrer Header [10025]
PASS: HTTP Parameter Override [10026]
PASS: Information Disclosure - Suspicious Comments [10027]
PASS: Viewstate Scanner [10032]
PASS: Secure Pages Include Mixed Content [10040]
PASS: CSP Scanner [10055]
PASS: Weak Authentication Method [10105]
PASS: Session ID in URL Rewrite [3]
PASS: Script Passive Scan Rules [50001]
PASS: Insecure JSF ViewState [90001]
PASS: Charset Mismatch [90011]
PASS: WSDL File Passive Scanner [90030]
PASS: Loosely Scoped Cookie [90033]
然后它会立即报告漏洞,如下所示:
WARN-NEW: Web Browser XSS Protection Not Enabled [10016] x 112
http://blog.joustie.nl/
http://blog.joustie.nl/robots.txt
http://blog.joustie.nl/sitemap.xml
http://blog.joustie.nl
http://blog.joustie.nl/atom.xml
WARN-NEW: Cross-Domain JavaScript Source File Inclusion [10017] x 108
http://blog.joustie.nl/
http://blog.joustie.nl
http://blog.joustie.nl/tags/personal/
http://blog.joustie.nl/2019/01/12/2018-05-29-personalblog/
http://blog.joustie.nl/2018/05/29/2018-05-20-met-zn-allen-1-wereld/
.....
FAIL-NEW: 0 FAIL-INPROG: 0 WARN-NEW: 7 WARN-INPROG: 0 INFO: 0 IGNORE: 0 PASS: 19
扫描完成后,报告将被创建并作为工件上传,如下所示:
$ cp /zap/wrk/gl-dast-report.json .
Uploading artifacts...
gl-dast-report.json: found 1 matching files
Uploading artifacts to coordinator... ok id=6 responseStatus=201 Created token=LbTRyRU-
Job succeeded
从前面的示例可以看到,通过利用 GitLab Runners 和 Docker,启动动态安全扫描非常简单。
依赖项检查
第三方组件或依赖项中的已知漏洞非常常见。它们甚至可能是 OWASP Top 10 中的“使用已知漏洞的组件”之一。OWASP Web 恶意软件扫描器(见 www.owasp.org)是一个用于 web 应用程序的恶意软件扫描器。它可以使用社区构建的签名和管理的数据库来扫描 web 应用程序。它通过测试 web 应用程序的每个文件,查找已知的恶意软件签名来工作。
这些已知的易受攻击组件应在早期开发阶段识别出来。定期在生产阶段进行依赖组件的漏洞扫描也是一种良好的实践,不仅仅是在开发阶段。
同样,GitLab 工作流中的依赖项扫描是通过 .gitlab-ci.yml 文件来控制的。它也使用 Docker-in-Docker 技术,如下所示:
dependency_scanning:
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run
--env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}"
--volume "$PWD:/code"
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
artifacts:
reports:
dependency_scanning: gl-dependency-scanning-report.json
对于本次测试,我们使用了与 SAST 扫描中相同的代码(github.com/CSPF-Founder/JavaVulnerableLab)来展示我们的结果。在这里,您可以看到在推送了一些新代码后,作业正在准备中。它应该会拉取 stable-dind 镜像,如下所示:
Running with gitlab-runner 11.7.0 (8bb608ff)
on host gGEycKK-
Using Docker executor with image docker:stable ...
Starting service docker:stable-dind ...
Pulling docker image docker:stable-dind ...
Using docker image sha256:5b626cc3459ad077146e8aac1fbe25f7099d71c6765efd6552b9209ca7ea4dc1 for docker:stable-dind ...
Waiting for services to be up and running...
Pulling docker image docker:stable ...
Using docker image sha256:73d492654a095a2f91078b2dfacd0cfe1a1fe25412fac54b4eb2f5a9609ad418 for docker:stable ...
运行镜像将执行依赖项扫描,如下所示:
Running on runner-gGEycKK--project-2-concurrent-0 via Joosts-MBP.fritz.box...
Fetching changes...
HEAD is now at e6b26fe Update .gitlab-ci.yml
From http://192.168.178.82/root/unsecure
e6b26fe..3aa3162 master -> origin/master
Checking out 3aa3162f as master...
Skipping Git submodules setup
$ export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
$ docker run --env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}" --volume "$PWD:/code" --volume /var/run/docker.sock:/var/run/docker.sock "registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
...
使用的技术以前叫做 Gemnasium。它在 2018 年被 GitLab 收购,您可以在** docs.gitlab.com/ee/user/project/import/gemnasium.html**中的声明中看到。
Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus/2.0.6/plexus-2.0.6.pom (17 kB at 621 kB/s)
[INFO] Gemnasium Maven Plugin
[INFO]
[INFO] Project's dependencies have been successfully dumped into: /tmp/app/gemnasium-maven-plugin.json
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.504 s
[INFO] Finished at: 2019-02-23T11:45:49Z
[INFO] ------------------------------------------------------------------------
如下代码所示,发现了所使用的 MySQL 库中的一些漏洞:
+----------------------------------------------------------------------------------------+
| Severity | Tool | Identifier |
+----------------------------------------------------------------------------------------+
| Unknown | Gemnasium | CVE-2015-7501 |
| |
| InvokerTransformer code execution during deserialization in commons |
| collections/commons-collections |
| Solution: Upgrade to the latest version |
| In pom.xml |
+----------------------------------------------------------------------------------------+
| Unknown | Gemnasium | CVE-2017-3523 |
| |
| Vulnerability in the MySQL Connectors in mysql/mysql-connector-java |
| Solution: Upgrade to the latest version |
| In pom.xml |
+----------------------------------------------------------------------------------------+
2019/02/23 11:45:51 [gemnasium-python] Detect project using plugin
2019/02/23 11:45:51 [gemnasium-python] Project not compatible
2019/02/23 11:45:51 [retire.js] Detect project using plugin
2019/02/23 11:45:51 [retire.js] Project not compatible
Uploading artifacts...
gl-dependency-scanning-report.json: found 1 matching files
Uploading artifacts to coordinator... ok id=7 responseStatus=201 Created token=1cdLFEJP
Job succeeded
任务通过上传扫描报告来结束。
就像其他扫描一样,依赖检查也使用相同的技术,在 Docker 容器内运行特定的扫描程序。在这种情况下,使用的扫描程序是 Gemnasium,它在去年被 GitLab 收购。如果发现有问题的依赖项,它将在输出中显示并出现在结果报告中。
GitLab 12.0 的一个不错的功能是,在你进行依赖扫描后,结果中列出的依赖项会被保存到你的项目中。这是安全/合规团队非常希望拥有的功能,能够跟踪整个企业中使用的依赖项:

总结
在本章中,我们讨论了在工作流中使用监控的情况,以及如何立即集成安全监控。GitLab 提供了现成的机会来设置这些功能。在本章中,我们还了解了 Prometheus 及其自定义监控的编写方式。在下一章中,我们将讨论 GitLab 的集成选项,以便在需要连接其他工具时使用。
问题
-
Prometheus 是受哪个系统启发的?
-
Prometheus 客户端的名称是什么?
-
GitLab Metrics Exporter 通常位于哪个路径?
-
GitLab 监控导出器使用了什么语言?
-
如何在 omnibus 包中启用内置的 Prometheus 服务器?
-
SAST 是什么意思?
-
DAST 是什么意思?
-
哪个文件用于控制安全测试?
进一步阅读
-
Prometheus 网站:
prometheus.io -
OWASP 扫描:
www.owasp.org -
实用站点可靠性工程,作者:Pethuru Raj Chelliah,Shreyash Naithani,和 Shailender Singh:
www.packtpub.com/virtualization-and-cloud/practical-site-reliability-engineering -
DevOps 安全实战,作者:Tony Hsu:
www.packtpub.com/in/networking-and-servers/hands-security-devops -
工业互联网应用开发,作者:Alena Traukina,Jayant Thomas,Prashant Tyagi,和 Kishore Reddipalli:
www.packtpub.com/in/application-development/industrial-internet-application-development
第十三章:将 GitLab 与 CI/CD 工具集成
在本章中,我们将介绍一些 GitLab 可以集成的工具。大多数情况下,公司不会仅使用一个工具来完成整个 DevOps 过程。GitLab 鼓励中小型公司这样做,但现实情况是,大型企业客户使用多种不同的工具和技术。我们将把 Jira 与 GitLab 连接,因为该工具在企业市场中得到广泛应用。当然,值得一提并尝试的还有久负盛名的 Jenkins 服务器,现代组织使用 Slack/Mattermost 或其他聊天工具进行实时协作。我们将在本章结束时,举例说明如何使用基本的 Webhook。
在本章中,我们将讨论以下主题:
-
使用 Jira 与 GitLab
-
与 Jenkins 连接
-
与 Mattermost 集成
-
使用 Webhook 处理事件
技术要求
对于管理综合安装,有一个名为 gitlab.rb 的中央配置文件。您需要创建它或复制一个示例。您可以在以下链接找到模板:gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template。该模板在升级后不会自动更新。在本章的许多部分中,我将引用并讨论该文件的部分内容。
本章的代码示例可以在本书的 GitHub 仓库中找到,链接:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter13。
使用 Jira 与 GitLab
Jira 是由 Atlassian 于 2002 年创建的 IT 项目管理工具,最初是为开发人员设计的软件。
Jira 最初是一个问题跟踪工具,即列出和管理任务的工具。任务 可以是任何东西:需要解决的问题,一个简单的待办事项,一个应用程序等等。然而,您也可以进一步扩展,将产品、客户、公司等信息放入该工具中。
JIRA 还是一个 工作流引擎。这意味着您可以定义 工作流(换句话说,流程),任务必须遵循这些流程。通过这种方式,您可以对每个项目或每个任务施加不同的流程。一个简单的任务工作流示例是:OPEN | IN PROGRESS | READY。
对于不同类型的任务,例如应用程序执行某些操作,您可以设置以下工作流程:OPEN | CONFIRMED | APPROVED | READY。
通过集成,GitLab 可以与 Jira 对接。尽管 GitLab 已经提供了很多 Jira 提供的项目管理功能,但在较大的组织中,它可以帮助将这些工具集成在一起。例如,当整体项目管理在 Jira 中进行时,您可以确保通过提交信息、合并请求等方式,在 GitLab 工作流中可以访问特定的链接。
作为示例,让我们在 Jira 中创建一个项目:
- 我们在 Atlassian 的云端服务上创建了一个账户(
www.atlassian.com/enterprise/cloud)。当你在 Atlassian 云端或本地设置好你的实例后,继续创建一个新项目:

- 当你的项目创建完成后,你将看到以下页面:

- 下一步是在该项目中创建一个问题。我们以最少的信息创建,命名为
Integrate GitLab and Jira。你会看到它创建了一个 ID 为GI-1的问题。现在我们在我们的项目管理工具中有了一个问题,我们希望将它与我们的 GitLab 实例链接:

- 现在,我们将在 Jira 中创建一个特殊的令牌,用于在 GitLab 中更新问题。请访问
id.atlassian.com,点击安全,然后点击创建并管理 API 令牌:

- 点击创建 API 令牌:

- 在你为它取个好名字并点击创建后,它将出现在列表中:

- 现在,我们需要去我们的 GitLab 实例中创建一个我们想要连接到 Jira 的项目:

- 当项目创建完成后,进入该项目的设置,查找集成中的 Jira:

- 你可以选择是否允许在 Jira 中通过提交或合并请求中的引用来创建评论。你需要提供你的 Jira 实例的网页 URL,在我们的案例中是
https://joustie.atlassian.net。
你的用户名是你的电子邮件地址,你可以填写之前在 Jira 中创建的令牌。
接下来是更难的部分。当你将 Jira 问题的状态更改为另一种状态时,你需要提供一个过渡 ID。在我们的示例中,这是11、21、31。那么,这是什么?我们在哪里找到它?这是一个好问题,你需要做的就是调用 Jira 的 API。它们代表问题可以处于的状态,你需要知道这些状态才能进行更改。
在以下示例中,我们调用了https://joustie.atlassian.net/rest/api/2/issue/GI-1/transitions。
当你找到了这些 ID 并保存了更改后,Jira 将会进行测试, hopefully,你会收到以下反馈:

如果你返回到集成页面,可以看到 Jira 集成的状态是绿色的。绿色表示良好:

如果出现问题或者你想了解更多这些调用的详细信息,可以查阅integrations_json.log,它位于 rails 日志目录下。例如,成功激活 Jira 集成的调用如下:
{"severity":"INFO","time":"2019-03-02T16:48:49.466Z","correlation_id":"28a67335-4f3d-40ed-826d-8ca0d6d34f84","service_class":"JiraService","project_id":12,"project_path":"root/gitlab-integration","message":"Successfully posted","client_url":"https://joustie.atlassian.net"}
现在,你可以通过 GitLab 解决 Jira 中的问题:
Joosts-iMac:gitlab-integration joostevertse$ git commit -m "Initial commit: Resolves GI-1"
[master (root-commit) 0418211] Initial commit: Resolves GI-1
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 test.txt
在项目概览中,你会发现问题已经移到“完成”状态:

如果你查看该问题本身,你会发现它的状态是已关闭(Closed):

如你所见,可以将像 Jira 这样的项目管理工具与 GitLab 集成,并保持问题同步。在本节中,我们展示了如何将基于云的 Jira 服务与本地 GitLab 安装进行集成。接下来,让我们继续与 Jenkins 连接。
将 Jenkins 与 GitLab 连接
Jenkins(Hudson 项目的一个分支)是一个持续集成平台。该平台主要用于重复执行和监控构建任务,以及自动构建和测试应用程序。许多免费的插件使得扩展 Jenkins 功能变得非常容易。一个例子就是它与其他系统(如 Sonar、Jira 或 CloudBees)的集成,或者改变其外观和感觉。通过使用合适的插件,可以构建一个完整的持续交付(Continuous Delivery)管道。
还有一个 GitLab 插件可以将 Jenkins 集成到 GitLab 工作流中。你可以自己下载并托管 Jenkins,或者购买云中的容量。
作为一个例子,我们使用了一个本地的 Jenkins 容器,并从 hub.docker.com/_/jenkins 拉取了一个容器。让我们开始吧:
- 当你的 Jenkins 容器配置并启动后,你需要确保已经安装了 GitLab 插件:

- 点击“管理 Jenkins”|“管理插件”:

- 如果你点击“可用”标签并筛选 GitLab,你可以选择 GitLab 插件:

- 安装完成后,你会看到它显示为“成功”:

-
现在,返回到“管理 Jenkins”页面,点击“配置系统”,然后滚动到 GitLab 部分。
-
给连接起个名字,提供正确的 URL,然后点击“添加”以获取一个 GitLab API token。这个 API token 可以在 GitLab 的设置中生成:

- 这里只需要填写 API token:

- 下一步是创建 Jenkins 和 GitLab 项目(本示例中)。我们选择了一个自由风格项目,并命名为
petclinic:

- 我们在 GitLab 上做了同样的操作:

- 在你的 Jenkins 项目中,向下滚动到“源代码管理”部分,并填写你的 GitLab 源代码仓库的 URL:

- 接下来,点击“添加”以添加凭据。你需要添加一个用户名和密码来连接到你的 HTTP Git 仓库(或者是 SSH 用户/密钥,或是 API 密钥):

- 然后,关闭弹出窗口并向下滚动到“构建触发器”部分。你可以在这里启用当 GitLab 推送更改时触发构建。此示例中,我们选择了在推送事件和合并请求事件时触发:

- 点击保存以保存你的设置。
为了测试和快速开发,我推荐使用 ngrok。它是一个安全的隧道程序,可以将本地计算机连接到动态 URL 主机服务。你可以在 ngrok.com 找到这个工具,下载二进制文件并将其放在本地路径中。
我们可以利用它从互联网(我们托管 GitLab 的云容器)隧道到本地 Docker 容器中的 Jenkins。
启动 ngrok,将互联网 URL 连接到我们在 Docker 中运行的本地 Jenkins(端口 8080):
Joosts-iMac:images_chapter12 joostevertse$ ngrok http 8080
启动 ngrok 后,屏幕上将显示以下输出:

现在,在仓库中修改一些代码并将其推送到 GitLab:
Joosts-iMac:petclinic joostevertse$ git push -u origin master
Enumerating objects: 130, done.
Counting objects: 100% (130/130), done.
Delta compression using up to 16 threads
Compressing objects: 100% (119/119), done.
Writing objects: 100% (130/130), 352.91 KiB | 12.17 MiB/s, done.
Total 130 (delta 26), reused 0 (delta 0)
remote: Resolving deltas: 100% (26/26), done.
To https://gitlab.joustie.nl/root/petclinic.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
Joosts-iMac:petclinic joostevertse$
你会在 ngrok 中看到钩子被触发:

通过进入 Jenkins,你会看到它接收到事件并开始构建项目:

如你所见,将 Jenkins 连接到 GitLab 非常简单。你可以选择让 Jenkins 对 GitLab 中的不同类型事件作出响应。
集成 Mattermost
Mattermost 是最受欢迎的开源 Slack 替代品,可以托管在你自己管理的专有平台上。
当团队使用这些工具时,他们可以通过专门的频道直接进行聊天交流,从而提高生产力。在这些频道中,他们可以交换快速信息片段——甚至是文件(图片/视频,任何文件)和应用程序使用的数据文件。
你可以通过浏览器使用 Mattermost,或者在移动设备上使用平台专用的应用程序。除了你的电子邮件地址外,不会使用任何特定的个人数据。
它的一个杀手级功能是,它能够轻松连接到 GitLab、Jira Jenkins、Nagios、Zabbix、Kopano 等第三方应用和系统!事实上,这家公司已被 GitLab 收购,Mattermost 也成为了 GitLab 全套软件包的一部分。换句话说,你可以轻松启用它并与 GitLab 一起运行。从 GitLab Ultimate 10.6 开始支持 ChatOps,但在 11.9 版本中引入了 GitLab Core。
作为示例,我们将设置一个新的 Mattermost 服务器与 GitLab 实例集成,以便使用斜杠命令。
因为 Mattermost 是 GitLab 全套软件包的一部分,你可以通过编辑 gitlab.rb 文件来启用/安装它,之后使用 gitlab-ctl 重新配置你的实例。
在 gitlab.rb 中需要更改的配置项如下:
mattermost['enable'] = true
在启动 Mattermost 后,你可以进入项目设置的集成页面,搜索 Mattermost 斜杠命令服务。点击添加到 Mattermost。此操作仅在 Mattermost 3.4 版本上自动有效,因此请确保你的 Omnibus 包不是太旧。
如果你的服务器上没有安装 Mattermost,你可以从 Docker Hub 拉取一个简单的 Mattermost 镜像。mattermost/mattermost-preview 就可以了。我们开始吧:
- 当你第一次在容器中登录时(默认情况下是通过
http://localhost:8065),你需要创建一个用户:

- 创建用户后,你需要创建一个团队:

- 该团队还需要一个 URL:

- 你可以点击汉堡菜单来查看团队的选项:

- 转到系统控制台:

- 查找自定义集成(Custom Integrations),并确保斜杠命令已启用。保存设置:

- 完成后,点击汉堡菜单,然后点击切换回去.... 在团队上下文中点击汉堡菜单,你可以点击集成(Integrations)。在这里,你可以点击斜杠命令(Slash Command),你将看到一个页面,在该页面中你可以定义一个可以由 Mattermost 触发的斜杠命令:

- 我们需要填写的信息可以从 GitLab 获取:

- 登录到 GitLab,并进入你的仓库设置部分中的集成(Integrations)。然后点击 Mattermost 斜杠命令。这里是你需要在 Mattermost 中填写的信息:

- 在以下截图中,你可以看到我们复制的设置。完成后点击保存(Save)或更新(Update):

- 现在,你将获得一个在 GitLab 中使用的令牌。复制 Mattermost 令牌:

- 将其粘贴到 GitLab 中的 Mattermost 集成设置页面,并保存更改:

- 现在,转到你的 Mattermost 团队频道,按下
*/*。如果你发出/gitlab help,Mattermost 会要求你连接你的 GitLab 账户:

- 你将被重定向到 GitLab,在那里你需要授权连接:

- 现在,通过返回到 Mattermost 并再次发出
/gitlab help,你将看到命令的选项。有几种选项,这些选项可以帮助你的支持人员进行 ChatOps 操作:

- 让我们创建一个新的问题,命名为
gitlab issue new test:

- 如果你返回 GitLab,你会发现为 GitLab 项目创建了一个新的 issue:

最终的集成是使用 CI 命令功能,详见此处:docs.gitlab.com/ee/ci/chatops/。
从 GitLab 11.9 版本开始,ChatOps 已经是 GitLab Core 的一部分,因此其功能不再局限于 GitLab 企业版。
在本节中,我们展示了如何将 GitLab 仓库的 issue 与 Mattermost 聊天应用程序集成。如果你创建自己的斜杠命令,可能性是无穷无尽的。现在,让我们来看看如何使用 webhook 来处理事件。
使用 webhook 处理事件
Webhook 被用作应用程序之间的信号。你可以把它看作是来自不同上下文的回调。此调用是通过 HTTP 协议(可能带有 SSL)发起的。尝试以最有效的方式实时提供信息,通常使用 JSON 作为数据格式。
其强大之处在于,只需尽可能少的操作就能获得反馈。通常,最多的工作是实现 信号 的部分。
作为概念验证,请考虑以下情况。假设当我们将新代码推送到 GitLab 时,我们需要从 GitLab 向我们自己构建的应用程序发送信号。
为了实现这个模型,我们选择了 Python 的轻量级 Flask 微框架:
from flask import Flask, request
import json
app = Flask(__name__)
def runsomething():
print "This is triggered"
@app.route('/',methods=['POST'])
def trigger():
data = json.loads(request.data)
print "New commit by: {}".format(data['commits'][0]['author']['name'])
print "New commit by: {}".format(data['commits'][0]['author']['email'])
print "New commit by: {}".format(data['commits'][0]['message'])
runsomething()
return "OK"
if __name__ == '__main__':
app.run()
让我们一步一步地走过这段代码。在以下代码中,基本的应用程序已经初始化。导入的内容主要是 Flask 框架的基础内容,特别是 request 对象。应用程序被实例化:
from flask import Flask, request
import json
app = Flask(__name__)
以下函数可以用来执行实际的工作:
def runsomething():
print "This is triggered"
然后是带有路由装饰器的方法,它解析请求中的特定信息。这里没有检查 —— 只是从 JSON webhook 中读取信息,运行 real work 函数,并返回 OK:
@app.route('/',methods=['POST'])
def trigger():
data = json.loads(request.data)
print "New commit by: {}".format(data['commits'][0]['author']['name'])
print "New commit by: {}".format(data['commits'][0]['author']['email'])
print "New commit by: {}".format(data['commits'][0]['message'])
runsomething()
return "OK"
以下的 main 部分与第一个代码块一起,构成了 Flask 的基本实现:
if __name__ == '__main__':
app.run()
当你使用 python server.py 运行这段代码时,它将在本地主机上打开端口 5000:
Joosts-iMac:gitlab-webhook joostevertse$ python server.py
* Running on http://127.0.0.1:5000/
如果我们希望互联网中的某些东西连接到它,我们可以使用老牌的 ngrok 来链接端口:
Joosts-iMac:Downloads joostevertse$ ngrok http 5000
ngrok 现在正在运行,如下图所示:

现在我们可以在 GitLab 中定义 webhook。你可以在 GitLab 项目的设置 | 集成部分找到它。定义好钩子后,你可以运行测试以验证其操作:

当通过 GitLab 触发调用时,我们会得到以下结果。这是通过 ngrok 代理的连接:

在 GitLab 中,如果你点击编辑按钮,你将看到 webhook 调用的结果。它将包含已发送的主体:

你还将看到另一端返回的响应。你可以清楚地看到 OK 响应:

这个调用的结果是我们的自定义方法被触发,并且一些特定信息,如作者、电子邮件和消息,打印在标准输出上:
Joosts-iMac:gitlab-webhook joostevertse$ python server.py
* Running on http://127.0.0.1:5000/
New commit by: Joost
New commit by: joustie@gmail.com
New commit by: Added text
This is triggered
127.0.0.1 - - [03/Mar/2019 17:10:39] "POST / HTTP/1.1" 200 -
我们已经看到,也可以使用像 webhook 这样的通用事件机制。你可以修改自己的软件或 商业现成 (COTS) 应用程序,以接收来自 GitLab 的事件。
总结
本章讨论了将 GitLab 与其他产品集成的方法。每种集成都有自己的特殊说明,但基本思路是你需要建立信任关系并映射属性。GitLab 已经提供了许多开箱即用的集成功能,这些称为 项目服务,文档可以在这里找到:docs.gitlab.com/ee/user/project/integrations/project_services.html。本章结束了本书的第三部分,我们讨论了 GitLab 的工作流及其背后的基本原理。
在本书的下一部分,我们将讨论 GitLab 最成功的部分:GitLab CI 和 Runner。我们将首先讲解如何为 GitLab CI 设置项目。
问题
-
Jira 用于什么?
-
Jira 是哪家公司开发的?
-
在 Jira 中,操作问题需要哪些 ID?
-
Jenkins 是从哪个项目分叉出来的?
-
Jenkins 使用什么机制来扩展功能?
-
什么是 ChatOps?
-
如何从 Mattermost 渠道控制事务?
-
在 GitLab 中,在哪里可以找到 webhook 的状态?
进一步阅读
-
Jira 8 精要 - 第五版,由 Patrick Lee 编写:
www.packtpub.com/in/application-development/jira-8-essentials-fifth-edition -
Jenkins 2.x 持续集成食谱 - 第三版,由 Mitesh Soni 和 Alan Mark Berg 编写:
www.packtpub.com/in/networking-and-servers/jenkins-2x-continuous-integration-cookbook-third-edition -
Jenkins 基础,由 Joseph Muli 和 Arnold Okoth 编写:
www.packtpub.com/in/networking-and-servers/jenkins-fundamentals -
GitLab ChatOps:
docs.gitlab.com/ee/ci/chatops/
第四部分:使用 GitLab CI 和 CI Runners
阅读完本节后,你将能够描述 GitLab CI 组件,创建管道和任务。你还将能够设置用于项目的 Runner。
本节包括以下章节:
-
第十四章,为 GitLab 持续集成设置你的项目
-
第十五章,安装和配置 GitLab Runners
-
第十六章,使用 GitLab Runners 与 Docker 或 Kubernetes
-
第十七章,自动扩展 GitLab CI Runners
-
第十八章,监控 CI 指标
第十四章:设置 GitLab 持续集成项目
持续集成(CI)是 极限编程(XP)最重要的支柱之一。自从 GitLab 在 8 版本中引入持续集成功能以来,它一直是 GitLab 最受欢迎的功能之一。持续集成在独立开发者和开源项目中非常流行,当前在其他市场领域也在迅速发展。
开始使用非常简单。正如我们在 第十章中展示的那样,创建产品,验证并打包它(在 发布 和 配置 部分),Auto DevOps 默认启用,因此当向项目添加代码时,会自动设置一个部署流水线,在该流水线中多个作业正在运行。这些作业将由你需要设置的 GitLab Runner 运行。这个设置完全可以根据开发者的需求进行配置。作业的结果会被收集并显示为通过或失败,并且这些结果是流水线逻辑的一部分。根据结果,流水线中的其他自动化过程可能会被触发。此功能的基础在于 .gitlab-ci.yml 文件。如果项目中存在该文件,它将被解析并启动不同的流水线和作业。
在本章中,我们将涵盖以下主题:
-
流水线
-
作业
-
创建
.gitlab-ci.yml -
配置 Runner
技术要求
要跟随本章的指示,请下载包含示例的 Git 仓库,地址为 GitHub:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter14。
必须在你的 GitLab 实例中启用 GitLab CI(请参见 第三章,使用 Web UI 配置 GitLab)。
流水线
在软件工程中,流水线通常被理解为一系列自动触发并将输入传递给下一个元素的事件链(包括过程、组件等)。它类似于现实世界中的物理管道。
在持续集成(CI)环境中,流水线是一个由多个顺序步骤组成的集合,用于整合来自不同开发者的代码。事件链由对源代码仓库(如 GitLab)的提交或推送触发。构建系统(例如 Jenkins 或 GitLab CI)会收到新版本的通知,进行编译和源代码处理,并执行单元测试。
在我们进一步深入之前,你需要了解的是,没有单元测试或其他自动化测试,不同开发者集成代码片段的工作将变得非常困难。所以,在开始构建流水线之前,确保你的开发者已经编写了测试代码。这样,你就可以确保代码已通过检查,并且至少符合一定的质量标准。
如果管道中的单元测试成功,下一步是运行集成测试。如果它们也成功,那么构建的制品可以推送或保存到二进制仓库,或者可以直接部署到一个暂存环境,在那里代码将运行。
一些管道甚至可以部署到验收环境或半生产环境,在那里会对构建的解决方案进行用户验证。这被称为持续部署(CD)。有些人称其为生产部署 CD,但这取决于你对该概念的定义。
现代构建软件将构建管道作为其架构的一部分。像 Jenkins 这样的产品已经在其工作流中采纳了这一概念,但云解决方案,如 Azure DevOps(即云中的 TFS),也采用了这一方法。
你可以在 GitLab 中找到你的项目管道,在左侧菜单栏中的 CI/CD 下,如下图所示:

菜单中的第一个项目是指向你管道概览的链接。在这里,你可以看到管道是否通过或失败,重新尝试管道,或下载制品,如下图所示:

菜单中的第二个链接会带你进入项目的作业列表(我们将在稍后介绍),如下图所示:

第三个链接是 GitLab CI 的一个功能,称为计划(Schedules)。如以下截图所示,我们已经为 Eventmanager 项目创建了一个管道执行计划:

如果你点击计划列表中的某一项,你会看到其中有许多可配置的项目。例如,你可以指定在哪个分支上运行,如下图所示:

了解更多关于你的构建信息的一个好方法是使用 GitLab CI 的图表功能,在这里你可以看到管道的各项指标,如下图所示:

以下截图展示了一个在 GitLab 中运行的管道的高级设计:

我们现在已经演示了 GitLab CI 如何整合构建管道的通用概念,并记录了管道各个步骤的成功或失败的几个指标。在接下来的章节中,我们将查看这些独立的步骤。
作业
管道配置从作业(jobs)开始:
-
作业是管道的最基本元素,由 GitLab Runner 执行。
-
作业是根据约束创建的,约束定义了在什么条件下它们应该执行。
-
作业是顶级元素,可以有任意名称,并且至少必须包含脚本元素作为最低要求。
-
可以有无限数量的作业。
在流水线概述中,您会找到几个作业。它们有一个状态,一个 ID,属于一个阶段,并且有一个名称,如下面的截图所示:

您可以通过将它们添加到名为 .gitlab-ci.yml 的配置文件中来创建作业。我们将在以下章节更深入地讨论此文件。
包含两个作业的流水线示例如下:
job1:
script: "execute-this-script-for-job1"
job2:
script: "execute-this-script-for-job2"
上述示例是一个基本的 CI/CD 流水线,包含两个命名作业(job1和job2),这些作业执行一个脚本部分(在我们的示例中,这没有任何操作)。在脚本部分中,您可以指定命令、脚本或一系列命令。例如,要构建 JavaScript,您可以将 script 设置为 npm build 或运行名为 unit-test.sh 的 shell 单元测试。
作业不在 GitLab 应用服务器上运行,而是由 GitLab Runner 接管。Runner 在其自己的环境中执行作业;例如,Runner A 可以构建job1,Runner B 可以构建job2。请注意,这些作业彼此独立。这些作业的结果在 GitLab 服务器上汇总。
创建 .gitlab-ci.yml
GitLab CI 如何与您的个人资料交互在很大程度上由 .gitlab-ci.yml 文件控制,必须将其添加到项目的根目录。当您将代码推送到存储库时,GitLab 将测试是否存在并为该特定提交启动一个带有作业的流水线。
文件的格式是 YAML Ain't Markup Language (YAML)。 YAML 目前是配置文件的广泛使用格式,最好描述为数据序列化语言。
我们在前面的章节中已经为您提供了一个 .gitlab-ci.yml 文件的示例,其中包含两个作业。在其他章节中,我们使用 .gitlab-ci.yml 文件来描述部署。那么,此文件的可用可能性是什么?
可能有很多组合,但工作的基本方式是首先在文件中定义阶段,然后添加脚本部分。此文件的完整参考资料可以在此处找到:docs.gitlab.com/ee/ci/yaml/README.html。
当您创建 YAML 文件时,知道保存后,它将通过 linter 检查文件的语法,如下面的截图所示,这是个好消息:

此文件的可能性是无限的,因此找出适合您的方法的最佳方法是查看 GitLab 网站上的开源项目,以获得灵感,网址为:docs.gitlab.com/ee/ci/examples/README.html。
配置 Runner
我们在第一章《GitLab 架构介绍》中简要描述了 GitLab Runner 的概念。Runners 本质上是运行在独立机器上的构建环境,这些机器连接到 GitLab 应用程序的服务器并请求执行任务。Runners 帮助自动化产品开发并实现 DevOps 集成。
我们在 GitLab 端和 GitLab Runner 客户端端配置了一个 Runner。请记住,并不存在单一类型的 Runner。
有不同种类的 Runner,包括:
-
一个 Shell 执行器
-
一个 Docker 执行器
-
一个 Docker Machine 和 Docker Machine SSH(自动扩展)执行器
-
一个 Parallels 执行器
-
一个 VirtualBox 执行器
-
一个 SSH 执行器
-
一个 Kubernetes 执行器
对于 GitLab CI 界面,这并不重要。所有 Runner 看起来都一样。
GitLab Runner 客户端可以在多个平台上使用,因为它是一个 Go 二进制文件,可以在许多平台上运行。配置文件名为config.toml,采用 TOML 格式,这比 YAML 格式简单。
该格式的规范是公开可用的,可以在github.com/toml-lang/toml找到。
GitLab Runner 功能
GitLab Runner 的功能包括以下内容:
-
能够并行运行多个任务
-
使用多个令牌与多个服务器(甚至是每个项目)
-
能够限制每个令牌的并发任务数量
GitLab Runner 可以执行的任务包括以下内容:
-
在没有容器或虚拟化的本地计算机上运行
-
在 Docker 容器内运行
-
在 Docker 容器内运行并通过 SSH 执行任务
-
使用 Docker 容器在不同的云和虚拟化虚拟机上自动扩展运行
-
通过连接到远程 SSH 服务器运行,在那里它可以被执行
其他功能包括以下内容:
-
GitLab Runner 支持 Bash、Windows Batch 和 Windows PowerShell
-
Runner 二进制文件可以在 GNU/Linux、macOS 和 Windows(所有 Docker 支持的平台)上运行
-
一个 Runner 允许自定义任务运行环境
-
运行器可以在不重启的情况下自动重新加载配置
-
设置非常简单,支持 Docker、Docker SSH、Parallels 或 SSH 运行环境
-
Runner 还支持缓存 Docker 容器
-
Runner 包支持在 GNU/Linux、macOS 和 Windows 上作为服务进行安装
-
你可以在 Runner 中启用嵌入式 Prometheus 度量 HTTP 服务器
你可以通过以管理员身份登录并检查 GitLab 左侧的 Runners 菜单,来查看 GitLab 中注册的 Runner 概况,如下图所示:

现在,你应该可以看到一个已经在 GitLab 实例上注册的 Runner 列表,如下所示:

如下图所示,点击一个 Runner,你将看到可以执行以下操作:
-
配置一个暂停的 Runner,使其不接受新任务
-
将 Runner 指定为受保护
-
设置 Runner 以拾取带有标签或不带标签的作业
-
锁定 Runner 到项目
-
为作业设置最大超时时间
-
标记 Runner

在左侧,您将能够看到哪些作业最近被 Runner 处理,正如下面的截图所示:

到目前为止,您已经了解了 GitLab CI 如何与 GitLab 产品契合,以及 GitLab Runner 如何与实例注册。我们还介绍了 GitLab Runner 的基本功能,并展示了如何为作业创建配置文件,使得作业可以被 Runner 拾取并执行。
总结
在本章中,我们讨论了 GitLab CI,这是 GitLab 在提供版本控制之后最关键的功能。我们解释了如何触发管道以及如何使用 .gitlab-ci.yml 文件设计管道。然后,我们展示了构成管道的作业。最后,我们提供了一些关于 GitLab Runner 如何融入该架构的信息。在下一章中,我们将向您展示更多关于 GitLab Runner 客户端的内容。
问题
-
请列出极限编程的一个支柱。
-
用来描述作业和管道的文件是什么?
-
构建系统如何知道何时在管道中构建软件?
-
到达集成阶段需要进行哪些测试?
-
在
.gitlab-ci.yml文件中使用哪个标签来执行作业? -
由 Runner 使用的配置文件叫什么名字?
-
一个 Runner 可以启动多少个并发会话?
-
如何获取 GitLab Runner 执行的指标?
进一步阅读
-
gRPC [Golang] 大师班:构建现代 API 和微服务 [视频] 由 Stephane Maarek 主讲:
www.packtpub.com/web-development/grpc-golang-master-class-build-modern-api-and-microservices-video。 -
实操 GitLab CI 自动 DevOps [视频] 由 Alan Hohn 主讲:
www.packtpub.com/in/application-development/hands-auto-devops-gitlab-ci-video。
第十五章:安装与配置 GitLab Runner
在本章中,我们将深入了解 GitLab Runner 客户端架构。通过学习本章节内容,您将理解 GitLab CI 和 Runner 之间的基本控制流程。在本章的第二部分,我将展示如何在不同操作系统上安装 Runner 软件。由于 Runner 程序是用 Golang 编写的,许多平台都能运行此客户端。Golang 以其出色的多平台支持而著称。
在本章中,我们将涵盖以下主题:
-
Runner 客户端架构
-
使用 shell 执行器创建基础的 Runner
技术要求
为了跟随本章的指导,请从 GitHub 下载包含示例的 Git 仓库:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter15.
本章的其他要求如下:
-
GitLab Runner 客户端 – Linux 64-bit:
gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64 -
Linux 32-bit:
gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386 -
Linux ARM:
gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm -
macOS 64-bit:
gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64 -
FreeBSD 64-bit:
gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-freebsd-amd64 -
FreeBSD 32-bit:
gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-freebsd-386 -
Windows 32-bit:
gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-386.exe -
Windows 64-bit:
gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-amd64.exe
Runner 客户端架构
我们在第一章中简要介绍了 GitLab 架构,介绍 GitLab 架构。文中解释了 GitLab Runner 如何注册到 GitLab 实例并等待执行任务。与作为 GitLab 前端应用服务器一部分的各个组件不同,Runner 的架构非常简单。Runner 与 GitLab 主机之间的通信基本上是单向的。
基本架构
主要的网络通信是从 GitLab Runner 到 GitLab CI,而不是反向通信。如下图所示:

该行为最好通过如下的顺序图来展示:

当 GitLab Runner 启动时,它会通过联系 GitLab URL 来尝试找到它的协调器。当它使用注册令牌注册时,它会获得一个特殊令牌以连接 GitLab。重启后,它会连接并等待 GitLab CI 的任务。它会定时轮询 GitLab,当没有任务时,它会减少检查 GitLab 的频率,以避免过多的网络流量。
当一个任务在 GitLab CI 中排队时,它会尝试寻找一个可用的 Runner。接收到命令后,它会克隆触发任务的特定提交,并执行 .gitlab-ci.yml 文件中定义的步骤。执行后,结果会发送回 GitLab。
GitLab CI 有两种类型的 Runner:
-
特定 Runner:作为开发人员,您可以创建自己的 Runner 并将其注册到 GitLab 项目中。只有这样,项目才会对该 Runner 可见。
-
共享 Runner:GitLab 管理员还可以指定一个 Runner 为共享 Runner。它可以从多个项目中接收任务。由于这可能被视为安全漏洞,因此在将 Runner 设置为共享时要小心。
在以下截图中,您可以看到 GitLab 项目的 CI/CD 配置,您可以在其中设置使用哪个 Runner:

GitLab Runner 克隆仓库并执行 .gitlab-ci.yml 文件中定义的步骤。可以在 GitLab 项目级别注入特殊变量,并对其进行保护:

我们已经解释了 Runner 平台的基本架构,现在将介绍如何在不同的操作系统上安装软件。
GitLab Runner 软件适用于以下操作系统:
-
Linux
-
FreeBSD
-
macOS
-
Windows
安装过程在所有系统上大致相同,只是细节上有所不同。在接下来的部分中,我们将展示如何在几种不同的操作系统上安装基本的 Runner。
使用 shell 执行器创建一个基本的 Runner
在安装 GitLab Runner 软件的机器上,你可以运行 shell 执行器来本地构建软件。该类型的执行器可以在所有安装了 Runner 软件的平台上运行。因此,你可以在类 Unix 系统上运行 Bash 或 Bourne shell,或者在 Windows 平台上运行 CMD 或 PowerShell。
这种构建方式并不十分安全,因为它可以访问 Runner 执行所在系统上的本地资源。更安全的执行器将在后续章节中介绍。
在下一节中,我们将展示如何为你的平台安装 GitLab Runner 软件。
在 Linux 上安装 Runner
如果你运行的是带有包管理系统的 Linux 发行版,例如
使用 yum 或 apt,你可以使用这种方式安装 GitLab Runner 包。或者,你也可以手动安装软件。首先,我们将介绍通过包管理器进行的安装。
使用包管理器
对于基于 yum 的系统,你可以添加官方的 GitLab 包存储库:
curl -o script.rpm.sh https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh
less script.rpm.sh #(check the contents, if you are fine with it make it executable and run it)
chmod +x script.rpm.sh
./script.rpm.sh
然后,你可以使用这个基本命令(作为 root 用户或使用 sudo)安装 GitLab:
yum install gitlab-runner
对于基于 apt 的系统,保持与最新版本同步稍微有些麻烦。我们可以添加 GitLab apt 存储库的链接,但不幸的是,Debian 在基本存储库中将该软件包命名为 GitLab。这意味着基本包会自动优先选择。解决方案是将软件包固定到正确的存储库。这可以通过向 /etc/apt/preferences.d 添加文件来实现:
cat <<EOF >> /etc/apt/preferences.d/pin-gitlab-runner.pref
Explanation: Pin GitLab-runner package
Package: gitlab-runner
Pin: origin packages.gitlab.com
Pin-Priority: 999
EOF
之后,你可以安装正确的 apt 包存储库:
curl -o script.deb.sh https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh
less script.deb.sh #(check the contents, if you are fine with it make it executable and run it)
chmod +x script.deb.sh
./script.deb.sh
前面的命令输出结果如下所示。在输出中,你可以看到它检查 gpg 密钥并添加包存储库:
Detected operating system as debian/stretch.
Checking for curl...
Detected curl...
Checking for gpg...
Detected gpg...
Running apt-get update... done.
Installing debian-archive-keyring which is needed for installing
apt-transport-https on many Debian systems.
Installing apt-transport-https... done.
Installing /etc/apt/sources.list.d/runner_gitlab-runner.list...done.
Importing packagecloud gpg key... done.
Running apt-get update... done.
The repository is setup! You can now install packages.
下一步更简单——你只需通过 apt-get 安装:
apt-get install gitlab-runner
在输出中,你会看到类似下面的信息,这意味着你已经安装了所有的二进制文件。然而,在运行之前,你需要先注册 GitLab Runner:
...
gitlab-runner: Service is not running.
...
注册 Runner 的过程在 注册 Runner 部分有更详细的说明。
更新包的方法与更新系统上所有其他包的方法相同:你可以运行 apt-get update 或 yum update 命令。
使用 apt-get 或 yum 安装时,包管理器会为你的 Linux 发行版提供一个包含必要配置文件和初始化脚本的 GitLab Runner 安装包。你也可以选择仅下载 Runner 的二进制文件并以通用方式运行它。
使用手动安装
只需下载适合你 CPU 架构(x86-64、x86-32 或 ARM)的二进制文件之一:
- x86-64 位架构:
curl -o /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
- x86-32 位架构:
curl -o /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386
- ARM 架构:这是适用于 ARM CPU 架构的二进制文件,列表如下:
curl -o /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm
当你以这种方式获取二进制文件时,你需要使其具有执行权限:
chmod +x /usr/local/bin/gitlab-runner
然后,你可以以 root 用户身份创建一个 GitLab Runner 用户:
useradd --create-home gitlab-runner
然后,你需要安装并将其作为服务运行:
gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
gitlab-runner start
虽然手动安装比通过包管理器要多一点工作,但这仍然不是复杂的过程。一个优点是,使用手动安装时,您可以安装更新的 runner 版本。包管理器维护者永远不会安装开发版本,而您可以安装。更新二进制文件也不是很难。
更新手动安装的 runner 二进制文件
我们在更新时遵循的过程是替换之前下载的 Golang 二进制文件。必须先停止运行,因此确保它没有运行,否则安装会失败。
停止服务(像之前一样,您需要 root 权限):
gitlab-runner stop
然后,下载一个新的二进制文件以替换旧的文件:
curl -o /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386
curl -o /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
设置 runner 二进制文件的执行权限:
chmod +x /usr/local/bin/gitlab-runner
再次启动 runner:
gitlab-runner start
手动安装更简单,但您必须自己管理更新。在这个自动化的时代,让您分发的包管理器来管理它更为合适。
在 Mac 上安装
就像在 Linux 上一样,有几种方法可以安装 GitLab Runner 软件。不同于 Linux 上 GitLab 推荐的包管理方式,对于 macOS,它们推荐手动安装。另一种安装方法是使用 Homebrew 安装方法,您之前在书中看到过(安装 Redis 部分,第一章的 介绍 GitLab 架构)。
手动安装 runner
首先,获取适用于您系统的二进制文件(使用 sudo):
sudo curl -o /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64
然后,就像我们之前做的那样,设置二进制文件为可执行:
$ sudo chmod +x /usr/local/bin/gitlab-runner
现在我们已经有了二进制文件,如果需要的话,可以作为其他用户运行该程序:
$ cd ~
$ gitlab-runner install
$ gitlab-runner start
安装后,runner 将在系统重启后自动运行。
安装和使用 Homebrew 包管理器
macOS 没有统一的软件包管理器,但最常用的是 Homebrew,您可以在 brew.sh/ 找到它。它使用包含脚本和设置的配方来安装二进制文件。
有一个 Homebrew 配方用于安装 GitLab Runner:
brew install gitlab-runner
下一步是将 runner 安装为一个服务(这也会启动它):
brew services start gitlab-runner
使用 macOS 作为 runner 平台有一些缺点。许多开发者使用 macOS runner 来构建 iOS 相关的软件,通常还会涉及 UI 测试。但这是无法自动化的。你必须作为系统服务(LaunchDaemon)在后台运行,而此时 UI 将无法访问。您只能在用户模式下运行 runner 才能访问 UI,这就是为什么必须始终登录才能运行 GitLab Runner。
更新 runner 通过执行 brew upgrade gitlab-runner 来完成。对于手动安装,这会稍微复杂一些。
更新手动安装的 runner 二进制文件
就像我们在 Linux 上做的一样,我们正在替换之前下载的 Golang 二进制文件。它也需要停止,因此确保它没有运行,否则安装会失败:
- 首先,我们需要停止服务:
gitlab-runner stop
- 就像我们之前做的那样,获取二进制文件以替换运行器的可执行文件:
curl -o /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64
- 使下载的二进制文件可执行:
chmod +x /usr/local/bin/gitlab-runner
- 重新启动 GitLab Runner 服务:
gitlab-runner start
在 Linux 和 macOS 平台上安装 GitLab Runner 软件的步骤大致相同。使用包管理器的好处是软件更容易升级。
在 Windows 上安装
与 GitLab 本身不同,GitLab 不支持在 Windows 上运行,但你可以在 Windows 机器上操作 GitLab Runner 软件。
根据你的 CPU 架构,有两种类型的运行器二进制文件:
-
32 位版本 (
gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-386.exe) -
64 位版本 (
gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-amd64.exe)
下载并将其复制到本地驱动器中的 gitlab-runner.exe 文件夹中;例如,C:-runner。
现在,你需要一个提升权限的命令提示符来注册并安装软件:
c:\cd c:\gitlab-runner
c:\gitlab-runner\gitlab-runner.exe register
注册步骤将在下一部分中展示,因为它们几乎对所有平台都是通用的。
注册成功后,你可以启动 GitLab Runner:
c:\gitlab-runner\gitlab-runner.exe install
c:\gitlab-runner\gitlab-runner.exe start
现在我们已经安装了运行器软件,下一步是与 GitLab 注册。
注册运行器
GitLab Runner 需要一些基本信息才能启动:
-
它可以找到 GitLab 的 URL,称为协调器 URL。
-
你可以在 GitLab 中找到的特殊令牌,用于注册运行器实例。
-
一个将在 GitLab CI 中显示的描述。
-
标签,可以分配给运行器,以便在 GitLab CI 中更容易找到它。
-
执行器的类型(记住有多种类型,所有类型都在 Runner 客户端架构 部分中命名)。
这些基本信息可以通过两种方式提供给运行器:交互式和非交互式。首先,我们将讨论交互式方式。
注册运行器的交互方式
连接到 GitLab 服务器的 Web 地址如下:
sudo gitlab-runner register
请输入 gitlab-ci 协调器的 URL(例如,gitlab.com):
https://gitlab-ee.joustie.com
你还需要输入运行器注册令牌,因为它是与 GitLab 注册所必需的:
Please enter the gitlab-ci token for this runner
xxx
运行器注册令牌可以在管理区域 | 概览 | 运行器页面或在项目下找到。
你希望有一个好的描述,以便以后可以找到运行器(默认值是 hostname):
Please enter the gitlab-ci description for this runner
[hostname] my-runner
输入应该适用于此运行器的标签(例如,javarunner)。这可以在 GitLab 中稍后更改:
Please enter the gitlab-ci tags for this runner (comma separated):
javarunner,another-javarunner
最重要的部分是确定执行器的类型(对于本章,我选择了 shell 执行器):
Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
shell
注册运行器的非交互方式
在更大的环境中,部署通常是通过脚本自动化的。因此,对于 GitLab Runner,也有一个非交互式安装方式,帮助自动化基础设施的构建。你可以为 GitLab register 命令指定子命令。要了解这些选项,请在命令行中输入以下内容:
gitlab-runner register -h
要注册一个 runner,使用最常见的选项,你可以按以下步骤操作:
sudo gitlab-runner register \
--description "docker-runner" \
--url "https://gitlab-ee.joustie.com/" \
--registration-token "xxxx" \
--executor "docker" \
--docker-image alpine:latest \
--non-interactive \
--tag-list "docker,aws" \
--run-untagged="true" \
--locked="false" \
当 runner 被注册后,它将在 GitLab 的 Runner 列表中显示:

注册过程基本上是告诉 runner GitLab 所在位置,并启动密钥交换以确保安全访问 GitLab。在注册成功后,runner 会将配置信息保存在一个 TOML 文件中,该文件内容如下:
concurrent = 1
check_interval = 0
[[runners]]
name = "runnerhost.joustie.nl"
url = "https://gitlab-ee.joustie.nl"
token = "801bd1f41a3bb7a42c0b6f43e9ffc8"
executor = "shell"
shell = "bash"
[runners.cache]
Shell 也可以设置为 sh 用于简单的 Bourne shell,或者在 Windows 上设置为 powershell。在 Linux 上,通常将其放置在 /etc/gitlab-runner/config.toml 中。
运行夜间版本
如果你愿意冒险,你也可以安装最新版本,该版本尚未发布。这是有风险的,可能会有一些 bug:
-
Linux:
-
macOS:
-
Windows:
-
FreeBSD:
现在,我们应该已经安装并准备好一个 runner 可供使用。
安装 runner 最简单的方法是使用主机操作系统上运行的包管理器。手动安装允许你轻松运行开发版本或补丁版本,因为它只有一个 Golang 二进制文件。
摘要
在本章中,我们展示了 GitLab Runner 的基本架构。然后,我们向你展示了如何在几个操作系统上使用 shell 执行器安装它。在大多数系统上,都有一种或多种自动化方法可以实现这一点,同时管理更新和平台兼容性。也有一种手动安装软件的方法,适用于每个系统。使用手动方法可以轻松运行 runner 的开发版本。runner 的注册过程可以一步一步完成,也可以通过一条命令完成。
在下一章中,我们将部署 GitLab Runner 到 Docker 容器中,并以更受管理的方式部署到 Kubernetes 集群中。
问题
-
runner 连接到 GitLab 的哪一部分?
-
在基于 Debian 的系统上安装正确的包需要执行什么额外操作?
-
runner 客户端是用什么语言编写的?
-
runner 默认的描述是什么?
-
用于将注册令牌传递给
gitlab register命令的命令参数是什么?
进一步阅读
-
动手实践 Go 全栈开发,作者:Mina Andrawos:
www.packtpub.com/web-development/hands-full-stack-development-go -
Windows 10 企业管理员实战,作者:Jeff Stokes、Manuel Singer 和 Richard Diver:
www.packtpub.com/in/networking-and-servers/windows-10-enterprise-administrators -
动手实践持续集成与交付,作者:Jean-Marcel Belmont:
www.packtpub.com/in/virtualization-and-cloud/hands-continuous-integration-and-delivery
第十六章:使用 GitLab Runner 与 Docker 或 Kubernetes
在上一章中,我们安装了带有 shell 执行器的 GitLab Runner。在本章中,我们将更详细地了解容器化的 GitLab Runners。你可以通过多种方式在容器中运行 GitLab Runner:
-
使用在自定义构建的 Docker 容器中运行的 Shell 执行器:这不推荐使用,因为你需要在之后负责构建和维护这个自定义容器。另一方面,如果你想严格控制容器内的组件及其行为,那么将 Runner 容器化可能是一个不错的选择。扩展此解决方案也需要更多的工作,因为 Runner 本身仅知道如何运行作业并连接到 GitLab。如果你的容器在作业后不重启,你将会在容器中收集状态(如
/tmp或其他地方的文件),因此需要准备好处理这些问题。 -
使用 Docker 执行器,该执行器为你的作业拉取并启动 Docker 镜像:这是一个更具扩展性的解决方案,另一个优点是没有状态。每次构建都会得到一个崭新的环境并重新开始。另一个好处是,你可以为作业创建服务,这是另一个容器或多个容器,这些容器会与作业容器并行启动。例如,你可以启动一个 MySQL 数据库,并且它将作为一个服务通过服务名称进行访问。
-
使用 Kubernetes 执行器,以便你可以使用 Kubernetes 集群:Runner 可以与集群管理 API 通信,并请求资源来启动容器。当队列中的作业数量减少时,Runner 会自动缩减容器数量,保持完全无状态。
-
使用 Docker 执行器并启用自动扩展(Docker Machines 创建新的 Runners):在此配置中,GitLab Runner 控制 Docker Machine 二进制文件。它可以实时创建新的 Runner 容器,并且在队列中的作业较少时会自动缩减。
在本章中,我们将了解运行 GitLab Runner 的基本方法,以及如何使用 Kubernetes 等管理系统来进行 orchestration。自动扩展执行器将在下一章介绍。之所以如此,是因为自动扩展的 Docker 执行器有很多选项,并且需要更多的规划和系统管理功能来维护。
本章将涵盖以下主题:
-
Runner 客户端架构
-
创建你自己的 Docker 化 GitLab Runner
-
使用预构建的 Docker 容器部署 GitLab Runners
-
使用 Kubernetes 集群启动 GitLab Runners
技术要求
为了跟随本章中的说明,请从 GitHub 下载本书的 GitHub 仓库以及可用的示例,链接为 github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter16。
本章的其他要求如下:
-
为你的平台安装 Docker
-
wget命令行下载工具(www.gnu.org/software/wget/) -
访问 Alpine Docker 镜像(
hub.docker.com/_/alpine) -
访问 Python Alpine Docker 镜像(
hub.docker.com/r/lgatica/python-alpine) -
访问 GitLab Runner Docker 镜像(
hub.docker.com/r/gitlab/gitlab-runner) -
你系统上的
kubectl工具(你可以在kubernetes.io/docs/tasks/tools/install-kubectl/找到安装说明) -
安装 Helm 或 Tiller(一个帮助你管理集群的 Kubernetes 工具:
helm.sh/docs/using_helm/)
Runner 客户端架构
如果你使用 Docker 中的 GitLab Runner,那么生成的架构与上一章中的架构有一个区别。Runner 二进制文件是在 Docker 容器内执行,而不是直接在主机系统上执行。下图展示了这种架构:

无论你是自己创建一个容器,还是使用来自互联网的现有容器,图像都是一样的。
如果你打算使用 Kubernetes 来编排你的 GitLab Runners,那么架构将略有不同。你可以看到,在集群内部,具有 Kubernetes 架构的GitLab Runner可以与集群的 Kubernetes API 通信,以便扩展 Runner 实例的数量:

这两种架构都将 Docker 容器作为核心操作单元。第一种架构在扩展、升级软件和设置网络方面需要更多的管理。而 Kubernetes 已经为大部分这类任务提供了安排。
现在,我们了解了这些架构之间的区别,并且知道 Runner 是在没有容器的情况下部署的,但这在实践中意味着什么呢?我们将在接下来的章节中探讨这一点。
创建你自己的 Docker 化 GitLab Runner
大多数人会使用预构建的 Docker 容器,但有时有理由去构建自己的。也许你在构建软件时有特殊需求,而这些需求在默认的 Docker 镜像中并未安装,或者由于安全限制,根本无法使用现有镜像。许多默认镜像中包含的软件存在漏洞。
让我们从 第十章 创建你的产品,验证并打包它 中的事件管理器文档开始,使用一个 Dockerfile 来创建我们自己的 Docker 化 GitLab Runner。你将在下一节中找到我们的第一次尝试。
我要强调的是,这个 Dockerfile 完全是为了演示目的。我们不推荐将这种方式用于生产系统,甚至是你自己的开发系统。它在这里用于展示如何轻松地将命令和服务封装到容器中。
文件的第一行如下:
FROM alpine:3.7
这是我们为容器使用的基础镜像。它来自 Alpine Linux 发行版,包含了运行程序所需的最低配置。它仅为 4.41 MB,并且在使用如 Clair(github.com/coreos/clair)等容器安全扫描工具时,不显示漏洞。你也可以从零开始构建容器(请参见 ericchiang.github.io/post/containers-from-scratch/),但这是一个非常繁琐的任务。Alpine Linux 镜像足够安全,并且可以立即使用。
在接下来的行中,我们安装了一些基本的包:
RUN apk add --no-cache \
ca-certificates \
git \
wget
我们需要 CA 证书,因为我们需要 GitLab Runner 客户端连接到我们的 HTTPS 端点,在那里会进行 TLS 握手。Git 二进制文件也是必需的,以便从项目中克隆代码。
最后,在容器创建序列中设置的最后一个命令用于下载、执行并将 GitLab Runner 注册到我们的 GitLab 服务器:
RUN wget https://s3.amazonaws.com/gitlab-runner-downloads/master/binaries/gitlab-runner-linux-386 && chmod +x gitlab-runner-linux-386 && \
./gitlab-runner-linux-386 register \
--non-interactive \
--url "https://gitlab-ee.joustie.nl/" \
--registration-token "xxxxxxx" \
--executor "shell" \
--shell "sh" \
--description "dockerized shell-runner" \
--tag-list "docker" \
--run-untagged="true" \
--locked="false"
为了本例的方便,我们将所有参数硬编码在 Dockerfile 中,展示如何轻松将命令容器化(通常不建议这样做):
-
non-interactive:如果没有这个选项,会出现一个对话框,引导你完成配置设置。 -
url:这是我们 GitLab 服务器的 URL。 -
registration-token:这是来自项目、组或 GitLab 实例范围内的 Runner 令牌。 -
executor:这指定了要实现的 Runner 类型。我们选择使用 shell 执行器。 -
shell:这指定了要使用的 shell 类型;可以是bash、sh或 Windows 上的powershell。我们选择了sh,因为它是最基础的。 -
description:这是你在 GitLab 中看到的描述。 -
tag-list:我们可以为 Runner 添加标签,以便在 GitLab 中进行更方便的管理。在这个例子中,我们使用了docker。 -
run-untagged:我们将其设置为true,意味着带标签或不带标签的任何作业都可以在这个 Runner 上运行。 -
locked:Runner 不与任何项目绑定。
下一步是使用 docker build 命令构建 Docker 容器(我们指定 –no-cache,这样每次都能重新构建)。输出如下,我们将逐步讲解。第一部分是拉取 Alpine 基础镜像:
$ docker build --no-cache -t dockerrunner .
Sending build context to Docker daemon 2.56kB
Step 1/4 : FROM alpine:3.9
3.9: Pulling from library/alpine
Digest: sha256:769fddc7cc2f0a1c35abb2f91432e8beecf83916c421420e6a6da9f8975464b6
Status: Downloaded newer image for alpine:3.9
---> 055936d39205
第一个 Docker 层已经创建。第二步是添加必要的包:
Step 2/4 : RUN apk add --no-cache ca-certificates git openssl tzdata wget
---> Running in 0ab19e2eef86
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz
(1/8) Installing ca-certificates (20190108-r0)
(2/8) Installing nghttp2-libs (1.35.1-r0)
(3/8) Installing libssh2 (1.8.2-r0)
(4/8) Installing libcurl (7.64.0-r1)
(5/8) Installing expat (2.2.6-r0)
(6/8) Installing pcre2 (10.32-r1)
(7/8) Installing git (2.20.1-r0)
(8/8) Installing wget (1.20.3-r0)
Executing busybox-1.29.3-r10.trigger
Executing ca-certificates-20190108-r0.trigger
OK: 21 MiB in 22 packages
Removing intermediate container 0ab19e2eef86
---> 17ab7c7dd1b9
前面的代码下载了我们指定的包以及一些依赖项。这仍然是一个适度的包量。在接下来的构建步骤中,GitLab Runner 二进制文件将被下载:
Step 3/4 : RUN wget https://s3.amazonaws.com/gitlab-runner-downloads/master/binaries/gitlab-runner-linux-386 && chmod +x gitlab-runner-linux-386 && ./gitlab-runner-linux-386 register --non-interactive --url "https://gitlab-ee.joustie.nl/" --registration-token "xxxx" --executor "shell" --shell "sh" --description "dockerized shell-runner" --tag-list "docker" --run-untagged="true" --locked="false"
---> Running in d90d35beaa37
--2019-05-22 21:05:18-- https://s3.amazonaws.com/gitlab-runner-downloads/master/binaries/gitlab-runner-linux-386
Resolving s3.amazonaws.com... 52.216.18.115
Connecting to s3.amazonaws.com|52.216.18.115|:443... connected.
HTTP request sent, awaiting response... 200 OK
....
2019-05-22 19:46:24 (2.22 MB/s) - 'gitlab-runner-linux-386' saved [25824256/25824256]
下一步是执行 Runner:
Runtime platform arch=386 os=linux pid=1 revision=5159dcdb version=11.12.0~beta.1484.g5159dcdb
Running in system-mode.
然后,我们需要注册它:
Registering runner... succeeded runner=Vh6hpU1D
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
Removing intermediate container b55a2ae6998a
---> be4317ad95b0
二进制文件已成功下载,注册也成功;已创建特定的 Runner 令牌,并且现在它是 Docker 镜像的一部分。构建的最后一部分设置了此镜像实例化 Docker 实例的入口点:
Step 4/4 : ENTRYPOINT ["./gitlab-runner-linux-386","run"]
---> Running in 8b54b77030bc
Removing intermediate container 8b54b77030bc
---> be206c4c268c
Successfully built be206c4c268c
Successfully tagged dockerrunner:latest
它自动将镜像标记为 latest。该镜像现在可在其构建执行的机器上使用。您可以使用以下命令查看它:
$ docker images
...
如果您去 GitLab 并在管理设置菜单中打开 Runner 列表,您会看到一个新的 Runner 出现:

现在,是时候启动 Runner 并尝试构建 eventmanager-documentation 项目了。您可以使用以下命令在前台启动 Runner:
$ docker run -ti dockerrunner
简短的暂停后,应该会出现以下输出:
Runtime platform arch=386 os=linux pid=1 revision=5159dcdb version=11.12.0~beta.1484.g5159dcdb
Starting multi-runner from /etc/gitlab-runner/config.toml ... builds=0
Running in system-mode.
Configuration loaded builds=0
listen_address not defined, metrics & debug endpoints disabled builds=0
[session_server].listen_address not defined, session endpoints disabled builds=0
如您所见,它已经成功加载了在注册阶段保存在容器中的配置。它还提到没有加载度量和调试会话服务器,因此该 Runner 不向外部世界暴露任何服务。它已连接到 GitLab 服务器,现正等待命令。
当我们尝试为主分支运行 eventmanager-documentation 项目的流水线时,它将把构建作业分配给新的 GitLab Runner:
Checking for jobs... received job=675 repo_url=https://gitlab-ee.joustie.nl/marketing/eventmanager-documentation.git runner=LT7jz43c
WARNING: Job failed: exit status 1 duration=347.5203ms job=675 project=10 runner=LT7jz43c
ERROR: Failed to process runner builds=0 error=exit status 1 executor=shell runner=LT7jz43c
不幸的是,它未能构建项目。如果我们查看作业日志,将得到以下输出:

很明显,作业失败的原因。我们创建了一个基本的 GitLab Runner 容器,但不支持 Python 语言。这就是它抱怨找不到Python 包管理器(PIP)的原因。我们需要 Python 来安装亚马逊 网络 服务 命令行 接口(AWS CLI)实用工具,它在本项目的 .gitlab-ci.yml 文件中定义。
这个问题很容易修复。我们可以将 Dockerfile 中的第一行改为以下内容:
FROM python:3.7-alpine
这将把 Docker 容器的基础镜像更改为包含 Python 的 Linux 版本。现在,您可以使用完全相同的前述命令行重新构建镜像,并重新启动容器。
如果我们再次运行此项目的流水线,作业将会成功:

您可以创建更加复杂的容器镜像,但这只是将 Runner 容器化的基本方法。
在本节中,我们已经创建了自己的 GitLab Runner 容器,并将其注册到 GitLab 实例。在下一节中,我们将使用 GitLab 从其网站提供的预构建镜像。
使用预构建的 Docker 容器来部署 GitLab Runners
提供了两种基本的预构建 Docker 容器版本(基于 Ubuntu 和基于 Alpine)。它们之间的主要区别在于 Alpine 版本更小,并且安全性更高。你可以在这里找到它:gitlab.com/gitlaborg/gitlabrunner/blob/master/dockerfiles/alpine/Dockerfile。
你可以使用参数来运行容器,这些参数将传递给容器内启动的 GitLab Runner 二进制文件。这也使得 GitLab Runner 注册到 GitLab 实例变得更加容易。记得在 创建你自己的 Docker 化 GitLab Runner 章节中,我们已经将 Runner 的注册功能内置到镜像中。你可以在 Docker Hub 上找到相关的镜像:hub.docker.com/r/gitlab/gitlab-runner/tags。
只需使用以下命令启动容器,它会自动下载正确的镜像:
$ mkdir /Users/shared/gitlab-runner && mkdir /Users/shared/gitlab-runner/config
$ docker run -d --name gitlab-runner -v /Users/shared/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
你可以使用以下命令检查正在运行的容器日志:
$ dockers logs -f gitlab-runner
ERROR: Failed to load config stat /etc/gitlab-runner/config.toml: no such file or directory builds=0
上述输出意味着 gitlab-runner 软件正在容器内运行,但尚未注册。
下一步是注册它并将配置文件保存在容器中(保存在你通过 -v 指定的配置卷中):
docker run --rm -v /User/shared/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner register \
--non-interactive \
--executor "docker" \
--docker-image python:3.7-alpine \
--url "https://gitlab-ee.joustie.nl/" \
--run-untagged="true" \
--registration-token "xxx" \
--description "docker-runner" \
--tag-list "eventmanager" \
--locked="false"
如果你在此之后查看容器日志(也许你留下了窗口打开),应该会看到一条消息:
...
Configuration loaded builds=0
这意味着 Runner 现在具有有效的配置,并且已与 GitLab 在线连接。
如果我们再次尝试触发 eventmanager-documentation 项目的管道,将会运行一个任务:
Checking for jobs... received job=660 repo_url=https://gitlab-ee.joustie.nl/marketing/eventmanager-documentation.git runner=8cX8wWCr
在 GitLab 中,你将看到此任务正在运行:

如果任务成功,容器的日志文件中会显示一条消息:
Job succeeded duration=1m17.7055922s job=660 project=10 runner=8cX8wWCr
这也可以在 GitLab 的任务日志中看到:

这就总结了使用相对简单的容器在本地运行 Docker 的两种方法:
-
构建你自己的容器
-
使用预构建的镜像
如果你有大量构建任务,如何管理它们呢?你可以使用 Kubernetes 等编排系统,这是下一节的主题。
使用 Kubernetes 集群启动 GitLab Runners
将 GitLab Runner 容器部署到 Kubernetes 集群的最佳方法是使用 GitLab Runner Helm chart。
它包含所有使用 GitLab Runner Kubernetes 执行器运行的配置信息。对于从 GitLab CI/CD 接收到的每个新任务,它会在指定的命名空间内配置一个新的 pod 来运行。
你可以使用以下命令进行安装:
$ helm install --namespace gitlab --name gitlabrunner -f values.yaml gitlab/gitlab-runner
这个命令可能需要一些时间才能完成。过一段时间后,你将看到如下输出:
NAME: gitlabrunner
LAST DEPLOYED: Tue May 21 21:11:15 2019
NAMESPACE: gitlab
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
gitlabrunner-gitlab-runner 5 0s
...
这显示了相当多的输出内容,但重要的部分是 Helm chart 的状态是 DEPLOYED。
GitLab Runner 现在应该已注册到可以访问的 GitLab 实例:gitlab-ee.joustie.nl/
我们在本地 Kubernetes 集群上运行了这个部署。
你可以通过以下命令查找哪些 Kubernetes pods 正在运行:
$ kubectl get pods -n gitlab
NAME READY STATUS RESTARTS AGE
gitlabrunner-gitlab-runner-787dddf5b5-58fzw 1/1 Running 0 2m
如你所见,Runner 已在 Kubernetes 中部署,并出现在 GitLab 管理部分的 Runner 列表中:

我们可以运行第十章中介绍的eventmanager-documentation项目的管道,创建你的产品,验证并打包它,以演示多个 Runner 在 Kubernetes 集群上启动的事实。以下是 GitLab 中的管道视图,展示了多个并行任务:

如果你查看单个任务,你会看到在集群中启动新的 Runner 资源需要一些时间。直到它准备好,任务会进行轮询:

过了一段时间,一个 Runner 被启动在 Kubernetes 集群中,任务被分配到它:

你可以查看 Kubernetes 集群中 pods 的列表,并看到许多 pods 已被创建:

过了一段时间,你会看到一些并行启动的任务正在完成(绿色表示):

最终,所有任务都会成功,管道将通过:

这项任务完成后,GitLab Runner 的数量再次减少。
通过这样做,我们向你展示了如何使用 Kubernetes 管理容器,并且如何处理动态扩展。
总结
在这一章中,我们向你展示了如何在容器中运行 GitLab Runners。首先,我们介绍了将现有 Runner 容器化的一种快速方法。然后,我们展示了如何使用 GitLab 自身提供的现有 Docker 镜像。之后,我们讨论了如何管理更多容器,并且如何通过 Kubernetes 以更经济的方式处理这些容器。
在下一章,我们将讨论另一种扩展 Runner 的方法:自动扩展。
问题
-
Docker 执行器的优势是什么?
-
Docker 容器的基本构建命令是什么?
-
哪个文件包含容器的构建指令?
-
小型 Linux 容器发行版的名称是什么?
-
使用什么工具可以更轻松地配置 Kubernetes 集群?
-
你如何设置 Kubernetes 中默认启动的 Runner 数量?
进一步阅读
-
来自 DevOps 专家的 Kubernetes 课程(Kubernetes 和 Docker),由陶伟、James Lee和Basit Mustafa编写:
www.packtpub.com/application-development/kubernetes-course-devops-guru-kubernetes-docker -
学习 Docker – Docker 18.x 基础,作者:加布里埃尔·N·申克:
www.packtpub.com/in/networking-and-servers/learn-docker-fundamentals-docker-18x -
实践持续集成与交付,作者:让-马塞尔·贝尔蒙特:
www.packtpub.com/in/virtualization-and-cloud/hands-continuous-integration-and-delivery
第十七章:自动扩展 GitLab CI Runners
在前一章节中,我们使用 Kubernetes 执行器扩展了 GitLab Runners。根据你对并发运行作业数量的要求,Kubernetes 集群中可用的 Runner 数量可以随时增减。拥有大量的 Runner 可能非常昂贵,即使它们被关闭了,仍然会产生费用。最好是按需创建 Runner,并在不再需要时销毁它们。
还有另一种 GitLab Runner 执行器,它可以以这种弹性方式动态添加或移除 Runner 实例,这就是所谓的 Docker Machine 执行器。我们将从架构的角度展示它的样子,解释一些配置,并提供使用 VirtualBox 驱动程序和 Amazon EC2 驱动程序运行 Docker Machine 执行器的示例。
在本章中,我们将讨论以下主题:
-
Runner 客户端架构
-
设置环境
-
配置 GitLab Runner
技术要求
你可以在本书的 GitHub 仓库中找到本章的代码文件:github.com/PacktPublishing/Mastering-GitLab-12/tree/master/Chapter17。
本章的其他要求如下:
-
Docker Machine 会随着适用于 macOS 或 Windows 的 Docker 软件分发版本自动安装。如果你没有安装它,可以通过以下链接找到它:
github.com/docker/machine/releases/。 -
你需要一台具有最新补丁的 Linux 路由主机。
-
需要访问 Docker 注册表镜像 (
hub.docker.com/_/registry)。 -
需要访问 MinIO Docker 镜像 (
hub.docker.com/r/minio/minio)。 -
你需要在路由主机上安装 VirtualBox (
www.virtualbox.org)。 -
你需要一个 AWS 账户,用于通过 EC2 基础设施进行扩展。
Runner 客户端架构
首先,我们将描述该解决方案的架构。基于前几章节提出的架构,我们有一个 GitLab 实例,GitLab CI 收到来自运行在专用主机上的 GitLab Runner 的请求。这可以是本地虚拟机,也可以是云中的实例。Runner 配备了 Docker Machine 程序。
Docker Machine 执行器类型本质上是一个执行 Docker Machine 命令的 GitLab Runner。通过 Docker Machine,你可以创建运行 Docker 引擎的虚拟主机。你可以使用它来控制这些主机,并创建安装了 Docker 引擎的新虚拟机,这些虚拟机可以运行 GitLab Runner 容器实例。以下是该过程的示意图:

在前面的图示中,你可以看到Docker Machine组件,并且它可以实例化多个 runner。图示中还有两个其他组件,分别是缓存服务器(可以存储构建的依赖项)和Docker 注册表代理(可以缓存来自 Docker Hub 等地方的 Docker 镜像)。这两个组件将在配置 Runner部分中详细说明。
设置环境
要启用基于 Docker Machine 的 Runner,必须执行以下步骤:
-
准备一个堡垒主机,作为 Docker 创建新机器的主机。
-
在此机器上部署 GitLab Runner 软件。
-
安装 Docker Machine。
准备堡垒主机
在这个示例中,我们选择了基于 macOS 的机器。这可以是一个 Linux 虚拟机或你的笔记本电脑—任何能够运行最新版本 GitLab Runner 软件的机器。该主机唯一的功能就是通过 Docker Machine 执行器执行 GitLab Runner 软件。由于它可以通过 docker-machine 命令控制多个 Runner 实例,因此它应该被严格安全配置,以防止攻击,它也因此成为潜在的攻击目标。
部署 GitLab Runner 软件
在 macOS 上,我们使用 Homebrew 包管理器。要安装 Runner 软件,可以执行以下命令:
brew install gitlab-runner
之后,你可以像 第十五章 中所示那样 register Runner,安装和配置 GitLab Runners:
gitlab-runner register
在注册过程中,当询问执行器时,选择 Docker Machine 执行器。
在 Runner 注册后,先不要启动它—我们需要编辑位于 ~/.gitlab-runner/config.toml 的 config.toml 配置文件,这个文件在 macOS 上可以找到。我们将在配置 Runner部分进行编辑。
首先,我们需要安装 Docker Machine 二进制文件,然后才能配置 Runner,以启动 Docker Machine 执行器。
安装 Docker Machine
如果你已经在 macOS 或 Windows 上安装了 Docker,那么你已经安装了该二进制文件。你可以通过使用以下命令来测试安装:
$ docker-machine -v
docker-machine version 0.16.1, build cce350d7
你可以使用这个工具创建新的 Docker 主机。它们可以在你的本地计算机或网络上创建,也可以通过像 Microsoft、Amazon 和 Google 这样的云服务提供商创建。Docker Machine 为许多系统提供插件。以下是它们的列表:
-
所有 VMware 产品
-
Virtualbox
-
微软 Hyper-V
-
Digitial Ocean
-
亚马逊 Web 服务 (EC2)
-
微软 Azure
-
Exoscale
-
Google 计算引擎
-
Scaleway
-
IBM Softlayer
-
Rackspace
-
OpenStack
-
Linode
如果你运行的是 Linux,可以从 github.com/docker/machine/releases/ 下载并安装 Docker Machine。
如果你查看 GitLab Runner 的 Dockerfile,地址是hub.docker.com/r/gitlab/gitlab-runner/dockerfile,它用于构建默认容器,其中有一行内容如下:
wget -q https://github.com/docker/machine/releases/download/v0.7.0/docker-machine-Linux-x86_64 -O /usr/bin/docker-machine && \
chmod +x /usr/bin/docker-machine
Docker Machine 二进制文件直接安装在这个容器镜像中,并由 GitLab Runner 软件使用。当你确认 Docker Machine 二进制文件可用后,下一步是配置 Runner。
配置 Runner
现在你已经安装了 Runner 软件和 Docker Machine,是时候编辑 Runner 配置文件了。在 macOS 上,你可以在~/.gitlab-runner/config.toml中找到config.toml文件。它位于你的主目录,因为 Runner 在 macOS 上以用户空间的方式运行。
现在,我们将查看一些你可以在config.toml文件中指定的配置选项,这些选项专门用于自动扩展的 Runner。
非高峰时段模式配置
大多数组织不需要 24/7 的容量,因为他们不需要一直使用 CI 运行器。大部分工作是在常规工作周的工作时间内完成的,周末对软件构建的需求较低。在这种情况下,让机器空闲等待作业是没有意义的。通过使用OffPeakPeriods选项指定时间表,你可以指定这些生产力较低的时段。在这些时段,控制创建运行器容量的参数会有所不同。你可以通过在其前面加上OffPeak来指定它们。因此,IdleCount变为OffpeakIdleCount,IdleTime变为OffPeakIdletime,等等。算法的功能保持不变。
在以下时间表中(这是常见的时间表),你可以看到工作日夜间、傍晚以及整个周末的非高峰时段:
[runners.machine]
OffPeakPeriods = [
"* * 0-9,18-23 * * mon-fri *",
"* * * * * sat,sun *"
]
分布式运行器缓存
GitLab Runners 内置了缓存机制。它可以在全局级别以及单个项目级别进行设置。
全局设置缓存
你可以在config.toml配置文件中设置路径,以便它缓存每个作业:
[runners.cache]
Path = "/node_modules"
在项目级别设置缓存
你可以在项目本身的.gitlab-ci.yml文件中设置要缓存的路径:
cache:
paths:
- node_modules/
上述设置适用于单个 Runner 主机的上下文。如果我们使用自动扩展,我们需要一种方法让所有运行器共享这个缓存,以帮助提高速度。我们可以使用外部存储,比如 S3 桶来充当缓存。只需在 Runner 的config.toml文件中添加[runners.cache.s3]部分:
[runners.cache.s3]
ServerAddress = "s3-website-us-east-1.amazonaws.com"
BucketName = "joustie-gitlab-runner-cache"
AccessKey = "xxx"
SecretKey= "xxxx"
Insecure = false
如果这是你第一次这样做,它会尝试从 S3 存储桶获取cache.zip文件。然而,如果没有这个文件,它会报错并继续:
Checking cache for default...
FATAL: file does not exist
在构建后,node_modules目录被依赖项填充,该目录的内容会被压缩并发送到 S3 存储桶:
Creating cache default...
node_modules/: found 5728 matching files
Uploading cache.zip to https://joustie-gitlab-runner-cache.s3.amazonaws.com/project/14/default
Created cache
Job succeeded
如果我们重试该任务,我们会发现现在 S3 中有一个 cache.zip 文件,它将被用来替代重新下载所有节点依赖:
Checking cache for default...
Downloading cache.zip from https://joustie-gitlab-runner-cache.s3.amazonaws.com/project/14/default
Successfully extracted cache
分布式容器注册表镜像
另一个可能会显著拖慢构建速度的情况是 Runner 持续从互联网下载 Docker 容器。创建一个代理来处理这一问题会是一个更好的选择。在 runners.machine 中,你可以指定应该使用哪个 engine-registry-mirror。如果在本地网络中使用,这将节省大量的流量。以下是我在示例项目中使用该功能时的配置:
MachineOptions = [
"engine-registry-mirror=http://192.168.1.10:6000"
]
最基本的方式是,Docker Machine 执行器使用 docker-machine 来生成新的 GitLab Runner 容器实例。
你可以将此与其他功能结合使用,比如共享缓存和使用专用的容器注册表,以便管理大量实例。
如果你为 Runner 启用了这些设置,你需要部署一个缓存服务器和一个注册表镜像服务,我们将在下一节中展示。
安装并运行代理容器注册表和缓存服务器
这两台额外的机器在你计划部署一个完整的弹性 GitLab Runner 集群时是必要的。如果你有超过五个可以同时运行的 Runner,运行一个注册表代理和缓存服务器将带来明显的优势。你还会得到一个额外的特性,即在架构中引入了一定的高可用性:当你的互联网连接不稳定或离线时,仍然可以进行构建。
对于一个代理容器注册表,你需要有一个实现 Docker Registry HTTP API V2 的代理,我们将在下一节中安装它。
代理容器注册表
有一个方便的 Docker 容器可以立即完成此任务。你可以通过以下代码立即启动该 Docker 容器:
docker run -d -p 6000:5000 -e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io --name runner-registry registry:2
如果你创建了这样的注册表代理并检查 Runner 开始执行任务时的日志文件,你会发现代理通过获取和缓存镜像来服务 Runner:
$ docker logs registry -f
172.17.0.1 - - [25/May/2019:14:28:40 +0000] "GET /v2/ HTTP/1.1" 200 2 "" "docker/18.09.6 go/go1.10.8 git-commit/481bc77 kernel/4.14.116-boot2docker os/linux arch/amd64 UpstreamClient(Go-http-client/1.1)"
...
配置项不多,但你可以在这里找到更多信息:hub.docker.com/_/registry。
缓存服务器
创建缓存服务器有两种选择。你可以选择在 Amazon 或其他云服务商获取一个 S3 桶,或者自行运行一个对象存储服务,例如 MinIO,详情请见min.io。
在 Amazon Web Services 中创建 S3 桶
登录到 Amazon Web Services 控制台,通过 Services | S3 找到 S3 仪表盘。点击创建桶(Create bucket):

我们将其命名为 joustie-gitlab-runner-cache,其余设置保持默认:

创建你自己的 MinIO 服务器
这也可以方便地作为 Docker 镜像使用。建议在专用主机上运行此服务,因为涉及的存储可能会变得相当庞大,而你不希望此服务影响到主机上运行的其他服务。
使用以下命令运行容器:
docker run -it -p 9005:9000 -v ~/.minio:/root/.minio -v /s3:/export --name caching-server minio/minio:latest server /export
请注意容器中挂载的 /s3 卷,该目录将用作存储缓存对象的目录。
上述命令的输出将在一段时间后出现:
latest: Pulling from minio/minio
e7c96db7181b: Already exists
94d4d681d0f2: Pull complete
664c3f016f88: Pull complete
b3235cce6961: Pull complete
Digest:sha256:244c711462a69303c0aa4f8d7943ba8b36dd55246e29da44c6653e39eaa42e70
Status: Downloaded newer image for minio/minio:latest
容器镜像将被下载。
之后,MinIO 容器将启动,并报告它使用的位置,以及 AccesKey 和 SecretKey,这些将被 Runner 使用:
Endpoint: http://172.17.0.4:9000 http://127.0.0.1:9000
AccessKey: xxx
SecretKey: xxx
我们将通过构建一个 Node.js 示例项目来演示缓存的使用。该项目包含一个 node_modules 目录,我们在 .gitlab-ci.yml 文件中指定该目录为缓存位置。
当你使用 GitLab Runner 构建 Node.js 项目时,它会在 Runner 作业日志文件中报告其使用的缓存情况:
node_modules/: found 5728 matching files
Uploading cache.zip to http://192.168.178.82:9005/joustie-gitlab-runner-cache/project/14/default
Created cache
Job succeeded
当我们查看运行 MinIO 容器的专用机器时,我们在 /s3 目录中输入 tree 命令时,发现了以下目录结构(这是 MinIO Docker 容器用来存储对象的目录):
$tree
.
└── joustie-gitlab-runner-cache
└── project
└── 14
└── default
3 directories, 1 file
我们再次运行了 GitLab Runner 作业,它在 MinIO 存储桶中找到了 cache.zip。
这是使用单一 Runner 的示例。不过,你可能希望使用这些选项来扩展 Runner 实例。我们将在下一部分中讨论这个问题。
扩展你的 Runner
在上一节中,我们配置了软件并准备了环境,以便能够扩展 Runner 数量,同时还提供了一些共享服务,如注册表代理和缓存服务器。现在,让我们看两个示例。我们将分别在配置为使用 VirtualBox 和配置为使用亚马逊 Web 服务的 Runner 上运行作业。VirtualBox 是 Oracle 提供的开源虚拟化解决方案,可以在 www.virtualbox.org/ 找到。
几乎所有的 Runner 配置文件(config.toml 文件)都是相同的;我们只需更改机器驱动部分。我们从 VirtualBox 选项开始。
使用本地 VirtualBox 实例的 Docker 机器
我们从本地的 gitLab-runner 服务开始,使用 VirtualBox 的 config 文件:
brew services start gitlab-runner
几秒钟后,我们将看到 VirtualBox 启动了多个虚拟机:

当我们在 第十章 中开始构建事件管理器项目时,创建你的产品,验证并打包它,我们发现它需要更多的 Runner(五个并行作业)来运行管道。因此,我们将启动更多的机器:

当构建完成后,在 IdleTime 过后,机器数量将减少:

如果你已经在某些服务器上安装了 VirtualBox,那么 VirtualBox 驱动程序是一个很好的选择。
使用在亚马逊 Web 服务(EC2)上创建的 Docker 机器
如果你将机器驱动程序从 VirtualBox 更改为 Amazon EC2 并重新启动 Runner,Docker Machine 将在 Amazon 上启动 Runner,前提是你的凭证已保存在主目录中或已插入到你的 shell 环境中。如果没有,你需要将其保存在 config.toml 文件中。
一段时间后,Runner 将出现在 EC2 Web 界面中:

当这些 Runner 启动时,你可以将 Docker 上下文切换到 Amazon 上的实例,这样你就可以运行 Docker 命令并控制这些机器:
eval $(docker-machine env runner-fatr91wm-elastic-runner-1558647049-87941946)
当你访问堡垒主机上的 Runner 日志时,你会看到它会将机器数量缩减到 IdleCount:
$docker logs runner-fatr91wm
gitlab-runner[69056]: WARNING: Removing machine : Too many idle machines
如果我们从之前的任务(事件管理项目)开始,我们会看到许多任务在构建管道启动后进入队列:

docker-machine ls 命令将显示由自动扩展的 GitLab Runner 在 AWS 上启动的多个 EC2 实例:
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
runner-fatr91wm-elastic-runner-1558702358-7e149fd0 * amazonec2 Running tcp://3.86.53.66:2376 v18.09.6
runner-fatr91wm-elastic-runner-1558702588-f8114aa8 - amazonec2 Running tcp://34.207.177.20:2376
runner-fatr91wm-elastic-runner-1558702591-6e1b6fd9 - amazonec2 Running tcp://34.235.132.231:2376
runner-fatr91wm-elastic-runner-1558702594-dd75b49b - amazonec2 Running tcp://35.175.240.226:2376
你还可以在 AWS Web 控制台中查看新的 Runner 实例:

在成功完成管道中的一些任务后,Runner 将再次缩减:

如你所见,在 Runner 配置文件中更改 Docker Machine 驱动程序并获得相同的行为非常简单。Runner 在 VirtualBox 中扩展的虚拟机用于运行 Docker 容器,同时也可以在亚马逊 Web 服务上运行。
概要
本章中,我们解释了 GitLab Runners 的自动扩展功能。与 Kubernetes 类似,它为你提供了按需创建 Runner 实例并在需求较少时缩减的选项。在幕后,它使用 Docker Machine 来管理这些副本。提供了几种驱动程序,可以立即在许多平台上创建 Runner 实例。
在下一章,我们将查看监控这些实例的选项。
问题
-
docker-machine执行器使用了什么 Docker 功能? -
在使用自动扩展时,推荐使用哪些额外的服务器?
-
Runner 的分布式缓存保存在什么文件中?
-
本章中使用的对象存储的名称是什么?
-
GitLab Runner 的配置文件名是什么?
-
作为注册表缓存使用的 Docker 镜像名称是什么?
进一步阅读
-
容器化入门,作者:Gabriel N. Schenker、Hideto Saito、Hui-Chuan Chloe Lee 和 Ke-Jou Carol Hsu:
www.packtpub.com/in/virtualization-and-cloud/getting-started-containerization -
Docker 高手进阶(第三版),作者:Russ McKendrick 和 Scott Gallagher:
www.packtpub.com/in/virtualization-and-cloud/mastering-docker-third-edition -
Oracle VM VirtualBox 入门,作者:Pradyumna Dash:
www.packtpub.com/in/virtualization-and-cloud/getting-started-oracle-vm-virtualbox -
动手实践 AWS 系统管理,作者:Glauber Gallego、Daniel Stori 和 Satyajit Das:
www.packtpub.com/in/virtualization-and-cloud/hands-aws-system-administration
第十八章:评估
介绍 GitLab 架构
-
它由 Dimitri Zaporozhets 和 Valery Sizov 于 2011 年开发。
-
公司由风险资本资助。
-
它主要使用 Ruby 开发,部分组件使用 Golang,前端使用 JavaScript。
-
GitLab Community Edition 使用开源 MIT 许可证;GitLab 企业版使用专有许可证。
-
开源是 GitLab 的核心价值之一。核心产品(GitLab CE)正是如此,企业版基于 CE 核心,但具有额外的功能,这些功能的开发是通过专有许可证支付的。
-
Unicorn、Sidekiq、NGINX、Gitaly、数据库、Redis 和 GitLab Workhorse。
-
一。
-
键值对,具有五种不同的数据类型。
-
共享的 NFS 文件系统。
-
GCP。
安装 GitLab
-
使用 Omnibus-GitLab 安装程序
-
TCP 端口
22、80和443 -
Ubuntu、Debian、CentOS/Red Hat、OpenSuse 和 Raspbian
-
gitlab-ctl -
2.9.5
-
pg_trgm扩展 -
gitlab/gitlab-ce
-
/srv -
Python
-
使用 Helm 图表
通过 Web UI 配置 GitLab
-
工具图标
-
用户、项目和团队
-
1 MB
-
InfluxDB 和 Prometheus
-
CodeSandbox
-
PlantUML
-
电路断路器
-
Git 保养
-
AutoDevOps
-
Zero
通过终端配置 GitLab
-
Postgresql、repmgr和consul服务 -
/home/git/gitlab/config -
IMAP
-
大文件存储
-
Mattermost
-
企业许可证
-
Rack Attack
-
kubectl
将项目从 GitHub 导入到 GitLab
-
合并请求
-
GitHub 导入器,GitHub 令牌,Gitlab-rake 导入
-
使用 GitHub 图标的 GitLab 账户通过基于 OAuth 的登录,或 GitLab 账户的电子邮件地址与 GitHub 账户的公共电子邮件地址相同
-
OAuth
-
个人令牌
-
仓库
-
github_importer和github_importer_advance_stage -
通过新建项目对话框
-
import:github -
是
从 CVS 迁移
-
CVS 是集中式的,Git 是分布式的
-
pserver
-
错误,CVS 使用文件集。
-
它记录了对象的唯一 SHA。
-
修正类型并
--amend上一个提交。 -
否
-
cvs init <location> -
git init -
Eric S. Raymond
-
git remote add gitlab url-to-gitlab-repo && git push gitlab master
从 SVN 切换
-
SVN 遵循集中式架构,而 Git 使用分布式网络。
-
svnserver服务器原生,svn 服务器通过 SSH 和 Apache 模块dav_svn -
在 SVN 服务器上
-
修订
-
双向合并
-
= 1.8.2
-
作为本地目录、网络共享或通过对象存储。
-
一次性迁移和镜像
-
将新的 Git 仓库推送到 GitLab
从 TFS 迁移仓库
-
协作与沟通
-
TFVC 使用集中式架构。
-
ALM(应用生命周期管理)
-
只需更改远程并将其推送到 GitLab 项目。
-
它们是路径作用域的。
-
在服务器上
-
Git-svn
-
Chocolatey
GitLab 远景:一体化工具链
-
软件开发生命周期
-
17
-
1970
-
克莱斯勒综合薪酬系统(C3)项目
-
必须有、应该有、可以有、会有(o 代表无意义)
-
Gent,2009
-
使用敏捷方法论的项目开发端
-
Puppet 和 Chef
创建你的产品,验证并打包
-
管理
-
一个问题
-
演变一个想法
-
/estimate 4d -
使用受保护的分支
-
Privileged=true -
将 URL 添加到环境中
-
在 GitLab 中启用容器注册表
发布与配置阶段
-
.gitlab-ci.yml -
一个部署工具
-
从一个工具中运行整个 DevOps 周期:GitLab
-
在 GitLab 注册表中
-
Clair
-
自动部署
-
两个
-
sitespeed.io
使用 Prometheus 进行监控
-
Borgmon
-
Exporters
-
/~/metrics -
Ruby
-
Set prometheus['enable'] = true in gitlab.rb -
静态应用安全性测试
-
动态应用安全性测试
-
.gitlab-ci.yml
将 GitLab 与 CI/CD 工具集成
-
项目管理
-
Atlassian
-
Transition-id
-
Hudson
-
插件
-
从聊天频道控制你的支持环境
-
使用斜杠命令
-
在你的项目的设置 | 集成中
为 GitLab 持续集成设置你的项目
-
持续集成(CI)
-
.gitlab-ci.yml -
通过源代码管理系统的通知
-
单元测试
-
脚本标签
-
config.toml -
没有限制;你可以在 runner 配置文件中指定此项
-
启用内置的 Prometheus 导出器
安装与配置 GitLab Runners
-
GitLab CI
-
钉住包
-
Golang
-
主机名
-
九个
-
--registration-token
使用 GitLab Runners 与 Docker 或 Kubernetes
-
它更安全并提供一个干净的构建环境
-
docker build -t container:v1 -
一个 Dockerfile
-
Alpine Linux
-
GitLab Runner Helm chart
-
修改 Helm chart 中的值
自动扩展 GitLab CI Runners
-
Docker Machine
-
注册表代理和缓存服务器
-
cache.zip -
S3
-
.gitlab-ci.yml -
注册表
监控 CI 指标
-
抓取
-
一个 Prometheus 导出器
-
- targets: -
9090 -
使用更高限制运行
-
rate
通过使用水平扩展创建基本的 HA 架构
-
高可用性
-
Go
-
弹性计算云
-
三个
-
Jinja
-
deploy-with-ansible.sh -
InService
-
connect_ssh.sh
管理混合 HA 环境
-
有时会遇到锁定问题,并且扩展性较差。
-
Sidekiq
-
ansible.tf -
一个
-
中间件
使你的环境完全分布式
-
Web,API 和 Git SSH
-
四个
-
三个
-
Sentinel
-
/-/grafana
使用 Geo 创建 GitLab 的分布式只读副本
-
两个
-
模块
-
/etc/gitlab -
哈希存储
-
通过使用回填


浙公网安备 33010602011771号