現状、サーバーサイドの多くはコンテナで動かすことが可能です。そのため、VMに直接アプリケーションをデプロイする機会はかなり減りました。最高ですね。しかし、UWPをはじめとして一定の要件下においてはコンテナ対応が技術的に難しく、VMへのデプロイをせざるを得ないケースがあります。
VM へのデプロイ、古くからおこなわれていますがクラウドネイティブにマネージドにとなると、案外手が限られます。なるべくクラウド側の意図しているであろう方法に沿うように組むのは大事な選択です。
今回は、Azure のVMに対してアプリケーションをデプロイするにあたり、Azure DevOps の Deployment Group を使ってマネージドさを見ながら実現してみましょう。
目次
- 目次
- TL;DR
- 概要
- Deployment Target とは
- Deployment Group の作成
- Deployment Agent の展開
- VM拡張でのDeployment Agentの展開
- Q&A
- Ref
TL;DR
Azure DevOps Pipeline を使うと、VMへのデプロイ時に動的にDeployment Targetを設定できます。 DevOps Pipeline を使わず、Terraform でVM作成時にDeployment Targetを設定することも可能です。
動的な構成ならAzureDevOps Pipeline 、静的な構成ならTerraform がよいのでオススメです。 いずれの方法でも、VMへのDeployment Targetのインストール/アンインストールがマネージドにハンドルされるので、なるべくこちらを使うといいでしょう。いずれもにしても、「VMにリモートログインしてマニュアルでDeployment Targetをインストールする」のはなるべく避けるのは大事です。
なおDeployment Group を用いるということは、VM Scaleset とは別のコンセプトになるので混ぜるな注意です。
概要
AWS においてVMへのデプロイとなると、Code Deploy を用いてCode Pipeline からの Managedなデプロイが定番です。VMに対してCI/CD側からパッケージを指定して、VM上のエージェントが指示された通り展開するわけです。
これと同様の仕組みをAzure で達成するのが、Azure DevOps のDeployment Group / Deployment Target です。 本稿では、ホストとなるVM群に対して、デプロイ時に停止したVMの開始、Deployment Targetでデプロイを実施、というコストも鑑みつつよくある感じの流れを見てみます。
Deployment Target とは
Deployment Target は、Azure DevOps の用語でデプロイ対象となるホスト(Windows/Linux) をさします。Build Agent と同様に、Deployment Agentがホスト上でサービス稼働し、Azure DevOps でDeployment Group として複数のDeployment Targetがグルーピングさて、生存確認や最終リリースを確認できます。
Deployment Targetは、Azure DevOps Pipeline からのデプロイ指示を受けてデプロイタスクが実行されます。(Build Agent同様ですね) どんな処理を行うかは、DevOps Pipeline 上でタスクとして定義、実行/停止指示ができ、Deployment Targetが入っているホスト上でその処理が直接実行されます。
WinRM/SSHなどを通したリモート展開と比べて考えることが少ない一方で、エージェントを展開しておく必要があります。とはいえ、AzureDevOps的にはDeployment Groupが処理の単位のため、デプロイ前にDeployment Groupを経由してホストの生存が確認できればデプロイに支障がありません。つまり、デプロイ前にインストールされていなくてもデプロイ時にDeployment Agent を動的に展開することで、ホストへの事前インストールを気にせずにデプロイに集中できます。
Use deployment groups in a release pipeline - Azure Pipelines | Microsoft Docs
Deployment Group の作成
Deployment Agent を作成する前に、デプロイ対象をグルーピングする単位である Deployment Groupを作成しておきましょう。
このあたりはドキュメントの通りに進めれば問題ありません。
Use deployment groups in a release pipeline - Azure Pipelines | Microsoft Docs
Azure DevOps で Pipelines から Deployment groups を選択しましょう。
Deployment Group 一覧が表示されるので、+ New
で追加しましょう。
Azure あるあるですが、ここでつける Deployment Group
の名前が Deployment Agentと直接紐づくため重要になります。(つまり変更が難しい)
今回は、Hogemoge-Dev-Deploy という名前でDeployment Groupを作成します。
作成したDeployment Group を開くと、次のようにエージェントを展開するための設定が表示されます。
ポイントはPersonal Access Token で、Deployment Agent が Azure DevOps と通信するときには現状必ずPATが必要です。このあたり、AWSのIAMのような統合ができておらず、本当にイケテナイです。PAT期限がることもあり、なかなか本当に厳しい。
Authenticate with personal access tokens - Azure DevOps | Microsoft Docs
Deployment Agent の展開
さて、AWS の Code Deployの場合は AWS提供ホストには事前にインストールされているのでインストールを考える必要はありません。
しかし、Azure提供ホストには Deployment Agentが事前にインストールされていません。Deployment Agentを展開するには2つの方法があります。
- Deployment Agentを手動で展開する
- VM拡張を使って展開する
Agent の展開に必要なPersonal Access Tokenの権限
インストールの前に、罠となるPATの権限について触れておきます。Deployment Group Agent の展開に関するドキュメントを見ると、エージェントのインストールには PATを利用するとあります。
Deploy a build and release agent on Windows - Azure Pipelines | Microsoft Docs
ドキュメントにはDeployment Groups
の Read & manage権限が必要とありますが、実際にエージェントを展開するときはこの権限だけでは足りず、「Deployment Group が見つかりません」と怒られます。
追加で、Project and Team
の Read 権限をPATに与えてあげましょう。
ではインストールを見ていきます。
Deployment Agentを手動で展開する
この記事の主題ではないので手動展開は無視してokです。しかし、Azure VMではなく、オンプレミスのホストにインストールする場合は手動で展開したいでしょう。その場合は、次の公式ドキュメントを見ればいいです。ただこの方法は、ホスト構成時に手作業やAnsibleなどでの余計な構成が必要となるため、Azure VM が対象の場合は仕方ないとき以外は受け入れたくはない選択肢です。
Deploy a build and release agent on Windows - Azure Pipelines | Microsoft Docs
マニュアルで展開する場合は、Use a personal access token in the script for authentication
を有効にして、Deployment Groupの展開PowerShell(Windows)/Bash(Linux) をコピペします。
当初、軽く流すつもりでしたがドキュメントとPowerShellスクリプトの両方に対応できていないトラップがあるので、簡単に触れておきます。自動生成されるPowerShellスクリプトは、--auth と --token 引数が間違っている
ため、これを削除して実行する必要があります。幸い、Auth と Tokenは実行中に聞かれるので、クリップボードにコピーした値を入れなおしましょう。*1
こんな感じになります。タグは何もなしでok、アカウントもデフォルトでokです。
これでDeployment Target が登録されます。おおむね、Build Agent と一緒です。
VM拡張を使って展開する
手動でインストールをしたくない時に、Azure がVMに対してマネージドに何かしらの処理を差し込むために提供している方法が「VM Extensions (VM拡張) 」です。
Azure DevOps の Deployment Agent も VM拡張が提供されており、ドキュメントも提供されています。この方法であれば、VM拡張の導入を定義をするだけで、Deployment Agentのインストール作業はマネージドに行われます。この記事は、マネージドを意図しているのでこちらを利用しましょう。
Provision agents for deployment groups - Azure Pipelines | Microsoft Docs
余談ですが、先ほどの手動エージェントインストールは Concepts-> Agents > Self-hosted <OS> agents
にあり、VM拡張でのインストールは Concepts > Releases > Provision Deployment Agents
にあるのなんというかドキュメントの作りが残念で実際にたどり着けてない人が周りに多いのでまずそう。
VM拡張でのDeployment Agentの展開
さて、一口にVM拡張でのDeployment Agentの展開といっても方法は2つあります。
- VMを構成時にDeployment Agentを展開する
- デプロイ時にResource Group単位でホストにDeployment Agentを展開する
それぞれどう違うのでしょうか。
もし、Resource Group内でVM が1つのDeployment Groupにまとめてokなら、「デプロイ時にResource Group単位でホストにDeployment Agentを展開する」を選択するのがオススメです。この方法なら、デプロイ時に自動的にエージェントが展開されて、VMの構成とデプロイが完全に分離されるだけでなく、Deployment Targetの管理が完全にマネージドになります。
一方で、Resource Gorup内の対象VMごとにDeployment Groupを分けたい場合は、構成時にDeployment Agentを展開する必要があります。TerraformやPlumiなどどの方法を使っても、VMに対してエージェント展開を管理する必要があるので避けたいところです。
順番にそれぞれの展開を見てみましょう。
VMを構成時にDeployment Agentを展開する
Terraform のModuleでさくっとやります。Windows を例にしますが、LinuxでもWindowsでも変わりません。
resource "azurerm_virtual_machine" "main" { name = "${var.name_prefix}-vm" location = "${var.location}" resource_group_name = "${var.resource_group_name}" network_interface_ids = ["${azurerm_network_interface.main.id}"] vm_size = "${var.vm_size}" delete_os_disk_on_termination = true storage_image_reference { publisher = "MicrosoftWindowsServer" offer = "WindowsServer" sku = "2016-Datacenter" version = "latest" } storage_os_disk { name = "${var.name_prefix}-os-disk" caching = "ReadWrite" create_option = "FromImage" managed_disk_type = "${var.managed_disk_type}" disk_size_gb = "${var.os_disk_size_gb}" } os_profile { computer_name = "${var.computer_name}" admin_username = "${var.vm_username}" admin_password = "${var.vm_password}" } os_profile_windows_config = { provision_vm_agent = true winrm { protocol = "HTTP" } } tags = "${var.tags}" } resource "azurerm_virtual_machine_extension" "main" { name = "TeamServicesAgent" resource_group_name = "${data.azurerm_resource_group.current.name}" location = "${var.location}" virtual_machine_name = "${module.assetgenerator.vm_name}" publisher = "Microsoft.VisualStudio.Services" type = "TeamServicesAgent" type_handler_version = "1.0" auto_upgrade_minor_version = true tags = "${local.tags}" settings = <<SETTINGS { "VstsAccountName": "${var.VstsAccountName}", "TeamProject": "${var.TeamProject}", "DeploymentGroup": "${var.DeploymentGroup}", "AgentName": "${module.assetgenerator.vm_name}" } SETTINGS protected_settings = <<SETTINGS { "PATToken": "${var.PAT_DEPLOYMENT_AGENT}" } SETTINGS }
適当にvariables は公開してもらうとして、azurerm_virtual_machine_extension
を使うことでVM作成時にDeployment Agent が自動的に展開されて、Deployment GroupにTargetが登録されます。手動での展開に比べて、圧倒的に楽なうえに自動展開できます。
最も重要なのは、azurerm_virtual_machine
リソースの os_profile_windows_config
にある provision_vm_agent = true
です。(os_profile_linux_configにはない)
有効でないとVM拡張が利用できず、デフォルトが false なので項目が抜けないようにします。provision_vm_agent
が有効化できるのは「VM作成時」だけなので作ってから気づいても手遅れです。
Azure Resource Manager: azurerm_virtual_machine - Terraform by HashiCorp
デプロイ時にResource Group単位でホストにDeployment Agentを展開する
Azure DevOps Pipelines のリリース時に、Resource Group内のVMに対してDeployment Agent をインストールします。この方法を使うと、デプロイの流れの中で対象が動的に定まるため、スケールアウト時も考慮不要になります。
リリースパイプラインのタスクを見てみましょう。
ポイントは、「Build Agent」と「Deployment Group」の処理が分かれることです。実際のデプロイは、Deployment Group単位で実行されますが、そのデプロイ先をBuild Agent で動的に作成します。
Build Agent でやることは2つです。
- 停止したVM のStart
- VM Extensions 経由で Deployment Agent のインストール
YAML を見ていきましょう。
「停止したVM のStart」は、AzureResourceGroupDeployment
を使うことでstart
actionでAzure DevOps から実行できます。ポイントは、Resource Group単位の処理になります。止めたままにしたいVMとか混じるのだめ。融通があんまり利きません。
steps: - task: AzureResourceGroupDeployment@2 displayName: 'Azure Deployment:Start action on VM' inputs: azureSubscription: YOUR_SUBSCRIPTION action: Start resourceGroupName: 'YOUR_RESOURCE_GROUP'
「VM Extensions 経由で Deployment Agent のインストール」もAzureResourceGroupDeployment
を使うことで、VM拡張でインストールされます。先ほど作ったDeployment Groupを特定できるように AzureDevOps のチーム名、Deployment Group名を指定しましょう。
steps: - task: AzureResourceGroupDeployment@2 displayName: 'Azure Deployment:Select Resource Group action on ResourceGroup' inputs: azureSubscription: YOUR_SUBSCRIPTION action: 'Select Resource Group' resourceGroupName: 'YOUR_RESOURCE_GROUP' enableDeploymentPrerequisites: ConfigureVMWithDGAgent teamServicesConnection: 'YOUR_SERVICE_CONNECTION' teamProject: 'YOUR_AZUREDEVOPS_TEAM' deploymentGroupName: 'YOUR_DEPLOYMENT_GROUP_NAME'
これらの処理がデプロイ前に実行されることで、実際にDeployment Groupでデプロイする前にVMの起動とエージェントの展開が約束されます。
ポイントは、このタスク自体にはPATの指定がなく、Azure DevOps Team Service Connection で設定します。
Connection URL には、https://YOUR_TEAM.visualstudio.com/
と指定します。
PATの権限に注意してください。VMへのエージェントインストール、デプロイ実施の両方が必要なので、
Service connections in Azure Pipelines and Team Foundation Server - Azure Pipelines | Microsoft Docs
あとはいい感じでデプロイしてあげればokです。
Q&A
いくつかやっている中ではまりどころがあったものの、文脈とずれるのでここで。
VM拡張で Deployment Agent をインストールしたあとアンインストールしたい
現時点では、アンインストールしようとするとエラーが出ます。VM拡張でのアンインストールは一部しか対応されていないのでそれに該当するらしい.... つまりVM作り直しましょう。
Failed to delete the virtual machine extension in Azure ARM VM - Stack Overflow
VM拡張のARM Templateはないの
あるっぽ。私は ARM Teamplte は使いません。
VSTS Agent Extension in ARM template · Issue #1044 · Microsoft/azure-pipelines-agent
PATの認証って365日だけどどうするの
永続できないのはセキュリティモデル的にはいいですが、マシンユーザー的な利用を想定していないあたりAzureの融通の利かなさ.... Azure DevOps 的には、Service Connectionを複数作れるので、次のいずれかがいいでしょう。
- PATのRegenerate で期限を新しくしてPATを再生成して、今のService ConnectionのPATを更新
- 期限前にPATを新しく発行して別にService Connection を作って割り当てを変える
Authenticate with personal access tokens - Azure DevOps | Microsoft Docs
Ref
Deploying to Azure VM using Deployment Groups | Azure DevOps Hands-on-Labs
How to install VSTS deployment group agents on Azure VMs | Mickaël Derriey's blog
*1:お、AnsibleやTerraform のprovisionerどうするの?隙が多い