More than one user per container?

Hellow, everyone!

Have you ever thought about making some treshold for starting new container for every N users? F.e. 5 users work in one container, but if we have new user (number 6) we will start new container for him/her and next 4 new users will join in this container…and so on… It will be looks like some kind of flexible shiny server…

This is actually a topic I wanted to discuss for a long time. In my opinion, ShinyProxy is intentionally limiting one user for one app(one container). This is first to enable the user authentication that free version Shiny Server doesn’t have and second to make different users be able to run the same app on concurrently. That said, the current solution is pretty neat.

However, while I’m trying to deploy all the apps to a Docker Swarm as a stack. It appears to me that if we deploy each app as a service with a certain amount of replicas, then we will be able to use the Swarm to do load balancing. We only need to scale each app individually when the demand changes. What Shinyproxy needs to do, is to proxy users to the certain app after authentication(an app will have a dedicated port) and pass in the user information as maybe URL parameters rather than environment vars(I don’t know if this is doable). Swarm will load balance the request behind Shinyproxy.

If your app doesn’t need authentication, then you can just do it yourself without even using ShinyProxy, just use a single docker-compose.yml file and add each app as a service with a preferred replica settings. You can decide if you need a proxy or not.

2 Likes

Thank you. But when I use docker compose it does not works. nginx load balancing cant find js and css files(
my yaml file
nginx:
build: ./nginx
links:
- shinyserv1:shinyserv1
- shinyserv2:shinyserv2
- shinyserv3:shinyserv3
ports:
- "80:80"
shinyserv1:
image: kuzmenkov/shinyserv
volumes:
- /srv/shiny-server/:/srv/shiny-server/
ports:
- "3838"
shinyserv2:
image: kuzmenkov/shinyserv
volumes:
- /srv/shiny-server2/:/srv/shiny-server/
ports:
- "3838"
shinyserv3:
image: kuzmenkov/shinyserv
volumes:
- /srv/shiny-server1/:/srv/shiny-server/
ports:
- “3838”

and nginx.conf
worker_processes 4;

events { worker_connections 1024; }

http {

upstream node-app {
      least_conn;
      server shinyserv1:3838 weight=10 max_fails=3 fail_timeout=30s;
      server shinyserv2:3838 weight=10 max_fails=3 fail_timeout=30s;
      server shinyserv3:3838 weight=10 max_fails=3 fail_timeout=30s;
}
 
server {
      listen 80;
 
      location / {
        proxy_pass http://node-app;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
      }
}

}

Do you have any idea?

Do these apps work when you don’t have nginx before them?

Thank you for response!
Yes, they works. I bet, problem is in nginx configuration. I tried to run two containers (one on 3838, second on 3839). And then:

1) ran with nginx on local host (nginx not in container).
configuration file:
upstream myapp1 {
server localhost:3838;
server localhost:3839;
}

server {
listen 80;
#server_name myapp.com www.myapp.com;
location / {
proxy_pass http://myapp1;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 90;
}
}
Result : cant load js and css files (all interactive maps and plots doesnt work and there are errors in Chrome console- can t GET …js or css)

2) stop nginx on local host and tried to get direct access to my_ip:3838 and my_ip:3839
Result: all good in both cases (all js and css are loaded, all maps and plots are working)

Maby you have nginx config for load balancing your app? Especially if this app works with leaflet map or highcharter library?

I’m sorry I can’t tell what is wrong, but I will try your setting in my environment to see if it works.

1 Like

@Alexey_Kuzmrnkov

In my case, I run Nginx as containers as well. Nginx is used only for reverse proxying. The load balancing should be handled by the Swarm gracefully.

Here is my docker-compose file

version: "3.1"
services:

  shiny-1:
    image: keqiangli/shiny-swarm
    ports:
      - "3838"
    deploy:
      replicas: 2
      restart_policy:
        condition: on-failure

  shiny-2:
    image: keqiangli/shiny-swarm
    ports:
      - "3838"
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure

  proxy-server:
    image: keqiangli/reverseproxy
    ports:
      - "8082:8082"
      - "8081:8081"
    deploy:
      placement:
        constraints: [node.role == manager]
      replicas: 1
      restart_policy:
        condition: on-failure

  visualizer:
    image: dockersamples/visualizer:stable
    ports:
      - "8080:8080"
    stop_grace_period: 1m30s
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]

And my Nginx is configured as below:

worker_processes 1;

events { worker_connections 1024; }

http {

    sendfile on;

    map $http_upgrade $connection_upgrade {
      default upgrade;
      ''      close;
    }

    upstream shiny-1 {
        server shiny-1:3838;
    }

    upstream shiny-2 {
        server shiny-2:3838;
    }

    server {
        listen 8082;

        location / {
            proxy_pass         http://shiny-1;
            proxy_redirect     http://shiny-1/ $scheme://$host/;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
	    proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_read_timeout 20d;
            proxy_buffering off;
        }
    }

    server {
        listen 8081;

        location / {
            proxy_pass         http://shiny-2;
            proxy_redirect     http://shiny-2/ $scheme://$host/;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
	    proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_read_timeout 20d;
            proxy_buffering off;
        }
    }

}

The Docker images used in this example are all very simple. The Shiny application is just the demo app plus debug information showing which swarm host runs the application. The reverseproxy image is only from nginx:alpine with the customized nginx.conf file.

1 Like

Thank you very much!!! I will try to reproduce your example!

It great solution for swarm cluster! But I am trying to run on one server two containers behind nginx on that server. And nginx is load balancer. I have the same problem((( May you try this kind of example?
Nginx.conf

#user www-data;

worker_processes 4;
#pid /run/nginx.pid;

events {
worker_connections 768;
# multi_accept on;
}

http {

sendfile on;

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

upstream myapp1 {
server localhost:3838;
server localhost:3839;
}

server {
listen 80;

  location / {
      proxy_pass         http://myapp1;
        proxy_redirect     http://myapp1/ $scheme://$host/;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Host $server_name;
  proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 20d;
        proxy_buffering off;
  }

}
}

If I change the replica settings in the docker compose file to 1, then it’s basically identical to what you are looking for. You just need to change the nginx.conf accordingly.

docker-compose.yml (If not on docker swarm, then just use docker-compose command, which will ignore some of the swarm specific settings). Although the two applications are named shiny-1 and shiny-2, they are using the same docker image hence they are the same application and also they run on the same host if you use docker-compose.

version: "3.1"
services:

  shiny-1:
    image: keqiangli/shiny-swarm
    ports:
      - "3838"
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure

  shiny-2:
    image: keqiangli/shiny-swarm
    ports:
      - "3838"
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure

  proxy-server:
    image: keqiangli/reverseproxy
    ports:
      - "80:80"
    deploy:
      placement:
        constraints: [node.role == manager]
      replicas: 1
      restart_policy:
        condition: on-failure

  visualizer:
    image: dockersamples/visualizer:stable
    ports:
      - "8080:8080"
    stop_grace_period: 1m30s
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]

nginx.conf, I changed to use nginx to do the load balancing here and used ‘shiny-1’, ‘shiny-2’, the two service name as the server name here.

worker_processes 1;

events { worker_connections 1024; }

http {

    sendfile on;

    map $http_upgrade $connection_upgrade {
      default upgrade;
      ''      close;
    }

    upstream myapp1 {
        server shiny-1:3838;
        server shiny-2:3838;
    }

    server {
        listen 80;

        location / {
            proxy_pass         http://myapp1;
            proxy_redirect     http://myapp1/ $scheme://$host/;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_read_timeout 20d;
            proxy_buffering off;
        }
    }
}

keqiangli/reverseproxy Dockerfile

FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf

If all these settings are used to run the services on the same host, then it meets all you requirements, and even better since it will recover the shiny applications when fail, and can be easily adapted to a Swarm environment as well.

1 Like

Thank you! It is works! Sumptuously!

Some additional comment. Plus I had a problem, because of I tried to mout local volume to container. It is better to COPY all files for app into container in DOCKERFILE, and not mout volumes.

If you want your data to be persisted, then you should mount volumes instead of copying files. Containers are designed to be ephemeral.

1 Like

I mount volume-now it looks better.
Sorry for my impudence, but I think I have the same problem(((
load balancing works well.
But GET request in browser has 404 error (f.e.GET http://my_ip/htmlwidgets-0.9/htmlwidgets.js 404 error).
And the same situations take place for other js files.
It is interesting, that if I try to make this request directly in browser window, I will get strange (unexpected) behavior: first attempt - OK (I get js file), second attempt - 404 error, 3rd attempt -OK, 4th attempt - 404 error…and so on…

I found on stackoverflow similar problem, but I can’t find out how to fix it for load balancing shiny (https://stackoverflow.com/questions/42599887/cant-get-the-data-from-a-post-request-in-a-node-app-behind-a-nginx-reverse-prox).

Have you ever got this kind of problem (strange GET behavior) when balancing shiny app with f.e. leaflet map?

I really do not now what to do. It is really random process

I’m sorry I don’t why you are getting this error. I need something that I can reproduce to be able to figure it out. If you can make some public docker images containing your app and a minimal reproducible example would be helpful.

Ok! I’ will prepeare container with my code. Thank you!

Hi! I have prepared repo with test app and description on GitHub

I don’t think I can help without a testing app. why not just copy your app to the docker image? I don’t know how to reproduce without your test app.

Sorry, I forgot add test app. Now it is here GitHub

I managed to reproduce it but didn’t find a solution. I’m guessing the error might be caused by Shiny server but when the replica is <=2, it works fine. You might need to seek help from Shiny community.

Thank you! It is good news for me, because it reproducible!