この記事について
携わっているプロジェクトで、SonarQubeをホストしたい場面がありました。
Terraformを使ってデプロイをするのに少しハマったため、備忘録として記事にしておきます。
目次
利用するサービスについて
AppService
- SonarQubeをホストする基盤として使用します
-
P0v3
を使用しましたが、スムーズに動きました
ストレージアカウント
- SonarQubeの設定値などを格納するために使用するストレージアカウントです
- 開発用途で速度や可用性を重視していないため、
StandardV2
のLRS
を選択しました
Azure Cosmos DB for PostgreSQL Cluster
- バックエンド用のSQLデータベースとして利用します
- コストが安いことを理由に選定し、
Burstable 1 vCore 2GiB RAM
を使用しています - 初めは組み込みH2データベースを使用していましたが、SonarQubeのアップデート時にマイグレーションが難しかったため、ちゃんとしたSQLデータベースを導入しました
リソースの作成
AppServiceを動かすために必要なリソース
AppServiceを構築するために必要なリソースの定義です。
//リソースグループ
resource "azurerm_resource_group" "sonarqube" {
name = "rg-sonarqube"
location = "japaneast"
}
//AppServicePlan
resource "azurerm_service_plan" "sonarqube" {
name = "plan-sonarqube"
location = azurerm_resource_group.sonarqube.location
resource_group_name = azurerm_resource_group.sonarqube.name
os_type = "Linux"
sku_name = "P0v3"
worker_count = 1
}
- リソースグループは説明不要ですね
- AppServicePlanではSKUを
P0v3
、Worker Countを1
で指定しています
ストレージアカウントとファイル共有
SonarQubeの設定情報などを格納するためのストレージ共有を作成します。パスはこちらを参考にしました
locals {
sonarqube_storage_share = toset([
"data",
"logs",
"extensions"
])
}
resource "azurerm_storage_account" "sonarqube" {
name = "stsonarqubemntb92f" //名前被り防止で乱数を追加
resource_group_name = azurerm_resource_group.sonarqube.name
location = azurerm_resource_group.sonarqube.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_share" "sonarqube" {
for_each = local.sonarqube_storage_share
name = each.key
storage_account_name = azurerm_storage_account.sonarqube.name
quota = 10
}
- ストレージアカウントの
name
はAzure上で一意に指定しないといけないので適当な乱数を追加しておきます - local値で必要なファイル共有を定義して
for_each
を使って3つ作っています
データベースの作成
次に、SonarQubeのデータベースとしてAzure Cosmos DB for PostgreSQL Clusterを設定します。
resource "random_password" "postgre" {
length = 20
special = false
}
resource "azurerm_cosmosdb_postgresql_cluster" "sonarqube" {
name = "pg-sonarqube"
resource_group_name = azurerm_resource_group.sonarqube.name
location = azurerm_resource_group.sonarqube.location
administrator_login_password = random_password.postgre.result
coordinator_storage_quota_in_mb = 32768
coordinator_vcore_count = 1
node_count = 0
coordinator_public_ip_access_enabled = true
sql_version = 15
coordinator_server_edition = "BurstableGeneralPurpose"
node_server_edition = "BurstableGeneralPurpose"
}
data "azapi_resource" "cosmosdb_cl" {
type = "Microsoft.DBforPostgreSQL/serverGroupsv2@2022-11-08"
resource_id = azurerm_cosmosdb_postgresql_cluster.sonarqube.id
response_export_values = ["*"]
}
- パスワードを考えたくないので
random_password
を使用して、DB接続用のパスワード生成しています。 - CosmosDB PostgreSQL Clusterは最小限の設定で構築しています。
- ここで使用しているazurerm_cosmosdb_postgresql_clusterのAttributes ReferenceにはDBの接続用のエンドポイントが含まれていないんです。これで少しハマりました。
-
azapi_resource
のdataソースを使用して、エンドポイントを取得する方法を取りました - このdataソースを使用すると、リソースの情報がJSONで取得できるため、
jsondecode
関数を用いて、エンドポイントのFQDNを取り出せます
-
${jsondecode(data.azapi_resource.cosmosdb_cl.output).properties.serverNames[0].fullyQualifiedDomainName
AppServiceの作成
最後にSonarQubeを稼働させるAppServiceを作成します
resource "azurerm_linux_web_app" "sonarqube" {
name = "ase-sonarqube-b92f" //名前被り防止で乱数を追加
location = azurerm_resource_group.sonarqube.location
resource_group_name = azurerm_resource_group.sonarqube.name
service_plan_id = azurerm_service_plan.sonarqube.id
https_only = true
dynamic "storage_account" {
for_each = local.sonarqube_storage_share
content {
name = storage_account.key
type = "AzureFiles"
account_name = azurerm_storage_account.sonarqube.name
share_name = storage_account.key
access_key = azurerm_storage_account.sonarqube.primary_access_key
mount_path = "/opt/sonarqube/${storage_account.key}"
}
}
site_config {
application_stack {
docker_image_name = "sonarqube:9.9-community"
docker_registry_url = "https://index.docker.io"
}
}
app_settings = merge({ for k in local.sonarqube_storage_share : "sonarqube_${k}" => "/opt/sonarqube/${k}" }, {
SONAR_JDBC_URL = "jdbc:postgresql://${jsondecode(data.azapi_resource.cosmosdb_cl.output).properties.serverNames[0].fullyQualifiedDomainName}:5432/citus?user=citus&password=${random_password.DB.result}"
SONAR_JDBC_USERNAME = "citus"
SONARQUBE_JDBC_PASSWORD = random_password.DB.result
SONAR_ES_BOOTSTRAP_CHECKS_DISABLE = true
})
}
- ストレージアカウントと同様、Azure上で一意にするため
name
に乱数を含めています - ストレージ共有をマウントする箇所は
dynamic
ブロックを使用してストレージ共有3つをマウントしています -
site_config
のapplication_stack
で適用したいコンテナイメージを指定できますhttps://hub.docker.com/_/sonarqube
で現在公開されているイメージが確認できます -
app_settings
では環境変数を設定します- ストレージ共有のパスはfor文で書きました。これは以下のように展開されます
> { for k in local.sonarqube_storage_share : "sonarqube_${k}" => "/opt/sonarqube/${k}" } { "sonarqube_data" = "/opt/sonarqube/data" "sonarqube_log" = "/opt/sonarqube/log" "sonarqube_extensions" = "/opt/sonarqube/extensions" }
- その他の値は事前に作成したPostgreSQLの設定値です
- 上記の設定値を
merge
関数を使用してTerraformに渡しています
- ストレージ共有のパスはfor文で書きました。これは以下のように展開されます
最後に
これでデプロイが完了し、AppServiceのURLにアクセスするとSonarQubeが起動し、初期設定が可能です。SonarQubeの設定方法については公式ドキュメントや他の記事を参照することをお勧めします。適宜利用用途に合わせて設定値を見直していただければと思います。お役に立てれば幸いです。