Using volumes for configuration and state
Application state is an important consideration when you're running applications in containers. Containers can be long-running, but they are not intended to be permanent. One of the biggest advantages with containers over traditional compute models is that you can easily replace them, and the replacement starts in seconds. When you have a new feature to deploy, or a security vulnerability to patch, you just build an upgraded image, stop the old container, and start a replacement from the new image.
Volumes let you manage that upgrade process by keeping your data separate from your application container. I'll demonstrate this with a simple web application that stores the hit count for a page in a text file - each time you browse to the page, the site increments the count.
The Dockerfile for the image dockeronwindows/ch02-hitcount-website uses multi-stage builds, compiling the application using the microsoft/dotnet image and packaging the final app using microsoft/aspnetcore as the base:
# escape=`
FROM microsoft/dotnet:1.1.2-sdk-nanoserver AS builder
WORKDIR C:\src
COPY src .
RUN dotnet restore; dotnet publish
# app image
FROM microsoft/aspnetcore:1.1.2-nanoserver
WORKDIR C:\dotnetapp
RUN New-Item -Type Directory -Path .\app-state
CMD ["dotnet", "HitCountWebApp.dll"]
COPY --from=builder C:\src\bin\Debug\netcoreapp1.1\publish .
In the Dockerfile I create an empty directory at C:\dotnetapp\app-state which is where the application will store the hit count in a text file. I've built the first version of the app into an image with the v1 tag:
docker image build --tag dockeronwindows/ch02-hitcount-website:v1 .
I'll create a directory on the host to use for the container's state, and run a container that mounts the application state directory from a directory on the host:
mkdir C:\app-state
docker container run -d -P `
-v C:\app-state:C:\dotnetapp\app-state `
--name appv1
dockeronwindows/ch02-hitcount-website:v1
I can get the IP address of the container from docker container inspect, and then browse to the site. When I refresh the page a few times I'll see the hit count increasing:
Now when I have an upgraded version of the app to deploy, I can package it into a new image tagged with v2. When the image is ready, I can stop the old container and start a new one, using the same volume mapping:
PS> docker container stop appv1
appv1
PS> docker container run -d -P `
-v C:\app-state:C:\dotnetapp\app-state `
--name appv2
dockeronwindows/ch02-hitcount-website:v2
f6433a09e9479d76db3cd0bc76f9f817acfc6c52375c5e33dbc1d4c9780feb6d
The volume containing the application state is being reused, so the new version will continue using the saved state from the old version. I have a new container with a new IP address. When I browse to it for the first time, I see the updated UI with an attractive icon, but the hit count is carried forward from version 1:
Application state can have structural changes between versions, which is something you will need to manage yourself. The Docker image for the open source Git server, GitLab, is a good example of this - the state is stored in a database on a volume, and when you upgrade to a new version, the app checks the database and runs upgrade scripts, if needed.
Application configuration is another place to make use of volumes. You can ship your application with a default configuration set built into the image but with a volume created for users to override the base configuration with their own values.
You'll see these techniques put to good use in the next chapter.