Deploy Docker Swarm on AWS EC2 via cloud-formation 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".
The rest of this post assumes we work from within the "project dir".
Configuration
Create a folder config
and names.sh
file in it.
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)
cloud-formation Template
Create a folder network
and a vpc.yml
file in it.
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
# categories:
- Key: Name
Value: !Sub ${Prefix}-vpc-1
# Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
# categories:
- 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
# categories:
- 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
# categories:
- Key: Name
Value: !Sub ${Prefix}-sn-priv-1
# NAT Elastic IP
NatGatewayEIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
# categories:
- Key: Name
Value: !Sub ${Prefix}-eip-1
# NAT Gateway
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayEIP.AllocationId
SubnetId: !Ref PublicSubnet
# categories:
- Key: Name
Value: !Sub ${Prefix}-ng-1
# Public route table
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
# categories:
- 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
# categories:
- 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
# categories:
- 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
# categories:
- 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 cloud-formation:"
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 cloud-formation:"
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:
Deploy
Finally let's run the "deploy" script to setup our network:
You should see output similar to this:
Deploying swift-swarm-vpc stack via cloud-formation:
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:
Congratulations!
We are done with Step 1. Network Setup
.
Next step is: Step 2. Storage