mirror of
https://github.com/cookiecutter/cookiecutter-django.git
synced 2024-11-22 09:36:52 +03:00
Add Experimental AWS Elastic Beanstalk support (#817)
Includes: * First pass at Elastic Beanstalk integration * Gets code and elasticache working * Very rudimentary documentation * Includes post hook cleanup
This commit is contained in:
parent
a950eb82f6
commit
52c0e071da
|
@ -42,6 +42,7 @@ Features
|
|||
* Works with Python 2.7.x or 3.5.x
|
||||
* Run tests with unittest or py.test
|
||||
* Customizable PostgreSQL version
|
||||
* Experimental support for Amazon Elastic Beanstalk
|
||||
|
||||
|
||||
Optional Integrations
|
||||
|
@ -146,6 +147,7 @@ Answer the prompts with your own desired options_. For example::
|
|||
4 - Apache Software License 2.0
|
||||
5 - Not open source
|
||||
Choose from 1, 2, 3, 4, 5 [1]: 1
|
||||
use_elasticbeanstalk_experimental: n
|
||||
|
||||
Enter the project and take a look around::
|
||||
|
||||
|
|
|
@ -21,5 +21,6 @@
|
|||
"postgresql_version": ["9.5", "9.4", "9.3", "9.2"],
|
||||
"js_task_runner": ["Gulp", "Grunt", "None"],
|
||||
"use_lets_encrypt": "n",
|
||||
"open_source_license": ["MIT", "BSD", "GPLv3", "Apache Software License 2.0", "Not open source"]
|
||||
"open_source_license": ["MIT", "BSD", "GPLv3", "Apache Software License 2.0", "Not open source"],
|
||||
"use_elasticbeanstalk_experimental": "n"
|
||||
}
|
||||
|
|
54
docs/deployment-with-elastic-beanstalk.rst
Normal file
54
docs/deployment-with-elastic-beanstalk.rst
Normal file
|
@ -0,0 +1,54 @@
|
|||
Deployment with Elastic Beanstalk
|
||||
==========================================
|
||||
|
||||
.. index:: Elastic Beanstalk
|
||||
|
||||
Warning: Experimental
|
||||
---------------------
|
||||
|
||||
This is experimental. For the time being there will be bugs and issues. If you've never used Elastic Beanstalk before, please hold off before trying this option.
|
||||
|
||||
On the other hand, we need help cleaning this up. If you do have knowledge of Elastic Beanstalk, we would appreciate the help. :)
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
* awsebcli
|
||||
|
||||
Instructions
|
||||
-------------
|
||||
|
||||
```
|
||||
# creates the directory of environments (servers)
|
||||
eb init -p python3.4 {{ cookiecutter.project_slug }}
|
||||
|
||||
# Creates the environment (server) where the app will run
|
||||
eb create {{ cookiecutter.project_slug }}
|
||||
# Note: This will fail on a postgres error, because postgres doesn't exist yet
|
||||
|
||||
# Make sure you are in the right environment
|
||||
eb list
|
||||
|
||||
# If you are not in the right environment
|
||||
eb use {{ cookiecutter.project_slug }}
|
||||
|
||||
# Set the environment variables
|
||||
python ebsetenv.py
|
||||
|
||||
# Go to EB AWS config. Create new RDS database (postgres, 9.4.9, db.t2.micro)
|
||||
# Get some coffee, this is going to take a while
|
||||
|
||||
# Deploy again
|
||||
eb deploy
|
||||
|
||||
# Take a look
|
||||
eb open
|
||||
```
|
||||
|
||||
FAQ
|
||||
-----
|
||||
|
||||
Why Not Use Docker on Elastic Beanstalk?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Because I didn't want to add an abstraction (Docker) on top of an abstraction (Elastic Beanstalk).
|
|
@ -25,6 +25,7 @@ Contents:
|
|||
faq
|
||||
troubleshooting
|
||||
my-favorite-cookie
|
||||
deployment-with-elastic-beanstalk
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
|
|
@ -115,7 +115,10 @@ def remove_heroku_files():
|
|||
"""
|
||||
Removes files needed for heroku if it isn't going to be used
|
||||
"""
|
||||
for filename in ["app.json", "Procfile", "requirements.txt", "runtime.txt"]:
|
||||
filenames = ["app.json", "Procfile", "runtime.txt"]
|
||||
if '{{ cookiecutter.use_elasticbeanstalk_experimental }}'.lower() != 'y':
|
||||
filenames.append("requirements.txt")
|
||||
for filename in ["app.json", "Procfile", "runtime.txt"]:
|
||||
file_name = os.path.join(PROJECT_DIRECTORY, filename)
|
||||
remove_file(file_name)
|
||||
|
||||
|
@ -179,6 +182,22 @@ def remove_copying_files():
|
|||
PROJECT_DIRECTORY, filename
|
||||
))
|
||||
|
||||
def remove_elasticbeanstalk():
|
||||
"""
|
||||
Removes elastic beanstalk components
|
||||
"""
|
||||
docs_dir_location = os.path.join(PROJECT_DIRECTORY, '.ebextensions')
|
||||
if os.path.exists(docs_dir_location):
|
||||
shutil.rmtree(docs_dir_location)
|
||||
|
||||
filenames = ["ebsetenv.py", ]
|
||||
if '{{ cookiecutter.use_heroku }}'.lower() != 'y':
|
||||
filenames.append("requirements.txt")
|
||||
for filename in filenames:
|
||||
os.remove(os.path.join(
|
||||
PROJECT_DIRECTORY, filename
|
||||
))
|
||||
|
||||
# IN PROGRESS
|
||||
# def copy_doc_files(project_directory):
|
||||
# cookiecutters_dir = DEFAULT_CONFIG['cookiecutters_dir']
|
||||
|
@ -258,5 +277,6 @@ if '{{ cookiecutter.use_lets_encrypt }}'.lower() == 'y' and '{{ cookiecutter.use
|
|||
if '{{ cookiecutter.open_source_license}}' != 'GPLv3':
|
||||
remove_copying_files()
|
||||
|
||||
# 4. Copy files from /docs/ to {{ cookiecutter.project_slug }}/docs/
|
||||
# copy_doc_files(PROJECT_DIRECTORY)
|
||||
# 12. Remove Elastic Beanstalk files
|
||||
if '{{ cookiecutter.use_elasticbeanstalk_experimental }}'.lower() != 'y':
|
||||
remove_elasticbeanstalk()
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
packages:
|
||||
yum:
|
||||
git: []
|
||||
postgresql94-devel: []
|
||||
libjpeg-turbo-devel: []
|
|
@ -0,0 +1,46 @@
|
|||
#This sample requires you to create a separate configuration file that defines the custom
|
||||
# option settings for CacheCluster properties.
|
||||
|
||||
Resources:
|
||||
MyCacheSecurityGroup:
|
||||
Type: "AWS::EC2::SecurityGroup"
|
||||
Properties:
|
||||
GroupDescription: "Lock cache down to webserver access only"
|
||||
SecurityGroupIngress :
|
||||
- IpProtocol : "tcp"
|
||||
FromPort :
|
||||
Fn::GetOptionSetting:
|
||||
OptionName : "CachePort"
|
||||
DefaultValue: "6379"
|
||||
ToPort :
|
||||
Fn::GetOptionSetting:
|
||||
OptionName : "CachePort"
|
||||
DefaultValue: "6379"
|
||||
SourceSecurityGroupName:
|
||||
Ref: "AWSEBSecurityGroup"
|
||||
MyElastiCache:
|
||||
Type: "AWS::ElastiCache::CacheCluster"
|
||||
Properties:
|
||||
CacheNodeType:
|
||||
Fn::GetOptionSetting:
|
||||
OptionName : "CacheNodeType"
|
||||
DefaultValue : "cache.t1.micro"
|
||||
NumCacheNodes:
|
||||
Fn::GetOptionSetting:
|
||||
OptionName : "NumCacheNodes"
|
||||
DefaultValue : "1"
|
||||
Engine:
|
||||
Fn::GetOptionSetting:
|
||||
OptionName : "Engine"
|
||||
DefaultValue : "redis"
|
||||
VpcSecurityGroupIds:
|
||||
-
|
||||
Fn::GetAtt:
|
||||
- MyCacheSecurityGroup
|
||||
- GroupId
|
||||
|
||||
Outputs:
|
||||
ElastiCache:
|
||||
Description : "ID of ElastiCache Cache Cluster with Redis Engine"
|
||||
Value :
|
||||
Ref : "MyElastiCache"
|
|
@ -0,0 +1,6 @@
|
|||
option_settings:
|
||||
"aws:elasticbeanstalk:customoption":
|
||||
CacheNodeType : cache.t1.micro
|
||||
NumCacheNodes : 1
|
||||
Engine : redis
|
||||
CachePort : 6379
|
17
{{cookiecutter.project_slug}}/.ebextensions/40_python.config
Normal file
17
{{cookiecutter.project_slug}}/.ebextensions/40_python.config
Normal file
|
@ -0,0 +1,17 @@
|
|||
container_commands:
|
||||
01_migrate:
|
||||
command: "source /opt/python/run/venv/bin/activate && python manage.py migrate"
|
||||
leader_only: True
|
||||
02_collectstatic:
|
||||
command: "source /opt/python/run/venv/bin/activate && python manage.py collectstatic --noinput"
|
||||
option_settings:
|
||||
"aws:elasticbeanstalk:application:environment":
|
||||
DJANGO_SETTINGS_MODULE: "config.settings.production"
|
||||
REDIS_ENDPOINT_ADDRESS: '`{ "Fn::GetAtt" : [ "MyElastiCache", "RedisEndpoint.Address"]}`'
|
||||
REDIS_PORT: '`{ "Fn::GetAtt" : [ "MyElastiCache", "RedisEndpoint.Port"]}`'
|
||||
"aws:elasticbeanstalk:container:python":
|
||||
WSGIPath: "config/wsgi.py"
|
||||
NumProcesses: 3
|
||||
NumThreads: 20
|
||||
"aws:elasticbeanstalk:container:python:staticfiles":
|
||||
"/static/": "www/static/"
|
|
@ -138,3 +138,13 @@ See detailed `cookiecutter-django Docker documentation`_.
|
|||
|
||||
.. _`cookiecutter-django Docker documentation`: http://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html
|
||||
{% endif %}
|
||||
{% if cookiecutter.use_elasticbeanstalk_experimental %}
|
||||
|
||||
Elastic Beanstalk
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
See detailed `cookiecutter-django Elastic Beanstalk documentation`_.
|
||||
|
||||
.. _`cookiecutter-django Docker documentation`: http://cookiecutter-django.readthedocs.io/en/latest/deployment-with-elastic-beanstalk.html
|
||||
|
||||
{% endif %}
|
||||
|
|
|
@ -177,16 +177,39 @@ TEMPLATES[0]['OPTIONS']['loaders'] = [
|
|||
|
||||
# DATABASE CONFIGURATION
|
||||
# ------------------------------------------------------------------------------
|
||||
{% if cookiecutter.use_elasticbeanstalk_experimental -%}
|
||||
# Uses Amazon RDS for database hosting, which doesn't follow the Heroku-style spec
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': env('RDS_DB_NAME'),
|
||||
'USER': env('RDS_USERNAME'),
|
||||
'PASSWORD': env('RDS_PASSWORD'),
|
||||
'HOST': env('RDS_HOSTNAME'),
|
||||
'PORT': env('RDS_PORT'),
|
||||
}
|
||||
}
|
||||
{% else %}
|
||||
# Use the Heroku-style specification
|
||||
# Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ
|
||||
DATABASES['default'] = env.db('DATABASE_URL')
|
||||
{%- endif %}
|
||||
|
||||
# CACHING
|
||||
# ------------------------------------------------------------------------------
|
||||
{% if cookiecutter.use_elasticbeanstalk_experimental -%}
|
||||
REDIS_LOCATION = "redis://{}:{}/0".format(
|
||||
env('REDIS_ENDPOINT_ADDRESS'),
|
||||
env('REDIS_PORT')
|
||||
)
|
||||
{% else %}
|
||||
REDIS_LOCATION = '{0}/{1}'.format(env('REDIS_URL', default='redis://127.0.0.1:6379'), 0)
|
||||
{%- endif %}
|
||||
# Heroku URL does not pass the DB number, so we parse it in
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django_redis.cache.RedisCache',
|
||||
'LOCATION': '{0}/{1}'.format(env('REDIS_URL', default='redis://127.0.0.1:6379'), 0),
|
||||
'LOCATION': REDIS_LOCATION,
|
||||
'OPTIONS': {
|
||||
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||||
'IGNORE_EXCEPTIONS': True, # mimics memcache behavior.
|
||||
|
|
31
{{cookiecutter.project_slug}}/ebsetenv.py
Normal file
31
{{cookiecutter.project_slug}}/ebsetenv.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
"""Converts a .env file to Elastic Beanstalk environment variables"""
|
||||
|
||||
from sys import exit
|
||||
from subprocess import check_call
|
||||
|
||||
try:
|
||||
import dotenv
|
||||
except ImportError:
|
||||
print("Please install the 'dotenv' library: 'pip install dotenv'")
|
||||
exit()
|
||||
|
||||
def main():
|
||||
command = ['eb', 'setenv']
|
||||
failures = []
|
||||
for key, value in dotenv.Dotenv('.env').items():
|
||||
if key.startswith('POSTGRES'):
|
||||
continue
|
||||
if value:
|
||||
command.append("{}={}".format(key, value))
|
||||
else:
|
||||
failures.append(key)
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print("{} requires a value".format(failure))
|
||||
else:
|
||||
print(' '.join(command))
|
||||
check_call(command)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
# PostgreSQL
|
||||
POSTGRES_PASSWORD=mysecretpass
|
||||
POSTGRES_USER=postgresuser
|
||||
|
@ -33,4 +34,4 @@ DJANGO_OPBEAT_SECRET_TOKEN
|
|||
{% endif %}
|
||||
{% if cookiecutter.use_compressor == 'y' -%}
|
||||
COMPRESS_ENABLED=
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
Loading…
Reference in New Issue
Block a user