title | description | author | ms.author | ms.prod | ms.topic | ms.date |
---|---|---|---|---|---|---|
Create multi-container apps with MySQL & Docker Compose |
Create multi-container applications with MySQL and Docker Compose and use the containers to scale your project in Visual Studio. |
ghogen |
ghogen |
vs-code |
tutorial |
05/31/2023 |
In this tutorial, you'll learn how to create multi-container apps. This tutorial builds on the getting started tutorials, Get started with Docker and Visual Studio Code. In this advanced tutorial, you'll update your application to work as described in this diagram and learn how to:
[!div class="checklist"]
- Start MySQL.
- Run your app with MySQL.
- Create the compose file.
- Run the application stack.
Using multiple containers allows you to dedicate containers for specialized tasks. Each container should do one thing and do it well.
Here are some reasons you might want to use multi-container apps:
- Separate containers you to manage APIs and front-ends differently than databases.
- Containers let you version and update versions in isolation.
- While you might use a container for the database locally, you may want to use a managed service for the database in production.
- Running multiple processes requires a process manager, which adds complexity to container startup/shutdown.
This tutorial continues the series of tutorials, starting with Create a container app. Start with that one, which includes prerequisites. Then do the tutorial Persist data in your app.
You also need the following items:
-
Docker Desktop for Windows or Mac includes Docker Compose. Run this command to verify:
docker-compose version
If you use the Linux operating system, Install Docker Compose.
As with the previous tutorials, you can accomplish most tasks from the VS Code EXPLORER view or the DOCKER view. You can select Terminal > New Terminal to open a command-line window in VS Code. You can also run commands in a Bash window. Unless specified, any command labeled as Bash can run in a Bash window or the VS Code terminal.
Containers, by default, run in isolation. They don't know anything about other processes or containers on the same computer. To allow one container to talk to another, use networking.
If two containers are on the same network, they can talk to each other. If they aren't, they can't.
There are two ways to put a container on a network: assign it at start or connect an existing container. In this example, you create the network first and attach the MySQL container at startup.
-
Create the network by using this command.
docker network create todo-app
-
Start a MySQL container and attach it the network.
docker run -d --network todo-app --network-alias mysql -v todo-mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=<your-password> -e MYSQL_DATABASE=todos mysql:5.7
This command also defines environment variables. For more information, see MySQL Docker Hub listing.
The command specifies a network alias,
mysql
. -
Get your container ID by using the
docker ps
command. -
To confirm you have the database up and running, connect to the database.
docker exec -it <mysql-container-id> mysql -p
Enter the password you used, above, when prompted.
-
In the MySQL shell, list the databases and verify you see the
todos
database.SHOW DATABASES;
You should see the following output.
+--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | todos | +--------------------+ 5 rows in set (0.00 sec)
-
Enter
exit
when you're ready to return to the terminal command prompt.
The todo app supports the setting of environment variables to specify MySQL connection settings.
MYSQL_HOST
The hostname for the MySQL server.MYSQL_USER
The username to use for the connection.MYSQL_PASSWORD
The password to use for the connection.MYSQL_DB
The database to use once connected.
Warning
Using environment variables to set connection settings is acceptable for development. We recommend against this practice for running applications in production. For more information, see Why you shouldn't use environment variables for secret data.
A more secure mechanism is to use the secret support provided by your container orchestration framework. In most cases, these secrets are mounted as files in the running container.
This procedure starts your app and connects that container to your MySQL container.
-
Use the following docker run command. It specifies the environment variables above.
docker run -dp 3000:3000 -w /app -v ${PWD}:/app --network todo-app -e MYSQL_HOST=mysql -e MYSQL_USER=root -e MYSQL_PASSWORD=<your-password> -e MYSQL_DB=todos node:20-alpine sh -c "yarn install && yarn run dev"
-
In VS Code, in the Docker view, right-click the app container and select View Logs. To view the logs from the command line, use the
docker logs
command.The result includes a line that indicates that the app is connected to the MySQL database.
# Previous log messages omitted $ nodemon src/index.js [nodemon] 1.19.2 [nodemon] to restart at any time, enter `rs` [nodemon] watching dir(s): *.* [nodemon] starting `node src/index.js` Connected to mysql db at host mysql Listening on port 3000
-
Enter
http://localhost:3000
into your browser. Add some items to your todo list. -
Connect to the MySQL database, as you did in the previous section. Run this command to verify that the items are being written to the database.
docker exec -ti <mysql-container-id> mysql -p todos
And in the MySQL shell, run the following commands.
use todos; select * from todo_items;
Your result will look like the following output.
+--------------------------------------+--------------------+-----------+ | id | name | completed | +--------------------------------------+--------------------+-----------+ | c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! | 0 | | 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome! | 0 | +--------------------------------------+--------------------+-----------+
At this point, you have an application that stores data in an external database. That database runs in a separate container. You learned about container networking.
Docker Compose helps define and share multi-container applications. With Docker Compose, you can create a file to define the services. With a single command, you can spin up everything or tear it all down.
You can define your application stack in a file and keep that file at the root of your project repo, under version control. This approach enables others to contribute to your project. They would only need to clone your repo.
-
At the root of the app project, create a file named
docker-compose.yml
. -
In the compose file, start by defining the schema version.
version: "3.7"
In most cases, it's best to use the latest supported version. For current schema versions and compatibility matrix, see Compose file.
-
Define the services, or containers, you want to run as part of your application.
version: "3.7" services:
[!TIP] Indentation is significant in .yml files. If you're editing in VS Code, Intellisense indicates errors.
-
Here's the command you used to your app container. You'll add this information to your .yml file.
docker run -dp 3000:3000 -w /app -v ${PWD}:/app --network todo-app -e MYSQL_HOST=mysql -e MYSQL_USER=root -e MYSQL_PASSWORD=<your-password> -e MYSQL_DB=todos node:20-alpine sh -c "yarn install && yarn run dev"
Define the service entry and the image for the container.
version: "3.7" services: app: image: node:20-alpine
You can pick any name for the service. The name automatically becomes a network alias, which is useful when defining the MySQL service.
-
Add the command.
version: "3.7" services: app: image: node:20-alpine command: sh -c "yarn install && yarn run dev"
-
Specify the ports for the service, which correspond to
-p 3000:3000
in the command above.version: "3.7" services: app: image: node:20-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000
-
Specify the working directory and the volume mapping
version: "3.7" services: app: image: node:20-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000 working_dir: /app volumes: - ./:/app
In Docker Compose volume definitions, you can use relative paths from the current directory.
-
Specify the environment variable definitions.
version: "3.7" services: app: image: node:20-alpine command: sh -c "yarn install && yarn run dev" ports: - 3000:3000 working_dir: /app volumes: - ./:/app environment: MYSQL_HOST: mysql MYSQL_USER: root MYSQL_PASSWORD: <your-password> MYSQL_DB: todos
-
Add the definitions for the MySQL service. Here's the command you used above:
docker run -d --network todo-app --network-alias mysql -v todo-mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=<your-password> -e MYSQL_DATABASE=todos mysql:5.7
Define the new service and name it mysql. Add your text after the
app
definition, at the same level of indentation.version: "3.7" services: app: # The app service definition mysql: image: mysql:5.7
The service automatically gets the network alias. Specify the image to use.
-
Define the volume mapping.
Specify the volume with a
volumes:
section at the same level asservices:
. Specify the volume mapping under the image.version: "3.7" services: app: # The app service definition mysql: image: mysql:5.7 volumes: - todo-mysql-data:/var/lib/mysql volumes: todo-mysql-data:
-
Specify the environment variables.
version: "3.7" services: app: # The app service definition mysql: image: mysql:5.7 volumes: - todo-mysql-data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: <your-password> MYSQL_DATABASE: todos volumes: todo-mysql-data:
At this point, the complete docker-compose.yml
looks like this:
version: "3.7"
services:
app:
image: node:20-alpine
command: sh -c "yarn install && yarn run dev"
ports:
- 3000:3000
working_dir: /app
volumes:
- ./:/app
environment:
MYSQL_HOST: mysql
MYSQL_USER: root
MYSQL_PASSWORD: <your-password>
MYSQL_DB: todos
mysql:
image: mysql:5.7
volumes:
- todo-mysql-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: <your-password>
MYSQL_DATABASE: todos
volumes:
todo-mysql-data:
Now that you have the docker-compose.yml
file, try it.
-
Make sure no other copies of the app and database are running. In the Docker extension, right-click any running container and select Remove. Or, at the command line, use the command
docker rm
as in previous examples. -
In the VS Code Explorer, right-click docker-compose.yml and select Compose Up. Or, at the command line, use this docker command.
docker-compose up -d
The
-d
parameter makes the command run in the background.You should see output like the following results.
[+] Building 0.0s (0/0) [+] Running 2/2 ✔ Container app-app-1 Started 0.9s ✔ Container app-mysql-1 Running
The volume was created as well as a network. By default, Docker Compose creates a network specifically for the application stack.
-
In the Docker extension, right-click the app container and select View Logs. To view the logs from the command line, use the
docker logs
command.mysql_1 | 2019-10-03T03:07:16.083639Z 0 [Note] mysqld: ready for connections. mysql_1 | Version: '5.7.27' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL) app_1 | Connected to mysql db at host mysql app_1 | Listening on port 3000
The logs from each of the services are interleaved into a single stream. With this behavior, you can watch for timing-related issues.
The service name is displayed at the beginning of the line to help distinguish messages. To view logs for a specific service, add the service name to the end of the logs command.
-
At this point, you should be able to open your app. Enter
http://localhost:3000
into your browser.
When you're done with these containers, you can remove them all simply.
- In VS Code Explorer, right-click docker-compose.yml and select Compose Down.
- At the command line, run
docker-compose down
.
The containers stop. The network is removed.
By default, named volumes in your compose file aren't removed.
If you want to remove the volumes, run docker-compose down --volumes
.
The prerequisites you used in this tutorial series can be used for future Docker development. There's no reason to delete or uninstall anything.
In this tutorial, you learned about multi-container apps and Docker Compose. Docker Compose helps dramatically simplify the defining and sharing of multi-service applications.
Here are some resources that might be useful to you: