{"id":85,"date":"2018-07-15T03:28:55","date_gmt":"2018-07-15T03:28:55","guid":{"rendered":"http:\/\/edspace.com\/blog\/?p=85"},"modified":"2018-09-26T01:51:15","modified_gmt":"2018-09-26T01:51:15","slug":"persisting-storage-in-docker","status":"publish","type":"post","link":"http:\/\/edspace.com\/blog\/2018\/07\/15\/persisting-storage-in-docker\/","title":{"rendered":"Persisting Storage in Docker"},"content":{"rendered":"<p>Generally speaking, Docker containers should have everything they need access to baked into the image. There are times, however, that it may be necessary to have additional files or directories provided to the container to persist information. These can include, but are not limited to:<\/p>\n<ul>\n<li>Configuration files<\/li>\n<li>Data persistence (usually only for local databases for development)<\/li>\n<li>Application package hotswap during development<\/li>\n<li>Saving artifacts generated by the application<\/li>\n<\/ul>\n<p>Docker has two ways to provide such storage: <i><strong>bind mounts<\/strong><\/i> and <i><strong>volumes<\/strong><\/i>.<\/p>\n<h2>Bind Mounts<\/h2>\n<p>Bind mounts are used to provide access to a directory on the host machine. On a Linux host, Docker allows you to bind a user defined directory into the root filesystem of the container, effectively allowing you to do the equivalent of <i>mount &#8211;bind<\/i> for your directly to link it directly to the container&#8217;s filesystem. This is ideal for providing custom configuration files or saving off build artifacts to your host directory. To mount a directory into a container, execute the following on an example container:<\/p>\n<pre class=\"lang:sh decode:true\">mkdir -p testsite\r\necho \"Hello, world!\" &gt; testsite\/index.html\r\ndocker run -d --rm --name test --mount type=bind,source=$(pwd)\/testsite,target=\/usr\/share\/nginx\/html -p 80:80 nginx<\/pre>\n<p>This creates a simple dummy site and then pulls down the\u00a0<span class=\"lang:default decode:true crayon-inline\">nginx<\/span> image, running it with the content of our simple site. If you open your web browser to <a href=\"http:\/\/localhost\/\">http:\/\/localhost<\/a>, you will see the &#8220;Hello, world!&#8221; message that we left in our sample directory. Alternatively, instead of the\u00a0<span class=\"lang:default decode:true crayon-inline\">&#8211;mount<\/span> option, you can use the older style <em>-v<\/em> syntax:<\/p>\n<pre class=\"lang:sh decode:true\">docker run -d --rm --name test -v $(pwd)\/testsite:\/usr\/share\/nginx\/html -p 80:80 nginx<\/pre>\n<p>It is recommended that you use the\u00a0<span class=\"lang:default decode:true crayon-inline \">&#8211;mount<\/span>\u00a0 option as it is more precise in its definition. The <span class=\"lang:default decode:true crayon-inline \">-v<\/span>\u00a0 option is only still available for legacy purposes.<\/p>\n<p>We can inspect the container and see that the mount is defined for our container by using <span class=\"lang:default decode:true crayon-inline \">docker inspect test<\/span>\u00a0:<\/p>\n<pre class=\"lang:js decode:true\">...\r\n        \"Mounts\": [\r\n            {\r\n                \"Type\": \"bind\",\r\n                \"Source\": \"\/tmp\/testsite\",\r\n                \"Destination\": \"\/usr\/share\/nginx\/html\",\r\n                \"Mode\": \"\",\r\n                \"RW\": true,\r\n                \"Propagation\": \"rprivate\"\r\n            }\r\n        ],\r\n...<\/pre>\n<p>We can specify bind mounts in a compose file as such:<\/p>\n<pre class=\"lang:yaml decode:true\">version: \"3.2\"\r\nservices:\r\n  web:\r\n    image: nginx:alpine\r\n    volumes:\r\n      - type: bind\r\n        source: .\/testsite\r\n        target: \/usr\/share\/nginx\/html\r\n<\/pre>\n<p>You can also map an individual file onto a container, but it is rare to do so. If using the\u00a0<em>-v<\/em> syntax, note that if the file is missing, a directory will be created with the name of the file that you specify. This can be confounding if you use this in a Compose file. More can be found on the Docker website:<\/p>\n<p><a href=\"https:\/\/docs.docker.com\/storage\/bind-mounts\/\">https:\/\/docs.docker.com\/storage\/bind-mounts\/<\/a><\/p>\n<p>Bind mounts on Docker for Mac do not use native bind mounting, but instead uses <i>osxfs <\/i>to attempt to provide a near-native experience for bind mounts. It is still slower than a native bind mount running on Linux, but should still work seamlessly with local HFS+ filesystems. By default, it only has access to the <i>\/Users, \/Volumes, \/private, <\/i>and <i>\/tmp<\/i> directories. See official documentation details on Docker&#8217;s official website:<\/p>\n<p><a href=\"https:\/\/docs.docker.com\/docker-for-mac\/osxfs\/\">https:\/\/docs.docker.com\/docker-for-mac\/osxfs\/<\/a><\/p>\n<h2>Docker Volumes<\/h2>\n<p>Docker volumes are file system mounts that are managed completely by the Docker engine. Historically, these have been called &#8220;named volumes,&#8221; just in case you see a reference to it in literature or in command line help or error messages. When a Docker volume is created, the directory is stored in the <span class=\"lang:default decode:true crayon-inline\">\/var\/lib\/docker\/volumes\/<\/span>\u00a0 directory. The typical use case for a named volume would be for something like data persistence or sharing data between containers. Let&#8217;s dig out the Pastr app from the first tutorial. We&#8217;ll add the mount in the <span class=\"lang:default decode:true crayon-inline \">docker-compose.yml<\/span>\u00a0 file:<\/p>\n<pre class=\"lang:yaml decode:true\">  database:\r\n    image: redis:latest\r\n    volumes:\r\n      - type: volume\r\n        source: pastrdatastore\r\n        target: \/data\r\n    ports:\r\n      - \"6379:6379\"\r\n...\r\nvolumes:\r\n  pastrdatastore:<\/pre>\n<p>The top-level <em>volumes<\/em> directive (at the bottom of the snippet) denotes that a datastore shall be created via this compose file. After starting the container with <span class=\"lang:default decode:true crayon-inline \">docker-compose up -d<\/span> the Docker engine will create the <em>pastrdatastore<\/em> volume.<\/p>\n<pre class=\"lang:sh decode:true \">$ docker volume ls\r\nDRIVER              VOLUME NAME\r\nlocal               pastr_pastrdatastore\r\n$ docker volume inspect pastr_pastrdatastore \r\n[\r\n    {\r\n        \"CreatedAt\": \"2018-09-25T20:13:41-05:00\",\r\n        \"Driver\": \"local\",\r\n        \"Labels\": {\r\n            \"com.docker.compose.project\": \"pastr\",\r\n            \"com.docker.compose.version\": \"1.22.0\",\r\n            \"com.docker.compose.volume\": \"pastrdatastore\"\r\n        },\r\n        \"Mountpoint\": \"\/var\/lib\/docker\/volumes\/pastr_pastrdatastore\/_data\",\r\n        \"Name\": \"pastr_pastrdatastore\",\r\n        \"Options\": null,\r\n        \"Scope\": \"local\"\r\n    }\r\n]\r\n<\/pre>\n<p>It creates it a volume in the\u00a0 <span class=\"lang:default decode:true  crayon-inline\">\/var\/lib\/docker\/volumes<\/span>\u00a0 directory. This volume is mounted to the database container, which we see when we inspect it with <span class=\"lang:default decode:true  crayon-inline \">docker inspect pastr_database_1<\/span>\u00a0.<\/p>\n<pre class=\"lang:default decode:true \">        \"Mounts\": [\r\n            {\r\n                \"Type\": \"volume\",\r\n                \"Name\": \"pastr_pastrdatastore\",\r\n                \"Source\": \"\/var\/lib\/docker\/volumes\/pastr_pastrdatastore\/_data\",\r\n                \"Destination\": \"\/data\",\r\n                \"Driver\": \"local\",\r\n                \"Mode\": \"rw\",\r\n                \"RW\": true,\r\n                \"Propagation\": \"\"\r\n            }\r\n        ],\r\n<\/pre>\n<p>Note that on a Linux machine, this volume exists on the native filesystem. However, on a Windows or Mac system, this volume exists within the virtual machine; you can&#8217;t access it directly, nor should you try to, even on a Linux machine. If you need to mount the data store to inspect its contents, you can run it with\u00a0<span class=\"lang:default decode:true crayon-inline\">docker run -it &#8211;rm &#8211;mount source=pastr_pastrdatastore,destination=\/mnt ubuntu \/bin\/bash<\/span>.<\/p>\n<p>For more details, please see the official Docker documentation:<\/p>\n<p><a href=\"https:\/\/docs.docker.com\/storage\/volumes\/#start-a-service-with-volumes\">https:\/\/docs.docker.com\/storage\/volumes\/#start-a-service-with-volumes<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Generally speaking, Docker containers should have everything they need access to baked into the image. There are times, however, that it may be necessary to have additional files or directories provided to the container to persist information. These can include, but are not limited to: Configuration files Data persistence (usually only for local databases for &hellip; <a href=\"http:\/\/edspace.com\/blog\/2018\/07\/15\/persisting-storage-in-docker\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Persisting Storage in Docker<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[5],"tags":[],"class_list":["post-85","post","type-post","status-publish","format-standard","hentry","category-docker"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/paDvvN-1n","jetpack-related-posts":[],"_links":{"self":[{"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/posts\/85","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/comments?post=85"}],"version-history":[{"count":6,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/posts\/85\/revisions"}],"predecessor-version":[{"id":96,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/posts\/85\/revisions\/96"}],"wp:attachment":[{"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/media?parent=85"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/categories?post=85"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/tags?post=85"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}