オブジェクトの広場はオージス総研グループのエンジニアによる技術発表サイトです

コンテナ・マイクロサービス

GitLab、ArgoCDを用いたCI/CD環境の運用管理

Terraformを活用してCI/CD環境構築し、IaCによる運用管理について検証
オージス総研 技術部 アドバンストテクノロジセンター
山中 克容
2023年1月25日

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パスワードの確認で取得したパスワード

ログインできれば動作確認まで完了です。

GitLab

(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を指定します。

Runner

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つ確認できれば導入完了です。

Runner

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パスワードの確認で取得したパスワード

ログインできれば動作確認まで完了です。

Argocd

4. TerraformによるCI/CD環境構築について

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

Terraform

4.1 GitLab Provider

まずはGitLab Providerです。IaCで何ができるかTerraformのドキュメントを簡単に見てみましょう。

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

GitLab

出典: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でどのようなマニフェストでも登録できそうなことがわかります。

KubernetesProvider

出典: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を実行すると下記のようにユーザーの作成が確認できます。

User

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を実行すると下記のようにグループおよび配下のプロジェクトが確認できます。

Group

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

Attach

ここまでで、ユーザーが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を実行すると下記のようにテンプレートファイルが確認できます。

Template

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にイメージが登録されます。

Runner

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 上記で払い出したアクセストークン

ArgoCD Repository

なお、今回は画面から環境設定を行っていますが、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上にアプリケーションがデプロイされます。

ArgoCD AppList

ArgoCD AppDetail

(3) ブラウザからアプリケーションへアクセス

ポートフォワード

kubectl port-forward svc/demo1pj1 -n demo1 8080:8080

ブラウザからアクセスして、Nginxのトップ画面が表示されることが確認できればCI/CD環境の完成です。

http://localhost:8080/

Nginx

6. おわりに

今回はTerraformを活用をするユースケースとしてGitLabとArgoCDをベースとしたプロジェクト環境をIaCで構築する手順を見てきました。今回作成したIaCのコードを利用した場合、ユーザーやプロジェクトの追加・削除などは設定ファイルを変更してTerraformを再実行するだけでよくなります。IaCコードをGitで管理しておくことで現状の確認や変更の履歴を追うこともできますし、変更時にレビューを挟むこともできますので、手動での運用が煩雑になりそうなケースでは選択肢として考えてもいいように感じました。

なお、今回はコードリポジトリにGitLabを利用しましたが、Github Providerでも同等の機能が提供されていますのでGithubを普段使われている方は、一度Github用にコードを変更して試してみていただければと思います。

次回は、今回作成した環境をさらに活用するユースケースとして、マージリクエスト単位での環境払い出し機能を追加してみます。