LoginSignup
2

More than 3 years have passed since last update.

posted at

updated at

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

はじめに

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を使用した場合のドキュメントがもっと増えることを期待します。

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
What you can do with signing up
2