Skip to content

Feat: improve proof-of-concept app #116

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

Merged
merged 10 commits into from
Jun 18, 2025
Merged

Feat: improve proof-of-concept app #116

merged 10 commits into from
Jun 18, 2025

Conversation

lalver1
Copy link
Member

@lalver1 lalver1 commented May 30, 2025

Closes #114

Adds the stations Streamlit app to the repo (before it was only a local branch) and wires up Django and Streamlit in an environment-dependent way so that the proof-of-concept districts app can run both locally and in the Cloud.

Currently this branch is on top of ci/cloud-infra but it will be rebased on main once PR #111 is merged.

Notes

AWS Copilot services

Originally, the streamlit service was defined as a copilot Backend Service. For an initial proof-of-concept this is not a convenient service type since the service is unreachable from the internet (recall that the web service embeds streamlit in an iframe, so streamlit needs to be reachable from the internet). For this PR, we will change changed streamlit to a Load Balanced Web Service. As we get closer to production, the architecture will likely may change to:

  • Configure web to act as a reverse proxy
    • streamlit-related iframes in web will point to a path in web but this path will be set up in the ngingx configuration to forward any requests to streamlit over the private network
  • Place streamlit in a private network (back to Backend Service) and configure it to deny all incoming traffic except for requests from the private IP address of web.

Django database fixtures

For AWS copilot deployments, we load the fixtures from S3 on each deploy by:

  1. Adding the pems-db addon to the web service
    This S3 bucket addon was created using

    copilot storage init --name pems-db --storage-type S3 --workload web --lifecycle workload
    

    and the output of the command was an addon manifest file. The S3 bucket's lifecycle is tied to the web service, so it will be deleted if the web service is deleted. Once the deployment process stabilizes, the S3 bucket's lifecycle will instead be tied to the copilot environment.

  2. Setting up the aws CLI in the app container
    The aws CLI will be needed for step 3, so it is installed in the app container. There is no need to re-install it in the dev container so the installation step in the dev container's Dockerfile has been removed.

  3. Running start_aws.sh using the web service manifest's CMD definition
    Since the web service manifest's CMD definition overrides the Dockerfile's CMD, we use this feature to run start_aws.sh which copies the fixtures from the S3 bucket and loads them into the SQLite database, while still keeping the original behavior for local builds of the app container (local development will still use start.sh in CMD)

Reviewing

Local behavior

To ensure that all the compose services still work locally, you can run bin/build.sh followed by starting up the dev container. Then you can start a debugging session for both Django: PeMS Client and Streamlit, no sidebar and see the locally running app in your browser.

AWS behavior

Running

copilot svc deploy -n streamlit

copilot svc deploy -n web

from inside the infra folder redeploys the services to AWS. Currently the dev environment is running at http://pems-d-publi-tdvc9tnwbm5n-1785238641.us-west-2.elb.amazonaws.com/ but it can change at any time until the environment is stabilized.

@lalver1 lalver1 self-assigned this May 30, 2025
Copy link

github-actions bot commented May 30, 2025

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  pems
  settings.py
  pems/core
  context_processors.py
  streamlit_app
  main.py
Project Total  

This report was generated by python-coverage-comment-action

@lalver1 lalver1 force-pushed the feat/app-stations branch from b32158d to 31a45b5 Compare June 4, 2025 21:47
@lalver1 lalver1 force-pushed the feat/app-stations branch 4 times, most recently from 271fa52 to 394f3da Compare June 12, 2025 20:27
<h2>Map</h2>
<div class="row" style="min-height: 450px;">
<div class="col-lg-12 border">
<iframe class="streamlit-app" src="{{ streamlit_host }}/stations--stations?embed=true&district_number={{ current_district.number }}">
Copy link
Member Author

@lalver1 lalver1 Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename class to streamlit-service
Realized that the custom streamlit-app class was not necessary since it can be replaced by Bootstrap's w-100 h-100.

def streamlit_host(request):
"""Context processor to add the Streamlit host URL part to the context."""

return {"streamlit_host": settings.STREAMLIT_HOST}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider top-level streamlit context

pems/settings.py Outdated

# Streamlit settings
STREAMLIT_LOCAL_PORT = os.environ.get("STREAMLIT_LOCAL_PORT", "")
STREAMLIT_HOST = os.environ.get("STREAMLIT_HOST", f"http://localhost:{STREAMLIT_LOCAL_PORT}")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

collapse these 2 variables to 1

@@ -10,3 +10,8 @@ header {
}
}
}

.streamlit-app {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a class in Bootstrap already that already sets these 2 properties? if so, use that one

@@ -35,6 +35,8 @@ EXPOSE 8501

COPY streamlit_app streamlit_app

COPY .streamlit .streamlit
Copy link
Member Author

@lalver1 lalver1 Jun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this to before COPY streamlit_app streamlit_app to take advantage of layer caching

@lalver1 lalver1 force-pushed the feat/app-stations branch 3 times, most recently from 2af3987 to 1e95879 Compare June 16, 2025 23:29
@lalver1 lalver1 marked this pull request as ready for review June 16, 2025 23:41
@lalver1 lalver1 requested a review from a team as a code owner June 16, 2025 23:41
thekaveman
thekaveman previously approved these changes Jun 18, 2025
Copy link
Member

@thekaveman thekaveman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to run this locally without any issues, thank you for the great write up!

Since I still need to renew my DOT account password, I didn't run anything against AWS. But the changes look good 👍

The only small comment would be to remove the pems/streamlit_sample app and corresponding streamlit_app/apps/sample since you have a real streamlit app now. No worries either way.

@lalver1 lalver1 force-pushed the feat/app-stations branch 2 times, most recently from 0864cb8 to 17aa7a4 Compare June 18, 2025 15:37
@thekaveman
Copy link
Member

thekaveman commented Jun 18, 2025

@lalver1 thanks for removing the sample app!

Do you want to try to squash that pre-commit commit into the commit that introduced those large text files? It seems like pre-commit is having an issue with the line endings extra whitespace, would be nice to get rid of that extra commit if it's easy enough.

@lalver1
Copy link
Member Author

lalver1 commented Jun 18, 2025

Thanks for the review @thekaveman! That was a good suggestion to remove the pems/streamlit_sample app and corresponding streamlit_app/apps/sample to keep things clean 🧹 .
Yep, let me do a little more cleanup on that pre-commit commit and /streamlit_service base URL, it would be nice to keep those changes in this PR 😄

lalver1 added 6 commits June 18, 2025 16:03
the District Stations Streamlit app is a simple proof-of-concept
that displays the traffic data collection stations in a district.
the django and streamlit apps are now wired up in a way that works
both for a local dev environment as well as a Cloud deployment.
since the web and streamlit services run on the same load balancer
in AWS, the http path defined in the manifest of each service must
be different from each other. the http path of the streamlit service
(streamlit_service) is also passed to the streamlit run command in
the entrypoint.sh script. also, the healthcheck path must be
defined in the manifest so that the service does not
continually restart.
this setup was causing an error when deploying to AWS since the Django
pems app resides in a different container. when running a local
debugger though, this error was not present since the local dev
container does have the pems app. since at this moment we are not
using Django models in the streamlit app, we are removing this ability.
copy the .streamlit directory into the streamlit service's image
so that the streamlit configuration is available to the container.
before, the configuration was only available to the local dev
container.
lalver1 added 4 commits June 18, 2025 16:03
for AWS copilot deployments, load the fixtures from S3 on each deploy:

1. add the pems-db addon to the web service
2. set up the aws CLI in the app container
   (remove it from the dev container since it'll already be available)
3. run start_aws.sh using the web service manifest's CMD definition
   (local development will still use start.sh in CMD)
also remove sample app from streamlit_app since we are not
using it anymore.
since the streamlit_sample Django app was removed, the load balancer
can now use /streamlit as the path to serve the streamlit copilot
service and not worry about path naming conflicts.
@lalver1 lalver1 force-pushed the feat/app-stations branch from e29a2b0 to 3578cfd Compare June 18, 2025 16:52
Copy link
Member

@thekaveman thekaveman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work 🚀 🚀

I left a few comments for follow-up, but this looks great for now!

http:
# Requests to this path will be forwarded to your service.
# To match all requests you can use the "/" path.
path: "/streamlit"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 😎

@@ -52,6 +52,23 @@ RUN useradd --create-home --shell /bin/bash $USER && \
apt-get install -qq --no-install-recommends build-essential nginx gettext && \
python -m pip install --upgrade pip
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's take a follow-up to add the Buildkit cache mounting feature to these Dockerfiles, for apt, pip, etc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect, I was also thinking that I should add this feature soon 😄

platform: linux/x86_64 # See https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/#platform
count: 1 # Number of tasks that should be running in your service.
exec: true # Enable running commands in your container.
command: bin/start_aws.sh
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see 👍


echo "Downloading $S3_FIXTURE_PATH from bucket $S3_BUCKET_NAME"
aws s3 cp "s3://${S3_BUCKET_NAME}/${S3_FIXTURE_PATH}" "${LOCAL_FIXTURE_PATH}"
echo "Download complete"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a follow-up, let's look into mounting the bucket as a volume that presents as a readable directory in the container. I assume this is possible with our setup (we do something similar in Azure).

This seems good for now 👍

def streamlit(request):
"""Context processor adds Streamlit-related information."""

return {"streamlit": {"url": settings.STREAMLIT_URL}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@lalver1 lalver1 merged commit bfa7ac5 into main Jun 18, 2025
9 checks passed
@lalver1 lalver1 deleted the feat/app-stations branch June 18, 2025 21:53
@lalver1 lalver1 mentioned this pull request Jun 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Wire up web and streamlit services for local and Cloud dev environments
2 participants