Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
370 lines (325 sloc) 12.5 KB
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Docker Swarm - Manager'
Parameters:
ParentVPCStack:
Description: 'Stack name of parent VPC stack based on vpc/vpc-*azs.yaml template.'
Type: String
KeyName:
Description: 'Optional key pair of the ec2-user to establish a SSH connection to the EC2 instance.'
Type: String
Default: ''
IAMUserSSHAccess:
Description: 'Synchronize public keys of IAM users to enable personalized SSH access (Doc: https://cloudonaut.io/manage-aws-ec2-ssh-access-with-iam/).'
Type: String
Default: false
AllowedValues:
- true
- false
InstanceType:
Description: 'The instance type for the EC2 instance.'
Type: String
Default: 't2.micro'
DesiredInstances:
Description: 'The number of manager nodes'
Type: Number
Default: 1
AllowedValues: [1,3,5,7]
ManagerSubnetsReach:
Description: 'Should the managers have direct access to the Internet or do you prefer private subnets with NAT?'
Type: String
Default: Public
AllowedValues:
- Public
- Private
SecurityGroups:
Description: Security group for which are allowed to talk to ASG
Type: CommaDelimitedList
TargetGroups:
Description: Security group for which are allowed to talk to ASG
Type: CommaDelimitedList
Default: ''
DockerVersion:
Description: 'Specifies the version of the Docker engine'
Type: String
Default: "17.03.0"
DockerRepository:
Description: 'Specifies if stable or edge repository should be used'
Type: String
Default: stable
AllowedValues:
- stable
- edge
JoinToken:
Description: 'The token to join the swarm cluster as a manager node'
Type: String
Default: ''
NoEcho: true
JoinTokenKmsKey:
Description: 'KMS key to decrypt swarm join tokens'
Type: String
Version:
Description: 'Specifiy version to trigger a re-deployment of launch configuration'
Type: String
Conditions:
HasKeyName: !Not [!Equals [!Ref KeyName, '']]
HasIAMUserSSHAccess: !Equals [!Ref IAMUserSSHAccess, 'true']
HasSwarmJoinToken: !Not [!Equals [!Ref JoinToken, '']]
Resources:
InstanceProfile:
Type: 'AWS::IAM::InstanceProfile'
Properties:
Path: '/'
Roles:
- !Ref IAMRole
IAMRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- 'ec2.amazonaws.com'
Action:
- 'sts:AssumeRole'
Path: '/'
Policies:
- PolicyName: logs
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
- 'logs:DescribeLogStreams'
Resource:
- 'arn:aws:logs:*:*:*'
- PolicyName: asg
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'autoscaling:DescribeAutoScalingGroups'
- 'autoscaling:DescribeAutoScalingInstances'
- 'ec2:DescribeInstances'
Resource:
- '*'
- PolicyName: kms
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'kms:Decrypt'
- 'kms:DescribeKey'
Resource:
- !Ref JoinTokenKmsKey
IAMPolicySSHAccess:
Type: 'AWS::IAM::Policy'
Condition: HasIAMUserSSHAccess
Properties:
Roles:
- !Ref IAMRole
PolicyName: iam
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'iam:ListUsers'
Resource:
- '*'
- Effect: Allow
Action:
- 'iam:ListSSHPublicKeys'
- 'iam:GetSSHPublicKey'
Resource:
- !Sub 'arn:aws:iam::${AWS::AccountId}:user/*'
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
# AvailabilityZones: !Ref AvailabilityZones
VPCZoneIdentifier:
- 'Fn::ImportValue': !Sub '${ParentVPCStack}-SubnetA${ManagerSubnetsReach}'
- 'Fn::ImportValue': !Sub '${ParentVPCStack}-SubnetB${ManagerSubnetsReach}'
- 'Fn::ImportValue': !Sub '${ParentVPCStack}-SubnetC${ManagerSubnetsReach}'
LaunchConfigurationName: !Ref LaunchConfiguration
MinSize: !Ref DesiredInstances
MaxSize: 7
DesiredCapacity: !Ref DesiredInstances
TargetGroupARNs: !Ref TargetGroups
MetricsCollection:
- Granularity: 1Minute
Metrics:
- GroupInServiceInstances
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}
PropagateAtLaunch: 'true'
CreationPolicy:
ResourceSignal:
Timeout: PT05M
UpdatePolicy:
AutoScalingRollingUpdate:
MinInstancesInService: !Ref DesiredInstances
MaxBatchSize: '1'
PauseTime: PT10M
SuspendProcesses:
- AlarmNotification
WaitOnResourceSignals: 'true'
LaunchConfiguration:
Type: AWS::AutoScaling::LaunchConfiguration
Metadata:
AWS::CloudFormation::Init:
configSets:
default:
!If
- HasSwarmJoinToken
- !If [HasIAMUserSSHAccess, [ssh-access, docker-ubuntu, swarm-join], [docker-ubuntu, swarm-join]]
- !If [HasIAMUserSSHAccess, [ssh-access, docker-ubuntu, swarm-init], [docker-ubuntu, swarm-init]]
ssh-access:
files:
'/opt/authorized_keys_command.sh':
content: |
#!/bin/bash -e
if [ -z "$1" ]; then
exit 1
fi
SaveUserName="$1"
SaveUserName=${SaveUserName//"+"/".plus."}
SaveUserName=${SaveUserName//"="/".equal."}
SaveUserName=${SaveUserName//","/".comma."}
SaveUserName=${SaveUserName//"@"/".at."}
aws iam list-ssh-public-keys --user-name "$SaveUserName" --query "SSHPublicKeys[?Status == 'Active'].[SSHPublicKeyId]" --output text | while read KeyId; do
aws iam get-ssh-public-key --user-name "$SaveUserName" --ssh-public-key-id "$KeyId" --encoding SSH --query "SSHPublicKey.SSHPublicKeyBody" --output text
done
mode: '000755'
owner: root
group: root
'/opt/import_users.sh':
content: |
#!/bin/bash
aws iam list-users --query "Users[].[UserName]" --output text | while read User; do
SaveUserName="$User"
SaveUserName=${SaveUserName//"+"/".plus."}
SaveUserName=${SaveUserName//"="/".equal."}
SaveUserName=${SaveUserName//","/".comma."}
SaveUserName=${SaveUserName//"@"/".at."}
if id -u "$SaveUserName" >/dev/null 2>&1; then
echo "$SaveUserName exists"
else
#sudo will read each file in /etc/sudoers.d, skipping file names that end in ‘~’ or contain a ‘.’ character to avoid causing problems with package manager or editor temporary/backup files.
SaveUserFileName=$(echo "$SaveUserName" | tr "." " ")
/usr/sbin/adduser "$SaveUserName"
echo "$SaveUserName ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/$SaveUserFileName"
fi
done
mode: '000755'
owner: root
group: root
'/etc/cron.d/import_users':
content: |
*/10 * * * * root /opt/import_users.sh
mode: '000644'
owner: root
group: root
commands:
'a_configure_sshd_command':
command: 'sed -i "s:#AuthorizedKeysCommand none:AuthorizedKeysCommand /opt/authorized_keys_command.sh:g" /etc/ssh/sshd_config'
'b_configure_sshd_commanduser':
command: 'sed -i "s:#AuthorizedKeysCommandUser nobody:AuthorizedKeysCommandUser nobody:g" /etc/ssh/sshd_config'
'c_import_users':
command: './import_users.sh'
cwd: '/opt'
services:
sysvinit:
sshd:
enabled: true
ensureRunning: true
commands:
- 'a_configure_sshd_command'
- 'b_configure_sshd_commanduser'
docker-ubuntu:
commands:
'a_get_certificates':
command: 'sudo apt-get install apt-transport-https ca-certificates curl software-properties-common'
'b_set_gpg_key':
command: 'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -'
'c_add_fingerprint':
command: 'sudo apt-key fingerprint 0EBFCD88'
'd_add_docker_repo':
command: !Sub 'sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) ${DockerRepository}"'
'e_update_aptget':
command: 'sudo apt-get update'
'f_install_docker':
command: !Sub 'sudo apt-get install -y docker-ce=${DockerVersion}~ce-0~ubuntu-xenial'
'g_create_service':
command: 'sudo service docker start'
'h_add_ubuntu_user_to_docker_group':
command: 'sudo usermod -aG docker ubuntu'
'i_verify_installation':
command: 'sudo docker run hello-world'
'k_verify_installation':
command: 'docker run hello-world'
swarm-init:
commands:
'a_join_swarm':
command: 'docker swarm init'
'b_swarm_healthcheck':
command: 'docker node ls'
swarm-join:
commands:
'a_join_swarm':
command: !Sub |
# Decrypt join token via KMS
echo -n "${JoinToken}" | base64 --decode > ciphertextblob
JOIN_TOKEN=$(aws kms decrypt --region ${AWS::Region} --ciphertext-blob fileb://ciphertextblob --query Plaintext --output text | base64 --decode)
INSTANCE_ID="`wget -q -O - http://instance-data/latest/meta-data/instance-id`"
ASG_NAME=$(aws autoscaling describe-auto-scaling-instances --instance-ids $INSTANCE_ID --region ${AWS::Region} --query AutoScalingInstances[].AutoScalingGroupName --output text)
for ID in $(aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names $ASG_NAME --region ${AWS::Region} --query AutoScalingGroups[].Instances[].InstanceId --output text);
do
# Ignore "myself"
if [ "$ID" == "$INSTANCE_ID" ] ; then
continue;
fi
IP=$(aws ec2 describe-instances --instance-ids $ID --region ${AWS::Region} --query Reservations[].Instances[].PrivateIpAddress --output text)
if [ ! -z "$IP" ] ; then
echo "Try to join swarm with IP $IP"
# Join the swarm; if it fails try the next one
docker swarm join --token $JOIN_TOKEN $IP:2377 && break || continue
fi
done
'b_swarm_healthcheck':
command: 'docker node ls'
Properties:
ImageId: ami-6f587e1c # Use Ubuntu to install latest docker-engine
InstanceType: !Ref InstanceType
SecurityGroups: !Ref SecurityGroups
IamInstanceProfile: !Ref InstanceProfile
KeyName: !If [HasKeyName, !Ref KeyName, !Ref 'AWS::NoValue']
BlockDeviceMappings:
- DeviceName: "/dev/xvdcz"
Ebs:
VolumeSize: '22'
UserData:
"Fn::Base64": !Sub |
#!/bin/bash -xe
sudo apt-get update
sudo apt-get -y upgrade
# Install AWSCli
sudo apt install -y awscli
# Install cfn-init for Ubuntu
apt-get -y install python-setuptools
easy_install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz
ln -s /root/aws-cfn-bootstrap-latest/init/ubuntu/cfn-hup /etc/init.d/cfn-hup
echo "${Version}"
cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource LaunchConfiguration
cfn-signal -e $? --region ${AWS::Region} --stack ${AWS::StackName} --resource AutoScalingGroup
Outputs:
AutoScalingGroup:
Description: 'Use this AutoScaling Group to identify Swarm Managers.'
Value: !Ref AutoScalingGroup