Using Azure Container Instance with multiple containers
Tags: azure, docker, devops
Published: 2020-05-11 20:39:57

If you need to run containers in Azure, there are plenty of options: 

* Azure Kubernetes Service, if you want a complete orchestration solution and if you need to manage your deployment and infrastructure
* Azure Red Hat OpenShift for the same reasons
* Azure Web App for containers if you want to deliver your app as a container
* Azure Service Fabric for containers and services orchestration 
* Azure Compute, to build your own solution
* Azure Container Instance with container groups

If you need to run multiple containers in a service you will things about Compute with an orchestrator, Service Fabric, AKS, or OpenShift. But the learning curve is important, and you will have to manage the solutions. AKS and OpenShift are not PaaS services, even if Azure manages clusters for AKS and OpenShift, you are responsible for everything else. 

The solution to run multiple containers in PaaS mode is to use Azure Web App for Container or Azure Container Instance.

The first one, Azure Web App, lets you use Docker compose to run multiple containers for your web application. It’s not the whole docker-compose application. You can not run a build action. The only possibility is equivalent to a docker-compose up command.

But this feature is still in preview. You have limited access to containers once deployed, you cannot use SSH for example against your containers.

The other solution is to use Azure Container Instance.  ACI is a service you can use to run a Windows or Linux container without managing the infrastructure behind it. You choose the computing resource you need, ram and CPU core (at least 1 Gb and 1 core) if you need to mount storage and how to use virtual networks. You can also use GPU if you need it (feature in preview).

You can use ACI to run a containers group. A Container group is a collection of containers running on the same host and sharing the same resources and life cycle. The concept is very similar to the pod in Kubernetes. For the moment, this feature is only available with Linux containers.

But you have some limitations. The first one is the number of core and memory you can assign to each container in a deployment. If your solution requires tree containers instance, one with 2 core and 2 Gb of ram and two others with one core and 1 Gb each you will need 4 core and 4 Gb. 

There is a limitation in CPU and ram for each container group. This limit depends on the region and the OS. 

| Region | Windows 2016 | Windows 2019 LTS | Linux |
|--------|--------------|------------------|-------|
| Canada Central, Central India, Central US, East Asia, , North Europe, South Central US, Southeast Asia, South India, UK South | 2 core / 3.5 Ram | 4 core / 16 Gb Ram | 4 core / 16 Gb Ram|
| East US, West US | 4 core / 14 Gb Ram  | 4 core / 16 Gb Ram | 4 core / 16 Gb Ram |
| West US 2, East US 2 | 2 core / 3.5 Ram | 2 core / 3.5 Ram | 4 core / 16 Gb Ram |
| Brazil South, West Europe | 4 core / 16 Gb Ram | 4 core / 16 Gb Ram | 4 core / 16 Gb Ram |
| Australia East, Japan East | 4 core / 16 Gb Ram | 4 core / 16 Gb Ram | 2 core / 8 Gb Ram |

You need to remember this limitation. If your solution needs to use a cache system or/and a database service you should use an Azure Service, Azure Cache for Redis, or Azure Database.

You can also integrate Azure Container instance with Virtual Network to allow communication with other services in a VNET. You can also use a service endpoint.

There is two way to deploy a containers group, YAML, or ARM Template. 

YAML deployment is like a docker-compose manifest. You describe a document on where to find and how to start your image, the number of resources needed (core and ram), network ports to open, and which command to run.

Let start by building a simple solution with two docker images, the first one will be the front end, listening on public IP on the 80 TCP port, and the second the backend API with no public direct access.
The YAML file start with 

```yaml
apiVersion: 2018-10-01
location: <AzureLocation>
name: <NameOfTheACIGroup>
type: Microsoft.ContainerInstance/containerGroups
```
The name and the location of the ACI groups.

Then you will need to define the properties of your Container groups, public IP, TCP ports to open, OS (Linux is the only possibility), the restart policy for containers, and the registry where to find your images.

```
properties:
  osType: Linux
  ipAddress:
    type: Public
    ports:
    - protocol: tcp
      port: '80'
  imageRegistryCredentials:
  - server: <AzureRegistry>.azurecr.io
    username: <UserName>
    password: <PassWord>
  restartPolicy: <Always|OnFailure|Never>
```

The restart policy governs how the containers in the groups should be restarted after the container's command end. If the restart policy is "never" and the container command exits, the container is not restarted.

Now you need to define which image should run and if they need to run a command.

```yaml
containers:
    - name: <InstanceName>
      properties:
        image: <AzureRegistry>.azurecr.io/<Image>:<Tag>
        command: [<ScriptOrApplication>, '<Arg>']
        ports:
          - port: 80
        resources:
          requests:
            cpu: 1.0
            memoryInGB: 1.0
    - name: <InstanceName>
      properties:
        image: <AzureRegistry>.azurecr.io/<Image>:<Tag>
        command: [<ScriptOrApplication>, '<Arg>']
        resources:
          requests:
            cpu: 1.0
            memoryInGB: 1.0
```

As you can see command should be in a square bracket
['your command', 'arg1','arg2',...,'argn']
To deploy the solution, you will need to use Azure CLI
```
az container create --resource-group <YourResourceGroup> --file <YourYamlFile>
```

To deploy the same solution with ARM template the syntax is almost the same 

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
      "containerGroupName": {
        "type": "string"
      },
      "azureContainerRegistryURI": {
        "type": "string"
      },
      "azureContainerRegistryUserName": {
        "type": "string"
      },
      "azureContainerRegistryPassword": {
        "type": "securestring"
      }
    },
    "variables": {},
    "resources": [
      {
        "name": "[parameters('containerGroupName')]",
        "type": "Microsoft.ContainerInstance/containerGroups",
        "apiVersion": "2018-10-01",
        "location": "[resourceGroup().location]",
        "properties": {
            "imageRegistryCredentials": [
                {
                    "server": "[parameters('azureContainerRegistryURI')]",
                    "username": "[parameters('azureContainerRegistryUserName')]",
                    "password": "[parameters('azureContainerRegistryPassword')]"
                }
            ],
          "restartPolicy": "Always",
          "osType": "Linux",
          "ipAddress": {
            "type": "Public",
            "ports": [
              {
                "protocol": "tcp",
                "port": 80
              }
            ]
          },
          "containers": [
            {
              "name": "<InstanceName>",
              "properties": {
                "image": "<AzureRegistry>.azurecr.io/<Image>:<Tag>",
                "command": [
                    "entrypoint.sh",
                     "Arg1"
                ],
                "resources": {
                  "requests": {
                    "cpu": 1,
                    "memoryInGb": 1
                  }
                },
                "ports": [
                  {
                    "port": 80
                  }
                ]
              }
            },
            {
              "name": "<InstanceName>",
              "properties": {
                "image": "<AzureRegistry>.azurecr.io/<Image>:<Tag>",
                "command": [
                    "entrypoint.sh",
                     "Arg1"
                ],
                "resources": {
                  "requests": {
                    "cpu": 1,
                    "memoryInGb": 1
                  }
                }
              }
            }
          ]
        }
      }
    ],
    "outputs": { }
  }
```

To deploy with a parameters file

```powershell
New-AzResourceGroupDeployment -Name aciarm01 -ResourceGroupName <YourResourceGroup> -TemplateFile .\aci.json -TemplateParameterFile .\aci.parameters.json
```

Azure Container Instance group is a great option if you need to deploy a few numbers of containers without having the manage the infrastructure. 
Azure Container Instance lets you execute a command inside your containers.

```
az container exec -g <YourResourceGroup>  --name <YourContainerGroupName>  --container-name <InstanceName> --exec-command "/bin/bash"
```
You can use containers in Azure Container Group with Managed identity, with Azure Storage if you need to mount a volume. 
Azure Container groups and Azure Container Instance lets you build a simple solution you can interface with your solution when you need a complete docker environment. You can integrate it with a serverless application if you need to run a workflow that may run longer than the default timeout in Azure Functions. You can use it for a small web project or batch service. More ACI can be used as a temporary resource for an AKS cluster as virtual nodes to run your workload.