Skip to content

Instantly share code, notes, and snippets.

@henrybravo
Last active February 15, 2018 18:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save henrybravo/8e77edf41a1aecc1763f55d1df5fdf95 to your computer and use it in GitHub Desktop.
Save henrybravo/8e77edf41a1aecc1763f55d1df5fdf95 to your computer and use it in GitHub Desktop.
This is an example of creating a custom production ready AWS ECS optimised Amazon Linux AMI with packer.io

Custom Amazon Linux ECS Optimised AMI with packer.io

Packer Build-Vars File

buildvars.json

{
    "aws_access_key_id": "{{env `AWS_ACCESS_KEY_ID`}}",
    "aws_secret_access_key": "{{env `AWS_SECRET_ACCESS_KEY`}}",
    "aws_session_token": "{{env `AWS_SESSION_TOKEN`}}",
    "time_zone": "{{env `TIME_ZONE`}}",
    "instance_type": "t2.large",
    "region": "eu-west-1",
    "availability_zone": "eu-west-1a",
    "ami": "ami-880d64f1",
    "vpc_id": "vpc-",
    "subnet_id": "subnet-",
    "security_group_id": "sg-",
    "ssh_keypair_name": "ssh-key-",
    "ssh_username": "ec2-user",
    "ami_name": "my-custom-ecs {{timestamp}}",
    "ami_description": "Docker AWS ECS Base Image 2017.09",
    "tags": {
      "OS_Version": "Amazon Linux",
      "Release": "amzn-ami-2017.09.h-amazon-ecs-optimized",
      "Docker_Version", "17.09.1-ce",
      "ECS-Agent_Version", "1.17.0",
      "ECS-Init_Version", ""
    }
}

Template

my_custom_ecs_ami.json

{
    "builders": [
    {
        "type": "amazon-ebs",
        "instance_type": "{{user `instance_type`}}",
        "availability_zone": "{{user `availability_zone`}}",
        "vpc_id": "{{user `vpc_id`}}",
        "subnet_id": "{{user `subnet_id`}}",
        "security_group_id": "{{user `security_group_id`}}",
        "ssh_username": "{{user `ssh_username`}}",
        "ssh_keypair_name": "{{user `ssh_keypair_name`}}",
        "ssh_agent_auth": true,
        "ssh_private_ip": false,
        "associate_public_ip_address": "true"
        "access_key": "{{user `aws_access_key_id`}}",
        "secret_key": "{{user `aws_secret_access_key`}}",
        "token": "{{user `aws_session_token`}}",
        "region": "{{user `region`}}",
        "source_ami": "{{user `ami`}}",
        "ami_name": "{{user `ami_name`}}-{{timestamp}}",
        "tags": {
            "OS_Version": "{{user `OS_Version`}}",
            "SourceAMI": "{{user `ami`}}",
            "DockerVersion": "{{user `Docker_Version`}}",
            "ECSAgentVersion": "{{user `ECSAgent_Version`}}",
            "ECS-Init_Version", "{{user `ECS-Init_Version`}}",
            "ami_description": "{{user `ami_description`}}",
        }
    }],
    "provisioners": [
    {
        "type": "shell",
        "script": "prephost.sh",
        "environment_vars": [
        "TIME_ZONE={{user `time_zone`}}"
        ]
    },
    {
        "type": "file",
        "source": "bootstrap.sh",
        "destination": "/home/ec2-user/bootstrap.sh"
    }],
}

Prepare the host

prephost.sh

#!/usr/bin/env bash
set -e

# Configure host to use timezone
# http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html
timezone=${TIME_ZONE:-GMT}

echo "### Setting timezone to $timezone ###"
sudo tee /etc/sysconfig/clock << EOF > /dev/null
ZONE="$timezone"
UTC=true
EOF

sudo ln -sf /usr/share/zoneinfo/$timezone /etc/localtime

# Enable NTP
sudo chkconfig ntpd on

# Additional operating system packages
packages="awslogs jq aws-cfn-bootstrap"

# Exclude Docker and ECS Agent from update
sudo yum -y -x docker\* -x ecs\* update

echo "### Installing extra packages: $packages ###"
sudo yum -y install $packages 


# Disable cloud-init repo updates or upgrades
sudo sed -i -e '/^repo_update: /{h;s/: .*/: false/};${x;/^$/{s//repo_update: false/;H};x}' /etc/cloud/cloud.cfg
sudo sed -i -e '/^repo_upgrade: /{h;s/: .*/: none/};${x;/^$/{s//repo_upgrade: none/;H};x}' /etc/cloud/cloud.cfg

echo "### Performing final clean-up tasks ###"
sudo service docker stop
sudo chkconfig docker off
sudo rm -f /var/log/docker /var/log/ecs/*
# An intermittent failure scenario sees this created as a directory when the
# ECS agent attempts to map it into its container, so do rm -Rf just in case
sudo rm -Rf /var/run/docker.sock

# Remove Docker network database
sudo rm -rf /var/lib/docker/network

# Remove docker0 interface if it has been created
sudo ip link del docker0 || true

First run bootstrap host

bootstrap.sh

#!/usr/bin/env bash
set -e

# Configure ECS Agent
echo "ECS_CLUSTER=${ECS_CLUSTER}" > /etc/ecs/ecs.config

# Set HTTP Proxy URL if provided
if [ -n $PROXY_URL ]
then
  echo export HTTPS_PROXY=$PROXY_URL >> /etc/sysconfig/docker
  echo HTTPS_PROXY=$PROXY_URL >> /etc/ecs/ecs.config
  echo NO_PROXY=169.254.169.254,169.254.170.2,/var/run/docker.sock >> /etc/ecs/ecs.config
  echo HTTP_PROXY=$PROXY_URL >> /etc/awslogs/proxy.conf
  echo HTTPS_PROXY=$PROXY_URL >> /etc/awslogs/proxy.conf
  echo NO_PROXY=169.254.169.254 >> /etc/awslogs/proxy.conf
fi

# Enable docker host networking mode if DOCKER_NETWORK_MODE is set to "host"
if [ $DOCKER_NETWORK_MODE = "host" ]
then
  sudo sed -i -e "s|^\(OPTIONS=\".*\)\"$|\1 --bridge=none --ip-forward=false --ip-masq=false --iptables=false\"|" \
    /etc/sysconfig/docker
fi

# Write AWS Logs region
sudo tee /etc/awslogs/awscli.conf << EOF > /dev/null
[plugins]
cwlogs = cwlogs
[default]
region = ${AWS_DEFAULT_REGION}
EOF

# Write AWS Logs config
sudo tee /etc/awslogs/awslogs.conf << EOF > /dev/null
[general]
state_file = /var/lib/awslogs/agent-state    
 
[/var/log/dmesg]
file = /var/log/dmesg
log_group_name = ${STACK_NAME}/ec2/${AUTOSCALING_GROUP}/var/log/dmesg
log_stream_name = {instance_id}

[/var/log/messages]
file = /var/log/messages
log_group_name = ${STACK_NAME}/ec2/${AUTOSCALING_GROUP}/var/log/messages
log_stream_name = {instance_id}
datetime_format = %b %d %H:%M:%S

[/var/log/docker]
file = /var/log/docker
log_group_name = ${STACK_NAME}/ec2/${AUTOSCALING_GROUP}/var/log/docker
log_stream_name = {instance_id}
datetime_format = %Y-%m-%dT%H:%M:%S.%f

[/var/log/ecs/ecs-init.log]
file = /var/log/ecs/ecs-init.log*
log_group_name = ${STACK_NAME}/ec2/${AUTOSCALING_GROUP}/var/log/ecs/ecs-init
log_stream_name = {instance_id}
datetime_format = %Y-%m-%dT%H:%M:%SZ
time_zone = UTC

[/var/log/ecs/ecs-agent.log]
file = /var/log/ecs/ecs-agent.log*
log_group_name = ${STACK_NAME}/ec2/${AUTOSCALING_GROUP}/var/log/ecs/ecs-agent
log_stream_name = {instance_id}
datetime_format = %Y-%m-%dT%H:%M:%SZ
time_zone = UTC
EOF

# Start services
sudo service awslogs start
sudo chkconfig docker on
sudo service docker start
sudo start ecs

# Exit gracefully if ECS_CLUSTER is not defined
if [[ -z ${ECS_CLUSTER} ]]
  then
  echo "Skipping ECS agent check as ECS_CLUSTER variable is not defined"
  exit 0
fi

# Loop until ECS agent has registered to ECS cluster
echo "Checking ECS agent is joined to ${ECS_CLUSTER}"
until [[ "$(curl --fail --silent http://localhost:51678/v1/metadata | jq '.Cluster // empty' -r -e)" == ${ECS_CLUSTER} ]]
  do printf '.'
  sleep 5
done
echo "ECS agent successfully joined to ${ECS_CLUSTER}"

Packer Build Command

packer build -var-file=buildvars.json my_custom_ecs_ami.json

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment