Pre-initialise application container

So we currently have Shinyproxy set up with a few applications in production, big thanks to the Open Analytics team, a point users complain to me about is waiting for the application to start up after they click on the link, which takes about
I’m wondering is there a way have a container that is already created and waiting so that when a user clicks on the link they get directed to that standby container?

1 Like

Hi @akenny,

Container startup can indeed take multiple seconds, depending on a number of factors.
This is an area where we are also considering alternatives to the current ‘on-demand’ launching approach.
However, keep in mind that shiny containers are launched for one specific shiny app, which is specified in the container’s startup cmd.

The API introduced in version 2.0 offers some options in this regard:

If you are running shinyproxy in the context of a larger application, you can use the API to spawn a container at any time, for example when a user logs in in your application’s portal.

If you are running shinyproxy ‘standalone’, you could achieve the same with a custom template: in the index page (which is where the user lands after login), an ajax call could be made to the API endpoint:

/app_direct/<appname>

This will ensure that a container is spawned for the specified app. If the user then clicks on the app link, they will immediately get routed to the running container, instead of spawning a new one.

Note: if heartbeat is enabled (by default, it is), this container will automatically close unless the user interacts with it before the heartbeat-timeout expires.

Hi @fmichielssen-
My docker image is about 2.5GB, when using

/app/{appname}

and clicking the link, it takes about 20 seconds to spin up the container and load the application.

I found it a bit too long - is it expected or are we doing something wrong with our setup?
Thanks for your opinion, Dylan

Hi @Dylan_Cissou,

I believe docker image size is not directly related to startup time, though there may be a link in some cases.
You can check which components of your image take up the most space, using this command:

 docker history <image-name>

E.g.

docker history openanalytics/shinyproxy-demo

Usually you’ll see some large chunks taken by apt-get install or yum install commands.

Regarding startup time, here are some of my thoughts:

  • Linux-based images can start up very fast; often the bottleneck is the container’s startup command, given by either the CMD line in the Dockerfile, or the container-cmd setting in ShinyProxy’s application.yml file.

  • In the case of ShinyProxy, the startup command is nearly always R which in turn launches an embedded HTTP server to serve the Shiny app.

  • ShinyProxy will consider the container to be ‘ready’ as soon as its R process becomes responsive to HTTP requests.

  • In Kubernetes, there may be an additional overhead to create a Service object that publishes the ports outside the Kube cluster (see this example for more details).

If you run the Shiny app in a regular R process (not dockerized), do you get similar startup times?

Hi @fmichielssen-
When using a local version of the app, it s opening up in in less than 2 sec.
We are indeed using kubernetes with shinyproxy so this could be the reason for the long startup indeed.

Thanks for your help, Dylan

Hi @Dylan_Cissou,

While some overhead (pod creation, service creation) is to be expected, going from 2sec to 20sec seems a bit harsh. We will check here to see if optimizations can be made.

Does one have access to USER or GROUP information in the template? This would be useful for a multi-app situation.

Thanks
Ralf

Hi @rstub,

Yes, templates have full access to the Thymeleaf engine, which means they can use expressions like:

<div>
Logged user: <span sec:authentication="name"></span>
Roles: <span sec:authentication="principal.authorities"></span>
</div>
1 Like

Hi,
the GET to /app_direct/<appname> works as well as POST to /api/proxy/<appname>, but only if used with authenticated user and spawns a container for that particular user. Our apps share the same container and volumes for all users and user-specific content is loaded through SHINYPROXY_USERNAME and SHINYPROXY_USERGROUPS environment variables. Is it possible to have one spare container running and have it assigned to the first user that ‘requests’ that container through that particular app?
Thanks
Jan

3 Likes

Hi,

We have received feedback from clients that it would be great to have reduced loading times. As @Jan_Kucera mentioned, having one (or perhaps multiple) spare containers ready to be assigned to users would be very nice to have.

If using Kubernetes, I assume that when proxy.kubernetes.image-pull-policy is set to ‘Always’, launching will take a few seconds extra as logs show that this forces an image to be pulled every time a pod is created. I have not checked this in detail yet, but perhaps something to keep in mind when using Kubernetes.

Best Regards,

Michael

Hi @fmichielssen,

we are currently also facing the issue of needing pre-initialised containers waiting for users since our app takes fairly long to load (more than 30 secs).

Since I have no prior experience with ajax and the interaction with your API:
Could you give a more detailled example on how the ajax call to the API endpoint has to look like (in the custom template index.html)? Is there an example config for this use case?

Thank you and kind regards

EDIT: I found the solution and I am glad to share it with other web development newbies.
Since jquery is supported, you just simply put this ajax get request in your index.html file. You can also pre-initialize multiple apps.

$.ajax({
type: “GET”,
url: “/app_direct/my_app”,
success: function(a) { console.log(a)}});

1 Like

Where exactly do I put the ajax get request in the index html file?

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head lang="en">
	<title th:text="${title}"></title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<link rel="stylesheet" media="screen" th:href="@{${bootstrapCss}}" />
	<link rel="stylesheet" media="screen" th:href="@{/css/default.css}"/>
	<link rel="stylesheet" media="screen" th:href="@{/assets/css/1-col-portfolio.css}"/>
	<script th:src="@{${jqueryJs}}"></script>
	<script th:src="@{${bootstrapJs}}"></script>
</head>
<body>
	<div th:replace="../fragments/navbar :: navbar"></div>

	<div class="container" id="applist">
		<div th:each="app: ${apps}">
			<div class="row" >
				<div class="col-md-7">
					<img class="img-responsive" th:src="@{/assets/img/{name}.png(name=${app.id})}"></img>
				</div>
				<div class="col-md-5">
					<h3 th:text="${app.displayName == null} ? ${app.id} : ${app.displayName}"></h3>
					<p th:if="${app.description != null}" th:text="${app.description}"></p>
					<a class="btn btn-primary" th:href="@{/app/}+${app.id}">Start  <span class="glyphicon glyphicon-chevron-right"></span></a>
				</div>
			</div>
			<hr></hr>
		</div>
	</div>
</body>
</html>

Where does the ajax call go inside the html file? I put the ajax call inside the html template file in the following way but it didn’t pre-initialize the container:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
	xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head lang="en">
	<title th:text="${title}"></title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<link rel="stylesheet" media="screen" th:href="@{${bootstrapCss}}" />
	<link rel="stylesheet" media="screen" th:href="@{/css/default.css}"/>
	<link rel="stylesheet" media="screen" th:href="@{/assets/css/1-col-portfolio.css}"/>
	<script th:src="@{${jqueryJs}}"></script>
	<script th:src="@{${bootstrapJs}}"></script>
</head>
<body>
	<div th:replace="../fragments/navbar :: navbar"></div>
	<script>
	$.ajax({
		type: “GET”,
		url: '/app_direct/shiny-yield-prediction',
		success: function(a) { console.log(a)}
	});
	</script>
	<div class="container" id="applist">
		<div th:each="app: ${apps}">
			<div class="row" >
				<div class="col-md-7">
					<img class="img-responsive" th:src="@{/assets/img/{name}.png(name=${app.id})}"></img>
				</div>
				<div class="col-md-5">
					<h3 th:text="${app.displayName == null} ? ${app.id} : ${app.displayName}"></h3>
					<p th:if="${app.description != null}" th:text="${app.description}"></p>
					<a class="btn btn-primary" th:href="@{/app/}+${app.id}">Start  <span class="glyphicon glyphicon-chevron-right"></span></a>
				</div>
			</div>
			<hr></hr>
		</div>
	</div>
</body>
</html>

shiny-yield-prediction is the container ID. What am I doing wrong? I have no html or jquery understanding.

@gacolitti, can you check your browser console (chrome: F12 -> console tab) for any errors?

Yes, I see:

(index):84 Uncaught SyntaxError: Invalid or unexpected token

Also, I’m running ShinyProxy 2.2.2 in a Docker container.

I have no idea what I’m doing. Just trying to get apps to load faster for users.

Where can I find documentation of that proxy API? URL https://www.shinyproxy.io/configuration/#proxy-api provided there https://www.openanalytics.eu/blog/2019/03/24/shinyproxy-2.2.0/ is broken