EKSでWeave Fluxを使ってGitOpsしてみる

2019.11.24

おはようございます、もきゅりんです。

最近は故あって、空いた時間をひたすら Amazon EKS Workshop に費やしております。

非常にコンテンツがボリューミーで中身が濃いです。

そのコンテンツの中から、GITOPS WITH WEAVE FLUXをやってみた、というものです。多少の行間を埋めるように心掛けました。

元々は、こちらのブログの内容かと思います。

Deploying GitOps with Weave Flux and Amazon EKS

今回やることの趣旨を簡単に述べると、Gitのみをソースとして、EKSにContinuous Delivery(以下CD。継続的なデリバリ)する、という内容です。

よく似たツールに Argo CD があります。

弊社記事でも紹介されています。

EKSでArgo CDのチュートリアルを試してみた

Argo CDと同様、Pull型の同期です。

用語

EKS Workshopでは章が進むにつれて順次、用語や概念、ツールが増えていきます。したがって、途中から参入した場合に、よくわからない単語がいきなり出てくることもあると思いますので、今回出てくる用語を簡単に説明しておきます。なお、AWSの基本的な用語は既知であるとします。

GitOps

Weaveworks が提唱するCDの手法。開発者にとって、馴染み深いGitのみを信頼できるソースとして利用してクラスターにデプロイするので、容易であるという考え方に基づく。

Helm

Helm とは、Chartと呼ばれる、複数のKubernetes(以下、k8s)リソース一式(下記のManifest)を管理・共有できるツールです。HelmとChartを用いて、k8sクラスタにアプリケーションを構築します。誤解を恐れず述べれば、アプリケーションの持ち運びや管理を容易にできるツール、というイメージ。

Manifest

k8sで利用するリソース設定ファイル。yml, yaml, json形式どちらでも利用できますが、自分はこれまでjsonで書かれたものを見たことありません。AWSで使われるCloudFormationのテンプレートと同じようなもの。

Custom Resource Definition

k8sには、DeploymentやPodといったリソースが元々ありますが、自身でそのようなリソースを定義して機能を拡張することができます。Workshopにも CUSTOM RESOURCE DEFINITION チャプターがあります。

概略図

今回実行するCI/CDの図です。

上下のGitHubで、それぞれアプリケーションのリポジトリと、Manifestファイルのリポジトリとが分かれて管理されています。

またECRに対しての矢印に注目すると、Weave Fluxの方からPullしているのが分かります。

archi image

環境の準備について

基本的には、GETTING STARTEDに沿って進めています。

Cloud9(EC2/t2.micro/Amazon Linux2)で作業します。

INSTALL KUBERNETES TOOLSでは、version1.13.7のkubectlのインストールですが、自分はv1.14.8でインストールしました。(Serverのバージョンに合わせた) *1

LAUNCH USING EKSCTLの通りに進めます。

Helmからhelm CLIのインストールまで対応します。

これでようやく準備が整いました。

CodePipelineで使うS3バケットおよび各種IAM Roleを作成する

自身のアカウントIDを用いた名前のS3バケットを作成します。 CodePipelineのIAMロール、CodeBuidのIAMロールのポリシー定義を取得して、AWS CLIを使って、それぞれロールに設定してあげます。

# Use your account number below
ACCOUNT_ID=$(aws sts get-caller-identity | jq -r '.Account')
aws s3 mb s3://eksworkshop-${ACCOUNT_ID}-codepipeline-artifacts

cd ~/environment

wget https://eksworkshop.com/weave_flux/iam.files/cpAssumeRolePolicyDocument.json

aws iam create-role --role-name eksworkshop-CodePipelineServiceRole --assume-role-policy-document file://cpAssumeRolePolicyDocument.json

wget https://eksworkshop.com/weave_flux/iam.files/cpPolicyDocument.json

aws iam put-role-policy --role-name eksworkshop-CodePipelineServiceRole --policy-name codepipeline-access --policy-document file://cpPolicyDocument.json

wget https://eksworkshop.com/weave_flux/iam.files/cbAssumeRolePolicyDocument.json

aws iam create-role --role-name eksworkshop-CodeBuildServiceRole --assume-role-policy-document file://cbAssumeRolePolicyDocument.json

wget https://eksworkshop.com/weave_flux/iam.files/cbPolicyDocument.json

aws iam put-role-policy --role-name eksworkshop-CodeBuildServiceRole --policy-name codebuild-access --policy-document file://cbPolicyDocument.json

GitHubで2つのリポジトリの準備とaccess tokenを発行する

Dockerイメージにbuildされるアプリケーションのリポジトリと、Manifestを管理するリポジトリをそれぞれ作成します。今回はCloud9上で対応するため、Publicリポジトリで進めています。

manifest git repo

また、CodePipelineがBuildするために、リポジトリにアクセスするためのaccess tokenを発行します。GitHub右上のアイコンからSettings > Developer settings > Personal access tokensです。

判別できる名前を付けて下図の通りに発行します。

app git repo

Helmを使ってWeave FluxをEKSにデプロイする

Get started with Flux using Helmの通り進めます。

Flux Custom Resource Definitionをインストールします。

kubectl apply -f https://raw.githubusercontent.com/fluxcd/flux/helm-0.10.1/deploy-helm/flux-helm-release-crd.yaml

Tiller *2がインストールされているか確認します。

helm ls

Error: could not find tillerが表示された場合は、INSTALL HELM CLIに戻りましょう。

Fluxをインストールします。

YOURUSERk8s-configは、それぞれGitHubの使用名とリポジトリ名で置き換えて下さい。

helm repo add fluxcd https://charts.fluxcd.io

helm upgrade -i flux \
--set helmOperator.create=true \
--set helmOperator.createCRD=false \
--set git.url=git@github.com:YOURUSER/k8s-config \
--namespace flux \
fluxcd/flux

名前空間fluxのpodを確認します。

$ kubectl get pods -n flux
NAME                                  READY   STATUS    RESTARTS   AGE
flux-5c678b8944-gg756                 1/1     Running   0          95s
flux-helm-operator-6877b9f564-j2dmp   1/1     Running   0          95s
flux-memcached-88db78d9d-h57rc        1/1     Running   0          95s

fluxctl をインストールして、sshキーを発行します。それをManifest用に作成したGitHubリポジトリのデプロイキーとして登録します。

sudo wget -O /usr/local/bin/fluxctl https://github.com/fluxcd/flux/releases/download/1.14.1/fluxctl_linux_amd64
sudo chmod 755 /usr/local/bin/fluxctl
fluxctl version

fluxctlは、デフォルトで名前空間"default"にポートフォワーディングします。--k8s-fwd-nsを付けることで指定した名前空間に設定することができます。

fluxctl identity --k8s-fwd-ns flux

ssh-rsa XXXXZ3NzaY1yc2EXXXXDXQXZXXXZXQYwXHtlWwXLipgktMbGuLQco9wqyYt0g5g5pO+28kdeakqn0uWYr7O9z0vb7K7eViS4R8agX/LPizGpFYLG0tDazwNWFyLM4RnvXQYRhiVEYFPZuIYHKoao8FiyYoZ588GLRGXDw3hkyfK17r1kxXqDdXWJ5HerrnLHe1UtXXLPljen2xFeijbxfPmHXM8/Oq3lm5aMNJ36vhOtqUgvnXiFUbn9Ua16pj1eouZeh2LNPGPzTTtYw5XDh/Mpswojoi5UGwn4RxDYGveN9K4TVpUJ4dbSYV4MoYE+IEy7TGIJVz0Z5SyeayYIWXnEOo+1JPbL5YDjd2S7GZKYkhjWWm2L

Keyの箇所に貼り付けます。

enroll ssh key

こうすることで、更新されたGitHubリポジトリにアクセスすることが可能となり、FluxはGitHubの構成とk8sクラスターにデプロイされた構成とを同期します。

CFnテンプレートでCodePipelineを構築する

記載された通りに項目を埋めてスタックを作成します。ECR、CodeBuildProject、CodePipelineを作成します。

codepipeline cfn

アプリケーション用のGitHubにgit pushする

まずはgit cloneします。YOURUSERはGitHubユーザ名です。

git clone https://github.com/YOURUSER/eks-example.git
cd eks-example

nginxのファイルとDockerfileを取得してpushします。 ビルドの進捗が見えるように、CodePipelineの画面を開いておきましょう。

echo "# eks-example" > README.md
mkdir src
wget -O src/hello.conf https://eksworkshop.com/weave_flux/app.files/hello.conf
wget -O src/index.html https://eksworkshop.com/weave_flux/app.files/index.html
wget https://raw.githubusercontent.com/aws-samples/eks-workshop/master/content/weave_flux/app.files/Dockerfile

tree
.
├── Dockerfile
├── README.md
└── src
    ├── hello.conf
    └── index.html

git add .
git commit -am "Initial commit"
git push

codepipeline success

ECRにもちゃんとDockerイメージが格納されています。

ecr image

Manifestを使ってデプロイする

つぎに、Manifest用のリポジトリをgit cloneします。

cd ..
git clone https://github.com/YOURUSER/k8s-config.git
cd k8s-config
mkdir namespaces workloads

そしたらManifestファイルを作成していきます。イメージ名はECRに作成されたもので更新してください。

なお、latestタグを付けておかないとデフォルトではlatestになりません。

cat << EOF > namespaces/eks-example.yaml
apiVersion: v1
kind: Namespace
metadata:
  labels:
    name: eks-example
  name: eks-example
EOF

cat << EOF > workloads/eks-example-dep.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: eks-example
  namespace: eks-example
  labels:
    app: eks-example
  annotations:
    # Container Image Automated Updates
    flux.weave.works/automated: "true"
    # do not apply this manifest on the cluster
    #flux.weave.works/ignore: "true"
spec:
  replicas: 1
  selector:
    matchLabels:
      app: eks-example
  template:
    metadata:
      labels:
        app: eks-example
    spec:
      containers:
      - name: eks-example
        image: YOURACCOUNT.dkr.ecr.us-east-1.amazonaws.com/eks-example:YOURTAG
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        livenessProbe:
          httpGet:
            path: /
            port: http
        readinessProbe:
          httpGet:
            path: /
            port: http
EOF

cat << EOF > workloads/eks-example-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: eks-example
  namespace: eks-example
  labels:
    app: eks-example
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app: eks-example
EOF

tree
.
├── namespaces
│   └── eks-example.yaml
├── README.md
└── workloads
    ├── eks-example-dep.yaml
    └── eks-example-svc.yaml

flux.weave.works/automatedは、コンテナイメージを自動で更新するかどうかを設定します。flux.weave.works/ignoreはdeploymentをしないように設定できるようです。

デフォルトの設定では、5分間ごとにGitHubの構成を自動Pullします。

git pushします。

git add .
git commit -am "eks-example-deployment"
git push

fluxのpodのログから確認します。

kubectl get pods -n flux
kubectl logs flux-5c678b8944-gg756 -n flux
kubectl describe service eks-example -n eks-example

エンドポイントにアクセスすると、こんな感じのが表示されます。

endpoint

次に、index.htmlを更新してみます。

cd ../eks-example
vi src/index.html
   # Change the <title> AND <h> to Demo Flux with GitOps

git commit -am "v2 Updating home page"
git push

自動で更新されます。 *3

v2 index

Helmでデプロイする

今度ではHelmで対応します。 *4

mkdir namespaces releases

Manifestファイルを作成します。

cat << EOF > namespaces/nginx.yaml
apiVersion: v1
kind: Namespace
metadata:
  labels:
    name: nginx
  name: nginx
EOF

cat << EOF > releases/nginx.yaml
---
apiVersion: flux.weave.works/v1beta1
kind: HelmRelease
metadata:
  name: mywebserver
  namespace: nginx
  annotations:
    flux.weave.works/automated: "true"
    flux.weave.works/tag.nginx: semver:~1.16
    flux.weave.works/locked: 'true'
    flux.weave.works/locked_msg: '"Halt updates for now"'
spec:
  releaseName: mywebserver
  chart:
    repository: https://charts.bitnami.com/bitnami/
    name: nginx
    version: 3.3.2
  values:
    usePassword: true
    image:
      registry: docker.io
      repository: bitnami/nginx
      tag: 1.16.0-debian-9-r46
    service:
      type: LoadBalancer
      port: 80
      nodePorts:
        http: ""
      externalTrafficPolicy: Cluster
    ingress:
      enabled: false
    livenessProbe:
      httpGet:
        path: /
        port: http
      initialDelaySeconds: 30
      timeoutSeconds: 5
      failureThreshold: 6
    readinessProbe:
      httpGet:
        path: /
        port: http
      initialDelaySeconds: 5
      timeoutSeconds: 3
      periodSeconds: 5
    metrics:
      enabled: false
EOF

tree
├── namespaces
│   └── nginx.yaml
├── README.md
└── releases
    └── nginx.yaml

タグやLockなどのCRDが付記されているのが分かります。

そしたらpushします。

git add .
git commit -am "Adding nginx helm release"
git push

確認します

kubectl get pods -n flux
kubectl logs flux-5c678b8944-gg756 -n flux

helm ls
kubectl get all -n nginx

エンドポイントにアクセスするといつものやつが出ます。

endpoint nginx

以上です。

なお、検証後は、くれぐれも後片付けに留意して下さいまし!

最後に

GitOps、確かに分かりやすいです。

思った以上にかなり楽にCI/CDできました。

どなたかのお役に立てば幸いです。

参考:

脚注

  1. 余談ですが、CI/CD WITH CODEPIPELINEのチャプターではbuildspec.yamlのkubectlのバージョンが古くてBuildに失敗するため、修正する必要があります。バージョンには要注意です。
  2. Helmは、k8sクラスタにhelmコマンドを受け付けるtillerをインストールし、各種リソースを制御します。
  3. ロールバック方法も紹介されていますが割愛します。
  4. Kustomizeを使ってデプロイすることもできるようなので、後日対応してみようかとは思っています。