アマゾンECS/ファルゲートのためのCi/CDパイプライン
説明
このポストでは、私はECS/FargateのためにCI/CDパイプラインを実装するためにTerraformでAWSの上で基盤を構築する方法を説明するつもりです.
アーキテクチャは異なる可用性ゾーンの2公共サブネットを持つVPCから成ります.望ましいタスクは2であり、各タスクはFargateと各パブリックサブネットで展開され、各タスクは同じECSサービスに属します.
アプリケーションロードバランサは、2つのタスク間の負荷をバランスさせるために使用されます.
この場合、主な目標はGolangで構築された簡単なHTTPサーバを含むDockerコンテナを実装することです.このHTTPサーバーを使用すると、各タスクのプライベートIPを取得することができます.
CODERMITリポジトリに新しい変更をプッシュすると、CodePipelineはそれらの変更を検出し、パイプラインをトリガし、新しいDockerイメージを作成し、タスクを更新するためにECSサービスに配備します.
建築
資源
https://github.com/erozedguy/CICD-Pipeline-for-Amazon-ECS-Fargate
ステップ
ステップ01 - IAMロールを作成し、CodeMmit資格情報を作成する
ECSタスクを使用してAWSサービスを呼び出すことができます.
AWSCodeCommitPowerUser
私の政策USER
HTTPS Git credentials for AWS CodeCommit
to clone
, push
, pull
にCodeCommit Repository
ステップ02:インフラストラクチャを構築するTerraformスクリプト
プロバイダ
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.51"
}
}
}
provider "aws" {
profile = "default"
region = "us-east-1"
}
VPC
The
vpc script
has VPC
, SUBNETS
and INTERNET GATEWAY
リソース.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"
]
}
役割と方針
のために
CodeBuild
へのアクセスを許可するIAMロール&ポリシーを作成する必要がありますECR
押すDocker images
ECRリポジトリで.また、S 3のバケツにアクセスする許可が必要ですartifacts
.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 = "*"
}
]
})
}
路線表
シングル
Route Table
両方の公共サブネット# 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}"
}
セキュリティグループ
最初のSECグループは
ECS Service
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"]
}
}
番目のSECグループはApplication Load Balancer
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}"]
}
ポート・・・5000Target Group
そのポートはコンテナに使われているのでresource "aws_lb_target_group" "tg-group" {
name = "tg-group"
port = "5000"
protocol = "HTTP"
vpc_id = "${aws_vpc.ecs-vpc.id}"
target_type = "ip"
}
ポート・・・80Listener
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クラスター
resource "aws_ecs_cluster" "ecs-cluster" {
name = "clusterDev"
}
タスク定義
containerPort
export TF_VAR_uri_repo = <ID_ACCOUNT>.dkr.ecr.<REGION>.amazonaws.com/<ECR_REPOSITORY_NAME>
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サービス
指定する
load balancer
ブロック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パイプライン
CodeMmitリポジトリ
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
}
}
ビルド気象研
containerName
and imageUri
名前のJSONファイルでimagedefinitions.json
. このファイルはアーティファクトです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
s 3を保存するバケツ
artifacts
resource "aws_s3_bucket" "bucket-artifact" {
bucket = "eroz-artifactory-bucket"
acl = "private"
}
コペイライン
指定
Source
, Build
, Deploy
ステージ注:
stages
公式文書をチェックする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"
}
出力
を取得するには
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シンプルサーバー
このコードはECSタスクのプライベートIPを取得するのに便利です
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
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 : TfHunvarを作成する
ステップ06 :インフラストラクチャの作成
コマンド
terraform init
terraform validate
terraform plan
terraform apply -auto-approve
ステップ07 : CodeCommitリポジトリにDockerFile、コード、およびbuildSpectファイルをアップロードする
buildspect.yml
, Dockerfile
and Golang Code
クローン化したリポジトリフォルダにcommit
ステップ08:パイプラインチェック
ステップ09 : ECSサービスのチェック
「展開」ステージが完了すると、ECSサービスのタスクをチェックします
ステップ10 :ターゲットグループをチェックする
ステップ11:アプリケーション負荷バランサの操作をチェックする
インフラストラクチャの削除
terraform destroy -auto-approve
Reference
この問題について(アマゾンECS/ファルゲートのためのCi/CDパイプライン), 我々は、より多くの情報をここで見つけました https://dev.to/erozedguy/ci-cd-pipeline-for-amazon-ecs-fargate-with-terraform-33naテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol