AWS ALB + Django + Gunicorn +- Nginx? - django

I am building an environment in AWS to host a django application. I am trying to figure out if I should be using nginx as part of the build.
I am listing a few different environments below for example/comparison purposes. All environments make use of an AWS ALB.
ENV 1
ALB -> dockercontainer running django
+uses inbuilt django webserver, static files working
-inbuilt django webserver not made for production use
ENV 2
ALB -> dockercontainer running django/gunicorn
+uses gunicorn (not django webserver)
-static files NOT working
ENV 3
ALB -> dockercontainer running django/gunicorn + nginx
note: I have not tested this configuration yet.
+uses gunicorn (not django webserver)
+uses nginx
static files should work
I read this stackoverflow post and understand the differing roles of gunicorn vs nginx.
I am being advised by a colleague that ENV 2 is all I need, that I should be able to serve static files with it, that the ALB provides similar functionality to NGINX. Is this correct?

Just to clarify - "ALB" stands for Application Load Balancer, which is differentiated from the older Elastic Load Balancer in that traffic can be routed based on URI.
However, whichever load balancer you're referring to, I believe you'll need nginx in the mix, as AWS load balancers don't offer any file serving capability. If your static files have a consistent URI pattern, you might be able to use an ALB to serve static files from S3 or CloudFront.

Related

How to disable nginx from elastic beanstalk platform?

I'm switching from nginx to Caddy so I set up Caddy and created a new AMI. When I deploy this AMI to my EB environment, it fails to launch because nginx fails to start during the launch.
Using EB platform Ruby 2.7 running on 64bit Amazon Linux 2, how is it possible to prevent nginx from launching?
If you choose to modify the default AMI, and not create a new one, then ...to override the Elastic Beanstalk nginx configuration, add the following line to your nginx.conf per AWS documentation, do the above "...to pull in the Elastic Beanstalk configurations for Enhanced health reporting and monitoring, automatic application mappings, and static files."
Have a look here... I posted some of those copied sample snippets for you below to walk you through,
a) you would setup your daemon by overriding the nginx default config,
then b) tell the overridden ngix config to look for Caddys extensions
c) and that extension would read its config file
// this where you over ride nginx config
include conf.d/elasticbeanstalk/*.conf;
a) For e.g. Configuring Apache HTTPD
The Tomcat, Node.js, PHP, and Python platforms allow you to choose the Apache HTTPD proxy server as an alternative to nginx. This isn't the default. The following example configures Elastic Beanstalk to use Apache HTTPD.
Example, here we replace the reverse proxy with apache .ebextensions/httpd-proxy.config (you try caddy)
option_settings:
aws:elasticbeanstalk:environment:proxy:
ProxyServer: apache
Background:
By default it ships with Nginx as the reverse proxy on port 80 to find your app. on EBS. So, you have two options a 1) new Custom AMI or 2) Modifying your AMI
"...Proxy configuration files provided in the .ebextensions/nginx directory should move to the .platform/nginx platform hooks directory. For details, expand the Reverse Proxy Configuration section in Extending Elastic Beanstalk Linux platforms."
Then look at predeploy and postdeploy config options here

Deploy Multiple services to Elastic Beanstalk - example with Static Files & Python Flask

Background
Elastic beanstalk can be configured to serve multiple different processes through the application load balancer configuration.
In this example, let's assume we're configuring a static file server alongside a flask api endpoint.
The below picture, shows that we can create two processes, one api, pointing to port 2000 and the default process passing through at port 80.
Adding the rules in the ALB, means that we can have the incoming listener route to specific processes based on the path prefix. In this example, anything that starts with /api/* is being forwarded on to the api application while the remaining traffic falls through to the default service.
Question
This is all well and good, we now have an elastic beanstalk environment that can host multiple services. However, what's not clear is how does one actually go about deploying to target a specific process?
In a single process scenario, you just type eb deploy on a directory that's had eb init applied to it and off you go. Now, how does one go about deploying multiple processes using eb deploy and specifically how can one target the 'api' process or the 'default' process with their deployment in this example?
The question remains for how to get a deployment to target processes, but there is
an option for how to get this working specifically with static files that does not involve tweaking the ALB as per the question.
Create a folder in your root called .ebextensions
Within that, create a file anything.config - let's call it python.config and add the code below
option_settings:
aws:elasticbeanstalk:environment:proxy:staticfiles:
/html: html
The syntax of /html: html maps the incoming url path to a path on your local path that you eb deploy from (same level as .ebextensions) - In this case, the url path /html is the same as the physical folder name on the server, but that need not be the case.
Important: Do not add a trailing slash e.g./html/: html/ will not work
Nginx configs behind the scenes
By default, elastic beanstalk uses NGinx, a proxy server, that allows you to configure how to map url prefixes to locations on the drive.
By creating the above setting in .ebextensions, a new config value is created for the nginx server that sits in front of your application inside the ec2 instance that runs your app.
You can see this in action yourself, by logging on to your instance and navigating to the nginx staging area config below:
/var/proxy/staging/nginx/conf.d/elasticbeanstalk/
Where you will see the following files:
00_application.conf 01_static.conf healthd.conf
This setting is responsible for creating the 01_static.conf file with the following contents, aliasing any html request to the folder of the same name deployed under /var/app/current/
location /html {
alias /var/app/current/html;
access_log off;
}
To rapidly test nginx configuration changes without requiring eb deploy cycles, you can tweak the config from the final location located at
/etc/nginx/conf.d/elasticbeanstalk/01_static.conf
And then do a
service nginx restart
That's how I discovered, that the trailing slash was causing issues in my case.

Does Nginx becomes redundant if we have AWS Application Load balancer for a Node application?

I have a Node application running on AWS. When moving into production, there is a very high chance that it will get a very high number of requests. I'm planning to host it using AWS ECS and there will be an AWS Application load balancer in front of the application.
When I looked at "How to deploy Node application in production", I saw that everybody is suggesting the use of Nginx in front of Node application.
My doubt is that, if we have the ALB in the architecture do we need to add the Nginx also? Is there any advantage if we use Nginx if we need to host the application for 1 million users?
It depends on how you are using the NGINX for load balancing. Application Load balancer surely brings a lot of features that can make NGINX redundant in your architecture but it is not exactly as advanced as NGINX. For example ALB only use round robin load balancing, while you can configure nginx for round-robin, least connnection, etc. ALB does not have any caching capabilities while nginx provides static content caching. ALB only uses path based routing while nginx can route on request headers, cookies, or arguments, as well as the request URL.
For further reading and source : https://www.nginx.com/blog/aws-alb-vs-nginx-plus/
Note : One other important fact of using nginx is cloud agnostic. So if you plan to switch cloud provider, you can take the nginx settings with you.
It depends on the rest of your architecture. If ALB can handle everything for you, you probably don't need nginx. Also, nginx has a learning curve in case you are a first time user.

Deploy flask-socketio on beanstalk

I can't quite get Flask-SocketIO working with my instance on AWS Elastic Beanstalk (ELB), with the requirement of running Flask-SocketIO with socketio.run(application), ELB appears to make the calls to the global application object itself.
The ELB documentation states Using application.py as the filename and providing a callable application object (the Flask object, in this case) allows Elastic Beanstalk to easily find your application's code.
My ELB instance logs show the error RuntimeError: You need to use the eventlet server. See the Deployment section of the documentation for more information.
Is there any way to approach this problem assuming that AWS calls application.run()?
Thanks in advance
Flask-SocketIO has very specific requirements on the load balancer and the web server. I think you can configure the ELB load balancer with sticky sessions and that would make it work, but the part that I think does not work is using the eventlet or gevent web servers, since AWS invokes the callable in its own way. What you need is a way to use socketio.run() or an equivalent procedure that starts the eventlet/gevent web server.
There has been some changes to AWS beanstalk lately. Per default it uses gunicorn and nginx.
I got the setup working using a single instance setup without loadbalancers. The loadbalancer config in beanstalk allows stickiness configuration but my applications design would work only on a single instance anyway, so I didn't care.
To create a single instance beanstalk environment:
eb create --single my_env
Then, configure the way gunicorn is started: Create a file Procfile (see aws docs)
For eventlet use this:
web: gunicorn --worker-class eventlet -w 1 application:application
And place this into requirements.txt:
gunicorn==20.1.0
eventlet==0.30.2
The particular versions are needed to prevent the cannot import name 'ALREADY_HANDLED' error, see here.
See flask-socketio doc for other deployment options besides gunicorn/eventlet.

AWS ECS Production Docker Deployment

I've recently started using Docker for my own personal website. So the design my website is basically
Nginx -> Frontend -> Backend -> Database
Currently, the database is hosted using AWS RDS. So we can leave that out for now.
So here's my questions
I currently have my application separated into different repository. Frontend and Backend respectively.
Where should I store my 'root' docker-compose.yml file. I can't decide to store it in either the frontend/backend repository.
In a docker-compose.yml file, Can the nginx serve mount a volume from my frontend service without any ports and serve that directory?
I have been trying for so many days but I can't seem to deploy a proper production with Docker with my 3 tier application in ECS Cluster. Is there any good example nginx.conf that I can refer to?
How do I auto-SSL my domain?
Thank you guys!
Where should I store my 'root' docker-compose.yml file.
Many orgs use a top level repo which is used for storing infrastructure related metadata such as CloudFormation templates, and docker-compose.yml files. So it would be something like. So devs clone the top level repo first, and that repo ideally contains either submodules or tooling for pulling down the sub repos for each sub component or microservice.
In a docker-compose.yml file, Can the nginx serve mount a volume from my frontend service without any ports and serve that directory?
Yes you could do this but it would be dangerous and the disk would be a bottleneck. If your intention is to get content from the frontend service, and have it served by Nginx then you should link your frontend service via a port to your Nginx server, and setup your Nginx as a reverse proxy in front of your application container. You can also configure Nginx to cache the content from your frontend server to a disk volume (if it is too much content to fit in memory). This will be a safer way instead of using the disk as the communication link. Here is an example of how to configure such a reverse proxy on AWS ECS: https://github.com/awslabs/ecs-nginx-reverse-proxy/tree/master/reverse-proxy
I can't seem to deploy a proper production with Docker with my 3 tier application in ECS Cluster. Is there any good example nginx.conf that I can refer to?
The link in my last answer contains a sample nginx.conf that should be helpful, as well as a sample task definition for deploying an application container as well as a nginx container, linked to each other, on Amazon ECS.
How do I auto-SSL my domain?
If you are on AWS the best way to get SSL is to use the built in SSL termination capabilities of the Application Load Balancer (ALB). AWS ECS integrates with ALB as a way to get web traffic to your containers. ALB also integrates with Amazon certificate manager (https://aws.amazon.com/certificate-manager/) This service will give you a free SSL certificate which automatically updates. This way you don't have to worry about your SSL certificate expiring ever again, because its just automatically renewed and updated in your ALB.