Skip to content

Instantly share code, notes, and snippets.

@alfredkrohmer
Last active December 11, 2022 10:40
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alfredkrohmer/0e15d36789348abaf64ce82bff31eb8a to your computer and use it in GitHub Desktop.
Save alfredkrohmer/0e15d36789348abaf64ce82bff31eb8a to your computer and use it in GitHub Desktop.
Wrapper script for an etcd cluster on AWS ECS
#!/bin/sh
# This script decides if we are part of the process of bootstrapping a new etcd cluster
# or if we are joining an already existing cluster.
set -eux -o pipefail
# own IP address
own_ip=$(curl -f -s http://169.254.169.254/latest/meta-data/local-ipv4)
# get default route to query ECS agent
gw=$(ip route show default 0.0.0.0/0 | awk '{print $3}')
# find our cluster
cluster=$(curl -f -s http://${gw}:51678/v1/metadata | jq --raw-output '.Cluster')
# find our own task by looking at tasks which have 'etcd' containers
arn=$(curl -f -s http://${gw}:51678/v1/tasks | jq --raw-output '.Tasks[] | select([.Containers[].Name == "etcd"] | any) | .Arn')
# get deployment of that task
deployment=$(aws ecs describe-tasks --cluster ${cluster} --tasks ${arn} | jq --raw-output '.tasks[0].startedBy')
# get a list of all services
services=$(aws ecs list-services --cluster ${cluster} | jq --raw-output '.serviceArns[]')
# find the service that did the deployment
service=$(aws ecs describe-services --cluster ${cluster} --services ${services} | jq --raw-output '.services[] | select([.deployments[].id == "'"${deployment}"'"] | any) | .serviceName')
# get all tasks of that service
tasks=$(aws ecs list-tasks --cluster ${cluster} --service-name ${service} | jq --raw-output '.taskArns[]')
# get the container instances the tasks are running on
instances=$(aws ecs describe-tasks --cluster ${cluster} --tasks ${tasks} | jq --raw-output '.tasks[].containerInstanceArn')
# get the EC2 instance IDs of those instances
ec2_ids=$(aws ecs describe-container-instances --cluster ${cluster} --container-instances ${instances} | jq --raw-output '.containerInstances[].ec2InstanceId')
# get the IPs of those EC2 instances
ips=($(aws ec2 describe-instances --instance-ids ${ec2_ids} | jq --raw-output '.Reservations[].Instances[].NetworkInterfaces[0].PrivateIpAddresses[0].PrivateIpAddress'))
# try every IP
existing_node=
for ip in ${ips}
do
if [ "${ip}" == "$own_ip" ]
then
continue
fi
# try to get a list of members of the cluster
members=$(curl -f -s http://${ip}:2379/v2/members) || continue
# there is at least one node of the cluster already online
peers=$(jq --raw-output '.[][].peerURLs[0]' <<< $members)
for peer in ${peers}
do
if [ "${peer}" == "http://${own_ip}:2380" ]
then
# we are alread part of the cluster before we have added ourself as a member;
# this means this is a new cluster
break
fi
done
# use this node to add ourself (see below)
existing_node=${ip}
break
done
export ETC_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
export ETC_LISTEN_PEER_URLS=http://0.0.0.0:2380
export ETC_ADVERTISE_CLIENT_URLS=http://${own_ip}:2379
export ETC_INITIAL_ADVERTISE_PEER_URLS=http://${own_ip}:2380
export ETCD_NAME=$(curl -f -s http://169.254.169.254/latest/meta-data/instance-id)
if [[ ${existing_node} ]]
then
# join existing cluster
export ETCD_DISCOVERY=
export ETCD_INITIAL_CLUSTER_STATE=existing
export ETCD_INITIAL_CLUSTER=$(jq --raw-output '.[] | map(.name + "=" + .peerURLs[0]) | join(",")' <<< $members)
# delete bad members
for peer in ${peers}
do
if ! [[ " ${${ips[@]/#/http://}/%/:2379} " =~ " ${peer} " ]]
then
# this member is not in the ECS cluster anymore, delete it
curl -f -s "http://${existing_node}:2379/v2/members/${peer}" -XDELETE
fi
done
# add ourself to the cluster
curl -f -s -XPOST "${existing_node}/v2/members" -H "Content-Type: application/json" -d "{\"peerURLs\": [\"http://${own_ip}:2380\"], \"name\": \"${ETCD_NAME}\"}"
else
# join new cluster, $ETCD_DISCOVERY is set externally
fi
exec etcd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment