Article:

背景

個人でk8sを活用し始めると、個人用のhelmレポジトリが欲しくなると思います(私は欲しくなりました)。とは言え、helmレポジトリはwebサーバとしてのホストが必要なので少々面倒です。

webで軽く調べてみると、github pagesを用いて作っている方法をよく見かけるのですが、helmレポとして公開するためにはtgzにアーカイブしたファイルが必要で、それをgitに含めるのはあんまりキレイではないですよね。

いろいろ悩みましたが、githubでコードをパッケージしてs3にデプロイする方法を取ってみることにしました。 こうすることで、gitレポジトリ内にtgzを配置する必要がなくなります。

またterraformで構成されているので、forkしてちょっとだけパラメーターを書き換えるだけですぐにこの環境が作れるはずです。

僕のレポジトリはhttps://github.com/totegamma/helmchartsにあるので参考にしてみてください。(ていうかこれのchartsの中身以外を丸々コピーで用は済むと思います。)

terraform

今回はs3とcloudfrontのシンプルな構成ではあるものの、terraformで構成管理することにします。複製したいときに楽ですしね。

また、helmの成果物のアップロードもterraformで行います。次のコードです。

terraform/s3.tf (抜粋)
1
2
3
4
5
6
7
8
resource "aws_s3_object" "web" {
    for_each = fileset("${path.cwd}/charts/", "*.{tgz,yaml}")
    bucket = aws_s3_bucket.www.id
    key = each.value
    content_type = lookup(local.mime_types, regex("\\.[^.]+$", each.value), null)
    source = "${path.cwd}/charts/${each.value}"
    etag = filemd5("${path.cwd}/charts/${each.value}")
}

基本的にfileset().tgz.yamlファイルを選択してs3とシンクしています。

複雑な部分はcontent_typeを決定している部分ですね。s3はメタデータとしてそのファイルのmimeタイプのフィールドがあるのですが、terraformで直接アップロードした場合デフォルトだとbinary/octet-streamの1択になってしまいます。拡張子などを自動で考慮してくれません。

なので、拡張子とmimeタイプの対応表を用意してあげる必要があります。うーんめんどくさい。が、こうするしかないみたいです。

github actions

この章は抜粋で解説するので、全体像はgit上のファイルを参照してください。

また、awsとの認証はOIDCでやっています。こちらの記事も併せて参考にしてみてください。

あとはhelmパッケージを作るだけですね。

s3とのシンク

まず、s3にあるtgz等をまるごとgithubactions上に引き戻します。 (最初の1回目のデプロイだと無意味&実行不可なのでこれはあとで付け加える必要があります…)

1
2
- name: download from s3
    run: aws s3 sync s3://<bucket name> charts

これは、後にhelm repo indexコマンドを叩いた際に、すでにs3にデプロイ済みの過去パッケージも含めたindex.yamlを生成するためにtgzが存在する必要がある為です。

helm package & index

chartsディレクトリ以下にあるすべてのディレクトリそれぞれをパッケージし、最後にhelm repo index .を行ってindex.yamlを生成します。

1
2
3
4
5
6
7
8
9
- name: generate helm repo
  run: |
    cd charts
    for ELEM in */; do
      NAME=$(echo $ELEM | tr -d '/')
      echo $NAME
      helm package $ELEM
    done
    helm repo index .    

terraformの実行

ここはただ実行するだけですね。特筆することなしです。

1
2
3
4
5
6
7
8
9
- name: Terraform Init
  run: terraform -chdir=terraform init -var="acm_certificate_arm=$CERTIFICATE_ARN"
  env:
    CERTIFICATE_ARN: ${{ secrets.CERTIFICATE_ARN }}

- name: Terraform Apply
  run: terraform -chdir=terraform apply -auto-approve -var="acm_certificate_arm=$CERTIFICATE_ARN"
  env:
    CERTIFICATE_ARN: ${{ secrets.CERTIFICATE_ARN }}

キャッシュの無効化

s3を更新してもcloudfrontにキャッシュされているので、新しく追加されたファイルに関しては問題ないものの、そもそものindex.yamlの最新版が降ってきません。ので、awsコマンドでキャッシュの更新を行います。

また、このとき必要なcloudfrontのdistibution idは、terraform showの結果をjqでコネコネして取得しました。

1
2
3
4
- name: invalidate cache
  run: |
    ID=$(terraform -chdir=terraform show -json | jq -r '.values.root_module.resources | map(select(.address == "aws_cloudfront_distribution.static-www"))[0] | .values.id')
    aws cloudfront create-invalidation --distribution-id $ID --paths "/*"