<目次>
Azure DevOpsからAzure App Serviceへ自動デプロイする最短CI/CD構築ガイド
この記事のねらい
STEP1:前提準備
STEP2:サービス接続の作成
STEP3:YAML パイプライン作成
STEP4:App Service の追加設定
STEP5:パイプライン初回実行
STEP6:デプロイ確認
STEP7:スワップ確認(staging → production)
その他:失敗時の切り分け
Azure DevOpsからAzure App Serviceへ自動デプロイする最短CI/CD構築ガイド
この記事のねらい
- Azure DevOps のリポジトリにコミットすると、Azure App Service(Web App)へ自動でデプロイされる“最短のしくみ”を作ります。
- Deployment Center は使わず、YAMLパイプライン+サービス接続で統一。スロット運用(staging→production)と、Free プラン直デプロイの両パターンに触れます。
できること
- Azure DevOps → App Service への YAML マルチステージ(Build → Deploy → Swap)
- サービス接続(ARM, Workload identity/手動SPどちらでも)作成の勘所
- Free プランでの妥協ポイントと設定の切替
やらないこと
- 複雑なマルチ環境(dev/test/prod)の運用設計
- IaC(Bicep/Terraform)によるフル自動化の深掘
STEP1:前提準備
● Azure App Service(Web App)を作成済み
基本はデフォルト設定で進めつつ、以下これだけ押さえれば「後で面倒にならない」設定のみを記載。
1. 基本
- Runtime stack: LTS版(例:.NET 8 / Node 18 / Python 3.11)
- OS: Linux(Windows依存があればWindows)
2. App Service プラン
- S1以上推奨(Always Onやスロットが使える)
3. デプロイ設定
- 継続的デプロイ: 無効(Deployment Centerは使わない) → Azure Pipelinesで管理するため、二重トリガー防止
4. 監視
- Application Insights: 有効(同一リージョンのワークスペース推奨)
(図111)
● Azure DevOps プロジェクト&リポジトリあり
(図112)
● テスト用の資産を登録
. ├─ azure-pipelines.yml # 既に用意したYAML ├─ app.py # Flaskアプリ本体 ├─ requirements.txt # 依存ライブラリ └─ .gitignore # 任意
① app.py
from flask import Flask # Flaskアプリのインスタンスを作成 app = Flask(__name__) # ルートURL用のハンドラ @app.get("/") def hello(): return "Hello from Azure App Service (New Production)!", 200 # ヘルスチェック用エンドポイント(App ServiceのHealth checkで利用可能) @app.get("/health") def health(): return "OK", 200
② requirements.txt
flask gunicorn
(図113)
● 権限:Azureのサブスクリプション共同作成者/所有者、Azure DevOpsのProject Administrator
→ 割愛
STEP2:サービス接続の作成
● Project Settings → Service connections → New service connection
(図211)
● 種別:Azure Resource Manager → Identity type: App registration (automatic)
● サブスクリプションとスコープ(該当Web AppのResource Group)を選ぶ
● 名前例:sc-azure-sub(後でYAMLに使います)
(図221)
(図222)
(図223)
>目次にもどる
STEP3:YAML パイプライン作成
● ローカルでYAML作成
• パイプラインYAMLサンプルを参考に azure-pipelines.yml という名前で保存します。
• リポジトリのルート直下に置くのが一番シンプルです。
```yaml # ================================================ # Azure DevOps → Azure App Service (Python) CI/CD # - main に push → ビルド → スロット(staging)へデプロイ → 本番へスワップ # - スロットを使わない場合は末尾の「Swap」ステージを削除し、 # 変数 slotName を 'production' にしてください。 # ================================================ # main ブランチにコミットされたら自動実行 trigger: branches: include: - main # 変数(あとで上書きしやすいようまとめる) variables: vmImage: 'ubuntu-latest' # ビルドエージェントOS azureServiceConnection: 'sc-azure-sub' # Service connection 名(Azure DevOpsで作成済みのもの) webAppName: 'my-webapp-prod' # AzureのApp Service(Web App)の名前 resourceGroup: 'rg-myapp-prod' # Web App が属するリソースグループ slotName: 'staging' # デプロイ先スロット名(直本番なら 'production')(▲) packagePath: '$(Build.ArtifactStagingDirectory)/drop.zip' # Zip Deploy で使うパッケージの保存先 stages: # ========================= # 1) Build & Package # ========================= - stage: Build displayName: Build & Package (Python) pool: vmImage: $(vmImage) jobs: - job: BuildPython displayName: Build Python app and create zip steps: # 【ポイント】Azure DevOpsのビルドマシンに指定バージョンのPythonを入れる - task: UsePythonVersion@0 displayName: 'Use Python 3.11' inputs: versionSpec: '3.11' # App Service 側のランタイムと合わせるのが無難 # 依存ライブラリのインストールと成果物のステージング - script: | set -e # 途中でエラーになったら終了(見落とし防止) python -m pip install --upgrade pip # ビルド成果物(後でzip化する中身)を置くフォルダを用意 mkdir -p $(Build.ArtifactStagingDirectory)/app # requirements.txt がある場合は、その内容を "app" フォルダにインストール # → App Service へ zip で送ると、そのまま実行に必要なライブラリが入っている状態になる if [ -f requirements.txt ]; then pip install -r requirements.txt -t $(Build.ArtifactStagingDirectory)/app fi # プロジェクトのソースコードを "app" フォルダへコピー # rsync を使うと不要なものを除外しやすい(tests や .venv など) rsync -av \ --exclude '.git' \ --exclude '.venv' \ --exclude 'tests' \ ./ $(Build.ArtifactStagingDirectory)/app/ displayName: 'Install deps & stage app files' # "app" フォルダの中身を zip に固める(App Service の Zip Deploy で使う形式) - task: ArchiveFiles@2 displayName: 'Archive app → drop.zip' inputs: rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/app' # zipに含める中身 includeRootFolder: false # 直下のファイル/フォルダのみ含める archiveType: 'zip' archiveFile: '$(packagePath)' # 変数で指定した zip 出力先 # 後段の Deploy ステージから取得できるよう、ビルド成果物として発行 - publish: $(Build.ArtifactStagingDirectory) displayName: 'Publish build artifact: drop' artifact: drop # ========================= # 2) Deploy to Slot (staging) # ========================= - stage: Deploy displayName: "Deploy to App Service (slot: $(slotName))" dependsOn: Build # Build が成功したら実行 condition: succeeded() # 失敗したら止める jobs: - deployment: DeployWebApp displayName: 'Zip Deploy to $(webAppName)/$(slotName)' environment: 'prod-staging' # Azure DevOpsのEnvironment名(承認ゲート等に使える) strategy: runOnce: deploy: steps: # 直前の Build ステージで発行した成果物(drop)を取得 - download: current artifact: drop # Azure App Service へ zip をデプロイ - task: AzureWebApp@1 displayName: 'AzureWebApp@1 Zip Deploy' inputs: azureSubscription: $(azureServiceConnection) # Service connection 名 appName: $(webAppName) # Web App 名 slotName: $(slotName) # スロット(未使用なら削除)(▲) package: '$(Pipeline.Workspace)/drop/drop.zip' # 上で作った zip のパス # ========================= # 3) Slot Swap → production(▲) # ========================= - stage: Swap displayName: Slot Swap to Production dependsOn: Deploy condition: succeeded() jobs: - job: SwapSlots displayName: 'Swap $(slotName) → production' steps: # スロットを production と入れ替える(Blue/Green 的な切替) - task: AzureAppServiceManage@0 displayName: 'AzureAppServiceManage@0 Swap Slots' inputs: azureSubscription: $(azureServiceConnection) # Service connection 名 WebAppName: $(webAppName) # Web App 名 ResourceGroupName: $(resourceGroup) # RG名(Swapタスクは必要) SourceSlot: $(slotName) # ここで指定したスロットを production と入替 Action: 'Swap Slots'
【重要】FreeやBasicの際は Swap ステージ ((3) Slot Swap → production(▲)の部分) を丸ごと削除し、slotName を ‘production’ に変更してください。
(図321)
● Gitでpush
git add azure-pipelines.yml git commit -m "Add Azure Pipelines definition" git push origin main
(図322)
(図323)
● Pipelines → Create pipeline → “Azure Repos Git” → “Existing Azure Pipelines YAML file”
(図311)
(図312)
(図313)
(図314)
(図324)
>目次にもどる
STEP4:App Service の追加設定
● Startup command を設定(gunicorn 起動)
Flask 例:gunicorn app:app(app.py に app = Flask(__name__) がある想定)
(図411)
● staging スロットを作成
(図412)
(図413)
STEP5:パイプライン初回実行
● Azure DevOps → Pipelines → 作成したパイプライン → Run pipeline
- Branch: main(必要なら変更)
- Parameters: なし(YAMLの変数で運用)
(図511)
(図512)
(図513)
(図514)
- Build(Pythonの依存解決→zip作成→artifact公開)
- Deploy(Zip Deploy が成功するか)
- Swap(staging→productionの入替)※スロット未使用ならこのステージは無し

STEP6:デプロイ確認
● Azure DevOps(Deployジョブ)
- AzureWebApp@1 のログに Successfully deployed が出るか
(図611)
(図612)
● Azure ポータル(App Service / 該当スロット)
- 診断 > ログストリーム:起動ログにエラーがないか
- Application Insights > Live Metrics:リクエストが来るとカウントされる
- URLにアクセス(stagingのURLでOK):アプリが応答するか
・https://-staging.azurewebsites.net/ ・直前までproductionだった“旧ビルド”が残っています
(図621)
STEP7:スワップ確認(staging → production)
●パイプラインの Swap ステージが成功することを確認
(図711)
●スワップ後、production URL でもアプリ動作を確認
(図721)
>目次にもどる
その他:失敗時の切り分け(超要約)
-
Build 失敗:requirements.txt の解決失敗/Pythonバージョン不一致 -
Deploy 失敗:サービス接続の権限不足/App 名/スロット名のタイプミス -
起動失敗(200出ない):Startup command(gunicorn の <module>:<app>)ミス/依存不足 -
Swap 後だけ不具合:本番だけの環境変数や接続先がスロット固有になっていない