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

Grant_Allen發表於2021-06-08

一,引言

     上一篇文章記錄了利用 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” 等

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

整個專案 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”

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

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

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

修改當前階段的名稱

Stage name:”Deploy AWS ECS“

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

下一步,我們就得先新增 ”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” 

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

點選 “1 job,0 task” 連結為部署階段新增新的任務

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

我們都知道,如果我們的基礎設施程式碼的開發是多人協助,並且是需要儲存狀態檔案,那麼我們就必須得先在執行Terraform 程式碼之前建立用於儲存 terraform 狀態檔案的S3,所以,我們得先新增 AWS CLI 來執行建立S3的動作。

點選圖中 "Agent job" 旁圈中的 “+”,並在 task 的搜尋框中輸入 “AWS CLI” ,點選 “Add” 進行新增

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

新增相關引數用於執行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 來儲存引數的)

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

接下來新增 Terraform 依賴SDK 的 task,搜尋 “Terraform tool installer”,選擇新增當前任務

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

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

Version 版本改為:“0.15.5”

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

接下來新增 Terraform 初始化的Task

Task 搜尋框中搜尋 “Terraform”,點選 “Add”

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

修改相關引數:

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 進行設定了

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

接下來新增 Terraform 生成部署計劃的Task

修改相關引數:

Display name:”Terraform:aws plan“

Provider 選擇:”aws“

Command 選擇:”plan“

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

AWS Services connection 選擇剛剛新新增的 terraform 的後端服務連線

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

最後,我們新增執行 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 的後端服務連線

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

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

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

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

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

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

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

點選 ”Create“

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

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

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

接下來,我們需要登陸到 AWS Console  檢視資源建立的狀況,ECS 執行成功

 Azure DevOps(二)利用Azure DevOps Pipeline 構建基礎設施資源找到利用 Terraform 構築好的ELB,複製 dns 名進行訪問

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

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

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 

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

相關文章