OpenTofu を利用した Amazon EKS の構築

2024-07-02
デベロッパーのためのクラウド活用方法

Author : 邵 正

近年、インフラストラクチャのコード化が注目されており、多くの企業で導入が進んでいます。インフラストラクチャのコード化により、繰り返しの手作業を減らし、効率的な開発が可能になります。その中でも、OpenTofu は、IaC ツールとして注目されています。

本ブログでは、Amazon Elastic Kubernetes Service (EKS) クラスターの構築を例にとり、OpenTofu を活用してインフラストラクチャを自動化する方法を解説します。


関連記事 : Terraform を利用した Amazon EKS の構築 »

このクラウドレシピ (ハンズオン記事) を無料でお試しいただけます »

毎月提供されるクラウドレシピのアップデート情報とともに、クレジットコードを受け取ることができます。 


AWS for Games

AWS for Games はより早い開発、よりスマートな運営、そしてより楽しいゲームへの成長という Build、Run、Grow の 3 つの柱に沿ってサポートします。今回は Build の柱 クラウドゲーム開発 のお話になります。


OpenTofu の概要

近年 Infrastructure as code (IaC) はその重要性が高まっており、多くの企業での採用が進んでいます。IaC はシステムの構築、管理、運用を自動化し、より迅速かつ効率的に行うことができます。

OpenTofu は、オープンソースでコミュニティが中心となって開発されており、Linux Foundation によって管理されているTerraform のフォークです。現在、OpenTofu は Terraform のドロップイン置き換えとして機能し、Terraform 1.5.x やほとんどの 1.6.x と互換性があります。コードの変更なしで OpenTofu に移行することができます。


EKS クラスター構築の実践例

EKS クラスターを構築する際には、多くのリソースが関与します。直接 aws_ecs_cluster を直接利用して構築する方法もありますが、オープンソースでメンテナンスされている Terraform のモジュール terraform-aws-eks を使用することで、簡単かつ効率的に本番環境で使用可能なクラスターを作成できます。EKS クラスターを構築する手順を紹介します。

1. OpenTofu Cli をインストールします。

今回は検証環境として、Amazon EC2 の Ubuntu 22.04 を使用しました。インストールは Homebrew を通じて行いましたが、YUM や APT などのパッケージ管理ツールを使用することも可能です。OpenTofu の 公式ドキュメント を参照してください。

$ brew install opentofu

# バージョンの確認
$ tofu version
OpenTofu v1.7.0
on linux_amd64
+ provider registry.opentofu.org/hashicorp/aws v5.48.0
+ provider registry.opentofu.org/hashicorp/cloudinit v2.3.4
+ provider registry.opentofu.org/hashicorp/null v3.2.2
+ provider registry.opentofu.org/hashicorp/random v3.6.1
+ provider registry.opentofu.org/hashicorp/time v0.11.1
+ provider registry.opentofu.org/hashicorp/tls v4.0.5

2. 今回構成するファイル群は以下のようになります。

$ tree .
.
├── backend.tf
├── main.tf
├── outputs.tf
├── providers.tf
├── terraform.tfstate
├── terraform.tfvars
├── variables.tf
└── versions.tf

3. Providers の設定を行います。

$ cat << EOF > providers.tf
provider "random" {}

# https://registry.terraform.io/providers/hashicorp/aws/latest/docs#aws-configuration-reference
# 環境変数でも設定は可能です。ここでは Terraform に直接設定を入れてます。
provider "aws" {
  region = var.region
}
EOF

4. Backend 設定を行います。

設定を簡易化するため、Terraform の state ファイルをローカルに保存していますが、実際運用するときは、Amazon S3 等安全な場所に入れておきましょう。一旦ローカルに保存したあとで、tofu init -migrate-state を使えば、自動的に S3 に移動してくれますので便利です。

$ cat << EOF > backend.tf
terraform {
  backend "local" {
    path = "terraform.tfstate"
  }
}
EOF

5. versions.tf も Terraform とあまり変わらないですが、required_version の部分は利用する OpenTofu のバージョンとなります。ただし、バージョンの制限は可能でしたが、Terraform か OpenTofu に制限する方法はありません。今後 OpenTofu 独自の拡張は、.tofu のファイルに記載できようにと OpenTofu コミュニティは検討しています。

$ cat << EOF > versions.tf
terraform {
  # required opentofu version
  required_version = ">= 1.7.0"

  required_providers {
    random = {
      source = "hashicorp/random"
      version = ">= 3.6.1"
    }
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.48.0"
    }
  }
}
EOF

6. Amazon VPC の構築を行います。terraform-aws-vpc を利用することで、Amazon VPC や関連する Subnet、Nat Gateway、Route Table などのリソースをまとめて作成できます。

$ cat << EOF > main.tf

# 共通で使う変数の設定
locals {
  azs = slice(data.aws_availability_zones.available.names, 0, 3)

  tags = {
    project    = var.name_prefix
    managed_by = "opentofu"
  }
}

# data を利用して現在のリージョンから使える aws availability zones を取得します。
# この手法を使えば様々なリソースを Dynamic に生成できます。
# 他によく使われている例としてたとえば、Amazon Linux や Ubuntu の AMI の取得が挙がられます。
data "aws_availability_zones" "available" {}

# VPC 関連のリソースを構築
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "~> 5.0"

  name = var.name_prefix
  cidr = var.vpc_cidr

  azs             = local.azs
  private_subnets = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 4, k)]
  public_subnets  = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 8, k + 48)]
  intra_subnets   = [for k, v in local.azs : cidrsubnet(var.vpc_cidr, 8, k + 52)]

  enable_nat_gateway     = true
  single_nat_gateway     = true
  create_egress_only_igw = true

  # 外向けの ELB はこちらの Subnet を利用するように
  public_subnet_tags = {
    "kubernetes.io/role/elb" = 1
  }

  # 内向けの ELB はこちらの Subnet を利用するように
  private_subnet_tags = {
    "kubernetes.io/role/internal-elb" = 1
  }

  tags = local.tags
}

7. EKS 本体の構築を行います。

こちらの例では Managed Group の EKS を構築する例となります。トラブルシューティングがやすいように SSH を設定したり、addon を入れる方法も示しています。

$ cat << EOF >> main.tf

# Managed Node に ssh 用の鍵を生成
module "key_pair" {
  source  = "terraform-aws-modules/key-pair/aws"
  version = "~> 2.0"

  key_name_prefix    = var.name_prefix
  create_private_key = true

  tags = local.tags
}

# VPC の CIDR から ssh を許可する firewall ルールを定義
resource "aws_security_group" "remote_access" {
  name_prefix = "${var.name_prefix}-remote-access"
  description = "Allow remote SSH access"
  vpc_id      = module.vpc.vpc_id

  ingress {
    description = "SSH access"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = [var.vpc_cidr]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = merge(local.tags, { Name = "${var.name_prefix}-remote" })
}

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"

  cluster_name                   = var.name_prefix
  cluster_version                = var.eks_version
  cluster_endpoint_public_access = true
  cluster_ip_family              = "ipv4"
  vpc_id                         = module.vpc.vpc_id
  subnet_ids                     = module.vpc.private_subnets
  control_plane_subnet_ids       = module.vpc.intra_subnets
  create_kms_key                 = false
  cluster_encryption_config      = {}

  # EKS addon をこちらで入れます
  # 利用可能な addon 一覧はこのコマンドから確認することができます
  # $ aws eks describe-addon-versions  \
  # --query 'sort_by(addons &owner)[].{publisher: publisher, owner: owner, addonName: addonName, type: type}' \
  # --output table
  cluster_addons = {
    coredns = {
      most_recent = true
    }
    kube-proxy = {
      most_recent = true
    }
    vpc-cni = {
      most_recent    = true
      before_compute = true
      configuration_values = jsonencode({
        env = {
          AWS_VPC_K8S_CNI_EXTERNALSNAT = "true"
          
          # https://docs.aws.amazon.com/eks/latest/userguide/cni-increase-ip-addresses.html
          ENABLE_PREFIX_DELEGATION = "true"
          WARM_PREFIX_TARGET       = "1"
        }
      })
    }
  }

  # x86_64 の manged node group を利用する例となりますが、
  # その以外に、AL2_ARM_64 や Fargate なども利用が可能です
  eks_managed_node_group_defaults = {
    ami_type = "AL2_x86_64"
    # 利用可能なインスタンスタイプはこちらのページから確認することができます https://console.aws.amazon.com/ec2/home?#InstanceTypes:v=3;instanceFamily=t3,t3a;defaultVCPus=%3C%5C=2
    # vpc-cniが有効になっているため、各インスタンスタイプで利用可能なIPアドレスの数を確認する必要があります
    instance_types = ["t3a.medium"]
  }

  eks_managed_node_groups = {
    default_node_group = {
      use_custom_launch_template = false
      disk_size = 20
      remote_access = {
        ec2_ssh_key               = module.key_pair.key_pair_name
        source_security_group_ids = [aws_security_group.remote_access.id]
      }
    }
  }

  # クラスターアクセスエントリ
  # 現在の caller identity を管理者として追加する
  enable_cluster_creator_admin_permissions = true

  # 他に EKS にアクセスできる IAM Role/User はこちらで付与することも可能です。
  # access_entries = {
  #   default = {
  #     kubernetes_groups = []
  #     principal_arn     = "FILL_THIS_PRINCIPAL_ARN"

  #     policy_associations = {
  #       default = {
  #         policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminPolicy"
  #         access_scope = {
  #           type = "cluster"
  #         }
  #       }
  #     }
  #   },
  # }

  tags = local.tags
}
EOF

8. variables.tf および terraform.tfvars のファイルでは、今回利用していた変数の定義をこのように設定しています。

$ cat << EOF > variables.tf
variable "eks_version" {
  type = string
  description = "The EKS version to use"
}

variable "vpc_cidr" {
  type = string
  description = "The CIDR block for the VPC"
}

variable "name_prefix" {
  type = string
  description = "The prefix to use for all resources"
}

variable "region" {
  type = string
  description = "The AWS region to deploy to"
}
EOF

$ cat << EOF > terraform.tfvars
eks_version = "1.29"
name_prefix = "opentofu-eks-example"
vpc_cidr    = "10.0.0.0/16"
region      = "ap-northeast-1"
EOF

9. 構築してみましょう。OpenTofu を使用した構築プロセスは、Terraform での経験と全く同様です。初めに init コマンドで初期化を行い、次に plan コマンドで変更内容の確認をし、最後に apply コマンドで実際に環境に変更を反映します。

$ tofu init
$ tofu plan -out ./plan
$ tofu apply ./plan

まとめ

本ブログでは、OpenTofu を活用してインフラストラクチャを自動化する方法を解説しました。

OpenTofu を利用することで、インフラストラクチャの自動化が簡単に行えるようになります。今回 OpenTofu を触ってみましたが、Terraform との互換性が高く、試したバージョンでは、required_version 以外にコードの変更は不要ですし、動きも安定していました。また、TF_DEBUG など、これまで Terraform の知見をそのまま使えるため、使いやすいのではないかと思います。現時点は Terraform の Doc サイトを見ることになりますが、ぜひ、この記事を参考にご自身の環境で OpenTofu を試してみてください。


builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

邵 正
アマゾン ウェブ サービス ジャパン合同会社
ソリューションアーキテクト

Linux カーネル、コンテナ、データベースにフォーカスしています。お客様と協力して様々なクラウドインフラストラクチャのチャレンジを一緒に解決することで、よりヘルシーな設計・運用ができるように、サポートしています。

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する