Terraformを活用したユースケースとして、開発者やプロジェクトといったインフラ環境を利用する側のリソースもIaCによって管理していく方法を検証します。
1. はじめに
Terraformの基本的な利用方法としてIaCによるインフラ環境の構築があります。これにより環境の再現性や管理性などが向上するため利用されている方も多いと思います。本連載でも第1回でTerraformを利用した環境構築について執筆しました。
今回はTerraformをさらに活用するユースケースを考えてみます。実際の案件で環境構築を行う場合、インフラ環境構築に加えてその環境を複数の利用者やプロジェクトで利用するための設定(アカウントの払い出しやリポジトリの準備、CI/CD環境構築など)を実施するケースがあります。ユーザーやプロジェクト数、また管理するアプリケーション等が少なければ手動での運用管理でも問題ないですが、多くなると都度初期設定が手間になったり、運用が行き届かなくなって不要な設定が残るような経験をした方もいるのではないでしょうか。今回はそういった設定も運用管理しやすくするようにIaC化による運用を検証してみます。
2. 全体像
今回はGitLabによるプロジェクト管理と、GitLabCIおよびArgoCDを利用したCI/CD環境を題材にIaCによる管理を検証していきます。ローカル環境(Windows上のWSL2を想定)にDockerおよびMinikubeを準備し、その上にGitLab、GitLabRunner、ArgoCDを導入します。CI/CD環境ができたら、それを利用してサンプルアプリケーションの導入まで行っていきます。全体像を図にすると以下のようになります。

3. 環境準備
ローカル環境(Windows上のWSL2)のMinikubeにGitLab、GitLabRunner、ArgoCDを導入します。導入にはHELMを利用します。
Docker、Minikube、HELMについては事前に準備しておいてください。今回の検証で利用しているバージョンは以下の通りです。
- Minikube v1.28.0
- Docker v20.10.12
- HELM v3.10.0

なお、ローカル端末上でMinikubeおよびGitLab、ArgoCDを構築し、動作検証を行うためにはマシンスペックにある程度余裕があるのが望ましいです。(CPU:8Core、メモリ:16Gくらいあれば動作すると思います)
3.1 Minikubeの事前設定
(1) 起動
Minikubeを起動します。GitLabは起動に必要なリソースがCPU:4Core(推奨)、メモリ:4G(必須)となっています。今回は少しメモリに余裕を持たせてCPU:4Core、メモリ:8Gで起動します。プロキシは企業内環境等、必要な場合のみ設定してください。
minikube start --kubernetes-version=v1.25.2 --cpus=4 --memory='8G' --driver=docker --docker-env http_proxy=$http_proxy --docker-env https_proxy=$https_proxy --docker-env no_proxy=$no_proxy
(2) addonの準備
GitLabへはIngress経由でアクセスするため、IngressAddonを有効にしておきます。
minikube addons enable ingress
また、イメージをAWSのECR(プライベート)で管理するため、Minikubeからプライベートリポジトリにアクセスできるようにregistry-credsを有効にしておきます。
minikube addons configure registry-creds
Do you want to enable AWS Elastic Container Registry? [y/n]: y -- Enter AWS Access Key ID: <アクセスキー> -- Enter AWS Secret Access Key: <シークレットアクセスキー> -- (Optional) Enter AWS Session Token: -- Enter AWS Region: <リージョン> -- Enter 12 digit AWS Account ID (Comma separated list): <AWSアカウントID> -- (Optional) Enter ARN of AWS role to assume: Do you want to enable Google Container Registry? [y/n]: n Do you want to enable Docker Registry? [y/n]: n Do you want to enable Azure Container Registry? [y/n]: n
※ AssumeRoleを利用している場合はAWS Session Token、ARN of AWS role to assumeも設定が必要です。
minikube addons enable registry-creds
起動するとkube-public配下にシークレットが作成されます。awsecr-credシークレットを確認してdataに認証情報が格納されていれば正しく動作しています。
kubectl get secret -n kube-public awsecr-cred -o yaml apiVersion: v1 data: .dockerconfigjson: eyJhdXRocyI6eyJodH・・・
注)企業内などプロキシ環境下ではregistry-credsが正しく起動しないことがあります。その場合は、registry-credsのDeploymentに下記のようなプロキシ設定を行った上、Podを再起動してください。
kubectl edit deploy -n kube-system registry-creds
...
spec:
template:
spec:
containers:
- env:
- name: HTTP_PROXY
value: <プロキシサーバー>
- name: HTTPS_PROXY
value: <プロキシサーバー>
- name: NO_PROXY
value: <除外ドメイン>
3.2 GitLabの導入
(1) GitLabをデプロイ
GitLabCEをMinikube上にデプロイします。外部からはgitlab-webservice-default.gitlab.svc.cluster.localでアクセスできるようにドメインとホスト名を指定します。
kubectl create ns gitlab
helm repo add gitlab https://charts.gitlab.io/
helm repo update
helm upgrade --install gitlab gitlab/gitlab \
--timeout 600s \
-f https://gitlab.com/gitlab-org/charts/gitlab/raw/master/examples/values-minikube-minimum.yaml \
--namespace gitlab \
--set global.edition=ce \
--set global.hosts.https=false \
--set global.ingress.tls.enabled=false \
--set certmanager.install=false \
--set global.ingress.configureCertmanager=false \
--set global.hosts.domain=gitlab.svc.cluster.local \
--set global.hosts.externalIP=127.0.0.1 \
--set global.hosts.gitlab.name=gitlab-webservice-default.gitlab.svc.cluster.local
正常に起動しているか確認します。(初回は10分くらいはかかります)
kubectl get po -n gitlab
NAME READY STATUS RESTARTS AGE gitlab-gitaly-0 1/1 Running 0 19m gitlab-gitlab-exporter-6df794fdd6-whlfg 1/1 Running 0 19m gitlab-gitlab-shell-7f9f9b98c8-ntml4 1/1 Running 0 19m gitlab-kas-7875669dc-5x9np 1/1 Running 0 19m gitlab-kas-7875669dc-9dcxn 1/1 Running 0 19m gitlab-migrations-1-mwmk7 0/1 Completed 0 19m gitlab-minio-74467697bb-jm5mn 1/1 Running 0 19m gitlab-minio-create-buckets-1-cn98n 0/1 Completed 0 19m gitlab-postgresql-0 2/2 Running 0 19m gitlab-redis-master-0 2/2 Running 0 19m gitlab-registry-cd4996664-g7vtv 1/1 Running 0 19m gitlab-sidekiq-all-in-1-v2-57868fcc78-dh6qw 1/1 Running 0 19m gitlab-toolbox-5c57497458-67dc9 1/1 Running 0 19m gitlab-webservice-default-678b9458d8-4qxwc 2/2 Running 0 19m
Ingressの設定も確認します。
kubectl get ingress -n gitlab
NAME CLASS HOSTS ADDRESS PORTS AGE gitlab-kas nginx kas.gitlab.svc.cluster.local 192.168.49.2 80 16h gitlab-minio nginx minio.gitlab.svc.cluster.local 192.168.49.2 80 16h gitlab-registry nginx registry.gitlab.svc.cluster.local 192.168.49.2 80 16h gitlab-webservice-default nginx gitlab-webservice-default.gitlab.svc.cluster.local 192.168.49.2 80 16h
gitlab-webservice-default.gitlab.svc.cluster.localの80番ポートでリクエストを受ける設定になっていれば正しく導入されています。
(2) GitLabの動作確認
rootパスワードの確認
kubectl get secret gitlab-gitlab-initial-root-password -n gitlab -ojsonpath='{.data.password}' | base64 --decode
※ 初期パスワードは必要に応じて変更してください。
Ingressの公開
Windows上のブラウザからIngressへ接続するためminikube tunnelを実行します。(sudoパスワードが要求されます)
minikube tunnel
ブラウザからGitLabへアクセス
http://gitlab-webservice-default.gitlab.svc.cluster.local/
※ Windowsのhosts設定でgitlab-webservice-default.gitlab.svc.cluster.localを127.0.0.1に向けておいてください
※ 企業内などプロキシ環境下の場合、プロキシ除外に*.svc.cluster.localを追加してください
ID : root PW : 上記rootパスワードの確認で取得したパスワード
ログインできれば動作確認まで完了です。

(3) アクセストークンの設定
この後のTerraformからGitLabへAPIアクセスするための事前準備としてGitLabのアクセストークンを生成しておきます(画面右上のユーザーメニュー>Edit Profile>Access Tokens)
| 項目 | 値 |
|---|---|
| Token name | 任意の名前 |
| Expiration date | 任意の日付 |
| Select scopes | api,write_repository,write_registry |
作成したトークンをローカル端末の環境変数にセットします。
export TF_VAR_gitlab_token=<作成したトークン>
3.3 GitlabRunnerの導入
GitLabのCI実行はGitlabRunnerがコントロールします。GitLabRunnerはGitLab本体とは別コンテナとして動作しますので以下の手順で導入します。
(1) config.yamlの作成
GitLabRunnerを導入する際の初期設定を作成します。初期設定はYAML(サンプルではconfig.yaml)形式のファイルで作成し、HEMLでインストール時に読み込むようにします。
Admin>Runners>Register instance runner からトークンを確認する
トークンを設定したconfig.yamlを作成
config.yaml
gitlabUrl: http://gitlab-webservice-default.gitlab.svc.cluster.local:8181/ runnerRegistrationToken: <上記手順で確認したトークン> rbac: create: true
また、今回はKubernetesにGitLabおよびGitLabRunnerをデプロイしているため、通信経路は以下の図のような構成となり、そのままではCI用PodからGitLabにアクセスできません。(リポジトリをcloneするためアクセスする必要があります)
そのため内部通信をServiceではなくIngressのIPに変更するようにhost_aliasesを指定します。

gitlabUrl: http://gitlab-webservice-default.gitlab.svc.cluster.local:8181/
runnerRegistrationToken: <上記手順で確認したトークン>
rbac:
create: true
runners:
config: |
[[runners]]
[runners.kubernetes]
namespace = "{{.Release.Namespace}}"
image = "ubuntu:16.04"
[[runners.kubernetes.host_aliases]]
ip = "<3.2 (1) で確認したIngressのIP>"
hostnames = ["gitlab-webservice-default.gitlab.svc.cluster.local"]
(2) Runnerのデプロイ
config.yamlの準備ができたら、GitLabRunnerをデプロイします。
kubectl create ns gitlab-runner helm upgrade --install default-gitlab-runner gitlab/gitlab-runner -f config.yaml --namespace gitlab-runner
GitLab管理画面からAdmin>Overview>Runnersを確認して、StatusがOnlineになっているRunnerが1つ確認できれば導入完了です。

3.4 ArgoCDの導入
(1) ArgoCDをデプロイ
HELMチャートからArgoCDをデプロイします。設定は今回はデフォルトのままとします。
kubectl create ns argocd
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
helm upgrade --install argocd argo/argo-cd \
-f https://raw.githubusercontent.com/argoproj/argo-helm/main/charts/argo-cd/values.yaml \
--namespace argocd
正常に起動しているか確認します。
kubectl get po -n argocd
NAME READY STATUS RESTARTS AGE argocd-application-controller-0 1/1 Running 0 15s argocd-applicationset-controller-7f4577c5fd-fwdn7 1/1 Running 0 15s argocd-dex-server-7cdb45c7c9-zhddc 1/1 Running 0 15s argocd-notifications-controller-65b77fb646-zktcj 1/1 Running 0 15s argocd-redis-577c6c8f5c-4lw2h 1/1 Running 0 15s argocd-repo-server-7bd9fd899c-7vxl6 1/1 Running 0 15s argocd-server-6686bbcf68-zj2s5 1/1 Running 0 15s
(2) ArgoCDの動作確認
adminパスワードの確認
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
※ 初期パスワードは必要に応じて変更してください。
ブラウザからArgoCDへアクセス
ポートフォワード
kubectl port-forward svc/argocd-server -n argocd 8443:443
https://localhost:8443/
ID : admin PW : 上記adminパスワードの確認で取得したパスワード
ログインできれば動作確認まで完了です。

4. TerraformによるCI/CD環境構築について
環境の準備ができましたのでTerraformによるCI/CD環境構築についてみていきます。第1回 環境構築をコード化し、管理・運用を楽にしようではTerraformによるAWSおよびEKS環境の構築について執筆しましたが、今回はGitLabProviderを用いたGitLabの環境構築、およびKubernetesProviderを用いたArgoCDでのCD環境構築を行います。なお、他にどのようなプロバイダが用意されているかはTerraformRegistryをご確認ください。

4.1 GitLab Provider
まずはGitLab Providerです。IaCで何ができるかTerraformのドキュメントを簡単に見てみましょう。
左メニューからResourcesを見ると、グループやプロジェクト、ユーザー、ブランチの操作に関するAPIが確認出来ますので、プロジェクト開始時に必要な準備はTerraformでも一通りできそうなことがわかります。

出典:https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs
4.2 Kubernetes Provider
次にKubernetes Providerについてです。こちらも同じくIaCで何ができるかTerraformのドキュメントを簡単に見てみましょう。
左メニューを見ると、Kubernetesが標準で用意している各種操作のAPI(apps/v1,core/v1など)が確認できます。また、カスタムリソースなどKubernetes標準以外のリソースを作成するためにmanifestリソースが用意されていますので、Terraformでどのようなマニフェストでも登録できそうなことがわかります。

出典:https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs
5. GitLab環境構築
それでは、TerraformのGitLabProviderを利用してリソースを作成していきます。 今回作成するGitLab環境のTerraform構成は下記のようにします。
なお、本記事内では一部のコードのみ抜粋して説明しています。コードの全体はこちらをご確認ください。
├ common 全プロジェクト共通リソース
│ ├ env パラメータ設定
│ └ modules
│ └ user ユーザー設定
└ example-group GitLabのグループ単位で作成するリソース
├ env パラメータ設定
└ modules
├ ecr AWS ECR設定
└ gitlab GitLab設定
├ group グループ設定
├ group_membership ユーザーとグループの紐づけ
├ project プロジェクト設定
└ template テンプレートファイル
- ユーザーは独立したTerraformプロジェクトで作成(複数グループに参加するため)
- GitLabのグループ単位でTerraformプロジェクトを作成
- グループ単位で作成するモジュールは下記の通り
- ECR
- GitLab Group
- GitLab Project
- GitLab Groupとユーザーの紐づけ
※ GitLab Projectがアプリケーション(リポジトリ)の単位となり、複数のProjectを束ねるのがGitLab Groupとなります。
5.1 ユーザー登録
GitLabのユーザー(開発者)の登録を行います。Terraformからユーザーを作成するにはgitlab_userリソースを利用します。今回作成するユーザーの仕様は以下の通りです。
- ユーザーごとの設定項目はID、名前、メールアドレス
- 初期パスワードは固定値(GitLabからメールが飛ばせる環境であれば、リセット可能)
- 権限は一般ユーザー
- グループの作成は不可、プロジェクトは5つまで
- 権限のないプロジェクトへのアクセス不可
この仕様をTerraformのパラメータとして次のように定義します。(test1、test2の2ユーザー作成例)
variable "gitlab_user_common" {
description = "GitLabユーザー共通設定"
type = object({
is_admin = bool # 管理者権限
projects_limit = number # 参加可能プロジェクト数
can_create_group = bool # グループ作成権限
is_external = bool # 権限の付与されていないプロジェクトへのアクセス可否
reset_password = bool # パスワードリセット
default_password = string
})
default = {
is_admin = false
projects_limit = 5
can_create_group = false
is_external = false
reset_password = false # 実際に運用する際はtrueが望ましい
default_password = "xxxxxxxxxx" # 任意のパスワードをセットする
}
}
variable "gitlab_users" {
description = "GitLab利用ユーザー(複数設定)"
type = map(object({
name = string
email = string # すべて小文字に置き換えられるので最初から小文字にしておくこと(大文字があると毎回差分になる)
}))
default = {
"ID000001" = {
name = "test1"
email = "id000001@svc.cluster.local"
},
"ID000002" = {
name = "test2"
email = "id000002@svc.cluster.local"
}
}
}
注) 実際に運用する際はTerraformのファイルに初期パスワードは書かず、reset_passwordを有効にするのが望ましいです。また、メールアドレスなど個人情報の扱いも注意してください。(パブリックリポジトリで公開しないなど)
上記パラメータを使用してユーザーリソースを作成するのは次のようになります。
resource "gitlab_user" "users" {
for_each = var.users
name = var.users[each.key].name
username = each.key
password = var.default_password
email = var.users[each.key].email
is_admin = var.is_admin
projects_limit = var.projects_limit
can_create_group = var.can_create_group
is_external = var.is_external
reset_password = var.reset_password
lifecycle {
ignore_changes = [name,password,email] # ユーザー自身での変更を許可するものは差分検知から除外
}
}
Terraformを実行すると下記のようにユーザーの作成が確認できます。

5.2 グループおよびプロジェクトの作成
次にグループおよびプロジェクトを作成します。GitLabではリポジトリの単位となるプロジェクトと、それを複数束ねるグループという概念があります。どのような単位でグループ化するかは要件に応じて決めればよいですが、案件をグループ、その中に複数アプリ(バックエンド、フロントエンド、ジョブ等)を作成するようなイメージがひとつわかりやすい利用例かと思います。
グループおよびプロジェクトを作成するにはgitlab_group、gitlab_projectリソースを利用します。また、先ほど作成したユーザーをグループに参加させるためにgitlab_group_membershipリソースを利用します。
例として、demo1グループを作成し、demo1グループにdemo1pj1、demo1pj2プロジェクトを作成します。また、先ほど作成したユーザーのうち、test1のみをdemo1グループに参加させます。 この仕様をTerraformのパラメータとして次のように定義します。
variable "project_setting" {
default = {
name = "demo1" # グループ
path = "demo1"
description = "demo1 project group"
users = [
"ID000001" # グループに所属するユーザーのリスト
]
projects = { # グループ内のプロジェクト
demo1pj1 = {
description = "demo1 group project1"
}
demo1pj2 = {
description = "demo1 group project2"
}
}
}
}
上記パラメータを使用してグループ、プロジェクト、グループとユーザーの紐づけリソースを作成するのは次のようになります。
resource "gitlab_group" "group" {
name = var.project_setting.name
path = var.project_setting.path
description = var.project_setting.description
visibility_level = "private" # 可視性は固定でprivateとしておく
}
resource "gitlab_project" "project" {
for_each = var.project_setting.projects
name = each.key
description = each.value.description
namespace_id = var.group.id
}
resource "gitlab_group_membership" "group_member" {
for_each = toset(var.project_setting.users)
group_id = var.group.id
user_id = var.users[each.key]
access_level = "developer" # アクセスレベルの種類はguest,reporter,developer,maintainer,owner 固定でdeveloperとしておく
}
※ ユーザーIDを取得するため5.1で作成したユーザー情報を読み込む必要があります。
Terraformを実行すると下記のようにグループおよび配下のプロジェクトが確認できます。

また、Membersを見てみると、test1が参加していることが確認できます。

ここまでで、ユーザーがGitLabを利用してプロジェクトを開始するための基本的な設定までできました。次はこのプロジェクトにテンプレートファイルを追加します。
5.3 テンプレートファイル作成
プロジェクトを開始する際、どのプロジェクトでも共通で必要となるファイルがあります。例えばGitLabCIを使ってKubernetesへデプロイする場合であれば、Dockerfile、GitLabCIファイル、Kubernetesのマニフェストファイルなどが必要になります。(Dockerfileはフレームワークが生成するケースなどもありますが)プロジェクトごとに設定内容に違いはありますので自動生成したまま運用できるわけではありませんが、少しでも開発をスタートしやすくするため最低限必要な設定等をテンプレートとしてプロジェクト生成と同時に作成しておきます。
今回はDockerfile、CIファイル(.gitlab-ci.yml)、Kubernetesマニフェスト(Deployment、Service、Kustomize)を作成します。
(1) Dockerfile(Dockerfile.tpl)
今回のサンプルではシンプルにnginxのイメージをそのまま利用します。(実際はアプリケーションに合わせたDockerfileのテンプレートを作成してください)
FROM nginx:alpine
(2) CIファイル(gitlab-ci.yml.tpl)
buildとmerge-requestの2ステージ用意します。
- buildステージ
- kanikoイメージを使ってKubernetes上でDockerイメージの作成を行い、ECRへプッシュ
- merge-requestステージ
- ビルドした際のタグ名でブランチを作成し、Deploymentマニフェストに記載しているリリース対象のイメージタグを変更
- マージリクエスト作成
サンプルではCIの実行条件は指定していないため、リポジトリに何か変更をPushするたびに下記定義のCIが実行されます。
stages:
- build
- merge-request
build_image_with_kaniko:
stage: build
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- |
cat > /kaniko/.docker/config.json <<EOF
{
"credsStore": "ecr-login"
}
EOF
/kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $ECR_URI/${group}/${project}:$CI_COMMIT_SHORT_SHA
release_manifests:
stage: merge-request
image:
name: alpine/git:latest
entrypoint: [""]
script:
- |
git config --global user.name gitlab
git config --global user.email gitlab@mail.domain
git checkout -b $CI_COMMIT_SHORT_SHA
sed -i 's/image: ${ecr_uri}\/${group}\/${project}:\(.*\)/image: ${ecr_uri}\/${group}\/${project}:'$CI_COMMIT_SHORT_SHA'/' ./manifests/deployment.yaml
git add -A
git commit -m '[ci skip] image update'
git push http://group_${ci_user}_bot:$PAT@gitlab-webservice-default.gitlab.svc.cluster.local/${group}/${project} $CI_COMMIT_SHORT_SHA
apk update
apk add curl
curl -X POST --header "PRIVATE-TOKEN: ${gitlab_token}" --header "Content-Type: application/json" --data "{\"source_branch\": \"$CI_COMMIT_SHORT_SHA\", \"target_branch\": \"main\", \"title\": \"auto merge request\"}" ${gitlab_url}${gitlab_api}projects/${project_path}/merge_requests
GitLabのRestAPIに関するドキュメントはこちら
MergeRequestのドキュメントはこちら
(3) Kubernetesマニフェスト
Deployment、Service、Kustomizeの3ファイルを用意します。Dockerfileで用意したNginxをデプロイし、Service経由でアクセスできるマニフェストになります。(マニフェスト自体は標準的なものですので、内容の詳細については特にここでは触れません)
Deployment(deployment.yaml.tpl)
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ${project_name}
name: ${project_name}
namespace: ${namespace}
spec:
replicas: 1
selector:
matchLabels:
app: ${project_name}
strategy: {}
template:
metadata:
labels:
app: ${project_name}
spec:
containers:
- image: ${image}
name: ${project_name}
resources: {}
Kubernetes Service(service.yaml.tpl)
apiVersion: v1
kind: Service
metadata:
labels:
app: ${project_name}
name: ${project_name}
namespace: ${namespace}
spec:
ports:
- name: 8080-8080
port: 8080
protocol: TCP
targetPort: 80
selector:
app: ${project_name}
type: ClusterIP
Kubernetes Kustomize(kustomization.yaml.tpl)
resources: - deployment.yaml - service.yaml
5.4 テンプレートファイルの登録
作成したテンプレートファイルをGitLabのリポジトリに登録するにはgitlab_repository_file、template_fileリソースを利用します。 下記はgitlab-ci.ymlを登録する場合の記載例になります。なお、本サンプルではテンプレートファイルはプロジェクト開始時の初期ファイル提供を想定しているので、プロジェクト開始後に開発者がGitLabから直接変更してもデグレしないように差分から外しています。変更をTerraformから行う運用の場合などは差分から外す必要はないので、この辺りは運用に合わせて調整する必要があります。
resource "gitlab_repository_file" "project_gitlab" {
for_each = gitlab_project.project # プロジェクトの数だけ作るのでループ
project = each.value.id
file_path = ".gitlab-ci.yml"
branch = "main"
content = base64encode("${data.template_file.project_gitlab[each.value.name].rendered}")
author_email = "terraform@author.email"
author_name = "Terraform"
commit_message = "feature: add service file"
lifecycle {
ignore_changes = [content]
}
}
data "template_file" "project_gitlab" {
template = "${file("../templates/gitlab-ci.yml.tpl")}"
for_each = var.setting.projects
vars = { # テンプレートファイル内の変数(${xxx})にセットする値
project = each.key
ci_user = var.group.id
}
}
同様にDockerfileやKubernetesのマニフェスト用にもリソースを作成します。こちらはサンプルコードをご確認ください。
Terraformを実行すると下記のようにテンプレートファイルが確認できます。

5.5 CI環境の構築
GitLabの設定およびCIファイルの準備までできましたので、次はCIを実行するための準備を行います。
(1) ECRの準備
TerraformでECRを準備します。リポジトリパスは“グループ名/プロジェクト名"とします。作成には第1回の記事の手順と同様AWSProviderを利用します。
resource "aws_ecr_repository" "project_repository" {
for_each = var.project_setting.projects
name = "${var.project_setting.name}/${each.key}" # グループ名/プロジェクト名
image_tag_mutability = var.ecr.image_tag_mutability # Mutable(上書き登録可否)設定
image_scanning_configuration {
scan_on_push = var.ecr.scan_on_push # イメージプッシュ時の脆弱性スキャン設定
}
}
(2) ECRへPushするための権限を準備
Gitlabからのアクセスを許可するためのアクセスキーをAWS側で発行します。(ECRへPushするための権限を付与しておいてください)
発行したらGitLabのグループごとの環境変数に設定します。 環境変数の設定は画面からでも可能ですし、下記のようにTerraformで指定しておくことも可能です。(今回のサンプルではTerraformで指定しています)
環境変数
export TF_VAR_access_key=<アクセスキー> export TF_VAR_secret_key=<シークレットアクセスキー>
環境変数の読み込み
variable "access_key" {
description = "AWSアクセス用アクセスキー(terraformコマンド実行時のパラメータもしくは環境変数TF_VAR_access_keyにて設定する)"
type = string
}
variable "secret_key" {
description = "AWSアクセス用シークレットキー(terraformコマンド実行時のパラメータもしくは環境変数TF_VAR_secret_keyにて設定する)"
type = string
}
グループごとの環境変数への設定
resource "gitlab_group_variable" "access_key" {
group = gitlab_group.group.id
key = "AWS_ACCESS_KEY_ID"
value = var.access_key
}
resource "gitlab_group_variable" "secret_key" {
group = gitlab_group.group.id
key = "AWS_SECRET_ACCESS_KEY"
value = var.secret_key
}
ここまででCIの準備はできましたのでCIを実行すればECRにイメージが登録されます。

5.6 CD環境の構築
最後にArgoCDを用いたCD環境を構築します。
(1) ArgoCDからGitLabへのアクセス設定
GitLab側でアクセストークンを払い出します。
グループ選択>Settings>Access Tokens
| 項目 | 値 |
|---|---|
| Token name | argocd |
| Expiration date | 任意の日付 |
| Select a role | Developer |
| Select scopes | read_repository,read_registry |
ArgoCD側でリポジトリ設定を行います。(demo1グループのdemo1pj1プロジェクト用設定例)
Settings>Repositories
| 項目 | 値 |
|---|---|
| Connection method | VIA HTTPS |
| Type | git |
| Project | default |
| Repository URL | http://gitlab-webservice-default.gitlab.svc.cluster.local:8181/demo1/demo1pj1 |
| Username | argocd |
| Password | 上記で払い出したアクセストークン |

なお、今回は画面から環境設定を行っていますが、Kubernetesのリソースからも登録できます。(ですので、Terraformで定義することも可能)詳しくは公式ドキュメントを参照してください。
(2) ArgoCDカスタムリソースの作成
KubernetesProviderを利用してTerraformからKubernetesへリソースの作成を行います。kubernetes_manifestリソースを使用してカスタムリソースであるArgoCDのApplicationを登録します。
resource "kubernetes_manifest" "application" {
for_each = var.project_setting.projects
manifest = {
"apiVersion" = "argoproj.io/v1alpha1"
"kind" = "Application"
"metadata" = {
"name" = "${each.key}"
"namespace" = "argocd"
}
"spec" = {
"destination" = {
"server" = "https://kubernetes.default.svc"
"namespace" = "${var.project_setting.name}"
}
"project" = "default"
"source" = {
"repoURL" = "http://gitlab-webservice-default.gitlab.svc.cluster.local:8181/${var.project_setting.name}/${each.key}"
"targetRevision" = "HEAD"
"path" = "manifests/"
}
"syncPolicy" = {
"automated" = {
"prune" = true
}
"syncOptions" = [
"CreateNamespace=true"
]
}
}
}
}
Terraformでリソースを作成したら、ArgoCDでアプリケーションが登録されていることを確認します。GitLab側でCIの実行結果をマージして最新状態をSYNCするとMinikube上にアプリケーションがデプロイされます。


(3) ブラウザからアプリケーションへアクセス
ポートフォワード
kubectl port-forward svc/demo1pj1 -n demo1 8080:8080
ブラウザからアクセスして、Nginxのトップ画面が表示されることが確認できればCI/CD環境の完成です。
http://localhost:8080/

6. おわりに
今回はTerraformを活用をするユースケースとしてGitLabとArgoCDをベースとしたプロジェクト環境をIaCで構築する手順を見てきました。今回作成したIaCのコードを利用した場合、ユーザーやプロジェクトの追加・削除などは設定ファイルを変更してTerraformを再実行するだけでよくなります。IaCコードをGitで管理しておくことで現状の確認や変更の履歴を追うこともできますし、変更時にレビューを挟むこともできますので、手動での運用が煩雑になりそうなケースでは選択肢として考えてもいいように感じました。
なお、今回はコードリポジトリにGitLabを利用しましたが、Github Providerでも同等の機能が提供されていますのでGithubを普段使われている方は、一度Github用にコードを変更して試してみていただければと思います。
次回は、今回作成した環境をさらに活用するユースケースとして、マージリクエスト単位での環境払い出し機能を追加してみます。
