In this lesson, we will go through deploying a web application with Compose. We will spend most of our time at the command-line and making changes to a Compose file.
We start by briefly introducing the web application, WordPress. Then we will get right into the demo.
We will use Visual Studio Code to look at the Compose file prepped for the application. You will open the Compose file, wordpress.yml,and review all the contents. We will discuss the two services comprising the image: the PHP application code and MySQL database.
During the demo, we will perform some of the following steps:
- Open the web application
- Pull the MySQL and application images
- Stop Compose without stopping the containers
- List the containers
- View the default network created
- Verify the application functionality
- Run the containers in detached mode
- Recreate the output and verify
- Bring down the application
- Remove any leftover images
Welcome back. This lesson will go through deploying a web application with Compose. This lesson is more applied than the previous ones. We’ll spend most of our time at the command-line and making changes to a Compose file.
I’ll start by briefly introducing the web application.
Then we’ll get right into the demo.
The web application that we’ll use is WordPress. WordPress is a popular content management system or CMS. You can create websites and blogs in WordPress. WordPress is written in PHP and uses MySQL as a database. The images for WordPress and MySQL are maintained by Docker. Both images have over 10 million pull on Docker Hub. This scenario relates to operating the application. This is where images have been created and you pull them from a registry, possibly Docker Hub, or your own corporate image registry. The following lesson gets into developing applications with Compose. Now the stage is set, so let’s hop over to Visual Studio Code to look at the Compose file I’ve prepped for the application.
Here is the Compose file, wordpress.yml. All of the contents just fit on the screen. Let’s take a moment to go through it since it ties together a lot of what we’ve seen in the course so far. There are two services, one for WordPress which is where the PHP application code exists and is served up by an apache web server, and one for the MySQL database. Both services use a specific tag on the image to have more control over the environment and to prevent unexpected changes from creeping in. Both services also have a restart key with the value of always. This is the same as the restart option for docker run. To make the application more production-worthy it’s a good idea to have the container restarted automatically if it exits for some reason. You definitely want to persist data for a CMS, so the database service is using a named volume called db_data. This mounts into /var/lib/mysql in the container and is where MySQL stores its database files. Each service has a set of environment variables configured. The db service uses a mapping for its environment variables to create users and a database called wordpress. The wordpress service uses a sequence of strings to configure the database user and host with equal signs separating the variable names from their values. Both syntax styles are allowed and equivalent. One variable to highlight is WORDPRESS_DB_HOST in the wordpress service. It configures the database hostname. The value that is assigned is db on port 3306, the default port for MySQL. There are no named networks in this file. How can the wordpress service connect to the db? Both services will be added to the default network that Compose will create. The last bit of configuration, is the publishing of the wordpress port. The app will be available on port 8000 on the Docker host. The string is enclosed in quotes as a best practice although not strictly necessary in this case because the container port, 80, wouldn’t mistakenly be interpreted as a base-60 number.
Switching over to my terminal, I’m in the webapp directory which contains the wordpress.yml compose file. I’m starting with a clean Docker environment. No containers, no volumes, and only the default Docker networks. I’ll bring the wordpress application up now, using the -f option to specify a custom compose file. The db’s mysql image gets pulled first followed by the wordpress image. I’ll speed this up while the image layers get pulled. Now you can see in the output that the container for the db service is created first followed by the container for the wordpress service. This is guaranteed because the db is in the wordpress services depends_on sequence. The webapp at the beginning of the container name is the project name and it defaults to the current directory name which is webapp. The up command then attaches to the containers and aggregates their output. Compose uses color to distinguish between the output from different containers. You can see WordPress attempting to connect to the database and failing. Recall that depends_on doesn’t wait until the database is ready, it only sequences the order containers are started in. Fortunately, WordPress follows the best practice of having the application handling failed connections and retrying until a connection is made. At this point, the db and wordpress services have finished initializing. I’ll stop the docker-compose command with ctrl+z instead of exiting with ctrl+c so the containers don’t get stopped. After clearing the screen, I’ll list the containers with docker ps and confirm that the containers made by compose are like any others. Checking on the volumes, we can see the db_data named volume created by Compose. The unnamed volume comes from the WordPress image. It declares a volume for the WordPress web assets that get served up by the Apache web server in the /var/www/html directory. Next, we can see the default network Compose created in the networks list.
To verify the application is functioning correctly, I’ll jump over to a browser and navigate to port 8000 on localhost where the WordPress service published its web server container port. The first time you use wordpress, you need to configure the language, a site title, and some user information. I’ll set the title to Composing. The other details aren’t important. With those details set, I can log in and see the admin dashboard. Up in the upper right corner I can navigate to the public site that’s hosted by default. Here it is with the Composing title that I specified earlier. Everything is working as expected. We successfully ran a web application in Compose!
Let’s see how the Compose change detection works. Say we decide to accept the risks of using the latest tag for the wordpress image. I’ll change the tag, save the file, and go back to the terminal.
I’ll repeat the up command except using the -d argument to run the containers in detached mode so the shell prompt will be returned to me after the command finishes. It starts by pulling down the latest version of the wordpress image. After that, it checks and sees that the db service container already running matches the configuration in the Compose file. There is no need to restart it. It then detects that the wordpress container doesn’t match the configuration in the Compose file and recreates it using the updated configuration. You can change the behavior of up to suit your needs in different scenarios though. If you were uncertain if any other services had changed configuration and wanted to avoid recreating the db container at all costs, you can specify the --no-deps argument to up along with the service you want to bring up. In the output, notice that no check of the db service is made. If you want to recreate all containers even if their Compose configuration hasn’t changed, you can pass the --force-recreate argument. The output indicates each container is being recreated now. To be certain, check the output of docker ps and see the containers have just been created. Now I will demonstrate bringing the application down with the down command. The output describes the steps Compose is taking, stopping containers, then removing containers, and lastly the default network Compose created. Docker ps -a verifies there is no trace of any service containers. I can bring the application back up again very quickly having previously downloaded the images. Now, if I load WordPress in the browser, what do you think I will see?
We don’t see the first-time configuration page, we see the same composing site as before. That’s because docker-compose down leaves the volumes by default.
You can change that default behavior though. Add --rmi all to remove all the images used in the Compose file, --volumes to delete the named volumes declared in the Compose file as well as well as any anonymous volumes attached to service containers, and --remove-orphans to delete any project containers that are no longer defined in the Compose file. This time you can see the removal of volumes and images in the output. There is still the old 4.9.0 wordpress image kicking around however. I’ll use the image prune command to remove any images that are left over.
That was pretty awesome! Managing the multi-container web app with Compose was painless. In the next lesson, we’ll see how Compose works when we need to build the images using Dockerfiles.
Logan has been involved in software development and research since 2007 and has been in the cloud since 2012. He is an AWS Certified DevOps Engineer - Professional, AWS Certified Solutions Architect - Professional, Microsoft Certified Azure Solutions Architect Expert, MCSE: Cloud Platform and Infrastructure, Google Cloud Certified Associate Cloud Engineer, Certified Kubernetes Security Specialist (CKS), Certified Kubernetes Administrator (CKA), Certified Kubernetes Application Developer (CKAD), and Certified OpenStack Administrator (COA). He earned his Ph.D. studying design automation and enjoys all things tech.