-
Notifications
You must be signed in to change notification settings - Fork 34
Add nginx load balancing support for multi-instance Streamlit deployments #336
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
Changes from all commits
be23790
0aa8937
85e6d0f
c07003f
604d184
6a6e0b1
48e331f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,7 +25,7 @@ USER root | |
|
|
||
| RUN apt-get -y update | ||
| # note: streamlit in docker needs libgtk2.0-dev (see https://yugdamor.medium.com/importerror-libgthread-2-0-so-0-cannot-open-shared-object-file-no-such-file-or-directory-895b94a7827b) | ||
| RUN apt-get install -y --no-install-recommends --no-install-suggests wget ca-certificates libgtk2.0-dev curl jq cron | ||
| RUN apt-get install -y --no-install-recommends --no-install-suggests wget ca-certificates libgtk2.0-dev curl jq cron nginx | ||
| RUN update-ca-certificates | ||
|
|
||
| # Install Github CLI | ||
|
|
@@ -84,11 +84,52 @@ COPY clean-up-workspaces.py /app/clean-up-workspaces.py | |
| # add cron job to the crontab | ||
| RUN echo "0 3 * * * /root/miniforge3/envs/streamlit-env/bin/python /app/clean-up-workspaces.py >> /app/clean-up-workspaces.log 2>&1" | crontab - | ||
|
|
||
| # Number of Streamlit server instances for load balancing (default: 1 = no load balancer) | ||
| # Set to >1 to enable nginx load balancer with multiple Streamlit instances | ||
| ENV STREAMLIT_SERVER_COUNT=1 | ||
|
|
||
| # create entrypoint script to start cron service and launch streamlit app | ||
| RUN echo "#!/bin/bash" > /app/entrypoint.sh | ||
| RUN echo "source /root/miniforge3/bin/activate streamlit-env" >> /app/entrypoint.sh && \ | ||
| echo "service cron start" >> /app/entrypoint.sh && \ | ||
| echo "streamlit run app.py" >> /app/entrypoint.sh | ||
| RUN echo -e '#!/bin/bash\n\ | ||
| set -e\n\ | ||
| source /root/miniforge3/bin/activate streamlit-env\n\ | ||
| \n\ | ||
| # Start cron for workspace cleanup\n\ | ||
| service cron start\n\ | ||
| \n\ | ||
| # Load balancer setup\n\ | ||
| SERVER_COUNT=${STREAMLIT_SERVER_COUNT:-1}\n\ | ||
| \n\ | ||
| if [ "$SERVER_COUNT" -gt 1 ]; then\n\ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-numeric The 🛠️ Proposed validation# Validate SERVER_COUNT is a positive integer
if ! [[ "$SERVER_COUNT" =~ ^[0-9]+$ ]] || [ "$SERVER_COUNT" -lt 1 ]; then
echo "ERROR: STREAMLIT_SERVER_COUNT must be a positive integer, got: '$SERVER_COUNT'" >&2
exit 1
fi🤖 Prompt for AI Agents |
||
| echo "Starting $SERVER_COUNT Streamlit instances with nginx load balancer..."\n\ | ||
| \n\ | ||
| # Generate nginx upstream block\n\ | ||
| UPSTREAM_SERVERS=""\n\ | ||
| BASE_PORT=8510\n\ | ||
| for i in $(seq 0 $((SERVER_COUNT - 1))); do\n\ | ||
| PORT=$((BASE_PORT + i))\n\ | ||
| UPSTREAM_SERVERS="${UPSTREAM_SERVERS} server 127.0.0.1:${PORT};\\n"\n\ | ||
| done\n\ | ||
| \n\ | ||
| # Write nginx config\n\ | ||
| mkdir -p /etc/nginx\n\ | ||
| echo -e "worker_processes auto;\\npid /run/nginx.pid;\\n\\nevents {\\n worker_connections 1024;\\n}\\n\\nhttp {\\n client_max_body_size 0;\\n\\n map \\$cookie_stroute \\$route_key {\\n \\x22\\x22 \\$request_id;\\n default \\$cookie_stroute;\\n }\\n\\n upstream streamlit_backend {\\n hash \\$route_key consistent;\\n${UPSTREAM_SERVERS} }\\n\\n map \\$http_upgrade \\$connection_upgrade {\\n default upgrade;\\n \\x27\\x27 close;\\n }\\n\\n server {\\n listen 8501;\\n\\n location / {\\n proxy_pass http://streamlit_backend;\\n proxy_http_version 1.1;\\n proxy_set_header Upgrade \\$http_upgrade;\\n proxy_set_header Connection \\$connection_upgrade;\\n proxy_set_header Host \\$host;\\n proxy_set_header X-Real-IP \\$remote_addr;\\n proxy_set_header X-Forwarded-For \\$proxy_add_x_forwarded_for;\\n proxy_set_header X-Forwarded-Proto \\$scheme;\\n proxy_read_timeout 86400;\\n proxy_send_timeout 86400;\\n proxy_buffering off;\\n add_header Set-Cookie \\x22stroute=\\$route_key; Path=/; HttpOnly; SameSite=Lax\\x22 always;\\n }\\n }\\n}" > /etc/nginx/nginx.conf\n\ | ||
| \n\ | ||
| # Start Streamlit instances on internal ports (localhost only)\n\ | ||
| for i in $(seq 0 $((SERVER_COUNT - 1))); do\n\ | ||
| PORT=$((BASE_PORT + i))\n\ | ||
| echo "Starting Streamlit instance on port $PORT..."\n\ | ||
| streamlit run app.py --server.port $PORT --server.address 127.0.0.1 &\n\ | ||
| done\n\ | ||
| \n\ | ||
| sleep 2\n\ | ||
| echo "Starting nginx load balancer on port 8501..."\n\ | ||
| exec /usr/sbin/nginx -g "daemon off;"\n\ | ||
| else\n\ | ||
| # Single instance mode (default) - run Streamlit directly on port 8501\n\ | ||
| echo "Starting Streamlit app..."\n\ | ||
| exec streamlit run app.py\n\ | ||
| fi\n\ | ||
| ' > /app/entrypoint.sh | ||
|
Comment on lines
92
to
132
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Massive inline script is fragile and hard to maintain — consider an external file. The entrypoint script is generated via a single Consider using a separate 🛠️ Proposed approachCreate a file -RUN echo -e '#!/bin/bash\n\
-set -e\n\
-...long inline script...
-' > /app/entrypoint.sh
+COPY entrypoint_simple.sh /app/entrypoint.shThis gives you:
🤖 Prompt for AI Agents |
||
| # make the script executable | ||
| RUN chmod +x /app/entrypoint.sh | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compounded unsupervised-process problem: Redis + RQ workers + Streamlit instances + nginx.
In multi-instance mode, this container runs at least 4+ background daemons (Redis, N RQ workers, M Streamlit instances) with nginx as PID 1. None of the background processes have supervision or restart capability, and nginx won't reap zombie children.
This is the same issue flagged in
Dockerfile_simplebut amplified here. A process supervisor likesupervisordwould be strongly recommended for this container, or at minimumtinias the init process.🤖 Prompt for AI Agents