リリース・デプロイ戦略を支える技術

ここでは Kubernetes 上で起動する server や worker の deploy 戦略について説明します。 Wantedly における Git 及び GitHub の運用方法が前提となるので事前に Git の慣習 を読んでおくと良いでしょう。

デプロイ基盤

GitHub Flow の実現方法

各 repository で CI/CD で下の2つのことを行うことで GitHub Flow を実現しています。

  • 全 branch: Docker image の build (kube ci-build)

  • master: Docker image の deploy (kube <env> deploy)

ここで ci-build は commit hash を tag にもつ docker image を作成して registry に保管し、 deploy コマンドが Kubernetes の Deployment に対する変更を反映することで rolling で deploy されます。

いわゆる CIOps という Push 型のデプロイです。

Docker image 以外の更新、つまり Kubernetes Manifest のデプロイについては Argo CD による GitOps という Pull 型のデプロイを採用しています。

Rolling Deploy

Rolling deploy では新旧2つのアプリケーションバージョンを一部ずつ置き換えていきます。 このため、アプリケーションにもよりますが最大で数分程度複数のバージョンが共存する時間ができることになるため、 すべての deploy は後方互換性をもたせる必要があります。 一例としてフロントエンドとサーバーサイドのコードを 1 つのコードベースで管理している microservice (wantedly/wantedly, wantedly/perk など) では API 追加とその API に依存する frontend は先に API のみを merge する必要があります。また逆に機能を落とす場合は先にフロントエンドからの依存がない状態を merge 仕切ってから API を落とす pull request を merge する必要があります。

Kubernetes Manifest 管理

Argo CD による GitOps

Kubernetes Manifest は Argo CD を導入して GitOps という Pull 型のデプロイを行っています。

もともと Wantedly では Docker image の deploy を CICD で行っていましたが、Deployment 以外の Kubernetes manifest は長らく手動での apply になっていました。 そのため、以下のような課題・問題がありました。

  • リリースフローの自動化・高速化・簡易化を行い、インフラチーム以外でも Kubernetes Manifest の管理を可能にすることで開発・リリースの速度を早めたい

  • apply 忘れなどで repository と実際に apply されている manifest が異なり、Kubernetes Cluster のメンテナンスや Upgrade のブロックになっていた

  • apply 忘れなどで repository と実際に apply されている manifest が異なり、リリース時にアプリケーションエラーを引き起こすインシデントになった

そこで Argo CD による GitOps を導入 (internal) し、特に kubernetes manifest の継続的なデプロイの自動化をゴールとして、これらの課題を解決しています。

ただし先述したとおり、アプリケーションコンテナのデプロイ は CICD で Kubernetes Deployment に対して変更を加えて Rolling Deploy を行っているため、2022年1月現在、docker image tag は GitOps の対象外となっています。 これは

  • 課題に対して docker image tag の GitOps は一旦スコープアウトできる

  • 障害対応時には kube <env> deploy <commit hash> コマンドで緊急切り戻しを行っているため、このフローのアジリティを壊したくなかった

といった背景がありました。

今後、docker image tag も含めて CIOps から GitOps への移行を検討するなど、さらなるデプロイ戦略の改善を進めています。

Kubernetes Manifest の自動生成・更新

GitOps に加えて、Wantedly の microservice ための Kubernetes Manifest を自動生成して継続的に更新する仕組みがあります。 たとえば kube generate autoscale を実行すると、その repository に必要な Deployment, HPA, Service, PDB といった Kubernetes Resource が生成されます。もちろん、Wantedly の microservice における推奨設定が自動的に入っています。

ingress や cronjob の他、Wantedly 内で独自定義された worker の Deployment などの様々な generate subcommand が用意されています。

$ kube generate                                              
Generate manifest files

Usage:
  kube generate [flags]
  kube generate [command]

Aliases:
  generate, g

Available Commands:
  autoscale          Generate manifest files for Deployment with Autoscale
  cronjob            Generate manifest files for Cronjob
  grpc               Generate manifest files for gRPC Deployment
  ingress            generate manifest files for Ingress to web pod
  internal-ingress   generate manifest files for Ingress resource for internal only
  namespace          Generate manifest files for Namespace
  rbac-kube-sh-by-ci Generate manifest files for resources to enable `kube sh` from CI
  redis              Generate manifest files for redis statefulset
  sidekiq-exporter   Generate manifest files for sidekiq-exporter
  worker             Generate manifest files for worker deployment

...

そしてこの自動生成された manifest は、 kube のバージョンが更新されるたびに新しくなります。最新版の manifest を保つために、Dependabot のような最新バージョンを検知して自動的に manifest 更新のプルリクエストを作ってくれる仕組みを作っています。 詳しく知りたい方はこちらも見てみてください。継続的な manifest 更新の取り組み (internal)

FeatureFlag

基本的にリリースブランチを持たない我々の開発手法ではコンフリクトを避けるためにコンスタントに master branch へ merge していくことが重要です。 しかしながら master branch で merge されると自動で deploy がなされるため、場合によっては変更をユーザーに見せたくないこともあります。 このような場合に FeatureFlag が有用です。 FeatureFlag は boolean, string のいずれかを返す method の返り値を request ごとに任意の値に変更できる Wantedly の社内基盤です。 値の変更を許可しておきたい method を事前に FeatureFlag の library で wrap しておくと Chrome Extension (internal) から override ができるようになります。 これを用いることでユーザーには機能を隠しておきつつ、qa 環境では簡単にその機能を顕在化させることができます。

Override 宣言のやり方

まず override できる method を指定する方法を示します。 現在 Rails と React がサポートされていますがそれ以外のフレームワークや言語に対応要望がある場合は Developer Experience Squad に相談してください。

Rails

最新 version の servicex が install されていることを確認してください。

module SomeNewFeature
  # この module を include すると module 内の任意の method が override 可能になる
  include Servicex::FeatureFlag::Trapper
  # 正規表現で特定の method に絞ることも可能
  feature_flag_trap_only /\?$/

  # 上記の正規表現に match するため override 可能になる
  # フラグ名は `SomeNewFeature.released?` となる
  def released?
    false
  end
end
module SomeNewFeature
  # より低いレイヤの API を用いることで method でなくても任意の場所での override が可能
  def released?
    return true if some_condition

    # some-flag-name という名前で override 可能になる
    Servicex::FeatureFlag::Interceptor.intercept("some-flag-name") do
      false
    end
  end
end

React

// 別途 context provider を挿入する必要があるので詳しいことは各 repository を参照
import { useBoolFlag } from "@wantedly_private/frontendx";

export const SomeComponent: React.FC<Props> = (props) => {
  const featureEnabled = useBoolFlag("some-awesome-new-feature");
  if (featureEnabled) {
    // ...
  }
};

(2022年2月現在booleanしか対応していない)

Override のやり方

Chrome Extension

上記の方法で override 可能にした method は Chrome Extension (internal) から override できます。

qa 環境にアクセスして Chrome Extension の popup を開くと response の render に使われた flag の一覧が表示されます。 ここで変更したい flag の override を値を更新してページをリロードすることでサーバーの挙動を override することができるためリリース前の機能などを試すことができます。

Kubefork

より高頻度に確認したい場合は、特定の Flag を有効にしたサーバーを別の URL で serve することができます。 具体例として下のような状況が実現できます。

  • 新しいデザインの LP を出すかどうかを new-lp という Flag で提供

  • https://new-lp.qa.wantedly.com のような URL を作成

  • ここにアクセスすると任意のブラウザにおいて Chrome Extension なしで new-lp が有効になっているものとして動作する

エンジニアチーム外の人にテストをお願いしたいときには特に便利です。

詳しい利用方法については fork のドキュメント の「特定の FeatureFlag が有効にしたものをテストしたい」のセクションを参照してください。

Pull Request Preview

上記のように FeatureFlag を使うことでリリースブランチを作らないようにすることが基本方針です。 しかしながら依存 Library の upgrade やアプリケーションの構成の変更などどうしてもリリースブランチを作る必要があることもあるでしょう。 こういったケースでは Pull Request Preview が便利です。 任意の Pull Request において /preview <env> とコメントすると次の図のように URL がコメントされるためここにアクセスするだけで preview が実現できます。

ブランチに更新があると追従されるため、数週間以上に渡ってリリースブランチを維持しなくてはいけないときに特に有効です。 この機能のバックエンドは マイクロサービスでもポチポチ確認するための Kubefork であるため、 ユーザーフェイシングでないマイクロサービスでも利用可能です。 2021 年 7 月現在、主要な repository はサポートされていますが自分の管理する repository が対応しているかについては Developer Experience Squad に問い合わせてください。

Migration

GitHub Flow では master branch に存在する commit はすべて deploy 可能であるべきです。 もちろんこれを完全に保証することは不可能ですが、自動デプロイを行っているため少なくとも最新の commit が deploy されて困る状態は許容できません。 したがって、データベースの migration に依存する commit の merge は migration のあとに行われる必要があります。

そこでこれを保証する CI として pending-migration-checker があります。

この check が落ちている場合は migration が必要です。 migration を kube prod sh などで実行して、/check migration とコメントすることで再度 check が実行されます。 詳しい設定方法などは上記の repository のリンク先を参照してください。

話を聞きに行きたい

もっと知りたい

最終更新