个性化阅读
专注于IT技术分析

零停机Jenkins在AWS上使用Terraform进行连续部署

本文概述

在当今互联网世界中, 几乎所有东西都需要24/7增长, 可靠性是关键。这意味着你的网站停机时间几乎为零, 避免了可怕的”未找到:404″错误页面, 或在推出最新版本时导致其他服务中断。

假设你已经为客户或者你自己创建了一个新应用程序, 并且设法获得了喜欢你的应用程序的良好用户群。你已经收集了用户的反馈, 然后去找开发人员, 要求他们构建新功能并使应用程序可供部署。准备好了之后, 你可以停止整个应用程序并部署新版本, 或者构建零停机CI / CD部署管道, 这将完成所有繁琐的工作, 无需人工干预即可将新版本推送给用户。

在本文中, 我们将确切讨论后者, 我们如何使用Terraform作为基础架构协调器, 在AWS Cloud上的Node.js中构建一个三层Web应用程序的连续部署管道。我们将使用Jenkins进行持续部署, 并使用Bitbucket托管我们的代码库。

代码库

我们将使用一个演示的三层Web应用程序, 你可以在此处找到代码。

回购包含Web和API层的代码。这是一个简单的应用程序, 其中Web模块调用API层中的一个端点, 该API层在内部从数据库中获取有关当前时间的信息, 然后返回到Web层。

回购的结构如下:

  • API:API层的代码
  • Web:Web层的代码
  • Terraform:使用Terraform进行架构流程的代码
  • Jenkins:用于CI / CD管道的Jenkins服务器的架构流程代码。

现在, 我们了解了需要部署的内容, 让我们讨论在AWS上部署此应用程序所需要做的事情, 然后我们将讨论如何将其纳入CI / CD管道。

Packer用法

由于我们将Terraform用于基础架构协调器, 因此为要部署的每个层或应用程序预烘焙映像是最有意义的。为此, 我们将使用Hashicorp的另一种产品, 即Packer。

Packer是一个开源工具, 可帮助构建将用于在AWS上部署的Amazon Machine Image或AMI。它可以用于为不同平台(例如EC2, VirtualBox, VMware等)构建映像。

这是有关如何使用Packer配置文件(terraform / packer-ami-api.json)为API层创建AMI的代码段。

{
 "builders": [{
    "type": "amazon-ebs", "region": "eu-west-1", "source_ami": "ami-844e0bf7", "instance_type": "t2.micro", "ssh_username": "ubuntu", "ami_name": "api-instance {{timestamp}}"
  }], "provisioners": [
    {
      "type": "shell", "inline": ["mkdir api", "sudo apt-get update", "sudo apt-get -y install npm nodejs-legacy"], "pause_before": "10s"
    }, {
      "type": "file", "source" : "../api/", "destination" : "api"
    }, {
    "type": "shell", "inline": ["cd api", "npm install"], "pause_before": "10s"
    }
  ]
}

并且你需要运行以下命令来创建AMI:

packer build -machine-readable packer-ami-api.json

我们将在本文稍后的Jenkins构建中运行此命令。以类似的方式, 我们还将在Web层上使用Packer配置文件(terraform / packer-ami-web.json)。

让我们看一下上面的Packer配置文件, 了解它的作用。

  1. 如前所述, Packer可用于为许多平台构建映像, 并且由于我们将应用程序部署到AWS, 因此我们将使用构建器” amazon-ebs”, 因为这是最容易上手的构建器。
  2. 配置的第二部分列出了预配置程序列表, 这些预配置程序更像脚本或代码块, 可用于配置映像。
    • 步骤1运行外壳配置程序以创建API文件夹, 并使用inline属性将Node.js安装在映像上, 该属性是你要运行的一组命令。
    • 步骤2运行文件供应器, 将我们的源代码从API文件夹复制到实例上。
    • 第3步再次运行Shell Provisioning, 但是这次使用script属性指定包含需要运行的命令的文件(terraform / scripts / install_api_software.sh)。
    • 步骤4将配置文件复制到Cloudwatch所需的实例, 该文件将在下一步中安装。
    • 步骤5运行外壳配置程序以安装AWS Cloudwatch代理。该命令的输入将是在上一步中复制的配置文件。我们将在本文后面详细讨论Cloudwatch。

因此, 从本质上讲, Packer配置包含有关所需的构建器的信息, 然后包含可根据需要配置映像的方式以任何顺序定义的预配器集。

设置Jenkins连续部署

接下来, 我们将研究设置一个Jenkins服务器, 该服务器将用于我们的CI / CD管道。我们还将使用Terraform和AWS进行设置。

设置Jenkins的Terraform代码位于jenkins / setup文件夹中。让我们看一下有关此设置的一些有趣的事情。

  1. AWS凭证:你可以向Terraform AWS提供程序(instance.tf)提供AWS访问密钥ID和秘密访问密钥, 也可以将凭证文件的位置提供给AWS提供程序中的shared_credentials_file属性。
  2. IAM角色:由于我们将从Jenkins服务器运行Packer和Terraform, 因此它们将在AWS上访问S3, EC2, RDS, IAM, 负载平衡和自动扩展服务。因此, 或者我们在Jenkins上提供用于Packer&Terraform的凭据以访问这些服务, 或者我们可以创建IAM配置文件(iam.tf), 并以此创建Jenkins实例。
  3. Terraform状态:Terraform必须在文件中的某个位置维护基础结构的状态, 并且使用S3(backend.tf), 你可以仅在其中维护它, 因此你可以与其他同事协作, 并且自该状态以来任何人都可以更改和部署维护在远程位置。
  4. 公钥/私钥对:你将需要与实例一起上传密钥对的公钥, 以便一旦启动就可以进入Jenkins实例。我们定义了aws_key_pair资源(key.tf), 你可以在其中使用Terraform变量指定公共密钥的位置。

设置詹金斯的步骤:

步骤1:为保持Terraform的远程状态, 你需要在S3中手动创建一个存储桶, 供Terraform使用。这将是Terraform之外唯一完成的步骤。确保在运行以下命令以指定你的AWS凭证之前运行AWS configure。

aws s3api create-bucket --bucket node-aws-jenkins-terraform --region eu-west-1 --create-bucket-configuration LocationConstraint=eu-west-1

第2步:运行Terraform初始化。这将初始化状态并将其配置为存储在S3上并下载AWS provider插件。

步骤3:执行Terraform Apply。这将检查所有Terraform代码并创建一个计划, 并显示完成此步骤后将创建多少资源。

步骤4:键入yes, 然后上一步将开始创建所有资源。命令完成后, 你将获得Jenkins服务器的公共IP地址。

步骤5:使用你的私钥将Ssh放入Jenkins服务器。 ubuntu是AWS EBS支持的实例的默认用户名。使用terraform apply命令返回的IP地址。

ssh -i mykey [email protected]

步骤6:通过访问http://34.245.4.73:8080, 启动Jenkins Web UI。可以在/ var / lib / jenkins / secrets / initialAdminPassword中找到该密码。

步骤7:选择”安装建议的插件”并为Jenkins创建一个Admin用户。

在Jenkins和Bitbucket之间设置CI管道

为此, 我们需要在Jenkins中安装Bitbucket插件。转到管理Jenkins→管理插件, 然后从可用插件中安装Bitbucket插件。

在Bitbucket存储库一侧, 转到设置→Webhooks, 添加一个新的webhook。该钩子会将存储库中的所有更改发送给Jenkins, 这将触发管道。

通过Bitbucker将Webhook添加到Jenkins连续部署中

Jenkins管道烘烤/构建图像

下一步将是在詹金斯中创建管道。

第一个管道将是一个Freestyle项目, 该项目将用于使用Packer构建应用程序的AMI。

你需要为Bitbucket存储库指定凭据和URL。

将凭证添加到Bitbucket

指定生成触发器。

配置构建触发器

添加两个构建步骤, 一个步骤用于为应用程序模块构建AMI, 而另一个步骤为Web模块构建AMI。

添加AMI构建步骤

完成此操作后, 你可以保存Jenkins项目, 现在, 当你将任何内容推送到Bitbucket存储库时, 它将触发Jenkins中的新构建, 这将创建AMI并将包含该Image的AMI号的Terraform文件推送到你可以在构建步骤的最后两行中看到S3存储桶。

echo 'variable "WEB_INSTANCE_AMI" { default = "'${AMI_ID_WEB}'" }' > amivar_web.tf
aws s3 cp amivar_web.tf s3://node-aws-jenkins-terraform/amivar_web.tf

Jenkins管道触发Terraform脚本

现在我们有了用于API和Web模块的AMI, 我们将触发一个构建以运行Terraform代码来设置整个应用程序, 然后再遍历Terraform代码中的组件, 这使该管道可以在服务停机时间为零的情况下部署更改。

我们创建另一个自由风格的Jenkins项目, nodejs-terraform, 它将运行Terraform代码来部署应用程序。

我们将首先在全局凭据域中创建一个”秘密文本”类型的凭据, 该凭据将用作Terraform脚本的输入。由于我们不想在Terraform和Git中对RDS服务的密码进行硬编码, 因此我们使用Jenkins凭据传递该属性。

创建用于Terraform ci cd的秘密

你需要像其他项目一样定义凭据和URL。

在构建触发器部分中, 我们将以某种方式将该项目与另一个项目链接, 以使该项目在上一个项目完成时开始。

将项目链接在一起

然后, 我们可以使用绑定配置先前添加到项目中的凭据, 以便在构建步骤中可用。

配置绑定

现在, 我们准备添加一个构建步骤, 该步骤将下载Terraform脚本文件(amivar_api.tf和amivar_web.tf), 该文件已由上一个项目上传到S3, 然后运行Terraform代码在AWS上构建整个应用程序。

添加构建脚本

如果一切配置正确, 现在如果将任何代码推送到Bitbucket存储库, 它将触发第一个Jenkins项目, 然后触发第二个项目, 并且应将应用程序部署到AWS。

适用于AWS的Terraform零停机时间配置

现在, 让我们讨论一下Terraform代码中的内容, 该内容使该管道可以在零停机时间内部署代码。

第一件事是Terraform为资源提供了这些生命周期配置块, 你可以在其中选择create_before_destroy作为标志, 这从字面上意味着Terraform在销毁当前资源之前应创建相同类型的新资源。

现在, 我们在aws_autoscaling_group和aws_launch_configuration资源中利用此功能。因此, aws_launch_configuration配置应配置哪种类型的EC2实例以及我们如何在该实例上安装软件, 而aws_autoscaling_group资源提供了一个AWS自动扩展组。

这里有趣的是, Terraform中的所有资源都应具有唯一的名称和类型组合。因此, 除非你为新的aws_autoscaling_group和aws_launch_configuration使用不同的名称, 否则将无法销毁当前名称。

Terraform通过为aws_launch_configuration资源提供name_prefix属性来处理此约束。定义此属性后, Terraform将为所有aws_launch_configuration资源添加一个唯一的后缀, 然后你可以使用该唯一名称来创建aws_autoscaling_group资源。

你可以在terraform / autoscaling-api.tf中检查以上所有代码

resource "aws_launch_configuration" "api-launchconfig" {
  name_prefix          = "api-launchconfig-"
  image_id             = "${var.API_INSTANCE_AMI}"
  instance_type        = "t2.micro"
  security_groups      = ["${aws_security_group.api-instance.id}"]

  user_data = "${data.template_file.api-shell-script.rendered}"

  iam_instance_profile = "${aws_iam_instance_profile.CloudWatchAgentServerRole-instanceprofile.name}"

  connection {
    user = "${var.INSTANCE_USERNAME}"
    private_key = "${file("${var.PATH_TO_PRIVATE_KEY}")}"
  }

  lifecycle {
    create_before_destroy = true
  }

}

resource "aws_autoscaling_group" "api-autoscaling" {
  name = "${aws_launch_configuration.api-launchconfig.name}-asg"

  vpc_zone_identifier  = ["${aws_subnet.main-public-1.id}"]
  launch_configuration = "${aws_launch_configuration.api-launchconfig.name}"
  min_size             = 2
  max_size             = 2
  health_check_grace_period = 300
  health_check_type = "ELB"
  load_balancers = ["${aws_elb.api-elb.name}"]
  force_delete = true

  lifecycle {
    create_before_destroy = true
  }

  tag {
    key = "Name"
    value = "api ec2 instance"
    propagate_at_launch = true
  }
}

零停机时间部署的第二个挑战是确保新部署已准备就绪, 可以开始接收请求。在某些情况下, 仅部署和启动新的EC2实例是不够的。

为了解决该问题, aws_launch_configuration具有一个属性user_data, 该属性支持本机AWS自动缩放user_data属性, 你可以使用该属性传递要在新实例启动时运行的任何脚本作为自动缩放组的一部分。在我们的示例中, 我们拖尾应用服务器的日志, 并等待启动消息出现。你还可以检查HTTP服务器并查看它们何时启动。

until tail /var/log/syslog | grep 'node ./bin/www' > /dev/null; do sleep 5; done

除此之外, 你还可以在aws_autoscaling_group资源级别上启用ELB检查, 这将确保在Terraform销毁旧实例之前, 已添加新实例以通过ELB检查。这就是ELB检查API层的样子。它检查/ api / status端点是否返回成功。

resource "aws_elb" "api-elb" {
  name = "api-elb"
  subnets = ["${aws_subnet.main-public-1.id}"]
  security_groups = ["${aws_security_group.elb-securitygroup.id}"]
  listener {
    instance_port = "${var.API_PORT}"
    instance_protocol = "http"
    lb_port = 80
    lb_protocol = "http"
  }
  health_check {
    healthy_threshold = 2
    unhealthy_threshold = 2
    timeout = 3
    target = "HTTP:${var.API_PORT}/api/status"
    interval = 30
  }

  cross_zone_load_balancing = true
  connection_draining = true
  connection_draining_timeout = 400
  tags {
    Name = "my-elb"
  }
}

摘要和后续步骤

因此, 这使我们到了本文的结尾。希望到现在为止, 你已经使用Jenkins部署和Terraform最佳实践已经部署了应用程序并以零停机CI / CD管道运行, 或者你可以更轻松地探索这一领域, 并且使部署所需的人工干预尽可能少可能。

在本文中, 所使用的部署策略称为”蓝绿色”部署, 其中, 我们有一个当前安装(蓝色), 它在部署和测试新版本(绿色)时会接收实时流量, 然后在新版本为准备好了除了此策略外, 还有其他方法可以部署应用程序, 本文”部署策略简介”对此进行了很好的解释。现在, 采用另一种策略就像配置Jenkins管道一样简单。

另外, 在本文中, 我假设API, Web和数据层的所有新更改都是兼容的, 因此你不必担心新版本与旧版本的通信。但是, 实际上, 情况并非总是如此。为了解决该问题, 在设计新发行版/功能时, 请始终考虑向后兼容性层, 否则你也需要调整部署以处理这种情况。

该部署管道中也缺少集成测试。由于你不希望任何内容未经测试就发布给最终用户, 因此在将这些策略应用于你自己的项目时, 绝对要牢记一点。

如果你对了解Terraform的工作方式以及如何使用该技术部署到AWS感兴趣, 我建议你使用Terraform AWS Cloud:Sane基础设施管理, 其中srcminierRadosławSzalski的同事介绍Terraform, 然后向你展示配置多平台服务所需的步骤。为团队提供环境和生产就绪的Terraform设置

赞(0)
未经允许不得转载:srcmini » 零停机Jenkins在AWS上使用Terraform进行连续部署

评论 抢沙发

评论前必须登录!