AWS Cluster AutoscalerをTerraformとHelmfileでデプロイする
Cluster Autoscalerを利用すると,CPUやメモリの要求量に応じてノード数を自動的に増減させることが可能です.
本稿では,Amazon EKSで以下を利用する方法を紹介します.
Cluster AutoscalerのPodにIAMロールを割り当てるため,あらかじめkube2iamもしくはkiamをデプロイしておく必要があります.詳しくは下記を参照してください.
(2020/9/23 追記)Cluster AutoscalerのHelm chartはgithub.com/kubernetes/autoscalerに移動しました。また、権限の割り当てはIAM Roles for Service Accountsが推奨されています。
Worker Auto Scaling Groupの修正
Auto Scaling GroupにAuto Discovery用のタグを追加します.
terraform-aws-eks module を利用している場合は以下のように autoscaling_enabled = true
を追加すればよいです.詳しくはモジュールの Autoscaling を参照してください.
worker_groups_launch_template_mixed = [ { # 中略 autoscaling_enabled = true asg_min_size = 1 asg_max_size = 8 asg_desired_capacity = 4 }, ]
kopsを利用している場合は kops edit ig nodes
で以下のようにノード用のタグを追加します.
cloudLabels: k8s.io/cluster-autoscaler/enabled: 1 k8s.io/cluster-autoscaler/CLUSTER_NAME: 1
IAM Roleの作成
Cluster AutoscalerがAuto Scaling Groupを取得したり変更したりするためのIAM Roleを作成します.必要なIAMポリシーは cluster-autoscaler/cloudprovider/aws に記載されています.
# IAM role for Cluster Autoscaler resource "aws_iam_role" "cluster_autoscaler" { name = "${var.cluster_name}-cluster-autoscaler" assume_role_policy = data.aws_iam_policy_document.kube2iam_assume_role.json } resource "aws_iam_role_policy" "cluster_autoscaler" { role = aws_iam_role.cluster_autoscaler.name policy = <<EOF { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "autoscaling:DescribeAutoScalingGroups", "autoscaling:DescribeAutoScalingInstances", "autoscaling:DescribeLaunchConfigurations", "autoscaling:DescribeTags", "autoscaling:SetDesiredCapacity", "autoscaling:TerminateInstanceInAutoScalingGroup" ], "Resource": "*" } ] } EOF } data "aws_iam_policy_document" "kube2iam_assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["ec2.amazonaws.com"] } principals { type = "AWS" identifiers = [module.eks.worker_iam_role_arn] } } }
Helm chartのデプロイ
stable/cluster-autoscaler chart をデプロイします.
releases: # https://github.com/helm/charts/tree/master/stable/cluster-autoscaler - name: cluster-autoscaler namespace: kube-system chart: stable/cluster-autoscaler values: - cloudProvider: aws awsRegion: ap-northeast-1 autoDiscovery: clusterName: {{ .Environment.Values.clusterName }} rbac: create: true podAnnotations: # IAM role for Cluster Autoscaler iam.amazonaws.com/role: {{ .Environment.Values.clusterName }}-cluster-autoscaler # https://github.com/helm/charts/tree/master/stable/metrics-server - name: metrics-server namespace: kube-system chart: stable/metrics-server environments: default: values: - clusterName: CLUSTER_NAME
Cluster Autoscalerのログにエラーが出ていないことを確認します.IAMロールやIAMポリシーが不適切な場合はエラーが出ます.
スケールアップの動作確認
ここでは以下のように echoserver をデプロイします.
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: echoserver namespace: echoserver spec: replicas: 1 template: metadata: labels: app: echoserver spec: containers: - image: gcr.io/google_containers/echoserver:1.10 name: echoserver ports: - containerPort: 8080
ここで,以下のようにCPU requestを設定します.1ノード2コアの場合は1500mにするとよいでしょう.要求値が大きすぎると,Cluster Autoscalerはノードを追加してもスケジュール不可能と判断してしまいます.
spec: template: spec: containers: - image: gcr.io/google_containers/echoserver:1.10 name: echoserver ports: - containerPort: 8080 resources: requests: cpu: 1500m
Cluster Autoscalerのログを確認します.以下のように Upcoming 1 nodes
と表示されると成功です.同時に,Auto Scaling GroupのDesired Capacityが書き換わっていることを確認します.しばらく経つと,ノードが増えてPodがスケジュールされるはずです.
I0927 11:50:35.158353 1 scale_up.go:263] Pod echoserver/echoserver-74fd7d865f-vkzqb is unschedulable I0927 11:50:35.158391 1 scale_up.go:300] Upcoming 0 nodes I0927 11:50:35.158521 1 scale_up.go:423] Best option to resize: ASG_NAME I0927 11:50:35.158540 1 scale_up.go:427] Estimated 1 nodes needed in ASG_NAME I0927 11:50:35.158556 1 scale_up.go:529] Final scale-up plan: [{ASG_NAME 4->5 (max: 8)}] I0927 11:50:35.158572 1 scale_up.go:694] Scale-up: setting group ASG_NAME size to 5 I0927 11:50:45.523906 1 scale_up.go:263] Pod echoserver/echoserver-74fd7d865f-vkzqb is unschedulable I0927 11:50:45.524041 1 scale_up.go:300] Upcoming 1 nodes I0927 11:52:36.144782 1 clusterstate.go:194] Scale up in group ASG_NAME finished successfully in 2m0.794268739s
スケールダウンの動作確認
前のセクションで設定したCPU requestを削除します.
デフォルトでは,Cluster Autoscalerがノードが必要ないと判断してから10分後に実際にノードを削除します(詳しくは How does scale-down work? を参照してください).Cluster Autoscalerのログを確認し,以下のように Terminating EC2 instance
が表示されると成功です.
I0927 11:57:07.790306 1 scale_down.go:407] Node ip-172-19-67-52.ap-northeast-1.compute.internal - utilization 0.055000 I0927 11:57:07.790634 1 static_autoscaler.go:359] ip-172-19-67-52.ap-northeast-1.compute.internal is unneeded since 2019-09-27 11:57:07.773690521 +0000 UTC m=+2997.491422805 duration 0s I0927 12:07:12.161679 1 static_autoscaler.go:359] ip-172-19-67-52.ap-northeast-1.compute.internal is unneeded since 2019-09-27 11:57:07.773690521 +0000 UTC m=+2997.491422805 duration 10m4.367847963s I0927 12:07:12.161767 1 scale_down.go:600] ip-172-19-67-52.ap-northeast-1.compute.internal was unneeded for 10m4.367847963s I0927 12:07:12.161868 1 scale_down.go:819] Scale-down: removing empty node ip-172-19-67-52.ap-northeast-1.compute.internal I0927 12:07:12.176857 1 delete.go:64] Successfully added toBeDeletedTaint on node ip-172-19-67-52.ap-northeast-1.compute.internal I0927 12:07:12.391908 1 auto_scaling_groups.go:269] Terminating EC2 instance: i-066bc60549f083e38
最後に,Auto Scaling Groupのdesired capacityが元に戻っていることを確認します.
GitLab RunnerとkanikoでDockerイメージをビルドする
DockerやKubernetesでGitLab Runnerを実行する場合,GitLab RunnerでDockerイメージをビルドするにはDocker in Dockerの特権モードを構成する必要があります.kanikoを利用すると,特権モードを使わずにDockerイメージをビルドできます.
本稿では,GitLab Runner上でkanikoを利用してDockerイメージをビルドし,Amazon ECRにDockerイメージをpushするまでの流れを紹介します. https://docs.gitlab.com/ee/ci/docker/using_kaniko.html で説明されている内容を元にしています.
ここでは,以下の前提とします.
- Kubernetes上のPodにIAM Roleを割り当てることが可能なこと(kube2iam等を利用)
- Kubernetes上にGitLab Runnerがデプロイされていること
- GitLabのリポジトリにソースコードとDockerfileが配置されていること
- Amazon ECRにリポジトリを作成済みであること
IAM Roleの割り当て
GitLab Runner workerがECRにアクセスできるように,GitLab Runner workerのPodにIAM Roleを割り当てます.具体的な方法は下記を参照してください.
.gitlab-ci.ymlの作成
kanikoを利用するには gcr.io/kaniko-project/executor
イメージを指定します.イメージには amazon-ecr-credential-helper が組み込まれており,/kaniko/.docker/config.json
で credHelpers
を設定するとIAM Roleを利用してECRにアクセスできます.
.gitlab-ci.yml
を以下のように設定します.
stages: - push push: stage: push image: name: gcr.io/kaniko-project/executor:debug entrypoint: [""] variables: ECR_HOST: xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com script: - | cat > /kaniko/.docker/config.json <<EOF { "credHelpers": { "${ECR_HOST}": "ecr-login" } } EOF - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $ECR_HOST/repository-name:latest dependencies: - build tags: - app
ビルドを実行した後,ECRのリポジトリにイメージが表示されると成功です.
トラブルシューティング
ECRにアクセスする権限がない場合や /kaniko/.docker/config.json
が適切に設定されていない場合は以下のエラーが表示されます.
error checking push permissions -- make sure you entered the correct tag name, and that you are authenticated correctly, and try again: checking push permission for "xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/repository-name:latest": unsupported status code 401; body: Not Authorized
参考までに, gcr.io/kaniko-project/executor
イメージの環境変数は以下に設定されています.
ENV DOCKER_CREDENTIAL_GCR_CONFIG=/kaniko/.config/gcloud/docker_credential_gcr_config.json ENV DOCKER_CONFIG=/kaniko/.docker/ ENV SSL_CERT_DIR=/kaniko/ssl/certs ENV PATH=/usr/local/bin:/kaniko ENV USER=/root ENV HOME=/root
(2019/9/27追記) kanikoのイメージは debug
タグを利用する必要があります.latest
タグのイメージでは以下のエラーが出て動作しないようです.
Error: failed to start container "build": Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"sh\": executable file not found in $PATH": unknown
terraform-aws-modules/acm/aws でTLS証明書を取得する
terraform-aws-modules/acm/aws を使うとACMの証明書を簡単に取得できます.コンソール作業が一切必要ないのがうれしい.
例えば,ドメイン foo.example.com
のRoute53 Hosted Zoneが存在する前提で *.foo.example.com
のTLS証明書を取得するには,以下のコードを追加します.
module "acm" { source = "terraform-aws-modules/acm/aws" domain_name = "*.foo.example.com" zone_id = data.aws_route53_zone.service.zone_id } data "aws_route53_zone" "service" { name = "foo.example.com" }
Terraformを実行すると以下のリソースが作成されます.
# module.acm.aws_acm_certificate.this[0] will be created + resource "aws_acm_certificate" "this" { + arn = (known after apply) + domain_name = "*.foo.example.com" + domain_validation_options = (known after apply) + id = (known after apply) + subject_alternative_names = [] + validation_emails = (known after apply) + validation_method = "DNS" } # module.acm.aws_acm_certificate_validation.this[0] will be created + resource "aws_acm_certificate_validation" "this" { + certificate_arn = (known after apply) + id = (known after apply) + validation_record_fqdns = (known after apply) } # module.acm.aws_route53_record.validation[0] will be created + resource "aws_route53_record" "validation" { + allow_overwrite = true + fqdn = (known after apply) + id = (known after apply) + name = (known after apply) + records = (known after apply) + ttl = 60 + type = (known after apply) + zone_id = "YOUR_ZONE_ID" } ... module.acm.aws_acm_certificate.this[0]: Creation complete after 7s [id=arn:aws:acm:us-east-1:ID:certificate/ID] module.acm.aws_route53_record.validation[0]: Creating... module.acm.aws_route53_record.validation[0]: Still creating... [10s elapsed] module.acm.aws_route53_record.validation[0]: Still creating... [20s elapsed] module.acm.aws_route53_record.validation[0]: Still creating... [30s elapsed] module.acm.aws_route53_record.validation[0]: Creation complete after 38s [id=ZONE_ID.foo.example.com._CNAME] module.acm.aws_acm_certificate_validation.this[0]: Creating... module.acm.aws_acm_certificate_validation.this[0]: Still creating... [10s elapsed] module.acm.aws_acm_certificate_validation.this[0]: Still creating... [20s elapsed] module.acm.aws_acm_certificate_validation.this[0]: Still creating... [30s elapsed] module.acm.aws_acm_certificate_validation.this[0]: Still creating... [40s elapsed] module.acm.aws_acm_certificate_validation.this[0]: Still creating... [50s elapsed] module.acm.aws_acm_certificate_validation.this[0]: Still creating... [1m0s elapsed] module.acm.aws_acm_certificate_validation.this[0]: Creation complete after 1m10s [id=2019-09-19 02:12:32 +0000 UTC]
モジュールの詳細は terraform-aws-modules/acm/aws を参照してください.