通过 Jenkins、Packer 和 Kubernetes 自动构建映像

创建自定义映像以启动 Google Compute Engine 实例或 Docker 容器可以缩短启动时间并提高可靠性。通过将软件预安装到自定义映像中,您还可以减少对不受控的第三方代码库的依赖。

您可以选择要在自定义映像中包含的软件和配置。一方面,最低配置的映像(在本文档中称为基础映像)包含基本操作系统映像(如 Ubuntu 14.10),还可能包括基本软件和配置。例如,您可以预安装 Java 或 Ruby 等语言运行时,配置远程日志记录或应用安全补丁程序。基础映像提供稳定的基线映像,用于进一步定制以服务于应用。

另一方面,完全配置的映像(在本文档中称为不可变映像)不仅包含基本操作系统映像或基础映像,还包含运行应用所需的所有内容。运行时配置(例如数据库连接信息或敏感数据)可以包含在映像中,也可以在启动时通过环境、元数据或密钥管理服务提供。

构建映像的过程与构建软件有很多共同点,例如:您已准备好代码(Chef、Puppet、bash 等)和编写代码的人;将编写的代码应用于基本映像以开始构建;构建成功后,输出工件;(通常会)对工件进行测试。许多软件构建的最佳做法也适用于映像构建,例如:通过版本控制来管理映像配置脚本;更改脚本以触发构建;自动执行映像构建;在构建结束时发布映像的工件版本或测试映像的工件。

您将学到的内容

在此解决方案中,您可以了解构建自定义映像的两种常规方法,以及如何使用一些常用的开源工具(包括 Jenkins、Packer、Docker 和 Kubernetes)创建自动化流水线以持续构建映像。此流水线与 Google Cloud Platform (GCP) 中的 Cloud Source Repositories 集成,并输出 Compute Engine 映像和 Docker 映像。

您将了解如何构建基础映像和不可变映像,并学习如何在 GCP 中跨多个项目管理对这些映像的访问权限。最后,借助本文档末尾的综合教程,您可以部署和使用此解决方案的开源参考实现。

映像类型

可扩缩和有弹性的 Web 应用解决方案中,使用 Ruby on Rails Web 应用作为在 GCP 上运行 Web 应用的参考。该解决方案的源代码不使用自定义映像;当 Compute Engine 实例启动时,启动脚本将安装 Chef Solo,然后由后者安装运行该应用所需的所有内容,包括 nginx、Ruby 2、cURL 和其他系统工具,以及 Unicorn、Rails 应用及其所有 gem、imagemagick 和应用配置。

下图描述了该启动过程。

无自定义映像的启动过程的示意图。

这个过程并不快,每个实例需要 10-15 分钟才能启动,具体时长取决于软件包所需的各种代码库的下载速度,并且假设托管这些软件包的每个代码库都是在线且可用的。在以下部分中,您将了解基础映像和不可变映像如何提高实例启动过程的性能和可靠性。

基础映像

创建基础映像时,您可以决定要在映像中包含的软件和软件包。在做出决定时,您需要考虑以下几点:

  • 安装速度。大软件包的下载速度很慢;必须基于数据源构建的软件可能很耗时;包含许多依赖项的软件包会使问题复杂化。您需要考虑在基础映像中包含这些类型的软件和软件包。
  • 远程代码库的可靠性。如果您没有在基础映像中包含该软件,而是在启动时下载该软件,请问您信任远程代码库的可用性吗?如果该代码库在启动期间不可用,它是否会使您的应用无法运行?为了减少对可能无法控制的远程代码库的依赖,请考虑在基础映像中包含关键依赖项。
  • 变化率。软件或软件包是否经常更改?如果是,请考虑不将其包含在基础映像内,而是将其存储在可靠的、可访问的位置,例如 Google Cloud Storage 存储分区。
  • 必需性或安全性。如果某些软件包(如日志记录和 OSSEC 等)被强制要求在组织中的每个实例上以特定配置运行,则这些软件包应安装在所有其他映像扩展的基础映像中。安全团队可以使用更高级的工具(如 Chef 或 Puppet)来构建 Docker 基础映像,而下游开发者可以使用 Dockerfile 轻松扩展基础映像。

这些条件表明,可扩缩和弹性网络应用解决方案中的 Ruby on Rails 应用的基础映像可以包括 Chef Solo、nginx、Ruby、cURL 和其他系统工具以及 Unicorn。而其他依赖项将在启动时安装。

下图描述了基础映像的启动过程:

包含基础映像的启动过程的示意图。

此示例中的功能实例从 Compute Engine 元数据服务检索其配置(例如,数据库连接字符串、API 密钥等)。您可以选择使用其他服务(如 etcd)或简单的 Cloud Storage 存储分区来管理配置。

下面的部分重点介绍用于自动构建此处所示的 Ruby 基础映像的工具。

不可变的映像

与基础映像不同,不可变映像的所有软件都包含在映像中。从映像启动实例或容器时,无需下载软件包或安装软件。可扩缩和弹性网络应用解决方案中的 Ruby on Rails 应用的不可变映像将包含所有软件,并且实例会在启动时传送流量。

包含不可变映像的启动过程的示意图。

配置和不可变映像

您可以选择让您的应用通过配置服务访问所需的配置数据,也可以在不可变映像中包含所有配置。如果您选择后一种方法,请务必考虑在映像中包含密钥所带来的安全隐患。如果要将不可变映像推送到 Docker Hub 中的公共代码库,则每个人都可以访问这些映像。由此可知,这些映像不应包含任何敏感或机密信息。

将不可变映像用作部署单元

使用不可变映像作为部署单元可消除配置偏移。配置偏移是指一个或多个实例处于与预期不同的状态。例如,当您将安全补丁程序应用于 100 个正在运行的容器并且其中一些无法更新时,就会发生这种情况。当进行任何更改时,映像将成为您部署的单元。如果操作系统需要应用软件补丁程序或更新日志记录配置,您可以构建包含这些更改的新映像,并通过发布新实例或容器并替换所有的旧实例来推出该映像。如果将应用配置捆绑在不可变映像中,即使是更新数据库连接字符串等简单的更改也意味着您需要创建和发布新映像。

自动化映像构建流水线的架构和实现

本部分介绍了使用 Jenkins、Packer、Docker 和 Google Kubernetes Engine (GKE) 自动构建自定义映像的自动化映像构建流水线的实现细节。其中的每个部分都包含了简介、架构图和图中组成部分的详细分析。

使用的软件和服务

这些软件和服务用于创建自动化映像构建器。

软件 用途
Jenkins Jenkins 是备受欢迎的开放源代码持续集成 (CI) 服务器。您可以使用 Jenkins 在包含映像配置脚本的其他项目中轮询 Git 代码库,然后根据这些代码库构建映像。
Packer Packer 是一种根据单一配置源为多个平台创建相同机器映像的工具。它支持许多不同的配置源(包括 Shell、Chef、Puppet、Ansible 和 Salt),并且可以为 Compute Engine 和 Docker 等输出映像。Jenkins 代理使用 Packer 根据 Git 代码库中的配置构建映像。
Docker Docker 是一个开放源代码的工具,用于将应用打包和部署为容器。此架构和教程中的 Jenkins(包括主节点和构建代理)部署为 Docker 容器。构建代理还输出 Docker 映像作为其架构之一。
GKE GKE 由开源技术 Kubernetes 提供支持,使您能够在 GCP 虚拟机上运行和管理 Docker 容器。
Container Registry Container Registry 可在 GCP 上提供安全私密的 Docker 映像存储空间。它在 GCP 上运行,可通过 HTTPS 端点进行访问。
Compute Engine GKE 使用 Compute Engine 虚拟机运行 Kubernetes 并托管 Jenkins 的主节点和构建代理容器。除了 Docker 映像之外,Jenkins 构建过程还会输出 Compute Engine 虚拟机映像。
Cloud Storage 您可以使用 Cloud Storage 来存储 Jenkins 配置的备份。
Nginx Nginx 提供反向代理功能。它可以将传入请求转发到 Jenkins 主节点的网络界面。通过配置,它可以终止 SSL 连接并提供基本身份验证。

映像构建器概览

下图显示了各种组件如何交互以创建可以自动构建虚拟机和 Docker 映像的系统。

包含映像构建器项目各组件的示意图。

您可以在 Jenkins 主节点上定义构建每个映像的作业。该作业将轮询源代码的代码库(此图中的 Git),该代码库包含配置脚本和描述如何构建映像的 Packer 模板。当轮询过程中检测到了更改时,Jenkins 主节点会将作业分配给构建代理。接着代理使用 Packer 运行构建,该构建会将 Docker 映像输出到 Container Registry,并将虚拟机映像输出到 Compute Engine。

Packer 和配置脚本

Packer 模板和关联的配置脚本共同定义了如何构建映像。它们被视为软件并存储在各自的 Git 代码库中。您构建的每个映像都有自己的包含 Packer 模板和配置脚本的代码库。

本部分概述了一种可能的 Packer 配置,该配置使用 Chef 通过添加 Ruby 和 rbenv 来自定义 Ubuntu 14.04。如需了解 Packer 的详情,请访问 https://www.packer.io/docs 并参阅其出色的文档。

映像命名和 Packer 变量

当您更改包含映像的 Packer 模板和配置脚本的 Git 代码库时,映像构建器都会构建映像。使用 Git 分支以及构建它们的提交 ID 命名或标记映像是值得提倡的做法。Packer 模板允许您定义变量并设置它们在运行时的值:

{
...
  "variables": {
      "Git_commit": "",
      "Git_branch": "",
      "ruby_version_name": "212",
      "project_id": "null"
  }
...
}

Jenkins 构建代理可以找到 Git 分支和提交 ID,并将它们作为变量提供给 Packer 命令行工具。您将在本文档的教程部分了解到相关信息。

预配程序的程序化配置

Packer 模板定义了一个或多个预配程序,这些预配程序描述了如何使用 Chef、Puppet 或 shell 脚本等工具来配置实例。Packer 支持多个预配程序。如需查看完整列表,请参阅 Packer 文档目录。此代码段定义了一个 chef-solo 预配程序,其中包含要运行以配置映像的实战宝典路径和配方:

{
  ...
  "provisioners": [
    {
      "type": "chef-solo",
      "install_command": "apt-get install -y curl && curl -L https://www.opscode.com/chef/install.sh | {{if .Sudo}}sudo{{end}} bash",
      "cookbook_paths": ["chef/site-cookbooks"],
      "run_list": [{{
        "recipe[ruby]",
        "recipe[ruby::user]",
        "recipe[ruby::ruby212]"
      ]
    }
  ],
  ...
}

chef 实战宝典和方法存储在与 Packer 模板相同的 Git 代码库中。

使用构建器定义映像输出

模板的 builders 部分定义了预配程序将运行以创建新映像的路径。要构建 Compute Engine 映像和 Docker 映像,请定义两个构建器:

{
  "variables": {...},
  "provisioners": [...],
  "builders": [
    {
      "type": "googlecompute",
      "project_id": "{{user `project_id`}}",
      "source_image": "ubuntu-1410-utopic-v20150202",
      "zone": "us-central1-a",
      "image_name": "{{user `ruby_version_name`}}-{{user `Git_branch`}}-{{user `Git_commit`}}"
    },
    {
      "type": "docker",
      "image": "ubuntu:14.10",
      "commit": "true"
    }
  ],
 ...
}

googlecompute 构建器包含一个 project_id 属性,该属性用于指示生成映像的存储位置。image_name 属性用于为生成的映像指定名称,还能连接变量以创建包含如下映像信息的名称:Ruby 的版本、Git 分支以及用于构建映像的 Git 提交 ID。googlecompute 构建器创建的映像的示例 URI 可能如下所示:

https://www.googleapis.com/compute/v1/projects/image-builder-project-name/global/images/ruby212-master-9909043

docker 构建器应该包含一个 post-processors 属性,该属性用于使用 Docker 注册表以及将存储映像的代码库来标记映像:

{
  "variables": {...},
  "provisioners": [...],
  "builders": [...],
  "post-processors": [
    [
      {
        "type": "docker-tag",
        "repository": "gcr.io/{{user `project_id`}}/ruby212",
        "tag": "{{user `Git_branch`}}-{{user `Git_commit`}}",
        "only": ["docker"]
      }
    ]
  ]
}

这个 post-processor 将使用运行构建时提供的 project_id 标记要在 Container Registry 中存储的映像。推送此 Docker 映像后,您可以检索此映像:

docker pull gcr.io/image-builder-project-name/ruby212:master-9909043

您要构建的每个映像的源代码库中都包含了 Packer 模板和配置脚本,并且 Jenkins 主节点会为每个映像定义一个作业,如下图所示。

包含自定义映像的映像构建器项目的示意图。

将 Jenkins 和 Packer 结合使用的一个优点是 Jenkins 可以检测并响应您对 Packer 模板或配置脚本所做的任何更新。例如,如果您更新 Ruby 基础映像中安装的 Ruby 版本,则 Jenkins 主节点会通过分配代理来克隆代码库,然后针对模板运行 Packer 以及构建映像来做出响应。

本解决方案末尾的教程将详细介绍配置 Jenkins 作业以执行 Packer 构建的过程。

项目隔离

Jenkins 主节点和构建代理在同一个 Cloud Platform 项目中一起运行,它们创建的映像会存储在此项目中。您可以按功能在项目中隔离应用。系统不会对项目收费,您只需为使用的资源付费。在此解决方案中,Jenkins 基础架构将在其自己的项目中运行,与其使用的源控代码库隔离。在下面的部分中将会讨论的 Jenkins 备份存储在项目内的 Google Cloud Storage 存储分区中。借助这些功能,Jenkins 就可充当映像中心,将映像共享给其他项目,而且其他项目也能够根据单独的访问控制规则来维护自己的代码库。

在整个组织中构建和共享映像

为了便于共享映像,该解决方案将存储在 Git 中的每个构建映像都放入单独的映像配置项目中。这种分离将映像构建器项目与构建映像隔离开来。在这种中心辐射式架构下,映像构建器项目作为中枢,映像配置项目作为辐条,使不同的团队可以更轻松地拥有和管理映像配置。

这种中心辐射式架构如下图所示。

用作中心辐射式系统的映像构建器项目的示意图。

访问控制(授予 Jenkins 集群访问每个映像项目的权限,并授予其他项目访问由 Jenkins 构建的映像的权限)将在下面的部分进行讨论。

每个映像一个项目

您创建的每个项目都有一个专用的基于 Git 的 Cloud Repository。您可以创建的项目数量没有限制,而且您只需为项目中使用的资源(如 Compute Engine 实例)付费。例如,如果您拥有 PHP、Ruby 和 Wordpress 映像,则每个映像都会在 Google Cloud Platform Console 中显示自己的项目,如下图所示。

映像构建器项目的示意图,其中每个自定义映像都拥有单独的项目。

您可以从源代码菜单项访问项目的 Cloud Repository。对于新项目,您可以选择初始化代码库的方式:例如,您可以镜像现有的 GitHub 或 Bitbucket 代码库、推送现有的本地 Git 代码库,或者从 Cloud Source Repositories 创建新的本地 Git 代码库,如下图所示。

演示如何使用 GCP Console 浏览源代码的屏幕截图。

下图显示了使用 Packer 模板和定义构建的 Chef 配方初始化的 Ruby 基础映像项目。

带有 Packer 模板和 Chef 方法的 Ruby 基础映像。

如需查看代码库的网址,请点击设置 。当您在 Jenkins 主节点上为代码库创建构建作业时,需要使用此网址,如下图所示。

Jenkins 主节点的源代码库设置。

Cloud Repository 的访问权限控制

Jenkins 映像构建器需要可以查看每个映像配置项目的 Cloud Repository 的权限。下图为中心辐射式架构的简化视图。

具有必要权限的映像构建器项目。

每个项目都必须使用映像构建器项目的计算服务帐号电子邮件地址授予对 Jenkins 映像构建器项目的访问权限。该地址格式为 \{PROJECT_ID\}-compute@developer.gserviceaccount.com。您可以在 GCP Console 中该项目的权限部分复制该地址,如下图所示。

从项目的权限部分复制的地址。

获得运行 Jenkins 映像构建器的项目的计算服务帐号电子邮件地址后,转至项目的权限部分,其中包含要从中构建映像的 Cloud Repository,选择添加成员,然后授予可以查看的权限,如下图所示。

设置可以查看项目的权限。

现在,在映像构建器项目中运行的 Jenkins 主节点可以在这些项目中对 Cloud Repository 进行轮询和拉取,并在您提交更改时构建新映像。

共享 Compute Engine 映像和 Docker 映像

由映像构建器创建的 Compute Engine 映像和 Docker 映像存储在映像构建器所属的项目中。其他项目中的应用将使用这些映像来启动 Compute Engine 实例和 Docker 容器,并且希望访问这些映像的每个应用项目必须具有可以查看映像构建器项目的权限。按照上一部分中定义的过程进行操作,首先查找每个应用项目的计算服务帐号,然后将其添加为具有可以查看映像构建器项目的权限的成员,如下图所示。

添加具有可以查看映像构建器项目的权限的其他项目。

Jenkins 备份和恢复

Jenkins 主节点包含一个预定义的作业,用于定期将 Jenkins 配置和作业历史记录备份到 Google Cloud Storage。默认情况下,作业会定期运行(每两个小时一次,每个工作日都要进行),如下图所示。

Jenkins 主节点的自动构建设置。

作业的构建步骤执行 shell 脚本,将密钥、用户、作业和历史记录归档到一个 tar 压缩文件中。作业会创建两个存档副本:一个以日期戳命名,一个命名为 LATEST,使您可以轻松自动地恢复最新的备份。您可以自定义此步骤以添加或移除要备份的内容,如下图所示。

如何自定义构建脚本。

构建后操作使用您创建的 Cloud Storage 插件和 Google 元数据凭据与 Google API 进行交互,并将备份归档上传到 Cloud Storage。日期戳和 LATEST 归档都会被上传。下图显示了该步骤的定义。

用于定义后构建操作的界面。

下图显示已累积了一些备份的存储分区:

项目的累积备份列表。

恢复备份

与前面部分中使用环境变量在 Nginx 反向代理中启用 SSL 或基本身份验证的方式相同,您可以使用环境变量来配置 Jenkins 主节点的复制控制器定义,以便在服务启动时恢复备份。以下代码是复制控制器定义的代码段:

{
  "kind": "ReplicationController",
  ...
  "spec": {
    ...
    "template": {
      "spec": {
        "containers": [
            {
              "name": "jenkins",
              "env": [
                {
                  "name": "GCS_RESTORE_URL",
                  "value": "gs://your-backup-bucket/jenkins-backup/LATEST.tar.gz"
                }
              ],
             ...
           }
        ]
      }
    }
  }
}

Jenkins 主节点的 Docker 映像在启动时会检查 GCS_RESTORE_URL 环境变量是否存在。如果存在,则假定该环境变量的值为备份的网址(包括 gs:// 架构),并且脚本会使用 Jenkins 主节点映像上安装的 gsutil 命令行工具来安全地下载和恢复备份。

恢复过程仅在启动容器时进行。要在启动 Jenkins 主节点后恢复备份,请将其复制控制器的大小调整为 0,更新控制器的定义以指向备份的网址,然后将控制器的大小设置回 1。本教程将介绍此操作。

教程

GitHub 上提供了本教程的完整内容(包括说明和源代码),网址为 https://github.com/GoogleCloudPlatform/kube-jenkins-imager

此页内容是否有用?请给出您的反馈和评价:

发送以下问题的反馈:

此网页
解决方案