├── shinyproxy ├── .gitignore ├── Dockerfile ├── application.yml └── fragments │ └── navbar.html ├── .gitignore ├── apps ├── test │ ├── ui.R │ └── server.R └── test2 │ └── app.R ├── nginx-forwarder ├── dockerfile └── nginx.conf ├── shinyproxy-docker-compose-example.Rproj ├── docker-compose.yml └── README.md /shinyproxy/.gitignore: -------------------------------------------------------------------------------- 1 | *.jar 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | shinyproxy-logs 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /apps/test/ui.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | fluidPage( 3 | verbatimTextOutput("print"), 4 | DT::DTOutput('tbl') 5 | ) 6 | -------------------------------------------------------------------------------- /shinyproxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jre 2 | 3 | RUN mkdir -p /opt/shinyproxy/ 4 | 5 | WORKDIR /opt/shinyproxy/ 6 | CMD ["java", "-jar", "/opt/shinyproxy/shinyproxy.jar"] 7 | -------------------------------------------------------------------------------- /apps/test/server.R: -------------------------------------------------------------------------------- 1 | function(input, output, session) { 2 | output$tbl <- DT::renderDT(iris, server = TRUE) 3 | output$print <- renderPrint({ 4 | print("Selected rows:") 5 | input$tbl_rows_selected 6 | }) 7 | } 8 | -------------------------------------------------------------------------------- /nginx-forwarder/dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.9 2 | 3 | COPY nginx.conf /etc/nginx/conf.d/nginx.conf 4 | 5 | # dockerfile uses `\` to escape, docker-compose uses $$ instead 6 | 7 | CMD /bin/bash -c "envsubst '\${APP_NAME}' < /etc/nginx/conf.d/nginx.conf > /etc/nginx/nginx.conf && exec nginx -g 'daemon off;'" 8 | -------------------------------------------------------------------------------- /shinyproxy-docker-compose-example.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: XeLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | -------------------------------------------------------------------------------- /apps/test2/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | text_pool = reactiveVal("anything typed here would be shared") 3 | ui = fluidPage( 4 | textAreaInput("text", "TEXT POOL", value = isolate(text_pool())) 5 | ) 6 | server = function(input, output, session) { 7 | observe({ 8 | updateTextAreaInput(session, "text", value = text_pool()) 9 | }) 10 | observeEvent( 11 | input$text, text_pool(input$text) 12 | ) 13 | } 14 | shinyApp(ui, server, options = list(host = "0.0.0.0", port = 3838)) 15 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | nginx-forwarder: 4 | build: ./nginx-forwarder 5 | image: nginx-forwarder 6 | app-in-compose: 7 | image: shrektan/r-production 8 | ports: 9 | - 3838:3838 10 | volumes: 11 | - "./apps/test2:/app" 12 | command: ['Rscript', '-e', "shiny::runApp('/app', host = '0.0.0.0', port = 3838)"] 13 | shinyproxy: 14 | build: ./shinyproxy 15 | ports: 16 | - 8080:8080 17 | environment: 18 | - "WORK_DIR=${PWD}" 19 | volumes: 20 | - "/var/run/docker.sock:/var/run/docker.sock" 21 | - "./shinyproxy-logs/server:/log" 22 | - "./shinyproxy-logs/container:/container-logs" 23 | - "./shinyproxy:/opt/shinyproxy" 24 | -------------------------------------------------------------------------------- /shinyproxy/application.yml: -------------------------------------------------------------------------------- 1 | proxy: 2 | title: ShinyProxy 3 | port: 8080 4 | hide-navbar: false 5 | template-path: ./template 6 | container-log-path: /container-logs 7 | docker: 8 | internal-networking: true 9 | container-network: "shinyproxy-docker-compose-example_default" 10 | specs: 11 | - id: app-normal 12 | container-cmd: ["R", "-e", "shiny::runApp('/apps/test', host = '0.0.0.0', port = 3838)"] 13 | container-image: shrektan/r-production 14 | container-network: "${proxy.docker.container-network}" 15 | container-volumes: ["${WORK_DIR}/apps:/apps"] 16 | - id: app-not-in-compose 17 | container-cmd: ["R", "-e", "shiny::runApp('/apps/test2', host = '0.0.0.0', port = 3838)"] 18 | container-image: shrektan/r-production 19 | container-network: "${proxy.docker.container-network}" 20 | container-volumes: ["${WORK_DIR}/apps:/apps"] 21 | - id: app-in-compose 22 | container-image: nginx-forwarder 23 | container-network: "${proxy.docker.container-network}" 24 | container-env: 25 | APP_NAME: app-in-compose 26 | logging: 27 | file: 28 | /log/shinyproxy.log 29 | 30 | server: 31 | servlet: 32 | context-path: / 33 | -------------------------------------------------------------------------------- /nginx-forwarder/nginx.conf: -------------------------------------------------------------------------------- 1 | events { 2 | worker_connections 4096; ## Default: 1024 3 | } 4 | 5 | http { 6 | default_type application/octet-stream; 7 | sendfile on; 8 | tcp_nopush on; 9 | server_names_hash_bucket_size 128; # this seems to be required for some vhosts 10 | 11 | server { 12 | listen 3838 default_server; 13 | listen [::]:3838 default_server ipv6only=on; 14 | 15 | root /usr/share/nginx/html; 16 | # index index.html index.htm; 17 | 18 | server_name 127.0.0.1; 19 | 20 | location / { 21 | proxy_pass http://${APP_NAME}:3838/; 22 | proxy_http_version 1.1; 23 | proxy_set_header Upgrade $http_upgrade; 24 | proxy_set_header Connection "upgrade"; 25 | proxy_read_timeout 600s; 26 | proxy_redirect off; 27 | 28 | proxy_set_header Host $http_host; 29 | proxy_set_header X-Real-IP $remote_addr; 30 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 31 | proxy_set_header X-Forwarded-Proto $scheme; 32 | } 33 | 34 | 35 | location ~ /\.ht { 36 | deny all; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deploy Shinyproxy via Docker Compose 2 | 3 | ## It's a demo of 4 | 5 | - Setting up Shinyproxy via docker compose 6 | - Customizing the home page via a template (see `shinyproxy/fragments/navbar.html`) 7 | - Deploying a "proxy" app that forwards the traffic to the "global" app, which is deployed via docker compose service 8 | 9 | ## How to use 10 | 11 | - Clone this repo to a local directory, e.g. `/usr/wd/shinyproxy-docker-compose-example` 12 | - Copy the shinyproxy jar file (downloaded from shinyproxy.io) to `./shinyproxy/shinyproxy.jar` 13 | 1. Download the most recent version of shinyproxy (e.g. shinyproxy-2.5.0.jar from https://www.shinyproxy.io/downloads/shinyproxy-2.5.0.jar). 14 | 1. Rename the .jar file as shinyproxy.jar 15 | 1. Move the file to shinyproxy-docker-compose-example/shinyproxy/shinyproxy.jar 16 | - You may want to tweak `docker-compose.yml` and `shinyproxy/application.yml` 17 | - Run `docker-compose build` to build the necessary images 18 | - Run `docker-compose up -d` to launch the apps 19 | - Direct to http://127.0.0.1:8080, you should see the shinyproxy running. 20 | 21 | ## Apps 22 | 23 | 1. `app-normal`: This is a regular shinyproxy app. Each user will have his/her own apps. It means a new docker container will be lanuched for each user. 24 | 2. `app-in-compose`: This is like a shiny server app. This app is only lanuched once by docker compose. Shinyproxy will only run a "proxy" (nginx based) container, which redirect the traffic to the "global" app. 25 | 3. `app-not-in-compose`: The underlying app is the same as `app-in-compose`, except that it's like a normal shinyproxy app, so different user doesn't share the same global environment. 26 | -------------------------------------------------------------------------------- /shinyproxy/fragments/navbar.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 |
21 | 22 | 71 | 72 | 73 | --------------------------------------------------------------------------------