Deploying Secure and Scalable Streamlit Apps on AWS with Docker Swarm, Traefik and Keycloak

[ad_1]

By Yihui Fan, Data Scientist

 

Background

 
Streamlit is a well-liked open-source framework for creating machine learning and visualisation apps in Python. Although it’s enjoyable making your personal Streamlit apps, deploying a production-grade app may be fairly painful. If you’re a data scientist who simply needs to get the work finished however doesn’t essentially need to go down the DevOps rabbit gap, this tutorial affords a comparatively simple deployment answer leveraging Docker Swarm and Traefik, with an choice of including person authentication with Keycloak. The first a part of the tutorial is supposed to be a mild introduction to Docker. We will deploy a dockerised Streamilit demo app regionally. The second half is about deploying the app within the cloud. These are two comparatively standalone elements. You can skip the primary part if you’re already acquainted with Docker.

 

Deploying apps regionally with Docker

 

Step 1: Installing Docker

 
Simply observe the instruction on this web page to obtain the most recent Docker Windows Desktop from Docker (Mac customers please see right here; for Linux customers, this set up script may be very useful, which we’ll use when deploying the apps on AWS situations). Docker is a extremely highly effective instrument however I’ll solely cowl the essential instructions and instruments on this submit. If you have an interest, please verify the tutorials on-line.

Please word the next system requirement:

  • Windows 10 64-bit: Pro, Enterprise, or Education (Build 15063 or later).
  • Hyper-V and Containers Windows options should be enabled.
  • The following {hardware} conditions are required to efficiently run Client Hyper-V on Windows 10:

If you might be utilizing Windows 7 or older model of macOS, you’ll be able to attempt the Docker Toolbox. It will create a small Linux VM (VirtualBox). This VM hosts Docker Engine for you on your Windows system. If you may have a more recent OS, then likelihood is you need to use its native virtualization and doesn’t require VirtualBox to run Docker.

 

Step 2: Building and working the Streamlit GAN demo app

 
Since this submit is about deployment, we gained’t be creating Streamlit apps from scratch however utilizing the GAN Face Generator demo app as an alternative. The app calls on TensorFlow to generate photorealistic faces, utilizing Nvidia’s Progressive Growing of GANs and Shaobo Guan’s Transparent Latent-space GAN technique for tuning the output face’s traits.

First, let’s clone the demo repo.

The demo-face-gan folder accommodates the information and the skilled GAN fashions (pg_gan and tl_gan), the app script app.py and the requirement.txt. Normally, we might create a digital setting and set up the modules specified within the requirement.txt. But let’s do it within the Docker means! Docker offers the flexibility to bundle and run an utility in a loosely remoted setting referred to as a container. A Docker container is nothing however an setting virtualized throughout run-time to permit customers to isolate functions from the system underpinning it. To spin off containers, we’d like Docker photographs, that are non-changeable recordsdata containing libraries, supply code, instruments and different recordsdata wanted to run functions. Let’s begin by making a Docker picture for the demo app.

We might want to create a file referred to as Dockerfile. You can consider it as a set of directions or blueprint to construct the picture. Copy the Dockerfile beneath to the demo-face-gan folder. Note that I’ve included feedback within the file to clarify every half. This might be one of many easiest Dockerfile. For particulars of different choices obtainable in Dockerfiles, please see the official doc.

# Dockerfile to create a Docker picture for the demo-face-gan Streamlit app

# Creates a layer from the python:3.7 Docker picture
FROM python:3.7

# Copy all of the recordsdata from the folders the Dockerfile is to the container root folder
COPY . .

# Install the modules specified within the necessities.txt
RUN pip3 set up -r necessities.txt

# The port on which a container listens for connections
EXPOSE 8501

# The command that run the app
CMD streamlit run app.py

 

Make positive Docker Desktop (daemon) is working and you may have cd to the listing the place the Dockerfile is and then run:

docker construct -f Dockerfile -t streamlit-demo:newest .

It will take some time to arrange the picture. Once it’s finished, we will run the demo in Docker by:

docker run -p 8501:8501 streamlit-demo

Visit http://localhost:8501 to view the app.

Streamlit Demo

 

Deploying the demo app on the cloud with Docker Swarm

 
We have efficiently run the app in a Docker container regionally. But with a purpose to make it simply accessible to different customers, we’ll deploy it to the cloud utilizing a container orchestration framework referred to as Docker Swarm. You can consider it as a cluster of machines on the cloud that hosts your app. You are in all probability questioning why you want Docker Swarm in case your app is hosted on a single server. Docker Captain Bret Fisher defined it properly right here. I summarised a few key advantages of Docker Swarm beneath:

  • Docker Swarm is absolutely supported by Docker Engine, which implies it solely takes a single line of command to create a Swarm.
  • You are future-proofed. If you need to scale out your app, you gained’t want to begin from scratch. Again, with Docker Swarm, it’s only a few instructions away from including extra nodes.
  • The vanilla Docker run and docker-compose is just designed for improvement, not manufacturing, because it lacks a few essential options out-of-the-box: 1) dealing with secret (that shops your keys and passwords securely) 2) auto-recovery of companies, 3) rollbacks and 4) healthchecks. The final one is especially essential for manufacturing.
  • You are in a position to do rolling updates with Docker Swarm, which implies no downtime on your app.
  • Finally, if you’re already utilizing docker-compose.yml file, it’s only a couple tweaks away to make it Docker Swarm pleasant!

In phrases of container orchestration instruments, Kubernetes is extra common. It covers nearly all of the use circumstances and may be extra versatile than Docker Swarm. Plus, many distributors undertake the ‘Kubernetes first’ help technique and some clouds even handle/deploy Kubernetes for you. However, I’d nonetheless argue that Docker Swarm is satisfactory for 80% of the use circumstances and means a lot simpler to arrange. This means you’ll be able to have your app working in hours moderately than days!

We will put our app behind Traefik, which is a reverse proxy/load balancer. If you may have learn my earlier submit Securing and monitoring ShinyProxy deployment of R Shiny apps, you in all probability marvel why I switched away from Nginx to Traefik. This is especially because of the ease of arrange. Nginx settings can find yourself in large config maps which are laborious to learn and handle. This will not be a problem with Traefik, which lets you use Docker labels to handle configs. We will see this later within the tutorial.

 

Step 1: Setting up the supervisor node

 
There are many cloud computing suppliers. In this tutorial, I’ll use AWS EC2 however the next steps may be simply applied in different platforms. First, please seek advice from this submit for launching an AWS EC2 occasion if you’re new to EC2. Since the app is definitely fairly computationally intensive, I selected the t3a.medium occasion (2 vCPU, Four GiB reminiscence) and choose the Ubuntu Server 18.04 LTS (HVM), SSD Volume Type AMI. Unfortunately, this isn’t eligible for the free tier and will incur some value (at $0.0376 per hour). You can change to your personal app, which can want much less sources in comparison with the GAN demo app. But you should still need to deploy on an occasion with at the least 2GiB of reminiscence because of different companies that we’d like for this tutorial.

You additionally have to open the related ports so the Swarm nodes can talk with one another and enable site visitors to your app. You ought to use the AWS Security Group (or equal from different Clouds) for simple setup and administration. Below are the particular settings:

Image

 

We will first arrange a supervisor node. Once you may have launched the occasion with the related ports opened, we’ll set up Docker Engine utilizing the setup script.

After putting in Docker, I’d counsel that you simply add your person to the ‘docker’ group in order that you can use Docker as a non-root person.

sudo usermod -aG docker ubuntu

And don’t overlook to log off and log again in once more for this transformation to take impact. Then you need to be capable to run docker instructions with out utilizing sudo.

 

Step 2: Setting up Docker Swarm

 
As talked about, you simply want one line of command to provoke a Docker Swarm, as it’s constructed into the usual Docker Engine.

You will see one thing like this:

Swarm initialized: present node (xxxx) is now a supervisor.

We then have to get the be part of token for managers and employees.

docker swarm join-token employee
docker swarm join-token supervisor

Note down the be part of instructions. To add nodes to the present Swarm as a supervisor or employee, you merely have to launch one other occasion, set up Docker Engine and run the be part of instructions. However, we don’t have to set them up for now.

 

Step 3: Setting up domains on your app and system dashboards

 
Let’s say you personal the area instance.com and you need to use the subdomain app.instance.com on your app. You have to create the next DNS data on your app and Traefik dashboard:

 

 

Step 4: Setting up Traefik stack

 
First, let’s arrange the Traefik reserved proxy/load balancer. Docker Swarm Rocks has a beautiful tutorial for it. I’ve summarised the important thing steps right here. First, we have to create an overlay community shared with Traefik and enable nodes on the Swarm to speak with one another. Note that that is completely different from the host-specific networks we create utilizing the default bridge driver, which solely permits networking between containers in a single server. The overlay community sits on prime of (overlays) the host-specific networks and permits containers related to it to speak securely when encryption is enabled.

docker community create --driver=overlay traefik-public

Get the Swarm node ID of this node and retailer it in an setting variable.

export NODE_ID=$(docker information -f '{{.Swarm.NodeID}}')

Create a tag on this node, in order that Traefik is all the time deployed to the identical node and makes use of the identical quantity.

docker node replace --label-add traefik-public.traefik-public-certificates=true $NODE_ID

Create an setting variable with your e-mail, for use for the era of Let’s Encrypt certificates.

export EMAIL=admin@instance.com

Create an setting variable with the area you need to use for the Traefik dashboard. If you specified a distinct area title earlier than, it’s essential replace the beneath code accordingly. You will entry the Traefik dashboard at this area.

export DOMAIN=traefik.sys.app.instance.com

Create an setting variable with a username (you’ll use it for the HTTP Basic Auth for Traefik dashboard).

Create an setting variable that shops the hashed password. Note that the beneath command will help you enter the password into an interactive immediate, which is safer simply typing into the shell (which will probably be saved within the shell historical past).

export HASHED_PASSWORD=$(openssl passwd -apr1)

Check when you have efficiently created a password:

It will appear to be:

$apr1$HOr/xJFw$uUY15r1qS.5AA2hk.ssda1

Now, let’s deploy the primary stack – Traefik. The writer at Docker Swarm Rocks did a tremendous job of creating this course of as straightforward as attainable. You merely have to obtain the yaml file.

curl -L dockerswarm.rocks/traefik.yml -o traefik.yml

I’ve copied the traefik.yml file beneath with some feedback inline that inform present what every a part of code does. This is a reasonably customary setup and we don’t want to alter something for this tutorial.

model: '3.3'

companies:

  traefik:
    # Use the most recent Traefik picture
    picture: traefik:v2.2
    ports:
      # Listen on port 80, default for HTTP, essential to redirect to HTTPS
      - 80:80
      # Listen on port 443, default for HTTPS
      - 443:443
    deploy:
      placement:
        constraints:
          # Make the traefik service run solely on the node with this label
          # because the node with it has the amount for the certificates
          - node.labels.traefik-public.traefik-public-certificates == true
      labels:
        # Enable Traefik for this service, to make it obtainable within the public community
        - traefik.allow=true
        # Use the traefik-public community (declared beneath)
        - traefik.docker.community=traefik-public
        # Use the customized label "traefik.constraint-label=traefik-public"
        # This public Traefik will solely use companies with this label
        # That means you'll be able to add different inside Traefik situations per stack if wanted
        - traefik.constraint-label=traefik-public
        # admin-auth middleware with HTTP Basic auth
        # Using the setting variables USERNAME and HASHED_PASSWORD
        - traefik.http.middlewares.admin-auth.basicauth.customers=${USERNAME?Variable not set}:${HASHED_PASSWORD?Variable not set}
        # https-redirect middleware to redirect HTTP to HTTPS
        # It may be re-used by different stacks in different Docker Compose recordsdata
        - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https
        - traefik.http.middlewares.https-redirect.redirectscheme.everlasting=true
        # traefik-http arrange solely to make use of the middleware to redirect to https
        # Uses the setting variable DOMAIN
        - traefik.http.routers.traefik-public-http.rule=Host(`${DOMAIN?Variable not set}`)
        - traefik.http.routers.traefik-public-http.entrypoints=http
        - traefik.http.routers.traefik-public-http.middlewares=https-redirect
        # traefik-https the precise router utilizing HTTPS
        # Uses the setting variable DOMAIN
        - traefik.http.routers.traefik-public-https.rule=Host(`${DOMAIN?Variable not set}`)
        - traefik.http.routers.traefik-public-https.entrypoints=https
        - traefik.http.routers.traefik-public-https.tls=true
        # Use the particular Traefik service api@inside with the net UI/Dashboard
        - traefik.http.routers.traefik-public-https.service=api@inside
        # Use the "le" (Let's Encrypt) resolver created beneath
        - traefik.http.routers.traefik-public-https.tls.certresolver=le
        # Enable HTTP Basic auth, utilizing the middleware created above
        - traefik.http.routers.traefik-public-https.middlewares=admin-auth
        # Define the port within the Docker service to make use of
        - traefik.http.companies.traefik-public.loadbalancer.server.port=8080
    volumes:
      # Add Docker as a mounted quantity, in order that Traefik can learn the labels of different companies
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # Mount the amount to retailer the certificates
      - traefik-public-certificates:/certificates
    command:
      # Enable Docker in Traefik, in order that it reads labels from Docker companies
      - --providers.docker
      # Add a constraint to solely use companies with the label "traefik.constraint-label=traefik-public"
      - --providers.docker.constraints=Label(`traefik.constraint-label`, `traefik-public`)
      # Do not expose all Docker companies, solely those explicitly uncovered
      - --providers.docker.exposedbydefault=false
      # Enable Docker Swarm mode
      - --providers.docker.swarmmode
      # Create an entrypoint "http" listening on deal with 80
      - --entrypoints.http.deal with=:80
      # Create an entrypoint "https" listening on deal with 80
      - --entrypoints.https.deal with=:443
      # Create the certificates resolver "le" for Let's Encrypt, makes use of the setting variable EMAIL
      - --certificatesresolvers.le.acme.e-mail=${EMAIL?Variable not set}
      # Store the Let's Encrypt certificates within the mounted quantity
      - --certificatesresolvers.le.acme.storage=/certificates/acme.json
      # Use the TLS Challenge for Let's Encrypt
      - --certificatesresolvers.le.acme.tlschallenge=true
      # Enable the entry log, with HTTP requests
      - --accesslog
      # Enable the Traefik log, for configurations and errors
      - --log
      # Enable the Dashboard and API
      - --api
    networks:
      # Use the general public community created to be shared between Traefik and
      # some other service that must be publicly obtainable with HTTPS
      - traefik-public

volumes:
  # Create a quantity to retailer the certificates, there's a constraint to verify
  # Traefik is all the time deployed to the identical Docker node with the identical quantity containing
  # the HTTPS certificates
  traefik-public-certificates:

networks:
  # Use the beforehand created public community "traefik-public", shared with different
  # companies that should be publicly obtainable by way of this Traefik
  traefik-public:
    exterior: true

 

When you may have the file in your server, cd to the file listing and use the next command to deploy a Docker Swarm stack.

docker stack deploy -c traefik.yml traefik

There is just one service on this stack. You can verify the standing of this service utilizing:

You will see one thing like beneath:

ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
moybzwb7mq15        traefik_traefik     replicated          1/1                 traefik:v2.2        *:80->80/tcp, *:443->443/tcp

It is known as as traefik_traefik as a result of it’s deployed right into a stack referred to as traefik and the service title can also be referred to as traefik. You can customise them when you like. Also, word that the REPLICAS variable reveals you the variety of copy of this service. ‘1/1’ means we would like just one copy and there may be one up and working. You can verify the log utilizing:

docker service logs traefik_traefik

A couple of minutes after deploying the stack, Traefik ought to arrange the SSL certificates on your web site utilizing Let’ Encrypt. You could discover that is a lot simpler and cleaner than my earlier answer. Now, try traefik.sys.app.instance.com. You ought to see the Traefik dashboard (use the username and password you simply set to log in).

Traefik Dashboard 1

Traefik is pretty sophisticated however we solely want to make use of a few of its capabilities so it’s nonetheless manageable. There are some key ideas of Traefik, which I’ve summarised beneath:

  • Providers: Discover the companies that stay on your infrastructure (their IP, well being, …). We are utilizing Docker right here.
  • Entrypoints: Listen for incoming site visitors (ports, …). We have the 80 and 443 open for HTTP and HTTPS site visitors.
  • Routers: Analyse the requests (host, path, headers, SSL, …). Currently, we solely route related to traefik.sys.app.instance.com. We can arrange different routers later.
  • Services: Forward the request to your companies (load balancing, …).
  • Middleware: May replace the request or make selections based mostly on the request (authentication, charge limiting, headers, …)

All the issues above may be created utilizing the instructions and labels within the traefik.yml file. For particulars, chances are you’ll need to verify the official Traefik documentation.

 

Step 5: Setting up the Streamlit GAN demo app stack

 
Now that we now have arrange the reverse proxy and load balancer, we will merely launch the demo Streamlit GAN app as a service within the Swarm. Please clone my GitHub repo.

The stapp.yml file is what we’d like. This yml file instructs Docker to arrange a service within the Swarm. Unlike containers, a service can have many replicas (containers from the identical picture) to verify the demand for the app may be met. These replicas will probably be deployed to completely different nodes of the Docker Swarm cluster and Traefik will make sure that they’re load-balanced. I’ve copied the stapp.yml file beneath and there’s something you need to know:

  1. There is just one service on this stack, which is named stapp.
  2. picture is ready to level to the Streamlit demo app uploaded to my Docker Hub repo. Using Docker Hub (or different related companies) to retailer photographs makes the deployment workflow a lot simpler. If you need to deploy your personal app, merely register a Docker Hub account and push the native picture to it. You can discover the fast begin information right here.
  3. The port for the stapp service is ready to 8501. This is as a result of after we create the picture, we set it to open this port as default. It could possibly be one thing else however we have to change the Dockerfile for the streamlit-demo picture accordingly. Note that the port beneath the labels additionally should be up to date.
  4. The overlay community traefik-public is utilized by the Traefik service, which can route the general public HTTP/HTTPS requests on the host (app.instance.com) to related companies.
  5. I commented out the - node.function==supervisor beneath the placement: constraints:. Because this service is the app itself, we don’t have to restrict it to the supervisor node. When we launch new employees, we would like the load balancer to work and deploy the replicas of the app service to the brand new employees. I truly choose to set it as much as solely deploy on employee nodes and let the supervisor node to deal with simply the Traefik stack. In this manner, we will keep away from the supervisor being overloaded with app requests. To do this, it’s essential set it to - node.function==employee.
  6. APP_DOMAIN is what we arrange earlier (e.g. app.instance.com). If you need to host a number of apps you can arrange completely different domains (e.g. app1.instance.comapp2.instance.com). You then have to arrange a router for every app and specify the area within the labels part of that service (e.g. changing APP_DOMAIN). Traefik will route the guests to completely different app companies based mostly on the area specified.
  7. Note that replicas has been set as much as one. You could need to tweak it to seek out the fitting quantity given your Swarm cluster and the demand. Of course, you’ll be able to scale it up and down afterwards.
model: '3.3'

companies:

  stapp:
    picture: presstofan/streamlit-demo
    # The labels part is the place you specify configuration values for Traefik.
    # Docker labels don’t do something by themselves, however Traefik reads these so
    # it is aware of methods to deal with containers.
    ports:
      - 8501
    networks:
      - traefik-public
    deploy:
      replicas: 1
      restart_policy:
        situation: on-failure
      # placement:
      #   constraints:
      #     - node.function==supervisor
      labels:
          - traefik.allow=true # allow traefik
          - traefik.docker.community=traefik-public # put it in the identical community as traefik
          - traefik.constraint-label=traefik-public # assign the identical label as traefik so it may be found
          - traefik.http.routers.stapp.rule=Host(`${APP_DOMAIN?Variable not set}`) # hearken to port 80 for request to APP_DOMAIN (use collectively with the road beneath)
          - traefik.http.routers.stapp.entrypoints=http
          - traefik.http.middlewares.stapp.redirectscheme.scheme=https # redirect site visitors to https
          - traefik.http.middlewares.stapp.redirectscheme.everlasting=true # redirect site visitors to https
          - traefik.http.routers.stapp-secured.rule=Host(`${APP_DOMAIN?Variable not set}`) # hearken to port 443 for request to APP_DOMAIN (use collectively with the road beneath)
          - traefik.http.routers.stapp-secured.entrypoints=https
          - traefik.http.routers.stapp-secured.tls.certresolver=le # use the Let's Encrypt certificates we arrange earlier
          - traefik.http.companies.stapp-secured.loadbalancer.server.port=8501 # ask Traefik to seek for port 8501 of the stapp service container
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

networks:
  traefik-public:
    exterior: true

 

Before deploying the app service, let’s arrange the setting variable APP_DOMAIN. This ought to be the area of your app you arrange earlier with your DNS supplier (e.g. app.instance.com). When customers visiting this area, Traefik will route it to the app service.

export APP_DOMAIN=app.instance.com

Then, let’s deploy the service with the stapp.yml file and name it app.

docker stack deploy -c stapp.yml app

We can verify the standing of the service utilizing:

You will see the next companies (it may possibly take a couple of minutes to obtain the pictures for the primary time):

ID                  NAME                MODE                REPLICAS            IMAGE                              PORTS
leqx49lfktgl        app_stapp           replicated          1/1                 presstofan/streamlit-demo:newest   *:30000->8501/tcp
2eu098a9usi6        traefik_traefik     replicated          1/1                 traefik:v2.2                       *:80->80/tcp, *:443->443/tcp

Give it a minute and verify app.instance.com and you will note the streamlit-demo app. If you go to the Traefik dashboard, now you can discover the extra router, service and middleware associated to the Streamlit app.

This is just one reproduction of this service so customers will share the useful resource from the identical container. We can scale up the variety of replicas:

docker service replace app_stapp --replicas 2

Now when you run:

You will see two containers:

CONTAINER ID        NAME                                          CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
847576f23b02        app_stapp.2.tcwdyysex8sh346zncbor5m3l         2.09%               580.9MiB / 3.797GiB   14.94%              187MB / 819kB       11.5MB / 0B         21
9017e6efae0d        app_stapp.1.qs5nv8w6pl2vy26ugmmf9g18p         2.36%               1.133GiB / 3.797GiB   29.82%              187MB / 7.13MB      12MB / 0B           21
552b874cbe5d        traefik_traefik.1.34r1hky0knrxw6eglwosby1zw   0.04%               15.27MiB / 3.797GiB   0.39%               7.02MB / 9.5MB      213kB / 45.1kB      8

And now when you go to app.instance.com, you may be routed to one of many two copies of the app.

There are many issues we will tweak utilizing the service replace, equivalent to CPU and reminiscence restrict. Please see right here for particulars.

 

Step 6: Scaling your Swarm cluster

 
Now involves the attention-grabbing half. Let’s say in case your app is getting common and you need to launch a further server to share the workload. You can simply add nodes to your Swarm.

First, we have to launch one other AWS occasion (or server from different Clouds). Repeat the steps above however this time we need to use the safety group settings for Swarm Workers.

Image

 

SSH into the brand new occasion and set up Docker Engine as above. When that’s finished, we have to be part of the Swarm we arrange and the be part of token we acquired from the Manager node would come in useful. Run it within the new occasion. The token would appear to be one thing beneath:

docker swarm be part of --token SWMTKN-1-xxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxx 172.x.x.x:2377

If profitable, you’d see the message ‘This node joined a swarm as a worker.’ If you now change to the Manager node shell and run:

You would see:

ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
5b6py13brdoihrcct9oy68wpq *   ip-172-31-49-53     Ready               Active              Leader              19.03.10
3j3ecsf4fhz3cwhu98w5cv1bo     ip-172-31-71-189    Ready               Active                                  19.03.10

Congratulations! You have simply created your two-node Swarm. Traefik solely will get deployed on the Manager node as we instructed however the the Streamlit app may be deployed to employee nodes (or solely on the employee node when you specified so within the yml file). Swarm will handle the load mechanically. You can use docker service ps SERVICE_NAME to verify the node the service is on. Note that the primary time you deploy a service to a node it’s going to take a while to obtain the pictures. But it gained’t be an issue later on because it ought to have the pictures cached. For instance, you might even see the 2 containers of the app are working on completely different nodes as proven beneath:

ID                  NAME                IMAGE                              NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
m4rb4xf1lzth        app_stapp.1         presstofan/streamlit-demo:newest   ip-172-31-3-100     Running             Running 13 minutes in the past
bf6ibptukrsz        app_stapp.2         presstofan/streamlit-demo:newest   ip-193-21-6-300     Running             Running Eight minutes in the past

Choosing the variety of nodes

Here are some notes on selecting the variety of nodes. In principle, you’ll be able to arrange multiple Manager nodes (e.g. three and even 5). However, the group version of Traefik gained’t help distributed Let’s Encrypt certificates, which means that solely one of many node may be your gateway and your DNS have to level the area to that node. If you might be constructing a fancy app that requires HA, chances are you’ll need to contemplate Traefik Enterprise Edition. For many use circumstances, making your major Manager node sufficiently highly effective (e.g. at the least 2 GiB of reminiscence, preferable Four GiB) and offloading Shiny apps to the employees could be adequate. There is much less of a constraint in selecting the variety of employee nodes (though ideally a good quantity) and the specs rely on the app you serve.

Rebalancing nodes

When you add a brand new node to a Swarm or a node reconnects to the Swarm after a interval of unavailability, the Swarm doesn’t mechanically give a workload to the idle nodes. According to Docker, this can be a design choice.

If the swarm periodically shifted duties to completely different nodes for the sake of stability, the purchasers utilizing these duties could be disrupted. The purpose is to keep away from disrupting working companies for the sake of stability throughout the swarm. When new duties begin, or when a node with working duties turns into unavailable, these duties are given to much less busy nodes. The purpose is eventual stability, with minimal disruption to the tip person.

If wanted, we may pressure the nodes to rebalance through the use of the command beneath:

docker service replace --force app_stapp

This is fairly useful when you have simply added or eliminated many nodes. However, the replace causes service duties to restart. Client functions could also be disrupted.

 

(Optional) Step 7: Adding a Keycloak authentication layer

 
Sometimes we could need to shield the app behind a person authentication layer. This may be achieved by way of many authentication suppliers. In this tutorial, I will probably be utilizing Keycloak, an open-source Identity and Access Management answer. I’ve beforehand proven how one can add authentication to ShinyProxy utilizing AWS Cognito. Using Keycloak will give us extra management. In a nutshell, we’ll create a Keycloak server which is used for managing customers and offering authentication. We will then add an authentication shopper in entrance of our Streamlit app. Ideally, the Keycloak server and the app ought to be hosted by completely different AWS situations for safety and reliability however for the sake of this tutorial, we’ll deploy each of them on the identical occasion.

Setting up Keycloak server

First, we have to create one other DNS report for the Keycloak console.

 

Then retailer the area title for later use:

export KEYCLOAK_DOMAIN=keycloak.sys.app.instance.com

The following Keycloak setup is essentially based mostly on this excellent recipe. I’ve modified it to work with the prevailing Traefik stack. First, let’s create two folders which we’ll later use to retailer the Keycloak database and backups.

sudo mkdir -p /var/knowledge/runtime/keycloak/database
sudo mkdir -p /var/knowledge/keycloak/database-dump

Clone the tutorial repo. Note that you could have already finished this step when you have adopted the primary a part of this tutorial. If that’s the case, merely cd into the listing.

In this listing, you can see the yml file for the Keycloak stack.

model: '3'

companies:
  keycloak:
    picture: jboss/keycloak
    depends_on:
      - keycloak-db
    setting:
      - DB_VENDOR=postgres
      - DB_DATABASE=keycloak
      - DB_ADDR=keycloak-db
      - DB_USER=keycloak
      - DB_PASSWORD=changeme
      - KEYCLOAK_USER=admin
      - KEYCLOAK_PASSWORD=changeme
      - PROXY_ADDRESS_FORWARDING=true # This is required to run keycloak behind traefik
      - KEYCLOAK_HOSTNAME=${KEYCLOAK_DOMAIN?Variable not set}
      - POSTGRES_USER=keycloak
      - POSTGRES_PASSWORD=changeme
    volumes:
      - /and so on/localtime:/and so on/localtime:ro
    networks:
      - traefik-public
      - keycloak-internal
    deploy:
      labels:
        - traefik.allow=true # allow traefik
        - traefik.docker.community=traefik-public # put it in the identical community as traefik
        - traefik.constraint-label=traefik-public # assign the identical label as traefik so it may be found
        - traefik.http.routers.keycloak.rule=Host(`${KEYCLOAK_DOMAIN?Variable not set}`) # hearken to port 80 for request to KEYCLOAK_DOMAIN (use collectively with the road beneath)
        - traefik.http.routers.keycloak.entrypoints=http
        - traefik.http.middlewares.keycloak-https-redirect.redirectscheme.scheme=https # redirect site visitors to https
        - traefik.http.middlewares.keycloak-https-redirect.redirectscheme.everlasting=true # redirect site visitors to https
        - traefik.http.routers.keycloak-secured.rule=Host(`${KEYCLOAK_DOMAIN?Variable not set}`) # hearken to port 443 for request to KEYCLOAK_DOMAIN (use collectively with the road beneath)
        - traefik.http.routers.keycloak-secured.entrypoints=https
        - traefik.http.routers.keycloak-secured.tls.certresolver=le # use the Let's Encrypt certificates we arrange earlier
        - traefik.http.companies.keycloak-secured.loadbalancer.server.port=8080 # ask Traefik to seek for port 8080 of the Keycloak service container

  keycloak-db:
    picture: postgres:10.1
    setting:
      - DB_VENDOR=postgres
      - DB_DATABASE=keycloak
      - DB_ADDR=keycloak-db
      - DB_USER=keycloak
      - DB_PASSWORD=changeme
      - KEYCLOAK_USER=admin
      - KEYCLOAK_PASSWORD=changeme
      - PROXY_ADDRESS_FORWARDING=true # This is required to run keycloak behind traefik
      - KEYCLOAK_HOSTNAME=${KEYCLOAK_DOMAIN?Variable not set}
      - POSTGRES_USER=keycloak
      - POSTGRES_PASSWORD=changeme
      - KEYCLOAK_LOGLEVEL=WARNING
    volumes:
      - /var/knowledge/runtime/keycloak/database:/var/lib/postgresql/knowledge
      - /and so on/localtime:/and so on/localtime:ro
    networks:
      - keycloak-internal

  keycloak-db-backup:
    picture: postgres:10.1
    setting:
      - PGHOST=keycloak-db
      - PGUSER=keycloak
      - PGPASSWORD=changeme
      - BACKUP_NUM_KEEP=7
      - BACKUP_FREQUENCY=1d
    volumes:
      - /var/knowledge/keycloak/database-dump:/dump
      - /and so on/localtime:/and so on/localtime:ro
    entrypoint: |
      bash -c 'bash -s  /dump/dump_`date +%d-%m-%Y"_"%H_percentM_percentS`.psql
        (ls -t /dump/dump*.psql|head -n $$BACKUP_NUM_KEEP;ls /dump/dump*.psql)|type|uniq -u|xargs rm -- {}
        sleep $$BACKUP_FREQUENCY
      finished
      EOF'
    networks:
      - keycloak-internal

networks:
  traefik-public:
    exterior: true
  keycloak-internal:
    driver: overlay
    ipam:
      config:
        # Setup distinctive static subnets for each stack you deploy. 
        # This avoids IP/gateway conflicts which may in any other case happen while you're creating/eradicating stacks quite a bit.
        - subnet: 172.16.49.0/24

 

Let’s deploy the Keycloak server service with the keycloak.yml file and name it keycloak.

docker stack deploy -c keycloak.yml keycloak

We can verify the standing of the service utilizing:

You will see two extra Keycloak companies (once more, it may possibly take a couple of minutes to obtain the pictures for the primary time):

ID                  NAME                          MODE                REPLICAS            IMAGE                   PORTS
xn43fgqs596d        keycloak_keycloak             replicated          1/1                 jboss/keycloak:newest
w3j4rmuwgk2p        keycloak_keycloak-db          replicated          1/1                 postgres:10.1
zmqu2zon25p1        keycloak_keycloak-db-backup   replicated          1/1                 postgres:10.1
2eu098a9usi6        traefik_traefik               replicated          1/1                 traefik:v2.2            *:80->80/tcp, *:443->443/tcp

Give it a minute and verify keycloak.sys.app.instance.com and you will note the Keycloak Welcome web page. Log into the Administrative Console with the username and password specified within the docker-compose file. The default username and password are “admin” and “changeme” (when you haven’t modified the yml file).

Keycloak Login

Setting up Keycloak shopper and person

First, let’s create a brand new shopper for the Streamlit app in Keycloak. In the Master realm, click on Clients and then Create.

Keycloak Create Client 1

We can name it stapp and use the openid-connect protocol.

Keycloak Create Client 2

After hitting save, we have to change the Access Type to confidential and add https://app.instance.com/* to Valid Redirect URIs. This is the place you may be directed to after efficiently logged in. Don’t overlook to alter it your personal area. Save it once more and go to the Credential tab and word down the Secret.

Keycloak Create Client 3

Now, we have to arrange a person for testing. click on Users and then Add User.

Keycloak Create User 1

Let’s give it a reputation referred to as take a look at. After saving it, go to the Credential tab and set the password to “changeme” and save once more.

Keycloak Create User 1

Setting up Traefik Forward Auth

We will arrange a Traefik Forward Auth service to care for the authentication course of. The person move is like this: A person who go to app.instance.com for the primary time get redirected to the Traefik Forward Auth service, which can ship the person to Keycloak for authentication. Once the person has logged in, she/he will probably be redirected again to Traefik Forward Auth service and it’s going to verify the authentication code. If profitable, the person will probably be redirected to the app service.

First, we have to take down the prevailing app stack (which doesn’t require authentication).

In the python-app-docker-swarm-stack folder, there may be one other file referred to as stapp-auth.yml, which can assist us to arrange the brand new app stack bundling the Streamlit app and the Traefik Forward Auth service.

model: '3.3'

companies:
  traefik-forward-auth:
    picture: thomseddon/traefik-forward-auth:2
    setting:
      - DEFAULT_PROVIDER=oidc
      - PROVIDERS_OIDC_ISSUER_URL=https://${KEYCLOAK_DOMAIN?Variable not set}/auth/realms/grasp
      - PROVIDERS_OIDC_CLIENT_ID=${AUTH_CLIENT_ID?Variable not set}
      - PROVIDERS_OIDC_CLIENT_SECRET=${AUTH_CLIENT_SECRET?Variable not set}
      - SECRET=change_to_a_random_string
      # INSECURE_COOKIE is required if not utilizing a https entrypoint
      # - INSECURE_COOKIE=true
      - LOG_LEVEL=debug
    networks:
      - traefik-public
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
           - node.function==supervisor
      labels:
        - "traefik.enable=true" # allow traefik
        - "traefik.docker.network=traefik-public" # put it in the identical community as traefik
        - "traefik.constraint-label=traefik-public" # assign the identical label as traefik so it may be found
        - "traefik.http.middlewares.traefik-forward-auth.forwardauth.address=http://traefik-forward-auth:4181"
        - "traefik.http.middlewares.traefik-forward-auth.forwardauth.authResponseHeaders=X-Forwarded-User"
        - "traefik.http.services.traefik-forward-auth.loadbalancer.server.port=4181"
        # obtain the authentication outcomes from Keycloak
        - "traefik.http.routers.forward-auth.rule=Host(`auth.${APP_DOMAIN?Variable not set}`)"
        - "traefik.http.routers.forward-auth.service=traefik-forward-auth"

  stapp:
    picture: presstofan/streamlit-demo
    depends_on:
      - traefik-forward-auth
    ports:
      - 8501
    networks:
      - traefik-public
    deploy:
      replicas: 1
      restart_policy:
        situation: on-failure
      # Use beneath if solely place the app on employees
      # placement:
      #   constraints:
      #     - node.function==employee
      labels:
        - "traefik.enable=true" # allow traefik
        - "traefik.docker.network=traefik-public" # put it in the identical community as traefik
        - "traefik.constraint-label=traefik-public" # assign the identical label as traefik so it may be found
        - "traefik.http.routers.stapp.rule=Host(`${APP_DOMAIN?Variable not set}`)"
        - "traefik.http.routers.stapp.entrypoints=http"
        # redirect HTTP to HTTPS and add SSL certificates
        - "traefik.http.middlewares.stapp-https-redirect.redirectscheme.scheme=https" # redirect site visitors to https
        - "traefik.http.middlewares.stapp-https-redirect.redirectscheme.permanent=true" # redirect site visitors to https
        - "traefik.http.routers.stapp-secured.rule=Host(`${APP_DOMAIN?Variable not set}`)" # hearken to port 443 for request to APP_DOMAIN (use collectively with the road beneath)
        - "traefik.http.routers.stapp-secured.entrypoints=https"
        - "traefik.http.routers.stapp-secured.tls.certresolver=le" # use the Let's Encrypt certificates we arrange earlier
        # assign app service
        - "traefik.http.routers.stapp-secured.service=stapp"
        - "traefik.http.routers.stapp-secured.middlewares=stapp-auth@docker"
        # redirect to Keycloak for authentication
        - "traefik.http.middlewares.stapp-auth.forwardauth.address=http://traefik-forward-auth:4181"
        - "traefik.http.middlewares.stapp-auth.forwardauth.authresponseheaders=X-Forwarded-User"
        - "traefik.http.services.stapp.loadbalancer.server.port=8501"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

networks:
  traefik-public:
    exterior: true

 

Before deploying it, we have to arrange a few setting variables (the shopper id and secret) which the yml refers to. Please change the key beneath to your personal.

export AUTH_CLIENT_ID=stapp
export AUTH_CLIENT_SECRET=77b5cf3b-4054-407f-b9d6-ba2c18cc5b70

Now, let’s run:

docker stack deploy -c stapp-auth.yml app

Give it a minute and then go to app.instance.com. You will see the Keycloak login web page. Once logged in, you may be directed to the Streamlit app. Note you could additionally log in with the admin account we created for Keycloak, however you’ll be able to’t use the take a look at account to log within the Keycloak Administrative Console. This is set by the function mapping of every person. There are many issues you are able to do with Keycloak. For instance, you’ll be able to enable person self registration, which is a helpful characteristic for some apps. Check the official doc if you’re .

 

(Optional) Step 8: Monitoring Docker Swarm with Swarmpit

 
Swarmpit offers easy and straightforward to make use of interface on your Docker Swarm cluster. You can handle your stacks, companies, secrets and techniques, volumes, networks and so on. To set this up, we first have to create one other “A” report with our DNS supplier to level to the Manager node IP deal with.

 

The technique of deploying the Swarmpit stack is similar to how we deployed the Traefik stack.

Set up the setting variables:

export DOMAIN=swarmpit.sys.app.instance.com
export NODE_ID=$(docker information -f '{{.Swarm.NodeID}}')

Create a label on this node, in order that the CouchDB database utilized by Swarmpit is all the time deployed to the identical node and makes use of the prevailing quantity:

docker node replace --label-add swarmpit.db-data=true $NODE_ID

Create one other label on this node, in order that the Influx database utilized by Swarmpit is all the time deployed to the identical node and makes use of the prevailing quantity:

docker node replace --label-add swarmpit.influx-data=true $NODE_ID

Download the swarmpit.yml

curl -L dockerswarm.rocks/swarmpit.yml -o swarmpit.yml

Or create one within the Manager node your self with the template beneath:

model: '3.3'

companies:
  app:
    picture: swarmpit/swarmpit:newest
    setting:
      - SWARMPIT_DB=http://db:5984
      - SWARMPIT_INFLUXDB=http://influxdb:8086
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    ports:
      - 888:8080
    networks:
      - web
      - traefik-public
    deploy:
      sources:
        limits:
          cpus: '0.50'
          reminiscence: 1024M
        reservations:
          cpus: '0.25'
          reminiscence: 512M
      placement:
        constraints:
          - node.function == supervisor
      labels:
        - traefik.allow=true
        - traefik.docker.community=traefik-public
        - traefik.constraint-label=traefik-public
        - traefik.http.routers.swarmpit-http.rule=Host(`${DOMAIN?Variable not set}`)
        - traefik.http.routers.swarmpit-http.entrypoints=http
        - traefik.http.routers.swarmpit-http.middlewares=https-redirect
        - traefik.http.routers.swarmpit-https.rule=Host(`${DOMAIN?Variable not set}`)
        - traefik.http.routers.swarmpit-https.entrypoints=https
        - traefik.http.routers.swarmpit-https.tls=true
        - traefik.http.routers.swarmpit-https.tls.certresolver=le
        - traefik.http.companies.swarmpit.loadbalancer.server.port=8080

  db:
    picture: couchdb:2.3.0
    volumes:
      - db-data:/choose/couchdb/knowledge
    networks:
      - web
    deploy:
      sources:
        limits:
          cpus: '0.30'
          reminiscence: 512M
        reservations:
          cpus: '0.15'
          reminiscence: 256M
      placement:
        constraints:
          - node.labels.swarmpit.db-data == true
  influxdb:
    picture: influxdb:1.7
    volumes:
      - influx-data:/var/lib/influxdb
    networks:
      - web
    deploy:
      sources:
        reservations:
          cpus: '0.3'
          reminiscence: 128M
        limits:
          cpus: '0.6'
          reminiscence: 512M
      placement:
        constraints:
          - node.labels.swarmpit.influx-data == true
  agent:
    picture: swarmpit/agent:newest
    setting:
      - DOCKER_API_VERSION=1.35
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - web
    deploy:
      mode: world
      sources:
        limits:
          cpus: '0.10'
          reminiscence: 64M
        reservations:
          cpus: '0.05'
          reminiscence: 32M

networks:
  web:
    driver: overlay
    attachable: true
  traefik-public:
    exterior: true

volumes:
  db-data:
    driver: native
  influx-data:
    driver: native

 

When prepared, deploy the stack utilizing:

docker stack deploy -c swarmpit.yml swarmpit

Check whether it is working:

It will present one thing like beneath:

ID             NAME                       IMAGE                      NODE                DESIRED STATE   CURRENT STATE          ERROR   PORT
kkhasdfvce30   swarmpit_agent.ndasdfav5   swarmpit/agent:newest      canine.instance.com     Running         Running Three minutes in the past
k8oasdfg70jm   swarmpit_agent.i9asdfjps   swarmpit/agent:newest      cat.instance.com     Running         Running Three minutes in the past
kcvasdft0yzj   swarmpit_agent.3jasdfd3k   swarmpit/agent:newest      snake.instance.com   Running         Running Three minutes in the past
9onasdfzopve   swarmpit_agent.r6asdfb20   swarmpit/agent:newest      snake.instance.com   Running         Running Three minutes in the past
fxoasdfwjrbj   swarmpit_db.1              couchdb:2.3.0              canine.instance.com     Running         Running Three minutes in the past
m4jasdf3369c   swarmpit_app.1             swarmpit/swarmpit:newest   cat.instance.com     Running         Running Three minutes in the past

Finally, give it a minute or two and go to swarmpit.sys.app.instance.com to entry the dashboard. You will probably be prompted to arrange an account the primary time you utilize it.

Swarmpit

 

Next Steps

 
That concludes this tutorial. We ought to have a scalable Streamlit app served by Docker Swarm with a secured host, authentication service and monitoring functionality. Using Docker Swarm and Traefik makes our lives a lot simpler and the deployment ought to be future-proofed. One factor that’s good to have is the flexibility to elastically scale the app, which means that new AWS EC2 situations will probably be mechanically deployed and be part of the Swarm cluster in response to the app demand. This proves to be a non-trivial activity and I’ll proceed to discover it sooner or later. But please submit a remark beneath when you occur to know a great way of attaining it!

 
Bio: Yihui Fan works as a data scientist at a administration consultancy agency. Yihui enjoys coding in R and Python, constructing knowledge merchandise and visualisation instruments and can also be a deep studying fanatic.

Original. Reposted with permission.

Related:

[ad_2]

Source hyperlink

Write a comment