class: title, cover, middle, center <h1>Desert Island Docker</h1> <h2>R Edition</h2> <div class="details"> <div class="conference">Brighton R Meetup</div> <div class="date">23 May 2024</div> <div class="author">Andrew B. Collier</div> </div> <img class="logo-full" src="img/fathom-logo-full.svg" width="20%"> --- class: middle, center .big7x[Which<br> .deep-sky.big12x[three]<br> Docker<br> images?] ??? What 3 Docker images would you want with you if you were shipwrecked on a desert island? **Pause!!** You might object to this question on at least two counts. --- class: middle .big6x[.deep-sky[1.] Nonsense!] ??? If I was shipwrecked then a Docker image would be one of the last things I'd need. --- class: middle .big6x[.deep-sky[2.] Utter nonsense!!] ??? A castaway generally doesn't have the luxury of foresight. --- ??? I'd contend that 1. three strategically chose Docker images would be essential to maintaining your productivity if, as an R developer, you were shipwrecked and 2. as an R developer you should _always_ be prepared. Good evening. I'm Andrew Collier. And as you might guess from my accent, I'm from South African. Howziiiiiiiit! I'm Lead Data Scientist for Fathom Data, a remote-only Data Science consulting company based in South Africa. Over the last few years the frequency with which I use Docker has escalated from once every couple of months to daily. It's probably my most useful tool. Having said that, I'm by no means an expert. Perhaps a enthusiastic amateur. --- class: middle .big6x[.deep-sky[1.] R] .big6x[.deep-sky[2.] Shiny] .big6x[.deep-sky[3.] Jupyter] --- class: middle, center <img src="img/warning.png" height="250px"> .big6x.warning[WARNING!] .big6x[No advanced content.] ??? I'll begin with a caveat: this is not an advanced talk about Docker. It's a high level introduction to what's possible with Docker, specifically aimed at R developers. --- class: section, center .big6x[What is Docker?] --- class: middle .big5x[It's _like_ a .deep-sky[Virtual Machine].] .footnote[* For example, you can use it to run Linux processes on a Windows machine.] ??? One might say that it's like a Virtual Machine and makes it possible for you to run, for example, Linux processes on a Windows machine. But it's a lot more than that. --- class: middle .big5x[But also .deep-sky[totally different].] .footnote[* Nonsense! You're explaining one technology by referring to another (equally obscure) technology.] ??? However, I realise that I'm trying to explain one technology by referring to another technology that might be equally obscure. Perhaps a diagram will help. --- class: inverse background-image: url("data:image/png;base64,#img/docker-versus-vm.png") background-repeat: no-repeat background-size: 100% auto ??? The setup we are probably most familiar with is the one depicted on the left. It's what you have on your laptop or desktop machine: an operating system with a collection of system library that can run one or more applications. The challenge with this is that it can be really tricky to handle multiple versions of the same application. Suppose, for example, that you needed to run both the current version of R and a historical version from a few years ago. Even virtual environments are not going to help you with that. On the other extreme are virtual machines. In this case you can host one or more complete guest operating systems. This can be very useful but it's also a rather heavyweight solution because multiple instances of the virtual machine will replicate the entire operating system, system libraries etc. There are two types of hypervisors. A _bare metal_ hypervisor sits directly on the hardware, below the operating system. These are high performance. A _hosted_ hypervisor sits on top of the operating system. Easier to configure but not as performant. There are a lot of things in an Operating System that we just don't need. Docker is a compromise between these two extremes. It allows you to have multiple guest processes (or "containers") running on your machine. These processes have just the minimal required runtime environment, so they are lightweight. <!-- ====================================================================== --> <!-- ====================================================================== --> <!-- === === --> <!-- === R === --> <!-- === === --> <!-- ====================================================================== --> <!-- ====================================================================== --> --- class: section, center .big12x.top[1] .big5x[Food & Water: .deep-sky[R]] ??? Many of these problems can be resolved by just using the R Docker image. --- background-image: url("data:image/png;base64,#img/cookie-cutter-docker.webp") background-repeat: no-repeat background-size: 100% 100% ??? It's time to introduce some terminology. Two terms are pervasive with Docker: image and container. You can think of an image as a template (or by analogy, a cookie cutter). A container then is what you get by applying the template (or using the same analogy, cut out a cookie). You can have multiple images (perhaps for Python, NGINX and R) and each can be used to create one or more containers. --- class: middle .big3x[A .deep-sky[container] comes from an .deep-sky[image].] ??? So, a container comes from an image. --- class: middle .big3x[Where does an .deep-sky[image] come from?] ??? But where does an image come from? --- class: inverse, middle <img src="img/image-official-rocker-tidyverse.png" width="100%" > <br><br> .big2x[https://hub.docker.com/r/rocker/tidyverse] More than 10 million downloads. 🤩 Around 200 000 downloads per week. ??? There are a variety of image sources, but the most common is Docker Hub. It hosts both "official" as well as "user" images. You can find the details of the R image at this URL. It's fairly popular: as you can see it's been downloaded more than 1 billion times. --- class: inverse, middle The `rocker` organisation publishes a number of images including: - `rocker/r-ver` — Specific R version 🟢 - `rocker/rstudio` — Adds RStudio ⭐ - `rocker/tidyverse` — Adds `{tidyverse}` and `{devtools}` - `rocker/verse` — Adds TeX and publishing packages ⭐ - `rocker/geospatial` — Adds geospatial packages - `rocker/shiny` — Adds Shiny server 🟢 - `rocker/shiny-verse` - `rocker/binder` — Adds requirements to run on https://mybinder.org/ - `rocker/cuda` — Adds CUDA support - `rocker/` - `rocker/` .footnote[* 🟢 — included in this talk; ⭐ — very handy indeed!] --- class: middle <img src="img/pull-run.svg" width="100%" > ??? There are two steps to using Docker. First you need to pull and image. Then you run the image to create a container. --- class: inverse, middle, center <!-- Image made with "screenshot - large" profile. --> <img class="shadow" src="img/r-ver-pull.png" width="950px"> ??? The first step towards creating a R container is to download (or "pull") the image using the `docker pull` command. That doesn't actually run the image. It just downloads it onto your host. --- class: inverse <!-- Image made with "screenshot - large" profile. --> .center[<img class="shadow" src="img/r-ver-run.png" width="950px">] .footnote[* Er, that's rather underwhelming. So what?] ??? To create a container you use the `docker run` command. The result might seem underwhelming. And it is. But there's a reason for that: containers are non-interactive by default. --- class: inverse <!-- Image made with "screenshot - large" profile. --> .center[<img class="shadow" src="img/r-ver-run-interactive.png" width="950px">] ??? If you want to interact with the container then you need to provide the `-i` (interactive) and `-t` (terminal) flags. You might be wondering what the "latest" is all about. --- class: middle .big3x[What's the `:latest` for?] -- .big3x[It's a .deep-sky[tag].] ??? Images come with tags. These are labels for different versions of an image. --- class: inverse, middle <img src="img/image-tag-example.png" width="100%" > --- class: middle .big3x[Use the .deep-sky[right tag]!] --- class: inverse, center <!-- Image made with "screenshot - large" profile. --> <img class="shadow" src="img/r-ver-new.png" width="950px"> ??? This code assumes that strings are converted into factors by default when creating a data frame. This changed in recent versions of R. This is a breaking change if you code relied on the implicit conversion taking place. --- class: inverse, center <!-- Image made with "screenshot - large" profile. --> <img class="shadow" src="img/r-ver-old.png" width="950px"> <!-- ====================================================================== --> <!-- ====================================================================== --> <!-- === === --> <!-- === SHINY === --> <!-- === === --> <!-- ====================================================================== --> <!-- ====================================================================== --> --- class: section, center .big12x.top[2] .big5x[Shelter: .deep-sky[Shiny]] --- class: inverse, middle <!-- 200% zoom in Firefox on laptop screen. Cropped in Gimp. --> <img src="img/image-official-rocker-shiny.png" width="100%" > <img src="img/image-official-rocker-shiny-latest.png" width="100%" > --- class: inverse, middle, center <!-- Image made with "screenshot - large" profile. --> <img class="shadow" src="img/shiny-run.png" width="950px"> --- class: inverse, middle <img class="shadow" src="img/shiny-unable-to-connect.png" width="100%" > --- class: inverse, middle, center <img class="shadow" src="img/thats-a-bummer.jpg" width="50%" > --- class: middle .big3x[A container is a .deep-sky[sealed environment]!] .footnote[* You could say that everything is "contained". Nothing comes in. Nothing goes out.] ??? But there's a problem. Not only are containers non-interactive by default, each container is a sealed environment. Nothing comes in or goes out. --- class: middle .big3x[ But you can 👊 .deep-sky[punch] 👊 holes in it. `-v` — mount a (disk) volume<br> `-p` — publish a (network) port ] ??? However, there are some run time flags that can be used to share information with a running container. --- class: inverse ```bash docker run rocker/shiny ``` But that's .deep-sky[inaccessible]! ☹️ -- Share the app directory (so you can access files). ```bash docker run \ * -v $(pwd)/shiny-faithful/:/srv/shiny-server/ \ rocker/shiny ``` -- Make it accessible via port 3838 (so you can access the app with your browser). ```bash docker run \ -v $(pwd)/shiny-faithful/:/srv/shiny-server/ \ * -p 3838:3838 \ rocker/shiny ``` --- class: inverse, middle <img class="shadow" src="img/shiny-old-faithful.png" width="100%" > --- class: section-slide-orange .big8x[Make<br>this<br>.underline[much more]</br>useful!] --- background-color: #fdfbf7 background-image: url("data:image/png;base64,#img/dockerfile-image-container.webp") background-repeat: no-repeat background-size: 100% auto .footnote[* Roll your own Docker image by creating a `Dockerfile`.] --- class: inverse, middle Create a `Dockerfile`: ```bash FROM rocker/shiny RUN R -e "install.packages('leaflet')" COPY app.R /srv/shiny-server/ ``` Then build and run: ```bash docker build -t shiny-leaflet . docker run -p 3838:3838 shiny-leaflet ``` 🚨 No need for `-v` because the application is baked into the image. ??? We can make this more useful by creating a derived image and installing Numpy on it. --- class: inverse, middle <img class="shadow" src="img/shiny-leaflet.png" width="100%" > <!-- ====================================================================== --> <!-- ====================================================================== --> <!-- === === --> <!-- === JUPYTER === --> <!-- === === --> <!-- ====================================================================== --> <!-- ====================================================================== --> --- class: section, center .big12x.top[3] .big5x[Build a Raft: .deep-sky[Jupyter]] --- class: inverse, middle <!-- 200% zoom in Firefox on laptop screen. Cropped in Gimp. --> <img src="img/image-jupyter-r-notebook.png" width="100%" > --- class: inverse, middle ```bash docker run \ * -p 8888:8888 \ * -v $(pwd):/home/jovyan \ jupyter/r-notebook ``` - The `-p` option makes it accessible via port 8888. - The `-v` option shares the content of a directory on the host. --- class: inverse, middle <!-- 170% zoom in Firefox on laptop screen. --> .center[<img class="shadow" src="img/jupyter.png" width="90%" >] --- class: middle .big3x[Let's make this more interesting by...] --- class: middle .big3x.right[... installing .deep-sky[`{rJava}`].] ??? This is a well known challenge with R. Perhaps not as challenging as getting out of `vim`, but it's up there. --- class: inverse, middle .center[<img class="shadow" src="img/installing-rjava.jpeg" width="90%" >] --- class: middle .big3x.deep-sky[True story.] --- class: inverse, middle Create a `Dockerfile`: ```bash FROM jupyter/r-notebook:r-4.3.1 USER root RUN apt update -y && \ apt install -y default-jdk ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64 ENV LD_LIBRARY_PATH=${JAVA_HOME}/lib/server:${LD_LIBRARY_PATH} RUN R CMD javareconf # Install {rJava} because it's a dependency of {mailR}. # RUN R -e 'install.packages("rJava", repos="cran.r-project.org")' && \ R -e 'install.packages("mailR", repos="cran.r-project.org")' COPY send-email.ipynb /home/jovyan ``` --- class: inverse, middle <!-- 170% zoom in Firefox on laptop screen. --> .center[<img class="shadow" src="img/jupyter-send-mail.png" width="90%" >] --- class: inverse, middle <!-- 150% zoom in Firefox on laptop screen. Hid some advertising. --> <!-- GO TO https://www.smtpbucket.com/emails?sender=sender@gmail.com --> .center[<img class="shadow" src="img/smtpbucket.png" width="90%" >] --- class: inverse background-image: url("data:image/png;base64,#img/emayili-details.png") background-repeat: no-repeat background-size: auto 100% background-position: right center .middle[ But if you want to send email from R, then check out `{emayili}`. <br><br> .center[<img class="shadow" src="img/emayili-hex.png" width="35%" >] <br> https://datawookie.github.io/emayili/ ] --- class: middle .leftcol60[ .big4x[1. .deep-sky[R] 2. .deep-sky[Shiny] 3. .deep-sky[Jupyter] ] Don't wait to be marooned... <br><br> .big2x[🌴 Use these images **now**! 🏝️] ] .rightcol40[ <div style="text-align: right;"> <img src="https://avatars.githubusercontent.com/u/6134409?v=4" width="200px"><br> Andrew B. Collier <code>andrew.b.collier@gmail.com</code> .smaller.list-style-none[ -
https://github.com/datawookie -
https://twitter.com/datawookie -
https://www.linkedin.com/in/datawookie ] <img class="logo-full" src="img/fathom-logo-full.svg" width="40%"> </div> ] <div style="clear: both;"></div> Slides at https://bit.ly/brighton-r-meetup-desert-island-docker. .footnote[* Add a mechanical keyboard and gaming mouse and I might never leave.] ??? As an R developer these are the images that I'd want to have with me on a desert island. They should keep me happy and productive until I'm rescued. Throw in a mechanical keyboard and gaming mouse and I might never want to leave. But don't tell my wife.