palindrome!

Dammit, I'm mad!

docker-composeで複数のhubotインスタンスを立ち上げる

やりたいこと

slackチームA用hubot、slackチームB用hubot、chatwork用hubot...という感じで、複数サービス/アカウントに向けて一気にhubotを動かしたくなりました。とりあえず開発環境として動く、というところまでなので、実際の運用についてはまた別途考えることにします。

開発中はホストOSでscirpts/*.coffeeを編集→各コンテナに反映、という流れが理想。

構成

ホストOS:OSX

  • docker: Docker version 1.11.0, build 4dc5990
  • docker-compose: docker-compose version 1.7.0, build 0d7bf73

コンテナ:

  • hubot-brain用のredis
  • hubot_slack
  • hubot_chatwork

hubotの応答ロジックを格納しているscriptsディレクトリにはホストOSのscriptsディレクトリをマウントし、ホスト側で編集した複数coffeescriptファイルを読み込ませます。

Dockerfile

まず、各hubotコンテナのベースとなるDockerイメージを作るためのDockerfileを用意します。

  • dockerユーザーを作ってrootで作業しないようにしてます
  • npmモジュールはnpm installコマンドで逐次インストールしていますが、package.jsonで管理したほうが綺麗なのかもしれない
  • adapterとしてhubot-slackとhubot-chatworkをインストールしていますが、必要に応じて他のadapterも入れられます
  • 開発中にスクリプト編集→hubotに再起動するよう命令、という流れで変更を反映したいので、foreverでhubotをデーモン化しています。このブログ記事を参考にさせてもらいました。
  • external-scriptsを読み込みたければ、COPYしましょう

blog.manaten.net

FROM node:latest

RUN apt-get update
RUN apt-get -y install sudo
RUN useradd -m -d /home/docker -s /bin/bash docker && echo "docker:docker" | chpasswd && adduser docker sudo
RUN echo "docker ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
WORKDIR /home/docker
USER docker

# workaround for https://github.com/npm/npm/issues/9863
RUN cd $(npm root -g)/npm \
&& sudo npm install fs-extra \
&& sudo sed -i -e s/graceful-fs/fs-extra/ -e s/fs.rename/fs.move/ ./lib/utils/rename.js

# npm installs
RUN npm install hubot coffee-script
RUN npm install hubot-slack hubot-chatwork
RUN npm install yo generator-hubot
RUN npm install forever

# ENV for node
ENV NODE_PATH=/usr/local/lib/node_modules:./node_modules \
    PATH=$PATH:./node_modules/.bin

RUN yes | yo hubot --defaults

# need to run "chown" because COPY command doesn't care USER directive
COPY scripts/hello.coffee ./scripts/
RUN sudo chown docker:docker ./scripts
# COPY external-scripts.json ./
COPY hubot-scripts.json ./

# need to create an empty file for forever
RUN touch ./.foreverignore
CMD forever -c coffee node_modules/.bin/hubot -a ${HUBOT_ADAPTER}

docker-compose.yml

redisコンテナと別々の2つのhubotコンテナを管理するためのdocker-compose用設定ファイルを用意します。

  • redisのvolumesは適当に指定してあります
  • hubotコンテナのvolumesでホスト側のscriptsをマウントするように設定してあります。
redis:
  image: redis:latest
  restart: always
  command: redis-server --appendonly yes
  ports:
    - '6379:6379'
  volumes:
    - /tmp

hubot_slack:
  restart: always
  build: .
  volumes:
    - $path_to_scripts:/home/docker/scripts
  ports:
    - '9999:9999'
  env_file: .env_hubot_slack
  environment:
    TZ: Asia/Tokyo
  links:
    - redis

hubot_chatwork:
  restart: always
  build: .
  volumes:
    - $path_to_scripts:/home/docker/scripts
  ports:
    - '9998:9998'
  env_file: .env_hubot_chatwork
  environment:
    TZ: Asia/Tokyo
  links:
    - redis

env_file

docker-compose.ymlで指定したenv_fileを用意します。

env_fileに各サービスで必要になる環境変数を設定しておくと、dockerがコンテナ内で使える環境変数として読み込んでくれるので便利です。

  • TODO:redisのURLは動的に取得したい

.env_hubot_slack

HUBOT_ADAPTER=slack
HUBOT_NAME=hubot-chan

HUBOT_SLACK_BOTNAME=hubot-chan
HUBOT_SLACK_TOKEN=xxxxxxxxxxxxx
HUBOT_SLACK_CHANNELS=test
HUBOT_SLACK_CHANNNELMODE=blacklist

REDIS_URL=redis://192.168.99.100:6379

.env_hubot_chatwork

HUBOT_ADAPTER=chatwork
HUBOT_NAME=hubot-chan

HUBOT_CHATWORK_TOKEN=xxxxxxxxxxxxx
HUBOT_CHATWORK_ROOMS=123
HUBOT_CHATWORK_API_RATE=350

REDIS_URL=redis://192.168.99.100:6379

scripts/hello.coffee

適当なscriptを用意します。

# Description:
#   Test
#
# Commands:
#   hubot hello - Say "Hi"

module.exports = (robot) ->
    robot.hear /HELLO$/i, (msg) ->
        msg.send "Hi"

update.coffee

hubotに向かってupdateと話しかけると、自殺します。 foreverが監視しているので起動してくれます。

# Description:
#   Test
#
# Commands:
#   hubot update - hubot will suicide because it is supposed to be re-launched by forever
child_process = require 'child_process'

module.exports = (robot) ->
    robot.respond /update/, (msg) ->
        process.exit()

hubot-scripts

redisをbrainとして使います。

["redis-brain.coffee"]

起動

docker-compose up -dコマンドで3つコンテナが立ち上がります。

確認

  1. helloと挨拶すると、hubotがHiと返答
  2. hello.coffeeを適当に編集(Hi -> Konnichiwa!)
  3. @hubot updateで更新
  4. helloと挨拶すると、hubotがKonnichiwa!と返答!!

これで、hubot-hogeアダプタを追加して必要なenv_fileを用意するだけで、hubotをいろんなチームに送り込める体制が整いました。

その他

  • 運用については検討中、せっかくdockerで作った&試してみたいという気持ちから、AWSのECSで細々と動かせないかなと考えています。
  • foreverはもうメンテナンスが止まっているので別のものを検討したほうがいいかもしれないです。
    • ファイルの変更を感知して自動起動する--watchもなぜかうまく動かせなかった(ので、updateコマンドを用意しました)