Deploy Docker Swarm on AWS EC2 via CloudFormation templates - Step 1 - Network Setup
Setup network infrastructure including new VPC (Virtual Private Cloud), public and private subnets, internet gateway, security group, and ssh access.
This post is part of a thread that includes these steps:
- Network Setup (this post)
- Storage
- Roles
- Manager Instance
- Worker Launch Template
- Worker Instances
- Docker Swarm
- Cleanup
Make sure you do this setup first:
Project Structure
For this deployment we will create 2 separate code repositories. The first one will be named swift-aws-ec2-swarm
and the second one will be named swift-aws-ec2-docker
.
As a start, create the swift-aws-ec2-swarm
directory in your home dir and switch to it. We will call this the “project dir”.
mkdir -p ~/swift-aws-ec2-swarm
cd ~/swift-aws-ec2-swarm
The rest of this post assumes we work from within the “project dir”.
Configuration
Create a folder config
and names.sh
file in it.
mkdir -p config
touch config/names.sh
nano config/names.sh
Copy and paste this code into names.sh
:
export AWS_DEFAULT_PROFILE=swift
prefix="swift-swarm"
ec2_key_pair="aws-ec2-key"
stack_vpc="$prefix-vpc"
stack_ebs="$prefix-ebs"
stack_iam_manager="$prefix-iam-manager"
stack_ec2_manager="$prefix-ec2-manager"
stack_iam_worker="$prefix-iam-worker"
stack_ec2_worker="$prefix-ec2-worker"
stack_ec2_worker_lt="$prefix-ec2-worker-lt"
vpc="$prefix-vpc-1"
subnet_pub_1="$prefix-sn-pub-1"
subnet_priv_1="$prefix-sn-priv-1"
security_group_pub_1="$prefix-sg-pub-1"
security_group_priv_1="$prefix-sg-priv-1"
IMPORTANT: Replace
swift
in the first line with the name of your AWS CLI profile or set todefault
if you don;t use different profiles.
Virtual Private Cloud (AWS VPC)
CloudFormation Template
Create a folder network
and a vpc.yml
file in it.
mkdir -p vpc
touch vpc/vpc.yml
nano vpc/vpc.yml
Copy and paste this code into vpc.yml
:
Description: Create VPC, public subnet, private subnet, Internet gateway, NAT gateway, security groups, and Route53 hosted zone for the VPC.
Parameters:
Prefix:
Description: An environment name that is prefixed to resource names
Type: String
VpcCIDR:
Description: Please enter the IP range (CIDR notation) for this VPC
Type: String
Default: 10.0.0.0/16
PublicSubnetCIDR:
Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone
Type: String
Default: 10.0.10.0/24
PrivateSubnetCIDR:
Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone
Type: String
Default: 10.0.20.0/24
Resources:
# VPC
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCIDR
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${Prefix}-vpc-1
# Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${Prefix}-igw-1
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref Vpc
InternetGatewayId: !Ref InternetGateway
# Public Subnet
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Ref PublicSubnetCIDR
AvailabilityZone: !Select [ 0, !GetAZs '' ]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${Prefix}-sn-pub-1
# Private Subnet
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Ref PrivateSubnetCIDR
AvailabilityZone: !Select [ 0, !GetAZs '' ]
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: !Sub ${Prefix}-sn-priv-1
# NAT Elastic IP
NatGatewayEIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub ${Prefix}-eip-1
# NAT Gateway
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayEIP.AllocationId
SubnetId: !Ref PublicSubnet
Tags:
- Key: Name
Value: !Sub ${Prefix}-ng-1
# Public route table
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub ${Prefix}-rt-pub-1
# Route to internet gateway
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
# Routes for public subnet
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet
# Routes for private subnet
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
Tags:
- Key: Name
Value: !Sub ${Prefix}-rt-priv-1
DefaultPrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
PrivateSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet
## Security groups
# Public
PublicSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for manager nodes
Tags:
- Key: Name
Value: !Sub ${Prefix}-sg-pub-1
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
# Private
PrivateSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for worker nodes
Tags:
- Key: Name
Value: !Sub ${Prefix}-sg-priv-1
VpcId: !Ref Vpc
## Ingress rules
# Public
PublicSecurityGroupIngressFromSelf:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !GetAtt [ PublicSecurityGroup, GroupId ]
IpProtocol: -1
SourceSecurityGroupId: !GetAtt [ PublicSecurityGroup, GroupId ]
PublicSecurityGroupIngressFromPrivate:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !GetAtt [ PublicSecurityGroup, GroupId ]
IpProtocol: -1
SourceSecurityGroupId: !GetAtt [ PrivateSecurityGroup, GroupId ]
# Private
PrivateSecurityGroupIngressFromSelf:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !GetAtt [ PrivateSecurityGroup, GroupId ]
IpProtocol: -1
SourceSecurityGroupId: !GetAtt [ PrivateSecurityGroup, GroupId ]
PrivateSecurityGroupIngressFromPublic:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !GetAtt [ PrivateSecurityGroup, GroupId ]
IpProtocol: -1
SourceSecurityGroupId: !GetAtt [ PublicSecurityGroup, GroupId ]
# Add DNS zone for our VPC
HostedZone:
Type: 'AWS::Route53::HostedZone'
Properties:
Name: swift.internal.
VPCs:
- VPCId: !Ref Vpc
VPCRegion: !Ref "AWS::Region"
HostedZoneConfig:
Comment: 'Hosted zone for swift.internal'
Outputs:
VpcId:
Description: A reference to the created VPC
Value: !Ref Vpc
PublicSubnetId:
Description: A reference to the public subnet in the 1st Availability Zone
Value: !Ref PublicSubnet
PrivateSubnetId:
Description: A reference to the private subnet in the 1st Availability Zone
Value: !Ref PrivateSubnet
PublicSecurityGroupId:
Description: A reference to the public SecurityGroup
Value: !Ref PublicSecurityGroup
PrivateSecurityGroupId:
Description: A reference to the private SecurityGroup
Value: !Ref PrivateSecurityGroup
HostedZoneId:
Description: A reference to the Route53 HostedZone
Value: !Ref HostedZone
Scripts
Next add a script deploy-vpc.sh
and paste this code in it:
#!/usr/bin/env bash
# switch to parent directory
script_path=`dirname ${BASH_SOURCE[0]}`
pushd $script_path/..
source config/names.sh
echo
echo "Deploying $stack_vpc stack via CloudFormation:"
echo 'https://us-west-2.console.aws.amazon.com/cloudformation/home'
echo
set -x
aws cloudformation deploy \
--profile swift \
--template-file vpc/vpc.yml \
--stack-name $stack_vpc \
--parameter-overrides Prefix=$prefix
popd
Let’s also add a clean up script rm-vpc.sh
:
#!/usr/bin/env bash
# switch to parent directory
script_path=`dirname ${BASH_SOURCE[0]}`
pushd $script_path/..
source config/names.sh
echo
echo "Removing $stack_vpc stack via CloudFormation:"
echo 'https://us-west-2.console.aws.amazon.com/cloudformation/home'
echo
set -x
aws cloudformation delete-stack \
--stack-name $stack_vpc
aws cloudformation wait stack-delete-complete \
--stack-name $stack_vpc
popd
Make the scripts executable:
chmod +x vpc/deploy-vpc.sh
chmod +x vpc/rm-vpc.sh
Deploy
Finally let’s run the “deploy” script to setup our network:
./vpc/deploy-vpc.sh
You should see output similar to this:
Deploying swift-swarm-vpc stack via CloudFormation:
https://us-west-2.console.aws.amazon.com/cloudformation/home
+ aws cloudformation deploy --template-file network/vpc.yml --stack-name swift-swarm-vpc --parameter-overrides Prefix=swift-swarm
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - swift-swarm-vpc
At this point your project structure should look like this:
.
├── config
│ └── names.sh
└── vpc
├── deploy-vpc.sh
├── rm-vpc.sh
└── vpc.yml
Congratulations!
We are done with Step 1. Network Setup
.
Next step is: Step 2. Storage