Slide 1

Slide 1 text

Packer/Ansible/Terraformを使った AWS Fargateへのデプロイ Cloud Native Kansai #4 株式会社ビヨンド 寺岡 佑樹

Slide 2

Slide 2 text

はじめに ・本資料は後日公開します ・資料中に記載されているコードGitHubに公開しています  https://github.com/nezumisannn/aws-fargate-pipeline-example ・資料公開したらTwitterでハッシュタグ付きでつぶやきます

Slide 3

Slide 3 text

Agenda ・自己紹介 ・利用するツール/サービスの紹介 ・このスライドで作れる環境の構成図 ・環境の作り方 ・まとめ

Slide 4

Slide 4 text

自己紹介 resource “my_profile” “nezumisannn” { name = “Yuki.Teraoka” nickname = “ねずみさん家。” company = “beyond” job = “Site Reliability Engineer” twitter = “@yktr_sre” skills = [“terraform”,”packer”] }

Slide 5

Slide 5 text

利用するツール

Slide 6

Slide 6 text

利用するツール Terraform Packer Ansible

Slide 7

Slide 7 text

利用するサービス

Slide 8

Slide 8 text

利用するサービス Elastic Container Registry Fargate Code Build Code Deploy Code Pipeline

Slide 9

Slide 9 text

構成図

Slide 10

Slide 10 text

構成図 Pipeline git push im age build deploy source

Slide 11

Slide 11 text

出来ること ・fargateへのデプロイの自動化(CI/CD) ・GitへのPushをトリガーに、Pull ⇒ Build ⇒ Deployまで処理できる

Slide 12

Slide 12 text

デモ

Slide 13

Slide 13 text

今回のゴール ・CodePipelineの一連の処理を実行できる ・実行した結果、ブラウザの画面に「hello world」と表示されるようにする

Slide 14

Slide 14 text

実装

Slide 15

Slide 15 text

コンテナのビルド Pipeline git push im age build deploy source

Slide 16

Slide 16 text

コンテナのビルド ・CodeBuildを利用 ・プロジェクトはTerraformで作成 ・ソースプロバイダにGitHubを設定 ・buildspec.ymlにビルド時の処理を記述 ・CodeBuildのビルド用コンテナにPacker/Ansibleをインストール ・Packerのbuildersにdockerを指定 ・provisionersでAnsibleを実行してpost-processorsでECRにpush

Slide 17

Slide 17 text

resource "aws_codebuild_project" "codebuild" { name = "example" service_role = "${aws_iam_role.role-codebuild.arn}" source { type = "GITHUB" location = "https://github.com/nezumisannn/aws-pipeline-example.git" git_clone_depth = 1 } environment { compute_type = "BUILD_GENERAL1_SMALL" image = "aws/codebuild/standard:1.0-1.8.0" image_pull_credentials_type = "CODEBUILD" privileged_mode = true type = "LINUX_CONTAINER" } artifacts { type = "NO_ARTIFACTS" } }

Slide 18

Slide 18 text

--- version: 0.2 phases: pre_build: commands: - curl -qL -o packer.zip https://releases.hashicorp.com/packer/0.12.3/packer_0.12.3_linux_amd64.zip && unzip packer.zip - curl -qL -o jq https://stedolan.github.io/jq/download/linux64/jq && chmod +x ./jq - apt-get install software-properties-common -y - apt-add-repository ppa:ansible/ansible -y - apt-get install ansible -y - apt-get install openssh-client -y - apt-get update - ansible --version - ./packer validate ./packer-build/build.json

Slide 19

Slide 19 text

build: commands: - echo "Setting AWS credentials" - curl -qL -o aws_credentials.json http://169.254.170.2/$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI > aws_credentials.json - aws configure set region ap-northeast-1 - aws configure set aws_access_key_id `./jq -r '.AccessKeyId' aws_credentials.json` - aws configure set aws_secret_access_key `./jq -r '.SecretAccessKey' aws_credentials.json` - aws configure set aws_session_token `./jq -r '.Token' aws_credentials.json` - echo "Build Packer" - ./packer build -debug ./packer-build/build.json - printf '{"Version":"1.0","ImageURI":"%s"}' XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/example-repo:latest > imageDetail.json artifacts: files: imageDetail.json

Slide 20

Slide 20 text

{ "variables": { "ecr_registry_url": "XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com", "ecr_repository": "XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/example-repo" }, "builders": [ { "type": "docker", "image": "alpine:latest", "commit": true, "run_command": [ "-d", "-t", "-i", "{{.Image}}", "/bin/sh" ], "changes": [ "VOLUME /data", "WORKDIR /data", "EXPOSE 80 443", "ENTRYPOINT [\"docker-entrypoint.sh\"]" ] } ],

Slide 21

Slide 21 text

"provisioners": [ { "type": "ansible", "user": "root", "playbook_file": "./ansible/operation.yml" } ], "post-processors": [ [ { "type": "docker-tag", "repository": "{{user `ecr_repository`}}", "tag": "latest" }, { "type": "docker-push", "ecr_login": true, "login_server": "{{user `ecr_registry_url`}}" } ] ] }

Slide 22

Slide 22 text

- name: Starting building... hosts: all tasks: - name: Installing nginx package: name: nginx state: present - name: Putting nginx config file copy: src: config/nginx/default.conf dest: /etc/nginx/conf.d/default.conf mode: 0755 owner: root group: root - name: Putting index.html copy: src: ../application/index.html dest: /usr/share/nginx/html/index.html mode: 0755 owner: root group: root

Slide 23

Slide 23 text

- name: Putting entrypoints copy: src: config/{{ item }} dest: /usr/local/bin/{{ item }} mode: 0755 owner: root group: root with_items: - docker-entrypoint.sh #!/bin/sh exec nginx -g 'pid /tmp/nginx.pid; daemon off;'

Slide 24

Slide 24 text

Fargateへのデプロイ Pipeline git push im age build deploy source

Slide 25

Slide 25 text

Fargateへのデプロイ ・CodeDeployを利用 ・Fargateとデプロイに必要なALB/ターゲットグループはTerraformで作成 ・ローリングアップデートではなくBlue/Greenデプロイを行う

Slide 26

Slide 26 text

Blue/Greenデプロイ Target 1 Target 2

Slide 27

Slide 27 text

resource "aws_alb" "alb" { name = "example" security_groups = ["${aws_security_group.alb-sg.id}"] subnets = "${var.subnets}" }

Slide 28

Slide 28 text

## Target Group resource "aws_alb_target_group" "target_group1" { name = "example-tg1" port = 80 protocol = "HTTP" target_type = "ip" vpc_id = "${var.vpc_id}" } resource "aws_alb_target_group" "target_group2" { name = "example-tg2" port = 80 protocol = "HTTP" target_type = "ip" vpc_id = "${var.vpc_id}" }

Slide 29

Slide 29 text

## ECS Service resource "aws_ecs_service" "ecs-service" { name = "service-nginx" cluster = "${aws_ecs_cluster.ecs-cluster.id}" task_definition = "${aws_ecs_task_definition.ecs-task.arn}" launch_type = "FARGATE" desired_count = 3 health_check_grace_period_seconds = 0 deployment_controller { type = "CODE_DEPLOY" } load_balancer { container_name = "nginx-web" container_port = "80" target_group_arn = "${aws_alb_target_group.target_group1.arn}" } network_configuration { assign_public_ip = true security_groups = [ "${aws_security_group.fargate-sg.id}" ] subnets = "${var.subnets}" } }

Slide 30

Slide 30 text

## CodeDeploy APP resource "aws_codedeploy_app" "codedeploy-app" { compute_platform = "ECS" name = "AppECS-cluster-example-service-nginx" }

Slide 31

Slide 31 text

resource "aws_codedeploy_deployment_group" "codedeploy-group" { app_name = "${aws_codedeploy_app.codedeploy-app.name}" service_role_arn = "${aws_iam_role.role-codedeploy.arn}" deployment_group_name = "DgpECS-cluster-example-service-nginx" deployment_config_name = "CodeDeployDefault.OneAtATime" ecs_service { cluster_name = "${aws_ecs_cluster.ecs-cluster.name}" service_name = "${aws_ecs_service.ecs-service.name}" } blue_green_deployment_config { deployment_ready_option { action_on_timeout = "CONTINUE_DEPLOYMENT" wait_time_in_minutes = 0 } terminate_blue_instances_on_deployment_success { action = "TERMINATE" termination_wait_time_in_minutes = 0 } }

Slide 32

Slide 32 text

load_balancer_info { target_group_pair_info { prod_traffic_route { listener_arns = [ "${aws_alb_listener.listener.arn}" ] } target_group { name = "${aws_alb_target_group.target_group1.arn}" } target_group { name = "${aws_alb_target_group.target_group2.arn}" } } }

Slide 33

Slide 33 text

resource "aws_codedeploy_deployment_group" "codedeploy-group" { app_name = "${aws_codedeploy_app.codedeploy-app.name}" service_role_arn = "${aws_iam_role.role-codedeploy.arn}" deployment_group_name = "DgpECS-cluster-example-service-nginx" deployment_config_name = "CodeDeployDefault.OneAtATime" ecs_service { cluster_name = "${aws_ecs_cluster.ecs-cluster.name}" service_name = "${aws_ecs_service.ecs-service.name}" } blue_green_deployment_config { deployment_ready_option { action_on_timeout = "CONTINUE_DEPLOYMENT" wait_time_in_minutes = 0 } terminate_blue_instances_on_deployment_success { action = "TERMINATE" termination_wait_time_in_minutes = 0 } }

Slide 34

Slide 34 text

Pipelineの作成 Pipeline git push im age build deploy source

Slide 35

Slide 35 text

Pipelineの作成 ・パイプラインのステージをそれぞれ追加する ・ソースステージ:Github ・ビルドステージ:CodeBuild ・デプロイステージ:ECS(ブルー/グリーン) ・git pushをするとPull ⇒ Build ⇒ Deployが自動実行されるようになる ・パイプラインの設定はTerraformで行う

Slide 36

Slide 36 text

resource "aws_codepipeline" "codepipeline" { name = "example-pipeline" stage { name = "Source" action { category = "Source" configuration = { "Branch" = "master" "Owner" = "XXXXXXXXXX" "PollForSourceChanges" = "false" "Repo" = "XXXXXXXXXX" } name = "Source" output_artifacts = [ "SourceArtifact", ] owner = "ThirdParty" provider = "GitHub" run_order = 1 version = "1" } }

Slide 37

Slide 37 text

stage { name = "Build" action { category = "Build" configuration = { "ProjectName" = "${aws_codebuild_project.codebuild.name}" } input_artifacts = [ "SourceArtifact", ] name = "Build" output_artifacts = [ "BuildArtifact", ] owner = "AWS" provider = "CodeBuild" run_order = 1 version = "1" } }

Slide 38

Slide 38 text

stage { name = "Deploy" action { category = "Deploy" configuration = { "AppSpecTemplateArtifact" = "SourceArtifact" "ApplicationName" = "${aws_codedeploy_app.codedeploy-app.name}" "DeploymentGroupName" = "${aws_codedeploy_deployment_group.codedeploy-group.name}" "Image1ArtifactName" = "BuildArtifact" "Image1ContainerName" = "IMAGE1_NAME" "TaskDefinitionTemplateArtifact" = "SourceArtifact" } input_artifacts = [ "BuildArtifact", "SourceArtifact", ] name = "Deploy" owner = "AWS" provider = "CodeDeployToECS" run_order = 1 version = "1" } } }

Slide 39

Slide 39 text

## Webhook Secret locals { webhook_secret = "XXXXXXXXXXXXXXXXXXXXXX" } ## CodePipeline Webhook resource "aws_codepipeline_webhook" "codepipeline-webhook" { name = "webhook-github" authentication = "GITHUB_HMAC" target_action = "Source" target_pipeline = "${aws_codepipeline.codepipeline.name}" authentication_configuration { secret_token = "${local.webhook_secret}" } filter { json_path = "$.ref" match_equals = "refs/heads/{Branch}" } }

Slide 40

Slide 40 text

## Github Webhook resource "github_repository_webhook" "repository-webhook" { repository = "aws-pipeline-example" name = "codepipeline-webhook" configuration { url = "${aws_codepipeline_webhook.codepipeline-webhook.url}" content_type = "json" insecure_ssl = true secret = "${local.webhook_secret}" } events = ["push"] }

Slide 41

Slide 41 text

まとめ ・FargateへのデプロイはAWSのデプロイ3兄弟を利用しよう ・OSSと組み合わせるとさらに便利に! ・デプロイ基盤を整えておくとリリース作業を省力化できる ・皆様のデプロイ基盤構築に役立てれば幸いです

Slide 42

Slide 42 text

END