hubotをSlackと連携してデーモン化するまでの手順
Hubotをデーモン化してSlackと連携させた時のメモ。
以下のツールは導入してあること前提。
- apt
- nodejs
- npm
- npm
- hubot
- pm2
hubotとSlackの初期設定
まず、以下のコマンドでhubotを準備。
$ mkdir nametake-bot $ cd nametake-bot $ yo hubot _____________________________ / \ //\ | Extracting input for | ////\ _____ | self-replication process | //////\ /_____\ \ / ======= |[^_/\_]| /---------------------------- | | _|___@@__|__ +===+/ /// \_\ | |_\ /// HUBOT/\\ |___/\// / \\ \ / +---+ \____/ | | | //| +===+ \// |xx| ? Owner: nametake <nametake.kyarabuki@gmail.com> ? Bot name: nametake-bot ? Description: A simple helpful robot for your Company ? Bot adapter: (campfire) slack # ここでSlackをしっかり指定する ? Bot adapter: slack
以下のコマンドで、ちゃんとhubotが動くか確認。
$ ./bin/hubot ~~~ いろいろ出る ~~~ nametake-bot> nametake-bot ping nametake-bot> PONG
次に、SlackでhubotのConfigure Appsからhubotを連携させる。
連携させた後に、連携させた設定からHUBOT_SLACK_TOKEN
を取得。
取得したTOKENを元に環境変数を設定。
$ export HUBOT_SLACK_TOKEN="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
環境を設定したらSlackと連携。
$ ./bin/hubot -a slack
Slackで@nametake-bot ping
とかやって連携しているか確認。
pm2でhubotをデーモン化
hubotを永続化するためにpm2というnode.js製のツールを使う。
pm2をjsonから起動するために、以下の様なapp.json
ファイルを作成。
{ "apps":[ { "name" : "hilbot", "args" : ["-a", "slack"], "script" : "./bin/hubot", "exec_mode" : "fork", "exec_interpreter" : "bash", "autorestart" : true, "env": { "NODE_ENV" : "production", "PORT" : "8080", } } ] }
HUBOT_SLACK_TOKEN
をJSONにまとめておきたい場合は、
env
の項目を以下のように書き換える。
"env": { "NODE_ENV" : "production", "PORT" : "8080" "HUBOT_SLACK_TOKEN" : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }
以下のコマンドでpm2でhubotをデーモン化。
$ pm2 start app.json
以下のコマンドでちゃんと動いているか確認。
$ pm2 list ┌──────────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────────────┬──────────┐ │ App name │ id │ mode │ pid │ status │ restart │ uptime │ memory │ watching │ ├──────────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────────────┼──────────┤ │ nametake-bot │ 0 │ fork │ 32342 │ online │ 1 │ 4D │ 79.945 MB │ disabled │ └──────────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────────────┴──────────┘
statusの欄がerrored
になっていたら、pm2 logs
というコマンドでログを確認して
エラーを修正する。
今回はリモートリポジトリに上げたhubotのソースをSlack上からpullさせたかったので、
ここのnew_update.coffee
を
丸々参考にさせて頂きました。
参考サイト
VimでOpenCVのneocompleteの補完が落ちる問題の解決
pyenvのanacondaで、condaで入れたopencvの補完をしようとすると、
Vim: Caught deadly signal SEGV Vim: Finished.
と出て、落ちる問題の解決方法。
環境はOSX10.10。brewでopencvが導入済み。 使用しているエディタはvimで、補完はneocomplete + jedi-vimで行っている、
結論だけ言うと、$PYENV_ROOT/versions/anaconda-*/lib/python2.7/site-packages/
の
cv.py
とcv2.so
を、/usr/local/Cellar/opencv/*/lib/python2.7/site-packages
にある
cv.py
とcv2.so
へのシンボリックリンクに置き換えることで解決した。
以下、全体のコマンド
$ cd $PYENV_ROOT/versions/anaconda-*/lib/python2.7/site-packages/ $ mv cv.py cv.py.org # バックアップ $ mv cv2.so cv2.so.org # バックアップ $ ln -s /usr/local/Cellar/opencv/*/lib/python2.7/site-packages/cv.py $PYENV_ROOT/versions/anaconda-*/lib/python2.7/site-packages/cv.py $ ln -s /usr/local/Cellar/opencv/*/lib/python2.7/site-packages/cv2.so $PYENV_ROOT/versions/anaconda-*/lib/python2.7/site-packages/cv2.so
ぶっちゃけ原因がわからなかったので教えて偉い人。
プロパティについて理解したのでまとめる
プロパティについて
研究室で質問されたのでPythonのプロパティについて解説しようと思う。
最初にプロパティとはなにかをIT用語辞典で引いていみると、
プロパティとは、オブジェクト指向プログラミングで使用される オブジェクトが保持している、そのオブジェクトの性質を表すデータ。 例えば、画像データのオブジェクトならば、高さや幅などのデータを プロパティとして持っている。
という定義になっている。
つまり、オブジェクト指向プログラミングでクラスのインスタンスから
アクセスできるimg.width
とかimg.height
とかの、width
やheight
のことである。
しかし、カプセル化の概念からすると、直接プロパティをいじれてしまうのは良くない。
そこで、オブジェクト指向プログラミングでは以下のコードのような
アクセサというメソッドとアクセス修飾子を使って、プロパティが
不用意にいじくりまわされるのを防いでいる。以下の例で言うと、
getWidth
やgetHeight
、setWidth
やsetHeight
がアクセサである。
class Image { private int width; private int height; public int getWidth() { return width; } public int getHeight() { return height; } public void setWidth(int width) { this.width = width; } public void setHeight(int height) { this.height = height; } } class Spam { public static void main(String args[]) { Image img = new Image(); img.setWidth(200); img.setHeight(100); System.out.println(img.getWidth()); System.out.println(img.getHeight()); } }
JavaやC++などではこのようにしてプロパティへアクセス制御をしているが、 ただアクセスを制御したいだけなら、確かに冗長な感じはする。
PythonやC#ではプロパティをインスタンス(使う側)からはメンバ変数に見えるが、 クラス(実装)ではコードブロック(メソッド)のようにして制御を行う。
具体的な例をPythonで示してみる。 以下のコードがプロパティを利用してアクセス制御を行っているコードになる。
#!/usr/bin/env python # -*- coding: utf-8 -*- class Image(object): def __init__(self): self._width = 300 self._height = 400 @property def width(self): return self._width @width.setter def width(self, width): self._width = width @property def height(self): return self._height @height.setter def height(self, height): self._height = height if __name__ == '__main__': img = Image() img.width = 200 img.height = 100 print img.width print img.height
クラスのメンバである_width
や_height
に対し、デコレータを利用してwidth
メソッドや
height
メソッドをproperty型に一度変換している(デコレータに関しては
前の記事を参照)。
property
の第一引数がgetter
になっているため、property
型でデコレートした
メソッドでは、そのプロパティが返したい値を返している。
また、Pythonのプロパティはsetterメソッドを持っているため、そのsetterメソッドで
メソッドをデコレートすることで、img.width = 200
のような形でアクセスできるように
なっている。
今回の例では代入と読み出しの両方を行っていたが、例えば、 読み出しは許可するが、代入を禁止したい場合、以下のようにsetterメソッドが 呼び出されたら例外を投げるなどをして、プロパティへのアクセス制御が行える。
class Image(object): ~~~ @width.setter def width(self, width): raise ValueError() ~~~
このようにプロパティを制御することで、クラスの設計者が意図しない値の代入や、 読み出し、不用意なプロパティの削除を防ぐことができる。
デコレータの基本について理解したのでまとめる
Pythonのデコレータに関して、理解していることをまとめてみる。
@
を使ったデコレータの書き方はただのシンタックスシュガー
(読み書きのしやすさのために導入される構文)。
それでは、何に対するシンタックスシュガーなのか。
まず、以下のようなプログラムがあるとする。
def egg(): return "egg" if __name__ == '__main__': print(egg()) # egg
実行すると、当たり前だが以下の様な結果になる。
egg
次に、先ほどのファイルにscramble
というメソッドを追加する。
def scramble(egg): def _scramble(): return "scramble " + egg() + "!" return _scramble def egg(): return "egg" if __name__ == '__main__': scramble_egg = scramble(egg) print(scramble_egg()) # scramble egg!
scramble
メソッドは、オブジェクトを1つ受け取り、内部に定義されている_scramble
メソッドを返している。
つまり、scramble
メソッド自体は引数に対して何かを行うことはなく、
ただ内部のメソッドを返しているだけの機能しか持っていない。
返される内部メソッドの_scramble
は、scramble
メソッドが受け取ったの返り値に、
"scramble "と"!"という文字列を装飾(デコレート)して返している。
if __name__ == '__main__'
では、scramble
メソッドにegg
メソッド渡し、
返り値をscramble_egg
という変数に格納している。
scramble
メソッドの返り値は_scramble
のため、scramble_egg
変数は
_scramble
メソッドの内容と同じ挙動をする。
そのため、ファイルを実行すると以下の様な実行結果になる。
scramble egg!
さて、if __name__ == '__main__'
の中ではscramble
メソッドの返り値はscramble_egg
という変数に格納していた。
ここで、すでに定義されているegg
メソッドにscramble
の返り値を格納してみる。
Pythonはメソッドも変数も全てがオブジェクトのため、こういうことができる。
def scramble(egg): def _scramble(): return "scramble " + egg() + "!" return _scramble def egg(): return "egg" if __name__ == '__main__': egg = scramble(egg) # この部分と print(egg()) # この部分
実行結果は上記と同じscramble egg!
。
egg
に対してscramble
メソッドの返り値を格納しているため、egg
メソッドは
元の「"egg"という文字列を返す」という機能が上書きされている。
しかし、ただ上書きされたわけではなく、scramble
メソッドに元の機能を引数として渡しているため、
「"egg"という文字列を返す」機能は、_scramble
内で使われることになる。
この状態におけるscramble
メソッドがデコレータとなる。
文字通り別のメソッドを装飾することができる。
これまでの手順を@
を使って書き換えると、以下のようなコードになる。
def scramble(egg): def _scramble(): return "scramble " + egg() + "!" return _scramble @scramble def egg(): return "egg" if __name__ == '__main__': print(egg()) # scramble egg!
Webサーバをapacheからnginxに移行
今までapacheで動かしていたWebサーバをnginxにようやく切り替えた。 その中でしたnginxの設定とか詰まったところとかメモ。
nginxの設定
全体に関わる設定
http://www.nametake.info
のように、特に設定をしていないサブドメインに
アクセスがあっても、全部nametake.infoのページと同じ内容が表示されるようにしたい。
ということで、以下のようにdefault.conf
ファイルを書いて、デフォルトのバーチャルホストを設定する。
また、server_name
を_
にすることで、catch-allサーバとして設定。
default.conf
server { listen 80 default_server; server_name _; root /var/www/html; ~~~略~~~ }
あとは、nginxのバージョン番号とかOSの情報をレスポンスヘッダに含めたくないので、以下をnginx.conf
に追記。
nginx.conf
http { ~~~略~~~ server_tokens off; }
SSLに関わる設定
やりたかったこと
nametake.info
のサブドメインで動かしているownCloudとかampacheの通信経路を暗号化したい- 設定ファイルはサービスごとに分割したい
owncloud.conf
とかampache.conf
とかに証明書の情報をいちいち書きたくない
やったこと
以下のようにSSL全体の設定も記述(と言ってもほとんどexample_ssl.confのまんま)。 サーバ証明書と秘密鍵は、apacheを設定した時に作成したオレオレ証明書があるので、それをそのまま流用。
変更したのはlisten
の部分のdefault_server
と、server_name
の部分のバーチャルホスト。
ssl.conf
server { listen 443 ssl default_server; server_name .nametake.info; ssl_certificate /my/cert/file; ssl_certificate_key /my/cert/key/file; ~~~略~~~ }
上記のようにssl.conf
としてSSL全体の設定ファイルを作成することで、それぞれのサービスで
SSLを使用するときは、以下のように記述するだけでOK。
owncloud.conf
server { # 80番ポートでアクセスしてきたら443番ポートにリダイレクト listen 80; server_name cloud.nametake.info; # enforce https return 301 https://$server_name$request_uri; } server { listen 443 ssl; server_name cloud.nametake.info; ~~~略~~~ }
SSL通信したいサーバでlisten 443 ssl
と書くだけ。これで個別のファイルに証明書の情報を書かなくてよくなる。
サービスごとの細かいnginxの設定は[nginx (サービス名)]
とかで検索して出てきた結果をコピペ。
詰まったところ
ownCloudのログイン画面でリダイレクトループする
原因
/var/lib/php/session/
のパーミッションがapache:apache
のままになっていた。
解決策
/var/lib/php/session/
のパーミッションをnginx:nginx
に変更。
こんなところのパーミッションの設定もapacheになっていたのは想定外だった…… (serverのrootに指定したディレクトリの所有者がapacheのままになっていたのに気付かなくて30分ぐらい悩んだのは内緒)
.gitignoreの範囲の指定方法
過去に書いた記事をそのまま移行します。
前提
$HOME
ディレクトリ上でドットファイルをgitを管理している。
.gitignoreはホワイトリスト方式で記述している。
やりたかったこと
$HOME └── .vim ├── bundle ├── snippet ├── syntax ├── template └── userautoload
上記の構造になっているvimの設定ファイルのbundle
ディレクトリ以外をgitの管理下に置きたい。
(bundle
ディレクトリだけはgitで管理したくない)
やったこと
ホワイトリスト方式で.gitignore
を設定したが、階層構造をとっている時の設定でつまずいた。
ダメだったパターン
以下のように記述したら$HOME/.vim/
以下のディレクトリ内部のファイルが読み込まれなかった。
# all file ignore /* /.* # target not ignore !/.gitignore !/.vimrc !/.vim/template/template.* !/.vim/userautoload/*.vim !/.vim/snippet/*.vim !/.vim/syntax/*.vim
よく考えると、上の3行ですべてのファイルとディレクトリを場外しているため、
$HOME/.vim
ディレクトリがそもそもgitから見えない。
そのため、!/.vim/userautoload/*.vim
のようにファイルを指定しても、
!/存在しないディレクトリ/userautoload/*.vim
となっているので、
読み込まれることがなかった。
成功したパターン
以下のように記述したら成功した。
# all file ignore /* /.* # target not ignore !/.gitignore !/.vimrc # .vim directry !/.vim/ /.vim/* !/.vim/template/ !template.* !/.vim/syntax/ !*.vim !/.vim/snippet/ !*.vim !/.vim/userautoload/ !*.vim
やっていることは、
!/.vim/
で$HOME/.vim/
ディレクトリをホワイトリストに追加/.vim/*
で$HOME/.vim/
以下のファイルとディレクトリを除外!/.vim/userautoload/
で該当のディレクトリをホワイトリストに追加!*.vim
で*.vim
ファイルをホワイトリストに追加
という感じ。
ただ、一つのファイルで管理すると見栄えが悪かったので以下のように分割。
# $HOME/.gitignore # all file ignore /* /.* # target not ignore !/.gitignore !/.vimrc !/.vim/
# $HOME/.vim/.gitignore # all file ignore /* /.* !/.gitignore # target not ignore !/template/ !template.* !/syntax/ !*.vim !/snippet/ !*.vim !/userautoload/ !*.vim
これで管理しやすくなった。