Rainbow Engine

IT技術を分かりやすく簡潔にまとめることによる学習の効率化、また日常の気付きを記録に残すことを目指します。

Azure Microsoft Python

Azure DevOpsからAzure App Serviceへ自動デプロイする最短CI/CD構築ガイド

投稿日:2025年8月14日 更新日:


<目次>

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)

●Stagesの進捗を確認
  • Build(Pythonの依存解決→zip作成→artifact公開)
  • Deploy(Zip Deploy が成功するか)
  • Swap(staging→productionの入替)※スロット未使用ならこのステージは無し
(図612)

目次にもどる

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 後だけ不具合:本番だけの環境変数や接続先がスロット固有になっていない

Adsense審査用広告コード


Adsense審査用広告コード


-Azure, Microsoft, Python
-

執筆者:


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

関連記事

Azure Synapse AnalyticsでRestAPIのデータを取得する方法

  <目次> Azure Synapse AnalyticsでRestAPIのデータを取得する方法  やりたいこと/概要  STEP0:前提条件  STEP1:リンクサービスの作成(例)   …

Azure Synapse AnalyticsのパイプラインからREST APIを呼び出す方法

  <目次> Azure Synapse AnalyticsのパイプラインからREST APIを呼び出す方法  やりたいこと/概要  STEP0:前提  STEP1:事前準備  STEP2: …

PythonでPandasライブラリを用いてcsvファイルを読み込む方法

<目次> (1) PythonでPandasライブラリを用いてcsvファイルを読み込む方法  (1-1) 構文  (1-2) サンプルプログラム  (1-3) read_csvの主要なオプションご紹介 …

Pythonで複数の画像を結合する方法をご紹介

<目次> (1) Pythonで複数の画像を結合する方法  (1-1) STEP1:Pillowのインストール  (1-2) STEP2:画像の読み込み  (1-3) STEP3:画像を結合  (1- …

Python3.5&Anaconda4.2のインストール手順(CentOS6)とHelloWorldの方法

(0)目次&概説 (1) インストール資源の入手  (1-1) ディストリビューション  (1-2) GitHubのpyenvのリポジトリのコピーを取得する (2) インストールの実施  (2-1) …

  • English (United States)
  • 日本語
Top