はじめに
Azure Container Apps(ACA)はAzure上にてコンテナ化されたアプリケーションを稼働させるためのプラットフォームです。
Azureには他にもAzure Kubernetes Service(AKS)という別のコンテナプラットフォームがありますが、それと比べて自由度は低いものの運用・構築が容易という特徴があります。
1つのコンテナアプリに対して1つのコンテナイメージをデプロイするのが一般的ですが、今回1つのコンテナアプリに対して複数のコンテナイメージをデプロイしたいという要望があり方法を調べてみました。
作るもの
下図のように、コンテナーアプリ内で複数のイメージを稼働させ、それぞれ8080と8081番のポートにて外部からアクセスできるようにさせたいです。(Container Apps Environmentの記述は省略しています)
またContainer Apps EnvironmentはVNet統合を行い、自分たちで管理しているネットワーク上に配置します。
検証用のアプリ
検証用にJavaを用いたアプリケーションを作成します。
アプリケーションにはGETに対してapp1 responded!!(app2ではapp2 responded!!)と返却する簡単なAPIと、もう一方のコンテナをコールする2つのAPIを作成します。
また別途app2には8081番ポートを使用するよう設定しています。
@RestController
public class SampleController {
@GetMapping("/api/sample")
public String sample() {
return "app1 responded!!";
}
@GetMapping("/api/callAnother")
public String callAnotherAPI() {
RestTemplate rest = new RestTemplate();
String url = "http://localhost:8081/api/sample";
ResponseEntity<String> response = rest.getForEntity(url, String.class);
return response.getBody();
}
}
検証用のAzureリソース
VNet上にContainer Apps Environmentとその上で稼働するコンテナーアプリ、追加の公開ポート設定の3つのリソースを作成します。
(VNetやサブネット、ACRは作成している前提とします。)
Container Apps Environment
既存VNet上へデプロイするため、infrastructure_subnet_idにてコンテナーアプリ専用のサブネットを指定しています。
サブネットのリソースでは別途、Container Apps Environmentへの委任設定を行っています。
resource "azurerm_container_app_environment" "multiple_sample" {
name = "multiple-sample-cae"
location = var.location
resource_group_name = var.resource_group_name
infrastructure_subnet_id = azurerm_subnet.container.id
workload_profile {
name = "Consumption"
workload_profile_type = "Consumption"
}
lifecycle {
ignore_changes = [
infrastructure_resource_group_name
]
}
}
コンテナーアプリ
先ほど作成したConainer Apps Environment上にコンテナーアプリを作成します。
ingress_portにはapp1にて使用している8080番ポートを指定しています。
resource "azurerm_container_app" "multiple-sample" {
name = "multiple-sample-app"
resource_group_name = var.resource_group_name
container_app_environment_id = azurerm_container_app_environment.multiple_sample.id
revision_mode = "Single"
workload_profile_name = "Consumption"
template {
min_replicas = 1
container {
name = "sample-app-1"
image = var.app1_image
cpu = 1
memory = "2Gi"
}
container {
name = "sample-app-2"
image = var.app2_image
cpu = 1
memory = "2Gi"
}
}
ingress {
target_port = 8080
exposed_port = 8080
external_enabled = true
transport = "tcp"
traffic_weight {
percentage = 100
latest_revision = true
}
}
secret {
name = "admin-password"
value = azurerm_container_registry.sample_apps.admin_password
}
registry {
server = azurerm_container_registry.sample_apps.login_server
username = azurerm_container_registry.sample_apps.admin_username
password_secret_name = "admin-password"
}
}
追加のポート設定
外部から両方のアプリへのアクセスを可能にするため、追加で8081番ポートも公開します。
azurermでは追加ポートの設定ができないため、この部分はazapiを用いて設定します。
コンテナーアプリに登録しているシークレットを読み込めずエラーが発生するため、azapi_uptdateではなくazapi_resource_actionを使用しています。
(この書き方ではリソースのアップデートごとに追加ポート設定が消えてしまうため、この部分は修正が必要ですね、、、)
resource "azapi_resource_action" "additional_port" {
type = "Microsoft.App/containerApps@2024-03-01"
resource_id = azurerm_container_app.multiple-sample.id
method = "PATCH"
body = {
properties = {
configuration = {
ingress = {
additionalPortMappings = [
{
external = true
targetPort = 8081
exposedPort = 8081
}
]
}
}
}
}
}
結果
App1が稼働する8080番ポートへのアクセス
8080番ポートへのアクセスではapp1からの応答が返ってきていることがわかります。
App2が稼働する8081番ポートへのアクセス
8081番ポートだとapp2からの応答が返ってきていることがわかります。
App1 -> App2へのアクセス
app1からapp2へのアクセスもできていることがわかります。
App2 -> App1へのアクセス
念のため確認しましたが、こちらも問題なくできていることがわかります。
まとめ
追加ポートの設定など改善が必要な部分もありますが、とりあえず期待通り動作することを確認でき良かったです!
本来なら異なるイメージであれば稼働するコンテナを分離すべきだとは思いますが、1つのコンテナで動作するアプリを機能ごとにチームを分けて開発したい時などには選択肢になるのではないでしょうか。