144 lines
4.8 KiB
Markdown
144 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)
|