Docker volume

Docker

In the last post, I wrote how to run Node.js application in a docker container but it was Hello, World. application. So it doesn’t save any data in the container. Let’s try to save data in a container this time and learn how to use volume.

Complete source code here

This is one of Docker learning series posts.

  1. Start Docker from scratch
  2. Docker volume
  3. Bind host directory to Docker container for dev-env
  4. Communication with other Docker containers
  5. Run multi Docker containers with compose file
  6. Container’s dependency check and health check
  7. Override Docker compose file to have different environments
  8. Creating a cluster with Docker swarm and handling secrets
  9. Update and rollback without downtime in swarm mode
  10. Container optimization
  11. Visualizing log info with Fluentd, Elasticsearch and Kibana
Sponsored links

Simple Server application

I created simple http server application which returns Hello, World! and save how many times it receives requests.

import * as http from "http";
import * as fs from "fs";

const host = "localhost";
const port = "80";

let count = 0;

const server = http.createServer((req, res) => {
    const data = (++count).toString();
    fs.writeFile("/data/text.txt", data, (error) => {
        if (error) {
            console.error(error);
            return;
        }
        console.log("data has been saved");
    });
    res.writeHead(200);
    res.end('Hello, World!');
});
server.listen(port);
console.log(`Running on http://${host}:${port}`)
Sponsored links

See Saved data for each container

Let’s create two images. The two images are with/without VOLUME keyword in Dockerfile.

cd <http-server directory>
docker image build -t yuto/http-server .
docker image build -t yuto/http-server-volume -f Dockerfile_volume .
docker container run -d --name http-server -p 9090:80 yuto/http-server
docker container run -d --name http-server-volume -p 9091:80 yuto/http-server-volume

Options

  • -f: Specify Dockerfile name (Dockerfile is read by default)
  • -d: Run the container in background
  • --name: Assign a name to the container
  • -p: Publish container’s port to host. <Container's port>:<Host's port>

Now host’s port 9090 is opened for http-server and port 9091 is opened for http-server-volume to port 80 for the containers. So we can access to the application by http://localhost:9090 and it shows Hello, World!. Let’s access to it with browser or curl command. My result is following. Hello, World!. is shown at the end.

$ curl http://localhost:9090/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    13    0    13    0     0    541      0 --:--:-- --:--:-- --:--:--   565Hello, World!

$ curl http://localhost:9091/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    13    0    13    0     0    764      0 --:--:-- --:--:-- --:--:--   764Hello, World!

After several calls, we can see how many times the server received requests by executing a command in a container by docker container exec <name> <command>. Let’s see the contents in text.txt. My result is following.

$ docker container exec http-server cat data/text.txt
13
$ docker container exec http-server-volume cat data/text.txt
7

Let’s restart the containers and see the file contents again.

docker container ls
docker container stop http-server http-server-volume
docker container start http-server http-server-volume
docker container exec http-server cat data/text.txt
docker container exec http-server-volume cat data/text.txt

The data still remain in both containers because container IDs are the same as previous launch. However, if the containers are removed the data is also removed in http-server but the data in http-server-volume still remains because the data is stored in volume which is managed in different place. The volume is something like USB stick and it can be attached to any containers. The volume is created when the container starts up because VOLUME command is used in Dockerfile. But when we want to use the same data after updating the image we need to explicitly specify the volume name when starting new container. Let’s check the volume name by executing following command.

$ docker container inspect -f '{{.Mounts}}' http-server-volume
[{volume 9fc55a94b17047b3b54a5ba1f9d4d69ff6897ae53273aed48061733cb7b728c0 /var/lib/docker/volumes/9fc55a94b17047b3b54a5ba1f9d4d69ff6897ae53273aed48061733cb7b728c0/_data /data local  true }]
$ docker volume ls
DRIVER              VOLUME NAME
local               9fc55a94b17047b3b54a5ba1f9d4d69ff6897ae53273aed48061733cb7b728c0

Let’s stop and remove both containers and check volume again. After removing containers the same volume still remains there.

docker container rm -f http-server http-server-volume
docker volume ls

When binding the same volume to the new container -v option needs to be used. Check the contents again after starting the containers.

  • -v: Bind mount a volume, <volume name>:<target directory>
docker container run -d --name http-server -p 9090:80 yuto/http-server
docker container run -d --name http-server-volume -v 9fc55a94b17047b3b54a5ba1f9d4d69ff6897ae53273aed48061733cb7b728c0:/data -p 9091:80 yuto/http-server-volume
docker container exec http-server cat data/text.txt
docker container exec http-server-volume cat data/text.txt

The result is following. Without volume, the data has gone but the remains with volume.

$ docker container exec http-server cat data/text.txt
cat: data/text.txt: No such file or directory
$ docker container exec http-server-volume cat data/text.txt
7

The same volume can be attached to other container by specifying container name with --volumes-from option.

$ docker container run -d --name http-server-volume2 --volumes-from http-server-volume -p 9092:80 yuto/http-server-volume
13689be9955fc4309c88bf453904830f97faf2df44a9c308b788d946dfe81665
$ docker container exec http-server-volume2 cat data/text.txt
7

--volumes-from requires container name and -v option requires volume name. It’s good if the volume name is readable name but the example above was hash key which is not suitable to use. It’s better to create named volume and use it. To create empty volume docker volume create <volume name> can be used.

docker container rm -f http-server http-server-volume http-server-volume2
docker volume create http-server
docker container run -d --name http-server-volume -v http-server:/data -p 9091:80 yuto/http-server-volume
docker container exec http-server-volume cat data/text.txt
curl http://localhost:9091
curl http://localhost:9091
docker container rm -f http-server-volume
docker container run -d --name http-server-volume -v http-server:/data -p 9091:80 yuto/http-server-volume
docker container exec http-server-volume cat data/text.txt

Even after sending two http requests and removing the container the same result is shown on the last command. By using this volume mechanism we can update the image without data lost.

Conclusion

Since many applications need to store data VOLUME is one of things which developers should know. Give it a try to modify the code and run it by yourself.

Comments

Copied title and URL