Bicepを使ってAKS+ACRを一瞬で構築/デプロイする
はじめに
先日Azureハッカソンというものに参加してきました。
参加の動機は「いろいろな人と知り合いたいなー」という軽いものでしたが、なんと最優秀賞をいただくことができました。2日間Azureのリソースにたくさん触れ、大変良い経験になったのですが、特にBicepに興味を持ったのでいろいろ試してみることにしました。
ちなみにハッカソン当日の様子については、同じチームのメンバが記事を書いてくれました
Bicep(AKS+ACR)のソースコード
さっそくBicepの説明に入っていきたいとこですが。。
説明なんかいらん!!ソースコードだけ見れればいいんだよ!って方のために。
az group create -n rg-XXXX -l japaneast
az deployment group create -f ./main.bicep -g rg-XXXX
上記コマンド実行後、param
で指定している項目を入力すると自動でデプロイされます。
main.bicep
@description('Resource Deployment Location.')
param location string = resourceGroup().location
@description('AKS Cluster Name')
param clusterName string
@description('AKS Cluster Managed Identity Name')
param managedIdName string = guid(clusterName)
@description('VNET Name Prefix')
param VNetAddressPrefix string = '10.10.0.0/16'
@description('SUBNET Name Prefix')
param SubnetAddressPrefix string = '10.10.1.0/24'
resource AKSVNet 'Microsoft.Network/virtualNetworks@2021-03-01' = {
name: 'vn-${clusterName}'
location: location
properties: {
addressSpace: {
addressPrefixes: [
VNetAddressPrefix
]
}
subnets: [
{
name: 'sn-${clusterName}'
properties: {
addressPrefix: SubnetAddressPrefix
}
}
]
}
}
resource AKSSubNet 'Microsoft.Network/virtualNetworks/subnets@2021-03-01' = {
parent: AKSVNet // https://githubmemory.com/repo/Azure/bicep/issues/1972
name: 'sn-${clusterName}'
}
// ユーザー割り当て Managed ID の作成
resource ManagedId 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
name: managedIdName
location: location
}
// ロールの作成と割り当て
@description('A new GUID used to identify the role assignment')
param roleNameGuid string = guid(managedIdName)
resource RoleAssignment 'Microsoft.Authorization/roleAssignments@2020-08-01-preview' = {
name: roleNameGuid
scope: AKSSubNet
properties: {
roleDefinitionId: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c'
principalId: ManagedId.properties.principalId
principalType: 'ServicePrincipal'
// https://githubmemory.com/repo/Azure/bicep/issues/3695
}
dependsOn: [
ManagedId
]
}
// AKS Cluster の作成
resource aks 'Microsoft.ContainerService/managedClusters@2021-08-01' = {
name: clusterName
location: location
identity: {
type: 'UserAssigned'
// userAssignedIdentities: ManagedIdと指定するとデプロイできない。
// https://stackoverflow.com/questions/64877861/the-template-function-reference-is-not-expected-at-this-location
userAssignedIdentities: {
'${ManagedId.id}': {}
}
}
properties: {
dnsPrefix: clusterName
enableRBAC: true
agentPoolProfiles: [
{
name: 'agentpool1'
count: 2
vmSize: 'standard_d2s_v3'
mode: 'System'
vnetSubnetID: AKSSubNet.id
}
]
}
}
// ACRの作成
@description('Provide a globally unique name of your Azure Container Registry')
param acrName string
@description('Provide a tier of your Azure Container Registry.')
param acrSku string = 'Basic'
resource acr 'Microsoft.ContainerRegistry/registries@2021-06-01-preview' = {
name: acrName
location: location
sku: {
name: acrSku
}
properties: {
adminUserEnabled: true
}
}
//https://docs.microsoft.com/ja-jp/azure/role-based-access-control/built-in-roles
var roleAcrPull = '7f951dda-4ed3-4680-a7ca-43fe172d538d'
resource assignAcrPullToAks 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
name: guid(resourceGroup().id, acrName, aks.id, 'AssignAcrPullToAks')
scope: acr
properties: {
description: 'Assign AcrPull role to AKS'
principalId: aks.properties.identityProfile.kubeletidentity.objectId //https://github.com/Azure/bicep/discussions/3181
principalType: 'ServicePrincipal'
roleDefinitionId: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/${roleAcrPull}'
}
dependsOn: [
aks
]
}
Bicepってなに?
Bicepは、いわゆるIaCを実現するためのツールの1つであり、宣言型の構文を使用してAzureリソースをデプロイすることができます。AzureにはARMテンプレートというものがありますが、JSONなので人間が理解するには少しわかりづらいです。そこで簡潔にかけるBicepが登場したようです。
他のツールと比較した時のBicepの利点は以下の7つがあります。(公式掲載)
- すべてAzureのリソースの種類と API バージョンのサポート
- 単純な構文
- 作成エクスペリエンス
- モジュール性
- Azure サービスとの統合
- 管理する状態または状態ファイルがない
- コストがかからないオープンソース
すべてのAzureリソースに対応しているはMicrosoftが提供しているので、当たり前といったところでしょうか笑。構文の単純さやコスト面に関しては、他のツールとあまり大差ない気がします。唯一の違いは「ステートレス」である部分です。利用場面や開発規模などにもよると思うので、一概に良い悪いは判断できないですね。
BicepでAKSとACRを構築する
最近CKSの勉強も始めたので、Kuberntes環境を簡単に構築できると便利だなと思い、AKSを対象とすることにしました。ACRはおまけです笑
まずはAKSで使用するパラメータを宣言します。
Bicepのデプロイ時のスコープはデフォルトでresourceGroup
となっていますが、明示的に指定することで、subscription
をスコープとすることもできます。
@description('Resource Deployment Location.')
param location string = resourceGroup().location
@description('AKS Cluster Name')
param clusterName string
@description('AKS Cluster Managed Identity Name')
param managedIdName string = guid(clusterName)
@description('VNET Name Prefix')
param VNetAddressPrefix string = '10.10.0.0/16'
@description('SUBNET Name Prefix')
param SubnetAddressPrefix string = '10.10.1.0/24'
次にVNetを作成します。
変数の埋め込みは${hogehoge}
のように記述できます。
resource AKSVNet 'Microsoft.Network/virtualNetworks@2021-03-01' = {
name: 'vn-${clusterName}'
location: location
properties: {
addressSpace: {
addressPrefixes: [
VNetAddressPrefix
]
}
subnets: [
{
name: 'sn-${clusterName}'
properties: {
addressPrefix: SubnetAddressPrefix
}
}
]
}
}
後述するRoleAssignment
のところでSubNetのオブジェクトでないと指定ができなかったので作成しています。
resource AKSSubNet 'Microsoft.Network/virtualNetworks/subnets@2021-03-01' existing = {
parent: AKSVNet
name: 'sn-${clusterName}'
}
ユーザー割り当てマネージドIDとは、複数のリソースに対して割り当て可能なマネージドIDになります。 AKSを構築する上でシステム割り当てのマネージドIDでも問題ありませんが、今回は勉強も兼ねてユーザー割り当てのマネージドIDを作成します。
resource ManagedId 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
name: managedIdName
location: location
}
ロールの作成と割り当てを行います。組み込みロールについては以下のページをご確認ください。idの記載もあります。
idで指定できるといってもぱっと見なんのロールなのかわからない。。
ロール名からのid逆引きができると便利そう(もしかして私が知らないだけで、何か方法があるのだろうか)
ちなみにguid()
とは、パラメーターに対するハッシュ関数の結果を36文字の文字列として返してくれる関数です。返される値は必ずしもグローバルに一意となるわけではありません。
例えば、サブスクリプションのスコープで一意の場合は①、リソース グループのスコープで一意の場合は②のような記述になります
guid(subscription().subscriptionId) #①
guid(resourceGroup().id) #②
ロールの作成と割り当てのコードはこちらです。
先程作成したマネージドIDにロールを割り当てます。
@description('Role assignment')
param roleNameGuid string = guid(managedIdName)
resource RoleAssignment 'Microsoft.Authorization/roleAssignments@2020-08-01-preview' = {
name: roleNameGuid
scope: AKSSubNet
properties: {
roleDefinitionId: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c'
principalId: ManagedId.properties.principalId
principalType: 'ServicePrincipal'
// https://githubmemory.com/repo/Azure/bicep/issues/3695
}
dependsOn: [
ManagedId
]
}
やっとAKSの作成まで辿り着きました。シンプルに見えますが、AKSの設定項目は実はめちゃくちゃ多いです。細かい設定をしたい方は覗いてみてください。
userAssignedIdentities
の部分ですが、ManagedId
オブジェクトを指定するとBicepからARMに変換した際にエラーを吐いてしまうため、無理やり指定しています。
resource aks 'Microsoft.ContainerService/managedClusters@2021-08-01' = {
name: clusterName
location: location
identity: {
type: 'UserAssigned'
// userAssignedIdentities: ManagedIdと指定するとデプロイできない
// https://stackoverflow.com/questions/64877861/the-template-function-reference-is-not-expected-at-this-location
userAssignedIdentities: {
'${ManagedId.id}': {}
}
}
properties: {
dnsPrefix: clusterName
enableRBAC: true
agentPoolProfiles: [
{
name: 'agentpool1'
count: 2
vmSize: 'standard_d2s_v3'
mode: 'System'
vnetSubnetID: AKSSubNet.id
}
]
}
}
ACRはこんな感じです。
@description('Azure Container Registry')
param acrName string
@description('Provide a tier of your Azure Container Registry.')
param acrSku string = 'Basic'
resource acr 'Microsoft.ContainerRegistry/registries@2021-06-01-preview' = {
name: acrName
location: location
sku: {
name: acrSku
}
properties: {
adminUserEnabled: true
}
}
最後にロール割り当てを行います。
コンテナーレジストリからImage PullするためのロールはAcrPull
といいidが7f951dda-4ed3-4680-a7ca-43fe172d538d
になります。
ここでハマったのがprincipalId
のところです。
勝手にAKSのマネージドIDと紐付けてやればいいと思っていたのですが、違いました。
デプロイしたPodがImagePullBackoff
だったのを、ずっとロールが足りていないのだろうと思って無駄な時間を過ごしてしまいました。。
以下のページに
AKS クラスター ノード プールのマネージド ID を使用
と記載ありました。Kubelet Identity
= Nodepool MSI
らしいです。
ドキュメントを読み飛ばすクセはなんとかしなきゃですね。
var roleAcrPull = '7f951dda-4ed3-4680-a7ca-43fe172d538d'
resource assignAcrPullToAks 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
name: guid(resourceGroup().id, acrName, aks.id, 'AssignAcrPullToAks')
scope: acr
properties: {
description: 'Assign AcrPull role to AKS'
principalId: aks.properties.identityProfile.kubeletidentity.objectId //https://github.com/Azure/bicep/discussions/3181
principalType: 'ServicePrincipal'
roleDefinitionId: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/${roleAcrPull}'
}
dependsOn: [
aks
]
}
デプロイ
リソースグループを作成してaz deployment group create
コマンドで、ファイルとリソースグループを指定するだけで簡単にデプロイできちゃいます。
az group create -n rg-XXXX -l japaneast
az deployment group create -f ./main.bicep -g rg-XXXX
おまけ(リソースの視覚化)
Visual Studio CodeのBicep拡張機能を使うと開発がとても捗るのでおすすめです。その中に面白い機能があります。それがVisualizerという、ファイル内のリソースの表現を視覚的に確認できる機能です。VSCode左上にあるビジュアライザーボタンを選択するとBicep Visualizerが開きます。
今回のAKS+ACRだとこのような図が作成されます。
ロールの割り当てとかは慣れるまで分かり難かったりするので初心者の方に対しては理解の助けになるかもしれませんね。
まとめ
一瞬でAKS+ACRが構築できるようになりました。これで突然K8sクラスタを使いたい!と思った時も対応できますね。
Bicepは簡単で理解しやすかったですが、Terraformのように状態管理をしているわけではないので、たまにコードとAzureの実態が整合性がとれなくなってリソースグループごと削除してしまうこともありました。ハッカソンや個人での検証といった用途には向いているかもしれませんが、実際に運用していくとなると大変かもしれません。
あとエラーが分かりにくかった笑
今回は触れませんでしたが、Bicepのモジュール化もやってみたいです。おそらくTerraformと同じようにモジュールを作れそうだなと思っています。
参考
Discussion