|
@@ -0,0 +1,221 @@
|
|
1
|
+---
|
|
2
|
+date: 2019-04-01
|
|
3
|
+title: Understanding Docker volume mounts
|
|
4
|
+url: /2019/04/01/understanding-docker-volume-mounts/
|
|
5
|
+image: /res/images/docker/logo.png
|
|
6
|
+description: It's basically magic.
|
|
7
|
+---
|
|
8
|
+
|
|
9
|
+One thing that always confuses me with Docker is how exactly mounting
|
|
10
|
+volumes behaves. At a basic level it's fairly straight forward: you
|
|
11
|
+declare a volume in a Dockerfile, and then either explicitly mount
|
|
12
|
+something there or docker automatically creates an anonymous volume
|
|
13
|
+for you. Done. But it turns out there's quite a few edge cases...
|
|
14
|
+
|
|
15
|
+<!--more-->
|
|
16
|
+
|
|
17
|
+### Changing ownership of the folder
|
|
18
|
+
|
|
19
|
+Perhaps the most common operation done on a Docker volume other than
|
|
20
|
+simply mounting it is trying to change the ownership of the directory.
|
|
21
|
+If your Docker process runs as a certain user you probably want the
|
|
22
|
+directory to be writable by that user.
|
|
23
|
+
|
|
24
|
+At first we might try something like:
|
|
25
|
+
|
|
26
|
+{{< highlight docker >}}
|
|
27
|
+FROM alpine
|
|
28
|
+RUN adduser -D -u 1113 test123
|
|
29
|
+USER test123
|
|
30
|
+VOLUME /testing
|
|
31
|
+{{< / highlight >}}
|
|
32
|
+
|
|
33
|
+But changing the user doesn't seem to have any effect on the volume.
|
|
34
|
+Why? Checking the docs for the
|
|
35
|
+[`USER` instruction](https://docs.docker.com/engine/reference/builder/#user)
|
|
36
|
+shows that only affects certain future operations -- namely
|
|
37
|
+`RUN`, `CMD`, and `ENTRYPOINT`. It doesn't affect the `VOLUME` instruction;
|
|
38
|
+if it did, you'd probably just get a permission denied error unless the user
|
|
39
|
+you switch to had privileges to create mount points.
|
|
40
|
+
|
|
41
|
+OK, so instead we might try using the good old `chown` command:
|
|
42
|
+
|
|
43
|
+{{< highlight docker >}}
|
|
44
|
+FROM alpine
|
|
45
|
+RUN adduser -D -u 1113 test123
|
|
46
|
+VOLUME /testing
|
|
47
|
+RUN chown test123 /testing
|
|
48
|
+{{< / highlight >}}
|
|
49
|
+
|
|
50
|
+But again, the directory is just owned by root at runtime.
|
|
51
|
+Back to the docs, this time for the
|
|
52
|
+[`VOLUME` instruction](https://docs.docker.com/engine/reference/builder/#volume)
|
|
53
|
+and towards the bottom is this little tidbit:
|
|
54
|
+
|
|
55
|
+> Changing the volume from within the Dockerfile: If any build steps change
|
|
56
|
+> the data within the volume after it has been declared, those changes will
|
|
57
|
+> be discarded.
|
|
58
|
+
|
|
59
|
+As soon as Docker hits `VOLUME` instruction, the directory becomes a mount
|
|
60
|
+point, and anything we do to the temporary volume mounted there is discarded
|
|
61
|
+during the build process. So we have to change the ownership *before* the
|
|
62
|
+instruction, which may seem a little counter-intuitive:
|
|
63
|
+
|
|
64
|
+{{< highlight docker >}}
|
|
65
|
+FROM alpine
|
|
66
|
+RUN adduser -D -u 1113 test123
|
|
67
|
+RUN mkdir /testing && chown test123 /testing
|
|
68
|
+VOLUME /testing
|
|
69
|
+{{< / highlight >}}
|
|
70
|
+
|
|
71
|
+Now when the container runs, the /testing directory is owned by the test123
|
|
72
|
+user. It's not quite over, yet, though. This works if we let Docker create
|
|
73
|
+a volume automatically for us, or if we create a named volume and mount that;
|
|
74
|
+if we try and mount a host directory, though, it falls flat:
|
|
75
|
+
|
|
76
|
+{{< highlight console >}}
|
|
77
|
+$ docker run --rm -it -v "$PWD/testing:/testing" testing ls -al /testing
|
|
78
|
+total 8
|
|
79
|
+drwxr-xr-x 2 1000 1000 4096 Apr 1 19:39 .
|
|
80
|
+drwxr-xr-x 1 root root 4096 Apr 1 20:44 ..
|
|
81
|
+{{< / highlight >}}
|
|
82
|
+
|
|
83
|
+Docker handles mounting host directories differently to mounting volumes,
|
|
84
|
+even though the syntax is basically the same. Host directories are bind
|
|
85
|
+mounted directly into the container, so the permissions and ownership
|
|
86
|
+are the same as the directory on your host. The only way to fix them are
|
|
87
|
+to either change the permissions on the host, or have the container
|
|
88
|
+change them at runtime (assuming it has sufficient privileges).
|
|
89
|
+
|
|
90
|
+One final wrinkle in all this happens when you use the same container
|
|
91
|
+in multiple containers. Here we have two images built from the
|
|
92
|
+Dockerfile above, one with userid 1113 and one with userid 1114:
|
|
93
|
+
|
|
94
|
+{{< highlight console >}}
|
|
95
|
+$ docker volume create testing
|
|
96
|
+testing
|
|
97
|
+
|
|
98
|
+$ docker run --rm -it -v testing:/testing testing1113 ls -nal /testing
|
|
99
|
+total 8
|
|
100
|
+drwxr-xr-x 2 1113 0 4096 Apr 1 19:49 .
|
|
101
|
+drwxr-xr-x 1 0 0 4096 Apr 1 20:51 ..
|
|
102
|
+
|
|
103
|
+$ docker run --rm -it -v testing:/testing testing1114 ls -nal /testing
|
|
104
|
+total 8
|
|
105
|
+drwxr-xr-x 2 1114 0 4096 Apr 1 20:47 .
|
|
106
|
+drwxr-xr-x 1 0 0 4096 Apr 1 20:52 ..
|
|
107
|
+
|
|
108
|
+$ docker run --rm -it -v testing:/testing testing1114 touch /testing/Hello
|
|
109
|
+
|
|
110
|
+$ docker run --rm -it -v testing:/testing testing1113 ls -nal /testing
|
|
111
|
+total 8
|
|
112
|
+drwxr-xr-x 2 1114 0 4096 Apr 1 20:52 .
|
|
113
|
+drwxr-xr-x 1 0 0 4096 Apr 1 20:53 ..
|
|
114
|
+-rw-r--r-- 1 0 0 0 Apr 1 20:52 Hello
|
|
115
|
+
|
|
116
|
+$ docker run --rm -it -v testing:/testing testing1114 ls -nal /testing
|
|
117
|
+total 8
|
|
118
|
+drwxr-xr-x 2 1114 0 4096 Apr 1 20:52 .
|
|
119
|
+drwxr-xr-x 1 0 0 4096 Apr 1 20:52 ..
|
|
120
|
+-rw-r--r-- 1 0 0 0 Apr 1 20:52 Hello
|
|
121
|
+{{< / highlight >}}
|
|
122
|
+
|
|
123
|
+Can you see what's going on? When the volume is empty, the ownership
|
|
124
|
+changes based on the mount point in the container. Once it has something
|
|
125
|
+in it, the ownership is fixed.
|
|
126
|
+
|
|
127
|
+So Docker behaves differently with regard to permissions:
|
|
128
|
+
|
|
129
|
+ * when the folder is mounted from the host vs a volume
|
|
130
|
+ * when the volume is empty vs having content
|
|
131
|
+
|
|
132
|
+### Pre-populating mounts with files from the image
|
|
133
|
+
|
|
134
|
+One of the more esoteric features of the way Docker handles volume
|
|
135
|
+mounts is that in some cases files from the image are copied over
|
|
136
|
+into the container. For example:
|
|
137
|
+
|
|
138
|
+{{< highlight console >}}
|
|
139
|
+$ docker volume create testing
|
|
140
|
+testing
|
|
141
|
+
|
|
142
|
+$ docker run --rm -it -v testing:/etc testing sleep 1
|
|
143
|
+
|
|
144
|
+$ docker run --rm -it -v testing:/tmp testing ls -al /tmp
|
|
145
|
+total 184
|
|
146
|
+drwxr-xr-x 15 root root 4096 Apr 1 20:58 .
|
|
147
|
+drwxr-xr-x 1 root root 4096 Apr 1 20:59 ..
|
|
148
|
+-rw-r--r-- 1 root root 4 Jun 7 2018 TZ
|
|
149
|
+-rw-r--r-- 1 root root 6 Dec 20 21:31 alpine-release
|
|
150
|
+...
|
|
151
|
+{{< / highlight >}}
|
|
152
|
+
|
|
153
|
+The first container we run mounts the newly created `testing` volume
|
|
154
|
+at `/etc`. Docker copies all the existing files and folders into the
|
|
155
|
+volume; when we then run the second container with the volume mounted
|
|
156
|
+at `/tmp`, we can see all of the files that were in the first container's
|
|
157
|
+`/etc`.
|
|
158
|
+
|
|
159
|
+As with permissions, this behaviour is anything but consistent. Say we
|
|
160
|
+switch from a volume to a host directory:
|
|
161
|
+
|
|
162
|
+{{< highlight console >}}
|
|
163
|
+$ mkdir testing
|
|
164
|
+$ docker run --rm -it -v "$PWD/testing:/usr/bin" testing sleep 1
|
|
165
|
+$ ls -al testing
|
|
166
|
+total 8
|
|
167
|
+drwxr-xr-x 2 root root 4096 Apr 1 22:05 .
|
|
168
|
+drwxr-xr-x 3 chris chris 4096 Apr 1 22:05 ..
|
|
169
|
+{{< / highlight >}}
|
|
170
|
+
|
|
171
|
+Nothing is copied in, and inside the container the folder will be empty.
|
|
172
|
+Based on our discoveries with permisions, it's reasonable to assume the
|
|
173
|
+same will happy with a non-empty volume too:
|
|
174
|
+
|
|
175
|
+{{< highlight console >}}
|
|
176
|
+$ docker volume create testing
|
|
177
|
+testing
|
|
178
|
+
|
|
179
|
+$ docker run --rm -it -v testing:/testing testing touch /testing/Hello
|
|
180
|
+
|
|
181
|
+$ docker run --rm -it -v testing:/usr/bin testing sleep 1
|
|
182
|
+
|
|
183
|
+$ docker run --rm -it -v testing:/tmp testing ls -al /tmp
|
|
184
|
+total 8
|
|
185
|
+drwxr-xr-x 2 root root 4096 Apr 1 21:09 .
|
|
186
|
+drwxr-xr-x 1 root root 4096 Apr 1 21:09 ..
|
|
187
|
+-rw-r--r-- 1 root root 0 Apr 1 21:08 Hello
|
|
188
|
+{{< / highlight >}}
|
|
189
|
+
|
|
190
|
+So at least that's consistent. If you're very observant, though, you
|
|
191
|
+might notice I switched from `/etc/` to `/usr/bin` in the examples.
|
|
192
|
+That's because within the container `/etc/` has some files bind-mounted
|
|
193
|
+into it, such as `/etc/resolv.conf`, and these *do* always result in files
|
|
194
|
+being created in the mounted volumes or folders:
|
|
195
|
+
|
|
196
|
+{{< highlight console >}}
|
|
197
|
+$ mkdir testing
|
|
198
|
+$ docker run --rm -it -v "$PWD/testing:/etc" testing sleep 1
|
|
199
|
+$ ls -al testing
|
|
200
|
+total 8
|
|
201
|
+drwxr-xr-x 2 chris chris 4096 Apr 1 22:12 .
|
|
202
|
+drwxr-xr-x 3 chris chris 4096 Apr 1 22:12 ..
|
|
203
|
+-rwxr-xr-x 1 root root 0 Apr 1 22:12 hostname
|
|
204
|
+-rwxr-xr-x 1 root root 0 Apr 1 22:12 hosts
|
|
205
|
+-rwxr-xr-x 1 root root 0 Apr 1 22:12 resolv.conf
|
|
206
|
+{{< / highlight >}}
|
|
207
|
+
|
|
208
|
+### Summary
|
|
209
|
+
|
|
210
|
+ * Docker treats mounting host folders and mounting volumes differently.
|
|
211
|
+ Don't just assume that you can swap one for another and get the exact
|
|
212
|
+ same behaviour.
|
|
213
|
+ * Empty volumes will inherit permissions and files from the image
|
|
214
|
+ they are mounted in; non-empty volumes and host folders will not.
|
|
215
|
+ * Relying on Docker copying files into volumes is a very bad idea,
|
|
216
|
+ as if you change those files in a future version of your image
|
|
217
|
+ they will not be copied unless the volume is deleted and
|
|
218
|
+ recreated.
|
|
219
|
+
|
|
220
|
+I can't find anywhere that these points are documented properly;
|
|
221
|
+if you know of anywhere, please drop me a message!
|