Running containers in the cloud can be hard and confusing. There are so many options to choose from and then understanding how all the different clouds work from virtual networks to security. Not to mention orchestrators. It’s a learning curve to say the least.
At Docker we are making the Developer Experience (DX) more simple. As an extension of that we want to provide the same beloved Docker experience that developers use daily and integrate it with the cloud. Microsoft’s Azure ACI provided an awesome platform to do just that.
In this tutorial, we take a look at running single containers and multiple containers with Compose in Azure ACI. We’ll walk you through setting up your docker context and even simplifying logging into Azure. At the end of this tutorial, you will be able to use familiar Docker commands to deploy your applications into your own Azure ACI account.
To complete this tutorial, you will need:
The integration with Azure ACI is very similar to working with local containers. The development teams have thought very deeply about the developer experience and have tried to make the UX for working with ACI as close as possible to working with local containers.
Let’s run a simple Nginx web server on Azure ACI.
You do not need to have the Azure CLI installed on your machine to run Docker images in ACI. Docker takes care of everything.
The first thing you need to do is to login to Azure.
$ docker login azure
This will open a browser window which will allow you to login to Azure.
Select your account and login. Once you are logged in, you can close the browser window.
Docker has the concept of a context. You can think of a context as a place where you can run docker containers.It’s a little more complicated than this but this is a good enough description for now. In this tutorial, we use our local context and the new ACI context.
Let’s first take a look at what contexts we currently have on our local development machine. Run the following command to see a list of contexts.
$ docker context list NAME TYPE DESCRIPTION DOCKER ENDPOINT KUBERNETES ENDPOINT ORCHESTRATOR default * moby Current DOCKER_HOST based configuration unix:///var/run/docker.sock https://kubernetes.docker.internal:6443 (default) swarm
Depending on if you have already created another context, you should only see one context. This is the default context that points to your local Docker engine labeled as “moby”. You can identify the current context that will be used for docker commands by the “*” beside the name of the active context.
Now let’s create an ACI context that we can run containers with. We’ll use the create aci command to create our context.
Let’s take a look at the help for creating an aci context.
$ docker context create aci --help Create a context for Azure Container Instances Usage: docker context create aci CONTEXT [flags] Flags: --description string Description of the context -h, --help help for aci --location string Location (default "eastus") --resource-group string Resource group --subscription-id string Location Global Flags: --config DIRECTORY Location of the client config files DIRECTORY (default "/Users/peter/.docker") -c, --context string context -D, --debug enable debug output in the logs -H, --host string Daemon socket(s) to connect to
Underneath the Flags section of the help, you can see that we have the option to set the location, resource-group, and subscription-id.
You can pass these flags into the create command. If you do not, the docker cli will ask you these questions in interactive mode. Let’s do that now.
$ docker context create aci myaci
The first thing the cli will ask is what subscription you would like to use. If you only have one then docker will use that one.
Using only available subscription : Azure subscription 1 (b3c07e4a-774e-4d8a-b071-xxxxxxxxxxxx)
Now we need to select the resource group we want to use. You can either choose one that has been previously created or choose “create a new resource group”. I’ll choose to create a new one.
Resource group "c3eea3e7-69d3-4b54-83cb-xxxxxxxxxxxx" (eastus) created
Okay, our aci context is set up. Let’s list our contexts.
$ docker context list
You should see the ACI context you just created.
Now that we have our ACI context set up, we can now run containers in the cloud. There are two ways to tell Docker which context you want your commands to be applied to.
The first is to pass the –context flag. The other is to tell Docker which context we want to use with all subsequent commands by switching contexts. For now, let’s use the –context flag.
$ docker --context myaci run -d --name web -p 80:80 nginx [+] Running 2/2 ⠿ web Created ⠿ single--container--aci Done web
Here you can see that Docker interacted with ACI and created a container instance named “web” and started a single instance.
Open your Azure portal and navigate to container instances.
We can also run Docker CLI commands that you are already familiar with such as ps and logs.
Let’s take a look at our running containers. But before we do that let’s switch our active context to the ACI context we setup above so we do not have to keep typing –context with every command.
$ docker context use myaci
Now let’s run the ps command without passing the –context flag.
$ docker ps CONTAINER ID IMAGE COMMAND STATUS PORTS web nginx Running 220.127.116.11:80->80/tcp
Nice, since we told Docker to use the myaci context, we see a list of containers running in our Azure account and not on our local machine.
Let’s make sure our container is running. Copy the IP address of the container from the above ps output and paste it into your browser address bar. You can see our Nginx web server running!
Like I mentioned above, we can also take a look at the container’s logs.
$ docker logs web
To stop and remove the container, run the following command.
$ docker rm web
That was pretty easy and the integration is smooth. With a few docker commands that you are already familiar with and a couple new ones, we were able to run a container in ACI from our development machine pretty quickly and simply.
But we’re not done!
We can also run multiple containers using Docker Compose. With the ACI integration, we now have the ability to run compose commands from the docker cli against ACI. Let’s do that next.
I’m using a simple Python Flask application that logs timestamps to a Redis database. Let’s fork the repository and then clone the git repository to your local machine.
Open your favorite browser and navigate to: https://github.com/pmckeetx/timestamper
Click on the “fork” button in the top right corner of the window. This will make a “copy” of the demo repository into your GitHub account.
On your forked version of the repository, click the green “Code” button and copy the github url.
Open up a terminal on your local machine and run the following git command to clone the repository to your local development machine.
Make sure you replace the <<github username>> with your GitHub username.
git clone firstname.lastname@example.org:<<github username>>/timestamper.git
Make sure you are in the root directory for the timestamper project and follow the following steps to build the images and start the application with Docker Compose.
First we need to add your Docker ID to the image in our docker-compose.yml file. Open the docker-compose.yml file in an editor and replace <<username>> with your Docker ID.
Next, we need to make sure we are using the local Docker context.
$ docker context use default
Now we can build and start our application using docker-compose.
$ docker-compose up --build Building frontend Step 1/7 : FROM python:3.7-alpine ---> 6ca3e0b1ab69 Step 2/7 : WORKDIR /app ... frontend_1 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) frontend_1 | * Restarting with stat frontend_1 | * Debugger is active! frontend_1 | * Debugger PIN: 622-764-646
Docker will build our timestamper image and then run the Redis database and our timestamper containers.
Navigate to http://localhost:5000 and click the Timestamp! button a couple of times.
Now let’s run our application on ACI using the new docker compose integration.
We’ll first need to push our image to Docker Hub so ACI can pull the image and run it. Run the following command to push your image to your Docker Hub account.
$ docker-compose push Pushing frontend (pmckee/timestamper:latest)... The push refers to repository [docker.io/pmckee/timestamper] 6e899582609b: Pushed ... 50644c29ef5a: Layer already exists latest: digest: sha256:3ce2607f101a381b36beeb0ca1597cce9925d17a0f826cac0f7e0365386a3042 size: 2201
Now that our image is on Hub, we can use compose to run the application on ACI.
First let’s switch to our ACI context.
$ docker context use myaci
Remember, to see a list of contexts and which is being used, you can run the list contexts command.
$ docker context list
Okay, now that we are using the ACI context, let’s start our application in the cloud.
$ docker compose up [+] Running 3/3 ⠿ timestamper Created ⠿ frontend Done ⠿ backend Done
Let’s verify that our application is up and running. To get the IP address of our frontend, let’s list our running containers.
$ docker ps CONTAINER ID IMAGE COMMAND STATUS PORTS timestamper_frontend pmckee/timestamper Running 18.104.22.168:5000->5000/tcp timestamper_backend redis:alpine Running
Copy the IP address and port listed above and paste into your favorite browser.
Let’s take a look at the logs for our Redis container.
$ docker logs timestamper_backend 1:C 13 Jul 2020 18:21:12.044 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo ... 1:M 13 Jul 2020 18:21:12.046 # Server initialized 1:M 13 Jul 2020 18:21:12.047 * Ready to accept connections
Yes, sir! That is a Redis container running in ACI! Pretty cool.
After you play around a bit, you can take down the compose application by running compose down.
$ docker compose down
We saw how simple it is now to run a single container or run multiple containers using Compose on Azure with our ACI integration. If you want to help influence or suggest features, you can do that on our public Roadmap.