Shiny dockerizado
Recentemente, o Julio discutiu no
blog da Curso-R
como usar o pacote golem
para facilitar o desenvolvimento de shiny apps em forma de pacotes e
como transformar esses
pacotes em “executáveis” de apenas uma linha. Nesse post eu vou continuar nessa
linha e falar sobre como pegar esse app de uma linha e embrulhá-lo em um
contêiner docker para que o seu deploy seja instantâneo.
Inst
Essencialmente toda a magia da transformação de um app em um docker ocorre na
pasta /inst
. Esse diretório, pouco utilizado no dia-a-dia, é útil quando
precisamos adicionar dados, templates ou qualquer conteúdo que pode ser
necessário em algum momento para o pacote e, portanto, devem ser facilmente
acessíveis.
Quando você transformar seu shiny app em pacote utilizando o tutorial do Julio,
você terá uma estrutura de diretórios padrão na raiz do seu app: pastas /R
,
/man
e assim por diante. Crie a pasta /inst
e dentro dela /app
; aqui é
onde seu app irá morar.
Em princípio, você só precisa de um arquivo app.R
dentro de /inst/app
contendo aquela linha mágica que executa seu app todo:
shiny::shinyApp(meuPacote:::app_ui(), meuPacote:::app_server)
Se você estiver desenvolvendo um app mais complexo, talvez você precise
criar também uma pasta /www
ou um
arquivo _auth0.yml
, mas
o resumo da ópera é que você deve ser capaz de rodar shiny::runApp()
nesta
pasta e ver o seu app funcionando perfeitamente. Durante o desenvolvimento, não
se esqueça de sempre reinstalar o seu pacote para que meuPacote:::app_ui()
e meuPacote:::app_server
mantenham-se atualizados!
O último arquivo que você pode querer adicionar a /inst/app
é a configuração
do seu servidor shiny. No meu caso, eu desenvolvo apps dockerizados para
subí-los em máquinas virtuais na nuvem que podem ser acessadas por qualquer um,
então preciso deixar clara qual porta deve ser utilizada pelo meu shiny. Como
minhas máquinas suportam o protocolo HTTP, meu shiny-server.conf
é o seguinte:
run_as shiny;
# Log all Shiny output to files in this directory
log_dir /var/log/shiny-server;
# Define a server that listens on port 80
server {
listen 80;
# Define a location at the base URL
location / {
# Host the directory of Shiny Apps stored in this directory
site_dir /srv/shiny-server;
# When a user visits the base URL rather than a particular application,
# an index of the applications available in this directory will be shown.
directory_index off;
}
}
Fazer com que o app seja ouvido na porta 80 permite que outra pessoas o acessem
sem precisar especificar uma porta no URL! Sendo assim, você pode disponibilizar
seu shiny app em meuApp.dominio.com.br
.
Dockerfile
Agora que a sua pasta /inst
está devidamente configurada, você precisa criar
um Dockerfile
na raiz do seu app. Isso irá incomodar o devtools::check()
,
mas basta adicionar o nome desse arquivo ao seu .Rbuildignore
para que ele
seja ignorado durante a verificação.
Se todas as dependências do seu aplicativo estiverem devidamente organizadas no
DESCRIPTION
e todas as suas bases internas estiverem propriamente exportadas
como documentado pelo Julio, então o seu Dockerfile
deve ser parecido com o
seguinte:
FROM rocker/shiny-verse
# Instalar bibliotecas para o tidyverse
RUN apt-get update -qq && apt-get -y --no-install-recommends install \
build-essential \
libcurl4-gnutls-dev \
libxml2-dev \
libssl-dev \
r-cran-curl \
r-cran-openssl \
curl \
gnupg1 \
r-cran-xml2
# Instalar seu próprio app (e suas dependências)
COPY ./ /tmp/app/
RUN R -e "remotes::install_local('/tmp/app/')"
# Copiar arquivos para o lugar certo
EXPOSE 80/tcp
RUN rm /srv/shiny-server/index.html
COPY ./inst/app /srv/shiny-server/
COPY ./inst/app/shiny-server.conf /etc/shiny-server/shiny-server.conf
# Run
CMD ["/usr/bin/shiny-server.sh"]
Deploy
Agora você já pode construir sua imagem docker e executá-la ou subí-la para
alguma máquina virtual. Para testar localmente o app, execute os comandos a
seguir no terminal e acesse localhost:8080
no navegador:
docker build -t meuApp pasta/para/meuApp
docker run -p 8080:80 meuApp