はじめに
開発環境でのアプリケーションの機能実装が完了すると、次に控えているのがステージング環境や本番環境へのデプロイ作業です。手順自体は決して複雑ではないものの、新しいイメージをプッシュして、アプリを再起動して、動作確認して……と、毎回手動で行うとなると地味に手間がかかります。
その“ちょっとした手間”を減らすために、GitHub Actions を使って デプロイ作業を自動化する CI/CD パイプラインを構築しました。こちらは今回の構築でやったことの備忘録です。
今回やったことはこちらの2つです。
①Azure Container Registry(ACR)へのイメージプッシュを自動化
②新イメージ適用のための Azure App Service の再起動を自動化
所属する開発チームのプロジェクトはリリース頻度が高いため、"ちりつも"ではありますが今回の改善で月あたり約4.5時間の時間削減につながりました。
この記事が日々業務改善を求められている人たちの参考になれば幸いです。
お知らせ
内容が少し長くなってしまったので先にお知らせです。
こちらの記事は ctc Advent Calendar 2025 の記事です。
12月も残りわずかになりました。これまでにも ctc(中部テレコミュニケーション株式会社)のメンバーが技術にまつわる知見を投稿していますので、ぜひご覧ください。
①でやったこと
Azure Container Registryの連携準備
ワークフローでAzure Container Registry(ACR)にアクセスできるようにするため、azure/docker-login@v1でログインをします。
これに必要なusernameとpasswordを、GithubのSecretsに登録していきます。
2. Githubで対象リポジトリのSettings > Secrets and variables > Actionsを開き、
「New repository secret」からSecretsを2つ登録します。
※一度登録すると登録した内容を確認できないので注意!
「Github Enterprise」を利用している場合は、Organization secretsに登録するのがおすすめです。Organization secretsは複数のリポジトリで同じSecretsを使うことができるため、リポジトリごとの登録が不要になります!
GitHub Actions のワークフロー作成
今回は複数リポジトリに自動デプロイを適用させたかったので、リポジトリ共通で利用できるワークフローを独立したリポジトリに配置し、各リポジトリから共通ワークフローを呼び出すアプローチを取りました。
これによりワークフローのメンテナンスもしやすくなります。(実際に共通化をしておいたおかげで②の改修の手間も少なく済みました)
呼び出し元ワークフロー(各リポジトリに配置)
name: Trigger ACR Deployment
on:
push:
branches:
- main
- develop
- production
jobs:
check-deployment:
runs-on: ubuntu-latest
outputs:
dockerfile_exists: ${{ steps.check.outputs.dockerfile_exists }}
repo_name: ${{ steps.check.outputs.repo_name }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check Dockerfile and extract repository name
id: check
run: |
if [ -f Dockerfile ]; then
echo "dockerfile_exists=true" >> $GITHUB_OUTPUT
echo "repo_name=${GITHUB_REPOSITORY##*/}" >> $GITHUB_OUTPUT
else
echo "dockerfile_exists=false" >> $GITHUB_OUTPUT
echo "repo_name=" >> $GITHUB_OUTPUT
fi
deploy-to-acr:
needs: check-deployment
if: ${{ needs.check-deployment.outputs.dockerfile_exists == 'true' }}
uses: [共通ワークフローのymlファイルを指定]
with:
image_name: xxxx
secrets:
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
詳細な解説は割愛しますが、呼び出し元ワークフローのポイントは以下の2点です。
- 指定ブランチへの push をトリガーにワークフローを起動
- Secretsは呼び出し元ワークフローから渡す(共通ワークフロー側での設定は不可)
共通ワークフロー(独立リポジトリに配置)
name: Deploy to Azure Container Registry
on:
workflow_call:
inputs:
image_name:
description: 'リポジトリ名'
required: true
type: string
secrets:
REGISTRY_USERNAME:
required: true
REGISTRY_PASSWORD:
required: true
jobs:
deploy-and-restart:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Login to Azure Container Registry
uses: azure/docker-login@v1
with:
login-server: <ログインサーバー>
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build and push Docker image
shell: bash
run: |
if [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
echo "Building image for main branch..."
docker build . -t <ログインサーバー>/${{ inputs.image_name }}:latest
docker push <ログインサーバー>/${{ inputs.image_name }}:latest
elif [[ "${GITHUB_REF}" == "refs/heads/develop" ]]; then
echo "Building image for develop branch..."
docker build . -t <ログインサーバー>/${{ inputs.image_name }}:develop
docker push <ログインサーバー>/${{ inputs.image_name }}:develop
elif [[ "${GITHUB_REF}" == "refs/heads/production" ]]; then
echo "Building image for production branch..."
DATE_TAG=$(date +'%Y%m%d')
docker build . -t <ログインサーバー>/${{ inputs.image_name }}:production -t <ログインサーバー>/${{ inputs.image_name }}:${DATE_TAG}
docker push <ログインサーバー>/${{ inputs.image_name }}:production
docker push <ログインサーバー>/${{ inputs.image_name }}:${DATE_TAG}
else
echo "No matching branch found. Skipping build."
fi
今回の共通ワークフローのざっくりとした流れは、
- 呼び出し元ワークフローから受け取ったSecret情報でACRにアクセス
- main / develop ブランチへのマージの場合:ACRに指定タグでプッシュ
- production ブランチへのマージの場合:ACRに指定タグ&日付タグでプッシュ
になります。フロー内の<ログインサーバー>部分には、さきほど控えたxxx.azure.ioが入ります。
ここで重要な設定をします。
共通ワークフローを独立したリポジトリに配置したので、組織内の他リポジトリからのアクセスを許可する必要があります。
GithubでSettings > Actions > Generalを開き、「Accessible from repositories in the '[組織名]'」を選択します。(Saveをお忘れなく!)

②でやったこと
サービスプリンシパルの準備
Azure App ServiceをGithub Actionsから再起動をするには、再起動ができる権限でアクセスする必要があります。①ではACRにのみアクセスできる権限でログインをしていたので、ここで新たに設定をしていきます。
今回は同じリソースグループにある複数のAzure App Serviceが対象だったので、そのリソースグループの共同作成者権限をもつサービスプリンシパルを利用してazure/login@v1でログインをします。
必要情報は以下の4つです。①でもあったSecrets登録と同じ手順で登録します。
-
AZURE_CLIENT_ID(Azure上では「アプリケーションID」と表示) -
AZURE_CLIENT_SECRET(サービスプリンシパル作成時のみ表示されるシークレット値) -
AZURE_SUBSCRIPTION_ID(対象のサブスクリプションID) -
AZURE_TENANT_ID(対象のテナントID)
ワークフローの追加
さきほど①で作成した2つのワークフローをそれぞれ編集します。
今回は develop ブランチへのマージがあったときのみ、ステージング環境を自動再起動するフローを追加しています。
呼び出し元ワークフローの編集
deploy-to-acr:
needs: check-deployment
if: ${{ needs.check-deployment.outputs.dockerfile_exists == 'true' }}
uses: xxx
with:
image_name: xxx
app_service_name: xxx #ステージング環境を指定
resource_group: xxx
secrets:
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
呼び出し元ワークフローには、ステージング環境を指定するための変数app_service_nameとresource_group、そしてさきほど登録したSecretsを渡すように追記しました。
共通ワークフローの編集
name: Deploy to Azure Container Registry
on:
workflow_call:
inputs:
image_name:
description: 'リポジトリ名'
required: true
type: string
app_service_name:
description: 'Azure App Service名'
required: false
type: string
resource_group:
description: 'リソースグループ名'
required: false
type: string
secrets:
REGISTRY_USERNAME:
required: true
REGISTRY_PASSWORD:
required: true
AZURE_CLIENT_ID:
required: false
AZURE_CLIENT_SECRET:
required: false
AZURE_TENANT_ID:
required: false
AZURE_SUBSCRIPTION_ID:
required: false
jobs:
deploy-and-restart:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
#<中略>
# App Service再起動(stagingのみ)
- name: Azure Login
if: github.ref == 'refs/heads/develop' && inputs.app_service_name != ''
uses: azure/login@v1
with:
creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}'
- name: Wait for image propagation
if: github.ref == 'refs/heads/develop' && inputs.app_service_name != ''
shell: bash
run: |
echo "⏳ イメージの更新を待機中... (60秒)"
sleep 60
echo "✅ 待機完了"
- name: Restart App Service
if: github.ref == 'refs/heads/develop' && inputs.app_service_name != ''
shell: bash
run: |
echo "🔄 App Service再起動開始"
echo "対象リソースグループ: ${{ inputs.resource_group }}"
echo "対象App Service名: ${{ inputs.app_service_name }}"
# App Serviceの存在確認
echo "📋 対象App Serviceの確認中..."
az webapp show --name ${{ inputs.app_service_name }} --resource-group ${{ inputs.resource_group }} --query "name" -o tsv
# 再起動実行
echo "🔄 再起動を実行します..."
az webapp restart \
--name ${{ inputs.app_service_name }} \
--resource-group ${{ inputs.resource_group }}
echo "✅ App Service再起動完了"
共通ワークフローには、develop ブランチにプッシュされている、かつ、ステージング環境のアプリ名が渡されている場合に指定されたAzure App Serviceを再起動するフローを追加しました。
また、ACRに新イメージがプッシュされてすぐに再起動を実施するとビルドが間に合ってないことがあったため、再起動前に60秒の待機時間を設けています。
ワークフローのテスト
ワークフローの動作はGithubの「Actions」から確認できます。
共通ワークフローの呼び出しからアプリの再起動まで、ブランチのマージをトリガーに自動で完了しました(達成感)。
まとめ
ここまでお読みいただきありがとうございます。
今回は、GitHub Actions を使って ACR へのプッシュと App Service の再起動を自動化したことで、デプロイ作業の手間を減らすことができました。
それだけでなく、手作業によるヒューマンエラーのリスクを減らすことができたのも自動化してよかったポイントですね。
今後もできるところから改善をして、より快適な開発環境を整えていければと思います。



