Skip to content
This repository was archived by the owner on Jul 11, 2023. It is now read-only.

Support multiple EBS volumes attached and persisted to single node ASG. #319

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions examples/single-node-asg-tester/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.PHONY: init ssh-key apply destroy clean

.DEFAULT_GOAL = help

## Runs terraform get and terraform init for env
init:
@terraform get
@terraform init

## Create ssh key
ssh-key:
@ssh-keygen -q -N "" -C "SSH key for vpc-scenario-1 example" -f ./id_rsa

## use 'terraform apply' to apply the setup.
apply:
@terraform apply

## use 'terraform destroy' to remove all resources from AWS
destroy:
@terraform destroy

## rm -rf all files and state
clean:
@rm -f id_rsa
@rm -f id_rsa.pub
@rm -f terraform.*.backup
@rm -f terraform.tfstate

## Show help screen.
help:
@echo "Please use \`make <target>' where <target> is one of\n\n"
@awk '/^[a-zA-Z\-\_0-9]+:/ { \
helpMessage = match(lastLine, /^## (.*)/); \
if (helpMessage) { \
helpCommand = substr($$1, 0, index($$1, ":")-1); \
helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \
printf "%-30s %s\n", helpCommand, helpMessage; \
} \
} \
{ lastLine = $$0 }' $(MAKEFILE_LIST)
5 changes: 5 additions & 0 deletions examples/single-node-asg-tester/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# single node asg tester

This example shows the basic usage of `single-node-asg` module, especially the multiple EBS attachments.

The module keeps one and only one instance up at all time. And the EBS volumes are reattached when a new instance is up. Hence they are always accessible.
55 changes: 55 additions & 0 deletions examples/single-node-asg-tester/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
locals {
cidr = "192.168.0.0/16"
private_subnet_cidrs = ["192.168.100.0/24", "192.168.101.0/24"]
public_subnet_cidrs = ["192.168.0.0/24", "192.168.1.0/24"]
region = "ap-northeast-1"
}

data "aws_availability_zones" "azs" { }

module "vpc" {
source = "fpco/foundation/aws//modules/vpc-scenario-2"
cidr = local.cidr
public_subnet_cidrs = local.public_subnet_cidrs
private_subnet_cidrs = local.private_subnet_cidrs
azs = data.aws_availability_zones.azs.names
name_prefix = "test"
region = local.region
}

module "ubuntu" {
source = "fpco/foundation/aws//modules/ami-ubuntu"
}

resource "aws_key_pair" "main" {
public_key = file("./id_rsa.pub")
}

resource "aws_security_group" "ssh" {
vpc_id = module.vpc.vpc_id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}

module "tester" {
source = "../../modules/single-node-asg"
name_prefix = "ebs"
name_suffix = "test"
key_name = aws_key_pair.main.key_name
ami = module.ubuntu.id
instance_type = "t2.micro"
subnet_id = module.vpc.public_subnet_ids[0]
security_group_ids = [aws_security_group.ssh.id]
region = local.region
data_volumes = [{ name = "a", device = "/dev/xvdm", size = 50 }, { name = "b", device = "/dev/xvdn" }]
}
1 change: 0 additions & 1 deletion modules/alb-default-forward/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ resource "aws_lb_listener" "lb-listener" {
}

resource "aws_lb_target_group" "lb-tg" {
name = "${var.name_prefix}-tg"
port = var.service_port
protocol = "HTTP"
vpc_id = var.vpc_id
Expand Down
1 change: 0 additions & 1 deletion modules/alb/main.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
resource "aws_lb" "alb" {
name = "${var.name_prefix}-alb"
internal = var.internal
load_balancer_type = "application"
security_groups = [aws_security_group.alb_sg.id]
Expand Down
7 changes: 7 additions & 0 deletions modules/asg/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
locals {
name_prefix_with_suffix = "${var.name_prefix}-${var.name_suffix}"
name_prefix_without_suffix = "${var.name_prefix}"

name_prefix = "${var.name_suffix != "" ? local.name_prefix_without_suffix : local.name_prefix_with_suffix}"

}
4 changes: 2 additions & 2 deletions modules/asg/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ resource "aws_autoscaling_group" "cluster" {
load_balancers = var.elb_names
max_size = var.max_nodes
min_size = var.min_nodes
name_prefix = "${var.name_prefix}-${var.name_suffix}"
name_prefix = local.name_prefix
placement_group = var.placement_group
termination_policies = var.termination_policies
protect_from_scale_in = var.protect_from_scale_in
Expand Down Expand Up @@ -71,7 +71,7 @@ resource "aws_autoscaling_group" "cluster" {
[
{
"key" = "Name"
"value" = "${var.name_prefix}-${var.name_suffix}"
"value" = local.name_prefix
"propagate_at_launch" = true
},
],
Expand Down
10 changes: 10 additions & 0 deletions modules/init-snippet-attach-ebs-volume/instance_id.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
if which wget; then
INSTANCE_ID="$(wget -O- http://169.254.169.254/latest/meta-data/instance-id)"
elif which curl; then
INSTANCE_ID="$(curl http://169.254.169.254/latest/meta-data/instance-id)"
fi

if [ "x$${INSTANCE_ID}" == "x" ]; then
echo 'There is no wget or curl tool installed. Hence bootstrap cannot get instance ID.'
exit 1
fi
66 changes: 14 additions & 52 deletions modules/init-snippet-attach-ebs-volume/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,70 +9,32 @@
*
*/

# variables used by this snippet of init shellcode
variable "device_path" {
default = "/dev/xvdf"
description = "path, to the device's path in /dev/"
type = string
}

variable "init_prefix" {
default = ""
description = "initial init (shellcode) to prefix this snippet with"
type = string
}

variable "init_suffix" {
default = ""
description = "init (shellcode) to append to the end of this snippet"
type = string
}

variable "log_level" {
default = "info"
description = "default log level verbosity for apps that support it"
type = string
}

variable "log_prefix" {
default = "OPS: "
description = "string to prefix log messages with"
type = string
}

variable "region" {
description = "AWS region the volume is in"
type = string
}

variable "wait_interval" {
default = "5"
description = "time (in seconds) to wait when looping to find the device"
type = number
}

variable "volume_id" {
description = "ID of the EBS volume to attach"
type = string
}

# render init script for a cluster using our generic template
data "template_file" "init_snippet" {
count = length(var.volume_ids)

template = file("${path.module}/snippet.tpl")

vars = {
device_path = var.device_path
init_prefix = var.init_prefix
init_suffix = var.init_suffix
device_path = var.device_paths[count.index]
log_prefix = var.log_prefix
log_level = var.log_level
region = var.region
volume_id = var.volume_id
volume_id = var.volume_ids[count.index]
wait_interval = var.wait_interval
}
}

data "template_file" "instance_id" {
template = file("${path.module}/instance_id.tpl")
}

output "init_snippet" {
value = data.template_file.init_snippet.rendered
value = <<EOF
${var.init_prefix}
${data.template_file.instance_id.rendered}
${join("\n", data.template_file.init_snippet.*.rendered)}
${var.init_suffix}
EOF
}

37 changes: 11 additions & 26 deletions modules/init-snippet-attach-ebs-volume/snippet.tpl
Original file line number Diff line number Diff line change
@@ -1,34 +1,19 @@
# start snippet - attach EBS volume
${init_prefix}
export AWS_DEFAULT_REGION=${region}
VOLUME_ID=${volume_id}
if which wget; then
INSTANCE_ID="$(wget -O- http://169.254.169.254/latest/meta-data/instance-id)"
elif which curl; then
INSTANCE_ID="$(curl http://169.254.169.254/latest/meta-data/instance-id)"
fi
echo "${log_prefix} will attach $${VOLUME_ID} via the AWS API in ${region}"
while ! aws ec2 attach-volume \
--volume-id "$${VOLUME_ID}" \
--instance-id "$${INSTANCE_ID}" \
--device '${device_path}'; do
echo "Attaching command failed to run. Retrying."
sleep '${wait_interval}'
done
echo "${log_prefix} $${VOLUME_ID} attached."
sleep '${wait_interval}' # Wait for device up

if [ "x$${INSTANCE_ID}" == "x" ]; then
echo 'OS not functioning'
else
echo "${log_prefix} will attach $${VOLUME_ID} via the AWS API in ${region}"
while ! aws ec2 attach-volume \
--volume-id "$${VOLUME_ID}" \
--instance-id "$${INSTANCE_ID}" \
--device '${device_path}'; do
echo "Attaching command failed to run. Retrying."
sleep '${wait_interval}'
done
echo "${log_prefix} $${VOLUME_ID} attached."

if [ ! -e ${device_path} ]; then
vol_id="$(echo "$${VOLUME_ID}" | tr -d '-')"
while [ ! -e /dev/disk/by-id/*-Amazon_Elastic_Block_Store_$${vol_id} ]; do
sleep '${wait_interval}'
done

dev_id="$(ls /dev/disk/by-id/*-Amazon_Elastic_Block_Store_$${vol_id} | head -1)"
dev_name="/dev/$(readlink "$${dev_id}" | tr / '\n' | tail -1)"
[ "$${dev_name}" == "${device_path}" ] || ln -s "$${dev_name}" "${device_path}"
fi

${init_suffix}
46 changes: 46 additions & 0 deletions modules/init-snippet-attach-ebs-volume/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
variable "device_paths" {
type = list(string)
description = "paths, to the device's path in /dev/"
default = []
}

variable "init_prefix" {
default = ""
description = "initial init (shellcode) to prefix this snippet with"
type = string
}

variable "init_suffix" {
default = ""
description = "init (shellcode) to append to the end of this snippet"
type = string
}

variable "log_level" {
default = "info"
description = "default log level verbosity for apps that support it"
type = string
}

variable "log_prefix" {
default = "OPS: "
description = "string to prefix log messages with"
type = string
}

variable "region" {
description = "AWS region the volume is in"
type = string
}

variable "wait_interval" {
default = "5"
description = "time (in seconds) to wait when looping to find the device"
type = number
}

variable "volume_ids" {
description = "IDs of the EBS volumes to attach"
type = list(string)
default = []
}
6 changes: 2 additions & 4 deletions modules/persistent-ebs/data.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ data "aws_iam_policy_document" "attach_ebs_policy_doc" {
"ec2:DetachVolume",
]

resources = [
"arn:${data.aws_partition.current.partition}:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:volume/${aws_ebs_volume.main.id}",
"arn:${data.aws_partition.current.partition}:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:instance/*",
]
resources = concat(["arn:${data.aws_partition.current.partition}:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:instance/*"],
[for x in aws_ebs_volume.main.*.id : "arn:${data.aws_partition.current.partition}:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:volume/${x}"])
}
}
11 changes: 11 additions & 0 deletions modules/persistent-ebs/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
locals {
volume_default = {
type = "gp2"
iops = 0
size = 15
encrypted = true
kms_key_id = ""
snapshot_id = ""
}
volumes_default = [for x in var.volumes : merge(local.volume_default, x)]
}
16 changes: 9 additions & 7 deletions modules/persistent-ebs/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,27 @@
*/

resource "aws_ebs_volume" "main" {
count = length(local.volumes_default)

availability_zone = var.az
size = var.size
type = var.volume_type
encrypted = var.encrypted
kms_key_id = var.kms_key_id
snapshot_id = var.snapshot_id
size = local.volumes_default[count.index].size
type = local.volumes_default[count.index].type
encrypted = local.volumes_default[count.index].encrypted
kms_key_id = local.volumes_default[count.index].kms_key_id
snapshot_id = local.volumes_default[count.index].snapshot_id

# merge Name w/ extra_tags
tags = merge(
{
"Name" = "${var.name_prefix}-${var.az}"
"Name" = "${var.name_prefix}-${var.az}-${local.volumes_default[count.index].name}"
},
var.extra_tags,
)
}

# IAM policy that allows attaching this EBS volume to an EC2 instance
resource "aws_iam_policy" "attach_ebs" {
name = "${var.name_prefix}-attach-ebs-${aws_ebs_volume.main.id}"
name = "${var.name_prefix}-attach-ebs"
policy = data.aws_iam_policy_document.attach_ebs_policy_doc.json
}

Expand Down
4 changes: 2 additions & 2 deletions modules/persistent-ebs/outputs.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
output "volume_id" {
value = aws_ebs_volume.main.id
output "volume_ids" {
value = aws_ebs_volume.main.*.id
description = "`id` exported from the `aws_ebs_volume`"
}

Expand Down
Loading