LoginSignup
7
1

More than 1 year has passed since last update.

eksctlで作成していたEKSリソースをterraformに置き換える

Last updated at Posted at 2021-04-04

目的

EKSのKubernetesバージョンを1.15>1.19へアップデートにてeksctlで作成したKubernetesクラスタを作り変えるので、管理上都合の良いterraformに置き換える。

課題感

  • Infrastructure as Codeの一環としてterraformを使っているが、eksctlで使っている部分がterraform化出来なく統一感がない
    • eksctlで作られるリソースとterraformで作られるリソースの境界線がわかりにくい
    • eksctlの場合リソースを作ることしかできずに変更ができないという制約がある
    • そのためAutoScalingGroupをterraformでも定義する必要があった

環境

  • EKSのKubernetes1.19を使用する
    • 検証時は1.18でやっていた
  • terraform (基本最新版 執筆時v0.14.6)

段取り

  1. EKSを動かすのに必要な前提を確認する
    1. 最低限必要なリソース
    2. 弊社の運用上必要なリソース
  2. eksctlで作っているリソースを確認する
    1. 明示的に定義しているリソース
    2. 暗示的に定義されるリソース
  3. terraformのeks.moduleで作られるリソースを確認する
  4. 切替後:eksctlで作成した環境を削除する

執筆上順番建てて書いていますが、実際は1~3を行ったり来たり、動作確認等しながらやりました。

前提の確認(抜粋)

EKSクラスタとワーカーノードが登場するのでごっちゃにならないように注意しましょう。

追加で設定している項目

  • スポットインスタンスを使用する
    • スポットインスタンスのプールの関係から2種類のインスタンスタイプを指定する
    • autoScalerにてCapacity,maxSizeを指定する
    • volumeSize: 50
    • SSH Keyは事前に用意したものを指定するが、普段はSSHのポートは開けない
  • コンテナのログをFluentD経由でCloudWatchLogsに書き込む 参考
    • (Fluent Bit の提供を開始しました。これにより、パフォーマンスの大幅な向上が見込めます) というのに執筆時に気づいた
    • EKSのログはCloudWatchLogsに書き込んでいない
  • X-Rayを使う
    • ノードのIAMロールにAWSXRayDaemonWriteAccessのアタッチ
  • CodeBuildからデプロイする
  • (ALBはterraformで作成したものを使用する)
    • AWS Load Balancer Controllerは使用していない
      • EKS側から作るALBで、良し悪しあるみたいですが、terraformで管理したいので使用しません。
      • ECSとかもターゲットにする可能性や、リダイレクト周りのコントロールとかもしているからというのもある。

eksctlで作られるリソース

eksctlは手っ取り早くEKSでkubernetesクラスタを作成することができる。
VPCからSecurityGroup、IAM周りまで内部的にはCloudFormationで一気に作ることができるので、検証したいときにすごい便利。
Amazon EKS – eksctlの開始方法など。詳しい使い方はeksctlを参照。

明示的に定義するリソース

弊社の場合、大枠としてはVPC,Subnetはterraformで作成し、EKSの部分をeksctlで作成
細かいIDは伏せていますが、実際に使っていたコンフィグファイルを載せます。
(今回精査したら使ってない機能もありましたが、載せています)

eks-cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: <cluster-name>
  region: ap-northeast-1
  version: "1.15"

vpc:
  id: vpc-0d6f6d357d5817636
  subnets:
    public:
      ap-northeast-1a:
        id: <subnet-ID>
      ap-northeast-1c:
        id: <subnet-ID>
      ap-northeast-1d:
        id: <subnet-ID>

nodeGroups:
  - name: ng-workers-1-15
    desiredCapacity: <num>
    maxSize: <num>
    volumeSize: 50
    volumeType: gp2
    targetGroupARNs:
      - arn:aws:elasticloadbalancing:ap-northeast-1:<accountID>:targetgroup/<targetgroup-name>/<arn>
    instancesDistribution:
      instanceTypes:
        - <instance-1>
        - <instance-2>
      onDemandBaseCapacity: 0 #スポットインスタンスを使うための設定
      onDemandPercentageAboveBaseCapacity: 0
    ssh:
      publicKeyName: <KeyName> #事前に作成しているKeyを指定
      allow: false #eksctl-xxxxx-nodegroup-eks-nodegroup-remoteAccessが作られるらしい
    iam:
      attachPolicyARNs:
        - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy #前提に書いた通り必要
        - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy #terraformのEKS moduleでもデフォルトtrueになっている。使ってなさそうだけどそのままに
        - arn:aws:iam::<accountID>:policy/<policy-name> #諸々アクセスに必要なユーザ管理ポリシー 
      withAddonPolicies:
        imageBuilder: true #ECRへのアクセスが許可されるらしいが、実態としてポリシーがアタッチされていないし、使っていない...
        autoScaler: true #専用のIAMポリシーが作られる
        albIngress: true #ノードのIAMロールにPolicyALBIngressがアタッチされる先述のAWS Load Balancer Controller機能、使ってないので不要
        xRay: true #ノードのIAMロールにAWSXRayDaemonWriteAccessがアタッチされる
        externalDNS: true #ノードのIAMロールにPolicyExternalDNSChangeSetがアタッチされるが、機能を使っていなかった
        cloudWatch: true #ノードのIAMロールにCloudWatchAgentServerPolicyがアタッチされる
    securityGroups:
      withShared: true #すべてのノードグループで共有されるセキュリティグループを使う
      withLocal: true #↓をアタッチする
      attachIDs: ['<sg-ID>'] #ALBからの通信を許可するSG

自動的に作成されるリソース

前提の確認(抜粋)で書いた内容が作成されている

terraformのeks.moduleで作られるリソースを確認する

最初はこちらProvision an EKS Cluster (AWS) | Terraform - HashiCorp Learnをベースに環境構築し、動作確認や、差異を比較しながら同等環境になるよう調整した。
大体eks.moduleも前提の確認(抜粋)で書かれている内容がデフォルト(自動的)で作成される。

  • Amazon EKS クラスターの IAM ロール
    • AmazonEKSClusterPolicy
    • AmazonEKSServicePolicy(不要らしいがデフォルトであるのでそのままに)
    • AmazonEKS_CNI_Policy(不要らしいがデフォルトであるのでそのままに)
  • Amazon EKS ノードIAMロール
    • AmazonEKSWorkerNodePolicy
    • AmazonEC2ContainerRegistryReadOnly
  • SG 若干書き方は違うが、考慮事項に準じている
  • 細かい部分には差異があり、追加で定義した部分等あるので後述するテンプレートを参照

staging/production毎に作成しているリソース

eks.tf
module "eks" {
  source          = "terraform-aws-modules/eks/aws"
  cluster_name    = "cluster-production-v2"
  cluster_version = "1.19"
  subnets         = [
    var.vpc["subnet_1a"],
    var.vpc["subnet_1c"],
    var.vpc["subnet_1d"]
  ]
  
  tags = { #Environment = "training"は不要なので削除
    "k8s.io/cluster-autoscaler/enabled" = "true"
    "k8s.io/cluster-autoscaler/cluster-production-v2" = "owned"
  }

  vpc_id           = var.vpc["vpc_id"]
  write_kubeconfig = false #ローカルにkubeconfigが置かれるのを無効化

  map_roles = [ #CodeBuildがkubeconfigを取得するためにアタッチしている
    {
      rolearn  = "arn:aws:iam::<accountID>:role/<role-name>"
      username = "codebuild"
      groups   = ["system:masters"]
    }
  ]

  worker_groups_launch_template = [
    {
      name                    = "workers-1-19"
      override_instance_types = ["<instance-1>","<instance-2>"]
      ami_id                  = "<ami-id>" #明示的に指定しないと、amiが最新版ではない場合差異となる
      root_volume_size        = 50
      root_volume_type        = "gp3"
      root_iops               = 3000
      spot_instance_pools     = 2
      asg_min_size            = <num>
      asg_max_size            = <num>
      asg_desired_capacity    = <num>
      enable_monitoring       = false #CloudWatchの詳細メトリクスをオフ
      key_name                = <key-name>
      kubelet_extra_args      = "--node-labels=node.kubernetes.io/lifecycle=spot"
      public_ip               = true
      additional_security_group_ids = [<sg-ID>]
      target_group_arns = [<target_group_arns>]
    },
  ]
}
data "aws_eks_cluster" "cluster" {
  name = module.eks.cluster_id
}

data "aws_eks_cluster_auth" "cluster" {
  name = module.eks.cluster_id
}
kubernetes.tf
provider "kubernetes" {
  host                   = data.aws_eks_cluster.cluster.endpoint
  token                  = data.aws_eks_cluster_auth.cluster.token
  cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data)
}
iam.tf
resource "aws_iam_role_policy_attachment" "worker_group" {
  role       = module.eks.worker_iam_role_name
  policy_arn = var.eks["cluster-auto-scaler"] #cluster-auto-scaler.jsonで作られるIAMポリシー
}

resource "aws_iam_role_policy_attachment" "node" {
  role       = module.eks.worker_iam_role_name
  policy_arn = <policy-arn> #ユーザ管理のポリシー
}

resource "aws_iam_role_policy_attachment" "xray" {
  role       = module.eks.worker_iam_role_name
  policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
}

resource "aws_iam_role_policy_attachment" "cloud_watch" {
  role       = module.eks.worker_iam_role_name
  policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
}
variables.tf
variable "eks" {
  type = map(string)
  default = {
    cluster-auto-scaler   = "<arn>"
  }
}

共通で定義しているリソース

Cluster Autoscaler-IAM ポリシーとロールを作成するを元にポリシーを作成
(執筆時に見つけた)autoscaling.mdを見るとhelmで定義できるけど、自動的に作られてしまうので、今回の方法で良いかも。

iam.tf
resource "aws_iam_policy" "cluster_auto_scaler" {
  name   = "cluster-auto-scaler"
  path   = "/"
  policy = file("iam/cluster-auto-scaler.json")
}
cluster-auto-scaler.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "autoscaling:DescribeAutoScalingGroups",
                "autoscaling:DescribeAutoScalingInstances",
                "autoscaling:DescribeLaunchConfigurations",
                "autoscaling:DescribeTags",
                "autoscaling:SetDesiredCapacity",
                "autoscaling:TerminateInstanceInAutoScalingGroup",
                "ec2:DescribeLaunchTemplateVersions"
            ],
            "Resource": "*"
        }
    ]
}

subnetのタグは1.18の情報をベースに準備していたので付与している。1.19を使う場合はおそらく不要なはず

vpc.tf
resource "aws_subnet" "subnet-pf-public-1" {
  vpc_id                  = aws_vpc.vpc-pf.id
  cidr_block              = var.vpc-pf.subnet_pub1_cidr_block
  availability_zone       = var.vpc-pf.subnet_pub1_az
  map_public_ip_on_launch = true
  tags = {
    "kubernetes.io/cluster/cluster-staging-v2"    = "shared"
    "kubernetes.io/cluster/cluster-production-v2" = "shared"
  }

切替後:eksctlで作成した環境を削除する

  1. asg.tfの削除(staging/production)
    • AutoScalingGroupはterraformで差分が出てしまい、明示的に定義していたもの
    • ファイルを削除し terraform apply
  2. eksctlでclusterの削除(staging/production)
    • eksctl delete cluster -f <コンフィグファイル名>で一発
    • CloudFormationのStackやEKS等々を見て削除を確認

おわりに

差分を比較すると細かい部分に目が行き、時間がかかったりする部分がありますが、AWSのドキュメントに載っている内容が満たされているかつサービス基盤として各動作が問題なければ割り切って置き換えてしまって良いかと思います。
これで晴れてterraformで統一的にリソース管理ができるようになりました。

Global Mobility Serviceでは、インフラエンジニア含めITエンジニアを募集しています。

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1