GCP + ネイティブアプリでのGitLab flow構築
はじめに
これは今さらな2021年振り返りカレンダーの2日目の記事です.
トゥイラーも良かったらフォローしていただけると嬉しいです🐦
前回はCloud Run上で動くサービス用にGitHub flowでのCD環境を構築する話でした.
前回の最後やいろんな記事で述べられているとおり,GitHub flowは審査が必要なネイティブアプリなど,アプリケーションの
リリースタイミングをこちら側でコントロールできないケースで不都合が発生します(モノレポ想定です).
例えばあるアプリについて,後方互換のない新機能が入ったバージョンv2がmainにマージされ,stg環境に
上がっているとします.
この時,prdのネイティブ以外の部分でなんらかのバグが見つかり,すぐに修正を行いたいとき,通常のサービスならば,
修正PRをmainにマージし,新規機能とともにリリースすることができます.
しかしネイティブアプリの場合,アプリの審査がリリース前に挟まれるため,修正をデプロイするには,
審査を待つか,既にmainに入っている新機能をrevertするしかありません.
対策としては,ネイティブアプリのレポジトリを分けることが考えられますが,
自分のいたチームでは全員が機能開発に対してバックエンド,アプリを両方書いていたため,
PRの管理やリリースバージョンの統一が面倒になるなど別の問題が想定されました.
そこで,今回はGitHub flowをやめ,GitLab flowを採用することとしました.
GitLab flowの詳細については以下に詳しく書いてあります.
要点をまとめると以下のような感じです.
- mainブランチに加え,環境(stg, prdなど)ごとにブランチを用意する.
- 新規の変更はmainからブランチを切って,mainにマージする.
- 検証環境にデプロイする際はmainをそのブランチへマージし,デプロイする
- 検証が終わったら,検証ブランチを本番ブランチにマージし,デプロイ
- 本番に近いブランチは,必ずその前の全ての検証ブランチを通っているようにする
仕様
今回はクライアントはiOSアプリ,バックエンドはCloud Runに乗せている形を想定します.
また,環境は一旦stg環境とprd環境の2つとします.
そして,仕様は次のような形です.
- 新規の変更はmainブランチから切り,mainブランチにマージ.また,検証用ブランチとしてstaging,本番ブランチとしてprductionを用意
- 常にmainからstgへのPRを生成し,リリースのタイミングでそれをstagingへマージし,リリースコマンドを打つ.このタイミングで,stagingからproductionへのPRを生成.
- staging上での検証が終わったら,stgからprdへのPRをマージ.アプリをビルドし,審査へ提出.審査にapiの変更が必要な場合はこのタイミングでデプロイ.
- 審査が通ったら,必要に応じてmigrationを行い,apiをデプロイ.
また,hotfixの流れは以下のような形です,
- stgからhotfixブランチを切り,stgにマージし動作確認.確認が終わったら,ブランチをmainとstgにマージ.
- stgをprdにマージし,審査&デプロイ.
実装
ディレクトリ構成は前回と同じです.
CDフローに必要なファイルなどは,release下に,環境ごとに用意します.
root/
├── .github/workflows
│ ├── release.yaml
│ ├── hotfix.yaml
│ └── prerelease.yaml
├── api
├── ui
├── release
│ ├── dev
│ └── prd
└── release.sh
新しいバージョンのリリースとstagingへのデプロイ
新規バージョンのリリースの際はまず,mainからstgingブランチへ差分をマージします.
この差分PRを常時生成するために,
git-pr-releaseを用いました.
git-pr-releaseでは,ブランチ同士を比較して,
差分がある場合はPRの自動生成と差分コミットの詳細を作ってくれます.
設定ファイルは以下のような感じです.
GIT_PR_RELEASE_BRANCH_STAGINGにはマージ元を,GIT_PR_RELEASE_BRANCH_PRODUCTIONにマージ先を設定します.
また,GIT_PR_RELEASE_TEMPLATEには生成するPRのテンプレートを設定することができます.
# prerelease.yaml
on:
push:
branches: [main]
jobs:
create-release-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup Ruby
uses: actions/setup-ruby@v1
with:
ruby-version: 2.7.x
- name: Create Staging release pull Request
env:
GIT_PR_RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GIT_PR_RELEASE_BRANCH_PRODUCTION: staging
GIT_PR_RELEASE_BRANCH_STAGING: main
GIT_PR_RELEASE_LABELS: staging
GIT_PR_RELEASE_TEMPLATE: .github/workflows/prerelease_template
run: |
gem install -N git-pr-release -v "1.9.0"
git-pr-release --no-fetch --squashed
次に,新規バージョンタグをプッシュした際の動きを実装します.
その際には以下を行います.
- リリースノートの生成
- stagingからproductionへのPR生成
- staging環境へのデプロイ
リリースノートの生成は,前回と同様以下を用います.
stagingから,productionへのPR生成は,上記のrelease.yamlに以下の動作を追加し行います.
リリース用のブランチ(名前はrelease/v0.0.0)を生成したのち,そのブランチからproductionへ
向けたPRを生成しています.
# release.yaml
on:
push:
tags:
- "v*"
name: Create Release
jobs:
build:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Get version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/}
############### 省略 ###############
# リリース用のブランチを生成
- name: Create Release Branch
id: create_release_branch
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git checkout pre-production
git checkout -b release/${{ steps.get_version.outputs.VERSION }}
git push origin release/${{ steps.get_version.outputs.VERSION }}
# 作成したリリースブランチからprdブランチへのPRを生成
- name: Create Prd Release Pull Request
id: create_release_pr
uses: repo-sync/pull-request@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
pr_title: Production Release ${{ steps.get_version.outputs.VERSION }}
pr_label: production-deploy
destination_branch: production
source_branch: release/${{ steps.get_version.outputs.VERSION }}
staging環境へのデプロイは,前回と同じく,cloud buildのトリガーを用います.
また,iosアプリの方のCDについてはまた次回以降で書く予定です.
productionへのデプロイ
productionへのデプロイは,以下の2つのステップに分かれています.
- ネイティブアプリの審査提出
- apiのデプロイ
ネイティブアプリの審査への提出は常に行われますが,apiのデプロイは
ケースによって審査に必須であったり必要なかったりします(バージョン整合のため).
なので,アプリの審査提出は自動化し,apiのデプロイは手動で行うこととします.
ネイティブアプリの審査提出に関しては,また次回以降で解説します.
apiのデプロイに関しては,前回と同様,release/prd下のスクリプトを叩くことで行います.
#!/bin/bash
function deploy() {
gcloud beta builds triggers run --tag $2 service-prd-api-trigger
}
$1 $@
cd release/prd
sh deploy.sh deploy v0.0.0
これで通常のCDフローは完了です.
hotfix
次はhotfixへの対応です.
hotfixというprefixを持つブランチがpushされた時,自動でmainとstagingブランチへのPRを生成するようにします
(ぼーっとしてるとどっちかを入れ忘れるため).
コードは以下のような形です.
# hotfix.yaml
on:
push:
branches: ["hotfix/*"]
env:
BRANCH_NAME: $(echo ${GITHUB_REF#refs/heads/})
jobs:
create_hotfix_pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Main Create Hotfix Pull Request
id: main_create_hotfix_pr
uses: repo-sync/pull-request@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
pr_title: "Main ${{ env.BRANCH_NAME }}"
pr_label: "hotfix"
destination_branch: main
- name: Pre-Production Create Hotfix Pull Request
id: preproduction_create_hotfix_pr
uses: repo-sync/pull-request@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
pr_title: "Pre-Production ${{ env.BRANCH_NAME }}"
pr_label: "hotfix"
destination_branch: staging
これで全体の構築が完了です.
その他細かい知見など
- git-pr-releaseはsquashマージと通常マージの両方に対応可能.
- mainからstagingブランチへのマージをsquashにしてしまうと,リリースノートがsquashコミットだけになって
差分の確認がしづらくなるので,ここは通常マージにしたほうが良い(めんどいので,もっといい方法ないかな...). - hotfixとmainでコンフリクトが起きた時の対処がよくわかっていない...
最後に
GitHub flowで起きる問題を解決した,GitLab flowの実装解説をしました.
といっても今回解説したのだと,stagingにアプリが上がってるときにhotfixが必要になった場合
またリバートしなくてはならないので,本当にこの問題を対策するときはstagingやmainへのマージを慎重に行ったり,apiデプロイするようにしたりなど追加の検討が必要そうです.
ただ柔軟性が高く拡張しやすいので,アプリならば最初から採用しておくのが楽かな〜という所感でした.
省略したアプリのCD周りなどについては,次回以降に書く予定です.
Discussion