Slide 1

Slide 1 text

IaCのサンプルコードを読む会 ⽇本仮想化技術株式会社 [email protected] 2023/12/20 1

Slide 2

Slide 2 text

⽇本仮想化技術株式会社 概要 • 社名:⽇本仮想化技術株式会社 • 英語名:VirtualTech Japan Inc. • 設⽴:2006年12⽉ • 資本⾦:3,000万円 • 本社:東京都渋⾕区渋⾕1-8-1 • 取締役:宮原 徹(代表取締役社⻑兼CEO)、伊藤 宏通(取締役CTO) • スタッフ:11名(うち、8名が仮想化技術専⾨エンジニアです) • URL:http://VirtualTech.jp/ • 仮想化技術に関する研究および開発 • 仮想化技術に関する各種調査 • 仮想化技術に関連したソフトウェアの開発 • 仮想化技術を導⼊したシステムの構築 • OpenStackの導⼊⽀援・新規機能開発 2 ベンダーニュートラルな 独⽴系仮想化技術の エキスパート集団 会社概要

Slide 3

Slide 3 text

発表者について • たなかともあき • 技術部所属 • お仕事 • DevOpsのOps • 執筆 • Think IT https://thinkit.co.jp/series/10843 • Software Design https://gihyo.jp/magazine/SD/archive/2023/202303 https://gihyo.jp/magazine/SD/archive/2023/202311 3

Slide 4

Slide 4 text

アジェンダ 4 • インフラ構成 • ファイル構成 • コードを読む

Slide 5

Slide 5 text

インフラ構成 5

Slide 6

Slide 6 text

インフラ構成 6

Slide 7

Slide 7 text

ファイル構成 7

Slide 8

Slide 8 text

ファイル構成 8 . ├── main.tf ├── outputs.tf ├── terraform.tfvars ├── variables.tf └── versions.tf

Slide 9

Slide 9 text

コードを読む 9

Slide 10

Slide 10 text

コードを読む 1. versions.tf 2. variables.tf 3. terraform.tfvars 4. main.tf 5. outputs.tf 10

Slide 11

Slide 11 text

versions.tf 11

Slide 12

Slide 12 text

versions.tf 12 terraform { required_version = "~> 1.6.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.29.0" } helm = { source = "hashicorp/helm" version = "~> 2.12.1" } } } • Terraformのバージョンを指定 • ~> ⼀番右のバージョンが指定された数値以上 • 1.6.0以上、1.7.0未満

Slide 13

Slide 13 text

13

Slide 14

Slide 14 text

14

Slide 15

Slide 15 text

15

Slide 16

Slide 16 text

16

Slide 17

Slide 17 text

variables.tf 17

Slide 18

Slide 18 text

variables.tf 18 variable "aws_account_id" { description = "環境取り違え防⽌のためTerraform実⾏対象のアカウントID" type = number } variable "aws_region" { description = "リソース作成先のAWSリージョン" type = string } variable "aws_vpc_cidr_block" { description = "VPC CIDR Block" type = string default = "10.0.0.0/16" } ... • デフォルト値が設定されていないの で必須の変数 • デフォルト値が設定されているので オプショナルな変数

Slide 19

Slide 19 text

terraform.tfvars 19

Slide 20

Slide 20 text

terraform.tfvars 20 aws_account_id = 123456789012 aws_region = "ap-northeast-1" #aws_vpc_cidr_block = "10.0.0.0/16” aws_eks_cluster_name = "demo" aws_eks_cluster_version = "1.28" aws_eks_nodegroups = 2 podinfo_chart_version = "6.5.3" podinfo_namespace = "app"

Slide 21

Slide 21 text

main.tf 21

Slide 22

Slide 22 text

AWSプロバイダー 22 provider "aws" { region = var.aws_region allowed_account_ids = [var.aws_account_id] }

Slide 23

Slide 23 text

VPC 23

Slide 24

Slide 24 text

VPC 24 resource "aws_vpc" "demo" { cidr_block = var.aws_vpc_cidr_block }

Slide 25

Slide 25 text

Public Subnet 25

Slide 26

Slide 26 text

利⽤可能なアベイラビリティゾーン 26 data "aws_availability_zones" "available" { state = "available" }

Slide 27

Slide 27 text

Public Subnet 27 resource "aws_subnet" "public" { for_each = toset(data.aws_availability_zones.available.names) vpc_id = aws_vpc.demo.id availability_zone = each.value cidr_block = cidrsubnet(var.aws_vpc_cidr_block, 8, index(tolist(toset(data.aws_availability_zones.available.names)), each.key)) }

Slide 28

Slide 28 text

28

Slide 29

Slide 29 text

listとset 29 $ echo 'tolist(["b", "a", "b", "c"])' | terraform console tolist([ "b", "a", "b", "c", ]) $ echo 'toset(["b", "a", "b", "c"])' | terraform console toset([ "a", "b", "c", ])

Slide 30

Slide 30 text

indexの実⾏結果 30 $ echo 'index(["ap-northeast-1c", "ap-northeast-1a", "ap-northeast-1d", "ap-northeast-1a"], "ap-northeast-1a")' | terraform console 1 $ echo 'index(["ap-northeast-1c", "ap-northeast-1a", "ap-northeast-1d", "ap-northeast-1a"], "ap-northeast-1c")' | terraform console 0 $ echo 'index(tolist(toset(["ap-northeast-1c", "ap-northeast-1a", "ap- northeast-1d", "ap-northeast-1a"])), "ap-northeast-1a")' | terraform console 0 $ echo 'index(tolist(toset(["ap-northeast-1c", "ap-northeast-1a", "ap- northeast-1d", "ap-northeast-1a"])), "ap-northeast-1c")' | terraform console 1

Slide 31

Slide 31 text

cidrsubnetの実⾏結果 31 $ echo 'cidrsubnet("10.0.0.0/16", 8, 0)' | terraform console "10.0.0.0/24" $ echo 'cidrsubnet("10.0.0.0/16", 8, 1)' | terraform console "10.0.1.0/24" $ echo 'cidrsubnet("10.0.0.0/16", 8, 2)' | terraform console "10.0.2.0/24" $ echo 'cidrsubnet("10.0.0.0/16", 4, 0)' | terraform console "10.0.0.0/20" $ echo 'cidrsubnet("10.0.0.0/16", 4, 1)' | terraform console "10.0.16.0/20" $ echo 'cidrsubnet("10.0.0.0/16", 4, 2)' | terraform console "10.0.32.0/20"

Slide 32

Slide 32 text

Public Subnet 32 resource "aws_subnet" "public" { vpc_id = aws_vpc.demo.id availability_zone = "ap-northeast-1a" cidr_block = "10.0.0.0/24" } resource "aws_subnet" "public" { vpc_id = aws_vpc.demo.id availability_zone = "ap-northeast-1c" cidr_block = "10.0.1.0/24" } resource "aws_subnet" "public" { vpc_id = aws_vpc.demo.id availability_zone = "ap-northeast-1d" cidr_block = "10.0.2.0/24" }

Slide 33

Slide 33 text

Public Subnet 33 aws_subnet.public["ap-northeast-1a"].id aws_subnet.public["ap-northeast-1c"].id aws_subnet.public["ap-northeast-1d"].id

Slide 34

Slide 34 text

Internet GatewayとRoute Table 34

Slide 35

Slide 35 text

Internet Gateway 35 resource "aws_internet_gateway" "public" { vpc_id = aws_vpc.demo.id }

Slide 36

Slide 36 text

Public Route Table 36 resource "aws_route_table" "public" { vpc_id = aws_vpc.demo.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.public.id } }

Slide 37

Slide 37 text

Route Table Association 37 resource "aws_route_table_association" "public" { for_each = aws_subnet.public subnet_id = each.value.id route_table_id = aws_route_table.public.id }

Slide 38

Slide 38 text

Private Subnet 38

Slide 39

Slide 39 text

Private Subnet 39 resource "aws_subnet" "private" { for_each = toset(data.aws_availability_zones.available.names) vpc_id = aws_vpc.demo.id availability_zone = each.value cidr_block = cidrsubnet(var.aws_vpc_cidr_block, 8, index(tolist(toset(data.aws_availability_zones.available.names)), each.key) + length(aws_subnet.public)) }

Slide 40

Slide 40 text

NAT GatewayとRoute Table 40

Slide 41

Slide 41 text

Elastic IP 41 resource "aws_eip" "nat_gateway" {}

Slide 42

Slide 42 text

NAT Gateway 42 resource "aws_nat_gateway" "private" { subnet_id = aws_subnet.public[data.aws_availability_zones.available.names[0]].id allocation_id = aws_eip.nat_gateway.id } resource "aws_nat_gateway" "private" { subnet_id = aws_subnet.public["ap-northeast-1a"].id allocation_id = aws_eip.nat_gateway.id }

Slide 43

Slide 43 text

Private Route Table 43 resource "aws_route_table" "private" { vpc_id = aws_vpc.demo.id route { cidr_block = "0.0.0.0/0" nat_gateway_id = aws_nat_gateway.private.id } }

Slide 44

Slide 44 text

Route Table Association 44 resource "aws_route_table_association" "private" { for_each = aws_subnet.private subnet_id = each.value.id route_table_id = aws_route_table.private.id }

Slide 45

Slide 45 text

Public 45

Slide 46

Slide 46 text

Private 46

Slide 47

Slide 47 text

EKS 47

Slide 48

Slide 48 text

Policy Document 48 data "aws_iam_policy_document" "cluster_assume_role" { statement { effect = "Allow" principals { type = "Service" identifiers = ["eks.amazonaws.com"] } actions = ["sts:AssumeRole"] } }

Slide 49

Slide 49 text

IAM Role 49 resource "aws_iam_role" "cluster" { name_prefix = "${var.aws_eks_cluster_name}-cluster-" assume_role_policy = data.aws_iam_policy_document.cluster_assume_role.json managed_policy_arns = [ "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy", "arn:aws:iam::aws:policy/AmazonEKSVPCResourceController", ] }

Slide 50

Slide 50 text

EKS 50 resource "aws_eks_cluster" "demo" { name = var.aws_eks_cluster_name role_arn = aws_iam_role.cluster.arn vpc_config { subnet_ids = [for k, v in aws_subnet.public : v.id] } version = var.aws_eks_cluster_version }

Slide 51

Slide 51 text

subnet_idsに渡してるリスト 51 $ terraform console > [for k, v in aws_subnet.public : v.id] [ "subnet-0bf86ee5ce3ef0ef8", "subnet-0136c78fa0ebcd5c1", "subnet-01c33f17f65692b33", ]

Slide 52

Slide 52 text

EKS Addon 52 resource "aws_eks_addon" "vpc_cni" { cluster_name = aws_eks_cluster.demo.name addon_name = "vpc-cni" } resource "aws_eks_addon" "coredns" { cluster_name = aws_eks_cluster.demo.name addon_name = "coredns" } resource "aws_eks_addon" "kube_proxy" { cluster_name = aws_eks_cluster.demo.name addon_name = "kube-proxy" }

Slide 53

Slide 53 text

EKSクラスター 53

Slide 54

Slide 54 text

EKSクラスターアドオン 54

Slide 55

Slide 55 text

Nodegroup 55

Slide 56

Slide 56 text

Policy Document 56 data "aws_iam_policy_document" "nodegroup_assume_role" { statement { effect = "Allow" principals { type = "Service" identifiers = ["ec2.amazonaws.com"] } actions = ["sts:AssumeRole"] } }

Slide 57

Slide 57 text

IAM Role 57 resource "aws_iam_role" "nodegroup" { name_prefix = "${aws_eks_cluster.demo.name}-nodegroup-" assume_role_policy = data.aws_iam_policy_document.nodegroup_assume_role.json managed_policy_arns = [ "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly", "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy", "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy", "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" ] }

Slide 58

Slide 58 text

Nodegroup 58 resource "aws_eks_node_group" "demo" { cluster_name = aws_eks_cluster.demo.name node_group_name_prefix = "${aws_eks_cluster.demo.name}-" node_role_arn = aws_iam_role.nodegroup.arn subnet_ids = [for k, v in aws_subnet.private : v.id] scaling_config { min_size = 1 max_size = var.aws_eks_nodegroups desired_size = var.aws_eks_nodegroups } update_config { max_unavailable = 1 } }

Slide 59

Slide 59 text

Nodegroup 59

Slide 60

Slide 60 text

アプリケーションのデプロイ 60

Slide 61

Slide 61 text

Helmプロバイダー 61 provider "helm" { kubernetes { host = aws_eks_cluster.demo.endpoint cluster_ca_certificate = base64decode(data.aws_eks_cluster.demo.certificate_authority.0.data) token = data.aws_eks_cluster_auth.demo.token } }

Slide 62

Slide 62 text

EKS Cluster Data Sources 62 data "aws_eks_cluster" "demo" { name = aws_eks_cluster.demo.name } data "aws_eks_cluster_auth" "demo" { name = aws_eks_cluster.demo.name }

Slide 63

Slide 63 text

アプリケーションのデプロイ 63 resource "helm_release" "podinfo" { name = "podinfo" repository = "https://stefanprodan.github.io/podinfo" chart = "podinfo" version = var.podinfo_chart_version namespace = var.podinfo_namespace create_namespace = true wait = true set { name = "replicaCount" value = var.aws_eks_nodegroups } }

Slide 64

Slide 64 text

outputs.tf 64

Slide 65

Slide 65 text

outputs.tf 65 output "aws_eks_update_kubeconfig" { value = "aws eks update-kubeconfig --name ${var.aws_eks_cluster_name} -- alias ${var.aws_eks_cluster_name}" } output "port_forward_command" { value = "kubectl port-forward -n ${var.podinfo_namespace} services/podinfo 9898:9898" }

Slide 66

Slide 66 text

確認 66 $ terraform output -raw aws_eks_update_kubeconfig aws eks update-kubeconfig --name demo --alias demo $ eval "$(terraform output -raw aws_eks_update_kubeconfig)" Updated context demo in /Users/tanaka/.kube/config $ kubectl get pods -n app NAME READY STATUS RESTARTS AGE podinfo-65d9887677-7l45r 1/1 Running 0 35m podinfo-65d9887677-gc45s 1/1 Running 0 35m

Slide 67

Slide 67 text

確認 67 $ terraform output -raw port_forward_command kubectl port-forward -n app services/podinfo 9898:9898 $ eval "$(terraform output -raw port_forward_command)" Forwarding from 127.0.0.1:9898 -> 9898 Forwarding from [::1]:9898 -> 9898 Handling connection for 9898 ...

Slide 68

Slide 68 text

確認 68

Slide 69

Slide 69 text

関連リンク • Terraform AWS provider https://registry.terraform.io/providers/hashicorp/aws/latest/ docs • Terraform Helm provider https://registry.terraform.io/providers/hashicorp/helm/latest /docs • サンプルコード https://github.com/VirtualTech-DevOps/terraform- codereading-demo 69

Slide 70

Slide 70 text

おしまい 70

Slide 71

Slide 71 text

71