概要
仕事でGitHub Actionsを用いて環境別にCDを構築する方法について構築したので、残しておこうと思います。
また、仕事ではCircleCIからGitHub Actionsへの移行を行いましたので、そちらの参考にしたものについてもついても以下に纏めておきます。
移行について
使用したツール: GitHub Actions Importer
以下の公式リファレンスがわかりやすかったです。
gh actions-importer audit circle-ci --output-dir tmp/audit
のコマンドを使用するとaudit_summary.md
に変換率が吐き出され、変換後ファイルも出力されました。
CircleCI パイプラインでサポートされている構文や環境変数のマッピングを参考に変換しきれていない部分について書き換えを行なえば完了です!
環境別での構築について
さて、ここからが本題です。
TWELVE-FACTOR APPでは環境を分ける意味についてこのようにあります。
アプリケーションは時に設定を定数としてコード内に格納する。これはTwelve-Factorに違反している。Twelve-Factorは 設定をコードから厳密に分離すること を要求する。設定はデプロイごとに大きく異なるが、コードはそうではない。
引用元: TWELVE-FACTOR APP
この考え方を意識すると、同一のワークフロー(ソースコード)を使用して検証環境や本番環境にリリースするのが良いということになります。
上記を踏まえて、GitHub Actionsで環境を選択してデプロイができるようになるワークフローを構築してきます。
Step:1 Jobの実行トリガーを設定する
GitHub Actionsではpush
やPR作成
などをトリガーとしてjobを起動させることができます。
今回は手動でjobを実行させるため、workflow_dispatch
を使用して設定を行います。このトリガーはinputs
を設定することで、外部から値を入力することができるようになります。
on:
workflow_dispatch:
inputs:
selected_environment: #他にはenvironmentを使用することもできる。
description: 'デプロイ環境を選択してください。'
required: true
default: '---'
type: choice
options:
- development
- stage
- production
Step2: Jobを2つに分ける
Jobを2つに分けるとはなんぞや?と思われるでしょう。
ここでは、デプロイの準備をするJobとデプロイを実行するJobを作成していきます。
まずはじめに、準備用のJobを作成します。
env:
SELECTED_ENVIRONMENT: ${{ github.event.inputs.selected_environment }}
jobs:
pre_deploy:
runs-on: ubuntu-latest
outputs: #環境毎に設定したい値は基本的にここで設定する。
WORKING_BRANCH: ${{ env.WORKING_BRANCH }} #deployジョブ内でcheckoutする時に使用する
TOKEN_NAME: ${{ env.TOKEN_NAME }}
steps:
- name: Setting development environment
if: env.SELECTED_ENVIRONMENT == 'development'
run: |
echo 'WORKING_BRANCH=develop' >> $GITHUB_ENV
echo 'TOKEN_NAME=DEVELOP' >> $GITHUB_ENV
- name: Setting staging environment
if: env.SELECTED_ENVIRONMENT == 'stage'
run: |
echo 'WORKING_BRANCH=stage' >> $GITHUB_ENV
echo 'TOKEN_NAME=STAGE' >> $GITHUB_ENV
- name: Setting production environment
if: env.SELECTED_ENVIRONMENT == 'production'
run: |
echo 'WORKING_BRANCH=master' >> $GITHUB_ENV
echo 'TOKEN_NAME=PRODUCTION' >> $GITHUB_ENV
ここで、行なっていることはworkflow_dispatch
で選択した値に一致する場合、環境変数に値を代入し、それをoutputsに設定するということです。
環境変数に設定しているのは、ワークフロー内で重複定義ができるためです。
outputsに設定しているのは後続のデプロイを実行するJobから参照できるようにするためとなります。
次に、デプロイを実行するJobを作成します。
deploy:
runs-on: ubuntu-latest
needs: pre_deploy
steps:
- name: Confirm TOKEN_NAME
run: echo ${{ env[format('DEPLOY_{0}', needs.pre_deploy.outputs.TOKEN_NAME)] }}
env:
DEPLOY_DEVELOP: 'development'
DEPLOY_STAGE: 'stage'
DEPLOY_PRODUCTION: 'production'
- name: Checkout branch
uses: actions/checkout@v3
with:
ref: ${{ needs.pre_deploy.outputs.WORKING_BRANCH }}
#ブランチのcheckoutが出来たので、以下にデプロイ処理を記述していく。
ここで鍵となるのは以下の3点となります。
needs: pre_deploy
${{ needs.pre_deploy.outputs.WORKING_BRANCH }}
${{ env[format('DEPLOY_{0}', needs.pre_deploy.outputs.TOKEN_NAME)] }}
1つずつ解説していきます。
needs: pre_deploy
これを設定することにより、Jobに依存関係が発生します。
pre_deployが実行されない場合、deployも実行されなくなります。
${{ needs.pre_deploy.outputs.WORKING_BRANCH }}
依存関係があるJobでは、needs.ジョブ名.outputs.変数名
で依存先のoutputsに設定されている変数の値を参照することができるようになります。
これを用いて、pre_deployで設定した値を参照できるようにしています。
${{ env[format('DEPLOY_{0}', needs.pre_deploy.outputs.TOKEN_NAME)] }}
これは、値を設定する応用的な使い方となります。
基本的には上記の方法を用いて値を設定していけば問題ないのですが、Secretsに登録された値を環境毎に環境変数に設定する場合echo
コマンドを使用することはできません。
そのため、悩んでいた所、チームリーダーの方が以下の記事を教えてくださいました。
GitHub Actionsでは${{ env.hoge }}
と${{ env[hoge] }}
は同じ意味となります。また、format関数を使用することもできます。
format関数はformat('hoge{0}', hoge)
のように記述すると第1引数内の{0}
に第2引数を代入して返してくれます。
そのため、前述のものはhogehoge
となります。
これらを踏まえて読み解くと、developの場合は${{ env[DEPLOY_DEVELOP] }}
となり、echo
コマンドを使用しないで環境毎に動的に値を変更できるようになるのです。
Step:3 デプロイ処理を記述していく
ここまでのStep:1, Step:2を参考に値を設定できたらあとはデプロイ処理を記述していくだけとなります。
今回は説明に入れませんでしたが、Cacheを使用するなどして時間短縮を図るのも良いかもしれません。
全体像
name: Deployment
on:
workflow_dispatch:
inputs:
selected_environment: #他にはenvironmentを使用することもできる。
description: 'デプロイ環境を選択してください。'
required: true
default: '---'
type: choice
options:
- development
- stage
- production
env:
SELECTED_ENVIRONMENT: ${{ github.event.inputs.selected_environment }}
jobs:
pre_deploy:
runs-on: ubuntu-latest
outputs: #環境毎に設定したい値は基本的にここで設定する。
WORKING_BRANCH: ${{ env.WORKING_BRANCH }} #deployジョブ内でcheckoutする時に使用する
TOKEN_NAME: ${{ env.TOKEN_NAME }}
steps:
- name: Setting development environment
if: env.SELECTED_ENVIRONMENT == 'development'
run: |
echo 'WORKING_BRANCH=develop' >> $GITHUB_ENV
echo 'TOKEN_NAME=DEVELOP' >> $GITHUB_ENV
- name: Setting staging environment
if: env.SELECTED_ENVIRONMENT == 'stage'
run: |
echo 'WORKING_BRANCH=stage' >> $GITHUB_ENV
echo 'TOKEN_NAME=STAGE' >> $GITHUB_ENV
- name: Setting production environment
if: env.SELECTED_ENVIRONMENT == 'production'
run: |
echo 'WORKING_BRANCH=master' >> $GITHUB_ENV
echo 'TOKEN_NAME=PRODUCTION' >> $GITHUB_ENV
deploy:
runs-on: ubuntu-latest
needs: pre_deploy
steps:
- name: Confirm TOKEN_NAME
#ここではenvですが、secretsで活用する想定
run: echo ${{ env[format('DEPLOY_{0}', needs.pre_deploy.outputs.TOKEN_NAME)] }}
env:
DEPLOY_DEVELOP: 'development'
DEPLOY_STAGE: 'stage'
DEPLOY_PRODUCTION: 'production'
- name: Checkout branch
uses: actions/checkout@v3
with:
ref: ${{ needs.pre_deploy.outputs.WORKING_BRANCH }}
#ブランチのcheckoutが出来たので、以下にデプロイ処理を記述していく。
まとめ
今回は仕事でGitHub Actionsを活用してCD環境を整備した際に学んだことを備忘録的にまとめました。
Jobの依存関係などを活用すればCI/CDの構築なども簡単に行えるようになると思いますので、チャレンジして見たいと思います。