Azure Pipelines を使用してAzure App Service にアプリケーションをデプロイしてみた

Azure Pipelines を使用してAzure App Service にアプリケーションをデプロイしてみた

Clock Icon2025.03.12

はじめに

コンサル部の神野です。皆さんはAzure Pipelinesをご存知ですか。
Azure DevOps Services(以下Azure DevOps)の1機能で、CI / CDパイプラインを構築することが可能になります。

今回は実際にAzure App Service(以下App Service)に対してアプリケーションをデプロイを試してみて、どう言ったやり方でデプロイが実施できるのか理解したく記事にしてみました。

Azure DevOpsについて

Azure DevOpsとは、Microsoftが提供する統合型のDevOpsツールセットで、ソフトウェア開発の全ライフサイクルをサポートします。コードのバージョン管理、CI/CDパイプラインの自動化、作業管理、テスト、さらにはパッケージ管理といった機能を一つのプラットフォームで提供しているサービスとなります。

具体的には、以下の主要機能が内包されています。

  • Azure Boards
    タスク、フィーチャー、バグなどの作業項目を管理するツール。スクラムやカンバンボードによるアジャイルなワークフローで使用
  • Azure Repos
    GitやTFVCを利用したソースコード管理を提供
  • Azure Pipelines
    ビルド、テスト、デプロイといったCI/CDパイプラインの自動化を実現
  • Azure Test Plans
    手動および自動テストの設計や実行、結果の管理を行うツール。
  • Azure Artifacts
    NuGet、npm、Mavenなどのパッケージ管理システムを統合。

これらの機能が一体となることで、Azure DevOpsはコードの開発からテスト、デプロイに至るまで、エンドツーエンドのプロセス管理を可能にしています。さらに、クラウドベースのサービスとして提供されるため、柔軟なスケーリングや迅速な環境構築が行える点も魅力の1つです。

本記事では、そんなAzure DevOpsの中でも特にAzure Pipelinesに注目し、App Serviceへのデプロイを通じて色々と触っていきたいと思います。

今回作成するシステム構成図

今回作成する環境は下記となります。

CleanShot 2025-02-16 at 01.54.22@2x

作成するリソース

App Serviceでアプリケーションをホストして、その環境にデプロイするようAzure Pipelinesの設定を入れていきます。App Serviceの1機能として提供されているデプロイスロットについては補足をご参照ください。

  • Azure
    • App Service
      • アプリケーションサーバーとして使用。プランはS1(Standard)を想定。
        • デプロイスロットはBlue/Greenデプロイを実施出来るよう2つ用意
    • Azure Container Registry(以下ACR)
      • コンテナイメージを格納
  • Azure DevOps
    • Azure Repos
      • アプリケーションのソースコードを格納
    • Azure Pipelines
      • デプロイ用のパイプラインを構築
        • Azure ReposのmasterブランチにソースコードがPushされたら、パイプラインが起動して下記ステップで起動
          1. コンテナイメージをビルド
          2. コンテナイメージをAzure Container Registryへプッシュ

デプロイの流れ

以下のステップでデプロイを進めていきます。

  1. 開発者がソースコードをAzure Reposにプッシュ
  2. Azure Reposの変更を検知し、Azure Pipelinesが起動
  3. Azure Pipelinesでコンテナイメージをビルド & Azure Container Registryへプッシュ
  4. Azure Container Registryにコンテナイメージがプッシュされる
  5. Azure App ServiceのGreenスロットへコンテナイメージをデプロイ
  6. Greenスロットで動作確認を実施
  7. 動作確認が完了したら、BlueスロットとGreenスロットをスワップし変更を本番環境へ適応

Azure 環境の作成

前提

今回はTerraformを使用するため事前にインストールが必要になります。
使用したバージョンは下記となります。

  • Terraform・・・v1.9.4(provider registry.terraform.io/hashicorp/azurerm v4.4.0)

またAzure環境は既に存在し、サブスクリプションおよびリソースグループに対して自身の権限はあるものとします。

環境作成

今回Terraformを使用して環境を作成します。
コードは下記となります。

main.tf
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=4.4.0"
    }
  }
}

provider "azurerm" {
  subscription_id                 = var.subscription_id
  resource_provider_registrations = "none"
  features {}
}

# 既存のリソースグループを参照
data "azurerm_resource_group" "rg" {
  name = var.resource_group_name
}

# ACRの作成
resource "azurerm_container_registry" "acr" {
  name                = "applicationregistry"
  resource_group_name = data.azurerm_resource_group.rg.name
  location            = data.azurerm_resource_group.rg.location
  sku                 = "Basic"
  admin_enabled       = true
}

# App Service Planの作成
resource "azurerm_service_plan" "app_plan" {
  name                = "app-service-plan"
  resource_group_name = data.azurerm_resource_group.rg.name
  location            = data.azurerm_resource_group.rg.location
  os_type             = "Linux"
  sku_name            = "S1"
}

# App Serviceの作成
resource "azurerm_linux_web_app" "app" {
  name                = "sample-app-yjinno-test"
  resource_group_name = data.azurerm_resource_group.rg.name
  location            = data.azurerm_resource_group.rg.location
  service_plan_id     = azurerm_service_plan.app_plan.id

  site_config {
    always_on = false
    application_stack {
      docker_image_name        = "${azurerm_container_registry.acr.login_server}/myapp:latest"
      docker_registry_url      = "https://${azurerm_container_registry.acr.login_server}"
      docker_registry_username = azurerm_container_registry.acr.admin_username
      docker_registry_password = azurerm_container_registry.acr.admin_password
    }
  }

  app_settings = {
    "WEBSITES_ENABLE_APP_SERVICE_STORAGE" = "false"
  }
}

resource "azurerm_linux_web_app_slot" "green" {
  name           = "green-slot"
  app_service_id = azurerm_linux_web_app.app.id

  site_config {
    always_on = false
    application_stack {
      docker_image_name        = "${azurerm_container_registry.acr.login_server}/myapp:latest"
      docker_registry_url      = "https://${azurerm_container_registry.acr.login_server}"
      docker_registry_username = azurerm_container_registry.acr.admin_username
      docker_registry_password = azurerm_container_registry.acr.admin_password
    }
  }
}

既にサブスクリプション及びリソースグループは作成済みで環境変数(terraform.tfvars)からそれぞれのIDや名称を取得する作りとしています。

試しにplanコマンドを実行して、問題がないか確認します。

planコマンド実行結果
実行コマンド & 結果
azure-terraform-test % terraform plan
data.azurerm_resource_group.rg: Reading...
data.azurerm_resource_group.rg: Read complete after 1s [id=/subscriptions/xxx/resourceGroups/yyy]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  + create

Terraform will perform the following actions:

  # azurerm_container_registry.acr will be created
  + resource "azurerm_container_registry" "acr" {
      + admin_enabled                 = true
      + admin_password                = (sensitive value)
      + admin_username                = (known after apply)
      + encryption                    = (known after apply)
      + export_policy_enabled         = true
      + id                            = (known after apply)
      + location                      = "japaneast"
      + login_server                  = (known after apply)
      + name                          = "applicationregistry"
      + network_rule_bypass_option    = "AzureServices"
      + network_rule_set              = (known after apply)
      + public_network_access_enabled = true
      + resource_group_name           = "yyy"
      + sku                           = "Basic"
      + trust_policy_enabled          = false
      + zone_redundancy_enabled       = false
    }

  # azurerm_linux_web_app.app will be created
  + resource "azurerm_linux_web_app" "app" {
      + app_settings                                   = {
          + "WEBSITES_ENABLE_APP_SERVICE_STORAGE" = "false"
        }
      + client_affinity_enabled                        = false
      + client_certificate_enabled                     = false
      + client_certificate_mode                        = "Required"
      + custom_domain_verification_id                  = (sensitive value)
      + default_hostname                               = (known after apply)
      + enabled                                        = true
      + ftp_publish_basic_authentication_enabled       = true
      + hosting_environment_id                         = (known after apply)
      + https_only                                     = false
      + id                                             = (known after apply)
      + key_vault_reference_identity_id                = (known after apply)
      + kind                                           = (known after apply)
      + location                                       = "japaneast"
      + name                                           = "sample-app-yjinno-test"
      + outbound_ip_address_list                       = (known after apply)
      + outbound_ip_addresses                          = (known after apply)
      + possible_outbound_ip_address_list              = (known after apply)
      + possible_outbound_ip_addresses                 = (known after apply)
      + public_network_access_enabled                  = true
      + resource_group_name                            = "yyy"
      + service_plan_id                                = (known after apply)
      + site_credential                                = (sensitive value)
      + webdeploy_publish_basic_authentication_enabled = true
      + zip_deploy_file                                = (known after apply)

      + site_config {
          + always_on                               = false
          + container_registry_use_managed_identity = false
          + default_documents                       = (known after apply)
          + detailed_error_logging_enabled          = (known after apply)
          + ftps_state                              = "Disabled"
          + http2_enabled                           = false
          + ip_restriction_default_action           = "Allow"
          + linux_fx_version                        = (known after apply)
          + load_balancing_mode                     = "LeastRequests"
          + local_mysql_enabled                     = false
          + managed_pipeline_mode                   = "Integrated"
          + minimum_tls_version                     = "1.2"
          + remote_debugging_enabled                = false
          + remote_debugging_version                = (known after apply)
          + scm_ip_restriction_default_action       = "Allow"
          + scm_minimum_tls_version                 = "1.2"
          + scm_type                                = (known after apply)
          + scm_use_main_ip_restriction             = false
          + use_32_bit_worker                       = true
          + vnet_route_all_enabled                  = false
          + websockets_enabled                      = false
          + worker_count                            = (known after apply)

          + application_stack {
              + docker_image_name        = (known after apply)
              + docker_registry_password = (sensitive value)
              + docker_registry_url      = (known after apply)
              + docker_registry_username = (known after apply)
            }
        }
    }

  # azurerm_linux_web_app_slot.green will be created
  + resource "azurerm_linux_web_app_slot" "green" {
      + app_metadata                                   = (known after apply)
      + app_service_id                                 = (known after apply)
      + client_affinity_enabled                        = false
      + client_certificate_enabled                     = false
      + client_certificate_mode                        = "Required"
      + custom_domain_verification_id                  = (sensitive value)
      + default_hostname                               = (known after apply)
      + enabled                                        = true
      + ftp_publish_basic_authentication_enabled       = true
      + hosting_environment_id                         = (known after apply)
      + https_only                                     = false
      + id                                             = (known after apply)
      + key_vault_reference_identity_id                = (known after apply)
      + kind                                           = (known after apply)
      + name                                           = "green-slot"
      + outbound_ip_address_list                       = (known after apply)
      + outbound_ip_addresses                          = (known after apply)
      + possible_outbound_ip_address_list              = (known after apply)
      + possible_outbound_ip_addresses                 = (known after apply)
      + public_network_access_enabled                  = true
      + site_credential                                = (sensitive value)
      + webdeploy_publish_basic_authentication_enabled = true
      + zip_deploy_file                                = (known after apply)

      + site_config {
          + always_on                               = false
          + container_registry_use_managed_identity = false
          + default_documents                       = (known after apply)
          + detailed_error_logging_enabled          = (known after apply)
          + ftps_state                              = "Disabled"
          + http2_enabled                           = false
          + ip_restriction_default_action           = "Allow"
          + linux_fx_version                        = (known after apply)
          + load_balancing_mode                     = "LeastRequests"
          + local_mysql_enabled                     = false
          + managed_pipeline_mode                   = "Integrated"
          + minimum_tls_version                     = "1.2"
          + remote_debugging_enabled                = false
          + remote_debugging_version                = (known after apply)
          + scm_ip_restriction_default_action       = "Allow"
          + scm_minimum_tls_version                 = "1.2"
          + scm_type                                = (known after apply)
          + scm_use_main_ip_restriction             = false
          + use_32_bit_worker                       = true
          + vnet_route_all_enabled                  = false
          + websockets_enabled                      = false
          + worker_count                            = (known after apply)

          + application_stack {
              + docker_image_name        = (known after apply)
              + docker_registry_password = (sensitive value)
              + docker_registry_url      = (known after apply)
              + docker_registry_username = (known after apply)
            }
        }
    }

  # azurerm_service_plan.app_plan will be created
  + resource "azurerm_service_plan" "app_plan" {
      + id                           = (known after apply)
      + kind                         = (known after apply)
      + location                     = "japaneast"
      + maximum_elastic_worker_count = (known after apply)
      + name                         = "app-service-plan"
      + os_type                      = "Linux"
      + per_site_scaling_enabled     = false
      + reserved                     = (known after apply)
      + resource_group_name          = "yyy"
      + sku_name                     = "S1"
      + worker_count                 = (known after apply)
    }

Plan: 4 to add, 0 to change, 0 to destroy.

実行結果も問題ないですね!問題ないので、applyコマンドを実行して、リソースを作成します。

applyコマンド
terraform apply

アプリケーションのソースコード

今回はWebフレームワークにFastAPIを使った簡単なアプリケーションを作成します。
リソースはappフォルダを作成してその中に配置していきます。
まずは必要なライブラリはrequirements.txtに記載します。

app/requirements.txt
fastapi
uvicorn

メインの処理はapp.pyに記載します。

app/app.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# CORSミドルウェアの設定
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/health")
async def health_check():
    return {"status": "healthy"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

コンテナイメージを起動するために、Dockerfileも作成します。

app/Dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

# App ServiceはPORT環境変数を提供します
CMD uvicorn app:app --host 0.0.0.0 --port ${PORT:-8000}

試しにローカルでも起動するか確認してみます。
コンテナイメージを立ち上げてレスポンスが下記のように返却されればOKです。

ローカルでのイメージビルド & 動作確認
# イメージのビルド
docker build -t container-app .
# イメージを起動
docker run -it container-app

INFO:     Started server process [7]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

# curlコマンドで起動しているか確認
curl http://localhost:8000
# レスポンス
{"message":"Hello World"}

Azure Pipelinesで実行するyamlファイル

Pipelineで実行する処理をyamlファイル上に記載します。ファイル名はazure-pipeline.ymlとして下記定義を記載します。

azure-pipeline.yml
trigger:
  - master

variables:
  acrName: 'applicationregistry'
  appName: 'sample-app-yjinno-test'
  imageRepository: 'fastapi-app'
  tag: '$(Build.BuildId)'
  stagingSlotName: 'green-slot' # ステージングスロットの名前を定義

stages:
- stage: Build
  jobs:
  - job: BuildAndTest
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    # Dockerビルドとプッシュ
    - task: Docker@2
      inputs:
        containerRegistry: 'container-connection'
        repository: '$(imageRepository)'
        command: 'buildAndPush'
        Dockerfile: 'app/Dockerfile'
        buildContext: 'app'
        tags: |
          $(tag)
          latest

- stage: Deploy
  jobs:
  - job: DeployToStaging
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    # ステージングスロットへのデプロイ
    - task: AzureWebAppContainer@1
      inputs:
        azureSubscription: 'service-connection-sample'
        appName: $(appName)
        slotName: $(stagingSlotName) # ステージングスロットを指定
        imageName: '$(acrName).azurecr.io/$(imageRepository):$(tag)'

このパイプライン定義を詳しく見ていきます。

まず、triggerセクションでは、masterブランチへの変更があった際に自動的にパイプラインが実行されるよう設定しています。

variablesセクションでは、パイプライン全体で使用する変数を定義しています。

  • ACR名、App Service名、イメージリポジトリ名などの基本情報
  • ビルドIDをタグとして使用することで、各デプロイを一意に識別
  • デプロイ先のステージングスロット名(Blue/Greenデプロイ用)

パイプラインは2つの主要ステージで構成されています。

  1. Buildステージ

    • Ubuntu環境でDockerイメージをビルド
    • Docker@2タスクを使用して、アプリケーションのDockerfileからイメージを作成
    • ビルドしたイメージに現在のビルドIDとlatestの2つのタグを付与
    • 事前設定したcontainer-connectionを使用してACRにイメージをプッシュ
      • この名前は後ほど作成するDocker Registryサービス接続の名前と一致させる必要があります。異なる名前で作成した場合は、このyamlファイルの値を変更してください。
  2. Deployステージ

    • AzureWebAppContainer@1タスクを使用
    • 事前設定したservice-connection-sample接続を使用
      • この名前は後ほど作成するAzure Resource Managerサービス接続の名前と一致させる必要があります。異なる名前で作成した場合は、このyamlファイルの値を変更してください。
    • App Serviceのgreen-slot(ステージングスロット)にビルドステージで作成したコンテナイメージをデプロイ

このパイプラインは、コードの変更がmasterブランチにプッシュされるたびに、自動的にDockerイメージをビルド・プッシュし、そのイメージをステージング環境にデプロイする一連の流れを自動化しています。本番環境へのスワップ(Blue/Greenデプロイの完了)は、動作確認後に手動で行うことを前提としています。

サービス接続の名前は、Azure DevOpsの「Project Settings」→「Service connections」で作成・確認できます。接続名はプロジェクト内で一意である必要があり、yamlファイル内の参照名と一致させる必要があります。

App Serviceの作成とローカルでのアプリケーションの確認および、パイプラインのテンプレートファイルは作成完了したので、次はAzure DevOpsの作成を進めていきます。

Azure DevOpsの環境作成

プロジェクト作成 ~ レポジトリへソースコードをPush

  1. まずはAzure DevOpsでプロジェクトを作成します。任意のプロジェクト名を入れてCreate Projectボタンを押下します。
    CleanShot 2025-01-26 at 21.50.56@2x

  2. プロジェクトを作成したら、Reposタブを押下します。
    CleanShot 2025-01-26 at 22.50.58@2x

  3. コピーリンクを押下して、レポジトリのURLをコピー
    CleanShot 2025-01-26 at 22.51.12@2x

  4. ローカルのアプリケーションソースコードをAzure ReposにPush

Gitへのコミット&プッシュ実行コマンド
#アプリケーションのディレクトリへ移動
cd ./app

#Gitリポジトリを初期化
git init

#ファイルをステージングエリアに追加
git add .

#コミットを作成
git commit -m "Initial commit"

#リモートリポジトリを追加
git remote add origin <コピーしたURL>

#masterブランチにプッシュ
git push -u origin master

補足ですが、認証情報のポップアップなどが求められた場合はログインして認証を実施してください。

Azure リソースとの接続設定

下記2点の設定を行います。これはAzureリソースにAzure DevOpsが問題なくアクセスできるようの認証設定みたいなものを行う必要があります。

  • Azure Resource Manager
    • Azureリソースとの接続設定
  • Container registry
    • Azure Container Registryとの接続設定

service connection

  1. 左下の歯車マークを押下
    image-20250216030346892-9642628

  2. Pipelines > Service Connectionタブをクリック
    CleanShot 2025-02-16 at 03.05.19@2x

  3. New Service Connectionボタンを押下
    CleanShot 2025-02-16 at 03.05.56@2x

  4. Azure Resource Managerを選択
    CleanShot 2025-02-16 at 03.06.45@2x

  5. 連携したいAzureのサブスクリプションおよびリソースグループを選択し、任意のService connection nameを入力し(今回はservice-connection-sampleとしてyamlファイルで設定する値と合わせています)、Grant access permission to all pipelinesにチェックを入れてSaveボタンを押下
    CleanShot 2025-02-16 at 18.04.50@2x

  6. 作成が完了したら、 一覧に作成したService connectionが追加されます
    CleanShot 2025-02-16 at 18.05.42@2x

  7. 再度、New service connectionを押下して、Docker Registryを選択してNextボタンを押下
    CleanShot 2025-03-09 at 15.15.43@2x

  8. Azure Container Registry を選択して、使用しているSubscriptionおよび、Terraformで作成したACR名(今回ならapplicationregistry)を選択して、任意のService Connection Name(今回はcontainer-connectionとしてyamlファイルで設定する値と合わせています)を入力後、Grant access permission to all pipelinesにチェックを入れてSaveボタンを押下
    CleanShot 2025-03-12 at 14.45.55@2x

Pipelineの作成 & 実行

  1. Pipelinesタブを押下
    CleanShot 2025-01-26 at 22.50.58@2x-9641649
  2. Create Pipelineボタンを押下
    CleanShot 2025-01-26 at 23.04.25@2x
  3. Azure Repos Gitを選択
    CleanShot 2025-02-16 at 02.52.28@2x
  4. 該当のレポジトリを選択
    CleanShot 2025-02-16 at 02.53.48@2x
  5. Existing Azure Pipelines YAML fileを選択
    CleanShot 2025-02-16 at 02.54.31@2x-9642087
  6. /azure-pipeline.yml ファイルを選択して、Continueボタンを押下
    CleanShot 2025-02-16 at 02.56.46@2x
  7. Saveボタンを押下
    CleanShot 2025-03-12 at 17.04.15@2x
  8. Run Pipelineボタンを押下
    CleanShot 2025-03-09 at 15.58.41@2x
  9. Runボタンを押下
    CleanShot 2025-03-09 at 15.59.31@2x

作成したパイプラインが実行されます!しばらくすると成功するかと思います。

こんな形でパイプラインの実行結果詳細を確認可能です。

CleanShot 2025-03-09 at 16.15.42@2x

また、BuildDeployなどのカードを押下いただくと各ブロックで実行結果なども見れます。

CleanShot 2025-03-09 at 16.16.42@2x

今度はデプロイが上手くいっているかApp Serviceの動作確認をしてみます。

App Serviceの動作確認

まずはAzureポータル上から、Terraformで作成したApp Serviceの画面を開きます。

CleanShot 2025-03-09 at 16.17.49@2x

規定のドメインにApp Serviceで自動生成されたURLがあるので、こちらにアクセスしてみます。

CleanShot 2025-03-09 at 16.07.41@2x-1504753

まだ本番環境用デプロイスロットにはコンテナイメージを反映していないためErrorとなります。

一方でTerraformで作成した検証環境用のデプロイスロットgreen-slot(以下green-slot)にはAzure Pipelinesからコンテナイメージをデプロイしているのでアプリケーションが実行されるか確認してみます。

CleanShot 2025-03-09 at 16.20.22@2x-1504855

規定のドメインに記載があるリンクを押下してアクセスしてみます。

CleanShot 2025-03-09 at 16.21.00@2x

green-slotはアプリケーションがデプロイされているのでレスポンスが期待通り返却されましたね!

CleanShot 2025-03-09 at 16.08.00@2x

ここでgreen-slotと本番環境用のデプロイスロットをスワップしてみます。
App Serviceにはデプロイスロットのスワップ機能が存在し、green-slotと本番環境用のデプロイスロットの中身を簡単に入れ替えてデプロイができるので試してみます。

green-slotの画面でスワップボタンを押下します。

CleanShot 2025-03-12 at 18.04.00@2x

Sourceがgreen-slotとなっていて、Targetが本番環境用のデプロイスロットになっているのでスワップしてみます。
CleanShot 2025-03-09 at 16.08.18@2x

スワップが完了したら再度両環境にアクセスしてみます。

本番環境の動作

CleanShot 2025-03-09 at 16.14.27@2x

green-slotの動作

CleanShot 2025-03-09 at 16.26.16@2x

無事切り替わりました!!本番環境はデプロイしたアプリケーションが、green-slotはアプリケーションがデプロイされていない状態となりました。お手軽に切り替えすることができて面白いですね!!

今度はソースコードの一部修正を行ってコミット & プッシュし、パイプラインが自動で実行されて、green-slotで変更が適応されているか確認し、スワップして本番適応のステップで試してみます。

修正箇所
@app.get("/")
async def root():
	# re-delopyを追加
    return {"message": "Hello World re-deploy"}

re-deployを追加してソースコードをコミット&プッシュします。
プッシュがmasterブランチに行われると、パイプラインが変更を検知して自動で開始されます。

しばらく待ってパイプラインが下記のように成功したら再度green-slotの環境を確認してみます。
CleanShot 2025-03-09 at 16.30.28@2x

green-slotのURLにアクセスします。

CleanShot 2025-03-09 at 16.31.42@2x

変更が適当されていますね!!一方で本番環境は未だスワップしていないので変わらずかも確認してみます。

CleanShot 2025-03-09 at 16.32.09@2x

本番環境は変更されていないですね。再度スワップしてみます。

CleanShot 2025-03-09 at 16.32.27@2x

スワップ後、再度両環境を確認してみます。

Swap後の本番環境

CleanShot 2025-03-09 at 16.34.09@2x-1505659

Swap後のgreen-slot

CleanShot 2025-03-09 at 16.34.25@2x

無事切り替わっていますね!!簡単にテストして環境を切り替えられるのはAデプロイスロット機能の魅力の1つに感じました。

おわりに

Azure Pipelinesを使ったデプロイはいかがだったでしょうか。直感的に使えるような印象です。また、App Serviceのデプロイスロット機能が面白く、検証↔︎本番のスワップがボタン1つでできるのでテスト→デプロイが容易に行えますね!
本記事が少しでも役に立ちましたら幸いです。最後までご覧いただきありがとうございました!!

補足:App Serviceのデプロイスロットについて

App Serviceのデプロイスロットは、Standard以上のプランでのみ利用可能な機能です。本番環境に影響を与えずに新バージョンをテストでき、スワップ操作で瞬時に切り替えられます。また、トラフィック分割機能を使えばカナリアリリースやA/Bテストも実現可能な機能となります。

環境変数などは環境個別の値としたい場合は、デプロイ スロットの設定にチェックを入れることでスワップされないように個別に設定することも可能です。

CleanShot 2025-03-12 at 16.41.32@2x

詳細は公式ドキュメントをご参照ください。

https://learn.microsoft.com/ja-jp/azure/app-service/deploy-staging-slots?tabs=portal

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.