Chatwork Creator's Note

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

読者になる

ACKを活用して使い捨てAWS検証環境(PR環境)を構築している話

イベント盛りだくさんだった夏がぼちぼち終わります。とはいえ秋もいろんなカンファレンスや勉強会が開かれるのでまだまだ気が抜けないと思っている @cw-furuya です。

ということでこのたび、MIXIさんに全面的にご協力いただき、EKSとAuroraの知見をシェアする勉強会を開催しました。

mixi.connpass.com

その中で「ACKを活用して使い捨てAWS検証環境を構築している話」というタイトルでお話させていただきました。 発表資料もありますので、そちらを見ていただきつつ載せきれなかった具体例などをこちらに残します。

資料はこちらをご覧ください。

speakerdeck.com

PR Generatorの活用

ここの具体的なマニフェスト構成はこんな感じです。(簡略ver)

.
├── README.md
├── environments
│   └── chatwork.yaml
├── helmfile.yaml
└── manifests
    └── chatwork
        ├── applicationsets-pr-test.yaml.gotmpl
        └── appprojects.yaml.gotmpl

helmfileのraw manifests機能を使って、applicationsetを定義しています。 また定義自体はgotmpl形式でenvironmentsから値を変更可能にしています。 メインどころであるapplicationsets-pr-test.yaml.gotmplの中身はだいたいこんな感じです。

{{- $root := . }}
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: chatwork-pr-test
  namespace: default
spec:
  syncPolicy:
    preserveResourcesOnDeletion: false
  generators:
    - pullRequest:
        filters:
        - branchMatch: "test-.*"
        github:
          ...
        requeueAfterSeconds: 180
  template:
    metadata:
      name: '{{`{{ branch }}`}}-chatwork'
    spec:
      destination:
        server: '{{ (fetchSecretValue $root.Values.pullRequestGenerators.eks.test.server) }}'
        namespace: '{{`{{ branch }}`}}-chatwork'
      project: chatwork
      source:
        path: xxx
        repoURL: xxx
        targetRevision: '{{`{{ branch }}`}}'
        plugin:
          env:
            ...
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: chatwork-irsa-pr-test
spec:
...
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: chatwork-dynamodb-pr-test
spec:
...
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: chatwork-sqs-pr-test
spec:
...

ポイントをいくつか。

  syncPolicy:
    preserveResourcesOnDeletion: false

この設定は、Argo CD Applicationを削除したタイミングで、配下のKubernetesリソースを削除するか否かを決めるパラメータです。 本番等はtrueにしています(事故ってApplicationが消えてもアプリのPod等は生き続ける)が、 PR環境についてはApplication削除タイミングでリソースも削除したいため、明示的にfalseにしています。

        requeueAfterSeconds: 180

GitHubのポーリング間隔[秒]です。上のサンプルでは端折ってしまいましたが、chatwork-irsa-pr-testの値は10とかにしてます。

      destination:
        server: '{{ (fetchSecretValue $root.Values.pullRequestGenerators.eks.test.server) }}'

スライドの「工夫したポイント」に記載した、アプリケーション展開先のEKSクラスタ情報です。 別途environmets側のファイルで、Parameter Storeからこんな感じで取得するようにしつつ、

pullRequestGenerators:
  eks:
    test:
      name: 'ref+awsssm:/[パラメータストアのパス]?region=ap-northeast-1'
      server: 'ref+awsssm:/[パラメータストアのパス]?region=ap-northeast-1'

fetchSecretValue関数で展開しています。 アプリケーションクラスタの移行時に修正漏れがないように、DRYにしたかった感じです。

        targetRevision: '{{`{{ branch }}`}}'

作成したArgo CD ApplicationはデフォルトでフックになったPRのブランチを向くようにしています。 これでPR IDなどをつけて諸々一意にしたマニフェストが参照され、アプリケーションが展開されます。

ACKの活用

ACKで作るリソース側(例としてIAM)のマニフェスト構成です。(さっきと似た感じですが)

.
├── README.md
├── environments
│   └── roles
│       ├── test.yaml
│       └── default.yaml
├── helmfile.yaml
└── manifests
    └── role.yaml.gotmpl

スライド中でも触れましたが、こちらもhelmfile + raw manifestsを使って、Namespace含めてIRSAに必要なリソースをまとめて作成しています。 role.yaml.gotmplの中身はこんな感じ。

apiVersion: v1
kind: Namespace
metadata:
  name: {{ .Values.iam.prefix }}chatwork
---
apiVersion: iam.services.k8s.aws/v1alpha1
kind: Policy
metadata:
  name: '{{ .Values.iam.prefix }}chatwork'
  namespace: '{{ .Values.iam.prefix }}chatwork'
spec:
  policyDocument: |
  ...
  name: '{{ .Values.iam.prefix }}ack-chatwork'
---
apiVersion: iam.services.k8s.aws/v1alpha1
kind: Role
metadata:
  name: '{{ .Values.iam.prefix }}ack-chatwork'
  namespace: '{{ .Values.iam.prefix }}chatwork'
spec:
  assumeRolePolicyDocument: |
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Federated": "arn:aws:iam::xxxxxxxxxxxx:oidc-provider/{{ fetchSecretValue .Values.iam.oidc_url }}"
                },
                "Action": "sts:AssumeRoleWithWebIdentity",
                "Condition": {
                    "StringEquals": {
                        "{{ fetchSecretValue .Values.iam.oidc_url }}:aud": "sts.amazonaws.com",
                        "{{ fetchSecretValue .Values.iam.oidc_url }}:sub": "system:serviceaccount:{{ .Values.iam.prefix }}chatwork:ack-chatwork"
                    }
                }
            }
        ]
    }
  name: '{{ .Values.iam.prefix }}ack-chatwork'
  policies:
  - "arn:aws:iam::xxxxxxxxxxxx:policy/{{ .Values.iam.prefix }}chatwork"
---
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::xxxxxxxxxxxx:role/{{ .Values.iam.prefix }}ack-chatwork
    eks.amazonaws.com/sts-regional-endpoints: "true"
    eks.amazonaws.com/token-expiration: "86400"
    argocd.argoproj.io/sync-wave: "1"
  name: ack-chatwork
  namespace: '{{ .Values.iam.prefix }}chatwork'

PR IDなどを使って都度prefixを変えることで、それぞれ異なる名前で生成されるようにしています。 また、Roleの信頼関係で必要なクラスタの情報は先程と同様にParameter Storeに格納して参照しています。

まとめ

Argo CD Pull Request GeneratorとAWS Controllers for Kubernetesについて、資料に記載しきれなかった具体的なマニフェストをご紹介しました。 これでばんばんPR環境を作っていきましょう!!!