Last active
March 26, 2021 18:26
-
-
Save cig0/dff16c05d270cb18d481a72dc2b89c77 to your computer and use it in GitHub Desktop.
AWS EKS eksctl: dirty Bash script - literally, it's a very much WIP - that I put together to quickly bring up / tear down an AWS EKS test/dev cluster when working on my Harness certification
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# This script creates a small dev/test EKS cluster using Spot instances. It is very much a WIP, don't run it blindly! | |
# You can run ./eks.bash to get a list of the available actions; use ./eks.bash template.create to create a ClusterConfig config-file ready to spin up a new cluster | |
# | |
# Shout-outs to: | |
# @totallyGreg [ https://github.com/totallyGreg ] | |
# @Erikdeirdre [ https://github.com/erikdeirdre ] | |
# For sharing their code which I took as a baseline to work with and from which I learned a few tricks | |
## Global variables initialization | |
# AWS-related variables | |
clusterName=${clusterName:-harness-training} | |
vcpus=${vcpus:-2} | |
memory=${memory:-8} | |
cpu_architecture=${cpu_architecture:-x86_64} | |
gpus=${gpus:-0} | |
burst_support=${burst_support:-false} | |
region=${region:-us-east-2} | |
eks_config_file=${eks_config_file:-config-file.yaml} | |
k8s_version=${k8s_version:-1.19} | |
k8s_context=${k8s_context:-@"$clusterName.$region".eksctl.io} | |
# Script variables | |
REALPATH=$(realpath "$0") | |
DIRNAME=$(dirname "$REALPATH") | |
DATEANDTIME=$(date +"%Y-%m-%d_%H.%M.%S") | |
CLUSTER_LOG_NAME="$DIRNAME/$clusterName-$DATEANDTIME" | |
CLUSTER_LOG_NAME_CREATE="$CLUSTER_LOG_NAME-create.log" | |
CLUSTER_LOG_NAME_TEARDOWN="$CLUSTER_LOG_NAME-teardown.log" | |
_confirmAsk=', shall we proceed (YES/n):' | |
## Functions | |
# Internal functions | |
function _help() { | |
# Check profile and actions, show a help message if something's wrong | |
# This function needs to receive an argument to validate the conditional check for supplied actions | |
# when called from a callable function | |
# TODO: originally this function used to check just a couple of cases, as it grew over time it now needs a better logic for parsing | |
case ${FUNCNAME[1]} in | |
template.create) | |
[ -z "$AWS_ACCOUNT" ] && | |
echo -e "\nError: missing account identifier" \ | |
"\nPlease set the environment variable AWS_ACCOUNT with your account identifier" && | |
exit 1 | |
[ -z "$EKS_SSH_KEY" ] && | |
echo -e "\nError: missing ssh key\nPlease set the environment variable EKS_KEY_SSH with a valid path/to/ssh_public_key" && | |
exit 1 | |
;; | |
*) | |
[ ! -s "$eks_config_file" ] && | |
echo -e "\nError: missing eksctl configutarion file" \ | |
"\nConfig file $eks_config_file not found, run $0 template.create to create a new one" && | |
exit 1 | |
[ -z "$AWS_PROFILE" ] && | |
echo -e "\nError: missing profile id" \ | |
"\nAWS_PROFILE environment variable not set" && | |
exit 1 | |
[ $# -eq 0 ] && echo -e "\nError: no action supplied\n" \ | |
"Possible actions are:\n-------- ------- ---" && | |
grep -E '^[[:space:]]*([[:alnum:]_]+[[:space:]]*\(\)|function[[:space:]]+([^_].*?)+[[:alnum:]_]+)' "$REALPATH" | awk -F' ' '{print $2}' && | |
exit 1 | |
# Scan this script for lines beginning with the word function, ignoring "internal" functions which begin with an underscore | |
;; | |
esac | |
} | |
function _confirm() { | |
# Call with a prompt string or use the default string | |
read -r -p "${1:-Are you sure? [YES/n]} " response | |
case "$response" in | |
[Y][E][S] | [Y]) | |
true | |
;; | |
*) | |
false | |
;; | |
esac | |
} | |
function _cluster.up() { | |
echo "Using config file $eks_config_file" | |
time eksctl create cluster -f "$eks_config_file" --auto-kubeconfig 2>&1 | tee "$CLUSTER_LOG_NAME_CREATE" && | |
echo "Logs written to file $CLUSTER_LOG_NAME_CREATE" && | |
return | |
echo "There was an error while creating the cluster, check logfile $CLUSTER_LOG_NAME_CREATE for details" && | |
exit 1 | |
} | |
function _cluster.delete() { | |
_confirm "About to remove cluster $clusterName and its associated resources$_confirmAsk" && | |
time eksctl delete cluster "$clusterName" 2>&1 | tee "$CLUSTER_LOG_NAME_TEARDOWN" && | |
echo "Logs written to file $CLUSTER_LOG_NAME_TEARDOWN" && | |
return | |
echo -e "\nTeardown cancelled by the user, no actions performed on the cluster" && | |
exit 0 | |
} | |
function _write-kubeconfig() { | |
eksctl utils write-kubeconfig -c "$clusterName" -r "$region" && | |
echo 'K8s config updated (~/.kube/config)' && | |
return || | |
echo 'There was an error updating the K8s configuration file (~/.kube/config)' && | |
exit 1 | |
} | |
# Callable functions | |
function cluster.teardown() { | |
_help h | |
profile.set | |
time _cluster.delete && | |
echo "The cluster $clusterName and its associated resources have been destroyed" && | |
exit 0 | |
} | |
function cluster.create() { | |
_help h | |
local iam_user | |
echo 'Acquiring AWS IAM UserName to setup EKS context...' | |
iam_user="$(aws --output=yaml iam get-user --query 'User.UserName' | sed -e 's/^"//' -e 's/"$//')" | |
[ -z "$iam_user" ] && echo 'There was an error retrieving current IAM user, please check your credentials an environment variables and try again' && | |
exit 1 || | |
echo "UserName is: $iam_user" | |
echo -e "\nSpinning up $clusterName EKS cluster..." | |
profile.set && | |
time instances.get && | |
time _cluster.up && | |
time _write-kubeconfig && | |
kubectl config use-context "$iam_user$k8s_context" && | |
kubectl config set-context --current --namespace=default && | |
echo "K8s context set to $iam_user$k8s_context, namespace: default" && | |
exit 0 | |
} | |
function instances.get() { | |
instances="$(ec2-instance-selector --vcpus="$vcpus" --memory="$memory" --cpu-architecture="$cpu_architecture" --gpus="$gpus" --burst-support="$burst_support" | | |
paste -d, -s -)" | |
echo "Using instances type: $instances" && | |
return || | |
echo 'There was an error acquiring the instances types' && | |
exit 1 | |
} | |
function profile.set() { | |
echo "AWS profile set to $AWS_PROFILE" | |
} | |
function template.create() { | |
_help h | |
local attachedPolicyARN | |
attachedPolicyARN=${attachedPolicyARN:-arn:aws:iam::"$AWS_ACCOUNT":policy/SecretsManager-bare} | |
local publicKeyPath | |
publicKeyPath=${publicKeyPath:-"$EKS_SSH_KEY"} | |
[ -f "$eks_config_file" ] && | |
echo "Error: $eks_config_file already exists and will be overwritten" && | |
exit 1 | |
# TODO: this heredoc isn't fully templetized yet | |
cat <<EOF >"$eks_config_file" | |
apiVersion: eksctl.io/v1alpha5 | |
kind: ClusterConfig | |
metadata: | |
name: $clusterName | |
region: $region | |
version: "$k8s_version" | |
managedNodeGroups: | |
- name: ng-spot | |
instanceTypes: ["m5.large","m5a.large","m5ad.large","m5d.large","m5dn.large","m5n.large","m5zn.large"] | |
desiredCapacity: 2 | |
minSize: 2 | |
maxSize: 2 | |
privateNetworking: true | |
volumeSize: 10 | |
ssh: | |
allow: true | |
publicKeyPath: $publicKeyPath | |
enableSsm: true | |
iam: | |
attachPolicyARNs: | |
- arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy | |
- arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy | |
- arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly | |
- $attachedPolicyARN | |
volumeType: gp2 | |
spot: true | |
EOF | |
# Check the config file was successfully created and has a size greater than zero | |
[ -s "$eks_config_file" ] && | |
echo -e "\nConfiguration file $eks_config_file successfully created" \ | |
"\nIf it looks good to you, run $0 cluster.create to begin creating the cluster" && | |
exit 0 || | |
echo "There was an error creating the configuration file $eks_config_file" && | |
exit 1 | |
} | |
## Run action | |
[ $# -eq 0 ] && _help || "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment