メインコンテンツまでスキップ

Software Design 202407

Github Actions

基本

  • 料金
    • パブリックリポジトリでは無料
    • プライベートリポジトリでは有料だが無料枠あり
      • Windows は Linux の 2 倍、Mac は 10 倍
  • 手軽さ
    • Webhook の設定が不要なので簡単
  • イベント / ワークフローをキックする
    • ワークフロー / 1 つ以上のジョブを実行する自動化されたプロセス
      • ジョブ / 同一のランナー内で実行される一連のステップのまとまり / 逆に言えばジョブが違えば全く異なるまっさらな環境で実行される
        • ステップ / 最小の単位 / 以下の 2 つが可能
          • runによるスクリプトの実行
          • usesによるアクション(事前定義された処理のプリセット)の実行
            • このときwithでパラメータを渡すことができる

ハンズオン

  • ${{}}という書式は、GihHub Actions 上で利用できる変数にアクセスするための書式
    • e.g. ${{ github.actor }}で実行した Github のユーザー名を取得できる
    • e.g. ${{ secrets.GITHUB_TOKEN }}で GitHub Actions で使えるデフォルトのトークンを取得できる
  • 以下ような用途には専用の actions が用意されているのでそれを使うと吉
    • docker コマンドを使った認証
    • docker コマンドを使ったレジストリへのプッシュ
    • Amazon ECS や Google Cloud Run などのデプロイ

実運用

  • コンテキスト
    • github コンテキスト
      • ワークフローをトリガーしたイベントやリポジトリに関する情報を持つ
        • ブランチ名 ${{ github.head_ref }}
        • PR の番号 ${{ github.event.pull_request.number }}
        • PR の作成者 ${{ github.event.pull_request.user.login }}
    • job, steps コンテキスト
      • 現在のジョブやステップに関する情報を持つ
        • ジョブの実行結果 ${{ job.status }}
        • ステップの実行結果 ${{ steps.<step-id>.conclusion }}
    • vars, secrets
      • あらかじめリポジトリや Organization に登録した、変数やシークレット情報
        • ${{ vars.SOME_VALUE }}
        • ${{ secrets.SOME_SECRET }}
      • vars と secrets の違いは、secrets はログで*でマスクされる点
  • アーティファクト
    • 成果物を保存するための仕組み
    • 専用の action が用意されている
    • 複数のジョブ間でのデータの受け渡しのためや、人間が結果を利用するために使う
  • 複数 job の順番制御
    • needsに依存先のジョブ名を書くことで行う
  • 成否による処理分岐
    • ステップやジョブにif: ${{ always() }}を指定することで、前段の処理が失敗しても必ず処理が実行されるようになる
    • その他、成功時のみ実行するif: ${{ success() }}、失敗時のみ実行するif: ${{ failure() }}もある
  • キャッシュ
    • actions/setup-***などにおいて、キャッシュを有効にするオプションが用意されている
    • actions/cacheを使うことで、任意のフォルダを自由にキャッシュすることも可能
      • この場合、key の設定には注意。ファイルのハッシュに加えて OS の種類を含める必要がある場合もある。
  • トリガーの種類
    • label トリガー
      • コストが高い処理について、手動でトリガーをかけるときなどに使う
    • スケジュール実行
      • バッチ処理や、不要リソースの定期削除などに使う
      • 最小間隔は 5 分
      • 必ずその時間に実行されるわけではなくベストエフォートなので、確実な実行が必要であれば自前でスケジューラを立てて、workflow を API を介して dispatch するなどの方法が必要
    • 手動トリガー(workflow_dispatch)
      • Github の Web 画面や API を介して、手動実行が可能になる。手元からgh workflow runも可能になる。
      • 対象のブランチを指定できる。また、あらかじめ指定したパラメータも指定できる。
      • 通常のトリガーと併用可能なので、転ばぬ先の杖として使える
  • トリガーのフィルタ
    • 特定のブランチでのみ実行するon.push.branches
    • 特定のフォルダ内に変更があったときのみ実行するon.pull_request.paths
      • ファイルのフォルダに加え、ワークフローの yaml も含めないと、ワークフロー自体の変更時にトリガーされないので注意
  • 権限
    • GITHUB_TOKEN
      • GihHub Actions が GitHub の特定リソースを扱うための権限
      • 特に設定無しで使える
      • contents, packages, metadata の 3 つがある
        • contents: read(規定), write, none
        • packages: read(規定), write, none
        • metadata: 常に読み取り許可で固定
      • 明示する場合は、contents と packages の両方を列挙しないと書かなかったものは none になるので注意
  • OIDC を用いたクラウドサービス連携
    • ワークフロー実行時に、GitHub から発行された OIDC トークンをクラウドプロバイダーに投げ、アクセストークンを得る方法
    • クラウド側では OIDC トークンの subject claim を見て、許可を判断する
      • subject claim の例: repo:<OWNER>/<REPO>:refs/heads/main
    • クラウド側では、事前に以下の設定を済ませておく必要がある
      • 受け入れる subject claim の形式(≒ 対象のリポジトリとブランチ)
      • 与える権限
  • セキュリティ対策
    • 特にパブリックなリポジトリではインジェクションに注意
      • なるべく actions を使うこと
      • 文字列をコンテキストから取る場合は、一度環境変数に入れてから使うなどの対策が必要

大規模開発での Actions の課題と解法

  • コンテナを使う方法
    • ジョブコンテナ
      • ジョブを実行するコンテナ
      • runs-onの階層でcontainerを指定する
    • サービスコンテナ
      • ジョブの開始時に起動する任意のコンテナ
      • DB を立ち上げてテストを実行する際などに使う
      • runs-onの階層でservicesを指定する
  • credentialsプロパティにレジストリの認証情報を書くことができる
    • docker のパブリックイメージを使う場合でも、pull rate limit を防ぐ意味で書いておくとよい
  • ランナーの種類
    • GitHub-hosted (larger) runner
    • Self-hosted runner
  • matrix strategy
    • ジョブの実行を複数の変数の組み合わせで行うための機能
    • ユースケース 1: テストの分割実行
      • 例えば jest は shared オプションを使うことで並列実行が可能
    • ユースケース 2: テストの動的実行
      • JSON を入力として受け取れるので、前段のジョブで条件の組み合わせ一覧を JSON で生成し、それを使って後段で複数のテストを実行することが可能
  • GITHUB_TOKEN の制約
    • 別のワークフローの実行や、別のプライベートリポジトリへのアクセスはできない
    • かといって PAT の利用は非推奨
    • GitHub Apps の仕組みに乗る方法がよい(詳細はドキュメント参照
  • キャッシュ
    • 上限は 10GB で、超えたら GitHub のお気持ちにより削除される
    • actions/cache/(save/restore)を使うことで、キャッシュするしないをコントロールできる
    • 例えば Dependabot の PR ではキャッシュ読み込みはするけど保存はしないなどの設定はよい取り組み

大規模開発でのワークフロー設計

  • ワークフローを DRY にする方法
    • Custom actions
      • ワークフローから呼び出す形で再利用可能なステップ群
      • 呼び出し元と同じランナーで実行される
      • ユースケース
        • setup-***系のアクションが存在しないツールのインストール
        • クラウドサービス等への認証
        • キャッシュの設定
        • 成果物やテストデータのアップロード
        • Slack 等への完了通知
    • Reusable workflows
      • 単体で再利用可能なワークフロー
      • 呼び出し元とは異なるランナーで実行されるため、値の受け渡しは inputs, outputs のみ利用可能
      • ユースケース
        • テストのワークフローをいろんな場所で実行したい
        • 環境ごとにデプロイしたい
  • イベントの詳細な種類
    • on.<event>.typesによりイベントの種類を細かく制御できる
    • e.g. プルリクエストなら、オープン時、再オープン時、更新時、クローズ時など
  • 必須ステータスチェックとジョブのスキップの両立
    • モノレポなどでは、対象ディレクトリの変更がなければテストをスキップしたりするが、このとき必須ステータスチェックに引っかかると困る
    • この場合、単一のワークフローの最初のジョブでテストの要否を判断し、後続のジョブでテストをスキップする構成に必要がある
  • 失敗したテストだけをテストする
    • 失敗したテストの情報をキャッシュに残しておき、2 回目以降の実行ではそこだけ実行することができる(詳細は本文参照)

脆弱性入門

  • XSS の種類
    • 反射型 XSS
      • 攻撃用の JavaScript が攻撃者の用意したサイトや URL に存在するタイプ
      • URL の例: https://example.com/?q=<script>alert(1)</script>
    • 持続型 XSS
      • 攻撃者が入力したコンテンツが Web サービスのデータベースに保存され、他のユーザーにも表示されるタイプ
    • DOM ベース型 XSS
      • サーバー側の処理を経由せず、クライアント側で不正な JavaScript が実行されるタイプ

ハピネスチームビルディング / モチベーション 3.0

内発的動機づけで主体的に行動するという考え方。1.0 は生きるため。2.0 はアメとムチ。3.0 は内発的動機づけ。これらのどれが一番効くのかは人によって違うので、それぞれの人に合わせたアプローチが必要。

  • 自律性: 自ら方向を決定したい
    • 個人の裁量を大きくして、自由にいろいろなことができるようにする
  • 熟達: 価値がある技能を上達させたい
    • 自分が学びたいスキルや知識を得られるようにする
    • 簡単すぎず難しすぎない仕事だと、フロー状態に入って熟達につながりやすい
  • 目的: 自分以外の人、もの、社会に貢献したい
    • 会社や社会に対する貢献を実感できる機会を用意する

Chrome の世界 / セキュリティ

OS のセキュリティ境界がプロセスであるのと同じように、Web ではオリジン(Origin)がセキュリティ境界として最適であると考えられてきた。この考えを Same Origin Policy / SOP という。origin はプロトコル、ホスト、ポートの組み合わせのこと。

  • https://www.example.com (比較対象)
  • http://www.example.com (プロトコルが違うので別オリジン)
  • https://www.example.com:8080 (ポートが違うので別オリジン)
  • https://sub.example.com (ホストが違うので別オリジン)

オリジンが異なると、通信ができないなどいくつかの制約がかかる。これを必要に応じて緩和する仕組みが Cross Origin Resource Sharing / CORS である。例えばサイト B のリソースをサイト A でも使っていい場合、サイト B のサーバーは以下のような HTTP ヘッダを付与する。

Access-Control-Allow-Origin: https://siteA.com

ブラウザは複数のオリジンを同一プロセスで扱うため、CORS だけではセキュリティ上の問題がまだ残っていた。このため、サイト(Site)ごとに異なるプロセスで扱うようになった。この仕組みを Site Isolation という。サイトはオリジンよりもゆるい分け方で、eTLD(TLD + ccTLD 等)とその一つ前のドメインを使って分ける。これを eTLD+1 という。

  • https://www.example.com (比較対象)
  • http://sub.example.com:8080 (プロトコルもホストもポートも違うけど、同一サイト)

ドメイン解体新書 / DNS のプライバシー

もろもろ検討されている技術はあるものの、進捗はあまりよろしくない。

  • DNS over (HTTPS|TLS|QUIC)
    • クライアントとキャッシュサーバ間の通信暗号化、改ざん防止、接続先認証
  • ODoH
    • oblivious DNS over HTTPS
    • クライアントとキャッシュサーバの間にプロキシを挟むことで、クライアントの IP アドレスを隠す仕組み
  • HTTPS(TLS)
    • クライアントとホスティングサーバ間の通信暗号化、改ざん防止、接続先認証
  • ENSI, ECH
    • Server Name Indication / SNI は、ひとつの IP アドレスに複数のドメインをホストできるようにする通信の仕組み
    • この情報はダダ漏れだったので、SNI を暗号化する仕組みである Encrypted SNI(ENSI)や、傍受を防ぐ Encrypted Client Hello(ECH)という仕組みが生まれた。