Skip to main content

[SOLVED] How to deal with persistent storage (e.g. databases) in Docker - docker

Mastering Persistent Storage in Docker: Your Ultimate Guide to Databases

Persistent storage is very important for managing databases in Docker containers. As our applications grow, we need reliable ways to store data. This guide will look at different ways to manage persistent storage in Docker. We want to make sure our data stays safe and easy to reach, even when containers stop or get removed. We will share helpful tips that can improve our workflow and make our work with databases in Docker better.

Here is a quick list of the solutions we will talk about in this chapter:

  • Solution 1: Using Docker Volumes for Data Persistence
  • Solution 2: Bind Mounts for Local Development
  • Solution 3: Using Docker Compose for Multi-Container Applications
  • Solution 4: Backing Up and Restoring Docker Volumes
  • Solution 5: Managing Database Data with Dockerfile and Initialization Scripts
  • Solution 6: Using External Storage Solutions with Docker

These solutions will give us the tools we need to manage persistent storage in Docker. For more information on Docker’s setup and how containers work with data, check out this guide on Docker Volumes and Docker Data Storage.

By the end of this article, we will understand how to set up and manage persistent storage solutions in Docker. This will help keep our databases running well.

Solution 1 - Using Docker Volumes for Data Persistence

We can use Docker volumes to manage storage that lasts in Docker containers. This is very useful for databases. Docker volumes let us keep data outside the container’s filesystem. This makes it simple to keep data even when we restart, upgrade, or delete containers.

Creating and Using Docker Volumes

  1. Create a Docker Volume: To create a Docker volume, we use this command:

    docker volume create my_volume
  2. Run a Container with a Volume: When we start a container, we can attach the volume to a specific path in the container. For example, if we use a MySQL database, we can attach the volume like this:

    docker run -d \
      --name my_mysql \
      -e MYSQL_ROOT_PASSWORD=my-secret-pw \
      -v my_volume:/var/lib/mysql \
      mysql:latest

    In this command:

    • -d runs the container in the background.
    • --name gives a name to the container.
    • -e sets a password for the MySQL root user.
    • -v shows the volume mapping from the host to the container.
  3. Inspecting the Volume: To see more info about our volume, we can use:

    docker volume inspect my_volume
  4. Listing Volumes: We can see all volumes on our system with:

    docker volume ls
  5. Removing Volumes: If we want to delete a volume, we must check that no containers are using it. Then we can run:

    docker volume rm my_volume

Benefits of Using Docker Volumes

  • Data Persistence: Data in volumes is not linked to a specific container. So our database data stays safe even if we remove the container.
  • Performance: Volumes work better for input and output compared to the container’s filesystem.
  • Backup and Restore: We can easily make backups of volumes and restore them. This helps us manage our database data well.

Example: Using Docker Volumes with PostgreSQL

We can also use Docker volumes with a PostgreSQL database. Here is an example:

docker volume create pgdata

docker run -d \
  --name my_postgres \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -v pgdata:/var/lib/postgresql/data \
  postgres:latest

This command makes a volume called pgdata and mounts it to the PostgreSQL data folder. The database will keep its data even when we restart the container.

For more details on managing Docker volumes, visit Docker Volumes Documentation.

By using Docker volumes, we can manage storage for our databases in Docker. This helps keep our data safe and reliable.

Solution 2 - Bind Mounts for Local Development

Bind mounts are a great way to handle storage in Docker. They are especially useful when we are developing locally. With bind mounts, we can map a folder or file from our host machine into a container. This lets us see file changes right away. This is important when we want to edit files on our host and see those changes in the container.

How to Set Up a Bind Mount

To make a bind mount, we need to tell Docker which host directory we want to use. We do this in the docker run command. Here is a simple example:

docker run -d \
  --name my-app \
  -v /path/on/host:/path/in/container \
  my-image

Example: Running a Node.js Application

Let’s say we have a Node.js app that we want to work on with live updates. We can use a bind mount like this:

  1. First, we create a directory on our host for our app:
mkdir ~/my-node-app
cd ~/my-node-app
  1. Next, we make a simple server.js file:
const http = require("http");

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader("Content-Type", "text/plain");
  res.end("Hello World\n");
});

const PORT = 3000;
server.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}/`);
});
  1. Now, we run the Docker container with a bind mount:
docker run -d \
  --name my-node-app \
  -v ~/my-node-app:/usr/src/app \
  -w /usr/src/app \
  -p 3000:3000 \
  node:14 node server.js

Key Points to Consider

  • Real-time Updates: Any changes we make to files in ~/my-node-app will show up in the container right away. This helps us to develop smoothly.

  • File Permissions: We need to think about file permissions. The user inside the container can be different from the user on the host. We can use the --user flag if needed.

  • Performance: For apps that need good performance, we should know that bind mounts can have some performance issues compared to Docker volumes. This is because of how filesystems work and the time it takes to sync.

Common Use Cases for Bind Mounts

  • Web Development: We can sync changes in source code for frameworks like React, Angular, or Vue.js.
  • Database Development: We can run database migrations and see changes right away without needing to rebuild the Docker image.
  • Configuration Management: We can change configs in real-time for apps that use external configuration files.

To learn more about how to store data in Docker, we can check out Using Docker Volumes for Data Persistence and How to Mount Host Directory in Docker.

Solution 3 - Using Docker Compose for Multi-Container Applications

We use Docker Compose to manage multi-container Docker applications. It helps us define and run these applications using one configuration file. This file is usually called docker-compose.yml. In this file, we say what services, networks, and volumes our application needs. It makes it easier to handle storage for our databases.

Setting Up Docker Compose

To start using Docker Compose, we need to follow these steps:

  1. Install Docker Compose: First, we must make sure Docker Compose is installed. We can check by running this command:

    docker-compose --version
  2. Create a docker-compose.yml file: We need to create this file to define our services (containers) and their settings.

Example docker-compose.yml Configuration

Here is a simple configuration for a web application that uses a MySQL database. It includes storage for the database:

version: "3.8"

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - web-data:/usr/share/nginx/html

  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: example
      MYSQL_DATABASE: mydatabase
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    volumes:
      - db-data:/var/lib/mysql

volumes:
  web-data:
  db-data:

Explanation of the Configuration

  • Services:

    • web: This service uses the Nginx image. It connects port 80 of the host to port 80 of the container. It also uses a volume called web-data for static files.
    • db: This service uses the MySQL 5.7 image. It sets up environment variables for the database password, name, user, and password. The volume db-data makes sure our MySQL data stays even if the container restarts.
  • Volumes: The volumes part at the bottom creates named volumes web-data and db-data. Docker will manage these volumes. This helps to keep data safe even if we stop or remove containers.

Running Your Application

To start our multi-container application with Docker Compose, we go to the folder with our docker-compose.yml file and run:

docker-compose up -d

The -d flag runs the containers in the background. This way, we can still use our terminal for other commands.

Managing Your Docker Compose Application

We can manage our application using these commands:

  • Stop the application:

    docker-compose down
  • View logs:

    docker-compose logs
  • Check the status of running containers:

    docker-compose ps

Conclusion

Using Docker Compose makes it easier to manage multi-container applications and their storage needs. We can define our services, environment variables, and volumes in one file. This tool is powerful for developers using Docker. For more tips on managing Docker containers and their storage, you can check this guide on Docker volumes.

Solution 4 - Backing Up and Restoring Docker Volumes

Backing up and restoring Docker volumes is very important for keeping our data safe. This is especially true when we use databases in Docker containers. Docker gives us an easy way to handle volume data using commands and some extra tools.

Backing Up Docker Volumes

To back up a Docker volume, we can use the docker run command. This command helps us create a temporary container. The container will mount the volume and copy its files to a tarball. Here is how we can do it:

  1. Find the volume we want to back up. We can list all volumes with:

    docker volume ls
  2. Make a backup by running a temporary container. We should replace your_volume_name and backup_file.tar with our volume name and the name we want for the backup file:

    docker run --rm -v your_volume_name:/data -v $(pwd):/backup alpine tar cvf /backup/backup_file.tar /data
    • --rm: This option removes the container after it finishes.
    • -v your_volume_name:/data: This mounts the Docker volume.
    • -v $(pwd):/backup: This mounts our current directory so we can save the backup file there.
    • alpine: This is a small Linux container we use to run the command.
    • tar cvf /backup/backup_file.tar /data: This creates a tarball of the volume’s data.

Restoring Docker Volumes

To restore a Docker volume from a backup, we follow these steps:

  1. Make the volume if it does not exist:

    docker volume create your_volume_name
  2. Restore the volume using the backup file. Replace your_volume_name and backup_file.tar with the right names:

    docker run --rm -v your_volume_name:/data -v $(pwd):/backup alpine sh -c "cd /data && tar xvf /backup/backup_file.tar --strip 1"
    • This command mounts the volume and the backup file. Then it extracts the files from the tarball into the volume.

Additional Tips

  • We should keep backup files in a safe place.
  • It is good to automate the backup process. We can use cron jobs or scripts for regular backups.
  • We can also think about using tools like docker-volume-backup for more features.

If we want to learn more about managing Docker volumes, we can check out how to manage Docker volumes and look at best practices for Docker data storage.

Solution 5 - Managing Database Data with Dockerfile and Initialization Scripts

We can manage database data easily in Docker by using initialization scripts in our Dockerfile. This method helps us set up our database with the needed schemas, tables, and initial data when the container starts.

Step-by-Step Guide

  1. Create a Dockerfile: First, we need to create a Dockerfile for our database image. If we are using MySQL, our Dockerfile can look like this:

    FROM mysql:5.7
    
    ENV MYSQL_ROOT_PASSWORD=my-secret-pw
    ENV MYSQL_DATABASE=mydatabase
    ENV MYSQL_USER=myuser
    ENV MYSQL_PASSWORD=mypassword
    
    COPY ./init.sql /docker-entrypoint-initdb.d/

    In this Dockerfile:

    • We set environment variables for MySQL. This includes the root password, database name, user, and password for the user.
    • We copy our SQL initialization script (init.sql) into the /docker-entrypoint-initdb.d/ folder. MySQL runs any .sql scripts in this folder when the container starts for the first time.
  2. Create the Initialization Script: Next, we create an init.sql file in the same folder as our Dockerfile. This script will have the SQL commands to set up our database. Here is an example:

    CREATE TABLE users (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(255) NOT NULL,
        email VARCHAR(255) NOT NULL UNIQUE
    );
    
    INSERT INTO users (name, email) VALUES
    ('John Doe', 'john@example.com'),
    ('Jane Smith', 'jane@example.com');
  3. Build the Docker Image: After we have our Dockerfile and initialization script ready, we can build our Docker image:

    docker build -t my-mysql-image .
  4. Run the Container: After we build the image, we run a container from it:

    docker run --name my-mysql-container -d my-mysql-image

    The database will be set up automatically with the data in the init.sql file.

Benefits of Using Initialization Scripts

  • Automated Setup: With these scripts, our database gets set up automatically with all schemas and data every time we create the container.
  • Consistency: This makes sure that each environment like development, testing, or production has the same initial data. This is very important for consistency.
  • Simplicity: This method makes the container orchestration easier and helps to avoid manual setup mistakes.

Additional Tips

  • If we want to run more SQL scripts, we can just add them to the docker-entrypoint-initdb.d/ folder in our Dockerfile. MySQL will run them in alphabetical order.
  • We should make sure our initialization scripts are idempotent. This means they can run many times without causing problems.
  • For more complex database setups, we can use tools like Docker Compose to manage multiple containers and coordinate our database with other services.

By using Dockerfiles and initialization scripts, we can manage database data in Docker containers effectively. This gives us a smooth and automated way to deploy our applications.

Solution 6 - Using External Storage Solutions with Docker

Using external storage solutions with Docker helps us keep our data safe. This is very important for databases and apps that need to share storage between many containers or grow easily. External storage lets us separate our data from the Docker container, which gives us more freedom and reliability.

Key External Storage Options

  1. Network File Systems (NFS): NFS lets many Docker containers use the same file system over the network. This is great for apps that need to share files or databases.

  2. Cloud Storage Solutions: We can use services like Amazon EBS, Google Cloud Persistent Disks, and Azure Managed Disks with Docker. They give us storage that can grow and is reliable.

  3. Block Storage: We can use third-party solutions like Portworx or OpenEBS for storage that lasts. They allow us to make copies, take snapshots, and give out storage as needed.

Configuring External Storage with Docker

Using NFS as External Storage

To use NFS for storage in Docker, we can follow these steps:

  1. Install NFS Server (on your host machine):

    sudo apt-get update
    sudo apt-get install nfs-kernel-server
  2. Configure NFS Exports:

    We need to edit the /etc/exports file to add a folder to share. For example:

    /path/to/nfs/share *(rw,sync,no_subtree_check)

    After this, we restart the NFS server:

    sudo exportfs -a
    sudo systemctl restart nfs-kernel-server
  3. Mount NFS in Docker:

    We can mount the NFS share in our Docker containers with the --mount option:

    docker run -d \
      --name my-app \
      --mount type=volume,source=my-nfs-volume,target=/data \
      my-image

    Change my-nfs-volume to the name of our NFS volume.

Using Cloud Storage with Docker

If we want to use a cloud storage solution like Amazon EBS, we can do these steps:

  1. Create an EBS Volume in the AWS Management Console.

  2. Attach the Volume to our EC2 instance that runs Docker.

  3. Format and Mount the Volume:

    sudo mkfs -t ext4 /dev/xvdf
    sudo mkdir /mnt/my-ebs
    sudo mount /dev/xvdf /mnt/my-ebs
  4. Run Docker with the Mounted Volume:

    We use the Docker -v option to mount the EBS volume:

    docker run -d \
      --name my-app \
      -v /mnt/my-ebs:/data \
      my-image

Considerations

  • Backup and Recovery: We should always have a backup plan for external storage. We can check this guide for how to back up Docker volumes.

  • Performance: Depending on the storage solution, we might see some delays. We must make sure our network and storage work well for our apps.

  • Security: We need to set up our external storage to keep data safe, especially with NFS or cloud services.

Using external storage solutions in Docker is a good way to manage data for databases and applications. For more details on managing data in Docker, we can visit Docker Volumes documentation.

Conclusion

In this article, we looked at some good ways to manage storage in Docker. We talked about using Docker Volumes, bind mounts, and Docker Compose.

These solutions help us keep our data safe for our databases. They also make our work smoother. When we use these methods, we can make sure our Docker setup works well. This helps us manage data better and makes our applications run faster.

Comments