Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(extension): Add option to enable async workers in Flask and Django #1986

Merged
merged 30 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
69c697e
Feat(extension): Added option to enable async workers in Flask and Dj…
alithethird Nov 12, 2024
3e26ed8
Merge branch 'main' into flask-async-worker
alithethird Nov 15, 2024
a4d82c0
Chore(doc): Add Spread test/tutorial doc
alithethird Dec 2, 2024
d9aa901
Merge branch 'flask-async-worker' of https://github.com/alithethird/c…
alithethird Dec 2, 2024
33c8796
Merge branch 'main' into flask-async-worker
alithethird Dec 2, 2024
7fa50ec
Chore(docs): Lint docs
alithethird Dec 2, 2024
9657bc3
Merge branch 'flask-async-worker' of https://github.com/alithethird/c…
alithethird Dec 2, 2024
364e3cc
Run CI
alithethird Dec 2, 2024
a5c872f
Chore(test): Fix spread test
alithethird Dec 3, 2024
5000827
Chore(test): Fix charmcraft version in spread test
alithethird Dec 3, 2024
5d7739e
Chore(docs): Changed tutorial to how-to. Updated spread test.
alithethird Dec 9, 2024
0a83b2a
Chore(): Fix spread test
alithethird Dec 9, 2024
d0176be
Merge branch 'main' into flask-async-worker
alithethird Dec 9, 2024
a69f8db
Chore(): Change microk8s version in spread test
alithethird Dec 9, 2024
b8e39a6
chore(doc): Applied comments
alithethird Dec 11, 2024
cee3cb4
Merge branch 'main' into flask-async-worker
alithethird Dec 12, 2024
812c906
Merge branch 'main' into flask-async-worker
alithethird Dec 17, 2024
e9e240f
Merge branch 'main' into flask-async-worker
alithethird Dec 18, 2024
5e7a762
Merge branch 'main' into flask-async-worker
alithethird Dec 19, 2024
40eaa15
chore(test): Update spread test to use rockcraft latest/edge
alithethird Dec 19, 2024
a78aa82
Merge branch 'flask-async-worker' of https://github.com/alithethird/c…
alithethird Dec 19, 2024
0852a04
chore(doc): Update howto
alithethird Dec 19, 2024
361d165
chore(doc): Update docs
alithethird Dec 20, 2024
12640ab
Merge branch 'main' into flask-async-worker
alithethird Dec 20, 2024
a7be9a8
Merge branch 'main' into flask-async-worker
alithethird Jan 6, 2025
b0899b2
Merge branch 'main' into flask-async-worker
alithethird Jan 7, 2025
8d2a695
chore(): Change option description.
alithethird Jan 7, 2025
7b06c99
Merge branch 'main' into flask-async-worker
alithethird Jan 8, 2025
101e37a
feat: Add async config to 12 Factor
alithethird Jan 10, 2025
1143426
Merge branch 'flask-async-worker' of https://github.com/alithethird/c…
alithethird Jan 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/howto/code/flask-async/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Flask
gevent
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# markers for including said instructions
# as snippets in the docs.
###########################################
summary: Getting started with Flask tutorial
summary: How to create async Flask Charm

kill-timeout: 90m

Expand All @@ -22,28 +22,11 @@ execute: |
unset CHARMCRAFT_REGISTRY_URL

# Add setup instructions
# (Ran into issues in prepare section)
# snap install rockcraft --channel=latest/edge --classic

# Install the latest rockcraft snap
# (This can be removed after the Rockcraft PR is merged)
# The PR: https://github.com/canonical/rockcraft/pull/747
snap install snapcraft --channel=latest/edge --classic
# Download rockcraft async-workers branch and alithethird fork
git clone -b flask-django-extention-async-workers https://github.com/alithethird/rockcraft
cd rockcraft
snapcraft pack
snap install --dangerous --classic rockcraft_*


snap install lxd
lxd init --auto
# snap refresh charmcraft --channel=latest/edge --amend
snap install rockcraft --channel=latest/edge --classic

snap install microk8s --channel=1.31-strict/stable
snap install juju --channel=3.5/stable
snap install juju --channel=3/stable

# Juju config setup
lxc network set lxdbr0 ipv6.address none
mkdir -p ~/.local/share

# MicroK8s config setup
Expand All @@ -61,7 +44,6 @@ execute: |
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install Flask
# [docs:create-venv-end]

flask run -p 8000 &
Expand All @@ -84,36 +66,6 @@ execute: |
sed -i "s/name: .*/name: flask-async-app/g" rockcraft.yaml
sed -i "s/amd64/$(dpkg --print-architecture)/g" rockcraft.yaml

# uncomment the parts main section
sed -i "s/# parts:/parts:/g" rockcraft.yaml
# uncomment the async-dependencies part
awk -i inplace -v block_key="flask-framework/async-dependencies" '
BEGIN {
in_block = 0;
comment_pattern = "^#[[:space:]]";
uncommented_line = "";
}

/^#[[:space:]]/ {
# Check if the line contains the block key
if (in_block == 0 && $0 ~ block_key) {
in_block = 1;
}
}

{
# If in_block is active, uncomment lines
if (in_block == 1) {
uncommented_line = gensub(comment_pattern, "", 1, $0);
if (uncommented_line == $0) {
in_block = 0;
}
print uncommented_line;
} else {
print $0;
}
}' rockcraft.yaml

# [docs:pack]
rockcraft pack
# [docs:pack-end]
Expand Down Expand Up @@ -160,7 +112,7 @@ execute: |
# [docs:deploy-juju-model-end]

# [docs:deploy-nginx]
juju deploy nginx-ingress-integrator --channel=latest/edge --revision 122
juju deploy nginx-ingress-integrator --channel=latest/edge --base [email protected]
juju integrate nginx-ingress-integrator flask-async-app
# [docs:deploy-nginx-end]

Expand All @@ -181,11 +133,13 @@ execute: |
juju config flask-async-app webserver-worker-class=gevent
# [docs:config-async-end]

juju wait-for application flask-async-app --query='status=="active"' --timeout 10m

# test the async flask service
NUM_REQUESTS=15
ASYNC_RESULT='TRUE'

echo "Firing $NUM_REQUESTS requests to $URL..."
echo "Firing $NUM_REQUESTS requests to http://flask-async-app/io..."

overall_start_time=$(date +%s)

Expand Down Expand Up @@ -221,8 +175,7 @@ execute: |
deactivate
rm -rf charm .venv __pycache__
# delete all the files created during the tutorial
rm flask-async-app_0.1_$(dpkg --print-architecture).rock flask-async-app_0.2_$(dpkg --print-architecture).rock \
flask-async-app_0.3_$(dpkg --print-architecture).rock rockcraft.yaml app.py \
rm flask-async-app_0.1_$(dpkg --print-architecture).rock rockcraft.yaml app.py \
requirements.txt migrate.py
# Remove the juju model
juju destroy-model flask-async-app --destroy-storage --no-prompt --force
Expand Down
53 changes: 53 additions & 0 deletions docs/howto/flask-async.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
======================================================
How to write a Kubernetes charm for an Async Flask app
alithethird marked this conversation as resolved.
Show resolved Hide resolved
======================================================

In this how-to guide we will configure a 12-factor Flask
alithethird marked this conversation as resolved.
Show resolved Hide resolved
application to use asynchronous Gunicorn workers to be
able to serve to multiple users easily.

Make the rock async
===================

Before packing the rock make sure to put the following in ``requirements.txt``
alithethird marked this conversation as resolved.
Show resolved Hide resolved
file:

.. literalinclude:: code/flask-async/requirements.txt

Configure the async application
alithethird marked this conversation as resolved.
Show resolved Hide resolved
===============================
alithethird marked this conversation as resolved.
Show resolved Hide resolved

Now let's enable async Gunicorn workers using a configuration option. We will
alithethird marked this conversation as resolved.
Show resolved Hide resolved
expect this configuration option to be available in the Flask app configuration
under the keyword ``webserver-worker-class``. Verify that the new configuration
alithethird marked this conversation as resolved.
Show resolved Hide resolved
has been added using
``juju config flask-async-app | grep -A 6 webserver-worker-class:`` which should
show the configuration option.
alithethird marked this conversation as resolved.
Show resolved Hide resolved

The worker class can be changed using Juju:

.. literalinclude:: code/flask-async/task.yaml
:language: bash
:start-after: [docs:config-async]
:end-before: [docs:config-async-end]
:dedent: 2

alithethird marked this conversation as resolved.
Show resolved Hide resolved
Now you can run
``curl --parallel --parallel-immediate --resolve flask-async-app:80:127.0.0.1 \
http://flask-async-app/io http://flask-async-app/io http://flask-async-app/io \
http://flask-async-app/io http://flask-async-app/io``
in they will all return at the same time.
alithethird marked this conversation as resolved.
Show resolved Hide resolved
alithethird marked this conversation as resolved.
Show resolved Hide resolved

Output will be similar to following:

.. code-block:: bash
alithethird marked this conversation as resolved.
Show resolved Hide resolved

ok
ok
ok
ok
ok

.. note::

It might take a short time for the configuration to take effect.
alithethird marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions docs/howto/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ How-To
charm-to-poetry
charm-to-python
shared-cache
flask-async
1 change: 0 additions & 1 deletion docs/tutorial/code/flask-async/requirements.txt

This file was deleted.

Loading
Loading