基于AWS CodeDeploy实现Golang API就地部署实践

6,435 阅读7分钟

一 背景

自动化代码部署以确保应用程序正常运行时间

Amazon CodeDeploy 是一项完全托管的部署服务,可自动将软件部署到计算服务,例如 Amazon EC2、Amazon Lambda 以及您的本地服务器。借助 Amazon CodeDeploy,您可以更轻松地快速发布新功能、避免在应用程序部署过程中出现停机,并简化应用程序的更新工作。

二 概述

AWS CodeDeploy旨在帮助用户完成应用的快速部署,按照用户指定的策略将代码部署在一组EC2服务器上。用户策略可以包括集群部署速度、部署事件通知、警报处理策略等。此外,CodeDeploy还可以和弹性负载均衡(Elastic Load Balancer)、自动扩展组(Auto Scaling Group)等服务结合,完成无缝升级和动态部署。

三 相关概念

为方便有效地组织部署任务,CodeDeploy设立了三个概念:应用(Application)、部署(Deployment),以及部署配置(Deployment Configuration)。

3.1 应用程序(Application)

应用程序是部署的核心,由部署组(Deployment Group)和代码修订(Revisions)组成。一个应用可以包含多个部署组,一个部署组又可以包含多台EC2服务器。同时,一个服务器也可以属于多个部署组,因为一个服务器可能同时运行多个应用。

3.1.1 部署组

创建或修改部署组时,如果添加EC2服务器,可以通过标签(Tag)对已有的EC2服务器进行筛选。所以,在创建EC2时一定要打上标签(Tag),便于在创建应用的部署组时找到对应业务的服务器。

此外,部署组还可以添加自动扩展组(Auto Scaling Group),以及用户自己机房的主机(On-Premise Instance)。

3.1.2 代码修订

代码修订保存了当前应用涉及到得所有代码,代码的存放位置可以在S3或Github。

如果用户自建代码托管,当需要部署时,可以在工作机上同步代码到本地,然后使用AWS命令行进行打包上传。

上面的命令可以将运行目录下得代码打包上传到S3,同时显示在关联应用的代码修订一栏中。

3.2 部署(Deployment)

每一次部署都有唯一的ID标记,并保存所有信息,如代码来源、部署时间、目标服务器、部署结果等。并且针对每一台服务器,都可以详细查看部署过程中的事件(如下载程序、安装前检查、 程序启动、安装后检查等7个事件),以便追踪部署的各个步骤。当部署出错时,可以快速定位和排查。

3.3 部署配置

部署配置存放了一次部署的服务器台数或百分比,在发起部署时需要指定所需配置。CodeDeploy默认提供了三种配置:一次部署一台、一次部署一半数量的服务器,以及一次完成全部部署。部署发起后,CodeDeploy会按照上述策略进行工作,指导完成部署组内全部服务器的更新。

四 实现流程

4.1 架构图

4.2 实现流程

  1. 创建应用程序,标识部署应用程序版本及计算平台。
  2. 指定部署类型配置部署组,可以指定应用于实例的标签、Amazon EC2 Auto Scaling组名,需在实例中安装并正常运行CodeDeploy。
  3. 指定部署配置,指定您的应用程序需同时部署多少实例,及部署成功和失败条件。
  4. 应用程序需通过CodeBuild或其他方式提前上传至Amazon S3或Github,应用程序需保护部署期间用到的脚本,还需要包含应用程序AppSpec文件。
  5. 将应用程序修订部署到部署组。
  6. 检查部署结果。

五 实战

5.1 手动创建AWS资源组

资源组 LB,EC2资源组

  • 创建VPC

利用terraform对VPC进行编排

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.62"    
    }
  }
}

provider "aws" {
    region = "cn-north-1"
}

locals {
  xuel_tag = {
    application = "xuel_app_test"
    environment = "dev"
    purpose = "person test"
    contact = "xuel@anchnet.com"
    creator = "xuelei"
    role = "cloud manager"
    owner = "xuel"
    project = "aws_partner_project"
    team = "smartops"
    organization = "mse"
    company = "anchnet"
  }
}
resource "aws_vpc" "main" {
  cidr_block       = "10.0.0.0/16"
  instance_tenancy = "default"  
  tags = local.xuel_tag
}

variable "public_subnet_cidrs" {
 type        = list(string)
 description = "Public Subnet CIDR values"
 default     = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}
 
variable "private_subnet_cidrs" {
 type        = list(string)
 description = "Private Subnet CIDR values"
 default     = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
}


variable "azs" {
 type        = list(string)
 description = "Availability Zones"
 default     = ["cn-north-1a", "cn-north-1b", "cn-north-1d"]
}

resource "aws_subnet" "public_subnets" {
 count             = length(var.public_subnet_cidrs)
 vpc_id            = aws_vpc.main.id
 cidr_block        = element(var.public_subnet_cidrs, count.index)
 availability_zone = element(var.azs, count.index)
 
  tags = local.xuel_tag
}
 
resource "aws_subnet" "private_subnets" {
 count             = length(var.private_subnet_cidrs)
 vpc_id            = aws_vpc.main.id
 cidr_block        = element(var.private_subnet_cidrs, count.index)
 availability_zone = element(var.azs, count.index)
 
  tags = local.xuel_tag
}

resource "aws_internet_gateway" "gw" {
 vpc_id = aws_vpc.main.id
 
  tags = local.xuel_tag
}

resource "aws_route_table" "second_rt" {
 vpc_id = aws_vpc.main.id
 
 route {
   cidr_block = "0.0.0.0/0"
   gateway_id = aws_internet_gateway.gw.id
 }
 
  tags = local.xuel_tag
}

resource "aws_route_table_association" "public_subnet_asso" {
 count = length(var.public_subnet_cidrs)
 subnet_id      = element(aws_subnet.public_subnets[*].id, count.index)
 route_table_id = aws_route_table.second_rt.id
}

  • 创建EC2并配置
  1. 创建EC2实例,由于需要安装codedeployAgent,选择EC2实例为公有子网,为其分配公网IP

由于后期EC2实例需要从S3中获取build好的部署文件,需要附加一个具备S3读权限的IAM用户。

创建或修改部署组时,如果添加EC2服务器,可以通过标签(Tag)对已有的EC2服务器进行筛选。所以,在创建EC2时一定要打上标签(Tag),便于在创建应用的部署组时找到对应业务的服务器。

  1. 为EC2实例安装codedeployAgent

仅当您部署到 EC2/本地计算平台时,才需要CodeDeploy代理。使用亚马逊 ECS 或 AWS Lambda 计算平台的部署不需要代理。CodeDeploy代理通过端口443使用HTTPS进行出站通信,因此需要安全组放开对应端口。其次需要选择CodeDeploy支持的EC2镜像。

CodeDeploy详细介绍:docs.aws.amazon.com/zh_cn/coded…

安装CodeDeploy:docs.aws.amazon.com/zh_cn/coded…

sudo yum update
sudo yum install ruby -y 
sudo yum install wget -y
#!/bin/bash
CODEDEPLOY_BIN="/opt/codedeploy-agent/bin/codedeploy-agent"
$CODEDEPLOY_BIN stop
yum erase codedeploy-agent -y
cd /home/ec2-user

wget https://aws-codedeploy-us-east-2.s3.us-east-2.amazonaws.com/latest/install

chmod +x ./install
sudo ./install auto

由于后期部署应用的端口为API服务8000,所以EC2安全组需要放开8000端口

5.2 创建目标群组

目标群组为负载均衡组,主要为CodeDeploy部署的后端组,

创建目标组,检查为8000端口,确保主机存活

注册主机

如果目标组未关联LB则为unused

创建LB并关联

创建NLB

关联LB后,查看目标组为正常

至此,后端组创建完成。

5.3 创建CodeDeploy

5.3.1 创建应用程序

5.3.2 配置源信息

源程序需要除了包含构建好的可执行文件外,还需要应用程序的生命周期脚本函数,其次需要AppSpec文件描述应用程序信息,最终将源应用程序上传至对象存储S3中,以供后续CodeDeploy使用。

main文件为构建好的Golang 二进制可执行程序,以.sh结尾的为应用程序的生命周期函数,用于在EC2实例上进行部署。

appspec.yml为描述应用程序的配置文件,在此将源文件移动到/opt/golang-api/目录下,之后执行一系列hooks脚本,实现应用程序生命周期管理。

version: 0.0 
os: linux 
files:
  - source: / 
    destination: /opt/golang-api/

hooks:
  ApplicationStart:
    - location: deploy.sh
      timeout: 10
  ValidateService:
    - location: validate.sh
      timeout: 10

  ApplicationStop:
  - location: stop.sh
    timeout: 10

将文件打包后上传至对象存储。

tar -zcvf mainv4.tar.gz build/

复制应用程序的S3 URL供后期使用。

5.3.3 创建部署组

在应用程序内创建部署组

需要创建输入具有授权 Amazon CodeDeploy 访问您的目标实例的 CodeDeploy 权限的服务角色。

需要后期访问ec2资源,授权ec2访问权限

创建部署组

在此指定了目标主机需要添加tag,app=xuel-codedeploy

为EC2主机打上标签

创建部署,选取刚才创建的部署组,选取修订位置,改文件为之前build好的golang api,内部包含服务安装appsec.yaml

查看部署日志

登录EC2主机查看进程,查看进程已经正常运行

六 测试

通过postman进行测试,获取LB地址,在代码中发送GET请求,返回API 版本,发送POST请求并传递json请求题,返回json请求体

发送GET请求

发送POST请求

至此就已成功利用CodeDeploy部署实现golang API部署。

七 注意事项

  • 本实践中,在之前codebuild进行了构建,将公上传至S3中,仅在codedeploy中进行部署,也可以在codedeploy中进行构建然后再继续执行部署。

八 反思

  • 目前使用就地部署,未使用蓝绿部署;
  • 为使用CodeBuild构建出制品;
  • 容器部署;
  • 利用CodePipeline将CodeBuild和CodeDeploy联和起来。

参考链接