5
votes

I'm in the process of writing Packer and Terraform code to create an immutable infra on aws. However, it does not seem very straightforward to install ext4 on a disk and mount it.

The steps seem simple:

  • Creating the ami with packer on t2.micro that contains all software, to be used first on test and afterwards on production.
  • Launch a r3.4xlarge instance from this ami that has a 300GB ephemeral disk. Format this disk as ext4, mount it and redirect /var/lib/docker to the new filesystem for performance reasons.
  • Complete the rest of the application launching.

First of all:

Is it best practice to create the ami with the same instance type you will use it for or to have one 'generic' image and start multipe instance types from that? What philosophy is the best?

  • packer(software versions) -> terraform(instance + mount disk) -> deploy?
  • packer(software versions) -> packer(instancetype specific mounts) -> terraform(instance) -> deploy?
  • packer(software versions, instance specific mounts) -> terraform -> deploy?

The latter is starting to look better and better but requires an ami per instance type.

What I have tried so far:

According to this answer it is better to use the user_data way of working instead of the provisioners way. So I'm going down that road.

This answer seemed promising but is so old it does not work anymore. I could update it but there might be a different, better way.

This answer also seemed promising but was complaining about the ${DEVICE}. I am wondering where that variable is coming from as there are no vars specified in the template_file. If I set my own DEVICE variable to xvdb then it runs, but does not produce a result because xvdb is visible in lsblk but not in blkid.

Here is my code. The format_disks.sh file is the same as the one mentioned above. Any help is greatly appreciated.

# Create a new instance of the latest Ubuntu 16.04 on an
# t2.micro node with an AWS Tag naming it "test1"
provider "aws" {
  region = "us-east-1"
}

data "template_file" "format-disks" {
  template = "${file("format_disk.sh")}"

  vars {
    DEVICE = "xvdb"
  }
}

resource "aws_instance" "test1" {
  ami           = "ami-98181234"
  instance_type = "r3.4xlarge"
  key_name = "keypair-1"               # This needs to be changed so multiple users can use this
  subnet_id = "subnet-a0aeb123"            # maps to the vpc for the us production
  associate_public_ip_address = "true"
  vpc_security_group_ids = ["sg-f3e91234"] #backendservers
  user_data = "${data.template_file.format-disks.rendered}"
  tags {
    Name = "test1"
  }
  ephemeral_block_device {
    device_name = "xvdb"
    virtual_name = "ephemeral0"
  }
}
1
In this situation I'd integrate that format_disk.sh script into your AMI with packer, along with some configuration to make it run on boot, and take this out of Terraform's hands altogether. Terraform would then just arrange for the disk to be attached, and the software in the AMI would take care of getting it formatted as part of its startup. This way you can also more easily coordinate that operation with any other application startup tasks, to ensure that e.g. an app that needs the filesystem doesn't boot until after the filesystem is formatted and mounted.Martin Atkins
That can be helpful indeed, thank you @MartinAtkins . I currently do a workaround where I start the instance with everything installed/configured and once it's up I run two ansible roles to format/map and move the application that needs to run on the local disk. Unfortunately this means that Terraform cannot do a rolling update of all instances. I will see if I can get your suggestion working.Evert
@Evert Can You please share that Ansible roles here So that i can reuse I have similar kind of requirement.Santosh Garole
Hello @SantoshGarole Unfortunately I no longer work at the same company so cannot send you those roles. They were quite basic though, I'm sure you can find similar examples elsewhere. Good luck.Evert

1 Answers

1
votes

Let me give you my thoughts about this topic.

I think the cloud-init is the key to AWS because you can create the machine you want dynamically. First, try to change some global script, will be used when your machine is starting. Then, you should add that script as user data I suggest you play with ec2 autoscaling at the same time, so, if you change the cloud-init script, you may terminate the instance, another one will be created automatically.

My structure directories.

.
|____main.tf
|____templates
| |____cloud-init.tpl

main.tf

provider "aws" {
  region = "us-east-1"
}

data "template_file" "cloud_init" {
  template = file("${path.module}/templates/cloud-init.tpl")
}

data "aws_ami" "linux_ami" {
  most_recent = "true"
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-2.0.????????.?-x86_64-gp2"]
  }
}

resource "aws_instance" "test1" {
  ami                         = data.aws_ami.linux_ami.image_id
  instance_type               = "r3.4xlarge"
  key_name                    = "keypair-1"       
  subnet_id                   = "subnet-xxxxxx"
  associate_public_ip_address = true
  vpc_security_group_ids      = ["sg-xxxxxxx"] 
  user_data                   = data.template_file.cloud_init.rendered
  root_block_device {
      delete_on_termination = true
      encrypted             = true
      volume_size           = 10
      volume_type           = "gp2"
  }

  ebs_block_device {
      device_name = "ebs-block-device-name"
      delete_on_termination = true
      encrypted             = true
      volume_size           = 10
      volume_type           = "gp2"
  }

  network_interface {
      device_index          = 0
      network_interface_id  = var.network_interface_id
      delete_on_termination = true
  }

  tags = {
    Name = "test1"
    costCenter = "xxxxx"
    owner = "xxxxx"
  }
}

templates/cloud-init.tpl

#!/bin/bash -x 

yum update -y
yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent

pip install aws-ssm-tunnel-agent

echo "[INFO] SSM agent has been installed!"
# More scripts here.

Would you like to have a temporal disk attached? Have you tried to add a root_block_device with delete_on_termination with a true as value? This way after destroying the aws ec2 instance resource, the disk will be deleted. It's a good way to save costs on AWS but be carefull, Just use it if the data stored on isn't important or if you've backed up.

If you need to attach an external ebs disk on this instance, you can use the AWS API, make sure you have the machine in the same AZ that the disk you can use it.

Let me know if you need some bash script but this is straightforward to do.