介绍

容器化正在迅速成为云环境中打包和部署应用程序的最受欢迎的方法。它提供的标准化,以及与完整虚拟机相比的资源效率和灵活性,使其成为现代 DevOps 思维的重要推动者。当您的应用程序和微服务完全容器化时,许多有趣的 云原生 部署、编排和监控策略变得可能。

Docker 容器目前是最常见的容器类型。尽管像 Docker Hub 这样的公共 Docker 镜像仓库充满了您可以 docker pull 并立即使用的容器化开源软件镜像,但对于私有代码,您需要支付服务来构建和存储您的镜像,或者运行自己的软件来完成这一任务。

GitLab Community Edition 是一个自托管的软件套件,提供 Git 仓库托管、项目跟踪、CI/CD 服务和 Docker 镜像注册表等功能。在本教程中,我们将使用 GitLab 的持续集成服务来从一个示例 Node.js 应用程序构建 Docker 镜像。然后,这些镜像将被测试并上传到我们自己的私有 Docker 注册表。

先决条件

在开始之前,我们需要设置一个安全的 GitLab 服务器一个 GitLab CI runner来执行持续集成任务。下面的部分将提供链接和更多详细信息。

使用 SSL 安全的 GitLab 服务器

为了存储我们的源代码、运行 CI/CD 任务和托管 Docker 注册表,我们需要在 Ubuntu 16.04 服务器上安装 GitLab 实例。GitLab 目前建议使用至少 2 个 CPU 核心和 4GB RAM 的服务器。此外,我们将使用 Let’s Encrypt 的 SSL 证书来保护服务器。为此,您需要一个指向服务器的域名。

您可以通过以下教程完成这些先决条件要求:

  • 如何在 DigitalOcean 上设置主机名 将向您展示如何使用 DigitalOcean 控制面板管理域名
  • Ubuntu 16.04 初始服务器设置 将帮助您设置一个非 root、启用 sudo 的用户,并启用 Ubuntu 的 ufw 防火墙
  • 如何在 Ubuntu 16.04 上安装和配置 GitLab 将向您展示如何安装 GitLab 并使用 Let’s Encrypt 的免费 TLS/SSL 证书进行配置

GitLab CI Runner

如何在 Ubuntu 16.04 上设置 GitLab CI 持续集成流水线 将为您提供 GitLab 的 CI 服务概述,并向您展示如何设置 CI runner 来处理作业。我们将在此教程中构建示例应用程序和 runner 基础设施的基础上进行。

步骤 1 — 设置特权的 GitLab CI Runner

在先决条件的 GitLab 持续集成教程中,我们使用 sudo gitlab-runner register 和其交互式配置过程设置了一个 GitLab runner。该 runner 能够在隔离的 Docker 容器内运行软件的构建和测试。

然而,为了构建 Docker 镜像,我们的 runner 需要完全访问 Docker 服务本身。配置此项的推荐方式是使用 Docker 的官方 docker-in-docker 镜像来运行作业。这需要授予 runner 特殊的 privileged 执行模式,因此我们将创建一个启用了此模式的第二个 runner。

由于使用特权 runner 存在安全影响,我们将创建一个特定于项目的 runner,该 runner 仅接受 hello_hapi 项目上的 Docker 作业(GitLab 管理员始终可以在以后手动将此 runner 添加到其他项目中)。从您的 hello_hapi 项目页面,点击左侧菜单底部的 设置,然后点击子菜单中的 CI/CD

!GitLab 项目设置菜单

现在点击 Runners settings 部分旁边的 Expand 按钮:

!GitLab “Runners settings” 展开按钮

将会有一些关于设置特定 Runner的信息,包括注册令牌。请记下此令牌。当我们使用它注册新的 runner 时,该 runner 将仅锁定到此项目。

!GitLab 项目特定的 runners 选项

在此页面上,点击 Disable shared Runners 按钮。我们希望确保我们的 Docker 作业始终在我们的特权 runner 上运行。如果存在非特权的共享 runner,GitLab 可能会选择使用该 runner,这将导致构建错误。

登录到具有当前 CI runner 的服务器上。如果您还没有设置好 runner 的机器,请返回并在继续之前完成先决条件教程中的 安装 GitLab CI Runner 服务 部分。

现在,运行以下命令来设置特权项目特定的 runner:

sudo gitlab-runner register -n \
  --url https://gitlab.example.com/ \
  --registration-token your-token \
  --executor docker \
  --description "docker-builder" \
  --docker-image "docker:latest" \
  --docker-privileged
注册 runner... 成功                     runner=61SR6BwV
成功注册 runner。可以随时启动它,但如果它已经在运行,则配置应该会自动重新加载!

请务必替换为您自己的信息。我们在命令行上设置了所有 runner 选项,而不是使用交互式提示,因为提示不允许我们指定 --docker-privileged 模式。

您的 runner 现在已经设置、注册并运行。要进行验证,请切换回您的浏览器。点击主 GitLab 菜单栏中的扳手图标,然后点击左侧菜单中的 Runners。您的 runners 将被列出:

!GitLab runner 列表

现在我们有一个能够构建 Docker 镜像的 runner,让我们为其设置一个私有的 Docker 注册表以便推送镜像。

步骤 2 — 设置 GitLab 的 Docker Registry

设置自己的 Docker registry 可以让您从自己的私有服务器推送和拉取镜像,增加安全性并减少工作流对外部服务的依赖。

GitLab 将通过几个配置更新来设置一个私有的 Docker registry。首先,我们将设置 registry 的 URL。然后,我们将(可选地)配置 registry 使用兼容 S3 的对象存储服务来存储其数据。

SSH 登录到您的 GitLab 服务器,然后打开 GitLab 配置文件:

sudo nano /etc/gitlab/gitlab.rb

滚动到 Container Registry settings 部分。我们将取消注释 registry_external_url 行,并将其设置为我们的 GitLab 主机名,端口号为 5555

registry_external_url 'https://gitlab.example.com:5555'

接下来,添加以下两行以告诉 registry 如何找到我们的 Let’s Encrypt 证书:

registry_nginx['ssl_certificate'] = "/etc/letsencrypt/live/gitlab.example.com/fullchain.pem"
registry_nginx['ssl_certificate_key'] = "/etc/letsencrypt/live/gitlab.example.com/privkey.pem"

保存并关闭文件,然后重新配置 GitLab:

sudo gitlab-ctl reconfigure
. . .
gitlab Reconfigured!

更新防火墙以允许流量访问 registry 端口:

sudo ufw allow 5555

现在切换到另一台已安装 Docker 的机器,并登录到私有 Docker registry。如果您的本地开发计算机上没有安装 Docker,则可以使用已设置为运行 GitLab CI 作业的任何服务器,因为它已经安装了 Docker:

[本地环境]
docker login gitlab.example.com:5555

系统将提示您输入用户名和密码。使用您的 GitLab 凭据登录。

[本地环境]

登录成功

成功!registry 已设置并运行。目前它将在 GitLab 服务器的本地文件系统上存储文件。如果您想要使用对象存储服务,继续进行本节。否则,跳转到步骤 3。

要为 registry 设置对象存储后端,我们需要了解关于我们对象存储服务的以下信息:

  • 访问密钥
  • 秘密密钥
  • 区域(例如,如果使用 Amazon S3,则为 us-east-1),或者如果使用兼容 S3 的服务,则为 区域端点https://nyc.digitaloceanspaces.com
  • 存储桶名称

如果您使用的是 DigitalOcean Spaces,您可以通过阅读《如何创建 DigitalOcean Space 和 API 密钥》来了解如何设置新的 Space 并获取上述信息。

当您获得对象存储信息后,打开 GitLab 配置文件:

sudo nano /etc/gitlab/gitlab.rb

再次滚动到 container registry 部分。查找 registry['storage'] 块,取消注释,并将其更新为以下内容,确保在适当的位置替换您自己的信息:

registry['storage'] = {
  's3' => {
    'accesskey' => 'your-key',
    'secretkey' => 'your-secret',
    'bucket' => 'your-bucket-name',
    'region' => 'nyc3',
    'regionendpoint' => 'https://nyc3.digitaloceanspaces.com'
  }
}

如果您使用的是 Amazon S3,则只需要 region 而不需要 regionendpoint。如果您使用的是 Spaces 等兼容 S3 的服务,则需要 regionendpoint。在这种情况下,region 实际上不配置任何内容,输入的值并不重要,但它仍然需要存在且不为空。

保存并关闭文件。

再次重新配置 GitLab:

sudo gitlab-ctl reconfigure

在您的其他 Docker 机器上,再次登录到 registry 以确保一切正常:

[本地环境]
docker login gitlab.example.com:5555

您应该会收到 登录成功 的消息。

现在我们已经设置好了 Docker registry,让我们更新应用程序的 CI 配置,以构建和测试我们的应用,并将 Docker 镜像推送到我们的私有 registry。

步骤 3 — 更新 gitlab-ci.yaml 并构建 Docker 镜像

要使我们的应用在 Docker 中构建,我们需要更新 .gitlab-ci.yml 文件。您可以通过从主项目页面点击该文件,然后点击 编辑 按钮来直接在 GitLab 中编辑此文件。或者,您可以将存储库克隆到本地计算机,编辑该文件,然后将其 git push 回 GitLab。操作如下:

[本地环境]
git clone git@gitlab.example.com:sammy/hello_hapi.git
cd hello_hapi

使用你喜欢的编辑器编辑文件

git commit -am “更新 CI 配置”
 git push
首先,删除文件中的所有内容,然后粘贴以下配置:
image: docker:latest
 services:• docker:dind
stages:
• build
• test
• release
variables:
 TEST_IMAGE: gitlab.example.com:5555/sammy/hello_hapi:$CI_COMMIT_REF_NAME
 RELEASE_IMAGE: gitlab.example.com:5555/sammy/hello_hapi:latestbefore_script:
• docker login -u gitlab-ci-token -p $CI_JOB_TOKEN gitlab.example.com:5555
build:
 stage: build
 script:
 - docker build --pull -t $TEST_IMAGE .
 - docker push $TEST_IMAGEtest:
 stage: test
 script:
 - docker pull $TEST_IMAGE
 - docker run $TEST_IMAGE npm testrelease:
 stage: release
 script:
 - docker pull $TEST_IMAGE
 - docker tag $TEST_IMAGE $RELEASE_IMAGE
 - docker push $RELEASE_IMAGE
 only:
 - master
确保使用你自己的信息更新高亮显示的 URL 和用户名,然后使用 GitLab 中的 **Commit changes** 按钮保存。如果你在 GitLab 之外更新文件,请提交更改并使用 `git push` 推送回 GitLab。

这个新的配置文件告诉 GitLab 使用最新的 Docker 镜像 (`image: docker:latest`) 并将其链接到 docker-in-docker 服务 (docker:dind)。然后定义了 `build`、`test` 和 `release` 阶段。`build` 阶段使用存储在仓库中的 `Dockerfile` 构建 Docker 镜像,然后将其上传到我们的 Docker 镜像仓库。如果成功,`test` 阶段将下载刚刚构建的镜像,并在其中运行 `npm test` 命令。如果测试阶段成功,`release` 阶段将拉取镜像,将其标记为 `hello_hapi:latest` 并将其推送回仓库。

根据你的工作流程,你还可以添加额外的 `test` 阶段,甚至是将应用程序推送到暂存或生产环境的 `deploy` 阶段。

更新配置文件应该已经触发了新的构建。返回 GitLab 中的 `hello_hapi` 项目,并点击提交的 CI 状态指示器:

!GitLab 提交通知与流水线状态图标

然后你可以点击任何阶段以查看其进度:

!GitLab 流水线详细信息

!GitLab 流水线阶段进度

最终,所有阶段应该显示绿色的勾号图标,表示它们都成功了。我们可以通过点击左侧菜单中的 **Registry** 项来找到刚刚构建的 Docker 镜像:

!GitLab 容器注册表镜像列表

如果你点击图像名称旁边的小“文档”图标,它将复制相应的 `docker pull ...` 命令到你的剪贴板。然后你可以拉取并运行你的镜像:

```command
[本地环境]
docker pull gitlab.example.com:5555/sammy/hello_hapi:latest
docker run -it --rm -p 3000:3000 gitlab.example.com:5555/sammy/hello_hapi:latest
[本地环境]

> hello@1.0.0 start /usr/src/app
> node app.js

服务器运行在:http://56fd5df5ddd3:3000

镜像已从注册表中拉取并在容器中启动。切换到浏览器并连接到端口 3000 上的应用程序进行测试。在这种情况下,我们正在本地机器上运行容器,因此可以通过以下 URL 的 localhost 访问:

http://localhost:3000/hello/test
你好,测试!

成功!你可以使用 `CTRL-C` 停止容器。从现在开始,每当我们将新代码推送到仓库的 `master` 分支时,我们将自动构建和测试一个新的 `hello_hapi:latest` 镜像。


## 结论

在本教程中,我们设置了一个新的 GitLab runner 来构建 Docker 镜像,创建了一个私有的 Docker 注册表来存储它们,并更新了一个 Node.js 应用程序,以便在 Docker 容器中构建和测试。

要了解更多关于此设置中使用的各个组件,请阅读 GitLab CE、GitLab 容器注册表和 Docker 的官方文档。