6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Amazon EKS #2Advent Calendar 2019

Day 15

Pulumi(golang)でEKS環境を構築する

Last updated at Posted at 2019-12-15

はじめに

AWS環境構築をコード化するためのツールとしてAWS CDK、Terraform、Ansible、Pulumiなどがあります。
今回は記述言語をgolangとしてPulumiでEKS環境を構築します。

Pulumiとは

「Modern Infrastructure as Code」を掲げた、クラウドインフラを構成するためのOSSです。
対応言語はJavaScript、TypeScript、Python、golangなどが挙げられます。
GCP、AWS、Azureに対応しており、Kubernetesリソースをデプロイすることも可能です。

まだ新しいこともあり公式ドキュメントや公式サンプルコードはTypeScript中心となっています。
普段バックエンドを触っている人にとってはgolangの方が嬉しいですね。

環境情報

macOS Mojave 10.14.1
pulumi: 1.7.0
golang: 1.12.4

実装

golangを使用して実装します。
事前にPulumiやgolangはインストールされていることは前提とします。

$GOPATH/src/pulumi-eks配下に以下のようにファイルを配置します。

├── Pulumi.yaml
├── main.go
├── iam.go
├── network.go
└── eks.go

Pulumi.yaml

プロジェクトのメタデータをPulumi.yamlに記述します。

Pulumi.yaml
name: pulumi-eks
runtime: go
description: pulumi EKS

main.go

EKSリソースを作成するための関数呼び出しをmain.goに記述します。

main.go
package main

import (
	"github.com/pulumi/pulumi/sdk/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		err := createEks(ctx)
		if err != nil {
			return err
		}
		return nil
	})
	
}

iam.go

EKSクラスタとEKSワーカーに付与するIAM Roleをiam.goに記述します。
このIAM Roleを関数の返り値とし、EKSクラスタ作成時に使用します。

iam.go
package main

import (
	"encoding/json"
	"github.com/pulumi/pulumi-aws/sdk/go/aws/iam"
	"github.com/pulumi/pulumi/sdk/go/pulumi"
)

func createEksClusterRole(ctx *pulumi.Context, roleName string) (*iam.Role, error) {
	assumeRolePolicy, err  := getEksAssumeRolePolicy()
	if err != nil {
		return nil, err
	}
	role, err := iam.NewRole(ctx, roleName, &iam.RoleArgs{
		Name: roleName,
		AssumeRolePolicy: assumeRolePolicy,
	})
	if err != nil {
		return nil, err
	}

	_, err = iam.NewRolePolicyAttachment(ctx, "eks-cluster-policy", &iam.RolePolicyAttachmentArgs{
		PolicyArn: "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
		Role: role.Name(),
	})
	if err != nil {
		return nil, err
	}

	_, err = iam.NewRolePolicyAttachment(ctx, "eks-service-policy", &iam.RolePolicyAttachmentArgs{
		PolicyArn: "arn:aws:iam::aws:policy/AmazonEKSServicePolicy",
		Role: role.Name(),
	})
	if err != nil {
		return nil, err
	}

	return role, nil
}

func getEksAssumeRolePolicy() (interface{}, error) {
	policy, _ := json.Marshal(map[string]interface{}{
		"Version": "2012-10-17",
		"Statement": []map[string]interface{}{
			{
				"Effect": "Allow",
				"Principal": map[string]interface{}{
					"Service": "eks.amazonaws.com",
				},
				"Action": "sts:AssumeRole",
			},
		},
	})
	return string(policy), nil
}

func createEksWorkerRole(ctx *pulumi.Context, roleName string) (*iam.Role, error) {
	assumeRolePolicy, err  := getEc2AssumeRolePolicy()
	if err != nil {
		return nil, err
	}
	role, err := iam.NewRole(ctx, roleName, &iam.RoleArgs{
		Name: roleName,
		AssumeRolePolicy: assumeRolePolicy,
	})
	if err != nil {
		return nil, err
	}

	_, err = iam.NewRolePolicyAttachment(ctx, "eks-worker-policy", &iam.RolePolicyAttachmentArgs{
		PolicyArn: "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy",
		Role: role.Name(),
	})
	if err != nil {
		return nil, err
	}

	_, err = iam.NewRolePolicyAttachment(ctx, "eks-cni-policy", &iam.RolePolicyAttachmentArgs{
		PolicyArn: "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy",
		Role: role.Name(),
	})
	if err != nil {
		return nil, err
	}

	_, err = iam.NewRolePolicyAttachment(ctx, "ecr-policy", &iam.RolePolicyAttachmentArgs{
		PolicyArn: "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly",
		Role: role.Name(),
	})
	if err != nil {
		return nil, err
	}

	return role, nil
}

func getEc2AssumeRolePolicy() (interface{}, error) {
	policy, _ := json.Marshal(map[string]interface{}{
		"Version": "2012-10-17",
		"Statement": []map[string]interface{}{
			{
				"Effect": "Allow",
				"Principal": map[string]interface{}{
					"Service": "ec2.amazonaws.com",
				},
				"Action": "sts:AssumeRole",
			},
		},
	})
	return string(policy), nil
}

network.go

VPCやサブネットをnetwork.goで作成します。
AZは事前に設定するリージョンから検出されます。
サブネット情報などをまとめて返り値としています。

network.go
package main

import (
	"fmt"
	"github.com/pulumi/pulumi-aws/sdk/go/aws"
	"github.com/pulumi/pulumi-aws/sdk/go/aws/ec2"
	"github.com/pulumi/pulumi/sdk/go/pulumi"
)

type Network struct {
	AvailabilityZones interface{}
	VpcId interface{}
	SubnetIds interface{}
}

func createNetwork(ctx *pulumi.Context) (*Network, error){
	azs, err := aws.LookupAvailabilityZones(ctx, &aws.GetAvailabilityZonesArgs{
		State: "available",
	})
	if err != nil {
		return nil, err
	}
	vpc, err := ec2.NewVpc(ctx, "eks-vpc", &ec2.VpcArgs{
		CidrBlock: "10.0.0.0/16",
	})
	if err != nil {
		return nil, err
	}

	ig, err := ec2.NewInternetGateway(ctx, "eks-ig", &ec2.InternetGatewayArgs{
		VpcId: vpc.ID(),
	})
	if err != nil {
		return nil, err
	}
	
	rt, err := ec2.NewRouteTable(ctx, "eks-rt", &ec2.RouteTableArgs{
		VpcId: vpc.ID(),
		Routes: []map[string]interface{}{{
			"cidrBlock": "0.0.0.0/0",
			"gatewayId": ig.ID(),
		},},
	})
	if err != nil {
		return nil, err
	}

	var subnetIds []pulumi.IDOutput
	usePrivateSubnets := false
	for i, az := range azs.Names.([]interface{}) {
		subnetName := fmt.Sprintf("eks-sn-%d", i)
		cidrBlock := fmt.Sprintf("10.0.%d.0/24", i)
		subnet, err := ec2.NewSubnet(ctx, subnetName, &ec2.SubnetArgs{
			VpcId: vpc.ID(),
			CidrBlock: cidrBlock,
			AvailabilityZone: az,
			MapPublicIpOnLaunch: !usePrivateSubnets,
		})
		if err != nil {
			return nil, err
		}

		rtaName := fmt.Sprintf("eks-rta-%d", i)
		_, err = ec2.NewRouteTableAssociation(ctx, rtaName, &ec2.RouteTableAssociationArgs{
			SubnetId: subnet.ID(),
			RouteTableId: rt.ID(),
		})
		if err != nil {
			return nil, err
		}
		subnetIds = append(subnetIds, subnet.ID())
	}

	network := new(Network)
	network.AvailabilityZones = azs
	network.VpcId = vpc.ID()
	network.SubnetIds = subnetIds

	return network, nil
}

eks.go

eks.goでEKSクラスタとEKSワーカーを作成します。
その前段としてNetworkとIAMの関数を呼び出しています。
今回はManaged Node Groupsでワーカーを立てるので事前にSecurity Groupを指定する必要がありません。

eks.go
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/go/aws/eks"
	"github.com/pulumi/pulumi/sdk/go/pulumi"
)

func createEks(ctx *pulumi.Context) error {
	network, err := createNetwork(ctx)
	if err != nil {
		return err
	}

	eksClusterRole, err := createEksClusterRole(ctx, "eks-cluster-role")
	if err != nil {
		return err
	}

	eksWorkerRole, err := createEksWorkerRole(ctx, "eks-worker-role")
	if err != nil {
		return err
	}

	eksClusterName := "eks-cluster"
	eksCluster, err := eks.NewCluster(ctx, eksClusterName, &eks.ClusterArgs{
		Name: eksClusterName,
		RoleArn: eksClusterRole.Arn(),
		VpcConfig: map[string]interface{}{
			"subnetIds": network.SubnetIds,
		},
	})
	if err != nil {
		return err
	}

	eksWorkerName := "eks-worker"
	_, err = eks.NewNodeGroup(ctx, eksWorkerName, &eks.NodeGroupArgs{
		ClusterName: eksCluster.Name(),
		NodeGroupName: eksWorkerName,
		NodeRoleArn: eksWorkerRole.Arn(),
		ScalingConfig: map[string]int{
			"DesiredSize": 2,
			"MaxSize": 3,
			"MinSize": 1,
		},
		SubnetIds: network.SubnetIds,
	})
	if err != nil {
		return err
	}

	return nil
}

実行

作成したプラグラムを実行します。

1. スタックの作成

$ pulumi stack init pulumi-eks

2. Pulumi AWSプラグインのインストール

$ pulumi plugin install resource aws 1.17.0

3. AWSリージョンの設定

$ pulumi config set aws:region ap-northeast-1

4. golangプログラムのコンパイル

$ go get .
$ go install .

5. デプロイ

$ pulumi up
Updating (eks):

     Type                              Name                Status      
 +   pulumi:pulumi:Stack               pulumi-eks-eks      created     
 +   ├─ aws:ec2:Vpc                    eks-vpc             created     
 +   ├─ aws:iam:Role                   eks-cluster-role    created     
 +   ├─ aws:iam:Role                   eks-worker-role     created     
 +   ├─ aws:iam:RolePolicyAttachment   eks-cluster-policy  created     
 +   ├─ aws:iam:RolePolicyAttachment   eks-service-policy  created     
 +   ├─ aws:iam:RolePolicyAttachment   eks-worker-policy   created     
 +   ├─ aws:iam:RolePolicyAttachment   eks-cni-policy      created     
 +   ├─ aws:iam:RolePolicyAttachment   ecr-policy          created     
 +   ├─ aws:ec2:Subnet                 eks-sn-1            created     
 +   ├─ aws:ec2:Subnet                 eks-sn-2            created     
 +   ├─ aws:ec2:InternetGateway        eks-ig              created     
 +   ├─ aws:ec2:Subnet                 eks-sn-0            created     
 +   ├─ aws:ec2:RouteTable             eks-rt              created     
 +   ├─ aws:eks:Cluster                eks-cluster         created     
 +   ├─ aws:ec2:RouteTableAssociation  eks-rta-1           created     
 +   ├─ aws:ec2:RouteTableAssociation  eks-rta-0           created     
 +   ├─ aws:ec2:RouteTableAssociation  eks-rta-2           created     
 +   └─ aws:eks:NodeGroup              eks-worker          created     
 
Resources:
    + 19 created

6. 確認

EKSクラスタが作成されるのを確認できます。
スクリーンショット 2019-12-15 23.56.51.png

コマンドラインでも確認できます。

$ aws eks update-kubeconfig --name eks-cluster

$ kubectl get node
kubectl get node
NAME                                           STATUS   ROLES    AGE   VERSION
ip-10-0-0-35.ap-northeast-1.compute.internal   Ready    <none>   19m   v1.14.7-eks-1861c5
ip-10-0-2-93.ap-northeast-1.compute.internal   Ready    <none>   19m   v1.14.7-eks-1861c5

おわりに

golangを記述言語としてPulumiを使用してEKSクラスタが構築できることを確認しました。
golangを使用した場合のドキュメントがもっと増えることを期待します。

6
2
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
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?