はじめに

本記事では、Terraform でEKSのGitOps パイプラインのデモ環境を構築する手順とデモした内容について、記載しています。

GitOps について学習する時の参考になれば幸いです。

GitOps とは

Learn what is GitOps and how you can implement GitOps workflows to reliably run Kubernetes in production and at scale. Also find out why GitOps is the more secure way to deploy apps to Kubernetes.

引用:

GitOpsは、次の2つに要約できます。
1. Kubernetesおよびその他のクラウドネイティブテクノロジーのオペレーティングモデル。コンテナ化されたクラスターとアプリケーションのGitのデプロイ、管理、モニタリングを統合する一連のベストプラクティスを提供します。
2. アプリケーションを管理するための開発者エクスペリエンスへの道。ここでは、エンドツーエンドのCICDパイプラインとGitワークフローが操作と開発の両方に適用されます。

Terraform で構築する全体構成図

GitOps パイプライン

EKS クラスター

構成の概要

GitOps は、下記のコンポーネントで構成しています。

機能 コンポーネント名 説明
ソースの管理 CodeCommit アプリのソースとKubernetes のマニフェストを管理します。
イメージの管理 ECR アプリのイメージを管理します。
CI CodePipeline + CodeBuild アプリのソースの変更をトリガーにして、イメージをビルドし、ECR にプッシュします。プッシュ後、イメージの差し替えをマニフェストのリポジトリにプルリクエストします。
CD Argo CD マニフェストの変更を監視し、変更を検出すると、Kubernetes の実行環境に反映します。

Terraform のコードと構成

Terraform でEKS のGitOps パイプラインを構築する. Contribute to okubo-t/aws-tf-eks-gitops-demo development by creating an account on GitHub.
$ tree aws-tf-eks-gitops-demo
aws-tf-eks-gitops-demo
├── eks-gitops-demo-app
│   ├── Dockerfile
│   └── index.html
├── eks-gitops-demo-k8s
│   ├── deployment.yaml
│   └── service.yaml
├── main.tf
├── modules
│   └── ci
│       ├── buildspec.yml
│       ├── cloudwatch_event.tf
│       ├── cloudwatch_logs.tf
│       ├── codebuild.tf
│       ├── codecommit.tf
│       ├── codepipeline.tf
│       ├── ecr.tf
│       ├── iam.tf
│       ├── iam_https_user.tf
│       ├── outputs.tf
│       ├── s3.tf
│       └── variables.tf
├── outputs.tf
├── provider.tf
└── versions.tf

デモ環境を構築するメインのファイル

main.tf

locals {
  aws_profile = "YOUR AWS ACCOUNT PROFILE NAME"
  aws_region  = "ap-northeast-1"
}

module "ci" {
  source = "./modules/ci"

  # PREFIX
  prefix = "eks-gitops"
  # ENVIRONMENT PREFIX
  env = "demo"

  # IMAGE CODECOMMIT REPOSITORY NAME
  codecommit_repository_for_image = "eks-gitops-demo-app"
  # BRANCH NAME
  branch_name = "master"
  # ECR REPOSITORY NAME
  ecr_name = "eks-gitops-demo-app"

  # K8S CODECOMMIT REPOSITORY NAME
  codecommit_repository_for_k8s = "eks-gitops-demo-k8s"
}

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "3.14.2"

  # VPC NAME
  name = "eks-gitops-demo-vpc"
  # VPC CIDR
  cidr = "10.0.0.0/16"
  # SUBNET
  azs             = ["ap-northeast-1a", "ap-northeast-1c"]
  public_subnets  = ["10.0.1.0/24", "10.0.2.0/24"]
  private_subnets = ["10.0.3.0/24", "10.0.4.0/24"]
  # NAT GATEWAY
  enable_nat_gateway = true
  single_nat_gateway = true
  # DNS
  enable_dns_hostnames = true
}

module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "18.26.0"

  # EKS CONTROL PLANE 
  cluster_name                    = "eks-gitops-demo"
  cluster_version                 = "1.22"
  cluster_endpoint_private_access = false
  cluster_endpoint_public_access  = true

  # EKS CLUSTER VPC AND SUBNETS
  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnets

  # EKS MANAGED NODE GROUPS
  eks_managed_node_groups = {
    gitops-demo = {
      desired_size   = 2
      instance_types = ["t3.small"]
    }
  }

  node_security_group_additional_rules = {
    admission_webhook = {
      description                   = "Admission Webhook"
      protocol                      = "tcp"
      from_port                     = 0
      to_port                       = 65535
      type                          = "ingress"
      source_cluster_security_group = true
    }

    ingress_node_communications = {
      description = "Ingress Node to node"
      protocol    = "tcp"
      from_port   = 0
      to_port     = 65535
      type        = "ingress"
      self        = true
    }

    egress_node_communications = {
      description = "Egress Node to node"
      protocol    = "tcp"
      from_port   = 0
      to_port     = 65535
      type        = "egress"
      self        = true
    }
  }
}

data "aws_eks_cluster" "cluster" {
  name = module.eks.cluster_id
}

data "aws_eks_cluster_auth" "cluster" {
  name = module.eks.cluster_id
}

GitOps パイプラインのデモ環境の構築

前提条件

  • Terraform がセットアップされていること
  • kubectl がセットアップされていること
  • AWS CLI がセットアップされていること
  • Argo CDのCLI がセットアップされていること

動作確認環境

$ terraform version
Terraform v1.1.9

$ aws --version
aws-cli/2.7.12 Python/3.9.11 Darwin/19.6.0 exe/x86_64 prompt/off

$ kubectl version --client --short
Client Version: v1.21.2

$ argocd version
argocd: v2.4.3+471685f.dirty

EKS クラスターのセットアップ

Terraform を実行します。

$ terraform init
$ terraform plan
$ terraform apply
結果:
Apply complete! Resources: 72 added, 0 changed, 0 destroyed.

下記コマンドを実行し、Terraform のoutput を控えておきます。

$ terraform output
結果:
CODECOMMIT_IMAGE_REPOSITORY_URL = "https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/eks-gitops-demo-app"
CODECOMMIT_INFRA_REPOSITORY_URL = "https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/eks-gitops-demo-k8s"
ECR_REPOSITORY_URL = "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/eks-gitops-demo-app"
app_name = "eks-gitops-demo-app"
argocd_sync_password = <sensitive>
argocd_sync_username = <sensitive>
eks_cluster_name = "eks-gitops-demo"

構築したEKSクラスターに接続できることを確認します。

$ aws eks update-kubeconfig \
  --region ap-northeast-1 \
  --name eks-gitops-demo \
  --profile [YOUR AWS ACCOUNT PROFILE NAME]
結果:
Added new context arn:aws:eks:ap-northeast-1:[AWSアカウントID]:cluster/eks-gitops-demo to /Users/*****/.kube/config

マニフェストファイルをリポジトリにプッシュ

下記2つのマニフェストファイルを terraform outputCODECOMMIT_INFRA_REPOSITORY_URL 先(マニフェストのリポジトリ)にプッシュします。

[AWSアカウントId]を自環境に変更します。

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: eks-gitops-demo-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: eks-gitops-demo-app
  template:
    metadata:
      labels:
        app: eks-gitops-demo-app
    spec:
      containers:
        - name: eks-gitops-demo-app
          image: [AWSアカウントId].dkr.ecr.ap-northeast-1.amazonaws.com/eks-gitops-demo-app:latest
          ports:
            - name: http
              containerPort: 80

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: eks-gitops-demo-app
spec:
  type: LoadBalancer
  selector:
    app: eks-gitops-demo-app
  ports:
    - port: 80
      targetPort: http

Argo CDのセットアップ

kubectl コマンドを使って、Argo CDをデプロイします。

$ kubectl create namespace argocd
結果:
namespace/argocd created

$ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

Argo CDのPod が稼働していることを確認します。

$ kubectl get pod -n argocd
結果:
NAME                                                READY   STATUS    RESTARTS   AGE
argocd-application-controller-0                     1/1     Running   0          25s
argocd-applicationset-controller-5f7d8fffb7-s2n6n   1/1     Running   0          26s
argocd-dex-server-c6dcf7fdb-6qqll                   1/1     Running   0          26s
argocd-notifications-controller-544ff7766d-pljxl    1/1     Running   0          26s
argocd-redis-75bdb869bf-8b2tv                       1/1     Running   0          26s
argocd-repo-server-649b4b66bd-4g22t                 0/1     Running   0          26s
argocd-server-5df946ff78-zq5tl                      0/1     Running   0          26s

下記コマンドで、Argo CDの管理者用のデフォルトのパスワードを取得します。

$ kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
結果:
Argo CDの管理者用のデフォルトのパスワード

下記コマンドで、ローカルポートをArgo CDサーバーにポートフォワードします。

$ kubectl port-forward svc/argocd-server -n argocd 8080:443
結果:
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080

下記コマンドで、Argo CDサーバーにログインします。

$ argocd login localhost:8080 \
  --username admin \
  --password [Argo CD管理者用のデフォルトのパスワード] \
  --insecure \
  --port-forward-namespace argocd
結果:
'admin:login' logged in successfully
Context 'localhost:8080' updated

下記コマンドで、Argo CDからマニフェストのリポジトリにHTTPS 接続する時に使用するIAMの情報を参照します。

$ terraform output argocd_sync_username
結果:
ユーザー名
$ terraform output argocd_sync_password
結果:
パスワード

下記コマンドで、マニフェストのリポジトリをArgo CDサーバーに追加します。

$ CODECOMMIT_INFRA_REPOSITORY_URL=https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/eks-gitops-demo-k8s

$ argocd repo add ${CODECOMMIT_INFRA_REPOSITORY_URL} \
  --username ユーザー名 \
  --password パスワード
結果:
Repository 'https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/eks-gitops-demo-k8s' added

Argo CDのアプリケーションとマニフェストのリポジトリを同期させる設定をします。

$ kubectl create namespace eks-gitops-demo-app
結果:
namespace/eks-gitops-demo-app created

$ argocd app create eks-gitops-demo-app \
  --repo ${CODECOMMIT_INFRA_REPOSITORY_URL} \
  --revision master \
  --path ./ \
  --dest-server https://kubernetes.default.svc \
  --dest-namespace eks-gitops-demo-app \
  --sync-policy automated \
  --auto-prune
結果:
application 'eks-gitops-demo-app' created

アプリケーションの同期設定が登録されていることを確認します。

$ argocd app list
NAME                 CLUSTER                         NAMESPACE            PROJECT  STATUS  HEALTH   SYNCPOLICY  CONDITIONS  REPO                                                                              PATH  TARGET
eks-gitops-demo-app  https://kubernetes.default.svc  eks-gitops-demo-app  default  Synced  Healthy  Auto-Prune  <none>      https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/eks-gitops-demo-k8s  ./    master

ブラウザでhttps://localhost:8080にアクセスして、Argo CDのGUIに接続します。

項目 入力内容
username admin
Password Argo CDの管理者用のデフォルトのパスワード

ログイン画面

GUI上でもアプリケーションが表示されていることを確認します。

これで、GitOps パイプラインのデモ環境の構築が完了です。

GitOps パイプラインのデモ環境の実行

構築したデモ環境で、アプリケーションをデプロイします。

アプリのソースをリポジトリにプッシュ

下記2つのファイルを terraform outputCODECOMMIT_INFRA_REPOSITORY_URL先(アプリのソースのリポジトリ)にプッシュします。

Dockerfile

FROM nginx:latest 
WORKDIR /usr/share/nginx/html
COPY index.html index.html

index.html

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>GitOps Demo</title>
</head>

<body>
    <h1>GitOps Demo App v1</h1>
</body>

</html>

CodeBuild の実行

アプリのソースのリポジトリの変更をトリガーにCodePipeline が開始され、CodeBuild が実行されます。

イメージをECR をプッシュ

CodeBuild で、イメージがビルドされ、ECRにプッシュされます。

マニフェストのリポジトリにプルリクエスト

CodeBuild で、ECRにプッシュされたイメージを差し替えするためのプルリクエストをマニフェストのリポジトリに送信します。

マージ

マニフェストのリポジトリへのプルリクエストの内容を確認して、マージします。

デプロイ

Argo CDが、マニフェストのリポジトリの変更を検知すると、Kubernetes の実行環境に反映します。

$ kubectl get pod,svc,deploy -n eks-gitops-demo-app
NAME                                      READY   STATUS    RESTARTS   AGE
pod/eks-gitops-demo-app-7f54cfd78-cpcqm   1/1     Running   0          101s
pod/eks-gitops-demo-app-7f54cfd78-wmbcj   1/1     Running   0          98s

NAME                          TYPE           CLUSTER-IP      EXTERNAL-IP                                                                   PORT(S)        AGE
service/eks-gitops-demo-app   LoadBalancer   172.20.232.32   a3114a987111542a2affe40c67071c69-111111111.ap-northeast-1.elb.amazonaws.com   80:30146/TCP   17m

NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/eks-gitops-demo-app   2/2     2            2           17m

動作確認

下記コマンドで出力されたEXTERNAL-IP のLaoadBalancer のDNS名を確認します。

% kubectl get svc -n eks-gitops-demo-app 
NAME                  TYPE           CLUSTER-IP      EXTERNAL-IP                                                                   PORT(S)        AGE
eks-gitops-demo-app   LoadBalancer   172.20.232.32   a3114a987111542a2affe40c67071c69-111111111.ap-northeast-1.elb.amazonaws.com   80:30146/TCP   18m

ブラウザでLaoadBalancer のDNS名にHTTPアクセスして、アプリケーションがデプロイされたことを確認します。

Argo CDのGUI上でも確認できます。
Node

NetWork

これで、GitOps パイプラインを使ったアプリケーションのデプロイが完了です。

リソースのクリーンアップ

マニフェストファイルのリポジトリのservice.yamlを削除して、Kubernetes の環境からELBを削除し、下記コマンドで確認します。

$ kubectl get svc -n eks-gitops-demo-app 
No resources found in eks-gitops-demo-app namespace.

下記のコマンドで、すべてのリソースを削除します。

$ terraform destroy
Destroy complete! Resources: 72 destroyed.

さいごに

GitOps パイプラインの入門的な内容ですが、参考になれば幸いです。

元記事はこちら

Terraform で EKS の GitOps パイプラインを構築する
著者:@okubot55


アイレットなら、AWS で稼働するサーバーを対象とした監視・運用・保守における煩わしい作業をすべて一括して対応し、経験豊富なプロフェッショナルが最適なシステム環境を実現いたします。AWS プレミアコンサルティングパートナーであるアイレットに、ぜひお任せください。

AWS 運用・保守サービスページ

その他のサービスについてのお問合せ、お見積り依頼は下記フォームよりお気軽にご相談ください。
https://www.iret.co.jp/contact/service/form/