Creates a standalone Docker host on EC2, optionally attaching an external EBS volume for persistent data.
This is convenient for quickly setting up non-production-critical Docker workloads. If you need something fancier, consider e.g. ECS, EKS or Fargate.
Assuming you have the AWS provider set up:
module "my_host" {
# Available inputs: https://github.com/futurice/terraform-utils/tree/master/aws_ec2_ebs_docker_host#inputs
# Check for updates: https://github.com/futurice/terraform-utils/compare/v11.0...master
source = "git::ssh://[email protected]/futurice/terraform-utils.git//aws_ec2_ebs_docker_host?ref=v11.0"
hostname = "my-docker-host"
ssh_private_key_path = "~/.ssh/id_rsa" # if you use shared Terraform state, consider changing this to something that doesn't depend on "~"
ssh_public_key_path = "~/.ssh/id_rsa.pub"
allow_incoming_http = true # by default, only incoming SSH is allowed; other protocols for the security group are opt-in
}
output "host_ssh_command" {
description = "Run this command to create a port-forward to the remote docker daemon"
value = "ssh -i ${module.my_host.ssh_private_key_path} -o StrictHostKeyChecking=no -L localhost:2377:/var/run/docker.sock ${module.my_host.ssh_username}@${module.my_host.public_ip}"
}
After terraform apply
, and running the host_ssh_command
, you should be able to connect from your local Docker CLI to the remote daemon, e.g.:
$ DOCKER_HOST=localhost:2377 docker run -d -p 80:80 nginx
Visit the IP address of your host in a browser to make sure it works.
Assuming you have the AWS provider set up:
resource "aws_ebs_volume" "my_data" {
availability_zone = "${module.my_host.availability_zone}" # ensure the volume is created in the same AZ the docker host
type = "gp2" # i.e. "Amazon EBS General Purpose SSD"
size = 25 # in GiB; if you change this in-place, you need to SSH over and run e.g. $ sudo resize2fs /dev/xvdh
}
module "my_host" {
# Available inputs: https://github.com/futurice/terraform-utils/tree/master/aws_ec2_ebs_docker_host#inputs
# Check for updates: https://github.com/futurice/terraform-utils/compare/v11.0...master
source = "git::ssh://[email protected]/futurice/terraform-utils.git//aws_ec2_ebs_docker_host?ref=v11.0"
hostname = "my-host"
ssh_private_key_path = "~/.ssh/id_rsa" # note that with a shared Terraform state, paths with "~" will become problematic
ssh_public_key_path = "~/.ssh/id_rsa.pub"
data_volume_id = "${aws_ebs_volume.my_data.id}" # attach our EBS data volume
}
output "host_ssh_command" {
description = "Run this command to check that the data volume got mounted"
value = "ssh -i ${module.my_host.ssh_private_key_path} -o StrictHostKeyChecking=no ${module.my_host.ssh_username}@${module.my_host.public_ip} df -h"
}
Note that due to a bug in Terraform, at the time of writing, you need to apply in two parts:
$ terraform apply -target aws_ebs_volume.my_data
...
$ terraform apply
...
Afterwards, running the host_ssh_command
should give you something like:
Filesystem Size Used Avail Use% Mounted on
udev 481M 0 481M 0% /dev
tmpfs 99M 752K 98M 1% /run
/dev/xvda1 7.7G 2.1G 5.7G 27% /
tmpfs 492M 0 492M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 492M 0 492M 0% /sys/fs/cgroup
/dev/loop0 88M 88M 0 100% /snap/core/5328
/dev/loop1 13M 13M 0 100% /snap/amazon-ssm-agent/495
/dev/xvdh 25G 45M 24G 1% /data
tmpfs 99M 0 99M 0% /run/user/1000
That is, you can see the 25 GB data volume mounted at /data
.
Assuming you have the AWS provider set up:
module "my_host" {
# Available inputs: https://github.com/futurice/terraform-utils/tree/master/aws_ec2_ebs_docker_host#inputs
# Check for updates: https://github.com/futurice/terraform-utils/compare/v11.0...master
source = "git::ssh://[email protected]/futurice/terraform-utils.git//aws_ec2_ebs_docker_host?ref=v11.0"
hostname = "my-docker-host"
ssh_private_key_path = "~/.ssh/id_rsa"
ssh_public_key_path = "~/.ssh/id_rsa.pub"
}
resource "null_resource" "provisioners" {
depends_on = ["module.my_host"] # wait until other provisioners within the module have finished
connection {
host = "${module.my_host.public_ip}"
user = "${module.my_host.ssh_username}"
private_key = "${module.my_host.ssh_private_key}"
agent = false
}
provisioner "remote-exec" {
inline = ["echo HELLO WORLD"]
}
}
Note that until direct support for the SSH protocol in the docker
provider lands in Terraform, this is a bit cumbersome. But it's documented here in case it's useful.
Assuming you have the AWS provider set up:
module "my_host" {
# Available inputs: https://github.com/futurice/terraform-utils/tree/master/aws_ec2_ebs_docker_host#inputs
# Check for updates: https://github.com/futurice/terraform-utils/compare/v11.0...master
source = "git::ssh://[email protected]/futurice/terraform-utils.git//aws_ec2_ebs_docker_host?ref=v11.0"
hostname = "my-docker-host"
ssh_private_key_path = "~/.ssh/id_rsa"
ssh_public_key_path = "~/.ssh/id_rsa.pub"
allow_incoming_http = true
}
output "docker_tunnel_command" {
description = "Run this command to create a port-forward to the remote docker daemon"
value = "ssh -i ${module.my_host.ssh_private_key_path} -o StrictHostKeyChecking=no -L localhost:2377:/var/run/docker.sock ${module.my_host.ssh_username}@${module.my_host.public_ip}"
}
provider "docker" {
version = "~> 1.1"
host = "tcp://127.0.0.1:2377/" # Important: this is expected to be an SSH tunnel; see "docker_tunnel_command" in $ terraform output
}
resource "docker_image" "nginx" {
name = "nginx"
}
resource "docker_container" "nginx" {
image = "${docker_image.nginx.latest}"
name = "nginx"
must_run = true
ports {
internal = 80
external = 80
}
}
output "test_link" {
value = "http://${module.my_host.public_ip}/"
}
Because the tunnel won't exist before the host is up, this needs to be applied with:
$ terraform apply -target module.my_host
This should finish by giving you the docker_tunnel_command
output. Run that in another terminal, and then finish with another terraform apply
. Afterwards, you should be able to visit the test_link
and see nginx greeting you.
Name | Description | Type | Default | Required |
---|---|---|---|---|
allow_incoming_dns | Whether to allow incoming DNS traffic on the host security group | string | "false" |
no |
allow_incoming_http | Whether to allow incoming HTTP traffic on the host security group | string | "false" |
no |
allow_incoming_https | Whether to allow incoming HTTPS traffic on the host security group | string | "false" |
no |
data_volume_id | The ID of the EBS volume to mount as /data |
string | "" |
no |
hostname | Hostname by which this service is identified in metrics, logs etc | string | "aws-ec2-ebs-docker-host" |
no |
instance_ami | See https://cloud-images.ubuntu.com/locator/ec2/ for options | string | "ami-0bdf93799014acdc4" |
no |
instance_type | See https://aws.amazon.com/ec2/instance-types/ for options; for example, typical values for small workloads are "t2.nano" , "t2.micro" , "t2.small" , "t2.medium" , and "t2.large" |
string | "t2.micro" |
no |
reprovision_trigger | An arbitrary string value; when this value changes, the host needs to be reprovisioned | string | "" |
no |
root_volume_size | Size (in GiB) of the EBS volume that will be created and mounted as the root fs for the host | string | "8" |
no |
ssh_private_key_path | SSH private key file path, relative to Terraform project root | string | "ssh.private.key" |
no |
ssh_public_key_path | SSH public key file path, relative to Terraform project root | string | "ssh.public.key" |
no |
ssh_username | Default username built into the AMI (see 'instance_ami') | string | "ubuntu" |
no |
swap_file_size | Size of the swap file allocated on the root volume | string | "512M" |
no |
swap_swappiness | Swappiness value provided when creating the swap file | string | "10" |
no |
tags | AWS Tags to add to all resources created (where possible); see https://aws.amazon.com/answers/account-management/aws-tagging-strategies/ | map | <map> |
no |
vpc_id | ID of the VPC our host should join; if empty, joins your Default VPC | string | "" |
no |
Name | Description |
---|---|
availability_zone | AWS Availability Zone in which the EC2 instance was created |
hostname | Hostname by which this service is identified in metrics, logs etc |
instance_id | AWS ID for the EC2 instance used |
public_ip | Public IP address assigned to the host by EC2 |
security_group_id | Security Group ID, for attaching additional security rules externally |
ssh_private_key | SSH private key that can be used to access the EC2 instance |
ssh_private_key_path | Path to SSH private key that can be used to access the EC2 instance |
ssh_username | Username that can be used to access the EC2 instance over SSH |