5
0

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 1 year has passed since last update.

Terraform Cloud Businessを利用し、Tanzu上のKubernetesリソースを管理する

Last updated at Posted at 2021-12-19

HashiCorp JapanでSolutions Engineerをしております、伊藤忠司と申します。
TUNA-JP (Tanzu User kNowledge Assembly Japan)のアドベントカレンダーの20日目という事で、HashiCorpが提供しているTerraform Cloud Businessで利用出来るTerraform Cloud AgentをTanzu上で動かして、Tanzu上のKubernetesリソースをTerraformのフレームワークで管理するという内容を紹介したいと思います。

以下の様な流れで本記事は記載しています。

目次

Terraform Cloud Businessとは

先ず、Terraform Cloud Business(以下TFCB)についてです。TFCBは、HashiCorpがマネージド・サービスとして提供しているTerraform Cloud(以下TFC)の有償版になります。

TerraformはIaCの一つでして、プロビジョニングに関わる手動での運用作業をコード化し、自働化する事が出来ます。

ヒトがクラウドのGUIやAPIを直接叩いて実行する代わりに、TerraformがクラウドのAPIを実行し、コードに従ってインフラのプロビジョニングを行います。これによって、ヒトが実施しないといけない作業を削減する事が出来ます。

また、Terraformはマルチクラウド環境で同じ仕組みが利用出来るようになっておりますので、プロビジョニング先の環境が、AWS、Azure、GCP、vSphereだろうと、同じワークフロー、同一の構成要素、同一の文法を用いて、プロビジョニングを実施して頂く事が可能になりますので、汎用性が非常に高いという特徴を持っています。

Terraformですが、実際に扱っていく際の典型的なワークフローは以下の様な形になります。
tf-workflow.png

Hashicorp Configuration Language(以下HCL)で記述されたソースコード、あとはクラウドにインフラをプロビジョニングする際のシークレット情報などの変数を用いて、terraform plan というコマンドを実行してあげます。

そうすると、TerraformがHCLを実行する場合に、どういった変更が行われるか?という、プラン内容を出力してくれます。そのあとは、プラン内容のレビューを実施します。

その内容が社内のポリシーに沿っているかどうか等をレビューし、時には修正依頼を掛けたりし、内容に問題がなければ、terraform apply を実行し、プロビジョニングを行っていくというワークフローになります。

プロビジョニングが無事成功すると、インフラの状態を記述したファイルであるステートファイルが生成されるといった形になります。

インフラを更新する様な際は、更新したHCL、最新のステートファイル、必要な変数とともに、terraform plan を実行し、レビューをし、問題無ければ、terraform apply というワークフローを回していく事になります。

ただ、Terraformを安全に利用していくためには、以下のような考慮事項が出てきます。

tf-workflow-considerations.png

この様な考慮事項に関して、HashiCorpが考えるTerraformを扱う上でのベストプラクティスに沿って、OOTBでTerraformをお使い頂ける様になっているのが、有償版Terraformになります。TFCBも、HashiCorpが提供している有償版Terraformの一つになります。

tf-workflow-with-enterprise.png

Terraform Cloud Agentとは

有償版Terraformをご利用頂く事で、OOTBで色々な機能が使える様になりますが、TFCのネットワーク構成を検討していく際の課題として挙げられるのが、TFCからプロビジョニング対象へのインバウンドでのリクエストがあります。

TFCが利用するIPレンジは公開されておりますので、お使いの環境のファイアーウォール等に対する設定変更して頂く事で利用する事が出来ますが、作業負荷の高い変更作業かと思います。
また、そもそも、自社環境に対してインバウンドのアクセスを許可するというのはセキュリティポリシー上難しいというのが、エンタープライズのお客様では特に多いのでは無いかと思います。

tfc-network-issue.png

この様なTFC利用時のネットワーク構成課題を解決するソリューションとして、TFCBで利用出来るTerraform Cloud Agents(以下TFC Agent)という機能が存在します。

tfcb-network.png

この機能をご利用頂くと、TFC上のWorkerインスタンスが対象となる環境に対して、プロビジョニングを行っていくのではなく、TFC Agentがプロビジョニングを行っていく形になります。

自社のデータセンターやクラウド環境にデプロイしたTFC AgentとTFCが連携し、TFCに発生した様々なイベント、Version Control System(VCS)の更新情報とかを、TFC Agentがアウトバウンドで取得していく形になります。
TFC内の更新をポーリングし、何かしら変更があったら、TFC Agentから、その更新を取得し、プロビジョニング対象となる環境に対して、TFC Agent上でterraform CLIを実行し、プロビジョニングを行っていくという形になります。

この構成を取る場合、HTTPSのエンドポイントが唯一のエンドポイントになりますので、TFC Agentからこのエンドポイントに対して、アウトバウンドのリクエストを出来るようにしてあげれば、他の設定は必要なく、かつエンドポイントのURLが変わる事は無いので、設定も永続的にお使い頂く事が可能になります。

TFC Agentは、Linuxのバイナリー、もしくはDockerコンテナとして利用可能ですので、プラットフォームフリーでお使い頂ける様になっております。

この後は、このTFC AgentをTanzu Kubernetes Grid multicloud(以下TKGm)上にデプロイし、TKGm上にデプロイしたTFC Agentを利用し、TKGm上にKubernetesリソースをTerraformのワークフローに従って、プロビジョニングを行っていくという内容をご紹介したいと思います。

Terraform Cloud Agentのセットアップ

TFCBをご利用出来る事が前提になりますが、こちらのManaging Agent Poolsの手順に従って、TFC上でAgent Poolを作成します。

Agent Poolを作成し、新しいトークンを作成するという手順を実施すると、以下のような形でDockerコンテナとして、TFC Agentを動かす際のインストラクションが表示されます。

# Connect to your Docker host and set the following environment variables. TFC_AGENT_NAME is optional.
$ export TFC_AGENT_TOKEN=...
$ export TFC_AGENT_NAME=<my_agent_name>

# Once the environment is configured, run the Docker container with the following command or download the agent file.
$ docker run -e TFC_AGENT_TOKEN -e TFC_AGENT_NAME hashicorp/tfc-agent:latest

今回はTKGmを用いて、TFC Agentの管理はKubernetesに任せる形にしますので、TFC_AGENT_TOKENのみコピーしておきます。

Terraform Cloud AgentのKubernetesへのデプロイ

Kubernetes環境にTFC Agentをデプロイしていきます。

ここで利用するKubernetesクラスタとしては、TKGm on vSphereを利用していますが、Tanzu Community Editionsでも同じ様なステップでKubernetesクラスタ環境をインストール出来ると思います。

TFC Agent用のネームスペースを作成し、先程取得したTFC_AGENT_TOKENを作成したネームスペースにsecretリソースとして作成します。

export KUBECONFIG=~/kubeconfig-lab-fender
k create ns tfc-agent
# 先程取得したTFC_AGENT_TOKENをBase64でエンコードします
echo -n "t..." |base64

Base64で出力された値をkeytoken に対するvalueとして、以下のようなマニフェストtfc-agent-secrets.yml を作成します。

apiVersion: v1
kind: Secret
metadata:
  name: agent
type: Opaque
data:
  token: t...

TFC Agentトークンを定義したSecretリソースを作成します。

k -n tfc-agent apply -f tfc-agent-secrets.yml

次に、TKGmのWorkloadクラスタにTFC AgentをデプロイさせるDeploymentリソースをdeployment-tfc-agent.ymlというファイル名で作成します。以下の様な形でマニフェストファイルを作成し、環境変数TFC_AGENT_TOKENTFC_AGENT_NAMEを設定します。

ここでは指定していませんが、TF_VAR_xxxという変数名で変数を指定しておく事で、Terraformを実行する際のTerraform変数として、TFC Agentに持たせてあげる事も可能です。

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: tfc-agent
  name: tfc-agent
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tfc-agent
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: tfc-agent
    spec:
      containers:
      - image: hashicorp/tfc-agent:1.0.2
        name: tfc-agent
        resources:
          requests:
            memory: "64Mi"
            cpu: "250m"
          limits:
            memory: "128Mi"
            cpu: "500m"
        env:
        - name: "TFC_AGENT_TOKEN"
          valueFrom:
            secretKeyRef:
              name: agent
              key: token
        - name: "TFC_AGENT_NAME"
          value: "tfc-agent-on-fender"
status: {}

このマニフェストファイルを使って、TKGmのWorkloadクラスタ上に、TFC Agentをデプロイします。

k -n tfc-agent apply -f deployment-tfc-agent-tanzu.yml 
$ k -n tfc-agent get all
NAME                                       READY   STATUS    RESTARTS   AGE
pod/tfc-agent-69cd86fb9f-mxt8r             1/1     Running   0          12m

NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/tfc-agent             1/1     1            1           45d

NAME                                             DESIRED   CURRENT   READY   AGE
replicaset.apps/tfc-agent-69cd86fb9f             1         1         1       12m

無事、TKGm上へTFC Agentのデプロイが完了したので、このTFC Agentを利用し、TKGm上にTerraformのワークフローを用いて、Kubernetesリソースをプロビジョニングしていきます。

Terraformを使ったKubernetesリソースのプロビジョニング

環境としては、以下のような形で準備しています。TerraformがKubernetes providerを利用する際に必要なシークレット情報は、VaultのK/Vストアに暗号化し、保管してあります。

pic-tfc-agent-on-tanzu.png

また、今回利用したTerraformのコードは以下の通りです。

main.tf
provider "vault" {
  address         = var.vault_address
  skip_tls_verify = true
  auth_login {
    path = "auth/approle/login"
    parameters = {
      role_id   = var.login_approle_id
      secret_id = var.login_approle_secret_id
    }
  }
}

data "vault_generic_secret" "vsphere" {
  path = "secret/lab_1t0chu_dev/tkg_fender"
}

provider "kubernetes" {
  host                   = data.vault_generic_secret.vsphere.data["host"]
  client_certificate     = data.vault_generic_secret.vsphere.data["client_certificate"]
  client_key             = data.vault_generic_secret.vsphere.data["client_key"]
  cluster_ca_certificate = data.vault_generic_secret.vsphere.data["cluster_ca_certificate"]
}

resource "kubernetes_namespace" "example" {
  metadata {
    labels = {
      mylabel = "tanzu-test"
    }

    name = "terraform-example-namespace"
  }
}

resource "kubernetes_deployment" "nginx" {
  metadata {
    name = "nginx-example-on-tanzu"
    labels = {
      App = "ScalableNginxExample"
    }
    namespace = kubernetes_namespace.example.id
  }

  spec {
    replicas = 4
    selector {
      match_labels = {
        App = "ScalableNginxExample"
      }
    }
    template {
      metadata {
        labels = {
          App = "ScalableNginxExample"
        }
      }
      spec {
        container {
          image = "bitnami/nginx:1.20.2"
          name  = "example"

          port {
            container_port = 80
          }

          resources {
            limits = {
              cpu    = "0.5"
              memory = "512Mi"
            }
            requests = {
              cpu    = "250m"
              memory = "50Mi"
            }
          }
        }
      }
    }
  }
}

resource "kubernetes_service" "nginx" {
  metadata {
    name      = "nginx-example"
    namespace = kubernetes_namespace.example.id
  }
  spec {
    selector = {
      App = kubernetes_deployment.nginx.spec.0.template.0.metadata[0].labels.App
    }
    port {
      node_port   = 30201
      port        = 80
      target_port = 80
    }

    type = "NodePort"
  }
}

TFC上のワークスペースの設定を行い、TKGm上にデプロイしたTFC Agent経由でプロビジョニングが行われる様に設定し、Terraform Runを実行します。

このワークスペースに関しては、VCS連携していますので、git commitなどワークスペースが連携しているレポジトリの更新をトリガーにTerraform Runが実行される様になっております。Terraform Runが実行されると、先ずはterraform planが実行され、実際にプロビジョニングが実行されると、どのような変更がなされるか出力されます。

pic-tfc-plan-1.png

プロビジョニングされるネームスペースリソースを見てみますと、以下の様にどの様な変更がされるか、視覚的に確認する事が出来ます。

pic-tfc-plan-2.png

KubernetesリソースはYAMLのマニフェストで管理される事が一般的かと思いますが、Terraform/HCLのフレームワークを使って、Kubernetesリソースを管理することで、Dry-Run、モジュラリティ、プロバイダやリソース間での値の参照などを行えるメリットがあります。

コードが問題無く通ったので、terraform applyを実行し、TKGm上にKubernetesリソースをプロビジョニングします。

pic-tf-apply.png

TKGmのWorkloadクラスタに、コードで宣言したKubernetesリソースがプロビジョニングされているかどうか確認してみます。

$ k get ns terraform-example-namespace
NAME                          STATUS   AGE
terraform-example-namespace   Active   99s
$ k -n terraform-example-namespace get all
NAME                                          READY   STATUS    RESTARTS   AGE
pod/nginx-example-on-tanzu-6f4477ddf5-77727   1/1     Running   0          106s
pod/nginx-example-on-tanzu-6f4477ddf5-d2gpv   1/1     Running   0          106s
pod/nginx-example-on-tanzu-6f4477ddf5-r4bdf   1/1     Running   0          106s
pod/nginx-example-on-tanzu-6f4477ddf5-wcsq8   1/1     Running   0          106s

NAME                    TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/nginx-example   NodePort   100.66.204.68   <none>        80:30201/TCP   80s

NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-example-on-tanzu   4/4     4            4           106s

NAME                                                DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-example-on-tanzu-6f4477ddf5   4         4         4       106s
$ k -n terraform-example-namespace describe deployment.apps/nginx-example-on-tanzu
Name:                   nginx-example-on-tanzu
Namespace:              terraform-example-namespace
CreationTimestamp:      Tue, 07 Dec 2021 22:27:42 +0900
Labels:                 App=ScalableNginxExample
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               App=ScalableNginxExample
Replicas:               4 desired | 4 updated | 4 total | 4 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  App=ScalableNginxExample
  Containers:
   example:
    Image:      bitnami/nginx:1.20.2
    Port:       80/TCP
    Host Port:  0/TCP
    Limits:
      cpu:     500m
      memory:  512Mi
    Requests:
      cpu:        250m
      memory:     50Mi
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-example-on-tanzu-6f4477ddf5 (4/4 replicas created)
Events:          <none>

問題なくKubernetesリソースがTKGmのWorkloadクラスタ上にプロビジョニング出来ている事を確認出来ました。

この様に、インフラリソースだけではなく、Kubernetesリソースに関しても、TerraformのKubernete providerを使って、Terraformのフレームワークに則った形で管理する事が出来ます。

まとめ

Terrformはインフラのプロビジョニングだけではなく、Kubernetesリソースも同一のフレームワークで管理出来ます。また、有償版TerraformはOOTBでTerraformのワークフローにおけるベストプラクティスがお使い頂ける様になっておりますので、ご興味あればこちらからお問い合わせ下さい。

5
0
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
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?