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

uWSGI support of nginx ingress controller? #143

Closed
yue9944882 opened this issue May 15, 2017 · 7 comments
Closed

uWSGI support of nginx ingress controller? #143

yue9944882 opened this issue May 15, 2017 · 7 comments
Labels
enhancement Pull requests for new features/feature enhancements

Comments

@yue9944882
Copy link

yue9944882 commented May 15, 2017

I didn't find a usable configuration in the source code when rendering ingress to a nginx conf file, for example, "uwsgi_pass" option support?

Any one have a practice to integrate uWSGI to the controller ?

@pleshakov
Copy link
Contributor

@yue9944882
uWSGI doesn't fit into Ingress. Could you share your use case?

@ghost
Copy link

ghost commented Jun 13, 2017

I'll explain this as I understand it.

uwsgi has its own binary protocol to avoid the overhead of gratuitous http parsing. http://uwsgi-docs.readthedocs.io/en/latest/FAQ.html#why-not-simply-use-http-as-the-protocol

When running uwsgi as an app server behind nginx, it is customary to use uwsgi_pass, as recommended in uwsgi's docs (e.g. http://uwsgi-docs.readthedocs.io/en/latest/Nginx.html) to tell nginx where to find the upstream and to talk to that upstream using the uwsgi binary protocol.

To be clear: nginx itself does NOT embed uwsgi in-process here, or get involved with WSGI or python: uwsgi is running in another process that may be somewhere else, to take care of the job of an (e.g.) python app server, and it simply talks to nginx as an upstream using the uwsgi protocol. This means all that is required on the nginx side is the ability to accept configuration of uwsgi as an upstream and the ability to speak that protocol as is often done with nginx.

So, not speaking for yue9944882, the very typical use case would be:

  • running a Python web application,
  • via a WSGI interface (most common and typically best option for most web apps written in e.g. Django or Flask),
  • using uwsgi as the app server (arguably one of the most common and best options for running WSGI apps behind nginx),
  • in the idiomatic recommended way for uwsgi (i.e. using the uwsgi binary protocol),
  • WITHOUT putting another superfluous proxy in the middle to translate between uwsgi protocol and http.

Supporting uwsgi_pass should be analogous to supporting other protocols that are used for app servers to talk to nginx.

Hope this helps.

@pleshakov
Copy link
Contributor

@harts-boundless
thanks for such a thorough response. The use case you described is valid.

However, for an edge load balancer, such as an Ingress controller, it is not typical to support uwsgi.
It is more common to have nginx as a reverse proxy along an uwsgi application server, and then an HTTP load balancer for load balancing those nginx proxies.

@vdboor
Copy link

vdboor commented Nov 29, 2017

What I'd rather do, is using the embedded HTTP server that uWSGI has: http://uwsgi-docs.readthedocs.io/en/latest/HTTP.html Your ingres can connect directly with that, so there is no need for a second nginx pod. This way you can run a single container that has uwsgi as main process. Use a config file like:

(update April 2022: incorporated latest insights)

[uwsgi]
strict = true
module = $(UWSGI_MODULE)
processes = $(UWSGI_PROCESSES)
threads = $(UWSGI_THREADS)
procname-prefix-spaced = uwsgi: $(UWSGI_MODULE)

# HTTP serving avoids the need for an Nginx container
http-socket = :8080
http-enable-proxy-protocol = 1
http-auto-chunked = true
http-keepalive = 75
http-timeout = 75
offload-threads = $(UWSGI_OFFLOAD_THREADS)

# Stats exposure
stats = :1717
stats-http = true

# Better startup/shutdown in docker:
die-on-term = true
lazy-apps = false
need-app = true
no-defer-accept = true

# Better behavior
# https://www.techatbloomberg.com/blog/configuring-uwsgi-production-deployment/
master = true
single-interpreter = true
enable-threads = true
py-call-osafterfork = true
thunder-lock = true
vacuum = true

# Logging
log-x-forwarded-for = true
#memory-report = true
#disable-logging = true
#log-slow = 200
#log-date = true

# Avoid errors on aborted client connections
ignore-sigpipe = true
ignore-write-errors = true
disable-write-exception = true

# Limits, and kill requests after 120 seconds
harakiri = 120
harakiri-verbose = true
post-buffering = 4096
buffer-size = 65535
#listen=1000
#max-fd=120000

# Reduce memory usage (Linux default is 8MB stack),
# Still on the safe side, even 512k can be sufficient for Python
thread-stacksize = 2048

# Custom headers for all files, not only those served by Django
add-header = X-Content-Type-Options: nosniff
add-header = X-XSS-Protection: 1; mode=block
add-header = Connection: Keep-Alive

# Static file serving with caching headers and gzip
static-map = /static=/app/web/static
static-map = /media=/app/web/media
static-safe = /usr/local/lib/python3.8/site-packages/
static-safe = /app/src/frontend/static/
static-gzip-dir = /app/web/static/
route-uri = ^/static/ addheader:Vary: Accept-Encoding

# Allow CDN's to cache the files
route-uri = ^/static/ addheader:Cache-Control: public
route-uri = ^/static/frontend/fonts/ addheader:Access-Control-Allow-Origin: *

# Static file serving with caching headers and gzip
# Only keep long cache for files that have cache busting paths
static-expires-uri = ^/media/cache/ 8640000
static-expires-uri = ^/static/.+\.[0-9a-f]{12}\..+$ 8640000
static-expires-uri = ^/static/CACHE/ 8640000
static-expires-uri = ^/static/ 3600
static-expires-uri = ^/media/(?!cache/) 3600

# Cache stat() calls
cache2 = name=statcalls,items=2000,keysize=200,blocksize=50
static-cache-paths = 86400

# Avoid caching static files with a 404, as another docker container might be serving it.
# This also avoids forwarding the request to the Python app.
error-route-status = 404 goto:error404
error-route = .* last:

error-route-label = error404
error-route-if = startswith:${PATH_INFO};/static/ remheader:Expires
error-route-if = startswith:${PATH_INFO};/static/ addheader:Cache-Control: no-cache
error-route = .* last:

# Redirect http -> https
if-not-env = UWSGI_ALLOW_HTTP=true
add-header = Strict-Transport-Security: max-age=16070400
route-if = equal:${HTTP_X_FORWARDED_PROTO};http redirect-permanent:https://${HTTP_HOST}${REQUEST_URI}
endif =

# Redirects:
# - www -> non www
route-host = ^www redirect-permanent:https://mywebsite.com${REQUEST_URI}
# - uppercased host headers
route-host = [A-Z] redirect-permanent:https://mywebsite.com${REQUEST_URI}

and set default values for your environment variables in your container:

ENV UWSGI_THREADS=10 \
    UWSGI_PROCESSES=2 \
    UWSGI_OFFLOAD_THREADS=10 \
    UWSGI_MODULE=myapp.wsgi:application

# Have gzipped versions ready for direct serving by uwsgi
RUN gzip --keep --best --force --recursive /app/web/static/

CMD ["/usr/local/bin/uwsgi", "--ini", "/app/uwsgi.ini"]
EXPOSE 8080
VOLUME /app/web/media

When you use Django, you can use whitenoise (http://whitenoise.evans.io/) to serve media/static files from your container. In this example, I've used UWSGI directly for that. Preferably, those are cached at your loadbalancer / ingres / cloudfrond environment to avoid hitting the container each time. Thanks to the static-gzip-dir setting these are already prepared to be sent as gzipped.

cdent added a commit to cdent/placedock that referenced this issue Mar 17, 2018
This is mostly for the sake of learning: the fact that the sqlite
database is in container means it's fairly useless: you can't scale
horizontally. Future work will explore different ways of dealing
what that.

The Dockerfile is update to reflect the latest placement extraction
changes and to put config in the container rather than using a
shared volume.

The placement-uwsgi.ini is update so it runs http directly.
There's no need for an nginx or apache setup, a k8s LoadBalancer
in deployment.yaml takes care of that.

The uwsgi ini is based on info at:
nginx/kubernetes-ingress#143 (comment)

Using minikube the steps are:

    eval $(minikube docker-env)
    docker build -t placedock:1.0 .
    kubectl apply -f deployment.yaml
    kubectl expose deployment placement-deployment --type=LoadBalancer
    minikube service placement-deployment --url
@isaachawley isaachawley added the enhancement Pull requests for new features/feature enhancements label Jul 30, 2018
jonathansick added a commit to lsst-sqre/ltd-keeper that referenced this issue Oct 3, 2019
jonathansick added a commit to lsst-sqre/ltd-keeper that referenced this issue Oct 7, 2019
See docs at https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html

This paves the way for us to drop the nginx sidecar and instead directly
use nginx-ingress as the web-facing server.

Ideas for many configurations configurations come from
nginx/kubernetes-ingress#143 (comment)
@smekkley
Copy link

It's a shame that this issue was closed without any discussion. It would have opened up more nginx capability. OP probably knew about uwsgi http support already... Fcgi support would be also great. Another benefit of uwsgi is not needing any xforwarded and all the headers http puts. I also see static files depending on nginx is better, rather than relying on uwsgi with shorter history of supporting http than nginx. Hitting containers is no issue with k8s, if it is, the application should be fixed.

@c-goosen
Copy link

Thank you @vdboor vdboor. Ive been contemplating editing uwsgi to do this, but your config was a great guide and working. Thanks for the assists.

@sanzenwin
Copy link

@vdboor , thanks, it is helpful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Pull requests for new features/feature enhancements
Projects
None yet
Development

No branches or pull requests

7 participants