Posted in:

Azure Container Instances offer you a really easy way to run a single Docker container in the cloud. It's essentially serverless containers. But what if you wanted to use ACI to deploy an application that consists of multiple containers?

For example, to deploy a WordPress blog, you need two containers - one for the PHP website part of WordPress, and the other for the MySql database. Now it's perfectly possible to use ACI and deploy these as two separate containers (or more accurately "container groups"). First deploy the MySql container, exposing port 3306 and then deploy the WordPress container, giving it the IP address and credentials of the MySql container.

But that does mean I need to publicly expose the MySql port on the internet, where I could be vulnerable to people attempting to guess my password. Is there any way to deploy the MySql container so that only the WordPress container has access to it?

Container Groups

Well, as I mentioned in my post yesterday about Azure Container Instances, this is possible by using "Container Groups". If I declare that one or more containers are in a "container group", then they will get deployed on the same server,

There are some downsides to this approach, and container orchestrators like Kubernetes have better ways of solving this problem, but for a very simple serverless scenario where we might want to spin up two containers that can see each other for a short time, ACI container groups might be a good fit.

Currently, the Azure CLI container command does not have much support for working with container groups. Instead, we need to create an ARM template.

Container Group ARM Template

Let's have a look at the template to deploy WordPress and MySql into a container group, and then we'll discuss how it works.

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "mysqlPassword": {
            "type": "securestring",
            "metadata": {
              "description": "Root password password for the MySQL database."
            }
          },
          "containerGroupName": {
              "type": "string",
              "defaultValue": "myContainerGroup",
              "metadata": {
                  "description": "Name for the container group"
              }
          },
          "dnsNameLabel": {
            "type": "string",
            "defaultValue": "aciwordpress",
            "metadata": {
                "description": "DNS Name Label for the container group"
            }
        }
    },
    "resources": [
      {
        "name": "[parameters('containerGroupName')]",
        "type": "Microsoft.ContainerInstance/containerGroups",
        "apiVersion": "2018-02-01-preview",
        "location": "[resourceGroup().location]",
        "properties": {
          "containers": [
            {
                "name": "front-end",
                "properties": {
                  "image": "wordpress",
                  "resources": {
                    "requests": {
                      "cpu": 1,
                      "memoryInGb": 1.0
                    }
                  },
                  "ports": [
                    {
                      "port": 80
                    }
                  ],
                  "environmentVariables": [
                      {
                          "name": "WORDPRESS_DB_PASSWORD",
                          "value": "[parameters('mysqlPassword')]"
                      },
                      {
                          "name": "WORDPRESS_DB_HOST",
                          "value": "127.0.0.1:3306"
                      }
                  ]
                }
              },
            {
                "name": "back-end",
                "properties": {
                  "image": "mysql:5.7",
                  "resources": {
                    "requests": {
                      "cpu": 1,
                      "memoryInGb": 1.0
                    }
                  },
                  "ports": [
                      {
                          "protocol": "tcp",
                          "port": "3306"
                      }
                  ],
                  "environmentVariables": [
                      {
                          "name": "MYSQL_ROOT_PASSWORD",
                          "value": "[parameters('mysqlPassword')]"
                      }
                  ]
                }
              }
          ],
          "osType": "Linux",
          "restartPolicy": "OnFailure",
          "ipAddress": {
            "type": "Public",
            "dnsNameLabel": "[parameters('dnsNameLabel')]",
            "ports": [
              {
                "protocol": "tcp",
                "port": "80"
              }
            ]
          }
        }
      }
    ],
    "outputs": {
      "containerIPv4Address": {
        "type": "string",
        "value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups/', parameters('containerGroupName'))).ipAddress.ip]"
      }
    }
}

ARM Template Configuration

The template itself is straightforward enough, but the challenge with ARM templates is knowing what the names of all the various configuration options are. The documentation here proved helpful.

You can see that we've got one container group, with two containers in. Each container has its own environment variables. I allow the WordPress container to find the MySql container by using the WORDPRESS_DB_HOST environment variable. In theory this could be set to localhost:3306 but because of some other issue to do with PHP I needed to use 127.0.0.1:3306.

On the WordPress container I expose port 80 and on the MySql container port 3306, but the container group as a whole only exposes port 80, which helps keep the attack surface small.

I've also set up a restartPolicy of OnFailure as the WordPress container can exit if it can't talk to the MySql database, which might happen if it starts up first. And I configured a dnsNameLabel for the container group to give us a nice friendly domain name to access our WordPress container through.

Deploying the template

Deploying the ARM template is nice and easy with the Azure CLI. Just create a resource group, deploy the template into it, and then query for the container group domain name:

# create a resource group for our
$resourceGroup = "AciGroupDemo"
$location="westeurope"
az group create -n $resourceGroup -l $location

# deploy the container group ARM template passing in custom parameters
$containerGroupName = "myWordpress"
$dnsNameLabel = "wordpressaci"

az group deployment create `
    -n TestDeployment -g $resourceGroup `
    --template-file "aci-wordpress.json" `
    --parameters 'mysqlPassword=My5q1P@s5w0rd!' `
    --parameters "containerGroupName=$containerGroupName" `
    --parameters "dnsNameLabel=$dnsNameLabel"

# get the domain name of our wordpress container
az container show -g $resourceGroup -n $containerGroupName `
        --query ipAddress.fqdn -o tsv

Summary

In this post I've been exploring the capabilities of container groups, rather than recommending best practices. As I said yesterday, they're not the best fit if you want long-running containers, as there are more cost-effective options. They're also not intended as a replacement for container orchestrators, but can be useful when you have a group of containers that work together and need to be deployed as a logical group. The need for ARM templates means they're not super easy to work with at the moment, but hopefully that will change as the tooling improves.

The code for this demo, along with a bunch of my other Azure Docker demos can be found here on GitHub.

Want to learn more about how easy it is to get up and running with Azure Container Instances? Be sure to check out my Pluralsight course Azure Container Instances: Getting Started.

Comments

Comment by Cristian

Hi, I get this error: DNS name label for container group is not supported before version '2018-02-01-preview'
Any ideas?

Cristian
Comment by Mark Heath

hmm, not sure, will have to try this again myself. Probably there's a non-preview version available now anyway. Also make sure you've got the latest Azure CLI

Mark Heath
Comment by Chris Ainsley

Just tried deploying this and I get "Error establishing a database connection" The db container appears to be up and running so I'm wondering if msft have changed something.

Chris Ainsley
Comment by Mark Heath

shame, not sure what's changed - it's been a while since I tried running this deom

Mark Heath
Comment by Felipe Rocha

same error here. You should create your db first. create database wordpress;

Felipe Rocha
Comment by Wisdom as a Service

I also get the error "Error establishing a database connection" does anyone solve this issue ?

Wisdom as a Service