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 Agentとは
- Terraform Cloud Agentのセットアップ
- Terraform Cloud AgentのKubernetesへのデプロイ
- Terraformを使ったKubernetesリソースのプロビジョニング
- まとめ
Terraform Cloud Businessとは
先ず、Terraform Cloud Business(以下TFCB)についてです。TFCBは、HashiCorpがマネージド・サービスとして提供しているTerraform Cloud(以下TFC)の有償版になります。
TerraformはIaCの一つでして、プロビジョニングに関わる手動での運用作業をコード化し、自働化する事が出来ます。
ヒトがクラウドのGUIやAPIを直接叩いて実行する代わりに、TerraformがクラウドのAPIを実行し、コードに従ってインフラのプロビジョニングを行います。これによって、ヒトが実施しないといけない作業を削減する事が出来ます。
また、Terraformはマルチクラウド環境で同じ仕組みが利用出来るようになっておりますので、プロビジョニング先の環境が、AWS、Azure、GCP、vSphereだろうと、同じワークフロー、同一の構成要素、同一の文法を用いて、プロビジョニングを実施して頂く事が可能になりますので、汎用性が非常に高いという特徴を持っています。
Terraformですが、実際に扱っていく際の典型的なワークフローは以下の様な形になります。
Hashicorp Configuration Language(以下HCL)で記述されたソースコード、あとはクラウドにインフラをプロビジョニングする際のシークレット情報などの変数を用いて、terraform plan
というコマンドを実行してあげます。
そうすると、TerraformがHCLを実行する場合に、どういった変更が行われるか?という、プラン内容を出力してくれます。そのあとは、プラン内容のレビューを実施します。
その内容が社内のポリシーに沿っているかどうか等をレビューし、時には修正依頼を掛けたりし、内容に問題がなければ、terraform apply
を実行し、プロビジョニングを行っていくというワークフローになります。
プロビジョニングが無事成功すると、インフラの状態を記述したファイルであるステートファイルが生成されるといった形になります。
インフラを更新する様な際は、更新したHCL、最新のステートファイル、必要な変数とともに、terraform plan
を実行し、レビューをし、問題無ければ、terraform apply
というワークフローを回していく事になります。
ただ、Terraformを安全に利用していくためには、以下のような考慮事項が出てきます。
この様な考慮事項に関して、HashiCorpが考えるTerraformを扱う上でのベストプラクティスに沿って、OOTBでTerraformをお使い頂ける様になっているのが、有償版Terraformになります。TFCBも、HashiCorpが提供している有償版Terraformの一つになります。
Terraform Cloud Agentとは
有償版Terraformをご利用頂く事で、OOTBで色々な機能が使える様になりますが、TFCのネットワーク構成を検討していく際の課題として挙げられるのが、TFCからプロビジョニング対象へのインバウンドでのリクエストがあります。
TFCが利用するIPレンジは公開されておりますので、お使いの環境のファイアーウォール等に対する設定変更して頂く事で利用する事が出来ますが、作業負荷の高い変更作業かと思います。
また、そもそも、自社環境に対してインバウンドのアクセスを許可するというのはセキュリティポリシー上難しいというのが、エンタープライズのお客様では特に多いのでは無いかと思います。
この様なTFC利用時のネットワーク構成課題を解決するソリューションとして、TFCBで利用出来るTerraform Cloud Agents(以下TFC Agent)という機能が存在します。
この機能をご利用頂くと、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_TOKEN
、TFC_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ストアに暗号化し、保管してあります。
また、今回利用した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
が実行され、実際にプロビジョニングが実行されると、どのような変更がなされるか出力されます。
プロビジョニングされるネームスペースリソースを見てみますと、以下の様にどの様な変更がされるか、視覚的に確認する事が出来ます。
KubernetesリソースはYAMLのマニフェストで管理される事が一般的かと思いますが、Terraform/HCLのフレームワークを使って、Kubernetesリソースを管理することで、Dry-Run、モジュラリティ、プロバイダやリソース間での値の参照などを行えるメリットがあります。
コードが問題無く通ったので、terraform apply
を実行し、TKGm上にKubernetesリソースをプロビジョニングします。
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のワークフローにおけるベストプラクティスがお使い頂ける様になっておりますので、ご興味あればこちらからお問い合わせ下さい。