Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby on CI #ginzarails

sue445
August 28, 2020

Ruby on CI #ginzarails

銀座Rails#24の発表資料です
https://ginza-rails.connpass.com/event/181807/

sue445

August 28, 2020
Tweet

More Decks by sue445

Other Decks in Technology

Transcript

  1. 自己紹介 • Go Sueyoshi (a.k.a. sue445) • ピクシブ インフラ部所属 ◦ AWS,

    GCP, CI ◦ アプリケーションコードを一切触らずにパフォーマンスチューニングす るリアルISUCON ◦ その他色々
  2. 自己紹介 • 自称YAMLエンジニア ◦ Ansible, docker-compose, CircleCI, GitLab CI, Travis

    CI Wercker, GitHub Actions, Serverless Framework, AWS SAM, Kubernetes • 自称CIマニア ◦ Jenkins(昔), Travis CI, CircleCI, Wercker, GitLab CI, GitHub Actions
  3. 自己紹介 • RubyKaigi 2019 Speaker ◦ Best practices in web

    API client development ◦ https://rubykaigi.org/2019/presentations/sue445.html • RubyKaigi 2020 Speaker ◦ Ruby on CI ◦ https://rubykaigi.org/2020/presentations/sue445.html ◦ しかし中止(◞‸◟)
  4. アジェンダ • なぜCIをするのか • CIの歴史 • 何をCI/CDするのか • いつCIを導入すべきか •

    いつ(どのタイミングで、どの周期で)CIすべきか • 何を使うべきか • スロービルド改善 • CIのキャッシュ戦略 • これからのCI
  5. まとめ • 何をしたいかや規模によって使うもの(SaaS、セルフホスティング含めて) が変わる ◦ 銀の弾丸はない • CIの仕組みやエコシステムにのっかることによっていくらでもリファクタリン グできる •

    手作業を自動化することによって生産性を何倍にもできる • 技術革新によってその時点の最適解がすぐに陳腐化する ◦ 作った当時の最適解が今の最適解とは限らない ◦ 必要に応じて式年遷宮も必要
  6. アジェンダ • なぜCIをするのか • CIの歴史 • 何をCI/CDするのか • いつCIを導入すべきか •

    いつ(どのタイミングで、どの周期で)CIすべきか • 何を使うべきか • スロービルド改善 • CIのキャッシュ戦略 • これからのCI
  7. (sue445は)なぜCIするか? • 人は間違えるが機械は間違えない ◦ 自分自身も信用しない ◦ Q. 機械も間違えるのでは? ▪ A.

    機械が間違えてるのではなく、機械を作った人間や機械に支 持を出した(コードを書いた)人間が間違えてる
  8. アジェンダ • なぜCIをするのか • CIの歴史 • 何をCI/CDするのか • いつCIを導入すべきか •

    いつ(どのタイミングで、どの周期で)CIすべきか • 何を使うべきか • スロービルド改善 • CIのキャッシュ戦略 • これからのCI
  9. 自前で頑張る期 • 2005年: Hudson • 2011年: Jenkins • この時代は自分でCIサーバを立てるのが主流 •

    自分でCIサーバの面倒を見る必要があって大変 ◦ 仕事を減らすために導入したはずなのに仕事が増えるジレンマ ◦ メンテナが属人化する問題(Jenkinsおじさん)
  10. SaaS登場 • 2011年: Travis CI • 2011年: CircleCI 1.0 •

    2011年: Wercker (box (non Docker)) • 2016年: CloudBees (SasS版Jenkins) • CIサービスが用意した環境を利用 ◦ 自分でサーバを管理しなくてよくなった反面、CIサービス毎のハック がつらい ◦ ローカルだと問題ないのにCIがなぜかコケる ◦ CI毎に互換性がないのでポータビリティが微妙
  11. Dockerの台頭 • 2014: Docker Hub • 2014: Wercker (Docker support)

    • 2017: CircleCI 2.0 (Docker support) • CIの実行環境がポータビリティになった ◦ ベースのDockerイメージが一緒ならどこで動かしても同じ環境なの が保証される
  12. アジェンダ • なぜCIをするのか • CIの歴史 • 何をCI/CDするのか • いつCIを導入すべきか •

    いつ(どのタイミングで、どの周期で)CIすべきか • 何を使うべきか • スロービルド改善 • CIのキャッシュ戦略 • これからのCI
  13. 普通のRuby製アプリのCI • test:rspecなど • lint : rubocop, slimlintなどのような静的解析 • deploy

    ◦ アプリ:capisrtanoなど ▪ デプロイを自動にするか手動にするかはアプリによって変わる (本番環境のみCI上から手動デプロイとか) ◦ ドキュメント:YARD, Storybookなど
  14. gemのCI • deployがない以外はアプリと構成は一緒 • RubyのバージョンやRailsのバージョン毎に分けたgemfileによってマトリ クスビルド ◦ マトリクステストのジョブ数を増やしすぎるとジョブの実行時間が長く なるのでほどほどに ◦

    今までの経験上、Rubyのバージョンはサポートしてる下限(最古)と 上限(最新)でいいことが多い ◦ Railsはマイナーバージョン毎に細かい挙動が変わるので全部テスト しないときつい
  15. アジェンダ • なぜCIをするのか • CIの歴史 • 何をCI/CDするのか • いつCIを導入すべきか •

    いつ(どのタイミングで、どの周期で)CIすべきか • 何を使うべきか • スロービルド改善 • CIのキャッシュ戦略 • これからのCI
  16. アジェンダ • なぜCIをするのか • CIの歴史 • 何をCI/CDするのか • いつCIを導入すべきか •

    いつ(どのタイミングで、どの周期で)CIすべきか • 何を使うべきか • スロービルド改善 • CIのキャッシュ戦略 • これからのCI
  17. gemの場合のCI実行周期 • 基本的にはアプリと同様(push, PRの単位) • 定期ビルドが重要 ◦ 自分のコードに変更がなくても依存してるgemの破壊的変更により masterブランチのビルドはすぐにぶっ壊れる ▪

    例)古いRubyのサポート切れ (required_ruby_version) ◦ 最低週1回は定期ビルドすべき ▪ 他の人のリポジトリにPR投げた時に自分の変更以外でビルドが コケるのがすごい嫌なので、自分のgemは全部weekly buildを してる
  18. アジェンダ • なぜCIをするのか • CIの歴史 • 何をCI/CDするのか • いつCIを導入すべきか •

    いつ(どのタイミングで、どの周期で)CIすべきか • 何を使うべきか • スロービルド改善 • CIのキャッシュ戦略 • これからのCI
  19. CIは何を使うべきか • 何を使うべきか x 3 ◦ CIサービス ◦ Dockerイメージ ◦

    実行環境 • だいたいみんなGitHub使っていると思うのでGitHub前提 • 自分が使ったことあるやつ中心
  20. gemでTravis CIやGitHub ActionsやCircleCI を選択する理由 • 「各Rubyのバージョン x 各Railsのバージョン」のようなマトリクスビルドが 書きやすい ◦

    https://docs.travis-ci.com/user/build-matrix/ • 最近CircleCIもマトリクスビルドを正式にサポートした ◦ https://circleci.com/blog/circleci-matrix-jobs/
  21. Travis CIのマトリクスビルド • メリット ◦ GitHub Actionsに比べると少ない記述量で書ける ▪ シンプルなgemのCIであればTravis CIで十分なことも多い

    • デメリット ◦ 5並列/ownerなので大量にgemをメンテして一気にPRマージすると ビルドが詰まる ◦ 30個のgemのweekly build 1.5時間
  22. GitHub Actionsのマトリクスビルド • メリット ◦ 20並列/repo ◦ ownerごとの上限はプランによって変わる ▪ https://help.github.com/en/actions/getting-started-wi

    th-github-actions/about-github-actions#usage-limits ◦ 30個のgemのweekly build 15分 • デメリット ◦ Travis CIに比べたら記述が冗長
  23. GitHub APIを使う場合なぜGitHub Actionsを 使った方がいいのか? • CI用のパーソナルアクセストークンの発行が不要なため ◦ https://help.github.com/ja/actions/configuring-and-manag ing-workflows/authenticating-with-the-github_token ◦

    secrets.GITHUB_TOKEN で使える • インストールアクセストークン ◦ hubコマンドで試したけど自分自身のプライベートリポジトリとパブリッ クリポジトリは見れるけど、他のプライベートリポジトリは見えなかった • 有効期限60分なので仮に漏れても被害は最小限
  24. 余談1: GitLabの場合 • GitLabではGitLab CIを使うのがデファクト ◦ 機能的にもCircleCIと同等なので遜色ない ◦ リポジトリとCIが統合しているのでメリットも多い ▪

    例)セットアップ不要、ビルドが終わったらMergeRequestを自動 マージ • ピクシブでのGitLab CI事例 ◦ https://speakerdeck.com/sue445/pixiv-tech-salon-numbe r-pixivtechsalon ◦ https://sue445.hatenablog.com/entry/2019/12/18/000108
  25. 余談: Dockerイメージソムリエが気をつけてること • Docker Hubではたくさんのイメージが公開されているが、頻繁に更新さ れていることが一番大事 ◦ Rubyの場合最低でも最新版が出たらすぐにパッチバージョン単位 (x.y.z)でtagがうたれてリリースされていないと厳しい ◦

    gemのCIを考えるとマイナーバージョン単位(2.7とか)でtagが打たれ ていると嬉しい • 個人が手動でリリースしてるようなDockerイメージを使うのはメンテされ なくなるリスクがあるので使いづらい
  26. https://hub.docker.com/r/circleci/ruby • _/ruby ベース • テストに必要なものが全部入りで便利(そこそこ重い) • nodeタグやbrowserタグもあるので必要に応じて使う ◦ https://circleci.com/docs/2.0/circleci-images/

    ◦ headless chromeのインストールに困ったらコレ • circleciと言いつつ他のCIでも普通に使える • 普通のdockerイメージと違って実行時のユーザがrootユーザじゃなく circleciユーザなのが注意(sudoはある)
  27. 余談: cimg/ruby • 最近CircleCIが新しいDockerイメージを出したらしい ◦ https://hub.docker.com/r/cimg/ruby ◦ circleci/rubyよりイメージが軽量化 ▪ circleci/ruby:2.7.1

    => 476.71 MB ▪ cimg/ruby:2.7.1 => 332.04 MB • 日本語の詳しいブログ ◦ https://budougumi0617.github.io/2020/06/08/circleci_cim g_go_2020/
  28. CIの実行環境は何を使うか • CircleCI ◦ 前述のDockerイメージのどれか • GitHub Actions ◦ GitHub

    ActionsのVM上でビルドを実行 ◦ GitHub ActionsのVM上でコンテナを起動しその中でビルドを実行
  29. GitHub ActionsのVM上でビルドを実行 • 基本的には ruby/setup-ruby でいい ◦ https://github.com/ruby/setup-ruby ◦ ruby

    orgなので安心感がある(eregonさんがメンテナ) • GitHub公式の actions/setup-ruby は最新版への追従が遅いので実 アプリで使うのは厳しい ◦ https://github.com/actions/setup-ruby • 詳しいこと ◦ https://mstshiwasaki.hatenablog.com/entry/2020/02/08/1 30844
  30. アジェンダ • なぜCIをするのか • CIの歴史 • 何をCI/CDするのか • いつCIを導入すべきか •

    いつ(どのタイミングで、どの周期で)CIすべきか • 何を使うべきか • スロービルド改善 • CIのキャッシュ戦略 • これからのCI
  31. スロービルド対策の方針 • ジョブ起動時のdockerイメージのpullが重いパターン ◦ 700MB超えるイメージを使うとこの問題に引っかかる ◦ たいていの場合Docker Hubで配布されてるイメージを使ってる場合 が多いので、alpine版やslim版があればそれを使う ▪

    パッケージが足りずに大量のパッケージをapt-get install(yum install)するコストも出てくるのでケースバイケース ◦ CIサービスによっては1ジョブで複数コンテナを起動できることが多い が減らすのも大事。(例:rubocop実行時はDBやredisのコンテナを 起動しないなど)
  32. スロービルド対策の方針 • gemなどのライブラリインストール ◦ CI側で適切にキャッシュしてやる(後述) • パッケージインストールやmake ◦ makeしたバイナリをCIのキャッシュに乗せるのも有り ◦

    インストールそのものが遅い場合はCI専用のDockerイメージを作っ た方が速いのだが、オレオレDockerイメージというメンテすべきもの が増えるので一長一短
  33. アジェンダ • なぜCIをするのか • CIの歴史 • 何をCI/CDするのか • いつCIを導入すべきか •

    いつ(どのタイミングで、どの周期で)CIすべきか • 何を使うべきか • スロービルド改善 • CIのキャッシュ戦略 • これからのCI
  34. 参考: index_shotgunの式年遷宮 • https://github.com/sue445/index_shotgun • Travis CI期 ◦ sqlite3, MySQL,

    PostgreSQLだけ • Travis CI + Wercker併用期 ◦ Travis CIでOracleインストールするだけで30分くらいかかってつら い ◦ この当時Dockerに対応してたのがWerckerのみだったのでOracle のCIするためにWercker利用
  35. 参考: index_shotgunの式年遷宮 • Travis CI + CircleCI 2.0併用期 ◦ Werckerも悪くはなかったが設定がymlで完結しないつらみ

    ▪ ワークフローの設定は画面で設定する必要がある ◦ CiecleCI 2.0が出たあたりで移行
  36. 参考: index_shotgunの式年遷宮 • GitHub Actions ◦ .circleci/config.yml 修正しただけでTravis CIのクソデカビルドが 走るのがつらい

    ◦ Travis CI並列数が全然足りない ▪ Travis CIだと5並列で50ビルド計1時間。ピーク期は70ビルド計 2時間 ◦ GitHub Actions発表直後に移行 ▪ 20並列で70ビルド20分
  37. まとめ • 何をしたいかや規模によって使うもの(SaaS、セルフホスティング含めて) が変わる ◦ 銀の弾丸はない • CIの仕組みやエコシステムにのっかることによっていくらでもリファクタリン グできる •

    手作業を自動化することによって生産性を何倍にもできる • 技術革新によってその時点の最適解がすぐに陳腐化する ◦ 作った当時の最適解が今の最適解とは限らない ◦ 必要に応じて式年遷宮も必要
  38. 複数リポジトリのCI設定の共通化(CI as a CI) • アプリケーションコードと同様CIの設定も継続的にメンテしていく ◦ みんなリファクタリング大好きだよね? • 複数リポジトリメンテしてるとリポジトリによって設定が違うと大変なので極

    力統一したい • 同一yamlを複数リポジトリでコピペして使い回すのが理想だがgemに よってサポートしてるrubyのバージョンが変わったりするので厳しい ◦ とはいえ8割方一緒なので共通化できる
  39. Usage (Generate a migration file) $ ./bin/generate --template=github_actions some_migration Write

    to /path/to/ci-config-itamae/cookbooks/migrate/github_actions/202 00311232827_some_migration.rb
  40. Usage (Edit a migration) file "#{node[:repo]}/.github/workflows/test.yml" do action :edit block

    do |content| next if content.include?("centos:8") content.gsub!(<<-YAML, <<-YAML) - centos:7 YAML - centos:7 - centos:8 YAML end only_if "ls #{node[:repo]}/.github/workflows/test.yml" end
  41. Usage (Apply to a repo) # dry run結果が問題なければ--dry-runを外して実行 $ ./bin/migrate

    --recipe=cookbooks/migrate/github_actions/20200311232827_som e_migration.rb -m "This is commit message" --repo=github.com/sue445/rubicure --dry-run
  42. Usage (Apply to multiple repos) # dry run結果が問題なければ--dry-runを外して実行 $ ./bin/migrate

    --recipe=cookbooks/migrate/github_actions/20200311232827_som e_migration.rb -m "This is commit message" --include=gem --dry-run
  43. 挙動 • ローカルの複数リポジトリに対して下記を実行 ◦ ghq get ◦ git pull ◦

    git checkout -b 【適当なブランチ名】 ◦ Itamaeでmigrationを適用 ◦ 差分があったらコミットしてPR投げる • 30個のリポジトリにPR投げるまで10分 ◦ これ作る前は手作業で半日がかり • PR投げられたらあとはビルドが終わるまで待つだけ