作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Gaurav Kohli的头像

Gaurav Kohli

Gaurav有12年以上的开发经验, scrum master, senior consultant, and product owner.

Previously At

Booking.com
Share

在当今的互联网世界里,几乎所有的东西都需要全天候运行,可靠性是关键. 这意味着您的网站接近零停机时间, 避开可怕的“Not found: 404”错误页面, 或者在推出最新版本时出现其他服务中断.

假设您已经为客户机构建了一个新的应用程序, or maybe yourself, 并设法获得一个喜欢你的应用程序的良好用户基础. 你已经收集了用户的反馈, 你去找你的开发人员,让他们构建新的功能,让应用程序为部署做好准备. With that ready, 您可以停止整个应用程序并部署新版本,也可以构建一个零停机CI/CD部署管道,它将完成向用户推送新版本的所有繁琐工作,而无需人工干预.

In this article, 我们将具体讨论后者, 我们如何在Node中构建一个三层web应用程序的持续部署管道.使用Terraform作为基础设施编排器. We’ll be using Jenkins 用于持续部署部分和Bitbucket托管我们的代码库.

Code Repository

我们将使用一个演示的三层web应用程序,你可以找到它的代码 here.

repo包含了web层和API层的代码. 这是一个简单的应用程序,其中web模块调用API层中的一个端点,该端点内部从数据库中获取有关当前时间的信息并返回给web层.

回购的结构如下:

  • API: API层的代码
  • Web: web层的代码
  • Terraform: 使用Terraform进行基础设施编排的代码
  • Jenkins: 用于CI/CD管道的Jenkins服务器的基础设施编排器代码.

现在我们了解了需要部署的内容, 让我们先讨论一下在AWS上部署这个应用程序需要做的事情,然后再讨论如何使它成为CI/CD管道的一部分.

Baking Images

因为我们正在使用Terraform作为基础设施编排器, 为想要部署的每个层或应用程序都预置映像是最有意义的. 为此,我们将使用hashicorp的另一种产品——Packer.

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

以下是Packer配置文件(起程拓殖/ packer-ami-api.json)用于为API层创建AMI.

{
 "builders": [{
    “类型”:“amazon-ebs”,
    “地区”:“一来”,
    “source_ami”:“ami - 844 e0bf7”,
    :“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”:“10”
    },
    {
      "type": "file",
      "source" : "../api/",
      "destination": "api"
    },

    {
    "type": "shell",
    "inline": ["cd api", "npm install"],
    “pause_before”:“10”
    }
  ]
}

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

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

我们将在本文后面的Jenkins构建中运行这个命令. 类似地,我们将使用Packer配置文件(起程拓殖/ packer-ami-web.json),也适用于web层.

让我们浏览一下上面的Packer配置文件,并了解它试图做什么.

  1. 如前所述,Packer可用于为以下对象构建映像 many platforms, 由于我们将应用程序部署到AWS,我们将使用构建器“amazon-ebs”,,因为这是最容易上手的构建器.
  2. 配置的第二部分是提供程序列表,这些提供程序更像是脚本或代码块,您可以使用它们来配置映像.
    • Step 1 运行shell提供程序来创建API文件夹并安装Node.. Js在图像上使用 inline 属性,它是要运行的一组命令.
    • Step 2 运行一个文件提供程序,将我们的源代码从API文件夹复制到实例.
    • Step 3 再次运行shell提供程序,但这次使用脚本属性指定文件(terraform/scripts/install_api_software).Sh),其中包含需要运行的命令.
    • Step 4 将配置文件复制到Cloudwatch所需的实例, 下一步安装哪个.
    • Step 5 运行shell提供程序来安装AWS Cloudwatch代理. 该命令的输入将是在前一步中复制的配置文件. 我们将在本文后面详细讨论Cloudwatch.

So, in essence, Packer配置包含有关您想要的构建器的信息,然后是一组提供程序,您可以根据希望如何配置映像以任何顺序定义这些提供程序.

设置Jenkins持续部署

接下来,我们将研究如何设置用于CI/CD管道的Jenkins服务器. 我们也将使用Terraform和AWS进行设置.

用于设置Jenkins的Terraform代码在文件夹中 jenkins/setup. 让我们来看看关于这个设置的一些有趣的事情.

  1. AWS credentials: 您可以向Terraform AWS提供商提供AWS访问密钥ID和秘密访问密钥(instance.tf),也可以将凭据文件的位置提供给属性 shared_credentials_file in the AWS provider.
  2. IAM role: 因为我们将在Jenkins服务器上运行Packer和Terraform, 他们会访问S3, EC2, RDS, IAM, load balancing, 以及AWS上的自动伸缩服务. 所以要么我们为帕克提供詹金斯的证据 & Terraform来访问这些服务,或者我们可以创建IAM Profile (iam.tf),使用它我们将创建一个Jenkins实例.
  3. Terraform state: Terraform必须在某个文件中维护基础设施的状态,并且使用S3 (backend.tf), 你可以把它维持在那里, 这样你就可以和其他同事合作了, 任何人都可以更改和部署,因为状态是在远程位置维护的.
  4. 公私钥对: 您需要将密钥对的公钥与实例一起上传,以便在Jenkins实例启动后可以ssh到它. We have defined an aws_key_pair resource (key.tf),其中使用Terraform变量指定公钥的位置.

设置Jenkins的步骤:

Step 1: 为了保持Terraform的远程状态, 您需要在S3中手动创建一个bucket,供Terraform使用. 这将是在terrraform之外完成的唯一步骤. Make sure you run AWS configure 然后运行下面的命令来指定您的AWS凭证.

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

Step 2: Run terraform init. 这将初始化状态并将其配置为存储在S3上,并下载AWS提供商插件.

Step 3: Run terraform apply. 这将检查所有Terraform代码并创建一个计划,并显示在此步骤完成后将创建多少资源.

Step 4: Type yes,然后上一步将开始创建所有资源. 命令执行完毕后,您将获得Jenkins服务器的公共IP地址.

Step 5: 使用您的私钥Ssh到Jenkins服务器. ubuntu 是AWS ebs支持实例的默认用户名吗. 返回的IP地址 terraform apply command.

SSH -i mykey ubuntu@34.245.4.73

Step 6: 执行以下命令启动Jenkins web UI http://34.245.4.73:8080. 密码可在 /var/lib/jenkins/secrets/initialAdminPassword.

Step 7: 选择“安装建议的插件”并为Jenkins创建一个Admin用户.

设置Jenkins和Bitbucket之间的CI管道

  1. 为此,我们需要在Jenkins中安装Bitbucket插件. Go to 管理Jenkins→管理插件 and from Available plugins install the Bitbucket plugin.
  2. 在Bitbucket回购端,转到 Settings → Webhooks, add a new webhook. 这个钩子将把存储库中的所有更改发送给Jenkins,这将触发管道.
    通过Bitbucker为Jenkins的持续部署添加webhook

Jenkins管道烘烤/构建图像

  1. 下一步将是在Jenkins中创建管道.
  2. 第一个管道将是一个Freestyle项目,它将用于使用Packer构建应用程序的AMI.
  3. 您需要为Bitbucket存储库指定凭据和URL.
    向bitbucket添加凭据
  4. 指定构建触发器.
    配置构建触发器
  5. Add two build steps, 一个用于构建应用模块的AMI,另一个用于构建web模块的AMI.
    添加AMI构建步骤
  6. Once this is done, 你可以保存Jenkins项目,现在, 当您将任何内容推送到Bitbucket存储库时, 它将触发Jenkins中的新构建,该构建将创建AMI并将包含该映像的AMI编号的Terraform文件推送到S3 bucket,您可以从构建步骤的最后两行中看到.
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管道触发地形脚本

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

  1. 我们创建了另一个freestyle Jenkins项目, nodejs-terraform,这将运行Terraform代码来部署应用程序.
  2. 我们将首先在全局凭据域中创建一个“秘密文本”类型的凭据, 哪些将被用作Terraform脚本的输入. 因为我们不想在Terraform和Git中硬编码RDS服务的密码, 我们使用Jenkins凭证传递该属性.
    创建一个秘密用于Terraform ci cd
  3. 您需要定义与其他项目类似的凭据和URL.
  4. 在构建触发器部分中, 我们将这个项目与另一个以某种方式,使这个项目开始时,前一个完成.
    将项目连接在一起
  5. 然后,我们可以配置之前使用绑定添加到项目中的凭据, 所以它在构建步骤中是可用的.
    配置绑定
  6. 现在我们准备添加一个构建步骤,它将下载Terraform脚本文件(amivar_api.tf and amivar_web.tf),然后运行Terraform代码,在AWS上构建整个应用程序.
    添加构建脚本

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

针对AWS的Terraform零停机配置

现在让我们讨论一下terrform代码中是什么使这个管道零停机地部署代码.

首先,Terraform提供了这些 生命周期配置模块 for resources 你有一个选择 create_before_destroy 作为一个标志,字面意思是terrraform应该在破坏当前资源之前创建一个相同类型的新资源.

现在我们在 aws_autoscaling_group and aws_launch_configuration resources. So aws_launch_configuration 配置应该供应哪种类型的EC2实例,以及如何在该实例上安装软件, and the aws_autoscaling_group 资源提供AWS自动伸缩组.

这里有一个有趣的问题,Terraform中的所有资源都应该有一个唯一的名称和类型组合. 所以除非你给新的起个不同的名字 aws_autoscaling_group and aws_launch_configuration,就不可能摧毁现在的那个.

Terraform通过提供 name_prefix property to the aws_launch_configuration resource. 一旦这个属性被定义,Terraform将添加一个唯一的后缀到所有的 aws_launch_configuration 资源,然后您可以使用该唯一名称来创建 aws_autoscaling_group resource.

您可以检查上述所有内容的代码 起程拓殖/ autoscaling-api.tf

资源"aws_launch_configuration" "api-launchconfig" {
  Name_prefix = "api-launchconfig-"
  Image_id =“${var.API_INSTANCE_AMI}"
  Instance_type = "t2.micro"
  Security_groups = ["${aws_security_group . cn.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 = "${文件" ("${var . key ".PATH_TO_PRIVATE_KEY} ")}”
  }

  lifecycle {
    Create_before_destroy = true
  }

}

资源"aws_autoscaling_group" "api-autoscaling" {
  名称=“${aws_launch_configuration . conf”.api-launchconfig.name}-asg"

  Vpc_zone_identifier = ["${aws_子网.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_balancer = ["${aws_elb.api-elb.name}"]
  force_delete = true

  lifecycle {
    Create_before_destroy = true
  }

  tag {
    key = "Name"
    值= "api ec2实例"
    Propagate_at_launch = true
  }
}

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

为了解决这个问题, aws_launch_configuration has a property user_data 支持原生AWS自动扩展 user_data 属性,您可以使用该属性传递希望在启动新实例时作为自动伸缩组的一部分运行的任何脚本. 在我们的示例中,我们跟踪应用服务器的日志并等待启动消息. 您还可以检查HTTP服务器并查看它们何时启动.

直到tail /var/log/syslog | grep '节点 ./bin/www' > /dev/null; do sleep 5; done

此外,您还可以启用ELB检查 aws_autoscaling_group resource level, 在Terraform销毁旧的实例之前,如何确保新实例通过ELB检查. This is how the ELB check for API layer looks like; it checks for the /api/status 返回成功的端点.

资源"aws_elb" "api-elb" {
  name = "api-elb"
  子网= ["${aws_子网.main-public-1.id}"]
  Security_groups = ["${aws_security_group . cn.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 /地位”
    interval = 30
  }

  Cross_zone_load_balancing = true
  connection_drain = true
  Connection_draining_timeout = 400
  tags {
    Name = "my-elb"
  }
}

总结和后续步骤

So this brings us to the end of this article; hopefully, by now, 要么您已经部署了应用程序,并使用Jenkins部署和Terraform最佳实践使用零停机CI/CD管道运行,要么您更愿意探索这一领域,并使您的部署尽可能少地需要人工干预.

In this article, 所使用的部署策略称为蓝绿部署,其中我们有一个当前安装(蓝),它在我们部署和测试新版本(绿)时接收实时流量,然后在新版本准备就绪后替换它们. 除了这个策略, 部署应用程序还有其他方法, 这篇文章很好地解释了这一点, 部署策略简介. 现在,调整另一种策略就像配置Jenkins管道一样简单.

Also, in this article, 我假定所有API的新变化, web, 数据层是兼容的,所以你不必担心新版本会和旧版本对话. 但在现实中,情况可能并不总是如此. 为了解决这个问题, 在设计新版本/功能时, 始终考虑向后兼容层,否则您将需要调整部署来处理这种情况.

集成测试也是这个部署管道中缺少的东西. 因为你不希望任何东西在没有经过测试的情况下就发布给最终用户, 在将这些策略应用于自己的项目时,一定要记住这一点.

如果您有兴趣了解更多关于Terraform的工作原理以及如何使用该技术部署到AWS, I recommend Terraform AWS Cloud: Sane基础设施管理 在那里,同事Toptaler Radosław Szalski解释了Terraform,然后向您展示了为团队配置多环境和生产就绪的Terraform设置所需的步骤

了解基本知识

  • What is Terraform?

    Terraform是一个工具,它可以很容易地编写版本控制的基础设施代码. 您可以使用它在100多个不同的服务提供商(如AWS)上编排基础设施, Alicoud, GCP, Azure, OpenStack等等.

  • 什么是CI/CD ?它的好处是什么?

    持续集成和持续部署是一种实践,您可以在每次代码更改时集成和测试您的软件. 稍后,将该代码部署到生产环境中.

    它的主要好处是减少了部署期间的手工工作和人为错误的机会.

  • 有哪些不同的部署策略?

    这取决于你的产品和你的技术实现, 你可以选择滚动策略, recreate strategy, blue-green, A/B testing, canary deployment, or shadow strategy.

  • What is Packer?

    Packer是一个工具,它可以很容易地为不同的平台(如AWS EC2)构建机器映像, Virtual Box, and VMWare.

  • 你能拿詹金斯怎么办?

    Jenkins是一个持续集成工具,它使软件团队能够为他们的项目构建集成管道. 您可以定制jenkins驱动的管道,以包含不同的软件开发过程,比如构建, testing, 对代码进行分段和静态分析.

  • Terraform是一种语言吗?

    No. Terraform is a tool, 它反过来使用Hashicorp配置语言(HCL)将基础设施描述为代码. HCL是一种声明性语言,定义所需的状态,而不是实现所需的步骤.

就这一主题咨询作者或专家.
Schedule a call
Gaurav Kohli的头像
Gaurav Kohli

Located in 荷兰阿姆斯特丹

Member since November 9, 2018

About the author

Gaurav有12年以上的开发经验, scrum master, senior consultant, and product owner.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Previously At

Booking.com

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.