A few people have asked me about my presentation environment recently. I present about 30-40 times a year and I’ve got my process (still not funny apparently) and tooling pretty much down pat.
For my content, I’ve long been a fan of Hakim El Hattab’s Reveal.JS. Reveal.JS is a presentation framework that allows you to create awesome slideshows that can be displayed via your browser via a builtin web server.
You can specify slides in HTML or Markdown.
<div class="slides"> <section> <h1>Your title</h2> <ul> <li>Your insightful argument</li> <li>More wisdom...</li> </ul> </section> </div>
I’ve recently combined this with Wetty, a terminal in the browser that allows me to have in slide SSH. No longer do I need to switch between my terminal and my slides whilst demo’ing. I find that’s pretty bloody cool (and people love it). I sometimes have as many people ask me how I do the “secret in browser terminal magic” than I do about the topic of my presentation.
One of the annoying aspects of using both tools is that they do get updated fairly regularly and installation is still basic and via
git clone. This means I need to keep them up to date to fix minor issues. It also means I end up with a checkout of both tools inside each repository that I store my presentations in, like my Introduction to Docker talk.
So I decided that I’d make my life easier by creating a Docker image for my presentation stack. I designed it so that the data (my content) would be separated from the code (the presentation stack). Each presentation repository contains the following files:
README.md images/ slides/slides.html
slides/slides.html file holds my slides and the
images directory holds any images I am using in my slides.
I then have a Docker image that holds my presentation stack. Here’s the
Dockerfile for my image.
FROM ubuntu:14.04 MAINTAINER James Turnbull <email@example.com> RUN apt-get -qqy update RUN apt-get -qqy install git nodejs npm RUN ln -s /usr/bin/nodejs /usr/bin/node WORKDIR /opt # Install reveal.js RUN git clone https://github.com/hakimel/reveal.js.git presentation WORKDIR /opt/presentation RUN npm install -g grunt-cli RUN npm install RUN sed -i "s/port: port/port: port,\n\t\t\t\t\thostname: \'\'/g" Gruntfile.js # Install wetty RUN git clone https://github.com/krishnasrinivas/wetty WORKDIR /opt/presentation/wetty RUN npm install # Add content ADD docker.css /opt/presentation/css/theme/docker.css ADD index.html /opt/presentation/index.html ADD images /opt/presentation/images/ ADD slides /opt/presentation/slides/ WORKDIR /opt/presentation EXPOSE 8000 CMD [ "grunt", "serve" ]
Dockerfile is pretty self-explanatory. I base the image on Ubuntu 14.04 (to get the latest NodeJS). I install NodeJS, NPM, and Git. I then install Reveal.JS, its dependencies and Grunt to power it.
By default Grunt only binds the Reveal.JS server to
localhost so I edit the
Gruntfile.js to update the server. This will bind Grunt to all interfaces so I can expose my presentation on the container’s external network interface.
I also add Wetty and its dependencies.
I then add some template content and images I use and a Docker CSS theme to render my slides using Docker’s colors and styles.
Finally I set my working directory to the presentation directory, expose port 8000 and specify a command to run,
grunt serve, when a container is launched from this image. So when a container is launched Grunt will serve out my presentation on port 8000, which I can then directly map 1:1 or on a port in Docker’s default port range.
I can then build my image (I actually use a Trusted Build but I could also build from the command line):
$ sudo docker build -t="jamtur01/docker-presentation" .
Then launch a container from my image!
$ sudo docker run -p 8000:8000 --name docker_presentation \ -v /Users/james/src/intro-to-docker/images:/opt/presentation/images \ -v /Users/james/src/intro-to-docker/slides:/opt/presentation/slides \ -d jamtur01/docker-presentation
This creates a container called
docker_presentation and mounts
/Users/james/src/intro-to-docker/slides into the container. I would update these paths for the specific presentation I am loading. The container is launched daemonized and port 8000 inside the container is mapped to port 8000 outside on the host.
If I now browse to
http://localhost:8000 I will see my Introduction to Docker presentation.
If I wanted to launch another presentation I could create a new container like so:
$ sudo docker run -P --name ops_mythology \ -v /Users/james/src/ops-mythology/images:/opt/presentation/images \ -v /Users/james/src/ops-mythology/slides:/opt/presentation/slides \ -d jamtur01/docker-presentation
I’ve updated my volume mounts to the new presentation and I am now mapping the port to one of Docker’s ports. This allows me to run more than one presentation locally rather being restricted to port 8000. Let’s see which port got assigned with the
docker port command.
$ sudo docker port 0261a0d6cb4b 8000 0.0.0.0:49159
If I now browse to
http://localhost:49159 I will see my Ops Mythology presentation.
Now I don’t need to worry about anything in my presentation stack or worse duplicate its code in every presentation. Now all I need to do is maintain my content and my images and I am done!