Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Production-ready Flask & Django apps on Kubernetes

Jamie Hewland
October 11, 2019
220

Production-ready Flask & Django apps on Kubernetes

This talk will explain how to host Flask, Django, and other WSGI web applications on Kubernetes, making effective use of the available abstractions.

Topics covered will include:

- Some of the benefits of running Flask & Django applications as part of a Service-oriented architecture (SOA) on Kubernetes.
- How the abstractions Kubernetes provides can be used to replace or integrate with existing well-known tools.
- General best practices for running Python web applications on Kubernetes.
- Reflections on some of the widely-used Python tools for running web applications and how well they suit modern containerised deployments.

Some basic familiarity with Docker containers as well as either Flask, Django, or similar Python-based web frameworks will be helpful. The talk is aimed at people interested in migrating their existing applications to Kubernetes.

Jamie Hewland

October 11, 2019
Tweet

Transcript

  1. 2 • ~3 years as a Site Reliability Engineer (SRE)

    working on container things • Now a Backend Engineer at Aruba working on Python services • Spoke at PyConZA 2017 about containerizing Django web apps: https://youtu.be/T2hooQzvurQ @jayhewland [email protected] Cape Town, South Africa Hi, I’m Jamie
  2. 3 About this talk • This is not an intro

    to Kubernetes or Python web apps • But if you are familiar with one, this talk may help you understand the other • There will be more YAML than Python All text is in Arial Kubernetes resources are always in a monospace font, bold, and Title Case: • Deployment • Pod • ConfigMap
  3. INTRO • Why Kubernetes? • “The WSGI Stack” ADAPTING TO

    KUBERNETES • Handling requests • Static file serving • Deployments • Configuration • Credentials • Database migrations THE FUTURE AGENDA
  4. 5 Why Kubernetes? • Huge ecosystem of tools through the

    CNCF to extend Kubernetes • Spend less time implementing something that has been done before Automation Ecosystem Abstraction • Deployments with minimal manual intervention • Fail-over when an app misbehaves • Tie together multiple tools • Compute/networking/ storage abstracted over multiple hosts • Developers don’t think about sysadmin tasks • Multi-cloud support • Declarative infrastructure requirements for apps • Improve resource usage
  5. 16 Most WSGI servers are designed like this: the web

    client -> ~~the internet~~ -> the web server (Nginx) -> local socket -> WSGI server -> your application The web server… • Buffers slow client requests so that app code can stay synchronous (Gunicorn) • Converts from HTTP to UWSGI protocol (uWSGI) • “Shields” the Python program With Kubernetes we can’t expect there to be a web server
  6. 17 Buffering reverse proxy Remove need for proxy Run async

    WSGI workers that don’t require a buffering proxy Service mesh Use a service mesh to “abstract the network layer”. Sidecar proxy in Pod Run a buffering proxy such as Nginx in the same Pod as the application ALTERNATIVE GOLD STAR ⭐ SOLUTION ✅ ✅ ❌ ❌ Don’t modify the app Maintain similarity with pre-K8S environment Additional container for each Pod More K8S YAML ✅ ✅ ✅ ❌ No proxy to set up Potentially lower overhead OK with an Ingress Potential code changes, app behaviour changes ✅ ✅ ❌ No proxy specific to app to set up All the pros of a service mesh Have to run a service mesh
  7. 18 Buffering reverse proxy Remove need for proxy Run async

    WSGI workers that don’t require a buffering proxy Service mesh Use a service mesh to “abstract the network layer”. Sidecar proxy in Pod Run a buffering proxy such as Nginx in the same Pod as the application ALTERNATIVE GOLD STAR ⭐ SOLUTION ✅ ✅ ❌ ❌ Don’t modify the app Maintain similarity with pre-K8S environment Additional container for each Pod More K8S YAML ✅ ✅ ❌ No proxy specific to app to set up All the pros of a service mesh Have to run a service mesh RECOMMENDED No proxy to set up Potentially lower overhead OK with an Ingress Potential code changes, app behaviour changes ✅ ✅ ✅ ❌
  8. 21 Serving static files location /static/ { alias /path/to/static/files; }

    location / { proxy_set_header … proxy_pass http://127.0.0.1:8000; } GET /admin/ [Django] GET /static/admin/css/base.css [Nginx] uWSGI: “[…] it’s inefficient to serve static files via uWSGI. Instead, serve them directly from Nginx and completely bypass uWSGI.”
  9. 22 Serving static files Serve static files from S3 Use

    django-storages or similar library to upload static files to S3 or similar WhiteNoise + CDN Serve optimised static files with Python using WhiteNoise library Sidecar web server in Pod Run a web server such as Nginx in the same Pod as the application, share a Volume with static files ALTERNATIVE GOLD STAR ⭐ SOLUTION ✅ ✅ ❌ ❌ Don’t modify the app Maintain similarity with pre-K8S environment Possibly additional container for each Pod Volume increases startup time ✅ ❌ ❌ Simple & easy Additional step to upload files Unoptimised (compression/caching headers) ✅ ✅ ✅ ❌ Automatically compressed assets Smart caching headers App & static files always in sync Potentially larger container images
  10. 23 Serving static files Serve static files from S3 Use

    django-storages or similar library to upload static files to S3 or similar WhiteNoise + CDN Serve optimised static files with Python using WhiteNoise library Sidecar web server in Pod Run a web server such as Nginx in the same Pod as the application, share a Volume with static files ALTERNATIVE GOLD STAR ⭐ SOLUTION ✅ ✅ ❌ ❌ ✅ ❌ ❌ Simple & easy Additional step to upload files Unoptimised (compression/caching headers) ✅ ✅ ✅ ❌ Automatically compressed assets Smart caching headers App & static files always in sync Potentially larger container images RECOMMENDED Don’t modify the app Maintain similarity with pre-K8S environment Possibly additional container for each Pod Volume increases startup time
  11. 24 WhiteNoise ❯ pip install whitenoise Django: 1. Add middleware

    class 2. (Optional) Add staticfiles storage class 3. Remember to run collectstatic Flask: 1. Add WSGI middleware 2. (Optional) Configure static file directories Isn’t serving static files from Python horribly slow? • Use a CDN • All assets generated offline • WSGI servers can use the sendfile syscall Advantages • Serve static files directly from Python • Compressed assets (w/correct headers): GZIP, Brotli • Automatic far-future cache headers http://whitenoise.evans.io/en/stable/
  12. 26 Deployments With containers we never change the running code,

    rather: • Build a new image with the new code • Run new container with new image • Switch traffic over to new container • Shut down old container https://martinfowler.com/bliki/BlueGreenDeployment.html Previously: update the code, then: • Gunicorn and uWSGI support graceful reload via SIGHUP • Some may just restart the process
  13. 27 Deployments Multiple Deployments Shift traffic between two or more

    Deployments Advanced deployment strategies Canarying, gradual red/ black deployments, traffic shadowing Deployment A Deployment resource with default settings such as RollingUpdate. NEXT STEPS GOLD STAR ⭐ SOLUTION ✅ ❌ ❌ Basic blue/green deploy Limited deployment history Limited update strategies ✅ ✅ ❌ ❌ Fast roll-backs Open the door to more advanced deployments Increased resource usage (run 2 copies) Need extra tool to manage deployment ✅ ❌ “Test in production!” Requires sophisticated tooling & expertise
  14. 28 Deployments Advanced deployment strategies Canarying, gradual red/ black deployments,

    traffic shadowing Deployment A Deployment resource with default settings such as RollingUpdate. NEXT STEPS GOLD STAR ⭐ SOLUTION RECOMMENDED ✅ ✅ ❌ ❌ Fast roll-backs Open the door to more advanced deployments Increased resource usage (run 2 copies) Need extra tool to manage deployment ✅ ❌ “Test in production!” Requires sophisticated tooling & expertise Multiple Deployments Shift traffic between two or more Deployments ✅ ❌ ❌ Basic blue/green deploy Limited deployment history Limited update strategies
  15. 31 Smaller instances Cons: • Increased overhead (especially RAM) •

    Greater need for automation Pros: • Finer-grained scaling • More redundancy • Better for canarying
  16. 33 Configuration Like code, we never want the config a

    container runs with to change: • Run new container with the new config • Switch traffic over to new container • Shut down old container Previously: update the config by… • Running Configuration Management tool (e.g. Chef, Puppet, Ansible) • Reload/restart server
  17. 34 Configuration ConfigMap Store config in a ConfigMap and import

    in app via env vars or file volume Immutable ConfigMaps Create a new ConfigMap for each config change Environment variables Set environment variables for containers in the Deployment BETTER BEST PRACTICE STARTING OUT ✅ ❌ ❌ It works Lots of YAML Have to pack everything into strings ✅ ❌ Separate app & config ConfigMaps are mutable ✅ ✅ ❌ Keep record of previous configs Not difficult to do (use Kustomize or Helm) Probably need additional tool
  18. 35 Configuration ConfigMap Store config in a ConfigMap and import

    in app via env vars or file volume Immutable ConfigMaps Create a new ConfigMap for each config change Environment variables Set environment variables for containers in the Deployment BETTER BEST PRACTICE STARTING OUT ✅ ❌ ❌ It works Lots of YAML Have to pack everything into strings ✅ ❌ Separate app & config ConfigMaps are mutable ✅ ✅ ❌ Keep record of previous configs Not difficult to do (use Kustomize or Helm) Probably need additional tool RECOMMENDED
  19. 38 Credentials Secret Store credentials in a Secret and and

    import in app via env vars or (preferably) file volume HashiCorp Vault or similar Store secrets (possibly dynamic ones) in a dedicated service Store with the configuration Store credentials just like you would any other configuration BEST PRACTICE GOLD STAR ⭐ STARTING OUT ✅ ❌ Possibly no worse than what you’re doing already Credentials stored in plain-text in etcd ✅ ❌ ❌ Separate sensitive secrets from config May have to create Secrets out-of-band Make sure your K8S is configured to use envelope encryption ✅ ✅ ❌ ❌ About as secure as it gets right now Other advantages to Vault, e.g. auditing Additional infrastructure Likely requires app- level changes
  20. 39 Credentials Secret Store credentials in a Secret and and

    import in app via env vars or (preferably) file volume HashiCorp Vault or similar Store secrets (possibly dynamic ones) in a dedicated service Store with the configuration Store credentials just like you would any other configuration BEST PRACTICE GOLD STAR ⭐ STARTING OUT ✅ ❌ Possibly no worse than what you’re doing already Credentials stored in plain-text in etcd ✅ ❌ ❌ Separate sensitive secrets from config May have to create Secrets out-of-band Make sure your K8S is configured to use envelope encryption ✅ ✅ ❌ ❌ About as secure as it gets right now Other advantages to Vault, e.g. auditing Additional infrastructure Likely requires app- level changes RECOMMENDED
  21. 42 Database migrations initContainers Add a container in the initContainers

    section to run on Pod startup BEST PRACTICE GOLD STAR ⭐ STARTING OUT ✅ ❌ ❌ Basic automation Run on every Pod start No post-/pre-deployment control ✅ ✅ ❌ Single process running migration Separate deployment of schema changes from deployment of app Automation? ✅ ❌ Automated Requires mature CD system Job triggered by CD Post- and/or pre- deployment Job run automatically by Continuous Deployment system Job triggered manually Run a one-off Job manually each time a migration needs to be run
  22. 43 Database migrations Job triggered manually Run a one-off Job

    manually each time a migration needs to be run Job triggered by CD Post- and/or pre- deployment Job run automatically by Continuous Deployment system initContainers Add a container in the initContainers section to run on Pod startup BEST PRACTICE GOLD STAR ⭐ STARTING OUT ✅ ❌ ❌ Basic automation Run on every Pod start No post-/pre-deployment control ✅ ❌ Automated Requires mature CD system RECOMMENDED Single process running migration Separate deployment of schema changes from deployment of app Automation? ✅ ✅ ❌
  23. 45 tl;dl Problem “Legacy” solution Recommended Kubernetes solution Buffering reverse

    proxy Nginx on host Pod with sidecar proxy Static file serving Nginx on host WhiteNoise + CDN Deployments WSGI server reload signal Deployment with blue/green strategy Configuration Configuration Management tool ConfigMap (and/or 12-factor app) Credentials Configuration Management tool Secret Database migrations Manual or CD step Job migration container
  24. 46 This is a lot to get right (or wrong)

    Why is it so complicated? •Established tools like Gunicorn and uWSGI were designed before containers/Kubernetes • Process management • Hot-code reload • User-switching • Daemonizing • Many tuneable settings
  25. 47 This is a lot to get right (or wrong)

    Why is it so complicated? •Established tools like Gunicorn and uWSGI were designed before containers/Kubernetes • A lot of stuff is not necessary for containers • Too many adjustable settings for beginners • Process management handled by Docker or Kubernetes • Expecting a traditional web server like Nginx is no longer a fair assumption • We’re not hosting LAMP servers • Learning curve from dev server to production- ready is steep (Kubernetes or not)
  26. 48 ASGI •Asynchronous Server Gateway Interface •async/await support: WebSockets &

    HTTP/2 •Early ASGI server implementations: •Daphne •Uvicorn •Hypercorn •Needs new frameworks: •Django Channels, Django 3 (coming soon) •Quart (async Flask port) •Starlette, Sanic, … •Async ecosystem still evolving
  27. 49 Thank you Further reading: • Deploying Django web applications

    in Docker containers: youtu.be/T2hooQzvurQ • Kubernetes, Local to Production with Django by Mark Gituma: bit.ly/k8slocaltoprod @jayhewland [email protected]