<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Phil Chen</title>
	<atom:link href="https://philchen.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://philchen.com</link>
	<description>Phil Chen</description>
	<lastBuildDate>Wed, 13 Nov 2024 17:58:21 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.7.5</generator>
	<item>
		<title>Building an Asynchronous GenAI Image Generator API</title>
		<link>https://philchen.com/2024/10/10/building-a-asynchronous-genai-image-generator-api/</link>
		
		<dc:creator><![CDATA[Phil Chen]]></dc:creator>
		<pubDate>Fri, 11 Oct 2024 01:00:20 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">https://philchen.com/?p=2291</guid>

					<description><![CDATA[<p>This article will cover building an asynchronous GenAI image generator API, leveraging Amazon Bedrock Titan Image Generator v2. The API creates asynchronously four unique images based on your prompt and stores them in S3. Generating the images asynchronously allows for a faster processing of multiple images. The API will accept an HTTP POST request with [&#8230;]</p>
<p>The post <a href="https://philchen.com/2024/10/10/building-a-asynchronous-genai-image-generator-api/">Building an Asynchronous GenAI Image Generator API</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>This article will cover building an asynchronous GenAI image generator API, leveraging <a href="https://aws.amazon.com/bedrock/" rel="noopener" target="_blank">Amazon Bedrock</a> Titan Image Generator v2. The API creates asynchronously four unique images based on your prompt and stores them in <a href="https://aws.amazon.com/s3/" rel="noopener" target="_blank">S3</a>. Generating the images asynchronously allows for a faster processing of multiple images. The API will accept an HTTP POST request with the requested images and file name and folder location.</p>
<p><img fetchpriority="high" decoding="async" src="https://philchen.com/wp-content/uploads/2024/10/ai-image-generator-1.png" alt="AI Image Generator API Process Flow" width="1500" height="1646" class="aligncenter size-full wp-image-2302" srcset="https://philchen.com/wp-content/uploads/2024/10/ai-image-generator-1.png 1500w, https://philchen.com/wp-content/uploads/2024/10/ai-image-generator-1-273x300.png 273w, https://philchen.com/wp-content/uploads/2024/10/ai-image-generator-1-933x1024.png 933w, https://philchen.com/wp-content/uploads/2024/10/ai-image-generator-1-768x843.png 768w, https://philchen.com/wp-content/uploads/2024/10/ai-image-generator-1-1400x1536.png 1400w" sizes="(max-width: 1500px) 100vw, 1500px" /></p>
<p><strong>API Overview:</strong></p>
<ul>
<ul>
Create a <strong>POST</strong> request to the <strong>/generate_images</strong> endpoint, that has <code>prompt</code>, <code>output_folder</code> and <code>file_name_prefix</code>.</p>
<ul>
<li><code>prompt</code> is the description of the image you want generated.</li>
</ul>
<ul>
<li><code>output_folder</code> is the folder path to the image files, part of the S3 key.</li>
</ul>
<ul>
<li><code>file_name_prefix</code> is the name of the image file which will end up with a Unix epoch timestamp appended to it, part of the S3 key.</li>
</ul>
<ul>
<strong><em>Example POST request BODY:</em></strong></p>
<p><code>{"prompt": "red porsche", "output_folder": "000/111", "file_name_prefix": "porsche_image"}</code>
</ul>
<p>Four unique images will be generated based on your prompt and uploaded to the S3 bucket you specify in <code>image_generation.py</code> under <code>AI_S3_BUCKET_NAME</code></p>
<p>You will get a JSON manifest of file names after the images are generated.</p>
<ul>
<strong><em>Example POST request Response:</em></strong></p>
<p><code>{"images": ["000/111/porsche_image-1728409676.376359.png", "000/111/porsche_image-1728409676.376369.png", "000/111/porsche_image-1728409676.376371.png", "000/111/porsche_image-1728409676.3763719.png"]}</code>
</ul>
</ul>
<p><strong>Code:</strong></p>
<ul>
GitHub repo with files referenced in this blog <a href="https://github.com/nethacker/ai-image-generator" rel="noopener" target="_blank">ai-image-generator</a>
</ul>
<p><strong>Prerequisites:</strong></p>
<ul>
<li><a href="https://aws.amazon.com/" rel="noopener" target="_blank">Amazon Web Services Account</a></li>
<li>Enable Amazon Bedrock Access (Specifically Amazon Titan Image Generator v2) see: <a href="https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html" rel="noopener" target="_blank">Manage access to Amazon Bedrock foundation models.</a></li>
<li>EC2 Instance Role with AmazonBedrockFullAccess and AmazonS3FullAccess applied to your EC2 instance (note you can make this more secure by making a custom policy).</li>
<li>Verified on EC2 Instance Ubuntu 22.04 and Ubuntu 24.04</li>
<li>Verified on Python 3.10, 3.11, 3.12</li>
<li>Virtualenv</li>
<li>AWS Default Region is set to us-east-1 you can change the region in the <code>image_generation.py</code> file under <code>region_name='us-east-1'</code></li>
<li>AWS S3 bucket</li>
</ul>
<p><strong>AWS Resource Costs</strong><br />
As with most AWS services you will incur costs for usage.</p>
<li><a href="https://aws.amazon.com/bedrock/pricing/" rel="noopener" target="_blank">https://aws.amazon.com/bedrock/pricing/</a></li>
<li><a href="https://aws.amazon.com/ec2/pricing/on-demand/" rel="noopener" target="_blank">https://aws.amazon.com/ec2/pricing/on-demand/</a></li>
</ul>
<p><strong>Step 0</strong><br />
Install some dependencies</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>

sudo apt -y update

sudo apt -y install build-essential openssl

sudo apt -y install libpq-dev libssl-dev libffi-dev zlib1g-dev

sudo apt -y install python3-pip python3-dev

sudo apt -y install nginx

sudo apt -y install virtualenvwrapper
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 1</strong><br />
Setup the Python environment<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd /home/ubuntu

virtualenv ai-image-generator_env

source ai-image-generator_env/bin/activate
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 2</strong><br />
Create a directory for your project.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
mkdir /home/ubuntu/ai-image-generator

cd /home/ubuntu/ai-image-generator
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 3</strong><br />
Create a file <code>requirements.txt</code> for your Python dependencies.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim requirements.txt
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
boto3
botocore
Pillow
watchdog
requests
flask
flask-json
gunicorn
setproctitle
</pre>
<p></code></pre>
			</div>
        </div>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
pip install -r requirements.txt
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 4</strong><br />
Create a file <code>image_generation.py</code> which has image generation code.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd /home/ubuntu/ai-image-generator

vim image_generation.py
</pre>
<p></code></pre>
			</div>
        </div><br />
Make sure you put the S3 bucket you want to use in the <code>AI_S3_BUCKET_NAME</code> section.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
import io
import os
import base64
import json
import random
import boto3
from PIL import Image

AI_S3_BUCKET_NAME = "YOUR_S3_BUCKET_NAME_HERE"
AI_IMAGE_GENERATOR_TMP_PATH = "/tmp/ai_image_generator/"
NEGATIVE_PROMPTS = "poorly rendered, poor background details"

# Set Titan generative AI image generation settings.
def ai_image_settings(prompt):

    # Unique AI image random seed generation.
    random_seed = random.randint(0,2147833647)

    body = {
                "taskType": "TEXT_IMAGE",
                "textToImageParams": {
                "text": prompt,                    # Required
                "negativeText": NEGATIVE_PROMPTS   # Optional
             },
            "imageGenerationConfig": {
            "seed": (random_seed), # Range: 0 to 214783647
            "numberOfImages": 1,   # Range: 1 to 5
            "quality": "premium",  # Options: standard or premium
            "height": 1024,        # Supported height list in the docs
            "width": 1024,         # Supported width list in the docs
            "cfgScale": 9          # Range: 1.0 to 10.0
            }
    }

    json_string = json.dumps(body, indent=4)
    return json_string

# AI Image generation and storage.
def ai_image_generation(prompt, local_file_name, output_folder):

    # Invoke Amazon Titan image generator v2.
    boto3_bedrock = boto3.client('bedrock-runtime', region_name='us-east-1')
    ai_model = boto3_bedrock.invoke_model(
        body=ai_image_settings(prompt),
        modelId="amazon.titan-image-generator-v2:0",
        accept="application/json",
        contentType="application/json"
    )

    # Process the AI image Base64.
    response_body = json.loads(ai_model.get("body").read())
    image_base64 = response_body["images"][0]

    # Decode Base64 bytes and save image.
    ai_image = Image.open(
        io.BytesIO(
            base64.decodebytes(
                bytes(image_base64, "utf-8")
            )
        )
    )

    ai_image.save(f"{AI_IMAGE_GENERATOR_TMP_PATH}/{output_folder}/{local_file_name}")

    # Store images in S3.
    s3 = boto3.client('s3')
    bucket = AI_S3_BUCKET_NAME
    file_name = (f"{AI_IMAGE_GENERATOR_TMP_PATH}/{output_folder}/{local_file_name}")
    key_name = os.path.join(output_folder, local_file_name)
    s3.upload_file(file_name, bucket, key_name, ExtraArgs={'ContentType': "image/png"})

    # Delete temporary local copy.
    os.remove(f"{AI_IMAGE_GENERATOR_TMP_PATH}/{output_folder}/{local_file_name}")

# Local cleanup function.
def delete_files_in_directory(directory_path, rm_dir=False):
    try:
        files = os.listdir(directory_path)
        for file in files:
            file_path = os.path.join(directory_path, file)
            if os.path.isfile(file_path):
                os.remove(file_path)

        if (rm_dir):
            os.rmdir(directory_path)

        print("All image files deleted successfully.")
    except OSError:
        print("Error occurred while deleting image files.")
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 5</strong><br />
Create a file <code>ai_image_generator.py</code> which has the Flask routing code and logic.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd /home/ubuntu/ai-image-generator

vim ai_image_generator.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
import os
import json
import time
import concurrent.futures
from flask import Flask, request
from flask_json import FlaskJSON, JsonError, json_response
from image_generation import ai_image_generation, delete_files_in_directory

AI_IMAGE_GENERATOR_TMP_PATH = "/tmp/ai_image_generator/"

ai_image_generator = Flask(__name__)
FlaskJSON(ai_image_generator)

# 500MB Upload File Size Limit
ai_image_generator.config['MAX_CONTENT_PATH'] = 500 * 1024 * 1024

# Data validations.
def ai_image_generator_data_validation(request_data):
    try:
        prompt = str(request_data['prompt'])
    except (KeyError, TypeError, ValueError) as e:
        raise JsonError(description='Invalid prompt.') from e
    try:
        output_folder = str(request_data['output_folder'])
    except (KeyError, TypeError, ValueError) as e:
        raise JsonError(description='Invalid output_folder.') from e

    try:
        file_name_prefix = str(request_data['file_name_prefix'])
    except (KeyError, TypeError, ValueError) as e:
        raise JsonError(description='Invalid file_name_prefix.') from e

    return prompt, output_folder, file_name_prefix

# Health check route.
@ai_image_generator.route("/")
def health():
    return json_response(health="true")

# Generate images route.
@ai_image_generator.route('/generate_images', methods=['POST'])
def generate_images():
    request_data = request.get_json(force=True)
    prompt, output_folder, file_name_prefix = ai_image_generator_data_validation(request_data)

    output_path = os.path.join(AI_IMAGE_GENERATOR_TMP_PATH, output_folder.strip())

    # Checks if output folder exists, makes one if not. Delete files in folder if it does exist.
    if not os.path.exists(output_path):
        os.makedirs(output_path)
    else:
        delete_files_in_directory(output_path)

    #  Use a pool of threads to execute generation of 4 images asynchronously.
    with concurrent.futures.ThreadPoolExecutor() as executor:
        futures = []

        file_name = [(f"{file_name_prefix}") + "-" + str(time.time()) + ".png", (f"{file_name_prefix}") + "-" + str(time.time()) + ".png", (f"{file_name_prefix}") + "-" + str(time.time()) + ".png", (f"{file_name_prefix}") + "-" + str(time.time()) + ".png"]
        for file in file_name:
            futures.append(executor.submit(ai_image_generation, prompt=prompt, local_file_name=file, output_folder=output_folder))
        for future in concurrent.futures.as_completed(futures):
            print(future.result())

    # Return JSON output of location/files generated.
    keys = []
    for file in file_name:
        keys.append((os.path.join(output_folder, file)))

    manifest = {"images": keys}

    return json.dumps(manifest)

if __name__ == "__main__":
    ai_image_generator.run(host='0.0.0.0')
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 6</strong><br />
Create your <code>wsgi.py</code> webserver gateway interface for the Flask microframework<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim /home/ubuntu/ai-image-generator/wsgi.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
from ai_image_generator import ai_image_generator
 
if __name__ == "__main__":
    ai_image_generator.run()
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 7</strong><br />
Create a file <code>gunicorn_ai-image-generator.conf</code> which will be the configurations Gunicorn will utilize. Gunicorn is a Python WSGI HTTP Server for UNIX. For more information about Gunicorn read <a href="https://gunicorn.org/" rel="noopener noreferrer" target="_blank">here</a>.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd /home/ubuntu/ai-image-generator

mkdir gunicorn

cd gunicorn

vim gunicorn_ai-image-generator.conf
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to <code>gunicorn_ai-image-generator.conf</code><br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
pidfile = 'ai_image_generator.pid'
worker_class = 'gthread'
workers = 5
worker_connections = 1000
timeout = 30
keepalive = 2
threads = 2
proc_name = 'ai_image_generator'
bind = '0.0.0.0:8080'
backlog = 2048
accesslog = 'access.log'
errorlog = 'error.log'
user = 'ubuntu'
group = 'ubuntu'
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 8</strong><br />
Create a Systemd service file. Systemd is a service manager and in this use case it will manage Gunicorn.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/systemd/system/ai-image-generator.service
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to ai-image-generator.service<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
[Unit]
Description=AI Image Generator API
After=network.target
 
[Service]
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu/ai-image-generator
Environment="PATH=/home/ubuntu/ai-image-generator_env/bin"
ExecStart=/home/ubuntu/ai-image-generator_env/bin/gunicorn --config=gunicorn/gunicorn_ai-image-generator.conf wsgi:ai_image_generator
 
[Install]
WantedBy=multi-user.target
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 9</strong><br />
Start your application via Systemd and enable it to auto start after reboot<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo systemctl start ai-image-generator

sudo systemctl enable ai-image-generator.service
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 10</strong><br />
Create your applications NGINX config <code>nginx_ai-image-generator.conf</code> that makes NGINX listen on port 80 and proxies requests to Gunicorn on port 8080. NGINX will help with connection handling increasing performance.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/nginx/sites-available/nginx_ai-image-generator.conf
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to nginx_ai-image-generator.conf<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
server {
    listen 80 default_server;

    location / {
        include proxy_params;
      	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP	$remote_addr;
      	proxy_set_header Host $http_host;
      	proxy_redirect off;
        proxy_buffers 8 24k;
        proxy_buffer_size 4k;
        proxy_pass http://127.0.0.1:8080;
    }
}
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 11</strong><br />
Delete your default NGINX conf file, enable your new NGINX conf and restart NGINX<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo rm /etc/nginx/sites-enabled/default

sudo ln -s /etc/nginx/sites-available/nginx_ai-image-generator.conf /etc/nginx/sites-enabled

sudo systemctl restart nginx
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 12</strong><br />
Time to test your API! From a command line you can test the API out by running:<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
curl -X POST --data '{"prompt": "red porsche", "output_folder": "000/111", "file_name_prefix": "porsche_image"}' http://{yourhost}:80/generate_images
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Notes:</strong></p>
<li>Make sure to open up port 80 in your EC2 Security Group associated to the instance.</li>
<li>For HTTPS (TLS) you can use AWS ALB or AWS CloudFront</li>
<li>This AI Image Generator API service does not take into consideration security controls, that is your responsibility.</li>
<li>Please read <a href="https://aws.amazon.com/bedrock/faqs/">Amazon Bedrock FAQ&#8217;s</a> for general questions about AWS LLM resources used.</li>
<p><strong>Summary:</strong></p>
<p>Hopefully this blog article helps you create a robust scalable AI driven image generation API. The idea is that a UI/UX of your design will leverage this API to display four images for the end user to choose from. Images will be generated at the same time so all four images will take the same amount of time as one image. The parameters allow for storage in some form of hierarchy with a folder path, and each image will have a unix epoch timestamp. An example structure could be account-number/project-number/file-name. The API will generated a JSON response with where the images are located in S3 for your UI/UX to handle.</p>
<p>If you like this blog article you may want to read my other article on <a href="https://philchen.com/2024/09/19/building-a-genai-pdf-query-app-with-a-rag-architecture/" rel="noopener" target="_blank">Building a GenAI PDF Query App with a RAG Architecture</a>.</p><p>The post <a href="https://philchen.com/2024/10/10/building-a-asynchronous-genai-image-generator-api/">Building an Asynchronous GenAI Image Generator API</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Building a GenAI PDF Query App with a RAG Architecture</title>
		<link>https://philchen.com/2024/09/19/building-a-genai-pdf-query-app-with-a-rag-architecture/</link>
		
		<dc:creator><![CDATA[Phil Chen]]></dc:creator>
		<pubDate>Thu, 19 Sep 2024 18:00:15 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">https://philchen.com/?p=2170</guid>

					<description><![CDATA[<p>This article will cover building a Generative AI PDF query web application that uses a retrieval-augmented generation (RAG) architecture. The PDF query web application will go through your PDF&#8217;s and provide you responses to your questions based on what is in your PDF&#8217;s. The importance of using RAG is the ability to scope the results [&#8230;]</p>
<p>The post <a href="https://philchen.com/2024/09/19/building-a-genai-pdf-query-app-with-a-rag-architecture/">Building a GenAI PDF Query App with a RAG Architecture</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>This article will cover building a Generative AI PDF query web application that uses a <a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation" rel="noopener" target="_blank">retrieval-augmented generation (RAG)</a> architecture. The PDF query web application will go through your PDF&#8217;s and provide you responses to your questions based on what is in your PDF&#8217;s. The importance of using RAG is the ability to scope the results of the generated response from the LLM in our case <a href="https://docs.anthropic.com/en/docs/about-claude/models" rel="noopener" target="_blank">Claude 3.5 Sonnet</a> with up-to-date, accurate, reliable responses. RAG allows for domain-specific contextually relevant responses tailored to your data rather than static training data.</p>
<p>The PDF query web application will leverage <a href="https://ai.meta.com/tools/faiss/" rel="noopener" target="_blank">Facebook AI Similarity Search (FAISS)</a> and <a href="https://docs.aws.amazon.com/bedrock/latest/userguide/titan-embedding-models.html" rel="noopener" target="_blank">Amazon Titan Embeddings</a> to create vector representations of unstructured text and the storage/search of those embeddings. <a href="https://www.langchain.com/" rel="noopener" target="_blank">LangChain</a> is utilized for the prompt template guiding the models response, RetrievalQA for pertinent data, and various PDF processing tools. We will use <a href="https://aws.amazon.com/bedrock/" target="_blank" rel="noopener">Amazon Bedrock</a> to access Claude 3.5 Sonnet and Amazon Titan Embeddings.</p>
<p><img decoding="async" src="https://philchen.com/wp-content/uploads/2024/09/flow-diagram-v2.png" alt="PDF Query RAG-Based LLM GenAI Web Application Process Flow" width="1568" height="1276" class="aligncenter size-full wp-image-2168" srcset="https://philchen.com/wp-content/uploads/2024/09/flow-diagram-v2.png 1568w, https://philchen.com/wp-content/uploads/2024/09/flow-diagram-v2-300x244.png 300w, https://philchen.com/wp-content/uploads/2024/09/flow-diagram-v2-1024x833.png 1024w, https://philchen.com/wp-content/uploads/2024/09/flow-diagram-v2-768x625.png 768w, https://philchen.com/wp-content/uploads/2024/09/flow-diagram-v2-1536x1250.png 1536w" sizes="(max-width: 1568px) 100vw, 1568px" /></p>
<p><strong>Code:</strong></p>
<p>GitHub repo with files referenced in this blog <a href="https://github.com/nethacker/pdf-query-rag-llm-app" rel="noopener" target="_blank">pdf-query-rag-llm-app</a></p>
<p><strong>Prerequisites:</strong></p>
<ul>
<li><a href="https://aws.amazon.com/" rel="noopener" target="_blank">Amazon Web Services Account</a></li>
<li>Enable Amazon Bedrock Access (Specifically Amazon Titan Embeddings and Claude 3.5 Sonnet) see: <a href="https://docs.aws.amazon.com/bedrock/latest/      userguide/model-access.html" rel="noopener" target="_blank">Manage access to  Amazon Bedrock foundation models</a>
<li>EC2 Instance Role with AmazonBedrockFullAccess Policy Attached (note you can make this more secure by making a custom policy)</li>
<li>Verified on EC2 Instance Ubuntu 22.04 and Ubuntu 24.04</li>
<li>Verified with Python 3.10, 3.11, 3.12
<li>Virtualenv</li>
<li>AWS Default Region is set to us-east-1 you can change the region in the <code>pdf_query_rag_llm_app.py</code> file under <code>region_name='us-east-1'</code></li>
</ul>
<p><strong>AWS Resource Cost:</strong></p>
<p>As with most AWS services you will incur costs for usage.</p>
<ul>
<li><a href="https://aws.amazon.com/bedrock/pricing/" rel="noopener" target="_blank">https://aws.amazon.com/bedrock/pricing/</a></li>
<li><a href="https://aws.amazon.com/ec2/pricing/on-demand/" rel="noopener" target="_blank">https://aws.amazon.com/ec2/pricing/on-demand/</a></li>
</ul>
<p><strong>EC2 Ubuntu Instance Setup:</strong><br />
(This article assumes you have a ubuntu user with <code>/home/ubuntu</code>)</p>
<p><strong>Step 0</strong><br />
Install some dependencies.</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>

sudo apt -y update

sudo apt -y install build-essential openssl

sudo apt -y install libpq-dev libssl-dev libffi-dev zlib1g-dev

sudo apt -y install python3-pip python3-dev

sudo apt -y install nginx

sudo apt -y install virtualenvwrapper
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 1</strong><br />
Setup the Python environment.</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd /home/ubuntu

virtualenv pdf-query-rag-llm-app_env

source pdf-query-rag-llm-app_env/bin/activate
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 2</strong><br />
Create a directory for your project.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
mkdir /home/ubuntu/pdf-query-rag-llm-app

cd /home/ubuntu/pdf-query-rag-llm-app
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 3</strong><br />
Create a file <code>requirements.txt</code> for your Python dependencies.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim requirements.txt
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
awscli
pypdf
streamlit
boto3
faiss-cpu
langchain-aws
langchain_community
</pre>
<p></code></pre>
			</div>
        </div>
<p>Install your Python dependencies.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
pip install -r requirements.txt
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 4</strong><br />
Create a file <code>pdf_query_rag_llm_app.py</code> which has the PDF query application code.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd /home/ubuntu/pdf-query-rag-llm-app

vim pdf_query_rag_llm_app.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
import os
import boto3
import streamlit as st
from langchain_aws import BedrockEmbeddings
from langchain_aws.chat_models import ChatBedrock
from langchain_community.vectorstores.faiss import FAISS
from langchain_community.document_loaders.pdf import PyPDFDirectoryLoader
from langchain.chains.retrieval_qa.base import RetrievalQA
from langchain.prompts import PromptTemplate
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.globals import set_verbose
set_verbose(False)

bedrock = boto3.client(service_name='bedrock-runtime', region_name='us-east-1')
titan_embeddings = BedrockEmbeddings(model_id="amazon.titan-embed-text-v1", client=bedrock)

# Data Preparation
def data_ingestion():
    loader = PyPDFDirectoryLoader("data")
    documents = loader.load()
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=2000, chunk_overlap=500)
    docs = text_splitter.split_documents(documents)
    return docs

# Vector Store Setup
def setup_vector_store(documents):
    vector_store = FAISS.from_documents(
        documents,
        titan_embeddings,
    )
    vector_store.save_local("faiss_index")

# LLM Setup
def load_llm():
    llm = ChatBedrock(model_id="anthropic.claude-3-5-sonnet-20240620-v1:0", client=bedrock, model_kwargs={"max_tokens": 2048})
    return llm

# LLM Guidelines
prompt_template = """Use the following pieces of context to answer the question at the end. Follow these rules:
1. If the answer is not within the context knowledge, state that you do not know, and do not fabricate an answer.
2. If you find the answer, create a detailed, and concise response to the question. Aim for a summary of max 200 words.
3. Do not add extra information not within the context.

{context}

Question: {question}
Helpful Answer:"""

# Prompt Template
prompt = PromptTemplate(template=prompt_template, input_variables=["context", "question"])

# Create QA Chain 
def get_result(llm, vector_store, query):
    qa_chain = RetrievalQA.from_chain_type(
        llm=llm,
        chain_type="stuff",
        retriever=vector_store.as_retriever(
            search_type="similarity", search_kwargs={"k": 3}
        ),
        chain_type_kwargs={"prompt": prompt},
        return_source_documents=True,
    )
    # Apply LLM
    result = qa_chain.invoke(query)
    return result['result']

# Streamlit Frontend UI Section
def streamlit_ui():
    st.set_page_config("PDF Query RAG LLM Application")
    st.markdown("""
        <style>
            .reportview-container {
                margin-top: -2em;
            }
            #MainMenu {visibility: hidden;}
            .stDeployButton {display:none;}
            footer {visibility: hidden;}
            #stDecoration {display:none;}
        </style>
    """, unsafe_allow_html=True)
    st.header("PDF Query with Generative AI")

    user_question = st.text_input(r":gray[$\textsf{\normalsize Ask me anything about your PDF collection.}$]")

    left_column, middleleft_column, middleright_column, right_column = st.columns(4, gap="small")
    if left_column.button("Generate Response", key="submit_question") or user_question:
        # first check if the vector store exists
        if not os.path.exists("faiss_index"):
            st.error("Please create the vector store first from the sidebar.")
            return
        if not user_question:
            st.error("Please enter a question.")
            return
        with st.spinner("Generating... this may take a minute..."):
            faiss_index = FAISS.load_local("faiss_index", embeddings=titan_embeddings,
                                           allow_dangerous_deserialization=True)
            llm = load_llm()
            st.write(get_result(llm, faiss_index, user_question))
            st.success("Generated")
    if middleleft_column:
        st.empty()
    if middleright_column:
        st.empty()
    if right_column.button("New Data Update", key="update_vector_store"):
        with st.spinner("Updating... this may take a few minutes as we go through your PDF collection..."):
            docs = data_ingestion()
            setup_vector_store(docs)
            st.success("Updated")

if __name__ == "__main__":
    streamlit_ui()
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 5</strong><br />
Create a <code>data</code> directory to hold your PDF&#8217;s. <em>*note you will want to put your PDF&#8217;s in this directory that you want to query.</em>.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd /home/ubuntu/pdf-query-rag-llm-app

mkdir data
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 6</strong><br />
Create a Systemd service file. Systemd is a service manager and in this use case it will manage streamlit.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/systemd/system/pdf-query-rag-llm-app.service
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to <code>pdf-query-rag-llm-app.service</code><br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
[Unit]
Description=PDF query with Generative AI Service
After=network.target
 
[Service]
User=root
Group=root
WorkingDirectory=/home/ubuntu/pdf-query-rag-llm-app
Environment="PATH=/home/ubuntu/pdf-query-rag-llm-app_env/bin"
ExecStart=/home/ubuntu/pdf-query-rag-llm-app_env/bin/streamlit run /home/ubuntu/pdf-query-rag-llm-app/pdf_query_rag_llm_app.py --server.port 8080 --server.headless true
 
[Install]
WantedBy=multi-user.target
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 7</strong><br />
Start your application via Systemd and enable it to auto start after reboot<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo systemctl start pdf-query-rag-llm-app.service

sudo systemctl enable pdf-query-rag-llm-app.service
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 8</strong><br />
Create your applications NGINX config <code>nginx_ai-image-generator.conf</code> that makes NGINX listen on port 80 and proxies requests to Streamlit on port 8080. NGINX will help with connection handling increasing performance.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/nginx/sites-available/nginx_pdf-query-rag-llm-app.conf
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to <code>nginx_pdf-query-rag-llm-app.conf</code><br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
server {
    listen 80 default_server;

    location / {
        include proxy_params;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP      $remote_addr;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_buffers 8 24k;
        proxy_buffer_size 4k;
        proxy_pass http://127.0.0.1:8080;
    }
    location /_stcore/stream {
        proxy_pass http://127.0.0.1:8080/_stcore/stream;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 86400;
    }
}
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 9</strong><br />
Delete your default NGINX conf file, enable your new NGINX conf and restart NGINX (Port 80)</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>

sudo rm /etc/nginx/sites-enabled/default

sudo ln -s /etc/nginx/sites-available/nginx_pdf-query-rag-llm-app.conf /etc/nginx/sites-enabled

sudo systemctl restart nginx
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 10</strong><br />
Test your application, remember to put your PDF&#8217;s that you want to query in the data directory on the instance and click <em>New Data Update</em> before querying.</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
http://{yourhost}
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Notes:</strong></p>
<ul>
<li>Make sure to open up port 80 in your EC2 Security Group associated to the instance.</li>
<li>For HTTPS (TLS) you can use AWS ALB or AWS CloudFront.</li>
<li>Depending on how many PDF&#8217;s you have, how big the PDF&#8217;s are, and your CPU specifications using the <em>New Data Update</em> button can take awhile as it builds your vector embeddings.</li>
<li>Any time you add PDF&#8217;s or change them make sure to click &#8220;New Data Update&#8221; to update/build your vector embeddings.</li>
<li>This application does not take into consideration security controls, that is your responsibility.</li>
<li>Please read <a href="https://aws.amazon.com/bedrock/faqs/" rel="noopener" target="_blank">Amazon Bedrock FAQ&#8217;s</a> for general questions about AWS LLM resources used.</li>
</ul>
<p><strong>Summary:</strong><br />
Hopefully this blog article helps you create a generative AI PDF query web application which is scoped to your specific data and context leveraging a RAG architecture. Retrieval-augmented generation (RAG) is a powerful architecture choice to both leverage a trained model in this case Claude 3.5 Sonnet, and your organizations specific data (vectorized) without re-training the LLM model, but rather augmenting its knowledge base.</p>
<p>If you like this blog article you may want to read my other article on <a href="https://philchen.com/2024/10/10/building-a-asynchronous-ai-image-generator-api/">Building an Asynchronous GenAI Image Generator API</a>.</p><p>The post <a href="https://philchen.com/2024/09/19/building-a-genai-pdf-query-app-with-a-rag-architecture/">Building a GenAI PDF Query App with a RAG Architecture</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Scaling a Python Flask App with NGINX using Multiple Containers with Docker Compose</title>
		<link>https://philchen.com/2019/07/15/scaling-a-python-flask-app-with-nginx-using-multiple-containers-with-docker-compose/</link>
		
		<dc:creator><![CDATA[Phil Chen]]></dc:creator>
		<pubDate>Mon, 15 Jul 2019 19:41:36 +0000</pubDate>
				<category><![CDATA[Scale]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[docker compose]]></category>
		<category><![CDATA[flask]]></category>
		<category><![CDATA[gunicorn]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[python]]></category>
		<guid isPermaLink="false">https://philchen.com/?p=2107</guid>

					<description><![CDATA[<p>This article will go over scaling a Python Flask application utilizing a multi-container docker architecture. Leveraging Docker Compose we will create a NGINX Docker container that will act as a load balancer with two Python Flask application containers it will direct traffic to. The Python Flask application will serve a web page via a GET [&#8230;]</p>
<p>The post <a href="https://philchen.com/2019/07/15/scaling-a-python-flask-app-with-nginx-using-multiple-containers-with-docker-compose/">Scaling a Python Flask App with NGINX using Multiple Containers with Docker Compose</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>This article will go over scaling a Python Flask application utilizing a multi-container docker architecture. Leveraging <a href="https://docs.docker.com/compose/overview/" target="_blank" rel="noopener noreferrer">Docker Compose</a> we will create a NGINX Docker container that will act as a load balancer with two Python Flask application containers it will direct traffic to. The Python Flask application will serve a web page via a GET request and will be running Gunicorn.</p>
<p><strong>Assumptions:</strong></p>
<ul>
<li>You have <a href="https://www.docker.com/" target="_blank" rel="noopener noreferrer">Docker</a> installed</li>
<li>These instructions are were done on a Mac</li>
<li>You are leveraging multiple containers on one host (docker engine), if not read the bottom of this post about &#8220;docker stack deploy&#8221;.</li>
</ul>
<p><strong>Quick Links:</strong></p>
<p>GitHub repo with files referenced in this blog <a href="https://github.com/nethacker/ubuntu-flask-gunicorn-nginx-docker-compose" target="_blank" rel="noopener noreferrer">ubuntu-flask-gunicorn-nginx-docker-compose</a></p>
<p>Docker Hub repo with some of the images used in the blog <a href="https://hub.docker.com/u/nethacker" target="_blank" rel="noopener noreferrer">here</a>.<br />
<span id="more-2107"></span></p>
<p><strong>File Structure:</strong><br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="no-line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"><br />
ubuntu-flask-gunicorn-nginx-docker-compose/<br />
    |<br />
    docker-compose.yml<br />
    |<br />
    app/<br />
    |   &#8211;&gt; Dockerfile<br />
    |   &#8211;&gt; src<br />
    |          &#8211;&gt; app01.py<br />
    |          &#8211;&gt; gunicorn_config.py<br />
    |          &#8211;&gt; wsgi.py<br />
    |          &#8211;&gt; requirements.txt<br />
    |<br />
    nginx/<br />
          &#8211;&gt; Dockerfile<br />
          &#8211;&gt; nginx.conf<br />
</code></pre>
			</div>
        </div><br />
<strong>Step 0</strong><br />
Create a directory for your project.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
mkdir ubuntu-flask-gunicorn-nginx-docker-compose

cd ubuntu-flask-gunicorn-nginx-docker-compose

mkdir -p app/src

mkdir nginx
</pre>
<p></code></pre>
			</div>
        </div><br />
<strong>Step 1</strong><br />
Create a file requirements.txt for your Python dependencies, such as Flask or Gunicorn. For more information about PIP requirements.txt read <a href="https://pip.readthedocs.io/en/1.1/requirements.html" target="_blank" rel="noopener noreferrer">here</a>.</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd app/src
</pre>
<p></code></pre>
			</div>
        </div>
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim requirements.txt
</pre>
<p></code></pre>
			</div>
        </div>
<p>Add the following to the requirements.txt<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="no-line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
Click==7.0
Flask==1.0.3
gunicorn==19.9.0
itsdangerous==1.1.0
Jinja2==2.10.1
MarkupSafe==1.1.1
Werkzeug==0.15.4
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 2</strong><br />
Create a file app01.py which will be a simple Flask web app. Flask is a microframework for Python based on Werkzeug and Jinja2. For more information about Flask read <a href="http://flask.pocoo.org/" target="_blank" rel="noopener noreferrer">here</a>.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim app01.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to app01.py<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
from flask import Flask
hello = Flask(__name__)

@hello.route("/")
def greeting():
    return "<h1 style="color: red;">Hello World!</h1>"

if __name__ == "__main__":
    hello.run(host='0.0.0.0')
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 3</strong><br />
Create a file wsgi.py which will be the gateway from the webserver to your app. For more information about WSGI read <a href="https://wsgi.readthedocs.io/en/latest/" target="_blank" rel="noopener noreferrer">here</a>.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim wsgi.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to wsgi.py<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
from app01 import hello

if __name__ == "__main__":
    hello.run()
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 4</strong><br />
Create a file gunicorn_config.py which will be the configurations Gunicorn will utilize. Gunicorn is a Python WSGI HTTP Server for UNIX. For more information about Gunicorn read <a href="https://gunicorn.org/" target="_blank" rel="noopener noreferrer">here</a>.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim gunicorn_config.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to <code>gunicorn_config.py</code><br />
*Note we set workers to 2 because if you only have one worker, and it’s handling a slow query, the heartbeat query will timeout which could remove it from a load balancer. Also container schedulers expect logs to come out on stdout and stderr so we have it set in the config as such. We also leverage /dev/shm vs default /tmp to avoid timeouts accessing ram vs disk.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="no-line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
pidfile = 'app01.pid'
worker_tmp_dir = '/dev/shm'
worker_class = 'gthread'
workers = 2
worker_connections = 1000
timeout = 30
keepalive = 2
threads = 4
proc_name = 'app01'
bind = '0.0.0.0:8080'
backlog = 2048
accesslog = '-'
errorlog = '-'
user = 'ubuntu'
group = 'ubuntu'
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 5</strong><br />
Create a Dockerfile which contains the commands to build your Python Flask App Docker image. For more information on Dockerfile read <a href="https://docs.docker.com/engine/reference/builder/" target="_blank" rel="noopener noreferrer">here</a>.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd ../

vim Dockerfile
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to Dockerfile<br />
*Note we pull a base ubuntu 18.04 image with python 3.7.3 from my Docker Hub repo you could point this to the official Ubuntu repo and add a Python image or install line. All the files are copied into the /home/ubuntu container directory and referenced from there.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
FROM nethacker/ubuntu-18-04-python-3:python-3.7.3
COPY src/requirements.txt /root/
RUN pip install -r /root/requirements.txt &amp;&amp; useradd -m ubuntu
ENV HOME=/home/ubuntu
USER ubuntu
COPY src/app01.py src/wsgi.py src/gunicorn_config.py /home/ubuntu/
WORKDIR /home/ubuntu/
EXPOSE 8080
CMD ["gunicorn", "-c", "gunicorn_config.py", "wsgi:hello"]
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 6</strong><br />
Create a NGINX configuration file to make a reverse proxy load balancer to your Python Flask application<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd ../nginx/

vim nginx.conf
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to your nginx.conf file for NGINX to act as a reverse proxy load balancer.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
events { worker_connections 1024; }

http {

 proxy_headers_hash_max_size 1024;
 proxy_headers_hash_bucket_size 64;

 upstream localhost {
    # References to our app containers, via docker compose
    server app01:8080;
    server app02:8080;
 }
 server {
    listen 80;
    server_name localhost;
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP      $remote_addr;
        proxy_redirect off;
        proxy_buffers 8 24k;
        proxy_buffer_size 4k;
        proxy_pass http://localhost;
        proxy_set_header Host $host;
    }
  }
}
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 7</strong><br />
Create a file Dockerfile which contains the commands to build your NGINX Docker image.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim Dockerfile
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to Dockerfile<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
FROM nethacker/ubuntu-18-04-nginx:1.17.1
RUN apt-get update &amp;&amp; apt-get install -y \
    build-essential \
    curl \
  &amp;&amp; rm -rf /var/lib/apt/lists/*
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 8</strong><br />
Create your <a href="https://docs.docker.com/compose/overview/" target="_blank" rel="noopener noreferrer">Docker Compose YAML</a> file outlining one NGINX container with two backend Python Flask application containers to which to direct traffic too. Also specify the NGINX port as well increasing shm size for Gunicorn to use.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd ../

vim docker-compose.yml

</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to docker-compose.yml file.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
version: '3.7'
services:
    app01:
        shm_size: '1000000000'
        build:
            context: ./app
        tty: true
        volumes:
            - './app/src:/home/ubuntu'
    app02:
        shm_size: '1000000000'
        build:
            context: ./app
        tty: true
        volumes:
            - './app/src:/home/ubuntu'
    nginx:
        build: ./nginx
        tty: true
        links:
            - app01
            - app02
        ports:
            - '80:80'
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 9</strong><br />
Build and Start your Docker images using Docker Compose, you will end up with one NGINX container with two backend Python Flask application containers. The NGINX container will listen on port 80 and forward traffic to the backend apps on 8080.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
docker-compose up --build --detach
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 10</strong><br />
Test your Docker container</p>
<p>If you didn&#8217;t modify the example you should be able to go to localhost port 80 in a browser and get a red &#8220;Hello World&#8221; message.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
http://localhost
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>You can also do the following to access your containers with a bash shell to poke around.</p>
<p>Find the running Docker container id you wish to examine.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
docker ps
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Get and interactive shell on the container.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
docker exec -it {id here} /bin/bash
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Hopefully this basic overview of Docker Compose and multi-containers on a single host/docker engine helps you scale your application.</p>
<p>On a side note,<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
docker-compose
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>command appears to be transitioning to:<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-slim-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
docker stack deploy
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>with the difference &#8220;docker-compose&#8221; can do builds in the docker-compose.yml and 2.0/3.0 spec with caveats, but &#8220;docker stack deploy&#8221; cannot do build commands in the docker-compose.yml and needs prebuilt images as well as 3.0 spec making docker compose nicer for development purposes but &#8220;docker stack deploy&#8221; a more production oriented method with multiple containers over multiple hosts leveraging swarm. In both cases docker-compose.yml is used and Docker will ignore commands not support by the respective compose vs stack call (silently). I will do a post on Docker Swarm next.</p><p>The post <a href="https://philchen.com/2019/07/15/scaling-a-python-flask-app-with-nginx-using-multiple-containers-with-docker-compose/">Scaling a Python Flask App with NGINX using Multiple Containers with Docker Compose</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>A scalable Flask application using Gunicorn on Ubuntu 18.04 in Docker</title>
		<link>https://philchen.com/2019/07/09/a-scalable-flask-application-using-gunicorn-on-ubuntu-18-04-in-docker/</link>
		
		<dc:creator><![CDATA[Phil Chen]]></dc:creator>
		<pubDate>Tue, 09 Jul 2019 22:05:39 +0000</pubDate>
				<category><![CDATA[Scale]]></category>
		<category><![CDATA[containers]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[flask]]></category>
		<category><![CDATA[gunicorn]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[web app]]></category>
		<guid isPermaLink="false">https://philchen.com/?p=2080</guid>

					<description><![CDATA[<p>This article will cover creating a scalable Flask application using Gunicorn on Ubuntu 18.04 in Docker. We loosely are defining Flask application to mean serving up a web page via a GET request, however this blog can be used as an example to build a website, Restful API, or any number of things leveraging Docker. [&#8230;]</p>
<p>The post <a href="https://philchen.com/2019/07/09/a-scalable-flask-application-using-gunicorn-on-ubuntu-18-04-in-docker/">A scalable Flask application using Gunicorn on Ubuntu 18.04 in Docker</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>This article will cover creating a scalable Flask application using Gunicorn on Ubuntu 18.04 in Docker. We loosely are defining Flask application to mean serving up a web page via a GET request, however this blog can be used as an example to build a website, Restful API, or any number of things leveraging Docker. Via a Dockerfile we will build a Docker image that you can modify for your own Flask application needs, the example in this blog will be a simple &#8220;hello world&#8221; web page.</p>
<p><strong>Quick Links:</strong></p>
<p>GitHub repo with files referenced in this blog <a href="https://github.com/nethacker/ubuntu-flask-gunicorn-docker" rel="noopener noreferrer" target="_blank">ubuntu-flask-gunicorn-docker</a></p>
<p>Docker Hub repo for the example app referenced in this blog <a href="https://hub.docker.com/r/nethacker/ubuntu-18-04-flask-gunicorn-example" rel="noopener noreferrer" target="_blank">nethacker/ubuntu-18-04-flask-gunicorn-example</a></p>
<p>Docker Hub repo for Ubuntu with Flask and Gunicorn with no app <a href="https://hub.docker.com/r/nethacker/ubuntu-18-04-flask-gunicorn" rel="noopener noreferrer" target="_blank">nethacker/ubuntu-18-04-flask-gunicorn</a></p>
<p><strong>Assumptions:</strong></p>
<li>You have Docker installed</li>
<li>These instructions are were done on a Mac</li>
<p><strong>Step 0</strong><br />
Create a directory for your project.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
mkdir ubuntu-flask-gunicorn-docker

cd ubuntu-flask-gunicorn-docker
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 1</strong><br />
Create a file requirements.txt for your Python dependencies, such as Flask or Gunicorn. For more information about PIP requirements.txt read <a href="https://pip.readthedocs.io/en/1.1/requirements.html" rel="noopener noreferrer" target="_blank">here</a>.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim requirements.txt
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to the requirements.txt<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
Click==7.0
Flask==1.0.3
gunicorn==19.9.0
itsdangerous==1.1.0
Jinja2==2.10.1
MarkupSafe==1.1.1
Werkzeug==0.15.4
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 2</strong><br />
Create a file app01.py which will be a simple Flask web app. Flask is a microframework for Python based on Werkzeug and Jinja2. For more information about Flask read <a href="http://flask.pocoo.org/" rel="noopener noreferrer" target="_blank">here</a>.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim app01.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to app01.py<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
from flask import Flask
hello = Flask(__name__)

@hello.route("/")
def greeting():
    return "<h1 style='color:red'>Hello World!</h1>"

if __name__ == "__main__":
    hello.run(host='0.0.0.0')
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 3</strong><br />
Create a file wsgi.py which will be the gateway from the webserver to your app. For more information about WSGI read <a href="https://wsgi.readthedocs.io/en/latest/" rel="noopener noreferrer" target="_blank">here</a>.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim wsgi.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to wsgi.py<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
from app01 import hello

if __name__ == "__main__":
    hello.run()
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 4</strong><br />
Create a file gunicorn_config.py which will be the configurations Gunicorn will utilize. Gunicorn is a Python WSGI HTTP Server for UNIX. For more information about Gunicorn read <a href="https://gunicorn.org/" rel="noopener noreferrer" target="_blank">here</a>.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim gunicorn_config.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to gunicorn_config.py<br />
*Note we set workers to 2 because if you only have one worker, and it’s handling a slow query, the heartbeat query will timeout which could remove it from a load balancer. Also container schedulers expect logs to come out on stdout and stderr so we have it set in the config as such. We also leverage /dev/shm vs default /tmp to avoid timeouts accessing ram vs disk.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
pidfile = 'app01.pid'
worker_tmp_dir = '/dev/shm'
worker_class = 'gthread'
workers = 2
worker_connections = 1000
timeout = 30
keepalive = 2
threads = 4
proc_name = 'app01'
bind = '0.0.0.0:8080'
backlog = 2048
accesslog = '-'
errorlog = '-'
user = 'ubuntu'
group = 'ubuntu'
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 5</strong><br />
Create a file Dockerfile which contains the commands to build your Docker image. For more information on Dockerfile read <a href="https://docs.docker.com/engine/reference/builder/" rel="noopener noreferrer" target="_blank">here</a>.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim Dockerfile
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to Dockerfile<br />
*Note we pull a base ubuntu 18.04 image with python 3.7.3 from my Docker Hub repo you could point this to the official Ubuntu repo and add a Python image or install line. All the files are copied into the /home/ubuntu container directory and referenced from there.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
FROM nethacker/ubuntu-18-04-python-3:python-3.7.3
COPY requirements.txt /root/
RUN pip install -r /root/requirements.txt && useradd -m ubuntu
ENV HOME=/home/ubuntu
USER ubuntu
COPY app01.py wsgi.py gunicorn_config.py /home/ubuntu/
WORKDIR /home/ubuntu/
EXPOSE 8080
CMD ["gunicorn", "-c", "gunicorn_config.py", "wsgi:hello"]
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 6</strong><br />
Build your Docker image<br />
*Note you can -t tag this with your own naming convention<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
docker build -t example/ubuntu-flask-gunicorn-docker:latest .
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 7</strong><br />
Start your Docker container from the Docker image you built. Docker container environments are different then VM&#8217;s because of this we set &#8211;shm-size to a bigger shared memory size. Gunicorn has a config entry to use shared memory (/dev/shm) vs disk (/tmp) for Gunicorn health checks to avoid timeouts accessing ram vs disk. The setting 80:8080 sets the Docker container to map localhost 80 to container port 8080.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
docker run --shm-size=256m --detach -p 80:8080 example/ubuntu-flask-gunicorn-docker:latest
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 8</strong><br />
Test your Docker container</p>
<p>If you didn&#8217;t modify the Dockerfile you should be able to go to localhost port 80 in a browser and get a red &#8220;Hello World&#8221; message.</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
http://localhost
</pre>
<p></code></pre>
			</div>
        </div>
<p>You can also do the following to access the container with a bash shell to poke around.</p>
<p>Find the running Docker container id.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
docker ps
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Get and interactive shell on the container.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
docker exec -it {id here} /bin/bash
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Hopefully this basic overview helps you build your own containers!</p><p>The post <a href="https://philchen.com/2019/07/09/a-scalable-flask-application-using-gunicorn-on-ubuntu-18-04-in-docker/">A scalable Flask application using Gunicorn on Ubuntu 18.04 in Docker</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to make a Python Web App in Virtualenv using Flask, Gunicorn, NGINX on Ubuntu 18.04</title>
		<link>https://philchen.com/2019/02/11/how-to-make-a-python-web-app-in-virtualenv-using-flask-gunicorn-nginx-on-ubuntu-18-04/</link>
		
		<dc:creator><![CDATA[Phil Chen]]></dc:creator>
		<pubDate>Tue, 12 Feb 2019 03:50:56 +0000</pubDate>
				<category><![CDATA[Systems]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[flask]]></category>
		<category><![CDATA[gunicorn]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[repo]]></category>
		<category><![CDATA[scalable]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[systemd]]></category>
		<category><![CDATA[ubuntu]]></category>
		<category><![CDATA[virtualenv]]></category>
		<category><![CDATA[web app]]></category>
		<guid isPermaLink="false">https://philchen.com/?p=2002</guid>

					<description><![CDATA[<p>This article will cover creating a scalable Python Flask web application on Ubuntu 18.04 using Gunicorn and NGINX. The web application also leverages Virtualenv to provide an isolated Python environment and Systemd as a service manager. We loosely are defining web application to mean serving up a web page via a GET request, however this [&#8230;]</p>
<p>The post <a href="https://philchen.com/2019/02/11/how-to-make-a-python-web-app-in-virtualenv-using-flask-gunicorn-nginx-on-ubuntu-18-04/">How to make a Python Web App in Virtualenv using Flask, Gunicorn, NGINX on Ubuntu 18.04</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>This article will cover creating a scalable <a href="https://www.python.org/" rel="noopener" target="_blank">Python</a> <a href="http://flask.pocoo.org/" rel="noopener" target="_blank">Flask</a> web application on <a href="https://www.ubuntu.com/" rel="noopener" target="_blank">Ubuntu 18.04</a> using <a href="https://gunicorn.org/" rel="noopener" target="_blank">Gunicorn</a> and <a href="http://nginx.org/" rel="noopener" target="_blank">NGINX</a>. The web application also leverages <a href="https://virtualenv.pypa.io" rel="noopener" target="_blank">Virtualenv</a> to provide an isolated Python environment and <a href="https://www.freedesktop.org/wiki/Software/systemd/" rel="noopener" target="_blank">Systemd</a> as a service manager. We loosely are defining web application to mean serving up a web page via a GET request, however this blog can be used as an example to build a website, Restful API, or any number of things. </p>
<p>*This is an updated approach to an older blog post of mine <a href="https://philchen.com/2015/08/08/how-to-make-a-scalable-python-web-app-using-flask-and-gunicorn-nginx-on-ubuntu-14-04" rel="noopener" target="_blank">How to make a Scalable Python Web App using Flask, Gunicorn, NGINX on Ubuntu 14.04</a> from 2015.</p>
<p><strong>I have created a GitHub repo <a href="https://github.com/nethacker/scalable-ubuntu-flask-gunicorn-nginx">scalable-ubuntu-flask-gunicorn-nginx</a> available for you to get started quickly with a setup.sh file that does all the steps below via one bash script.</strong></p>
<p>If you would like to do this step by step please continue reading below:</p>
<p><strong>Assumptions:</strong></p>
<li>You have a Ubuntu 18.04 Server or Cloud Instance</li>
<li>You are using ubuntu as your user and have sudo privileges </li>
<p><strong>Step 0</strong><br />
Update your Ubuntu packages<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt update
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 1</strong><br />
Time Matters! Sync&#8217;ing time is important all servers drift which can effect applications negatively.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt -y install ntpdate

sudo ntpdate pool.ntp.org

sudo apt -y install ntp
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 2</strong><br />
Install Python Dependencies and Virtualenv, you will be installing Python 3 and leveraging Virtualenv to have an isolated environment for your Python dependencies specific to your application.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt -y install build-essential openssl

sudo apt -y install libpq-dev libssl-dev libffi-dev zlib1g-dev

sudo apt -y install python3-pip python3-dev

sudo pip3 install virtualenvwrapper
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 3</strong><br />
Install NGINX, it will act as a reverse proxy for scaling purposes.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt -y install nginx
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 4</strong><br />
Configure your .bashrc file so that your ubuntu user can use Virtualenv<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim ~/.bashrc
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Add the following to the bottom of your .bashrc file<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
# default location of virtual environment directories
export WORKON_HOME=$HOME/.virtualenvs
# default python version to use with virtualenv
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
export VIRTUALENVWRAPPER_VIRTUALENV_ARGS=' -p /usr/bin/python3 '
source `which virtualenvwrapper.sh`
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 5</strong><br />
Source your .bashrc file to activate your changes<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
source ~/.bashrc
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 6</strong><br />
Create a directory to keep applications in<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
mkdir /home/ubuntu/flask_apps

cd /home/ubuntu/flask_apps
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 7</strong><br />
Create your Virtualenv project environment named app01_env<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
virtualenv app01_env
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 8</strong><br />
Activate your Virtualenv and install Python packages for your application specific to the new project environment. The three packages listed are gunicorn (webserver), flask (Python microframework), and setproctitle (to view your processes friendly name when using ps). After use deactivate to remove yourself from this environment.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
source app01_env/bin/activate

pip install gunicorn

pip install flask

pip install setproctitle

deactivate
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 9</strong><br />
Create your Python Flask application that says Hello World<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim /home/ubuntu/flask_apps/app01_env/app01.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>app01.py<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
from flask import Flask
hello = Flask(__name__)

@hello.route("/")
def greeting():
    return "<h1 style='color:red'>Hello World!</h1>"

if __name__ == "__main__":
    hello.run(host='0.0.0.0')
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 10</strong><br />
Create your webserver gateway interface for the Flask microframework<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim /home/ubuntu/flask_apps/app01_env/wsgi.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>wsgi.py<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>
from app01 import hello

if __name__ == "__main__":
    hello.run()
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 11</strong><br />
Create your Gunicorn config file which holds your Gunicorn settings. Gunicorn is essentially your webserver for your application. The below settings are optimized for a dual core server.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vim /home/ubuntu/flask_apps/app01_env/gunicorn_config.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>gunicorn.py<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
pidfile = 'app01.pid'
worker_class = 'gthread'
workers = 5
worker_connections = 1000
timeout = 30
keepalive = 2
threads = 2
proc_name = 'app01'
bind = '0.0.0.0:8080'
backlog = 2048
accesslog = 'access.log'
errorlog = 'error.log'
user = 'ubuntu'
group = 'ubuntu'
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 12</strong><br />
Create a Systemd service file. Systemd is a service manager and in this use case it will manage Gunicorn.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/systemd/system/app01.service 
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>app01.service<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
[Unit]
Description=Gunicorn for Flask app01
After=network.target

[Service]
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu/flask_apps/app01_env
Environment="PATH=/home/ubuntu/flask_apps/app01_env/bin"
ExecStart=/home/ubuntu/flask_apps/app01_env/bin/gunicorn --config=gunicorn_config.py wsgi:hello

[Install]
WantedBy=multi-user.target

</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 13</strong><br />
Start your application via Systemd and enable it to auto start after reboot<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo systemctl start app01

sudo systemctl enable app01.service

</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 14</strong><br />
Create your applications NGINX config that makes NGINX listen on port 80 and proxies requests to Gunicorn on port 8080. NGINX will help with connection handling increasing performance.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/nginx/sites-available/nginx_app01.conf

</pre>
<p></code></pre>
			</div>
        </div></p>
<p>nginx_app01.conf<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
server {
    listen 80 default_server;

    location / {
        include proxy_params;
      	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP	$remote_addr;
      	proxy_set_header Host $http_host;
      	proxy_redirect off;
        proxy_buffers 8 24k;
        proxy_buffer_size 4k;
        proxy_pass http://127.0.0.1:8080;
    }
}

</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 15</strong><br />
Delete your default NGINX conf file, enable your new NGINX conf and restart NGINX<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo rm /etc/nginx/sites-enabled/default

sudo ln -s /etc/nginx/sites-available/nginx_app01.conf /etc/nginx/sites-enabled

sudo systemctl restart nginx

</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 16</strong><br />
If the server or instance you installed this on has port 80 available for you to reach. Enter the server IP or hostname into your browser and you should see &#8220;Hello World!&#8221; in red, which tells you your Python Flask application is working.</p>
<p><strong>Performance Settings</strong></p>
<li>Gunicorn is configured with 5 workers leveraging threading (2 per worker) each worker can handle 1000 connections. Typical formula is 2 workers per cpu core + 1 worker</li>
<li>Gunicorn Timeout value is set to 30 seconds and keepalive is set to 2 seconds</li>
<li>NGINX is set to use 8 buffers and 24K size per buffer when reading a response from the proxied server for a single connection</li>
<li>NGINX is set to 4k buffer size used for headers from the pool</li>
<li>NGINX worker_processes are set to &#8220;auto&#8221; for detecting number of cores and setting that value</li><p>The post <a href="https://philchen.com/2019/02/11/how-to-make-a-python-web-app-in-virtualenv-using-flask-gunicorn-nginx-on-ubuntu-18-04/">How to make a Python Web App in Virtualenv using Flask, Gunicorn, NGINX on Ubuntu 18.04</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Working with IP address information using Ruby</title>
		<link>https://philchen.com/2016/06/02/working-with-ip-address-information-using-ruby/</link>
		
		<dc:creator><![CDATA[Phil Chen]]></dc:creator>
		<pubDate>Thu, 02 Jun 2016 19:16:08 +0000</pubDate>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[ipaddr]]></category>
		<category><![CDATA[Ruby]]></category>
		<guid isPermaLink="false">http://3.84.23.23/?p=1924</guid>

					<description><![CDATA[<p>I recently needed to create an IP address range in array format, and came across a nice Ruby Class &#8220;ipaddr&#8221; for working with IP address information. My goal was to white list an IP by cross referencing it with the IP address range. Here is a standalone example of converting the IP Range into an [&#8230;]</p>
<p>The post <a href="https://philchen.com/2016/06/02/working-with-ip-address-information-using-ruby/">Working with IP address information using Ruby</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>I recently needed to create an IP address range in array format, and came across a nice Ruby Class &#8220;ipaddr&#8221; for working with IP address information. My goal was to white list an IP by cross referencing it with the IP address range.</p>
<p>Here is a standalone example of converting the IP Range into an array.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-ruby"></p>
<pre>
#!/usr/bin/env ruby

require 'ipaddr'

def ip_range(first, last)
  first = IPAddr.new(first)
  last  = IPAddr.new(last)
  (first..last).map(&amp;:to_s)
end

range_calc = ip_range("172.30.20.0", "172.30.20.255")

puts range_calc.to_a
</pre>
<p></code></pre>
			</div>
        </div><br />
Here is an example in Sinatra of using this method to check against<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-ruby"></p>
<pre>
require 'ipaddr'

get '/blah/blah/blah' do

def ip_range(first, last)
  first = IPAddr.new(first)
  last  = IPAddr.new(last)
  (first..last).map(&amp;:to_s)
end

range_calc = ip_range("172.30.20.0", "172.30.20.255")

$WHITE_LIST_HOSTS = ["127.0.0.1","173.255.251.204","54.223.213.220"] + range_calc.to_a

request_ip = @env['HTTP_X_REAL_IP']
  if
    !$WHITE_LIST_HOSTS.include? request_ip

  return "Not Authorized"

  else
    Do work here...
  end
end
</pre>
<p></code></pre>
			</div>
        </div><br />
Here is another standalone example variation using a method of ip-addr, if the requesting ip is in the range it will return true.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-ruby"></p>
<pre>
#!/usr/bin/env ruby

require 'ipaddr'

first = IPAddr.new("172.30.20.0/24")

requesting_ip = IPAddr.new("172.30.20.200")

puts first.include?(requesting_ip)
</pre>
<p></code></pre>
			</div>
        </div><br />
Hope this is helpful. The Class &#8216;<a href="http://ruby-doc.org/stdlib-2.0.0/libdoc/ipaddr/rdoc/IPAddr.html" target="_blank" rel="noopener">ipaddr</a>&#8216; has other useful methods you should check out.</p><p>The post <a href="https://philchen.com/2016/06/02/working-with-ip-address-information-using-ruby/">Working with IP address information using Ruby</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Create a Private Docker Registry Using AWS and Docker</title>
		<link>https://philchen.com/2015/11/16/how-to-create-a-private-docker-registry-using-aws-and-docker/</link>
		
		<dc:creator><![CDATA[Phil Chen]]></dc:creator>
		<pubDate>Mon, 16 Nov 2015 19:00:49 +0000</pubDate>
				<category><![CDATA[Systems]]></category>
		<category><![CDATA[docker]]></category>
		<guid isPermaLink="false">http://3.84.23.23/?p=1888</guid>

					<description><![CDATA[<p>This article will cover creating a Docker Registry leveraging Amazon Web Services (AWS) and a Docker registry container itself. This registry will be built on AWS EC2 instance running Ubuntu 14.04 and leveraging an EBS Volume so that you can take backup snapshots of your registry. This setup will be secured by leveraging VPC and [&#8230;]</p>
<p>The post <a href="https://philchen.com/2015/11/16/how-to-create-a-private-docker-registry-using-aws-and-docker/">How to Create a Private Docker Registry Using AWS and Docker</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>This article will cover creating a Docker Registry leveraging Amazon Web Services (AWS) and a Docker registry container itself. This registry will be built on AWS EC2 instance running Ubuntu 14.04 and leveraging an EBS Volume so that you can take backup snapshots of your registry. This setup will be secured by leveraging VPC and AWS Security Groups. (You should probably put authentication in even if its in your private network but I wont cover that.)</p>
<p><strong>Step 0</strong><br />
Create your AWS Environment</p>
<p>Create a AWS Security Group for your Docker Registry Instance allowing port 80. Launch an AWS EC2 Instance running Ubuntu 14.04 in the designated region and VPC of your choice through the AWS Console applying the Security Group you created. Create and size an AWS EBS Volume to your needs and attach it to the instance you started via the AWS Console.</p>
<p><strong>Step 1</strong><br />
Time Matters!</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
ubuntu$ sudo apt-get update

ubuntu$ sudo ntpdate pool.ntp.org
 
ubuntu$ sudo apt-get install ntp
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 2</strong><br />
Install Build Tools <em>*just because</em></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
ubuntu$ sudo apt-get install build-essential
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 3</strong><br />
Create a file system for the EBS Volume you created and mount it.</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
ubuntu$ sudo cat /proc/partitions

major minor  #blocks  name

   7        0  104857600 loop0
   7        1    2097152 loop1
 202        0  104857600 xvda
 202        1  104848222 xvda1
 252        0  104857600 dm-0
 202       80  104857600 xvdf
 252        1   10485760 dm-1

ubuntu$ sudo mkfs -t ext4 /dev/xvdf

ubuntu$ sudo mkdir /data

ubuntu$ sudo mount /dev/xvdf /data

ubuntu$ sudo vim /etc/fstab

*ADD BELOW
/dev/xvdf    /data   ext4    defaults    1 1

</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 4</strong><br />
Install Docker</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
ubuntu$ sudo apt-get update

ubuntu$ sudo apt-get -y install docker.io

ubuntu$ sudo ln -sf /usr/bin/docker.io /usr/local/bin/docker
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 5</strong><br />
Install Docker Registry Container</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
ubuntu$ sudo docker run -d -p 80:5000 --restart=always -v /data:/var/lib/registry registry:2
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 6</strong><br />
Create a Security Group for your AWS ELB opening port 443 via the AWS Console</p>
<p><strong>Step 7</strong><br />
Create an Internal AWS ELB that has a Load Balancer Port 443 and Server Port 80 and apply the Security Group you created in the previous step, then put your AWS EC2 instance in it. Create friendly DNS CNAME to that load balancer. This is needed since Docker Registry needs to leverage SSL.</p>
<p><strong>Step 8</strong><br />
From a SEPARATE Development Docker Server Test Your New Registry</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
ubuntu$ sudo docker pull ubuntu

ubuntu$ sudo docker tag ubuntu the-dns-name-to-elb/mytestimage:1

ubuntu$ sudo docker push the-dns-name-to-your-elb/mytestimage

ubuntu$ sudo docker pull the-dns-name-to-your-elb/mytestimage
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>*NOTES</strong></p>
<ul>
<li>When creating the ELB you will want to apply your SSL Certificate for port 443</li>
<li>This setup is only securing things via the network layer in theory you should apply authentication</li>
<li>Snapshot your EBS Volume you created at whatever interval you like</li>
<ul><p>The post <a href="https://philchen.com/2015/11/16/how-to-create-a-private-docker-registry-using-aws-and-docker/">How to Create a Private Docker Registry Using AWS and Docker</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Performance Tune Ubuntu 14.04 LTS Trusty in AWS EC2</title>
		<link>https://philchen.com/2015/10/28/how-to-performance-tune-ubuntu-14-04-lts-trusty-in-aws-ec2/</link>
		
		<dc:creator><![CDATA[Phil Chen]]></dc:creator>
		<pubDate>Wed, 28 Oct 2015 21:53:45 +0000</pubDate>
				<category><![CDATA[Scale]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[ubuntu]]></category>
		<guid isPermaLink="false">http://3.84.23.23/?p=1868</guid>

					<description><![CDATA[<p>This article will explain how to performance tune Ubuntu 14.04 LTS Trusty in Amazon Web Services EC2. Building a good base AWS AMI is important and if your using Ubuntu 14.04 this will hopefully be of some help. Step 0 Time Matters! Make sure you have NTP installed otherwise do the following: Step 1 Increase [&#8230;]</p>
<p>The post <a href="https://philchen.com/2015/10/28/how-to-performance-tune-ubuntu-14-04-lts-trusty-in-aws-ec2/">How to Performance Tune Ubuntu 14.04 LTS Trusty in AWS EC2</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>This article will explain how to performance tune Ubuntu 14.04 LTS Trusty in Amazon Web Services EC2. Building a good base AWS AMI is important and if your using Ubuntu 14.04 this will hopefully be of some help.</p>
<p><strong>Step 0</strong><br />
Time Matters! Make sure you have NTP installed otherwise do the following:<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt-get update

sudo ntpdate pool.ntp.org
 
sudo apt-get install ntp
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 1</strong><br />
Increase the default file descriptor limit of 1024. TCP/IP sockets are considered open files so increasing this will help you handle more connections.</p>
<p>Append the below to your limits.conf file<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/security/limits.conf
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
root		soft	nofile		65535
root		hard	nofile		65535
*		soft	nofile		65535
*		hard	nofile		65535
</pre>
<p></code></pre>
			</div>
        </div>
<p>Append the below to your sshd_config file *Note this might already exist<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/ssh/sshd_config
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
UsePAM yes
</pre>
<p></code></pre>
			</div>
        </div>
<p>Append the below to your PAM sshd file *Note this might already exist<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/pam.d/sshd
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
session required pam_limits.so
</pre>
<p></code></pre>
			</div>
        </div>
<p>Append the below to your PAM common-session file<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/pam.d/common-session
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
session required pam_limits.so
</pre>
<p></code></pre>
			</div>
        </div>
<p>Append the below to your sysctl.conf file<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/sysctl.conf
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
fs.file-max = 762427
</pre>
<p></code></pre>
			</div>
        </div>
<p>Run<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo sysctl -p
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 2</strong><br />
Save your SSD drives and leverage RAM by avoiding the use of swap. With this setting the kernel will swap only to avoid an out of memory condition.</p>
<p>Append the below to your sysctl.conf file<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/sysctl.conf
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
vm.swappiness = 0
</pre>
<p></code></pre>
			</div>
        </div>
<p>Run<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo sysctl -p
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 3</strong><br />
Configure Kernel Network Performance Settings</p>
<p>Append the below to your sysctl.conf file<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/sysctl.conf
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
# Increase the number of connections
net.core.somaxconn = 1000

# Increase number of incoming connections backlog
net.core.netdev_max_backlog = 5000

# Maximum Socket Receive Buffer
net.core.rmem_max = 16777216

# Default Socket Send Buffer
net.core.wmem_max = 16777216

# Increase the maximum total buffer-space allocatable
net.ipv4.tcp_wmem = 4096 12582912 16777216
net.ipv4.tcp_rmem = 4096 12582912 16777216

# Increase the number of outstanding syn requests allowed
net.ipv4.tcp_max_syn_backlog = 8096

# For persistent HTTP connections
net.ipv4.tcp_slow_start_after_idle = 0

# Increase the tcp-time-wait buckets pool size to prevent simple DOS attacks
net.ipv4.tcp_tw_reuse = 1

# Allowed local port range
net.ipv4.ip_local_port_range = 10240 65535
</pre>
<p></code></pre>
			</div>
        </div>
<p>Run<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo sysctl -p
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 4</strong><br />
Disable file access time logging. Setting the noatime effects removing a write for every read. Typically when a file is read the system updates the inode for the file with an access time so that the last access time is recorded, which basically entails a write to the file system. Unless you are running some sort of mirror you probably do not need the access time written.</p>
<p>Add the noatime attribute to your mount in fstab<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/fstab
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
LABEL=cloudimg-rootfs	/	 ext4	defaults,noatime,discard	0 0
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 5</strong><br />
Increase history and make your command prompt more informative, nothing more sad then typing history and not seeing some old commands you forgot to take not of. Also these changes will help you know where your at from a path standpoint.</p>
<p>Append the below to your profile file<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/profile
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell">
<pre>
# HISTORY SETTINGS
export HISTTIMEFORMAT='%F %R '
export HISTSIZE=2000
export HISTFILESIZE=2000
export HISTCONTROL=ignoredups

# Command Prompt Settings

export PS1='\[\033[1;34m\][\u@\h:\w]\$\[\033[0m\]'
</pre>
<p></code></pre>
			</div>
        </div><br />
*You will have to log out and back in for these changes to take effect.</p>
<p>After your done make a new AMI image and you should have a decently strong foundation for your application specific AMI&#8217;s. If your not making an image you may want to reboot the instance to ensure your changes took, specifically in the case of the fstab noatime.</p><p>The post <a href="https://philchen.com/2015/10/28/how-to-performance-tune-ubuntu-14-04-lts-trusty-in-aws-ec2/">How to Performance Tune Ubuntu 14.04 LTS Trusty in AWS EC2</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to configure a Postfix, Stunnel, and Amazon Simple Email Service (SES) on Ubuntu 14.04</title>
		<link>https://philchen.com/2015/09/28/how-to-configure-a-postfix-stunnel-and-amazon-simple-email-service-ses-on-ubuntu-14-04/</link>
		
		<dc:creator><![CDATA[Phil Chen]]></dc:creator>
		<pubDate>Mon, 28 Sep 2015 17:35:30 +0000</pubDate>
				<category><![CDATA[Systems]]></category>
		<category><![CDATA[postfix]]></category>
		<category><![CDATA[ses]]></category>
		<category><![CDATA[stunnel]]></category>
		<guid isPermaLink="false">http://3.84.23.23/?p=1814</guid>

					<description><![CDATA[<p>This article will cover leveraging Amazon Simple Email Service (SES) in concert with Postfix and Stunnel as a solution for sending email at scale. Postfix is a open source mail transfer agent (MTA) that is widely used. Stunnel is an open-source multi-platform computer program, used to provide universal TLS/SSL tunneling service. Amazon SES is a [&#8230;]</p>
<p>The post <a href="https://philchen.com/2015/09/28/how-to-configure-a-postfix-stunnel-and-amazon-simple-email-service-ses-on-ubuntu-14-04/">How to configure a Postfix, Stunnel, and Amazon Simple Email Service (SES) on Ubuntu 14.04</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>This article will cover leveraging Amazon Simple Email Service (SES) in concert with Postfix and Stunnel as a solution for sending email at scale. </p>
<p>Postfix is a open source mail transfer agent (MTA) that is widely used. Stunnel is an open-source multi-platform computer program, used to provide universal TLS/SSL tunneling service. Amazon SES is a cost-effective outbound-only email-sending service built on the reliable and scalable infrastructure that Amazon.com has developed to serve its own customer base.</p>
<p>Combining these solutions together creates a flexible and powerful outbound email solution.</p>
<p><strong>Step 0</strong><br />
Time Matters! Make sure you have NTP installed otherwise do the following:<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt-get update

sudo ntpdate pool.ntp.org
 
sudo apt-get install ntp
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 1</strong><br />
Setup AWS SES by doing these steps:</p>
<ul>
<li><a href="http://aws.amazon.com/ses" target="_blank" rel="noopener">Sign up for SES</a></li>
<li><a href="https://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-domain-procedure.html" target="_blank" rel="noopener">Verify your Domain with SES</a></li>
<li><a href="https://docs.aws.amazon.com/ses/latest/DeveloperGuide/smtp-credentials.html" target="_blank" rel="noopener">Obtaining Your Amazon SES SMTP Credentials</a></li>
</ul>
<p><strong>Step 2</strong><br />
Install Stunnel<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt-get update

sudo apt-get install stunnel
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Configure stunnel.conf (In this case we are using us-west-2 (Oregon)<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd /etc/stunnel/

sudo vim stunnel.conf
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
[smtp-tls-wrapper]
accept = 127.0.0.1:1125
client = yes
connect = email-smtp.us-west-2.amazonaws.com:465
</pre>
<p></code></pre>
			</div>
        </div>
<p>Enable Stunnel<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd /etc/default

sudo vim stunnel4
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
# /etc/default/stunnel
# Julien LEMOINE <speedblue@debian.org>
# September 2003

# Change to one to enable stunnel automatic startup
ENABLED=1
FILES="/etc/stunnel/*.conf"
OPTIONS=""

# Change to one to enable ppp restart scripts
PPP_RESTART=0
</pre>
<p></code></pre>
			</div>
        </div>
<p>Start Stunnel<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo service stunnel4 restart
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 3</strong><br />
Install Postfix if it isn&#8217;t already<br />
*NOTE Select “Internet Site” and Enter “yourdomain.com” when prompted.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt-get update

sudo apt-get install postfix

cd /etc/postfix
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Configure sender_dependent_relayhost with the sender email address<br />
*Make sure your email addressed being relayed matches what application is sending it.<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim sender_dependent_relayhost
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
user@yourdomain.com	127.0.0.1:1125
</pre>
<p></code></pre>
			</div>
        </div>
<p>Make your sender_dependent_relayhost.db file<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo postmap /etc/postfix/sender_dependent_relayhost
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Configure /etc/postfix/password with your SES SMTP credentials<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/postfix/password
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
127.0.0.1:1125 [SMTP Username]:[SMTP Password]
</pre>
<p></code></pre>
			</div>
        </div>
<p>Set Permissions of /etc/postfix/password<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo chown root:root /etc/postfix/password

sudo chmod 600 /etc/postfix/password
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Make your password.db file<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo postmap /etc/postfix/password
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo vim /etc/postfix/main.cf
</pre>
<p></code></pre>
			</div>
        </div>
<p>Configure with these lines /etc/postfix/main.cf:<br />
*You can clear out the default configurations in this file and add the below<br />
*Make sure you put your domain in the myhostname field<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
myhostname = yourhostnamehere
sender_dependent_relayhost_maps = hash:/etc/postfix/sender_dependent_relayhost
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/password
smtp_sasl_security_options =
mydestination = localhost 
inet_protocols = ipv4
inet_interfaces = all
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Reload PostFix:<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo postfix reload
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>Test your new outbound mail system:<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt-get install mailutils

echo "TEST" | mail -s subject whateveremail@gmail.com
</pre>
<p></code></pre>
			</div>
        </div><br />
The email should be received to whateveremail@gmail.com from youremail@yourdomain.com via amazonses.com</p>
<p>Things to note when configuring this outbound email system.</p>
<ul>
<li>Make sure the relay address matches the email your app is sending from</li>
<li>Make sure you don&#8217;t have any firewall rules blocking port 1125</li>
<li>Make sure you use the correct SMTP credentials from AWS SES</li>
</ul>
<p>Happy email sending!</p><p>The post <a href="https://philchen.com/2015/09/28/how-to-configure-a-postfix-stunnel-and-amazon-simple-email-service-ses-on-ubuntu-14-04/">How to configure a Postfix, Stunnel, and Amazon Simple Email Service (SES) on Ubuntu 14.04</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to make a Scalable Python Web App using Flask, Gunicorn, NGINX on Ubuntu 14.04</title>
		<link>https://philchen.com/2015/08/08/how-to-make-a-scalable-python-web-app-using-flask-and-gunicorn-nginx-on-ubuntu-14-04/</link>
		
		<dc:creator><![CDATA[Phil Chen]]></dc:creator>
		<pubDate>Sat, 08 Aug 2015 23:30:48 +0000</pubDate>
				<category><![CDATA[Systems]]></category>
		<category><![CDATA[flask]]></category>
		<category><![CDATA[gunicorn]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[systems]]></category>
		<category><![CDATA[upstart]]></category>
		<guid isPermaLink="false">http://3.84.23.23/?p=1778</guid>

					<description><![CDATA[<p>This article will cover creating a Scalable Python Web Application using Flask (microframework for Python) Gunicorn (Python WSGI HTTP Server for UNIX) and NGINX on Ubuntu 14.04. There are other more full featured Python web frameworks such as Django but if you need a light weight web app or API server this stack can be [&#8230;]</p>
<p>The post <a href="https://philchen.com/2015/08/08/how-to-make-a-scalable-python-web-app-using-flask-and-gunicorn-nginx-on-ubuntu-14-04/">How to make a Scalable Python Web App using Flask, Gunicorn, NGINX on Ubuntu 14.04</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>This article will cover creating a Scalable Python Web Application using <a href="http://flask.pocoo.org/" target="_blank" rel="noopener">Flask</a> (microframework for Python) <a href="http://gunicorn.org/" target="_blank" rel="noopener">Gunicorn</a> (Python WSGI HTTP Server for UNIX) and <a href="http://nginx.org/" target="_blank" rel="noopener">NGINX</a> on <a href="http://releases.ubuntu.com/14.04/" target="_blank" rel="noopener">Ubuntu 14.04</a>. There are other more full featured Python web frameworks such as <a href="https://www.djangoproject.com/" target="_blank" rel="noopener">Django</a> but if you need a light weight web app or API server this stack can be a nice solution.</p>
<p><strong>ATTENTION! THERE IS AN UPDATED VERSION OF THIS BLOG AT THE LINK BELOW:</strong></p>
<p><a href="https://philchen.com/2019/02/11/how-to-make-a-python-web-app-in-virtualenv-using-flask-gunicorn-nginx-on-ubuntu-18-04">How to make a Python Web App in Virtualenv using Flask, Gunicorn, NGINX on Ubuntu 18.04</a></p>
<p><strong>Step 0</strong><br />
Time Matters!<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt-get update

sudo ntpdate pool.ntp.org
 
sudo apt-get install ntp
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 1</strong><br />
Install PIP<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt-get install python-pip
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 2</strong><br />
Install Python Development<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt-get install python-dev
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 3</strong><br />
Install Flask<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo pip install Flask
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 4</strong><br />
Install Gunicorn<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo pip install gunicorn
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 5</strong><br />
Create a Python Flask Application<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd ~

mkdir /home/ubuntu/FLASK

cd /home/ubuntu/FLASK

vim hello.py
</pre>
<p></code></pre>
			</div>
        </div></p>
<p>hello.py<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-python"></p>
<pre>

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 6</strong><br />
Create a pre-fork Gunicorn startup script to run your Flask Application</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd /home/ubuntu/FLASK

vim gunicorn.sh
</pre>
<p></code></pre>
			</div>
        </div>
<p>gunicorn.sh<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
#!/bin/bash

NAME="hello"
FLASKDIR=/home/ubuntu/FLASK
SOCKFILE=/home/ubuntu/FLASK/sock
USER=root
GROUP=root
NUM_WORKERS=3

echo "Starting $NAME"

# Create the run directory if it doesn't exist
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR

# Start your gunicorn
exec gunicorn hello:app -b 0.0.0.0:8080 \
  --name $NAME \
  --workers $NUM_WORKERS \
  --user=$USER --group=$GROUP \
  --bind=unix:$SOCKFILE
</pre>
<p></code></pre>
			</div>
        </div></p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
chmod 755 /home/ubuntu/FLASK/gunicorn.sh
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 7</strong><br />
Create a Gunicorn upstart script</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
cd /etc/init/

sudo vim gunicorn.conf
</pre>
<p></code></pre>
			</div>
        </div>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>

#Upstart Script

description "gunicorn"

respawn
respawn limit 15 5

start on runlevel [2345]
stop on runlevel [06]

env SCRIPTS_BIN=/home/ubuntu/FLASK

script
chdir $SCRIPTS_BIN
exec ./gunicorn.sh
end script
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>Step 8</strong><br />
Install NGINX</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo apt-get install nginx

cd /etc/nginx/conf.d/

mkdir /home/ubuntu/www

sudo vim flask.conf
</pre>
<p></code></pre>
			</div>
        </div>
<p>flask.conf *Make sure you change the server_name<br />
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
upstream gunicorn_server {

  server localhost:8080 fail_timeout=0;
}

    server {
        listen       80;
        server_name  your-web-app-hostname-here.com;

	root /home/ubuntu/www;
	client_max_body_size 4G;
	keepalive_timeout 5;
        proxy_read_timeout 900;

        location / {
	try_files $uri @app;
        }

	location @app {
      	 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Real-IP	$remote_addr;
      	 proxy_set_header Host $http_host;
      	 proxy_redirect off;
      	# pass to the upstream gunicorn server mentioned above 
	 proxy_pass http://gunicorn_server;
	}

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
   }
</pre>
<p></code></pre>
			</div>
        </div></p>
<p><strong>Step 9</strong><br />
Start Gunicorn and NGINX</p>
<div class="dm-code-snippet light no-background no-background-mobile dm-normal-version" style="background-color:#abb8c3;" snippet-height="">
			<div class="control-language">
                <div class="dm-buttons">
                    <div class="dm-buttons-left">
                        <div class="dm-button-snippet red-button"></div>
                        <div class="dm-button-snippet orange-button"></div>
                        <div class="dm-button-snippet green-button"></div>
                    </div>
                    <div class="dm-buttons-right">
                        <a id="dm-copy-raw-code">
                        <span class="dm-copy-text">Copy Code</span>
                        <span class="dm-copy-confirmed" style="display:none">Copied!</span>
                        <span class="dm-error-message" style="display:none">Use a different Browser</span></a>
                    </div>
                </div>
                <pre class="line-numbers"><code id="dm-code-raw" class="no-wrap language-shell"></p>
<pre>
sudo service gunicorn start

sudo service nginx restart
</pre>
<p></code></pre>
			</div>
        </div>
<p><strong>More Information:</strong></p>
<li>The formula for how many Gunicorn workers to run should be (2 x $num_cores) + 1</li>
<li>There are different types of Gunicorn <a href="http://gunicorn-docs.readthedocs.org/en/latest/design.html#server-model" target="_blank" rel="noopener">workers</a></li>
<li>You can configure <a href="http://gunicorn-docs.readthedocs.org/en/latest/design.html#how-many-threads" target="_blank" rel="noopener">threads</a> in Gunicorn</li>
<li>If it applies you can configure <a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html" target="_blank" rel="noopener">NGINX Caching</a> to help with performance</li><p>The post <a href="https://philchen.com/2015/08/08/how-to-make-a-scalable-python-web-app-using-flask-and-gunicorn-nginx-on-ubuntu-14-04/">How to make a Scalable Python Web App using Flask, Gunicorn, NGINX on Ubuntu 14.04</a> first appeared on <a href="https://philchen.com">Phil Chen</a>.</p>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
