中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

1時間超えのCIを10分以下にした話

経緯

app.wercker.com

今までwercker を使っていたのだが、数年前は良いサービスだったのにOracleなんかに買われたせいで最近はインスタンスが目に見えて遅かったり、不安定だったりやってられないので、引っ越しを検討していた。

比較したサービス

wercker

最近明らかに遅いインスタンスが混ざり込む様になった

(超相乗り状態?)

  • 並列実行数=2
  • 1vCPU 2GB RAM
無料プランのみ??

情報が出てこない・・・

CircleCI

circleci.com

言わずと知れた最大手。CI業界の基準

ピュアなコンテナ環境ではなさそう(chrootっぽい何か?)

その影響か、ホストのカーネルの問題か解らないが、稀にCircleCI無いでしか起きないSEGV等が起きたり互換性周りの面倒事が起きる。

実行環境のリソースは細かく選択でき、料金が異なる。

料金はクレジットの単位で設定されており

25000クレジット$15で購入するので

$15 / 25000credit = $0.0006 / credit

resource class
Class vCPUs RAM credit
small 1 2GB 5 / 分
medium (default) 2 4GB 10 / 分
medium+ 3 6GB 15 / 分
large 4 8GB 20 / 分
xlarge 8 16GB 40 / 分
2xlarge(2) 16 32GB 80 / 分
2xlarge+(2) 20 40GB 100 / 分

なぜか、料金表が見当たらず、、赤字は予想。

Free Plan
  • 無料枠 2500 クレジット / week
  • 並列実行数=1
  • 3ユーザまで
  • Mediumのみ

よって無料枠は

 250分 / 週 = 4.2時間 / 週

  • パブリックリポジトリの場合 400000クレジット/月を付与
Performance Plan

クレジットの使用 - CircleCI

  • 並列実行数=80
  • 3ユーザ($15)+ ユーザ毎($15)
  • Docker レイヤー キャッシュへのアクセス(実行毎200クレジット)
  • 1口辺り25000クレジット($15)x 毎月最低2口 = $30〜

仮にsmallを100時間稼働させた場合(他にも、dockerイメージのキャッシュで実行毎200クレジット掛かったりするが、イメージDLと実行時間の相殺の問題なので一旦無視。)

 (ユーザ料金)$15 + 6000分 * 5credit * $0.0006 = $33(支払いは$45)

となる。

スタートアップなら此位で収まるプロダクトも多いだろう。

エンジニアが少ない場合でも、多い場合でも、ユーザ毎$15が重くなりがちな料金体系(少数エンジニアで生産性が高ければお得)

セルフホスティング

どうやら自前のホスティング環境で実行することも出来るようだが、わざわざCircleCIを選ぶ理由が思いつかないので、調べていない。

Github actions

github.co.jp

今まで仲良くしていたCIサードパーティに手の平を返して牙を向いたGithub様の刺客!

バックエンドはMicrosoft Azure。

明らかにインフラリソースの優遇を受けているであろうダンピング価格でCI環境を提供している。

パブリックリポジトリは全部無料

プライベートリポジトリでも巨大な無料枠が用意されている。

rootのHOMEが"/home/github" になっておりイメージの環境調整が面倒

プライベートリポジトリの場合は、JOBの実行時間とストレージ使用量がそれぞれ無料枠+従量の料金体系になっている。CIとして使う場合はストレージ使用量は気にならず、JOB実行時間だけ考えれば良い。

ジョブ実行時間の従量部分は 0.008$ / 分

Free
  • 無料枠2000分 / 月、ストレージ500MB
  • 並列実行数=20
Pro(月額$4)
  • 無料枠3000分 / 月、ストレージ1GB
  • 並列実行数=20

仮に100時間稼働させた場合(細かいことを言えば、どのCIを使おうが掛かる月額4$は除外しても良いが・・・)

 $4 + 3000分 * $0 + 3000分 * $0.008 = $28

一見、CircleCIより少し安い位だけど、プルリが少なくて50時間程度だった場合はほぼ無料になってしまう。

セルフホストランナー

サーバを自前で用意する。無料

CIが走った時にspot instanceでランナーを起動したいが、ランナーをGithubに接続する際に必要なワンタイムトークンを生成するAPIが提供されていないので困難。

github.community

実際の引っ越しの成果

元々の状態

f:id:hiroppon:20210611022858p:plain:w600

71分、これは早いインスタンスを引いた時で、ハズレを引くと3倍遅いしハズレ率が5割以上。

安定しないCIなんぞ糞である

Github actionsに引っ越し

f:id:hiroppon:20210611023513p:plain:w600

65分、あまり変わらないように見えるがハズレが無く安定したのでかなりのストレス軽減

だが、、もっと早くなるのが解っている!

並列化

.github/workflows/main.yml
name: CI
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
  workflow_dispatch:
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      max-parallel: 12
      matrix:
        test_script:
          - run_sherpajs.sh
          - run_sherpa_server.sh
          - run_sherpa_admin_advertisement_br.sh
          - run_sherpa_admin_advertisement_br_multi.sh
          - run_sherpa_admin_advertisement_br_movie.sh
          - run_sherpa_admin_advertisement_sn.sh
          - run_sherpa_admin_advertisement_internal.sh
          - run_sherpa_admin_action_widget.sh
          - run_sherpa_admin_campaigns_sherpa.sh
          - run_sherpa_admin_campaigns_company_user.sh
          - run_sherpa_admin_others.sh
          - run_sherpa_admin_report.sh
    container:
      image: crumbjp/sherpa_ci
      credentials:
         username: crumbjp
         password: ${{ secrets.DOCKERHUB_PASSWORD }}
    steps:
      - uses: actions/checkout@v2

      - name: prepare
        run: bash ci/prepare.sh

      - name: services
        run: bash ci/services.sh

      - name: run ${{ matrix.test_script }}
        run: bash ci/run_parallel.sh ${{ matrix.test_script }}

マトリックスを使うと並列テストが作れる。

f:id:hiroppon:20210611023953p:plain:w600

12分!これが無料だから恐ろしい!!

しかし12並列ともなると10分のテストで120分ものジョブ時間を消費するので、我々の開発状況では3000分の無料枠は数日で使い切ってしまった。このペースだと従量課金で$300/月程度になりそうなのでセルフホストランナーも検討。

t4g.medium (vCPUx2 4GB RAM) x 720時間 = $31.1

2ランナー/サーバー x 6 = 12ランナー $186.6

コスト的には検討に乗りそうだ。

ARMのdocker imageは色々苦労があるがそれはまた別の機会に・・・

f:id:hiroppon:20210611023948p:plain:w600

中を見るとInitialize containers(docker イメージDL)に結構時間を掛けている。

Githubが用意しているランナーに我々のdockerイメージがキャッシュされている可能性はほぼ無い。

セルフホストランナーであればここが削れるはずだ。

セルフホストランナー化

f:id:hiroppon:20210611113343p:plain:w600

10分切った!

我々のプロダクトでは固定のStaging環境を維持しており、これをセルフホストランナーに流用して元々余らせていたリソースを活用。

本番サーバーでもリザーブインスタンスの期間の関係でリソースを余らせているサーバーを幾つか見繕って流用。 (一時的な処置)

結果、実質の出費無しで12本のランナーを確保することができた。

f:id:hiroppon:20210611115512p:plain:w600

目論見通りInitialize containersは数秒に収まっている。

またランナーにジョブが飛んでくるまでに30秒〜2分位のラグがあるようだ。

ローカルマシンより早い

今までローカルテストで確認してからプルリを作っていたが、もうカジュアルにプルリ作ってCIでテスト通す方が楽。 しかも料金は気にしなくて良い。

開発スタイルがガラっと変わりました。

もう腿を火傷する必要は無い!!

まとめ

1. CI環境は多くの場合、github actions が良い選択

2. ヘビーユースならばセルフホストランナーの方が良い

3. 並列テスト最高