143 lines
4.8 KiB
Markdown
143 lines
4.8 KiB
Markdown
---
|
||
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. There’s 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, we’d want
|
||
to build the container on our CI server and then push to a Docker registry, butI
|
||
didn’t 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)
|