Mounting an EBS volume to Docker on AWS Elastic Beanstal
Mounting an EBS volume to a Docker instance running on Amazon Elastic Beanstalk (EB) is surprisingly tricky. The good news is that it is possible.
I will describe how to automatically create and mount a new EBS volume (optionally based on a snapshot). If you would prefer to mount a specific, existing EBS volume, you should check out leg100's docker-ebs-attach (using AWS API to mount the volume) that you can use either in a multi-container setup or just include the relevant parts in your own Dockerfile.
The problem with EBS volumes is that, if I am correct, a volume can only be mounted to a single EC2 instance - and thus doesn't play well with EB's autoscaling. That is why EB supports only creating and mounting a fresh volume for each instance.
Why would you want to use an auto-created EBS volume? You can already use a docker VOLUME to mount a directory on the host system's ephemeral storage to make data persistent across docker restarts/redeploys. The only advantage of EBS is that it survives restarts of the EC2 instance but that is something that, I suppose, happens rarely. I suspect that in most cases EB actually creates a new EC2 instance and then destroys the old one. One possible benefit of an EBS volume is that you can take a snapshot of it and use that to launch future instances. I'm now inclined to believe that a better solution in most cases is to set up automatic backup to and restore from S3, f.ex. using duplicity with its S3 backend (as I do for my NAS).
Anyway, here is how I got EBS volume mounting working. There are 4 parts to the solution:
1-3.: .ebextensions/01-ebs.config:
4.: Dockerrun.aws.json and Dockerfile:
I will describe how to automatically create and mount a new EBS volume (optionally based on a snapshot). If you would prefer to mount a specific, existing EBS volume, you should check out leg100's docker-ebs-attach (using AWS API to mount the volume) that you can use either in a multi-container setup or just include the relevant parts in your own Dockerfile.
The problem with EBS volumes is that, if I am correct, a volume can only be mounted to a single EC2 instance - and thus doesn't play well with EB's autoscaling. That is why EB supports only creating and mounting a fresh volume for each instance.
Why would you want to use an auto-created EBS volume? You can already use a docker VOLUME to mount a directory on the host system's ephemeral storage to make data persistent across docker restarts/redeploys. The only advantage of EBS is that it survives restarts of the EC2 instance but that is something that, I suppose, happens rarely. I suspect that in most cases EB actually creates a new EC2 instance and then destroys the old one. One possible benefit of an EBS volume is that you can take a snapshot of it and use that to launch future instances. I'm now inclined to believe that a better solution in most cases is to set up automatic backup to and restore from S3, f.ex. using duplicity with its S3 backend (as I do for my NAS).
Anyway, here is how I got EBS volume mounting working. There are 4 parts to the solution:
- Configure EB to create an EBS mount for your instances
- Add custom EB commands to format and mount the volume upon first use
- Restart the Docker daemon after the volume is mounted so that it will see it (see this discussion)
- Configure Docker to mount the (mounted) volume inside the container
1-3.: .ebextensions/01-ebs.config:
# .ebextensions/01-ebs.config
commands:
01format-volume:
command: mkfs -t ext3 /dev/sdh
test: file -sL /dev/sdh | grep -v 'ext3 filesystem'
# ^ prints '/dev/sdh: data' if not formatted
02attach-volume:
### Note: The volume may be renamed by the Kernel, e.g. sdh -> xvdh but
# /dev/ will then contain a symlink from the old to the new name
command: |
mkdir /media/ebs_volume
mount /dev/sdh /media/ebs_volume
service docker restart # We must restart Docker daemon or it wont' see the new mount
test: sh -c "! grep -qs '/media/ebs_volume' /proc/mounts"
option_settings:
# Tell EB to create a 100GB volume and mount it to /dev/sdh
- namespace: aws:autoscaling:launchconfiguration
option_name: BlockDeviceMappings
value: /dev/sdh=:100
4.: Dockerrun.aws.json and Dockerfile:
Dockerrun.aws.json
: mount the host's /media/ebs_volume
as /var/easydeploy/share
inside the container:
{
"AWSEBDockerrunVersion": "1",
"Volumes": [
{
"HostDirectory": "/media/ebs_volume",
"ContainerDirectory": "/var/easydeploy/share"
}
]
}
Dockerfile
: Tell Docker to use a directory on the host system as /var/easydeploy/share
- either a randomly generated one or the one given via the -m
mount option to docker run
:
...
VOLUME ["/var/easydeploy/share"]
...