🦦

【ACI】サイドカーコンテナ内でCaddyをリバースプロキシとして使用する

2023/10/03に公開

はじめに

前回、Azure Container Registryにdockerイメージを登録したので、そのイメージを使ってAzure Container Instances にデプロイします。
https://zenn.dev/articles/1a6ba4df34448b

その際に、8080のままだとアレなので、ついでにドメインも作ってもらって、HTTPSでもアクセスできるようにします。
https://learn.microsoft.com/ja-jp/azure/container-instances/container-instances-container-group-automatic-ssl

ほんとはAzure Files を使わないやり方をしたかったんですが、慣れてなくてサンプルのままやることにしました。CaddyのDockerfile用意しないとダメなんかなぁ。

ちなみに、コマンドはAzure Cloud Shellのbashで実施してます。

ストレージ アカウントの準備

Azure Files を作成して、マウントするディレクトリを共有します。

  • ストレージアカウントの作成
azure cloud shell
$ az storage account create \
  --name devacitestcaddy24492 \
  --resource-group dev_aci_test \
  --location japaneast
The public access to all blobs or containers in the storage account will be disallowed by default in the future, which means default value for --allow-blob-public-access is still null but will be equivalent to false.
{
  "accessTier": "Hot",
  "allowBlobPublicAccess": true,
  "allowCrossTenantReplication": null,
  "allowSharedKeyAccess": null,
  "allowedCopyScope": null,
  "azureFilesIdentityBasedAuthentication": null,
  "blobRestoreStatus": null,
  "creationTime": "2023-10-03T08:33:47.186692+00:00",
  "customDomain": null,
  "defaultToOAuthAuthentication": null,
  "dnsEndpointType": null,
  "enableHttpsTrafficOnly": true,
  "enableNfsV3": null,
  "encryption": {
    "encryptionIdentity": null,
    "keySource": "Microsoft.Storage",
    "keyVaultProperties": null,
    "requireInfrastructureEncryption": null,
    "services": {
      "blob": {
        "enabled": true,
        "keyType": "Account",
        "lastEnabledTime": "2023-10-03T08:33:47.264840+00:00"
      },
      "file": {
        "enabled": true,
        "keyType": "Account",
        "lastEnabledTime": "2023-10-03T08:33:47.264840+00:00"
      },
      "queue": null,
      "table": null
    }
  },
  "extendedLocation": null,
  "failoverInProgress": null,
  "geoReplicationStats": null,
  "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxa367d3/resourceGroups/dev_aci_test/providers/Microsoft.Storage/storageAccounts/devacitestcaddy24492",
  "identity": null,
  "immutableStorageWithVersioning": null,
  "isHnsEnabled": null,
  "isLocalUserEnabled": null,
  "isSftpEnabled": null,
  "keyCreationTime": {
    "key1": "2023-10-03T08:33:47.249211+00:00",
    "key2": "2023-10-03T08:33:47.249211+00:00"
  },
  "keyPolicy": null,
  "kind": "StorageV2",
  "largeFileSharesState": null,
  "lastGeoFailoverTime": null,
  "location": "japaneast",
  "minimumTlsVersion": "TLS1_0",
  "name": "devacitestcaddy24492",
  "networkRuleSet": {
    "bypass": "AzureServices",
    "defaultAction": "Allow",
    "ipRules": [],
    "resourceAccessRules": null,
    "virtualNetworkRules": []
  },
  "primaryEndpoints": {
    "blob": "https://devacitestcaddy24492.blob.core.windows.net/",
    "dfs": "https://devacitestcaddy24492.dfs.core.windows.net/",
    "file": "https://devacitestcaddy24492.file.core.windows.net/",
    "internetEndpoints": null,
    "microsoftEndpoints": null,
    "queue": "https://devacitestcaddy24492.queue.core.windows.net/",
    "table": "https://devacitestcaddy24492.table.core.windows.net/",
    "web": "https://devacitestcaddy24492.z11.web.core.windows.net/"
  },
  "primaryLocation": "japaneast",
  "privateEndpointConnections": [],
  "provisioningState": "Succeeded",
  "publicNetworkAccess": null,
  "resourceGroup": "dev_aci_test",
  "routingPreference": null,
  "sasPolicy": null,
  "secondaryEndpoints": {
    "blob": "https://devacitestcaddy24492-secondary.blob.core.windows.net/",
    "dfs": "https://devacitestcaddy24492-secondary.dfs.core.windows.net/",
    "file": null,
    "internetEndpoints": null,
    "microsoftEndpoints": null,
    "queue": "https://devacitestcaddy24492-secondary.queue.core.windows.net/",
    "table": "https://devacitestcaddy24492-secondary.table.core.windows.net/",
    "web": "https://devacitestcaddy24492-secondary.z11.web.core.windows.net/"
  },
  "secondaryLocation": "japanwest",
  "sku": {
    "name": "Standard_RAGRS",
    "tier": "Standard"
  },
  "statusOfPrimary": "available",
  "statusOfSecondary": "available",
  "storageAccountSkuConversionStatus": null,
  "tags": {},
  "type": "Microsoft.Storage/storageAccounts"
}
  • 接続文字列の取得
azure cloud shell
$ AZURE_STORAGE_CONNECTION_STRING=$(az storage account show-connection-string --name devacitestcaddy24492 --resource-group dev_aci_test --output tsv)
$ echo $AZURE_STORAGE_CONNECTION_STRING
DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=devacitestcaddy24492;AccountKey=*****************************************************/*****************************************************;BlobEndpoint=https://devacitestcaddy24492.blob.core.windows.net/;FileEndpoint=https://devacitestcaddy24492.file.core.windows.net/;QueueEndpoint=https://devacitestcaddy24492.queue.core.windows.net/;TableEndpoint=https://devacitestcaddy24492.table.core.windows.net/
  • フォルダの共有
azure cloud shell
$ ACICADDY_STORAGE=devacitestcaddy24492
$   az storage share create \
  --name proxy-caddyfile \
  --account-name $ACICADDY_STORAGE

There are no credentials provided in your command and environment, we will query for account key for your storage account.
It is recommended to provide --connection-string, --account-key or --sas-token in your command as credentials.

In addition, setting the corresponding environment variables can avoid inputting credentials in your command. Please use --help to get more information about environment variable usage.
{
  "created": true
}
$ az storage share create \
  --name proxy-config \
  --account-name $ACICADDY_STORAGE

There are no credentials provided in your command and environment, we will query for account key for your storage account.
It is recommended to provide --connection-string, --account-key or --sas-token in your command as credentials.

In addition, setting the corresponding environment variables can avoid inputting credentials in your command. Please use --help to get more information about environment variable usage.
{
  "created": true
}
$   az storage share create \
  --name proxy-data \
  --account-name $ACICADDY_STORAGE

There are no credentials provided in your command and environment, we will query for account key for your storage account.
It is recommended to provide --connection-string, --account-key or --sas-token in your command as credentials.

In addition, setting the corresponding environment variables can avoid inputting credentials in your command. Please use --help to get more information about environment variable usage.
{
  "created": true
}
  • ストレージ アカウント キーを取得
    取得したらメモっておきます。
azure cloud shell
$ az storage account keys list -g dev_aci_test -n $ACICADDY_STORAGE
[
  {
    "creationTime": "2023-10-03T08:33:47.249211+00:00",
    "keyName": "key1",
    "permissions": "FULL",
    "value": "*****************************************************/*****************************************************"
  },
  {
    "creationTime": "2023-10-03T08:33:47.249211+00:00",
    "keyName": "key2",
    "permissions": "FULL",
    "value": "*****************************************************/*****************************************************"
  }
]

Caddyfile を準備する

あとで出てくるymlファイルに指定する『dns-labelの値+ region名 + azurecontainer.io』でCaddyfileを作成します。ドメイン名になります。ここが一致しないとアクセスできなくなるっぽいです。

Caddyfile
dev-backend-apps.xcwa3cwc3dudzg0c.japaneast.azurecontainer.io {
    reverse_proxy http://localhost:8080
}

必要かどうかわからないんですが、念のため先ほど作ったストレージのproxy-caddyfileにCaddyfileをアップロードしておきます。

コンテナー グループをデプロイする

YAML ファイルを作成する

ファイル名はなんでもよいです。ACIをデプロイするためのymlファイルを作成します。

dev-aci.yml
name: dev-backend-apps
apiVersion: "2023-05-01"
location: japaneast
properties:
  containers:
    - name: reverse-proxy
      properties:
        image: caddy:latest
        ports:
          - protocol: TCP
            port: 80
          - protocol: TCP
            port: 443
        resources:
          requests:
            memoryInGB: 1.0
            cpu: 1.0
          limits:
            memoryInGB: 1.0
            cpu: 1.0
        volumeMounts:
          - name: proxy-caddyfile
            mountPath: /etc/caddy
          - name: proxy-data
            mountPath: /data
          - name: proxy-config
            mountPath: /config
    - name: dev-backend-apps
      properties:
        environmentVariables: []
        image: devacitest.azurecr.io/backend-apps:v1.0-beta
        ports:
          - port: 8080
            protocol: TCP
        resources:
          requests:
            cpu: 1.0
            memoryInGB: 1.5
  imageRegistryCredentials:
    - server: devacitest.azurecr.io
      username: devacitest
      password: "AzureContainerRegistryのパスワード"
  ipAddress:
    autoGeneratedDomainNameLabelScope: SubscriptionReuse
    dnsNameLabel: dev-backend-apps
    fqdn: dev-backend-apps.xcwa3cwc3dudzg0c.japaneast.azurecontainer.io
    ports:
      - protocol: TCP
        port: 80
      - protocol: TCP
        port: 443
    type: Public
  osType: Linux
  volumes:
    - name: proxy-caddyfile
      azureFile:
        shareName: proxy-caddyfile
        storageAccountName: "devacitestcaddy24492"
        storageAccountKey: "ストレージ アカウント キー"
    - name: proxy-data
      azureFile:
        shareName: proxy-data
        storageAccountName: "devacitestcaddy24492"
        storageAccountKey: "ストレージ アカウント キー"
    - name: proxy-config
      azureFile:
        shareName: proxy-config
        storageAccountName: "devacitestcaddy24492"
        storageAccountKey: "ストレージ アカウント キー"

このファイルをCloud Shell にアップロードします。

コンテナー グループをデプロイする

  • コンテナグループをデプロイします。
Azure Cloud Shell
$ az container create -g dev_aci_test --file ./dev-aci.yml 
{
  "extendedLocation": null,
  "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxa367d3/resourceGroups/dev_aci_test/providers/Microsoft.ContainerInstance/containerGroups/dev-backend-apps",
  "identity": null,
  "kind": null,
  "location": "japaneast",
  "managedBy": null,
  "name": "dev-backend-apps",
  "plan": null,
  "properties": {
    "containers": [
      {
        "name": "reverse-proxy",
        "properties": {
          "environmentVariables": [],
          "image": "caddy:latest",
          "instanceView": {
            "currentState": {
              "detailStatus": "",
              "startTime": "2023-10-03T08:48:38.93Z",
              "state": "Running"
            },
            "events": [
              {
                "count": 1,
                "firstTimestamp": "2023-10-03T08:47:58Z",
                "lastTimestamp": "2023-10-03T08:47:58Z",
                "message": "pulling image \"caddy@sha256:3d1bf053476f2415b40e728c37e1112ee7551fa154a63d6f62b275c13fea8166\"",
                "name": "Pulling",
                "type": "Normal"
              },
              {
                "count": 1,
                "firstTimestamp": "2023-10-03T08:48:10Z",
                "lastTimestamp": "2023-10-03T08:48:10Z",
                "message": "Successfully pulled image \"caddy@sha256:3d1bf053476f2415b40e728c37e1112ee7551fa154a63d6f62b275c13fea8166\"",
                "name": "Pulled",
                "type": "Normal"
              },
              {
                "count": 1,
                "firstTimestamp": "2023-10-03T08:48:38Z",
                "lastTimestamp": "2023-10-03T08:48:38Z",
                "message": "Started container",
                "name": "Started",
                "type": "Normal"
              }
            ],
            "restartCount": 0
          },
          "ports": [
            {
              "port": 80,
              "protocol": "TCP"
            },
            {
              "port": 443,
              "protocol": "TCP"
            }
          ],
          "resources": {
            "limits": {
              "cpu": 1.0,
              "memoryInGB": 1.0
            },
            "requests": {
              "cpu": 1.0,
              "memoryInGB": 1.0
            }
          },
          "volumeMounts": [
            {
              "mountPath": "/etc/caddy",
              "name": "proxy-caddyfile"
            },
            {
              "mountPath": "/data",
              "name": "proxy-data"
            },
            {
              "mountPath": "/config",
              "name": "proxy-config"
            }
          ]
        }
      },
      {
        "name": "dev-backend-apps",
        "properties": {
          "environmentVariables": [],
          "image": "devacitest.azurecr.io/backend-apps:v1.0-beta",
          "instanceView": {
            "currentState": {
              "detailStatus": "",
              "startTime": "2023-10-03T08:48:39.206Z",
              "state": "Running"
            },
            "events": [
              {
                "count": 1,
                "firstTimestamp": "2023-10-03T08:47:58Z",
                "lastTimestamp": "2023-10-03T08:47:58Z",
                "message": "pulling image \"devacitest.azurecr.io/backend-apps@sha256:d0d6c07034ea776ec87848516ac4123e2299356870f0630baebe236d8bada15e\"",
                "name": "Pulling",
                "type": "Normal"
              },
              {
                "count": 1,
                "firstTimestamp": "2023-10-03T08:48:10Z",
                "lastTimestamp": "2023-10-03T08:48:10Z",
                "message": "Successfully pulled image \"devacitest.azurecr.io/backend-apps@sha256:d0d6c07034ea776ec87848516ac4123e2299356870f0630baebe236d8bada15e\"",
                "name": "Pulled",
                "type": "Normal"
              },
              {
                "count": 1,
                "firstTimestamp": "2023-10-03T08:48:39Z",
                "lastTimestamp": "2023-10-03T08:48:39Z",
                "message": "Started container",
                "name": "Started",
                "type": "Normal"
              }
            ],
            "restartCount": 0
          },
          "ports": [
            {
              "port": 8080,
              "protocol": "TCP"
            }
          ],
          "resources": {
            "requests": {
              "cpu": 1.0,
              "memoryInGB": 1.5
            }
          }
        }
      }
    ],
    "imageRegistryCredentials": [
      {
        "server": "devacitest.azurecr.io",
        "username": "devacitest"
      }
    ],
    "initContainers": [],
    "instanceView": {
      "events": [
        {
          "count": 1,
          "firstTimestamp": "2023-10-03T08:48:25.972Z",
          "lastTimestamp": "2023-10-03T08:48:25.972Z",
          "message": "Successfully mounted Azure File Volume.",
          "name": "SuccessfulMountAzureFileVolume",
          "type": "Normal"
        },
        {
          "count": 1,
          "firstTimestamp": "2023-10-03T08:48:31.979Z",
          "lastTimestamp": "2023-10-03T08:48:31.979Z",
          "message": "Successfully mounted Azure File Volume.",
          "name": "SuccessfulMountAzureFileVolume",
          "type": "Normal"
        },
        {
          "count": 1,
          "firstTimestamp": "2023-10-03T08:48:37.997Z",
          "lastTimestamp": "2023-10-03T08:48:37.997Z",
          "message": "Successfully mounted Azure File Volume.",
          "name": "SuccessfulMountAzureFileVolume",
          "type": "Normal"
        }
      ],
      "state": "Running"
    },
    "ipAddress": {
      "autoGeneratedDomainNameLabelScope": "SubscriptionReuse",
      "dnsNameLabel": "dev-backend-apps",
      "fqdn": "dev-backend-apps.xcwa3cwc3dudzg0c.japaneast.azurecontainer.io",
      "ip": "52.156.xx.xxx",
      "ports": [
        {
          "port": 80,
          "protocol": "TCP"
        },
        {
          "port": 443,
          "protocol": "TCP"
        }
      ],
      "type": "Public"
    },
    "isCustomProvisioningTimeout": false,
    "osType": "Linux",
    "provisioningState": "Succeeded",
    "provisioningTimeoutInSeconds": 1800,
    "sku": "Standard",
    "volumes": [
      {
        "azureFile": {
          "shareName": "proxy-caddyfile",
          "storageAccountName": "devacitestcaddy24492"
        },
        "name": "proxy-caddyfile"
      },
      {
        "azureFile": {
          "shareName": "proxy-data",
          "storageAccountName": "devacitestcaddy24492"
        },
        "name": "proxy-data"
      },
      {
        "azureFile": {
          "shareName": "proxy-config",
          "storageAccountName": "devacitestcaddy24492"
        },
        "name": "proxy-config"
      }
    ]
  },
  "resourceGroup": "dev_aci_test",
  "sku": null,
  "tags": null,
  "type": "Microsoft.ContainerInstance/containerGroups"
}
  • 確認
$ az container show --resource-group dev_aci_test --name dev-backend-apps --output table
Name                                      ResourceGroup          Status    Image                                                                       IP:ports              Network    CPU/Memory       OsType    Location
----------------------------------------  ---------------------  --------  --------------------------------------------------------------------------  --------------------  ---------  ---------------  --------  ----------
dev-backend-apps  dev_aci_test  Running   devacitest.azurecr.io/backend-apps:v1.0-beta,caddy:latest  52.156.xx.xxx:80,443  Public     1.0 core/1.0 gb  Linux     japaneast

あとは https://dev-backend-apps.xcwa3cwc3dudzg0c.japaneast.azurecontainer.io にアクセスして、想定通りの画面が出ればOKです。

おわりに

Caddyにいろいろ手間取ったので、仮想ネットワーク内に入れるのを忘れました。
あと、ContainerRegistryはマネージドIDでアクセスできるようにしているんだけど、ymlに書けるとよいのだが。
また次の機会に。。

Discussion