Azure DevOps(二)利用Azure DevOps Pipeline 構建基礎設施資源

一,引言

     上一篇文章記錄了利用 Azure DevOps 跨雲進行構建 Docker images,並且將構建好的 Docker Images 推送到 AWS 的 ECR 中。今天我們繼續講解 Azure DevOps 的 Pipeline,利用 Release Pipeline 實現 Terraform for AWS Infrastructure Resources 自動部署,我們的目標是將 images 部署到 AWS ECS 上。

-------------------- 我是分割線 --------------------

1,Azure DevOps(一)利用Azure DevOps Pipeline 構建應用程序鏡像到AWS ECR

2,Azure DevOps(二)利用Azure DevOps Pipeline 構建基礎設施資源

二,正文

1,Terraform Code 

根據之前利用 Terrraform 部署Azure 資源的時候,我們都知道需要將各個資源模塊劃分 Common Module。同樣的,我們當前需要部署的AWS的基礎設施資源也劃分出多個模塊,例如,"ECS","Security Group",“ELB”,“IAM”,“VPC” 等

整個項目 mian.tf 的入口,該文件中包含了各個模塊嵌套調用等

當前TF Code 中以及集成了 CodeDeploy 的Common Module 可以實現ECS 的藍綠部署,大家下載 TF 代碼後,可以自行魔改。

provider "aws" {
  region = "ap-northeast-1"
}

terraform {
  backend "s3" {
    region  = "ap-northeast-1"
    profile = "default"
  }
}

locals {
  container_name = "cnbateblogweb"
  name           = "cnbateblogwebCluster"
  service_name   = "cnbateblogwebservice"
  http_port      = ["80"]
  cidr_block     = "10.255.0.0/16"
  container_port = tonumber(module.alb.alb_target_group_blue_port)
}

module "securitygroup" {
  source                 = "../modules/securitygroup"
  enabled_security_group = true
  security_group_name    = "cnbateblogwebCluster_ecs_securitygroup"
  security_group_vpc_id  = module.vpc.vpc_id

  from_port_ingress = 9021
  to_port_ingress   = 9021

  from_port_egress = 0
  to_port_egress   = 0
}

# module "codedeploy" {
#   source                     = "../modules/codedeploy"
#   name                       = "example-deploy"
#   ecs_cluster_name           = local.name
#   ecs_service_name           = local.service_name
#   lb_listener_arns           = [module.alb.http_alb_listener_blue_arn]
#   blue_lb_target_group_name  = module.alb.aws_lb_target_group_blue_name
#   green_lb_target_group_name = module.alb.aws_lb_target_group_green_name

#   auto_rollback_enabled            = true
#   auto_rollback_events             = ["DEPLOYMENT_FAILURE"]
#   action_on_timeout                = "STOP_DEPLOYMENT"
#   wait_time_in_minutes             = 1440
#   termination_wait_time_in_minutes = 1440
#   test_traffic_route_listener_arns = []
#   iam_path                         = "/service-role/"
#   description                      = "This is example"

#   tags = {
#     Environment = "prod"
#   }
# }

module "ecs_fargate" {
  source           = "../modules/ecs"
  name             = local.name
  service_name     = local.service_name
  container_name   = local.container_name
  container_port   = local.container_port
  subnets          = module.vpc.public_subnet_ids
  security_groups  = [module.securitygroup.security_group_id]
  target_group_arn = module.alb.alb_target_group_blue_arn
  vpc_id           = module.vpc.vpc_id

  container_definitions = jsonencode([
    {
      name      = local.container_name
      image     = "693275195242.dkr.ecr.ap-northeast-1.amazonaws.com/cnbateblogweb:28" #"693275195242.dkr.ecr.ap-northeast-1.amazonaws.com/cnbateblogweb:28" #"docker.io/yunqian44/cnbateblogweb:laster"
      essential = true
      environment = [
        { name : "Location", value : "Singapore" },
        { name : "ASPNETCORE_ENVIRONMENT", value : "Production" }
      ]
      portMappings = [
        {
          containerPort = local.container_port
          protocol      = "tcp"
        }
      ]
    }
  ])

  desired_count                      = 1
  deployment_maximum_percent         = 200
  deployment_minimum_healthy_percent = 100
  deployment_controller_type         = "ECS"
  assign_public_ip                   = true
  health_check_grace_period_seconds  = 10
  platform_version                   = "LATEST"
  cpu                                = 256
  memory                             = 512
  requires_compatibilities           = ["FARGATE"]
  iam_path                           = "/service_role/"
  description                        = "This is example"
  enabled                            = true

  ecs_task_execution_role_arn = aws_iam_role.default.arn

  tags = {
    Environment = "prod"
  }
}

resource "aws_iam_role" "default" {
  name               = "iam-rol-ecs-task-execution"
  assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
}

data "aws_iam_policy_document" "assume_role_policy" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ecs-tasks.amazonaws.com"]
    }
  }
}

data "aws_iam_policy" "ecs_task_execution" {
  arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

resource "aws_iam_policy" "ecs_task_execution" {
  name   = aws_iam_role.default.name
  policy = data.aws_iam_policy.ecs_task_execution.policy
}

resource "aws_iam_role_policy_attachment" "ecs_task_execution" {
  role       = aws_iam_role.default.name
  policy_arn = aws_iam_policy.ecs_task_execution.arn
}

module "alb" {
  source                     = "../modules/elb"
  name                       = "elb-cnbateblogweb"
  vpc_id                     = module.vpc.vpc_id
  subnets                    = module.vpc.public_subnet_ids
  enable_https_listener      = false
  enable_http_listener       = true
  enable_deletion_protection = false

  enabled_lb_target_group_blue   = true
  aws_lb_target_group_blue_name  = "elb-cnbateblogweb-blue"
  health_check_path              = ""
  enabled_lb_target_group_green  = true
  aws_lb_target_group_green_name = "elb-cnbateblogweb-green"

  enable_http_listener_blue      = true
  http_port_blue                 = 80
  target_group_blue_port         = 9021
  enable_http_listener_rule_blue = true


  enable_http_listener_green      = true
  http_port_green                 = 8080
  target_group_green_port         = 8080
  enable_http_listener_rule_green = true
}

module "vpc" {
  source                    = "../modules/vpc"
  cidr_block                = local.cidr_block
  name                      = "ecs-fargate"
  public_subnet_cidr_blocks = [cidrsubnet(local.cidr_block, 2, 0), cidrsubnet(local.cidr_block, 2, 1)]
  public_availability_zones = data.aws_availability_zones.available.names
}

data "aws_caller_identity" "current" {}

data "aws_availability_zones" "available" {}

 

具體的各個模塊的Common Moudle 點擊文章底部的 github 鏈接

2,Azure DevOps 設置 Release Pipeline 

回到上一篇文章中創建好的 Azure DebOps 的項目中,docker images 的構築我們已經在CI 階段的 pipeline 中已經完成了。我們需要創建部署階段的 Release Pipeline

選擇 “CnBateBlogWeb_AWS” 的項目,選擇 “Release” 菜單,點擊 “New Pipeline”

選擇模板的時候,先點擊 “Empty”

修改當前階段的名稱

Stage name:”Deploy AWS ECS“

下一步,我們就得先添加 ”Artifacts“(工件),也就是我們寫的 TF 代碼

點擊 ”+Add“,選擇 TF 代碼源 ”GitHub“

Service:”Github_Connection44“

Source(repository):”yunqian44/AWS_ECS“(注意:大家選擇fork 到自己的github 倉庫名稱)

Default branch:“master”

Default version:“Latest from the default branch”

Source alias:“yunqian44_AWS_ECS”

點擊 “Add” 

點擊 “1 job,0 task” 鏈接爲部署階段添加新的任務

我們都知道,如果我們的基礎設施代碼的開發是多人協助,並且是需要保存狀態文件,那麼我們就必須得先在執行Terraform 代碼之前創建用於保存 terraform 狀態文件的S3,所以,我們得先添加 AWS CLI 來執行創建S3的動作。

點擊圖中 "Agent job" 旁圈中的 “+”,並在 task 的搜索框中輸入 “AWS CLI” ,點擊 “Add” 進行添加

添加相關參數用於執行AWS CLI 命令創建S3

Display name:“AWS CLI:Create S3 for Saving terraform state file”

AWS Credentials 選擇之前手動加的 AWS 的 ServiceConnection

AWS Region:“Asia Pacific(Tokyo)[ap-northeast-1]”

Command:“s3”

Subcommand:“mb”

Options and parameters:“s3://$(terraform_statefile)”  (注意:$(terraform_statefile) 是通過Pipleline Variable 來保存參數的)

接下來添加 Terraform 依賴SDK 的 task,搜索 “Terraform tool installer”,選擇添加當前任務

經過查詢,我們需要修改當前 Terraform 的版本

Version 版本改爲:“0.15.5”

接下來添加 Terraform 初始化的Task

Task 搜索框中搜索 “Terraform”,點擊 “Add”

修改相關參數:

Display name:“Terraform:aws init”

Provider 選擇:”aws“

Command:”init“

Configuration directory:選擇 Terraform 代碼執行的目錄

AWS backend configuration

aws service connection 大家可以選擇創建一個新的

Bucket:”$(terraform_statefile)“ 已通過Pipeline Variable 進行設置了

Key:”$(terraform_statefile_key)“ 已通過Pipeline Variable 進行設置了

接下來添加 Terraform 生成部署計劃的Task

修改相關參數:

Display name:”Terraform:aws plan“

Provider 選擇:”aws“

Command 選擇:”plan“

Configuration directory: 選擇 terraform 代碼的工作目錄

AWS Services connection 選擇剛剛新添加的 terraform 的後端服務連接

最後,我們添加執行 Terraform 部署計劃的 Task

Display name:”Terraform:aws auto-apply“

Provider:”aws“

Command:”validate and apply“

Configuration directory: 選擇 terraform 代碼的工作目錄

Additional command arguments:”-auto-approve“

AWS Services connection 選擇剛剛新添加的 terraform 的後端服務連接

修改當前 Release Pipeline 的名稱,點擊 ”Save“

關於pipeline 的觸發條件,我就不進行設置了,也不是今天演示的目的。

3,測試 Azure DevOps 自動化部署基礎設施資源

手動觸發 Pipeline,點擊 ”Create release“ 進行手動觸發

點擊 ”Create“

等待整個Pipeline 的執行完成,我們可以看到日誌整個的輸出,併成功進行部署

接下來,我們需要登陸到 AWS Console  查看資源創建的狀況,ECS 運行成功

 找到利用 Terraform 構築好的ELB,複製 dns 名進行訪問

Bingo!!!!! 成功。大功告成!!!!!

🎉🎉🎉🎉🎉!!!!

三,結尾

今天的實際操作比較多,大多都是集中在Azure DevOps 上,我們都是需要在Azure DevOps 上進行操作的,大家要多加練習。至於 Terraform 代碼方面沒有過多的講解,主要是因爲結合之前部署Azure 資源,大家都會Terraform 有了一定的理解了。所以大家可以自行下載,進行分析修改。

參考資料:Terraform 官方Terraform For AWS 文檔AWS CLI 文檔

AWS_ECS github:https://github.com/yunqian44/AWS_ECS

文章來自博主本人自己的博客:https://allenmasters.com/post/2021/6/8/azure-devopsazure-devops-pipeline

歡迎大家關注博主的博客:https://allenmasters.com/

作者:Allen 

版權:轉載請在文章明顯位置註明作者及出處。如發現錯誤,歡迎批評指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章