ОПИСАНИЕ
В этом посте я собираюсь объяснить, как построить инфраструктуру на AWS с Terraform для реализации конвейера CI/CD для ECS/Fargate. Архитектура состоит из VPC с двумя публичными подсетями в различных зонах доступности. Желаемые задачи 2, и каждая задача развернута в каждой публичной подсети с FARGATE, и каждая задача принадлежит одному и тому же сервису ECS. Балансировщик нагрузки приложения используется для сбалансировки нагрузки между двумя задачами. В этом случае основная цель состоит в том, чтобы реализовать контейнер Docker, который содержит простой HTTP -сервер, построенный с Golang. Этот HTTP -сервер позволяет получить частную IP -адреса каждой задачи. Если я введу новые изменения в репозиторий CodeCommit, CodePipeline обнаруживает эти изменения, запускает трубопровод и создает новое изображение Docker, а затем развертывает его в службе ECS для обновления задач.
АРХИТЕКТУРА
РЕСУРСЫ
https://github.com/erozedguy/CICD-Pipeline-for-Amazon-ECS-Fargate
Шаги
Шаг 01 — Создайте роль IAM и учетные данные CodeCommit
Создайте роль услуги для задачи службы Elastic Container ( Позволяет ECS -задачам вызовать службы AWS от вашего имени.)
Прикрепить
Awscodecommitpoweruser
Политика моемуПОЛЬЗОВАТЕЛЬ
Генерировать
HTTPS GIT учетные данные для AWS Codecommit
кклон
,push
,тянуть
кКодекс -репозиторий
Шаг 02: Сценарии терраформ для строительства инфраструктуры
Поставщики
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 3.51" } } } provider "aws" { profile = "default" region = "us-east-1" }
VPC
VPC Script
имеет VPC
, Подсети
и Интернет -шлюз
Ресурсы.
resource "aws_vpc" "ecs-vpc" { cidr_block = "${var.cidr}" tags = { Name = "ecs-vpc" } } # PUBLIC SUBNETS resource "aws_subnet" "pub-subnets" { count = length(var.azs) vpc_id = "${aws_vpc.ecs-vpc.id}" availability_zone = "${var.azs[count.index]}" cidr_block = "${var.subnets-ip[count.index]}" map_public_ip_on_launch = true tags = { Name = "pub-subnets" } } # INTERNET GATEWAY resource "aws_internet_gateway" "i-gateway" { vpc_id = "${aws_vpc.ecs-vpc.id}" tags = { Name = "ecs-igtw" } }
Переменные для VPC
variable "cidr" { type = string default = "145.0.0.0/16" } variable "azs" { type = list(string) default = [ "us-east-1a", "us-east-1b" ] } variable "subnets-ip" { type = list(string) default = [ "145.0.1.0/24", "145.0.2.0/24" ] }
IAM Роли и политика
Для Кодовая строительство
необходим для создания роли и политики IAM, чтобы обеспечить доступ к ECR
Чтобы толкнуть и тянуть Docker Images
в репозитории ECR. Кроме того, необходимо разрешение на доступ к ведро S3 для хранения Артефакты
Анкет
resource "aws_iam_role" "codebuild-role" { name = "codebuild-role" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = "sts:AssumeRole" Effect = "Allow" Principal = { Service = "codebuild.amazonaws.com" } }, ] }) } resource "aws_iam_role_policy" "codebuild-policy" { role = "${aws_iam_role.codebuild-role.name}" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Action = ["codecommit:GitPull"] Effect = "Allow" Resource = "*" }, { Action = [ "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "ecr:CompleteLayerUpload", "ecr:GetAuthorizationToken", "ecr:InitiateLayerUpload", "ecr:PutImage", "ecr:UploadLayerPart"] Effect = "Allow" Resource = "*" }, { Action = [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"] Effect = "Allow" Resource = "*" }, { Action = [ "s3:PutObject", "s3:GetObject", "s3:GetObjectVersion", "s3:GetBucketAcl", "s3:GetBucketLocation"] Effect = "Allow" Resource = "*" } ] }) }
Маршрутные столы
Одиночный Таблица маршрутов
Для обеих публичных подсет
# TABLE FOR PUBLIC SUBNETS resource "aws_route_table" "pub-table" { vpc_id = "${aws_vpc.ecs-vpc.id}" } resource "aws_route" "pub-route" { route_table_id = "${aws_route_table.pub-table.id}" destination_cidr_block = "0.0.0.0/0" gateway_id = "${aws_internet_gateway.i-gateway.id}" } resource "aws_route_table_association" "as-pub" { count = length(var.azs) route_table_id = "${aws_route_table.pub-table.id}" subnet_id = "${aws_subnet.pub-subnets[count.index].id}" }
Группы безопасности
Первая группа секунды для Служба ECS
resource "aws_security_group" "sg1" { name = "golang-server" description = "Port 5000" vpc_id = aws_vpc.ecs-vpc.id ingress { description = "Allow Port 5000" from_port = 5000 to_port = 5000 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } egress { description = "Allow all ip and ports outboun" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }
Вторая секундовая группа для Приложение нагрузка балансировщика
resource "aws_security_group" "sg2" { name = "golang-server-alb" description = "Port 80" vpc_id = aws_vpc.ecs-vpc.id ingress { description = "Allow Port 80" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] ipv6_cidr_blocks = ["::/0"] } egress { description = "Allow all ip and ports outboun" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }
Приложение нагрузка балансировщика
resource "aws_lb" "app-lb" { name = "app-lb" internal = false load_balancer_type = "application" security_groups = [aws_security_group.sg2.id] subnets = ["${aws_subnet.pub-subnets[0].id}", "${aws_subnet.pub-subnets[1].id}"] }
Порт № 5000 используется в Целевая группа
Потому что этот порт используется для контейнера
resource "aws_lb_target_group" "tg-group" { name = "tg-group" port = "5000" protocol = "HTTP" vpc_id = "${aws_vpc.ecs-vpc.id}" target_type = "ip" }
Порт № 80 используется для Слушатель
resource "aws_lb_listener" "lb-listener" { load_balancer_arn = "${aws_lb.app-lb.arn}" port = "80" protocol = "HTTP" default_action { type = "forward" target_group_arn = "${aws_lb_target_group.tg-group.arn}" } }
ECS и ECR
Репозиторий ECR
resource "aws_ecr_repository" "ecr-repo" { name = "ecr-repo" }
ECS Cluster
resource "aws_ecs_cluster" "ecs-cluster" { name = "clusterDev" }
Определение задачи
- В этой части важно указать
Контейнерпорт
- Создайте Env var:
Export tf_var_uri_repo =
.dkr.ecr.
resource "aws_ecs_task_definition" "task" { family = "HTTPserver" network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] cpu = 256 memory = 512 execution_role_arn = data.aws_iam_role.ecs-task.arn container_definitions = jsonencode([ { name = "golang-container" image = "${var.uri_repo}:latest" #URI cpu = 256 memory = 512 portMappings = [ { containerPort = 5000 } ] } ]) }
Служба ECS
Укажите балансировщик нагрузки
блокировать
resource "aws_ecs_service" "svc" { name = "golang-Service" cluster = "${aws_ecs_cluster.ecs-cluster.id}" task_definition = "${aws_ecs_task_definition.task.id}" desired_count = 2 launch_type = "FARGATE" network_configuration { subnets = ["${aws_subnet.pub-subnets[0].id}", "${aws_subnet.pub-subnets[1].id}"] security_groups = ["${aws_security_group.sg1.id}"] assign_public_ip = true } load_balancer { target_group_arn = "${aws_lb_target_group.tg-group.arn}" container_name = "golang-container" container_port = "5000" } }
CI/CD Pipeline
Кодекс -репозиторий
resource "aws_codecommit_repository" "repo" { repository_name = var.repo_name }
Проект CodeBuild
resource "aws_codebuild_project" "repo-project" { name = "${var.build_project}" service_role = "${aws_iam_role.codebuild-role.arn}" artifacts { type = "NO_ARTIFACTS" } source { type = "CODECOMMIT" location = "${aws_codecommit_repository.repo.clone_url_http}" } environment { compute_type = "BUILD_GENERAL1_SMALL" image = "aws/codebuild/standard:5.0" type = "LINUX_CONTAINER" privileged_mode = true } }
BUILDSPEC.YML
- Этот файл очень важен для создания изображения Docker и привлечь его к ECR репозиторий
- Для обновления службы ECS важно указать
СОДЕРЖАНИЕ ИМЯ
иImageuri
В файле JSON с именемImagedefinitions.json
. Этот файл является артефактом - Этот файл должен быть в репозитории CODECOMMIT
version: 0.2 phases: pre_build: commands: - echo Logging in to Amazon ECR... - echo $AWS_DEFAULT_REGION - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin 940401905947.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com - REPOSITORY_NAME="ecr-repo" - REPOSITORY_URI=940401905947.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$REPOSITORY_NAME - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) - IMAGE_TAG=${COMMIT_HASH:=latest} build: commands: - echo Building the Docker image... - docker build -t $REPOSITORY_NAME:latest . - docker tag $REPOSITORY_NAME:latest $REPOSITORY_URI:latest - docker tag $REPOSITORY_NAME:latest $REPOSITORY_URI:$IMAGE_TAG post_build: commands: - docker push $REPOSITORY_URI:latest - docker push $REPOSITORY_URI:$IMAGE_TAG - printf '[{"name":"golang-container","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json artifacts: files: imagedefinitions.json
S3 Bucket для хранения артефактов
resource "aws_s3_bucket" "bucket-artifact" { bucket = "eroz-artifactory-bucket" acl = "private" }
Codepipeline
Укажите Источник
, Сборка
В Развернуть
Стадии
Примечание : кодировать стадии
Проверьте официальную документацию https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference.html
resource "aws_codepipeline" "pipeline" { name = "pipeline" role_arn = "${data.aws_iam_role.pipeline_role.arn}" artifact_store { location = "${aws_s3_bucket.bucket-artifact.bucket}" type = "S3" } # SOURCE stage { name = "Source" action { name = "Source" category = "Source" owner = "AWS" provider = "CodeCommit" version = "1" output_artifacts = ["source_output"] configuration = { RepositoryName = "${var.repo_name}" BranchName = "${var.branch_name}" } } } # BUILD stage { name = "Build" action { name = "Build" category = "Build" owner = "AWS" provider = "CodeBuild" version = "1" input_artifacts = ["source_output"] output_artifacts = ["build_output"] configuration = { ProjectName = "${var.build_project}" } } } # DEPLOY stage { name = "Deploy" action { name = "Deploy" category = "Deploy" owner = "AWS" provider = "ECS" version = "1" input_artifacts = ["build_output"] configuration = { ClusterName = "clusterDev" ServiceName = "golang-Service" FileName = "imagedefinitions.json" } } } }
ДАННЫЕ
Этот раздел предназначен для использования созданных ролей IAM
data "aws_iam_role" "pipeline_role" { name = "codepipeline-role" } data "aws_iam_role" "ecs-task" { name = "ecsTaskExecutionRole" }
Выходы
Чтобы получить DNS ALB и URL -адрес кодового репозитория
output "repo_url" { value = aws_codecommit_repository.repo.clone_url_http } output "alb_dns" { value = aws_lb.app-lb.dns_name }
Дополнительные переменные
variable "repo_name" { type = string default = "dev-repo" } variable "branch_name" { type = string default = "master" } variable "build_project" { type = string default = "dev-build-repo" } variable "uri_repo" { type = string #The URI_REPO value is in a TF_VAR in my PC }
Шаг 03: HTTP Simple Server с Golang
Этот код полезен для получения частного IP -адресов ECS -задач
package main import ( "fmt" "log" "net" "net/http" ) func main() { log.Print("HTTPserver: Enter main()") http.HandleFunc("/", handler) log.Fatal(http.ListenAndServe("0.0.0.0:5000", nil)) } // printing request headers/params func handler(w http.ResponseWriter, r *http.Request) { log.Print("request from address: %q\n", r.RemoteAddr) fmt.Fprintf(w, "%s %s %s\n", r.Method, r.URL, r.Proto) fmt.Fprintf(w, "Host = %q\n", r.Host) fmt.Fprintf(w, "RemoteAddr = %q\n", r.RemoteAddr) if err := r.ParseForm(); err != nil { log.Print(err) } for k, v := range r.Form { fmt.Fprintf(w, "Form[%q] = %q\n", k, v) } fmt.Fprintf(w, "\n===> local IP: %q\n\n", GetOutboundIP()) } func GetOutboundIP() net.IP { conn, err := net.Dial("udp", "8.8.8.8:80") if err != nil { log.Fatal(err) } defer conn.Close() localAddr := conn.LocalAddr().(*net.UDPAddr) return localAddr.IP }
Шаг 04: Dockerfile
- Этот DockerFile создайте изображение с HTTP -сервером с Golang
- Этот файл должен быть в репозитории CODECOMMIT
FROM golang:alpine AS builder ENV GO111MODULE=on \ CGO_ENABLED=0 \ GOOS=linux \ GOARCH=amd64 WORKDIR /build COPY ./HTTPserver.go . # Build the application RUN go build -o HTTPserver ./HTTPserver.go WORKDIR /dist RUN cp /build/HTTPserver . # Build a small image FROM scratch COPY --from=builder /dist/HTTPserver / EXPOSE 5000 ENTRYPOINT ["/HTTPserver"]
Шаг 05: Создать TF_VAR
Шаг 06: Создайте инфраструктуру
Команды
Terraform Init
Terraform Palidate
Терраформский план
Terraform Apply -Aut -Aprove
Когда творение закончено, мы получаем выходы
Шаг 07: Загрузите DockerFile, код и сборка файлов в репозиторий CodeCommit
Клонировать репозиторий
Копия
BuildSpect.yml
,Dockerfile
иGolang Code
к клонированной папке хранилища и Тогда сделайсовершить
Git push к репозиторию кодекса
Шаг 08: Проверьте трубопровод
- Когда стадия «сборка» будет выполнена, проверьте изображение Docker в репозитории ECR
Шаг 09: Проверьте службу ECS
Когда будет выполнена этап «развертывания», проверьте задачи в службе ECS
Шаг 10: Проверьте целевую группу
Шаг 11: Проверьте работу балансировщика нагрузки приложения
Последний шаг: удалить инфраструктуру
Terraform уничтожит -увеса
Оригинал: «https://dev.to/erozedguy/ci-cd-pipeline-for-amazon-ecs-fargate-with-terraform-33na»