Github ActionsでクロスコンパイルしてGithub Releaseにアップロードする
追記: GoReleaserのbabarotさんの記事があったので紹介。GoReleaser使ったほうが手軽でよさそう。 Go で書いた CLI ツールのリリースは GoReleaser と GitHub Actions で個人的には決まり | tellme.tokyo
追記: GoReleaserのプロジェクトがGithub Actionを公開したようなのでこちら使ってみるといいかもしれません。 https://github.com/goreleaser/goreleaser-action
この前kube-promptのリリースでミスをしてしまいissueが立て続けに2件上がったことがあったのですが、Github Actionsが自分のリポジトリで使えるようになったのでリリースを自動化することにしました。
触ってみてわかったのですが、↓の記事を書いたときとは多くの変更がありました。workflowの記述フォーマットがyamlに変更されていたり、Azure Pipelinesを裏側で使うようになったことでSupported virtual environmentsにmacOSやWindowsが増えたり、その影響でCustom actionはDockerコンテナベースではなくJavaScriptアプリケーションとして記述できるようになったりしています。JavascriptでCustom actionを記述するのはまた今度にして、とりあえず今回はyaml形式のworkflowを使っていきます。
クロスコンパイルとGithub releasesへのアップロード
kube-promptではこんな感じでいくつかバイナリをGithub Releaseに含めています。今回は tagが作成されたタイミングでフックしてこれらのbinaryをcross compile, zipで圧縮してGithub Release画面を作成します。
まずworkflowのevent trigger指定します。 今回からcronのように定期実行もできるようになっていますが、基本的には GithubのWebhook event を起動トリガーにすることがほとんどかと思います。git tagのcreateイベントをトリガーにしたいので次のようになります 1 。
on: create: tags: - v*.*.*
次にjobs propertyより実際にGithub Actionsで行いたい操作を記述します。 そんなに長くなくて↓のような感じです。
jobs: release: # job_id name: Build runs-on: ubuntu-latest steps: - name: Set up Go 1.12 uses: actions/setup-go@v1 with: version: 1.12 id: go - name: Check out code into the Go module directory uses: actions/checkout@master - name: Build env: GO111MODULE: on GOPATH: /home/runner/work/ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | export CREATE_EVENT_REF_TYPE=$(jq --raw-output .ref_type "$GITHUB_EVENT_PATH") export TAGNAME=$(jq --raw-output .ref "$GITHUB_EVENT_PATH") if [ "$CREATE_EVENT_REF_TYPE" != "tag" ]; then echo "ref_type is not a tag: ${CREATE_EVENT_REF_TYPE}" && exit 78; fi make cross go get -u github.com/tcnksm/ghr $GOPATH/bin/ghr -n ${TAGNAME} -b "ʕ◔ϖ◔ʔ Release ${TAGNAME}" -draft ${TAGNAME} pkg/
jobs.<job_id>.runs-on
に実行環境を指定することができて、今回からmacOSやWindowsが追加されました。kube-promptはCGOの依存とかもなく普通にクロスコンパイルできるので、一番速度や動作が安定してそうなubuntuにしています。他のCIサービスと同様に、matrixも組めるのでテストとか必要に応じてやっておくといいかと思います。- GITHUB_TOKENは以前までだと自分でsecretsに登録する必要がありましたが、今回からはその必要も無いようです。yaml内で
${{secrets.GITHUB_TOKEN}}
とするだけで取り出せます。- どこまでが古いドキュメントにしか書かれてなくて、どこまでが新しいドキュメントに書かれてるのか分からずこれに気づくの少し時間がかかってしまいました。
$GITHUB_EVENT_PATH
にはWebhook eventのJSONファイルがそのまま入っています。今回はcreate
イベントにフックしているので、 こちら にあるように、ref_type
フィールドからtag名、ref
フィールドからrevision hashが取り出せます。make cross
の処理はこんな感じです https://github.com/c-bata/kube-prompt/blob/4e31373f5a10443746b4ab93d584ea0ec17d4e61/Makefile#L31-L43- 最後にghrをgo getしてきてアップロードします。Githubのtokenは
GITHUB_TOKEN
環境変数を見てくれているので特にコマンド実行時には指定する必要がありません。 - ~ちなみに
actions/setup-go
は$GOPATH
が空で$GOPATH/bin
にもパスが通っていないようなので、明示的に指定しました。直したほうがいい気がしますが、JavaScriptベースのCustom actionとして定義されているらしく、まだ自分も調べてないところだったので今度必要になって調べることがあればついでに提案してみたいとおもいます。~uses
で何もDockerイメージが指定されていないstepは、runs-on
で指定したホスト上でそのまま動いているかもです。setup-go
はそのホスト上にGoの環境を構築してくれているということな気がします。ただ環境変数とかは引き継げないのかな?- https://github.com/actions/setup-go
全体像はこんな感じです。試しに v0.0.1 みたいなタグをpushして無事にバイナリ付きでGithub Releaseが作成されることは確認しました。
https://github.com/c-bata/kube-prompt/blob/master/.github/workflows/release.yml
おまけ: golangci-lintとgo testの実行
Goptunaという最近開発してるプロジェクトはまだCIを導入していなかったので、Lintとテストを実行できるようにしました。 他のプロジェクトでもほとんどそのまま使える点も多いと思うのでよければ参考にしてみてください。
name: Run tests and lint checks on: pull_request: branches: - master jobs: lint: name: Lint checking on Ubuntu runs-on: ubuntu-latest steps: - name: Set up Go 1.12 uses: actions/setup-go@v1 with: version: 1.12 id: go - name: Check out code into the Go module directory uses: actions/checkout@master - name: Running golangci-lint env: GO111MODULE: on GOPATH: /home/runner/work/ run: | go get -u github.com/golangci/golangci-lint/cmd/golangci-lint GOCILINT=${GOPATH}/bin/golangci-lint make lint test: name: Test on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] steps: - name: Set up Go 1.12 uses: actions/setup-go@v1 with: version: 1.12 id: go - name: Check out code into the Go module directory uses: actions/checkout@master - name: Running go tests run: make test
https://github.com/c-bata/goptuna/blob/master/.github/workflows/run-tests.yml
追記:
Go関係ないですが、ついでにSphinxのドキュメントをビルドしてGCSにアップロードするscript書いたのでgcloudとかgsutil使いたい方とか参考にしたい方はどうぞ。本当は https://github.com/actions/gcloud を使おうと思ったのですが柔軟性なくて今回の用途には使えなかったのでやめました。
Github Actions Workflow to build your sphinx documentation and upload it to Google Cloud Storage. https://gist.github.com/c-bata/ed5e7b7f8015502ee5092a3e77937c99
おわりに
travis CIを長く使ってきましたが、Windowsの動作が不安定だったり苦労している点が結構ありました。 ActionsはAzure Pipelinesがバックエンドにあることもあり、立ち上がりがかなり速い印象です。 forkしたらそのままActionsが使えるというメリットもあるので、OSSでは積極的に使っていこうと思います。
LLVMのauto-vectorizationとc2goasmによるGo Plan9 Assemblyの生成によるSIMD最適化
InfluxDBの開発チームはApache Arrowの技術に注目していて、ArrowのGo実装の開発にも積極的に参加しています。Stuart Carine (InfluxDBの開発チームメンバー)がApache ArrowのGo実装に取り入れたc2goasmとLLVMを使った最適化が以前話題になりました。
c2goasm はClangで生成したアセンブリをGo Plan9 Assemblyに変換できる汎用的なコマンドラインツールです。しかしその活用事例はほとんどが、Goコンパイラーが行っていないSSEやAVX命令を用いたSIMD最適化です。この記事ではAVX2によるSIMD演算を実現するための c2goasm の使い方を整理します。最終的にどれくらい速くなるかでいうとfloat64のsum演算は10x以上高速化しました。
$ go test -bench . goos: darwin goarch: amd64 pkg: github.com/c-bata/sample-c2goasm BenchmarkSumFloat64_256-4 5000000 282 ns/op BenchmarkSumFloat64_1024-4 1000000 1234 ns/op BenchmarkSumFloat64_8192-4 200000 10021 ns/op BenchmarkSumFloat64_AVX2_256-4 50000000 23.5 ns/op BenchmarkSumFloat64_AVX2_1024-4 20000000 95.9 ns/op BenchmarkSumFloat64_AVX2_8192-4 2000000 904 ns/op PASS ok github.com/c-bata/sample-c2goasm 10.911s
c2goasm
- Github: https://github.com/minio/c2goasm
- 作者minioによる解説: https://blog.minio.io/c2goasm-c-to-go-assembly-bb723d2f777f
c2goasm の使い方は次のとおりです。
ツールとしての作り込みが少し雑な印象はありハマりどころもありますが、この3つのステップを踏めばSIMDを使って最適化したC/C++のコードを少ないオーバーヘッドでGoから呼び出せます。
CgoによるCの関数呼び出し
Goにおいてデファクトスタンダードとなっている cgo は性能面において優れたソリューションではありません 1 。c2goasmで生成されたGo Plan9 Assemblyのサブルーチン呼び出しは、他のGoの関数呼び出しと同程度の効率で実行できるようです。
GithubリポジトリのREADMEにはcgoとの性能比較が紹介されています。 https://github.com/minio/c2goasm#benchmark-against-cgo
Cのコードの記述
float64のarrayに入った値の合計値を計算する関数を用意します。 SIMDによる演算で合計値の計算をするのは少しアルゴリズム的な工夫が必要です。
#include <immintrin.h> void sum_float64_avx_intrinsics(double buf[], size_t len, double *res) { __m256d acc = _mm256_set1_pd(0); for (int i = 0; i < len; i += 4) { __m256d v = _mm256_load_pd(&buf[i]); acc = _mm256_add_pd(acc, v); } acc = _mm256_hadd_pd(acc, acc); // a[0] = a[0] + a[1], a[2] = a[2] + a[3] *res = _mm256_cvtsd_f64(acc) + _mm_cvtsd_f64(_mm256_extractf128_pd(acc, 1)); }
このようにintrinsicsを使って自分でかくこともできますが、実装が少し大変です。 LLVMのAuto-Vectorization にこの辺の最適化は任せられるなら楽ができます。
void sum_float64(double buf[], int len, double *res) { double acc = 0.0; for(int i = 0; i < len; i++) { acc += buf[i]; } *res = acc; }
これをClangでコンパイルしてみます。
$ clang -S -mavx2 -masm=intel -mno-red-zone -mstackrealign -mllvm -inline-threshold=1000 -fno-asynchronous-unwind-tables -fno-exceptions -fno-rtti -c sum_avx_intrinsics.c
オプションはこの辺を参考にしてください。
実行すると ~.s
とアセンブリファイルが生成されるので中を見るとこのような感じです。
.section __TEXT,__text,regular,pure_instructions .build_version macos, 10, 14 .intel_syntax noprefix .globl _sum_float64 ## -- Begin function sum_float64 .p2align 4, 0x90 _sum_float64: ## @sum_float64 ## %bb.0: ... vxorps xmm0, xmm0, xmm0 mov qword ptr [rsp + 40], rdi mov dword ptr [rsp + 36], esi mov qword ptr [rsp + 24], rdx vmovsd qword ptr [rsp + 16], xmm0 ...
vmovsdといった命令やレジスタ xmm0 を利用していることからLLVMがSIMD命令を活用していることがわかります。サブルーチン名 _sum_float64
はGoの関数定義で必要なので覚えておきます。
※ xmm0レジスタは128 bitsしかないレジスタのため、64 bitsを消費するdoubleの演算は2要素しか一度に演算できません。おそらくSSEの命令を利用しています。ここで ymm0 など128 bitsのレジスタを使い AVX2の命令を呼び出してほしいのですが、Clangのバージョンや最適化オプションによりうまくいったりイカなかったりするようです。詳細はGithubをご覧ください。
Goの関数定義
package c2goasm_sample import "unsafe" //go:noescape func __sum_float64(buf, len, res unsafe.Pointer) func SumFloat64Avx2(a []float64) float64 { var ( p1 = unsafe.Pointer(&a[0]) p2 = unsafe.Pointer(uintptr(len(a))) res float64 ) __sum_float64(p1, p2, unsafe.Pointer(&res)) return res }
シグネチャはアセンブリ内のサブルーチン名に _
を追加したものです。今回のようにもしサブルーチン名が _
から始まっていたら、 __
で始まることに注意してください。
https://github.com/minio/c2goasm/blob/0325a40cfd1fc6a5097e69eaf0292990eb6cee6a/arguments.go#L85
c2goasm実行
$ go get -u github.com/minio/asm2plan9s $ go get -u github.com/minio/c2goasm $ go get -u github.com/klauspost/asmfmt/cmd/asmfmt $ c2goasm -a -f _lib/sum_avx_intrinsics.s sum_avx.s
これでGoアセンブリが生成されます。第2引数が出力ファイルですが、Goのファイル名を sum_avx.go
としたときは拡張子を .s
に変えただけの sum_avx.s
を指定します。
https://github.com/minio/c2goasm/blob/0325a40cfd1fc6a5097e69eaf0292990eb6cee6a/c2goasm.go#L252
ここまでが一連の流れです。
ベンチマーク
Pure Goで実装した次の関数とベンチマークにより比較します。
package c2goasm_sample func SumFloat64(a []float64) float64 { var sum float64 for i := range a { sum += a[i] } return sum }
ベンチマークのコードは次の通り。
package c2goasm_sample_test import ( "math/rand" "testing" ) func init() { rand.Seed(0) } func initializeFloat64Array(n int) []float64 { var max float64 = 1024 var min float64 = 0 x := make([]float64, n) for i := 0; i < n; i++ { x[i] = rand.Float64() * (max - min) + min } return x } func benchmarkFloat64Sum(b *testing.B, n int) { x := initializeFloat64Array(n) b.ResetTimer() for i := 0; i < b.N; i++ { SumFloat64(x) } } func benchmarkFloat64SumAvx2(b *testing.B, n int) { x := initializeFloat64Array(n) b.ResetTimer() for i := 0; i < b.N; i++ { SumFloat64Avx2(x) } } func BenchmarkSumFloat64_256(b *testing.B) { benchmarkFloat64Sum(b, 256) } func BenchmarkSumFloat64_1024(b *testing.B) { benchmarkFloat64Sum(b, 1024) } func BenchmarkSumFloat64_8192(b *testing.B) { benchmarkFloat64Sum(b, 8192) } func BenchmarkSumFloat64_AVX2_256(b *testing.B) { benchmarkFloat64SumAvx2(b, 256) } func BenchmarkSumFloat64_AVX2_1024(b *testing.B) { benchmarkFloat64SumAvx2(b, 1024) } func BenchmarkSumFloat64_AVX2_8192(b *testing.B) { benchmarkFloat64SumAvx2(b, 8192) }
実行結果
$ go test -bench . goos: darwin goarch: amd64 pkg: github.com/c-bata/sandbox-go/c2goasm BenchmarkSumFloat64_256-4 5000000 277 ns/op BenchmarkSumFloat64_1024-4 1000000 1205 ns/op BenchmarkSumFloat64_8192-4 100000 10401 ns/op BenchmarkSumFloat64_AVX2_256-4 2000000 768 ns/op BenchmarkSumFloat64_AVX2_1024-4 500000 2872 ns/op BenchmarkSumFloat64_AVX2_8192-4 100000 23946 ns/op PASS ok github.com/c-bata/sandbox-go/c2goasm 10.474s
遅くなっちゃった...
追加調査
Apache Arrowでの利用事例がある以上、なにかコンパイラーオプションとかが理由で性能が十分に引き上げられていない。気になってる点は次のあたり
- xmm0とか使ってるし、vxorpsとかvmovsdとかもAVX2じゃなくてSSEとかの命令を使っていそう
- LLVM Auto-Vectorizationのドキュメント読みつつ、Clangのコンパイルオプション見直したほうが良い。
- https://github.com/apache/arrow/blob/master/go/arrow/math/float64_avx2_amd64.s とかはちゃんとAVX2使ってることが確認できる
- LLVMの最適化に頼らずintrinsicsを使って実装した処理で性能がちゃんとあがるのかも試してみたけど、実行時にセグフォで死んだ
- https://github.com/c-bata/sample-c2goasm/blob/master/_lib/add_float32_avx2_intrinsics.s を見る限りアセンブリ生成まではおそらく問題なくて、Go側の呼び出し時のポインタの扱いに問題がある。
- Apache Arrowのパフォーマンスがちゃんとあがってるのかベンチマークとってみる。
追記: うまくいきました。
ひとまずClang 7.0.1+O2オプション+pragmaヒントでうまくいった。Clangのバージョンとか最適化オプションによって結構動かなくなってしまうみたい。細かい調査はまた今度。
— Masashi Shibata (@c_bata_) 2019年3月17日
試しにClangでアセンブリだしてみてだめなら自分でAVX2のイントリンシック使って書くとかになりそうhttps://t.co/zp22jGNEAo
Clang-7.0.1を利用する際には、次のコンパイルオプションを使用してください。
$ /usr/local/Cellar/llvm/7.0.1/bin/clang -S -O2 -mavx2 -masm=intel -mno-red-zone -mstackrealign -mllvm -inline-threshold=1000 -fno-asynchronous-unwind-tables -fno-exceptions -fno-rtti -c sum_float64.c
追記: SSEのコードはなぜ遅くなっていたのか。
SSEでも遅くはならないんじゃないかなというのが気になっていたけれど、なんとなく原因がイメージついた気がします。 InfluxDataの記事にあるとおりsumの計算を最適化してCで直接書くとAVX2のコードはこんな感じになります。
void sum_float64_avx_intrinsics(double buf[], size_t len, double *res) { __m256d acc = _mm256_set1_pd(0); for (int i = 0; i < len; i += 4) { __m256d v = _mm256_load_pd(&buf[i]); acc = _mm256_add_pd(acc, v); } acc = _mm256_hadd_pd(acc, acc); // a[0] = a[0] + a[1], a[2] = a[2] + a[3] *res = _mm256_cvtsd_f64(acc) + _mm_cvtsd_f64(_mm256_extractf128_pd(acc, 1)); }
重要なのはこの _mm256_hadd_pd
の処理で こちらの説明にあるように詰め込まれている要素の加算 を担当します。128 bitsしかないSSE用のレジスタ xmm0 とかだと8 bytes 必要な double の値を詰め込んでも2つしかのらないので、それにたいして hadd_pd みたいな処理(これはAVX2の処理ですがそれに相当する処理がSSEにもあるのかと思います) をすると全く並列化されてないことになります。
細かいところはアセンブリが使ってる命令をみればはっきりしそうです。
-
GoのポインタをCに渡せないようにポインタのバリデーション処理が走るなど、GC側の都合による性能劣化が発生するようです。詳細は Why cgo is slow @ CapitalGo 2018 - Speaker Deck をご覧ください。↩
Github Actionsでbranch作成/削除にフックしてFeature環境を構築する
最近Github Actionsを触る機会があったのですが、まだ自分のgithub accountはbetaのwait list待ちで業務で使ってるrepostioryでしか使えないので、使い方とかポイントを忘れないようにメモ。ついでにいくつか公式のactionにPR送ったり、KubernetesのIngress Rulesを編集するためのActionを公開していたりもするので、こちらも紹介します。
- Add deleted_branch and deleted_tag filters by c-bata · Pull Request #42 · actions/bin · GitHub
- Add github action for kubectl by c-bata · Pull Request #9 · actions/gcloud · GitHub
- GitHub - abema/github-actions-ingress-rules-editor: Edit ingress rules to build feature environments automatically on Github Actions.
- GitHub - c-bata/go-actions: go-actions provides the utilities for Github Actions.
Feature環境の自動作成
業務では開発中の機能を手軽にDev環境で確認するために、特定の命名規則に従ったブランチ名でGithubにpushすると、自動でdev環境のKubernetesクラスターにリリースしIngressでエンドポイントを用意して閲覧できるようにしています。これ自体はそんなに珍しくなくて検索するといくつか同じような記事が見つかります。
- ECSを使ってPR毎に確認環境を構築する社内ツールをOSSで開発してます! - Speee DEVELOPER BLOG
- GitHubへのpush時に、featureブランチ環境を自動作成する - LCL Engineers' Blog
これまでもSpeeeさんの事例のようにwebhook eventを監視してoperationを行うサーバーを用意して解決することはできました。弊社ではCircle CIなどでfeature環境を作成したりもしています。 ただSpeeeさんの事例では自分たちでサーバーを用意して運用しないといけません。また弊社がこれまでやっていたようにCIサービスでfeature環境を作成する場合にはbranchの削除にトリガーできません。社内の別のチームではbotを立ててbranchの削除を監視してたりもしたみたいですが、これだけのためにbotたてるのも少し手間になります。
GithubのあらゆるイベントにトリガーできるGithub Actionsを使えば、branchがpushされたときにfeature環境を作成し、branchが削除されたときにfeature環境を削除するといったオペレーションを、自分たちでサーバーを管理することなく実現できます。業務ではKubernetesを使っているので、全体像としては次のような感じになります。
feature-abc
のようにfeature-*
の命名規則に従ってbranchを作成しGithubにPush- Kubernetes Deploymentをbranch用に作成
- Kubernetes Serviceをfeatureブランチ用に新規で作成 1
- Ingress (ingress-gce) でエンドポイント作成 2
- Google Cloud DNSのRecordsetsの作成
https://feature-abc-webapp.foo.com
でアクセスして動作確認
Github Actions
基本的な使い方は公式ドキュメントをみてください。
https://developer.github.com/actions/
いくつか悩んだり調べた中でメモしておきたいポイントを中心に残します。
credential情報の管理
外部に漏れては困る情報は Secret によりGithub RepositoryのSettingsで指定できます (参照 https://developer.t.com/actions/creating-workflows/storing-secrets/)。 Actionsを追加する際にも「Secret」というフィールドがありますが、そこから指定してもやってることは同じです。
実は1月頃に一度Github Actionsの利用を検討したことがあったのですが、当時はまだLimited Public Beta期間中でProduction Secretsを保存してはいけませんでした。 今回はLimited Public Betaがとれたため、改めてGithub Actionsを調査することにしました。
ブランチ名のフィルター
pushイベントに対してすべてトリガーしてほしいわけではなく、特定の命名規則に従ったbranchでのみ実行してほしいものです。GITHUB_REFS
という環境変数の中に refs/head/feature-A
のような形式でブランチ名やタグ名が入っています。 refs/head/
のprefixを削除して利用すればOKです。公式で用意されている↓のactionがこの操作をしてくれているのでこちらを利用しましょう。
bin/filter at master · actions/bin · GitHub
ただbranch削除時のfilterにはこの方法が使えません。 delete
triggerは GITHUB_REF
にdefault branchつまりmasterを指定が指定されています。環境変数からbranch名を取り出すことはできません。そのかわり GITHUB_EVENT_PATH
環境変数が示す場所にWebhookのevent情報がそのままjson形式で入っています。
delete
でtriggerしたときは DeleteEvent の形式なので、 ref
フィールドよりブランチ名が取り出せます。公式で用意してほしい機能なので↓にPRをだしました。
まだマージされていないので c-bata/bin/filter@master
を指定して使っています。 deleted_branch feature-*
のようにargsを指定すれば使えます。
マージされたので公式の actions/bin/filter@master
を使用してください。そちらには deleted_tag
フィルターも追加しています。
GCPのService Accountからgcloudの認証を行う
公式で用意されている↓のactionを用いることで実現できます。Service AccountはSecret GCLOUD_AUTH
にbase64 encodeしたservice accountのjsonファイルを与えればOKです (ex: base64 ./service-account.json
)。
gcloud/auth at master · actions/gcloud · GitHub
少し驚いたのですがgcloudコマンドの実行は別のactionとして定義し、↓を利用して実行します。
gcloud/cli at master · actions/gcloud · GitHub
gcloudコマンドのcredential情報は、Homeディレクトリ以下に作成されます。Github Actionsは裏側で /github/workspace
を常にマウントしそこをHomeディレクトリに設定しているようです。このディレクトリは次のactionでもそのままの状態で引き継がれます。gcloudの認証とgcloudコマンドの実行は別のactionでやるのがGithub Actionらしいやり方なようです。
kubectlの実行
gcloud authができるようになったので、kubernetes clusterのcredentials情報を取得してkubectlを実行します。既存でよさそうなものがなかったのですが、https://github.com/actions/gcloud で管理されるのがみんな幸せかと思うので PRを出しました。
Add github action for kubectl by c-bata · Pull Request #9 · actions/gcloud · GitHub
gcloudコマンドにならって、PROJECT_IDやZONE、K8S clusterをセットするactionとkubectlの実行用actionを分割しました。まだマージはされていないので c-bata/gcloud/kubectl-config@master
および c-bata/gcloud/kubectl@master
を指定して使っています。
ingress rules書き換えツールの実行
deploymentsやserviceをfeature環境ごとに個別に作っていたようにingressもfeature環境ごとにつくることもできるのですが、大きいチームだったので大量にFeature環境が立ち上がりLoadbalancerの作成上限に引っかかったことがありました。そのため全てのfeature環境で1つのingressを使いまわし、Spec.Rulesに振り分け設定を追加して webapp.foo.com
や feature-a-webapp.foo.com
を振り分けています。管理の都合上もその方がいいかなと思います。IngressのSpec.Rulesの編集にはもともとnodeで書かれたscriptが社内で使われていたのですが、kubectlのwrapperになっていてclient-goが使えるGoで書いたほうが色々楽だったので今回書き直しました。↓で公開しています。
c-bata/go-actions
github actionsの調査もかねてutilityライブラリ作りました。 正直使うほどでもないシーンが多いと思いますが、よければ使ってみてください。
GitHub - c-bata/go-actions: go-actions provides the utilities for Github Actions.
面倒だったこと
branchを削除したときにはmasterブランチのmain.workflowが参照され、実行されます。そのためbranchの削除にtriggerして何らかの処理を行いたいとき、一度そのbranchをmasterにマージして削除しないと動作確認ができません。
Add deleted_branch and deleted_tag filters by c-bata · Pull Request #42 · actions/bin · GitHub みたいな機能はとりあえず書いてmasterにマージしてbranchを削除して、問題があればまたbranchを作ってmasterにマージしてbranchを削除しないと確認できずmasterのcommit logが結構汚れます。仕事のrepositoryでそれをやることになったので申し訳ないなと思いながら開発してました。
おわりに
はやく自分のrepositoryでも使ってみたい
-
執筆時点では ingress-gce がClusterIPへのヒモ付に対応してないのでServiceTypeはNodePortを使用しています。NodePortの番号は特に指定していないのでKubernetes側にrandomに割り振ってもらっています。↩
-
次の手順でGoogle Cloud DNS Recordsetsを作りますが、もしそちらをterraformで管理して消し忘れとかをなくしたいのであれば、Google compute address(静的IPアドレス)の払い出しもTerraformで行って、Ingress側の
metadata.annotations.kubernetes.io/ingress.global-static-ip-name
で指定して使うのが管理の都合上いいかと思います。↩