This week I’m presenting a session at the 2016 Boston installment of SPTechCon . At this show I’m doing two presentations, one of which shows how to build an Office Add-in for Outlook using Angular 2 . As a developer, I find the most effective way to learn is to show someone what the goal or final result of a demo will be, then to go back and do it. A huge bonus is when I can walk away with the implementation & even replicate the demo quickly and easily myself. As a presenter, that’s not always easy, especially in this loosely connected cloud world.
Let’s take my session this week. An Office Add-in, in its most minimalist form, is an HTTPS secured website with an XML file that tells the Office client about the application & website. If I was attending my session here’s what I’d want:
- Lecture consisting of a few slides - really… just a few slides… because as we all know, power corrupts, and PowerPoint corrupts absolutely
- Demo of the final working solution so I can see what we’re trying to accomplish
- Live coding of the demo from scratch or an acceptable starting point
- Because live coding demos don’t always go to plan, if the presenter was running out of time, they need a way to quickly jump to the working solution so you can walk through the code
- Link to download the code so I can explore it later on my own time
- A way to easily spin up the demo in my environment
Sound like a plan? Me too… so let me explain to you how I’m doing it. It’s all in the magical beauty of Docker ! The solution & presentation can be found in this repo: andrewconnell/pres-ng2-officeaddin .
What’s Docker ? Is is the company behind one of the most widely adopted container specifications & ecosystems out there. So what’s a container? Go read my Docker & Containers 101 - What is Docker? post if you aren’t familiar with Docker & containers. But if you want to just get the gist:
By now you know what a virtual machine (VM) is… it’s a software implementation of an entire system… applications & operating system. A VM has no hardware… it shares that with the underlying computer it’s on. VMs usually take a few minutes to be provisioned (because they have big virtual hard drives that are tens to hundreds of GB in size) and a few minutes to spin up (because they are full blown operating systems).
A container takes this abstraction one level further. Within the VM you have a thin layer installed, the Docker Engine. You then create an image which includes JUST the code your application needs to run, no OS code. This means that an image is much smaller and spins up much faster, usually in just a few milliseconds, because there’s no booting of an OS. On the VM you then create an instance of the image, which is a container, with just a single command line statement.
Similar to differencing disks that we had with VMs, containers are based on another container. So your container may be based on the Node.js v5 container that’s based on another container that’s based on the Ubuntu 16.04 container. When you get an image for a container, it will download all the layers that your local machine doesn’t already have. For instance, if you already got the Node.js v5 container (and all the ones it’s based on), getting your container that’s based on the Node.js v5 container may be a small download.
I started out by figuring out what the best starting point would be. Starting with the
Angular 2 Quickstart
sample, I cloned it locally and then killed the
.git folder. Then I did a bunch of manual tweaks as I needed a webserver for my demo. This left me with a good starting point: a project that included a Node.js webserver hosting a REST API behind HTTPS and serving up the sample Angular 2 Quickstart application. At that point, I committed the changes and tagged them.
Next, I created the Office Add-in using Angular 2 and made sure everything worked.
The last step in the process was containerizing the application. This is the easiest step. To do this add a new text file to the root of the project named
FROM node:5 # create folders, group & user RUN mkdir -p /ac/demo /home/nodejs && \ groupadd -r nodejs && \ useradd -r -g nodejs -d /home/nodejs -s /sbin/nologin nodejs && \ chown -R nodejs:nodejs /home/nodejs # add NPM & Typings configs, install all modules & TypeScript type defs WORKDIR /ac/demo COPY package.json typings.json tsconfig.json /ac/demo/ RUN npm install --unsafe-perm=true # copy codebase for app into container RUN mkdir src COPY src /ac/demo/src RUN chown -R nodejs:nodejs /ac/demo USER nodejs # run app CMD npm start
The comments in the file explain what’s going on, but let me give you a little breakdown:
- The image is based on the official image by the Node.js foundation with Node.js v5 installed.
- Create two folders & a new group (nodejs) and user in that group (nodejs) and give the group ownership rights on the folders.
- Create a new folder
/ac/demooff the root of the system, copy the NPM
typings.json& TypeScript compiler configuration
tsconfig.jsoninto the folder and then run
npm installto download all the NPM packages (this also runs the
typings installstep which downloads the type definitions for TypeScript).
- Create a new folder
srcand copy the contents of the current working folder’s
srcfolder into it.
- Set the user to nodejs and if no startup command is provided, run
npm startwhen an instance of the image is started.
To build the image, run the following command (
-t tells Docker what to name the image… the
. on the end tells Docker where to find the
$ docker build -t pres-ng2-officeaddin .
What’s cool about this scenario is that now, I have, and you have access to, a single package that contains the entire demo application. For this scenario, a webserver is started on port 3433 over HTTPS.
To get the site working, all you have to do is run the following command on your machine:
$ docker run -it -p 443:3433 pres-ng2-officeaddin
andrewconnell/pres-ng2-officeaddin:ng2-demo-startfor the image to skip building the image & just download the image I built and available on my public Docker hub repo here: hub.docker.com/r/andrewconnell/pres-ng2-officeaddin
This will spin up a new container based on that image, mapping port 443 on your host machine to port 3433 in the container. It will also start it in interactive mode (
-i) with a TTY command prompt (
-t… combined with the
Here’s what it looks like when you run that without ever using docker before. Because containers are nested on top of other containers, be aware you might be about to download about 880MB of container layers. This first downloads all the layers for the container to run and then spins it up which you see about a minute into it. I then quit the container and do a few queries to see a list of all the images on my machine (
docker images), the running containers (
docker ps) and then all containers, even those run in the past who aren’t running (
docker ps -a).
I then installed the add-in into Outlook using the URL of the add-in’s manifest file:
https://localhost:443/app.xml & tada, it works!
Kill the container by hitting CTRL + C a few times in the command prompt / terminal window.
As the presenter, you want to make sure you grab the images before your session because as we know, conference internet can be problematic & the whole idea of using your tethered phone for a connection doesn’t work so great in some big conference centers or hotel ballrooms, especially when you have an 880MB download to do. For my case, I’ll grab my image without running it before the session using the following:
$ docker pull andrewconnell/pres-ng2-officeaddin:ng2-demo-start
This is the tricky part… you want to not destroy the work you did for the working solution while quickly jumping back in time to start doing the live coding demo and, at the same time, giving yourself a failsafe to jump back to the working solution quickly in case you’re running out of time. This is where Docker sprinkled with a little Git saves the day.
I’m going to use the container you just ran as the core of my demo. Why? Because in a presentation room, I don’t want to go through the process of downloading all the NPM packages my application uses (or, if I was using .NET, NuGet packages). Since the container already has all the NPM packages in it’s
node_modules folder & I’ve downloaded the container, I’ll be good to go. The same is true for the
typings folder for my TypeScript type definitions.
But what about the source code for the app? That’s also in the container. Here’s where the trick comes in: When I start the container I’ll tell it to map the path to the source code in the container to a folder on the host laptop! Then all I have to do is get the local code back to the starting point. Git to the rescue!
# get the list of tags for the repo $ git tag -l ng2-demo-tag # use the tag name to get the SHA of it's commit $ git rev-list -n 1 ng2-demo-start cb02f3b21038c6c304a93397afb79ebc09f99027 # reset the workspace back to the way it was at that tag $ git reset [COMMIT-SHA] . # undo the changes to the files that existing then, but have been edited in the final version $ git checkout -- demos/ng2-outlook-minicrm/src/client/index.html $ git checkout -- demos/ng2-outlook-minicrm/src/client/app/main.ts $ git checkout -- demos/ng2-outlook-minicrm/src/client/app/app.component.ts # remove all stuff that was added after this comment $ git clean -fdx
At this point, the locally cloned repo for the application is back the way it was at the starting point. So now the trick is to get it running. To do this, I need to put my Docker hat back on and create a new file that will be used to spin up the container, but with some special parameters. This is a Docker Compose file:
version: '2' services: webapp: # use the image that's clean, ready to do the live coding demo image: andrewconnell/pres-ng2-officeaddin:ng2-demo-start ports: - "443:3433" # map the current folder root on HOST => CONTAINER so container will # use files from HOST as I code locally # but leave node_modules & typings in the container so don't have to # download them again volumes: - ./src:/ac/demo/src # run TSC in watch mode & node in debug mode command: npm run dev
The comments explain most of what’s going on that you should understand from what I’ve already covered, but let me elaborate a bit.
- The highlighted lines say map the local folder
./srcto the folder in the container located at
/ac/demo/src. So now, when the container runs like it did last time in the working solution, everything is the same except the code is not coming from the container, but instead from my laptop. As you saw, in the last step I just reset the code on my laptop to the starting point so I just bypassed the final working solution!
- The last line says instead of using the
npm startas the startup command in the container as defined in the
npm run dev. A quick peek at
package.jsonshows that this does the same as the
Now to spin up the container with this configuration, just run:
$docker-compose -f docker-compose.demo.yml up
Now hit the site at https://localhost and you get the starting point:
At this point, I can start doing the live coding demo.
docker-compose.yml. That is what I’m using in the actual session to spin up the working solution… I’ll just run
docker-compose upinstead of
docker run ...and this will spin up two containers. One (service=webapp) will have the working solution & another (service=vorlonjs) that’s hosting the slick debugging tool by Microsoft’s DX team: VorlonJS .
What if you need to bailout and show the working version again? Again, Docker & git play a key role here.
First, reset the code to the final working version in the cloned repo so you can explain it to your attendees:
$ git reset --hard HEAD
Now that the code is set, kill the old running container and start it up using the original
docker run command you used above. This time, it uses the working app in the container, not your mapped folder one.
How do you remove this stuff you just downloaded to your machine? You the container you ran is still there, just not in a running state. Then there’s the 800MB collection of layers that makeup the image. Here’s what you do:
- remove the container using
docker rm [container ID]
- remove the image using
docker rmi [image ID]
docker runcommand you ran above.
If you’re new to Docker, this might be a bit much to bite off for a session. However if you’ve used Docker in any way previously, you’ll agree that there’s very little I had to do here to set this up as I only created two files (
docker-compose.demo.yml) and wrote down a few commands in git. All of this is easily repeatable in other demos.
Maybe this works for you as a presenter, maybe not… it works for me and I wanted to share it to give you a good use case of containers in the real world.
If you’re at the SPTechCon conference this week and wonder this is all fine and good, but I don’t do Node.js and I don’t want to work in Linux (because today, production containers are a Linux only thing). Well for those of you in the .NET & Windows world, containers are coming to you too! Docker supports Windows containers, something that’s coming in Windows Server 2016 (already working in the latest tech preview) so you can use classic .NET applications in containers. Or, you can leverage .NET Core / ASP.NET Core to build .NET applications that run on any platform.
Oh wait, I almost forgot…
There were two more steps I mentioned above in my presentation goal. One was a link to where you can get the presentation. Well for this one, go here:
The other was where you can get the working solution. Well you can build it yourself from the GitHub repo in the previous paragraph, or you can download it to your machine:
$ docker pull andrewconnell/pres-ng2-officeaddin
or just download & run it in one command:
$ docker run -it -p 433:3433 andrewconnell/pres-ng2-officeaddin:ng2-demo-start