👾

GitHub CI/CD実践ガイド を読んでみた

2024/07/17に公開

はじめに

現在業務で GitHub Actions を使って CI/CD を組んでいますが、雰囲気で使っている感が否めないのできちんと GitHub の CI/CD に入門すべく本を読んでみました。

https://gihyo.jp/book/2024/978-4-297-14173-8

学習の習慣化を行っているので毎日少しずつ読んでいくスタイルで進めました。
また、実行できるコードは極力実際に動かしてみます。

目次

[基礎編]

  • 第1章 ソフトウェア開発とGitHub
  • 第2章 GitHub Actionsの基礎概念
  • 第3章 ワークフロー構文の基礎
  • 第4章 継続的インテグレーションの実践
  • 第5章 運用しやすいワークフローの設計
  • 第6章 アクションによるモジュール化

[実践編]

  • 第7章 クリーンなリポジトリの維持
  • 第8章 Dependabotによる依存関係バージョンアップ
  • 第9章 GitHub Releasesによるリリース自動化
  • 第10章 GitHub Packagesによるパッケージ管理
  • 第11章 OpenID Connectによるセキュアなクラウド連携
  • 第12章 コンテナオーケストレーションのデプロイメント
  • 第13章 アクションのオープンソース化

[応用編]

  • 第14章 GitHub Actionsの高度な使い方
  • 第15章 GitHub Actionsのセキュリティ
  • 第16章 セキュリティのシフトレフト
  • 第17章 GitHub Appsトークンによるクロスリポジトリアクセス
  • 第18章 継続的デリバリーの実践

読書メモ

以下章ごとのメモを記載していきます。
詳しくは本を読んでください!

第1章 ソフトウェア開発とGitHub 6/24読了

  • gh コマンド入れた

第2章 GitHub Actionsの基礎概念 6/25、26読了

  • 終了ステータスを正しく扱うことが大切
  • choice 型とか使ったこと無かった
  • GitHub-Hosted Runners にインストールされているソフトウェアの詳細
  • アクションの探し方 GitHub Marketplace

第3章 ワークフロー構文の基礎 6/26、27読了

  • コンテキストの種類
  • 環境変数の扱い
  • コンテキストを直接シェルコマンドに渡すのはアンチパターン:中間環境変数を使う
    • コンテキストはシェルコマンドへハードコードせず、環境変数を経由して渡す
    • 環境変数はすべてダブルクォーテーションで囲む
  • 複数のワークフローで値を共有する Variables
    • 中間環境変数を経由すること
  • 機密情報は Secrets で扱う
    • Secrets はログ出力しない
    • リテラル
    • 演算子
      • 異なる型の比較は避ける
    • オブジェクトフィルター
      • コンテキスト参照の特殊記法
      • 配列やオブジェクトから指定したプロパティを抜き出して配列を生成する
  • 関数
    • 文字列比較
    • 文字列生成
    • JSON 操作
    • ハッシュ生成
  • 条件分岐
    • ステータスチェック関数
      • success()
      • failure()
      • cancelled()
      • always()
    • ワークフロー実行のスキップ
  • ステップ間のデータ共有
    • GITHUB_OUTPUT 環境変数
    • GITHUB_ENV 環境変数 ・・・ 事実上のグローバル変数なので不用意な利用は控える
  • GitHub API
    • GITHUB_TOKEN シークレット
    • パーミッションはジョブレベルとワークフローレベルで定義可能
    • パーミッションを明示的に定義していない場合はソースコードの読み込みは暗黙的に許可されている
  • スターターワークフロー
    • GitHub が提供している、リファレンス実装のコレクション

第4章 継続的インテグレーションの実践 6/28、6/30 読了

  • go コードを使った CI 時の自動テストのサンプル解説
  • 通常のフィルターと ignore フィルターは同時に使えない
  • paths 系とそれ以外のフィルターを同時使用すると AND 条件になる
  • Glob で一括指定する方法
  • アクティビティタイプ:イベントの種類に応じた制御が可能
    • プルリクエストイベントだけアクティビティ省略時の挙動が異なる
      • types: [opened, synchronize, reopened] と同等
  • セットアップアクション:使用言語のバージョン指定
    • バージョンファイルでの指定もできる
  • 静的解析
    • actionlint:GitHub Actions の静的解析ができる
    • タイムアウトとデフォルトシェルはすべてのワークフローに導入する価値がある
  • タイムアウトは分単位で指定できる(時間課金なので大事)
    • ジョブレベル、ステップレベルで定義可能
    • GitHub Actions のデフォルトタイムアウトは 360分とながい!
    • すべてのワークフローへタイムアウトを設定すべき
  • シェル 起動シェルをステップごとに変更できる デフォルトは Bash
    • シェル指定は明示的にすべき
      • pipefail オプションの有効化が定石 bash --noprofile --norc -eo pipefail {0}
    • デフォルトシェルで一括で指定
  • Concurrency
    • Concurrency グループを定義して多重起動を抑制できる(大文字、小文字を区別しないので注意)
      • 複数起動しなくなり、2つ目移行のワークフローは実行待機になる
    • 古いワークフローの自動キャンセル cancel-in-progress: true
  • 継続的インテグレーションの黄金率
    • ソフトウェア開発の時速可能性を高め、長期に渡る価値提供を実現する
      • クリーンに保つ
      • 高速に実行する
      • ノイズを減らす
  • 自動テストの運用プラクティス
    • ユニットテストを中心にする
    • たまに落ちるテストをリトライでごまかさない
      • フレーキーテストは虫歯と一緒で自然には減らない!
    • リファクタリングで壊れるテストを根絶する
      • フラジャイルテストを減らす
    • スローテストの実行タイミングをずらす
      • どこで妥協するか?
    • テスト実行時の挙動を制御する
      • 部分実行
      • 並列実行
      • シャッフル実行
      • カテゴリ実行
    • チームでテスト設計を学ぶ
  • 静的解析の運用プラクティス
    • 不要な警告は無視せず抑止する
      • 抑止理由を書き残す!
    • 新規の警告を増やさない
      時にはわりきりも必要
    • 警告理由を理解する

第5章 運用しやすいワークフローの設計 7/1 読了

  • ログ出力
    • ステップデバッグログ
    • ランナー診断ログ
    • ワークフローコマンドで自身でもデバッグログを出力可能
  • Bash トレーシングオプション
    • グループ化も可能
    • 手動マスクも可能
  • アノテーション、ジョブサマリーで情報を出力できる。
    • Slack 通知もできる
  • ジョブはデフォルトでは並列実行、逐次実行したい場合は needs で依存関係を定義
    • ジョブ間でのデータの受け渡しもできる
  • マトリックス:単一のジョブ定義で、複数ジョブを実行できる
  • Environments:環境ごとに Variables や Secrets を管理できる
  • キャッシュアクションでキャッシュの制御が可能
    • キャッシュキーの設計が重要
  • アーティファクトを一時的に GitHub が提供するストレージに保存できる
    • ブラウザ、ワークフロー内でダウンロードできる

第6章 アクションによるモジュール化 7/2 読了

  • アクションの実装方法
    • Composite Action -> 一番簡単
    • Javascript Action
    • Docker Container Action
  • リモートアクションは独立したリポジトリへ配置
    • 可視性はリポジトリの可視性と連動
  • ローカルアクションはワークフローと同じリポジトリへ配置
  • アクションにはメタデータファイルが必要
    • 実装にはメタデータ構文を使い、メインロジックや入出力インターフェイスを記述する
  • メタデータ構文はワークフロー構文と似ているが、shell キーが省略できない
    • Variables や Secrets へもアクセスできない
  • GITHUB_TOKEN は github.token プロパティを使う
  • アクションでは環境変数への依存を避け、ログはグループ化する

第7章 クリーンなリポジトリの維持 7/3 読了

ここから実践編

  • コードレビューについて
  • ブランチプロテクションについての説明
  • コードオーナーについて
  • ソースコードに平文でクレデンシャルを書かない
  • シークレットスキャンを使うとクレデンシャルの混入を検知できる
    • プッシュプロテクションもある
  • ドキュメントを書く

第8章 Dependabotによる依存関係バージョンアップ 7/4 読了

Dependabot の章。
実際に導入してますが Terraform くらいしか使ってなかったので勉強になりました。

  • 何もしないとソフトウェアは壊れる
  • 依存関係のバージョンアップに Dependabot は有効
  • 最新バージョンへ自動アップデートさせるには Dependabot version updates
  • 言語のパッケージエコシステムやスケジュールも設定できて、特定条件の除外も可能
  • Dependabot のプルリクエストをトリガーにしてワークフローを動かせる
  • Dependabot が起動するワークフローでは Dependabot secrets を別途登録する(同じ名前にしたほうがいい)
  • バージョンアップ時の影響範囲の確認は大事
  • パッチバージョンだけ自動マージするや、開発環境は自動マージにするなども検討する

第9章 GitHub Releasesによるリリース自動化 7/5 読了

  • ソフトウェアによってリリース方法は異なるが、ビルド、バージョニング、アナウンスなどは共通
    • ここはリポジトリが分かれてたりしてうまくやれてないところがある。。
  • セマンティックバージョニングが一番有名
    • 信頼性は高くないけどわかりやすい
  • GitHub Releases はリリースノートの自動生成も可能
  • GitHub Actions から GitHub Releases を操作して自動化も可能
  • Git タグにルールをセットすると変更、削除から保護できる

第10章 GitHub Packagesによるパッケージ管理 7/6 読了

  • GitHub Packages はマネージドなパッケージレジストリで、複数の言語パッケージとコンテナイメージが扱える
  • パッケージとリポジトリは論理的に別リソース
    • リンクしてパーミッション設定が継承できる。自動リンクが便利
  • 可視性のデフォルトはプライベート(一度パブリックにしたらプライベートに戻せない)

サポートしているパッケージレジストリ

パッケージレジストリ 言語 パッケージエコシステム
npm Registry Javascript npm
Apache Maven Registry Java Maven
Gradle Registry Java Gradle
RubyGems Registry Ruby RubyGems
NuGet Registry .NET NuGet
Container Registry なし Docker

第11章 OpenID Connectによるセキュアなクラウド連携 7/7 読了

  • クラウドプロバイダへのアクセスには OpenID Connect を使う -> これはできいた
  • 静的クレデンシャルの利用はアンチパターン
  • OpenID Connect 一時クレデンシャルを払い出せて、アクセス元制限もできる
  • OpenID Connect を使うには OIDC Trust と Cloud Roles の準備が必要
  • 最小権限で運用して別リポジトリからアクセスできないことを確認する

第12章 コンテナオーケストレーションのデプロイメント 7/8 読了

  • ECR, ECS を利用してのデプロイは普段行っていたので理解できた
  • 複数環境のデプロイでは Environments が有効
    • デプロイメントプロテクションルールは Environments ごとに、ワークフローの機能条件を指定できる。
  • AWS Copilot によるプロビジョニングを知らなかったので試してみたい

第13章 アクションのオープンソース化 7/9 読了

  • GitHub Marketplace へのアクション公開の方法が丁寧に書かれていた
  • アクションにもテストが書ける
    • 4フェーズテストが有効
  • 公開にはバージョンをつける
  • README に概要や、入出力インターフェースだけでなく、環境変数やパーミッションなども記載する
  • 1つのことをうまくやる

第14章 GitHub Actionsの高度な使い方 7/10 読了

ここから応用編
内容が難しい 😅

  • Reusable Workflows を使うとワークフロー全体をカプセル化できて再利用できる
  • fromJSON 関数は動的なマトリックス生成や、文字列の型変換につかえる
  • Continue on Error はエラーが発生しても握り潰せる
  • マトリックスジョブがエラーになるとデフォルトでは他のジョブも停止する
  • プライベートのアクションや Reuseable Workflows のアクセス制御に注意する
    • 同一アカウント内の他リポジトリや Dependabot からアクセスするには明示的な設定が必要

第15章 GitHub Actionsのセキュリティ 7/11 読了

色々と気をつけないといけない箇所があることがわかった!

  • GitHub Actions への攻撃事例は増えている
    • さまざまなシステムと連携するので強力な権限が集中している
  • 設計原則
    • アタックサーフェスの最小化
    • 多層防御
    • 最小権限
  • GitHub はリポジトリが基本的なセキュリティ境界でデフォルト設定では寛容になっている
  • サードパーティアクションを使う場合は慎重に
  • github コンテキストの信頼できないデータは、中間環境変数で無害化する
  • ShellCheck で静的解析
  • 構造化データは Secrets へ保存しない

第16章 セキュリティのシフトレフト 7/12 読了

ソフトウェア開発のライフサイクル全体でセキュリティにとりくんでいくための章

  • シフトレフトの考えで、開発初期から継続的にセキュリティ対策に取り組む
  • 依存関係の脆弱性に役立つもの
    • Dependency graph
    • Dependabot alerts
    • Dependabot security updates
  • シークレットスキャンを自分で実装するなら Secretlint
  • 途中でシークレットスキャンを導入するなら Gitleaks のヒストリスキャンが役に立つ
  • ローカル環境でシークレットスキャンをするなら Git フック
  • コードからセキュリティ問題を検出するには Static Application Security Testing
  • OS パッケージを含めてイメージスキャンするなら Trivy
  • セキュリティ設定ミスを見つけるには IaC の低的解析ツールを使う

第17章 GitHub Appsトークンによるクロスリポジトリアクセス 7/13 読了

GitHub Apps トークンを利用してクロスリポジトリアクセスをする例が書かれていました。
GitHub Actions の実行時に他のリポジトリのコードを checkout できていました。
内容は結構難しめ。

  • GITHUB_TOKEN で権限が足りない場合はデプロイキーや GitHub Apps トークンを使う
    • 一個人が払い出したクレデンシャルは絶対に避ける
  • GitHub Apps トークンは都度生成する
  • GitHub Apps トークンを利用するには GitHub Apps の事前セットアップが必要

第18章 継続的デリバリーの実践 7/14 読了

継続的デリバリーについて包括的に説明してくれている章でした。
GitHub のスコープ以外に継続的デリバリーに役立つトピックを紹介してくれています。
色々な本が参考文献としてあがっていて、いいとこ取りできる章になっていました。

  • ソフトウェアデリバリーパフォーマンスは組織パフォーマンスと高い相関がある
    • 開発スピードが速い組織ほど品質も高い!
  • バージョン管理が大事。フィーチャートグルなども使って1日一度はデフォルトブランチにマージする
  • テストの目的は調査と検証
  • 頻繁にリリースして恐怖を克服!
    • 誰でもロールバックできるように練習する
  • データベースの変更はマイグレーションツールで行い、安全に一歩ずつ行う
  • IaC ではデフォルトブランチのコードだけ本番環境へ適用する
    • 構成ドリフトを検出したら早急に対応する
  • 疎結合なアーキテクチャを実現し、テスト容易性とデプロイ容易性をたかめる
    • コンウェイの法則を考慮して組織設計にも注意を払う
  • 実装者自ら運用に責任をもち、ユーザーの期待に答える
  • 継続的デリバリーを実践して、継続的な学習サイクルを確立する

最後に

読んでみると知らないことばかりでした😅
GitHub Actions は奥が深い 😵
読書のペースとしてはだいたい1日0.5〜1章ほぼ毎日読み進めることができました。(30分程度)
本の内容に結構ボリュームがあり、まとめながら読んではみたものの頭に入っていないところもあるので今後も辞書的において読み返したいと思います。
細かなところが今までおざなりになっていたことがわかったので少しずつ取り入れて運用を改善していきたいとおもいます!

レスキューナウテックブログ

Discussion