atomaka.com/_posts/2015-11-21-dockerizing-lubot.md

143 lines
4.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
layout: post
title: Dockerizing Lubot
tag:
- docker
- technical
---
Lubot is the [Lansing Codes](http://lansing.codes/) chat bot responsible for
bringing updates about all local events to chatters everywhere…or just in
Lansing. Previously, it resided on a Heroku Free instance and was required to
sleep for six hours out of every twenty-four hours. After some issues with him
waking up, we began looking for alternatives. Since I already had a server
hosting several Docker containers, it seemed like the best choice.
Docker is basically a way to create a container that can be easily distributed
across many operating systems. Using it, we can take the code, runtime, and
libraries that Lubot requires and put them in a package. You can read more about
it in the
[Official “What is Docker” article](https://www.docker.com/what-docker).
To begin, we need to determine the necessary dependencies for our application.
Lubot is built using NodeJS and uses npm to manage dependencies. Specifically,
we are using Node v5.0.0 and npm v3.3.9. Theres an official Node Dockerfile to
use to begin with so it is pretty easy to start.
```
FROM node
ENV NODE_VERSION 5.0.0
ENV NPM_VERSION 3.3.9
```
After that, we want to take care of the dependencies for our application.
Because of the way Docker works, we want to cache this step so when our
packages.json file does not change, we do not have to rebuild our dependencies.
```
ADD package.json /tmp/
RUN cd /tmp && npm install
RUN mkdir -p /opt/hubot && cp -a /tmp/node_modules /opt/hubot
```
Then, we need to add the application code to the container.
```
ADD . /opt/hubot
WORKDIR /opt/hubot
```
Finally, we can start the service.
```
CMD ["/opt/hubot/bin/hubot", "--adapter", "slack"]
```
Combine these steps and we end up with a
[Dockerfile](https://github.com/lansingcodes/lubot/blob/master/Dockerfile). This
gets added to the repisitory so that we can build the application. Building an
image is easy.
```
docker build -t lansingcodes/lubot .
```
This will download and build the necessary filesystems, caching where necessary
and giving us a runable container image. Starting the container is also simple.
```
docker run lansingcodes/lubot
```
Lubot expects some environment variables to be there. But since we are in a
container, no environment variables exist on the system and we need to pass them
in. Our new run command accounts for this.
```
docker run -d --restart=always --name lubot \
-e HUBOT_SLACK_TOKEN=$HUBOT_SLACK_TOKEN \
-e TWITTER_LANSINGCODES_CONSUMER_KEY=$TWITTER_LANSINGCODES_CONSUMER_KEY \
-e TWITTER_LANSINGCODES_CONSUMER_SECRET=$TWITTER_LANSINGCODES_CONSUMER_SECRET \
-e TWITTER_LANSINGCODES_ACCESS_TOKEN=$TWITTER_LANSINGCODES_ACCESS_TOKEN \
-e TWITTER_LANSINGCODES_ACCESS_TOKEN_SECRET=$TWITTER_LANSINGCODES_ACCESS_TOKEN_SECRET \
-e GOOGLE_API_KEY=$GOOGLE_API_KEY \
-e LUBOT_MEETUP_API_KEY=$LUBOT_MEETUP_API_KEY \
-e TZ=$TZ \
-e REDIS_URL=$REDIS_URL \
lansingcodes/lubot
```
Lubot is now running in a container. However, Heroku also provided easy
continuous deployment when combined with [Circle CI](https://circleci.com/).
Being able to have changes deployed when the master branch changes is handy.
Circle CI allows us to specify post-build commands to run. Typically, wed want
to build the container on our CI server and then push to a Docker registry, butI
didnt have one of those available. We can still use Circle CI to execute
commands on a remote server with SSH. This makes our deploy process simple.
- clone the repository on our remote server
- build the docker image from that repositry
- run the docker image that was build
Our CI build file will trigger these actions via a script.
```
scp deploy/deploy.sh lubot@app.atomaka.com:/home/lubot
ssh lubot@app.atomaka.com "bash /home/lubot/deploy.sh"
```
And then, deploy.sh will take care of the parts we already discussed.
```
#!/bin/bash
cd $HOME
source lubotrc
git clone https://github.com/lansingcodes/lubot.git
cd $HOME/lubot
sudo docker build -t lansingcodes/lubot .
cd $HOME
rm -rf $HOME/lubot
sudo docker rm -f lubot
sudo docker run -d --restart=always --name lubot \
-e HUBOT_SLACK_TOKEN=$HUBOT_SLACK_TOKEN \
-e TWITTER_LANSINGCODES_CONSUMER_KEY=$TWITTER_LANSINGCODES_CONSUMER_KEY \
-e TWITTER_LANSINGCODES_CONSUMER_SECRET=$TWITTER_LANSINGCODES_CONSUMER_SECRET \
-e TWITTER_LANSINGCODES_ACCESS_TOKEN=$TWITTER_LANSINGCODES_ACCESS_TOKEN \
-e TWITTER_LANSINGCODES_ACCESS_TOKEN_SECRET=$TWITTER_LANSINGCODES_ACCESS_TOKEN_SECRET \
-e GOOGLE_API_KEY=$GOOGLE_API_KEY \
-e LUBOT_MEETUP_API_KEY=$LUBOT_MEETUP_API_KEY \
-e TZ=$TZ \
-e REDIS_URL=$REDIS_URL \
lansingcodes/lubot
```
Deploying Lubot is now just as easy as it was with Heroku and he never has to
sleep again.
- [Other details surrounding the deployment](https://github.com/lansingcodes/lubot/tree/master/deploy)
- [Lubot repository](https://github.com/lansingcodes/slackbot)