Skip to content

Commit

Permalink
use auto scaling group to create Dokku instance in all cases
Browse files Browse the repository at this point in the history
  • Loading branch information
tobiasmcnulty committed Oct 13, 2017
1 parent e4a54a0 commit dbfe372
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 91 deletions.
25 changes: 9 additions & 16 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -325,11 +325,18 @@ The CloudFormation stack creation should not finish until Dokku is fully install
<http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-signal.html>`_ is used in the
template to signal CloudFormation once the installation is complete.

Load Balancer
~~~~~~~~~~~~~

The Dokku stack includes a load balancer and a free SSL certificate via AWS Certificate Manager, so
HTTPS needs to be configured on the Dokku instance only if you wish to encrypt traffic between the
load balancer and the Dokku instance.

DNS
~~~

After the stack is created, you'll want to inspect the Outputs for the PublicIP of the instance and
create a DNS ``A`` record (possibly including a wildcard record, if you're using vhost-based apps)
After the stack is created, you'll want to inspect the Outputs for the `LoadBalancerDNSName` and
create a DNS ``CNAME`` record (possibly including a wildcard record, if you're using vhost-based apps)
for your chosen domain.

For help creating a DNS record, please refer to the `Dokku DNS documentation
Expand Down Expand Up @@ -370,20 +377,6 @@ http://python-sample.your.domain/
For additional help deploying to your new instance, please refer to the `Dokku documentation
<http://dokku.viewdocs.io/dokku/deployment/application-deployment/>`_.

Let's Encrypt
~~~~~~~~~~~~~

The Dokku stack does not create a load balancer and hence does not include a free SSL certificate
via Amazon Certificate Manager, so let's create one with the Let's Encrypt plugin, and add a cron
job to automatically renew the cert as needed::

ssh ubuntu@<your domain or IP> sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git
ssh dokku@<your domain or IP> config:set --no-restart python-sample [email protected]
ssh dokku@<your domain or IP> letsencrypt python-sample
ssh dokku@<your domain or IP> letsencrypt:cron-job --add python-sample

The Python sample app should now be accessible over HTTPS at https://python-sample.your.domain/

Contributing
------------

Expand Down
95 changes: 31 additions & 64 deletions stack/dokku.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
import troposphere.autoscaling as autoscaling
import troposphere.cloudformation as cloudformation
import troposphere.ec2 as ec2
import troposphere.iam as iam
from troposphere import (
Base64,
Equals,
FindInMap,
Join,
Output,
Parameter,
Ref,
Tags
)
from troposphere import AWS_STACK_NAME, Base64, FindInMap, Join, Parameter, Ref
from troposphere.policies import CreationPolicy, ResourceSignal

from .assets import assets_management_policy
from .common import container_instance_type
from .domain import domain_name
from .environment import environment_variables
from .load_balancer import load_balancer
from .logs import logging_policy
from .template import template
from .vpc import container_a_subnet, vpc
from .vpc import container_a_subnet, container_b_subnet, vpc

key_name = template.add_parameter(
Parameter(
Expand Down Expand Up @@ -90,27 +83,6 @@
label="SSH CIDR",
)

load_balancer_enable = template.add_parameter(
Parameter(
"LoadBalancerEnable",
Description="Whether or not to place a Elastic Load Balancer (with an auto-generated SSL "
"certificate) in front of your Dokku instance.",
Type="String",
AllowedValues=["true", "false"],
Default="false",
),
group="Load Balancer",
label="Enable Load Balancer",
)

use_load_balancer = "DokkuUseLoadBalancer"
template.add_condition(use_load_balancer, Equals(Ref(load_balancer_enable), "true"))

# Don't create the load balancer unless it's needed, and don't import it until the
# load_balancer_enable Parameter is defined (so it shows up first in the interface)
from .load_balancer import load_balancer # noqa: E402
load_balancer.Condition = use_load_balancer

# "16.04 hvm ssd" AMIs from https://cloud-images.ubuntu.com/locator/ec2/
template.add_mapping('RegionMap', {
"ap-northeast-1": {"AMI": "ami-0417e362"},
Expand Down Expand Up @@ -180,19 +152,15 @@
]
))

# Elastic IP for EC2 instance
eip = template.add_resource(ec2.EIP("Eip"))

# The Dokku EC2 instance
ec2_instance_name = 'Ec2Instance'
ec2_instance = template.add_resource(ec2.Instance(
ec2_instance_name,
launch_configuration_name = 'LaunchConfiguration'
launch_configuration = template.add_resource(autoscaling.LaunchConfiguration(
launch_configuration_name,
ImageId=FindInMap("RegionMap", Ref("AWS::Region"), "AMI"),
InstanceType=container_instance_type,
KeyName=Ref(key_name),
SecurityGroupIds=[Ref(security_group)],
SecurityGroups=[Ref(security_group)],
IamInstanceProfile=Ref(instance_profile),
SubnetId=Ref(container_a_subnet),
BlockDeviceMappings=[
ec2.BlockDeviceMapping(
DeviceName="/dev/sda1",
Expand All @@ -219,10 +187,10 @@
'update-rc.d cfn-hup defaults\n',
# call our "on_first_boot" configset (defined below):
'cfn-init --stack="', Ref('AWS::StackName'), '" --region=', Ref('AWS::Region'),
' -r %s -c on_first_boot\n' % ec2_instance_name,
' -r %s -c on_first_boot\n' % launch_configuration_name,
# send the exit code from cfn-init to our CreationPolicy:
'cfn-signal -e $? --stack="', Ref('AWS::StackName'), '" --region=', Ref('AWS::Region'),
' --resource %s\n' % ec2_instance_name,
' --resource %s\n' % launch_configuration_name,
])),
Metadata=cloudformation.Metadata(
cloudformation.Init(
Expand Down Expand Up @@ -293,10 +261,10 @@
# trigger the on_metadata_update configset on any changes to Ec2Instance metadata
'[cfn-auto-reloader-hook]\n',
'triggers=post.update\n',
'path=Resources.%s.Metadata\n' % ec2_instance_name,
'path=Resources.%s.Metadata\n' % launch_configuration_name,
'action=/usr/local/bin/cfn-init',
' --stack=', Ref('AWS::StackName'),
' --resource=%s' % ec2_instance_name,
' --resource=%s' % launch_configuration_name,
' --configsets=on_metadata_update',
' --region=', Ref('AWS::Region'), '\n',
'runas=root\n',
Expand All @@ -309,25 +277,24 @@
),
),
),
Tags=Tags(
Name=Ref("AWS::StackName"),
),
))

# assign our load balancer (if any) the Dokku instance
load_balancer.Instances = [Ref(ec2_instance)]

# Associate the Elastic IP separately, so it doesn't change when the instance changes.
eip_assoc = template.add_resource(ec2.EIPAssociation(
"EipAssociation",
InstanceId=Ref(ec2_instance),
EIP=Ref(eip),
))

template.add_output([
Output(
"PublicIP",
Description="Public IP address of Elastic IP associated with the Dokku instance",
Value=Ref(eip),
),
])
autoscaling_group = autoscaling.AutoScalingGroup(
"AutoScalingGroup",
template=template,
VPCZoneIdentifier=[Ref(container_a_subnet), Ref(container_b_subnet)],
MinSize=1,
MaxSize=1,
DesiredCapacity=1,
LaunchConfigurationName=Ref(launch_configuration),
LoadBalancerNames=[Ref(load_balancer)],
HealthCheckType="EC2",
HealthCheckGracePeriod=300,
Tags=[
{
"Key": "Name",
"Value": Join("-", [Ref(AWS_STACK_NAME), "dokku_instance"]),
"PropagateAtLaunch": True,
}
],
)
28 changes: 17 additions & 11 deletions stack/load_balancer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
group="Load Balancer",
label="Web Worker Port",
))
elif os.environ.get('USE_DOKKU') == 'on':
# TODO: optionally support both port 80 and port 443
web_worker_port = 80
else:
# default to port 80 for EC2 and Elastic Beanstalk options
web_worker_port = Ref(template.add_parameter(
Expand All @@ -43,17 +46,20 @@
label="Web Worker Port",
))

web_worker_protocol = Ref(template.add_parameter(
Parameter(
"WebWorkerProtocol",
Description="Web worker instance protocol",
Type="String",
Default="HTTP",
AllowedValues=["HTTP", "HTTPS"],
),
group="Load Balancer",
label="Web Worker Protocol",
))
if os.environ.get('USE_DOKKU') == 'on':
web_worker_protocol = 'HTTP'
else:
web_worker_protocol = Ref(template.add_parameter(
Parameter(
"WebWorkerProtocol",
Description="Web worker instance protocol",
Type="String",
Default="HTTP",
AllowedValues=["HTTP", "HTTPS"],
),
group="Load Balancer",
label="Web Worker Protocol",
))

tcp_health_check_condition = "TcpHealthCheck"
template.add_condition(
Expand Down

0 comments on commit dbfe372

Please sign in to comment.