Software Design 202312
開発を加速する CI/CD
- ブランチ戦略
- Github Flow
- ブランチは main だけ
- main が進んだら自動的にデプロイされる
- Git Flow
- 5 つくらいブランチがあるやつ
- タグを打ってリリース
- リリース日が決まっている場合などに向いている
- Trunk-based Development
- Github Flow と似ているが、最低 1 日 1 回はメインブランチに変更を取り込む点が異なる
- Github Flow
- 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 の差分のみを検証することはできない
- 任意のタイミング
- いまなにが反映されているのか把握しづらいのでおすすめしない
- PR 作成時 / Pull Request Environment
- 検証からデプロイを自動化する場合の流れの例
- 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%だけ別コンテナにトラフィックを流す
- GitOps
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 のサイズを小さくする
- 複雑な仕様
- なるべくシンプルな仕様にする
- 要件定義の場に実装者とレビュアーが同席する
- 開発者のスキル不足
- メンターをつけて重点的にフォローする以外にない
- feature ブランチが大きすぎる
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)
- 具体的には、ユーザの発言に加えて ChatGPT への指示を添付して、必要な結果を得る
- ChatGPT の Chat completions API を使って必要な情報を生成してアプリで利用する
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)を使ってカラム追加する方法
- ただし参照系しかリファクタできない
- 段階的に子テーブルに情報を移していく方法
- 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 で構成する、簡単に書ける
- R2
- 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 による設定不備検知により、小さく始めるのが吉