Edited at

Slackと連携するHubotをDocker in AWSで構築

More than 3 years have passed since last update.


やりたいこと


  • Slackと連携できるHubotを立てる

  • デプロイ方法は、Github -> CircleCI -> AWSとする

  • slackへのAccess Tokenなどは環境変数で渡す(誰でも利用しやすいように)

  • scriptsは誰でも追加しやすいように、localレポジトリのをDockerへ持っていくようにする


まずはHubotに必要なものを揃える


  • node.js

  • npm

Mac環境ならどちらもbrew installで簡単に用意できる

YeomanとYeomanのgenerator-hubotを用意する。

-gをつけずにbundle的なのりでローカルにインストールしたかったが、実行にあたってのパス設定が面倒そうなので諦めた。

% npm install -g yo generator-hubot

% yo hubot
% bin/hubot

これでhubotとの対話インターフェースが立ち上がる。



  • scriptsディレクトリ以下にスクリプトを置くと、機能追加ができる。


  • hubot-scriptsディレクトリ以下には、hubot-scripts.jsonに定義したscriptsがインストールされる。利用できるscriptsはこちら


  • package.jsonに利用したいthird-party npm packageを追記すると、external-scripts.jsonに値が追加される。


  • ERROR hubot-heroku-alive included, but missing HUBOT_HEROKU_KEEPALIVE_URLというメッセージが表示されてしまうので、external-scripts.jsonからheroku packageを削除。


Dockerの用意


  • Dockerfileを書く

  • hubotの実行にnode.jsが必要なので、Docker registryからnode:latestを持ってくる。-> https://registry.hub.docker.com/u/library/node/

  • あとは、ローカル側に用意したhubotの構成をDockerインスタンス上に移設して起動する設定をDockerfileに書く。


Dockerを起動してみる


まずはbuild

% docker build -t hubot .

そうすると、イメージが作成される。

% docker images

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
hubot latest 663c24a4b22a 54 seconds ago 717.4 MB

作成されたイメージ上で、hubotを起動してみる

% docker run -it hubot bin/hubot

hubot> hubot ping
PONG


Slackの用意


Integration Settings


  • Hubotを有効に。


    • API Tokenを得られる

    • Iconのカスタマイズ、Nameのカスタマイズもできる




Dockerの起動

% docker run -e HUBOT_SLACK_TOKEN=${HUBOT_SLACK_TOKEN} -e HUBOT_SLACK_TEAM=${HUBOT_SLACK_TEAM} -e PORT=${PORT} -d -p 9999:9999 -v "$(pwd)":/hubot hubot

これでチームにhubotが参加してくる。


CircleCIの用意


circle.yml

machine:

services:
- docker
dependencies:
cache_directories:
- "~/docker"
pre:
- if [[ -e ~/docker/hubot.tar ]]; then docker load --input ~/docker/hubot.tar; fi
- docker build -t hubot .
- mkdir -p ~/docker
- docker save hubot > ~/docker/hubot.tar
test:
override:
- docker run -p 9999:9999 -v "$(pwd)":/hubot hubot


  • circleCI上でDockerコンテナを起動するときは、services: dockerが必要

  • dependenciesのところは、dockerのイメージを毎回registryからダウンロードしなくてよいようにキャッシュしている


Hubotへスクリプトの追加


stamp.coffee

# Description:

# A way to interact with the Stamp URI.
#
# Commands:
# hubot stamp me <keyword> - The Original. Queries Stamp Images for <keyword> and returns a stamp image.
# hubot stamp list - Show a list of keywords.

stamps = JSON.parse(process.env.HUBOT_STAMPS)

module.exports = (robot) ->
robot.respond /stamp me (.*)/i, (msg) ->
keyword = msg.match[1]
msg.send stamps[keyword] ? "No stamp for #{keyword}"

robot.respond /stamp list/i, (msg) ->
keys = for key, value of stamps
key
msg.send keys.join('\n')


scripts以下に配置するとhubotの起動時に読み込んでくれる。

決められたフォーマットでコメントを書いておくと、hubot helpしたときにコマンドの説明を表示してくれる。このscriptでは環境変数HUBOT_STAMPSにJSONを登録しておくと、keyに紐付いたimageのURLを返してくれる。


AWSへAnsibleを使ってデプロイする


hubot.yml

- hosts: aws

user: ec2-user
vars_files:
- vars/env.yml
- vars/private.yml
tasks:
- git: repo=https://github.com/osamunmun/hubot.git dest=/home/ec2-user/hubot accept_hostkey=yes
- file: path=/home/ec2-user/cache state=directory
- shell: cd /home/ec2-user/hubot; sudo ./docker-build.sh
- shell: sudo docker stop $(sudo docker ps -a -q)
- shell: cd /home/ec2-user/hubot; sudo docker run -e HUBOT_STAMPS={{ HUBOT_STAMPS }} -e HUBOT_SLACK_TOKEN={{ HUBOT_SLACK_TOKEN }} -e HUBOT_SLACK_TEAM={{ HUBOT_SLACK_TEAM }} -e PORT={{ PORT }} -p 9999:9999 -d -v $(pwd):/hubot hubot/slack


CircleCIからデプロイする


  • circleCIの環境変数に以下の値を登録しておく


    • ANSIBLE_VAULT

    • AWS_ACCESS_KEY_ID

    • AWS_DEFAULT_REGION

    • AWS_SECRET_ACCESS_KEY

    • AWS_SECURITY_GROUP_ID

    • HUBOT_SLACK_TEAM

    • HUBOT_SLACK_TOKEN



  • circleCIのSSH Permissionsにデプロイ先のEC2インスタンスへアクセスできる秘密鍵を登録しておく


circle.yml

machine:

services:
- docker
dependencies:
cache_directories:
- "~/cache"
pre:
- ./docker-build.sh
- npm install
- sudo pip install awscli
- sudo pip install ansible
- echo "${ANSIBLE_VAULT}" > ~/vault.txt
test:
override:
- ./node_modules/.bin/gulp mocha
- docker run -e HUBOT_SLACK_TOKEN=${HUBOT_SLACK_TOKEN} -e HUBOT_SLACK_TEAM=${HUBOT_SLACK_TEAM} -e PORT=${PORT} -d -p 9999:9999 -v "$(pwd)":/hubot hubot/slack
deployment:
production:
branch: master
commands:
- sh ./deploy.sh


deploy.sh

#!/bin/sh

set -ex

IP=`curl -s ifconfig.me`

trap "aws ec2 revoke-security-group-ingress --group-id ${AWS_SECURITY_GROUP_ID} --protocol tcp --port 22 --cidr ${IP}/32" 0 1 2 3 15
aws ec2 authorize-security-group-ingress --group-id ${AWS_SECURITY_GROUP_ID} --protocol tcp --port 22 --cidr ${IP}/32
cd ansible; ansible-playbook -i hosts hubot.yml --vault-password-file ~/vault.txt



ansible/hubot.yml

- hosts: aws

user: ec2-user
vars_files:
- vars/env.yml
- vars/private.yml
tasks:
- git: repo=https://github.com/osamunmun/hubot.git dest=/home/ec2-user/hubot accept_hostkey=yes
- file: path=/home/ec2-user/cache state=directory
- shell: cd /home/ec2-user/hubot; sudo ./docker-build.sh
- shell: sudo docker stop $(sudo docker ps -a -q)
- shell: cd /home/ec2-user/hubot; sudo docker run -e HUBOT_STAMPS={{ HUBOT_STAMPS }} -e HUBOT_SLACK_TOKEN={{ HUBOT_SLACK_TOKEN }} -e HUBOT_SLACK_TEAM={{ HUBOT_SLACK_TEAM }} -d -v $(pwd):/hubot hubot/slack