初期化コンテナーで Azure Container Instances をデプロイする

完了

実際のアプリケーションが起動する前に特定のタスクを実行することが必要な場合があります。 たとえば、コンテナーからの受信接続を受け入れるように特定のサービスを構成することや、シークレットを Azure Key Vault からボリュームに挿入することが必要な場合があります。 初期化 (init) コンテナーを使用すると、これらの前提条件の検証または初期化タスクを実装できます。

init コンテナーはサイドカー パターンの一例ですが、このコンテナーは、コンテナー グループ内の他のすべてのコンテナーが開始される前に実行されます。 コンテナー グループ内のアプリケーション コンテナーは、定義された init コンテナーでそのタスクが正常に完了した後にのみ開始されます。 Azure Container Instances init コンテナーは、Kubernetes init コンテナーと同じ概念です。

顧客は、IP アドレスではなく完全修飾ドメイン名 (FQDN) を使用して API に到達したいと考えています。 また、コンテナー グループを再作成する場合に FQDN が変更されないようにしたいとも考えています。 このユニットでは、顧客が常に IP アドレスではなくドメイン名を使用して API にアクセスできるようにするために、init コンテナーを使用してドメイン ネーム システム (DNS) を更新します。

次の図は、Container Instances init コンテナーのトポロジを示しています。

Diagram that shows the topology of an ACI Init container.

init コンテナーは、アプリケーション コンテナーに割り当てられている IP アドレスを取得し、API クライアントでコンテナーに到達するために使用される DNS エントリを更新します。 init コンテナーとアプリケーション コンテナーは同じネットワーク スタックを共有するため、init コンテナーから参照可能な IP アドレスは、アプリケーション コンテナーで使用されるアドレスと同じです。

初期化スクリプトと DNS ゾーンを作成する

最初に、Azure サービス プリンシパルを作成します。init コンテナーではマネージド ID を使用できないため、init コンテナーはこのサービス プリンシパルを使用して、アプリケーションの IP アドレスを取得し、DNS を更新します。 この例では、わかりやすくするために、サービス プリンシパルに共同作成者ロールを割り当てます。 運用環境では、より厳格な制限が必要な場合があります。

  1. Azure portal の Azure Cloud Shell で、次のコードを実行してサービス プリンシパルを作成します。

    # Create service principal for authentication
    scope=$(az group show -n $rg --query id -o tsv)
    new_sp=$(az ad sp create-for-rbac --scopes $scope --role Contributor --name acilab -o json)
    sp_appid=$(echo $new_sp | jq -r '.appId') && echo $sp_appid
    sp_tenant=$(echo $new_sp | jq -r '.tenant') && echo $sp_tenant
    sp_password=$(echo $new_sp | jq -r '.password')
    
  2. アプリケーション クライアントでコンテナー インスタンスにアクセスするための Azure プライベート DNS ゾーンを作成し、そのゾーンを仮想ネットワークに関連付けます。

    # Create Azure DNS private zone and records
    dns_zone_name=contoso.com
    az network private-dns zone create -n $dns_zone_name -g $rg 
    az network private-dns link vnet create -g $rg -z $dns_zone_name -n contoso --virtual-network $vnet_name --registration-enabled false
    

    Note

    この DNS ゾーンは、前のユニットで作成した DNS ゾーンとは異なります。前のゾーンは、コンテナー インスタンスで Azure SQL Database にアクセスするために使用されました。

  3. スクリプトを init コンテナーに挿入する方法は数多くあります。 この場合は、Azure Files 共有を使用してスクリプトを格納します。 次のコードを実行して、初期化スクリプトと Azure Files 共有を作成し、スクリプトを共有にアップロードします。

    # Create script for init container
    storage_account_name="acilab$RANDOM"
    az storage account create -n $storage_account_name -g $rg --sku Standard_LRS --kind StorageV2
    storage_account_key=$(az storage account keys list --account-name $storage_account_name -g $rg --query '[0].value' -o tsv)
    az storage share create --account-name $storage_account_name --account-key $storage_account_key --name initscript
    init_script_filename=init.sh
    init_script_path=/tmp/
    cat <<EOF > ${init_script_path}${init_script_filename}
    echo "Logging into Azure..."
    az login --service-principal -u \$SP_APPID -p \$SP_PASSWORD --tenant \$SP_TENANT
    echo "Finding out IP address..."
    # my_private_ip=\$(az container show -n \$ACI_NAME -g \$RG --query 'ipAddress.ip' -o tsv) && echo \$my_private_ip
    my_private_ip=\$(ifconfig eth0 | grep 'inet addr' | cut -d: -f2 | cut -d' ' -f 1) && echo \$my_private_ip
    echo "Deleting previous record if there was one…"
    az network private-dns record-set a delete -n \$HOSTNAME -z \$DNS_ZONE_NAME -g \$RG -y
    echo "Creating DNS record..."
    az network private-dns record-set a create -n \$HOSTNAME -z \$DNS_ZONE_NAME -g \$RG
    az network private-dns record-set a add-record --record-set-name \$HOSTNAME -z \$DNS_ZONE_NAME -g \$RG -a \$my_private_ip
    EOF
    az storage file upload --account-name $storage_account_name --account-key $storage_account_key -s initscript --source ${init_script_path}${init_script_filename}
    

    この初期化スクリプトでは Azure CLI を使用して、コンテナー インスタンスの IP アドレスを検出し、プライベート DNS ゾーンに A レコードを作成するコマンドを実行します。 このスクリプトでは、環境変数として検出されることが予想されるサービス プリンシパルのアプリケーション ID とシークレットを使用して認証を行います。

init コンテナーを使用してコンテナー グループをデプロイする

前のユニットで使用したファイルを基にして YAML ファイルを作成できるようになりました。 次の YAML コードで、以下の項目に注意してください。

  • initContainers セクションが作成されています。
  • initContainer では、Azure CLI が既にインストールされている microsoft/azure-cli:latest イメージが使用されます。
  • init コンテナーでは、Azure Files ボリュームからマウントされた /mnt/init/ フォルダーに格納されているスクリプトが実行されます。
  • リソース グループ、コンテナー インスタンス名、サービス プリンシパル資格情報は、変数として渡されます。
  • サービス プリンシパル シークレットは、セキュリティで保護された環境変数として渡されます。
  • コンテナーの YAML 定義の残りの部分は、前のユニットから変更されていません。
  1. 次のコードを実行して YAML ファイルを作成します。

    # Create YAML
    aci_subnet_id=$(az network vnet subnet show -n $aci_subnet_name --vnet-name $vnet_name -g $rg --query id -o tsv)
    aci_yaml_file=/tmp/acilab.yaml
    cat <<EOF > $aci_yaml_file
    apiVersion: '2023-05-01'
    location: $location
    name: $aci_name
    properties:
      subnetIds:
      - id: $aci_subnet_id
      initContainers:
      - name: azcli
        properties:
          image: mcr.microsoft.com/azure-cli:latest
          command:
          - "/bin/sh"
          - "-c"
          - "/mnt/init/$init_script_filename"
          environmentVariables:
          - name: RG
            value: $rg
          - name: SP_APPID
            value: $sp_appid
          - name: SP_PASSWORD
            secureValue: $sp_password
          - name: SP_TENANT
            value: $sp_tenant
          - name: DNS_ZONE_NAME
            value: $dns_zone_name
          - name: HOSTNAME
            value: $aci_name
          - name: ACI_NAME
            value: $aci_name
          volumeMounts:
          - name: initscript
            mountPath: /mnt/init/
      containers:
      - name: nginx
        properties:
          image: nginx
          ports:
          - port: 443
            protocol: TCP
          resources:
            requests:
              cpu: 1.0
              memoryInGB: 1.5
          volumeMounts:
          - name: nginx-config
            mountPath: /etc/nginx
      - name: sqlapi
        properties:
          image: erjosito/yadaapi:1.0
          environmentVariables:
          - name: SQL_SERVER_FQDN
            value: $sql_server_fqdn
          - name: SQL_SERVER_USERNAME
            value: $sql_username
          - name: SQL_SERVER_PASSWORD
            secureValue: $sql_password
          ports:
          - port: 8080
            protocol: TCP
          resources:
            requests:
              cpu: 1.0
              memoryInGB: 1
          volumeMounts:
      volumes:
      - secret:
          ssl.crt: "$ssl_crt"
          ssl.key: "$ssl_key"
          nginx.conf: "$nginx_conf"
        name: nginx-config
      - name: initscript
        azureFile:
          readOnly: false
          shareName: initscript
          storageAccountName: $storage_account_name
          storageAccountKey: $storage_account_key
      ipAddress:
        ports:
        - port: 443
          protocol: TCP
        type: Private
      osType: Linux
    tags: null
    type: Microsoft.ContainerInstance/containerGroups
    EOF
    
  2. 生成された YAML ファイルを検証して、変数の置換が正しく機能したことを確認します。

    # Check YAML file
    more $aci_yaml_file
    
  3. コンテナー インスタンスを作成します。 アプリケーション コンテナーと NGINX コンテナーが開始される前に init コンテナーが実行されるため、これらのインスタンスの作成にはしばらく時間がかかります。

    # Deploy ACI
    az container create -g $rg --file $aci_yaml_file
    
  4. init コンテナーによって Azure プライベート DNS ゾーンに A レコードが作成されたことを確認します。

    # Verify A records in the Azure Private DNS zone
    az network private-dns record-set a list -z $dns_zone_name -g $rg --query '[].{RecordName:name,IPv4Address:aRecords[0].ipv4Address}' -o table
    
  5. SQL API エンドポイントを使用して、コンテナーに到達できることをテストします。 IP アドレスではなくドメイン名を使用してコンテナーにアクセスします。

    # Test
    aci_fqdn=${aci_name}.${dns_zone_name} && echo $aci_fqdn
    ssh -n -o BatchMode=yes -o StrictHostKeyChecking=no $vm_pip "nslookup $aci_fqdn"
    ssh -n -o BatchMode=yes -o StrictHostKeyChecking=no $vm_pip "curl -ks https://$aci_fqdn/api/healthcheck"
    ssh -n -o BatchMode=yes -o StrictHostKeyChecking=no $vm_pip "curl -ks https://$aci_fqdn/api/sqlversion"
    ssh -n -o BatchMode=yes -o StrictHostKeyChecking=no $vm_pip "curl -ks https://$aci_fqdn/api/sqlsrcip"
    
  6. 各コンテナーの個々のコンテナー インスタンス ログを検査できます。 たとえば、次のコードを実行して、init コンテナー ログにアクセスします。

    # Init container logs
    az container logs -n $aci_name -g $rg --container-name azcli
    
  7. 課金が継続されないように、Azure リソース グループを削除して、このユニットとモジュール用に作成したすべてのリソースをクリーンアップします。

    # Clean up module
    az group delete -n $rg -y --no-wait