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

Software Design 202312

開発を加速する CI/CD

  • ブランチ戦略
    • Github Flow
      • ブランチは main だけ
      • main が進んだら自動的にデプロイされる
    • Git Flow
      • 5 つくらいブランチがあるやつ
      • タグを打ってリリース
      • リリース日が決まっている場合などに向いている
    • Trunk-based Development
      • Github Flow と似ているが、最低 1 日 1 回はメインブランチに変更を取り込む点が異なる
  • CI/CD 基盤整備のポイント
    • セルフホストはなるべく避ける
    • 使うツールはなるべく絞りこむ
    • 認証の選択肢
      • 🟢 OIDC にする (サービスが対応しているなら)
      • 🟡 環境変数に静的なトークンを埋め込む
      • 🟡 HashiCorp Valut など外部のシークレット管理サービスを使う

CI

  • ソフトウェアライフサイクル
    • プラン
    • コード
    • ビルド(CI)
    • テスト(CI)
    • リリース(CI)
    • デプロイ(CD)
    • 運用
    • 監視
  • CI とは
    • コードをビルド&テストして、品質の担保された成果物を生成すること
  • monorepo or polyrepo
    • polyrepo の方が CI はシンプルになる
    • 一方で、同じような CI 設定を共有するための仕組みが必要になることもある

CD

  • CD とは
    • デプロイの自動化をすること
  • CD の目的
    • 人手によるミスをなくす
    • 人的コストの削減
  • まずは、シンプルに、小さく、自律的なしくみを作るのがよい
  • 検証環境のデプロイ
    • 検証の目的はあらかじめ明確にしておくこと
    • いつデプロイするか?
      • PR 作成時 / Pull Request Environment
        • PR が作成されたら、その差分のみを反映した独自の検証環境が作成される
        • お金がかかる
      • 特定ブランチやタグの変更時
        • 技術的に容易でコストも安い
        • PR の差分のみを検証することはできない
      • 任意のタイミング
        • いまなにが反映されているのか把握しづらいのでおすすめしない
    • 検証からデプロイを自動化する場合の流れの例
      • develop ブランチなど特定のブランチが更新される
      • 検証環境が更新される
      • E2E が実行される
      • テストが完了したら本番にデプロイする
  • 本番環境のデプロイ
    • 検証環境のデプロイと考え方は同じ
    • デプロイ後に以下をやるとよい
      • E2E の実行
      • Datadog 等のオブザーバビリティツールとの連携
  • パターン
    • お手軽パターン
      • 構成
        • CD/CD: Github Actions 等
        • 検証環境: AWS Fargate や Cloud Run
        • ブランチ戦略:GitHub Flow
        • CD パイプライン: 予め用意されている仕組みを最大限活用する
        • セキュリティ: OIDC による認証
      • 全てマネージドサービスを使う
      • 以下を設定しておく
        • リリースブランチへの直プッシュ禁止
        • PR には承認者が必須
        • PR はステータスチェックの通貨が必須
    • GitOps (Argo CD) を使用したパターン
      • GitOps とは、Git においた構成ファイルを正としたうえで、Kubernetes 等のサービスを宣言的に管理する手法
      • Kubernetes 側から Argo 側にポーリングするので Kubernetes API を触らなくて済む
      • デメリット
        • Argo の定期的なバージョンアップが必要
        • CI と CD で使うツールが分断される
    • セルフホストランナーを使用したパターン
      • (おすすめしない)

ある会社での具体例

  • CI
    • コンテナのビルド
    • ユニットテストの実行
    • 起動テストの実行
    • TODO コメントの Issue 化
    • クラス図の作成
    • Renovate
      • バージョン管理
    • SonarCloud
      • テストカバレッジの可視化
      • 静的解析
  • CD
    • GitOps
      • マニフェストファイルの変更により自動的にデプロイされる
    • カナリアリリース機能
      • Argo Rollouts により 10%だけ別コンテナにトラフィックを流す

Trunk-based Development

  • Trunk = メインブランチのこと
  • 2 種類ある
  • Feature Branch を一切使わない方法
    • PR はつくらず、main ブランチに直接コミットする
    • 数時間、遅くとも 1 日以内にメインブランチへの統合が必要
    • レビューは同期的に画面共有などして行う
    • テストはローカル環境で行う(マジか)
    • メインブランチでテストが失敗したときは自動でリバートする
  • Feature Branch を使う方法
    • まずはこちらからやってみるのがおすすめ
    • short-lived feature branches
    • ブランチは数日以内に削除されなければならない
    • PR の作成が可能
    • 非同期レビューが可能
    • 事前テストが可能
  • マージが遅くなる典型的な理由と対策
    • feature ブランチが大きすぎる
      • 差分が 600 行を超えたら CI で自動的に落とす
      • 先出し・後出しできる変更は PR わける
      • タスク分解が十分か検討する
      • Feature Flags を活用する
    • レビューの遅延
      • 同期でレビューする
      • 残課題は TODO コメント化して先にマージしちゃう
      • PR のサイズを小さくする
    • 複雑な仕様
      • なるべくシンプルな仕様にする
      • 要件定義の場に実装者とレビュアーが同席する
    • 開発者のスキル不足
      • メンターをつけて重点的にフォローする以外にない

ChatGPT を組み込んだサービスの開発

  • 利用方法
    • 生成 AI プラットフォーム(ChatGPT など)を使う
    • オープンソースの LLM を使う
      • ただし ChatGPT レベルのものはまだない
    • SaaS を使う
  • LLM に自社タスクを適応させるか
    • プロンプトエンジニアリング <- おすすめ
    • ファインチューニング <- 時間と手間がかかる
  • Retrieval-Augmented Generation (RAG)
    • 情報源を自社の DB にする
    • ハルシネーションが激減する
  • ChatGPT プラグインのメリット
    • 最新のデータを利用可能
    • ハルシネーションの減少
    • 苦手な算術演算等を外出しすることで誤りを減らす
  • ChatGPT プラグインの作り方
    • JSON と OpenAPI 定義を書くだけなのでわりと簡単
  • AI チャットの作り方
    • ChatGPT の Chat completions API を使って必要な情報を生成してアプリで利用する
      • 具体的には、ユーザの発言に加えて ChatGPT への指示を添付して、必要な結果を得る
        • e.g. 会話に店の予約の意図が含まれているかどうかを JSON で返してもらう
        • e.g. 外部 API を呼ぶための情報を JSON で返してもらう(Function calling)

Datadog によるクラウド横断のモニタリング

  • 良さ
    • 様々な対象からログやメトリクスを収集し、一元管理できる
    • ダッシュボードを作成できる
    • 柔軟なアラート設定
    • Terraform に対応しているので設定を IaC 化できる
  • セットアップ
    • 公式ガイド見れば簡単
    • Datadog がクラウドサービスから Pull する場合と、逆にクラウドサービスから Datadog に Push する場合がある

ログの管理

  • ログは JSON で出力するとよい
    • フィルタリング、並べかえ、項目の表示非表示が可能になる
    • JSON で出せない場合は Grok 構文でパースすることも可能
  • インデックス
    • 送信されたログのうち、ログエクスプローラーから検索可能なログのこと
    • ログをアプリ側で選別せずとも Datadog 側でインデックス対象を選別できる
    • インデックスを使うほどお金がかかる
  • アーカイブ
    • 長期保存のためにインデックスの保存期間を過ぎたログを S3 などに転送する機能
    • アーカイブから復元も可能

ダッシュボード

  • 観点
    • プロダクトの現在の状況を人目で把握できるか
    • 異常検知後の原因分析が速やかにできるか
    • 将来のための傾向分析ができるか
  • 1 プロダクト 1 ダッシュボードがおすすめ
  • レイヤ構成の例
    • 概要レイヤ
      • 正常稼働しているかどうかを把握するためのレイヤ
      • 正常なら緑、異常なら赤などにする
        • リソースのメトリクス(CPU やメモリの使用率、リクエスト数、エラー数など)
        • プロダクト固有のメトリクス(商品検索・ログイン・決済の成功率など)
    • 重要な指標のレイヤ
      • 障害発生時に問題の切り分けを迅速にするためのレイヤ
      • アプリケーション・ビジネスとして重要なメトリクスや、可用性や性能面でボトルネックになりやすいメトリクスを含める
        • ユーザーの同時接続数
        • アプリケーションエラー数
        • メッセージキューの状態
        • DB 負荷やコネクション数
        • 平均レスポンスタイム
        • 定期ジョブの成否
    • 重要でない指標のレイヤ
      • その他のメトリクスをまとめたレイヤ

アラート運用

  • アラートの重要度を決める
    • A なるはや
    • B 4 時間以内
    • C 24 時間以内
    • D 一週間以内
  • Runbook の整備
    • システムの概要、運用手順、対応方法、エスカレーション方法、緊急対応手順などを書く

Go 言語の並列処理

  • 徹底的にシンプル化されており「並列化したい処理を書く」ことしかできない
    • ゴルーチンを変数に代入できない
    • ゴルーチンの ID や状態を取得できない
    • ゴルーチンをスケジューリングできない
    • ゴルーチンから戻り値を受け取れない
      • その代わりチャネルがある
        • 関数のネストを飛び越えられる End-toEnd な情報共有手段
        • 異なるゴルーチン間での処理を疎結合にするインターフェース

実践データベースリファクタリング

  • スマートカラム
    • 意味を含んだ ID のこと
    • たとえば、6 桁ある userId の一桁目が1なら管理者で 9 なら一般ユーザーというようなもの
  • 問題
    • パフォーマンス劣化
    • 値を取り出すまで意味がわからない
    • ロジック追加が困難
  • 解消法
    • View や生成列(STORED | VIRTUAL)を使ってカラム追加する方法
      • ただし参照系しかリファクタできない
    • 段階的に子テーブルに情報を移していく方法

Cloudflare Workers

  • Faas であり、Lambda 等の仲間
  • 0 ミリ秒でコールドスタートできる
  • 利用できるのは JavaScript と WebAssembly(Rust ほか)
    • 余談だけど、もともと WebAssembly はブラウザで動くように設計されたものだったが、最近は可搬性の高いバイナリフォーマットとして、ブラウザ以外での注目が増している
  • gzip 後のサイズが 1MB を超えないほうがパフォーマンスが良い
  • Workers から利用可能な他の Cloudflare サービス
    • R2
      • S3 互換のオブジェクトストレージ
      • Egress 料金が衝撃の無料
    • KV
      • 結果整合のキーバリューストア
      • 全世界から読み取られる前提の動的に変化する値に最適
    • Cache API
      • Web 標準の Cache API に則っている
      • 1 オブジェクト 512MB、1 リクエスト 5GB まで
      • 各ロケーションごとにキャッシュしておきたいものに最適
    • D1 (Open Beta)
      • SQLite ベースの RDB
      • リードレプリカが各ロケーションに自動で作成される
    • Cloudflare Queues (Open Beta)
      • キューイングのサービス
      • 処理の非同期化に使える
      • キューを送信する Producer Worker と 受信する Consumer Worker で構成する、簡単に書ける
  • AWS 等を使わなくても Cloudflare 単体でアプリを構築できる
  • かなりの低価格で利用できる

Amazon CloudWatch

CloudWatch メトリクス

  • パフォーマンスデータの集積所
  • 用語
    • 名前空間
      • メトリクスを分類するためのカテゴリ
      • AWS サービスごとに名前空間が用意されている
      • e.g. AWS/EC2 など
    • メトリクス
      • e.g. CPU 使用率、メモリ使用率など
    • ディメンション
      • 同じメトリクス名を持つデータをさまざまな属性で分類するためのもの
      • e.g. EC2 だと InstanceId、InstanceType、ImageId など
    • ユニット
      • メトリクスの値の単位
      • e.g. %、秒、カウントなど

CloudWatch アラーム

  • 3 つの状態がある
    • 🟢 OK
    • 🔴 Alertm
    • 🟡 Insufficient Data
  • 通知の種類
    • メール、Slack、電話など
  • 急激な変化を検知する「異常検出」という機能がある
  • おすすめのアラームを推奨してくれる「アラームに関する推奨事項」という機能がある

CloudWatch Logs

  • 構成要素
    • ロググループ(親)
      • リソース(e.g. 特定の ECS クラスター)単位で出力される
      • ログストリームを複数持つ
      • 保存期間はデフォルトで無期限
        • 必要に応じて短くしたり、S3 に退避したりする
    • ログストリーム(子)
      • ある程度の期間でぶつ切りにされてまとまっている
  • ログ監視
    • 正規表現等によりログを監視して、アラームを発生させることができる
  • 機密情報のマスク
    • ポチポチするだけで設定できるが日本語対応は不十分
  • ライブテール
    • リアルタイムにログを目視で確認できる
  • ログのインサイト
    • ログに対して SQL ライクなクエリを実行できる

AWS のクラウドセキュリティ / 発見的ガードレール

  • 脅威の種類
    • 人的脅威
      • 意図的 (e.g. 不正アクセス)
      • 偶発的 (e.g. 誤操作)
    • 環境的脅威 (e.g. 自然災害)
  • 発見的ガードレールの検知対象は 2 つ
    • 攻撃や意図しない操作
      • リアルタイムでの検知が必要
    • 設定不備
      • 定期的な検知が必要
  • 発見的ガードレールに関連するサービス
    • AWS Security Hub
    • AWS Config
    • AWS Trusted Advisor
    • Amazon GuardDuty
    • Amazon Macie
    • Amazon Inspector
  • 構築手順
    • まずは GuardDuty による脅威検知と、AWS Security Hub と AWS Config による設定不備検知により、小さく始めるのが吉