/var/log/study

つまり雑記

PHP, Python, Golang を NGINX Unit で動かしてみた

NGINX Unit

ホームページは以下

www.nginx.com

もしくはミラーだけどGitHubが以下となる

github.com

RestAPIやJSONで設定できる、phpPHP-FPMやpythonwsgiサーバーなど言語ごとのアプリケーション・サーバーを集約したアプリケーションサーバーという感じ。なのでNginxの後ろで動くサーバーという認識で大丈夫なのかな?

まだversionは0.1なので、今後どんどん成長していくはず。

現状は以下に対応しているとのこと

Python 2.6, 2.7, 3

PHP 5, 7

Go 1.6 or later

ざっくりとした所感

  • プロダクトに関して
    • 言語ごとのミドルウェア運用がNGINX Unitに集約されて嬉しい可能性がある
    • Docker + NGINX Unit も嬉しいが、NGINX Unitだけでも十分に嬉しいかも
    • ベンチマークを取ってどれだけさばけるか試したい
  • 現状のドキュメントに関して
    • インストールはドキュメントどおり行えばOK
    • アプリケーション・サーバーとしての動作
      • PHPはさっくりと動かせた
      • Pythonもちょっとwsgiに関して少し調べれば動かせた。
      • Golangissue を見つけなかったら動かせなかったと思う
    • まだ細かいチューニング方法とかもドキュメントには記述されていなさそう?

と、言うことで、3つの言語で動かした際の動かし方を残しておく (最初にドキュメントを読んで、詰まった際の補助資料くらいに思うと良いはずです。)

動かし方

インストール後のサービスの動かし方

systemctl enable unitd.service
systemctl start unitd.service

気づき

  1. curl --unix-socket /run/control.unit.sock http://localhost/ で設定を確認できる.
  2. systemctl restart unitd.service をしたら設定が揮発する模様?

各言語ごとの動かし方をまとめ

要点だけ

PHP

設定で指定する rootindex で指定するファイルを置くだけ

Python

設定で指定するpathwsgi.py を設置, wsgi.py の中で applicationwsgiアプリケーションになっていればOK

例えばFlaskなら以下の様な感じ

#!/usr/bin/env python

from flask import Flask

application = Flask(__name__)

@application.route("/")
def index():
    return "<h1>Hello NGINX Unit</h1>"

Golang

こっちが一筋縄では行かなかった。

まず、unit をインストールしているだけではダメでCentOSでは unit-devel をインストールしておく必要があることと、

export GOPATH=/home/user/go_apps をしておいた後に、 unit パッケージをインポートして使う。

上記の状態で、以下のようなソースをビルドし、設定の executable で指定したパスに配置する。

package main

import (
    "fmt"
    "net/http"
    "unit"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "<h1>Hello NGINX Unit</h1>")
    })
    unit.ListenAndServe(":8000", nil)
}

普通のgolangのアプリと違うのは、 ListenAndServe するのが、httpではなく unit な点だと思う

個人的感想

NGINXが新しくサーバーをリリースする!というのは個人的に驚き。

ApacheよりもNGINXに慣れ親しんでいる世代ということもあり、頑張ればソースコードを読み切る事ができそうな気がする規模なので読みたい。

以下は全部この記事を書いた際のメモ

とりあえず動かしてみる

CentOS7でやってみる

[root@mycentos7 go-app]# cat /etc/redhat-release
CentOS Linux release 7.3.1611 (Core)

↑のインストールにそって yum -y install unit で問題なくうまくいく

ドキュメント的にはインストールで終わっているので動かし方を探ると以下の感じで動き出す。定石どおり。

[root@mycentos7 unit]# systemctl status unitd
● unitd.service - NGINX Unit
   Loaded: loaded (/usr/lib/systemd/system/unitd.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
systemctl enable unitd
systemctl start unitd
[root@mycentos7 unit]# systemctl status unitd
● unitd.service - NGINX Unit
   Loaded: loaded (/usr/lib/systemd/system/unitd.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2017-09-08 00:42:25 JST; 6s ago
  Process: 2502 ExecStart=/usr/sbin/unitd $UNITD_OPTIONS (code=exited, status=0/SUCCESS)
 Main PID: 2503 (unitd)
   CGroup: /system.slice/unitd.service
           ├─2503 unit: main [/usr/sbin/unitd --log /var/log/unitd.log --pid /run/unitd.pid]
           ├─2505 unit: controller
           └─2506 unit: router

プロセスとしては動いていそう。

[root@mycentos7 unit]# ps aux | grep unitd
root      2503  0.0  0.0  16168   632 ?        Ss   00:42   0:00 unit: main [/usr/sbin/unitd --log /var/log/unitd.log --pid /run/unitd.pid]
root      2516  0.0  0.0 112644   964 pts/0    R+   00:44   0:00 grep --color=auto unitd

lsofを叩いてもunitdが掴んでいるポートはなさそう?

[root@mycentos7 log]# lsof -i
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
chronyd   715 chrony    1u  IPv4  13918      0t0  UDP localhost:323
chronyd   715 chrony    2u  IPv6  13919      0t0  UDP localhost:323
sshd     1038   root    3u  IPv4  16617      0t0  TCP *:ssh (LISTEN)
sshd     1038   root    4u  IPv6  16626      0t0  TCP *:ssh (LISTEN)
master   1648   root   13u  IPv4  17586      0t0  TCP localhost:smtp (LISTEN)
master   1648   root   14u  IPv6  17587      0t0  TCP localhost:smtp (LISTEN)
dhclient 2260   root    6u  IPv4  18318      0t0  UDP *:bootpc
dhclient 2260   root   20u  IPv4  18307      0t0  UDP *:46788
dhclient 2260   root   21u  IPv6  18308      0t0  UDP *:gilatskysurfer

ドキュメントを読むと

By default, the Unit API is available in the control socket file unit.control.sock.

と書いてあるので、探すと /run/control.unit.sock があることがわかった。

[root@mycentos7 log]# find / -name 'control.unit.sock'
/run/control.unit.sock

ミニマルと呼ばれている設定を投げつけることにする

ドキュメントどおり以下の設定ファイルを

{
  "listeners": {
    "*:8300": {
      "application": "blogs"
    }
  },
  "applications": {
    "blogs": {
      "type": "php",
      "workers": 20,
      "root": "/www/blogs/scripts",
      "index": "index.php"
    }
  }
}

curl -X PUT -d @./start.json --unix-socket /run/control.unit.sock http://localhost/ で投げつける

レスポンスは以下の様

{
    "success": "Reconfiguration done."
}

ではphpを設置してみる。

[root@mycentos7 ~]# mkdir -p /www/blogs/scripts
[root@mycentos7 ~]# touch /www/blogs/scripts/index.php
[root@mycentos7 ~]# echo -e "<?php\n\nphpinfo();" > /www/blogs/scripts/index.php
[root@mycentos7 ~]# cat /www/blogs/scripts/index.php
<?php

phpinfo();

curl localhost:8300 できちんと反応する

自分のホストのIPだと反応がないと思ったが、firewalldを切ったらきちんと反応した.

ちなみにドキュメントの何処かに書いて有りそうだが、 curl --unix-socket /run/control.unit.sock http://localhost/ で設定を確認できる.

また systemctl restart unitd.service をしたら設定が揮発した。たぶんココらへんは今後改良される点なはず。

Python

こうなったらpythonもためしたい。

またしてもドキュメントどおりの設定を投げつけ、

{
  "applications": {
    "blogs": {
      "type": "php",
      "user": "nobody",
      "group": "nobody",
      "workers": 20,
      "root": "/www/blogs/scripts",
      "index": "index.php"
    },
    "wiki": {
      "type": "python",
      "user": "nobody",
      "group": "nobody",
      "workers": 10,
      "path": "/www/wiki",
      "module": "wsgi"
    }
  },
  "listeners": {
    "*:8300": {
      "application": "blogs"
    },
    "*:8400": {
      "application": "wiki"
    }
  }
}

/www/wiki/wsgi.py を以下のように作る

[root@mycentos7 wiki]# cat wsgi.py
#!/usr/bin/env python

from flask import Flask

application = Flask(__name__)

@application.route("/")
def index():
    return "<h1>Hello NGINX Unit</h1>"

こちらもそつなく動く

golang

ついでだからgolangも書いておく。今度はドキュメントからチョット変更して以下のような設定を投げつける

{
  "applications": {
    "blogs": {
      "type": "php",
      "user": "nobody",
      "group": "nobody",
      "workers": 20,
      "root": "/www/blogs/scripts",
      "index": "index.php"
    },
    "wiki": {
      "type": "python",
      "user": "nobody",
      "group": "nobody",
      "workers": 10,
      "path": "/www/wiki",
      "module": "wsgi"
    },
    "go_server": {
      "type": "go",
      "executable": "/www/server/bin/server",
      "user": "nobody",
      "group": "nobody"
     }
  },
  "listeners": {
    "*:8300": {
      "application": "blogs"
    },
    "*:8400": {
      "application": "wiki"
    },
    "*:8500": {
      "application": "go_server"
    }
  }
}

でもって以下のようなコードを作って... と思ったら、golangは普通にnet/httpを使うのではダメな模様

https://github.com/nginx/unit/issues/8/ に書いてある/usr/share/doc/unit/examples/go-app/let-my-people.goを覗くと unit というパッケージがある

package main

import (
    "fmt"
    "net/http"
    "unit"
)

func handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Add("Content-Type", "text/plain");

    fmt.Fprintf(w, "Method : %s\n", r.Method)
    fmt.Fprintf(w, "URL    : %s\n", r.URL.Path)
    fmt.Fprintf(w, "Host   : %s\n", r.Host)
}

func main() {
    http.HandleFunc("/", handler)
    unit.ListenAndServe("8000", nil)
}

上記をissue真似て, sudo yum -y install unit-devel をしたあとexport GOPATH=/home/user/go_apps, 以下のファイルを go build して /www/server/bin/server としてファイルを設置したら curl localhost:8500が上手くいった.

package main

import (
    "fmt"
    "net/http"
    "unit"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "<h1>Hello NGINX Unit</h1>")
    })
    unit.ListenAndServe(":8000", nil)
}

以上でPHP, Python, GolangでUnitを動かしてみる事自体は完了となる

備忘録 自宅リージョン 2017/08/26

yaaamaaaguuu.hatenablog.com

yaaamaaaguuu.hatenablog.com

↑の記事から半年の間に色々と試した結果、自宅の構成が固まってきたので備忘録を書こうと思った。

vSphere環境を自由に使える状態になったので、ミドルウェアの使いかたの学習やちょっとしたアプリ開発VMがほしいときにクラウドサービスを使う必要が無くなった。

(ちなみにvSphereとかはライセンスの問題で定期的に入れ替えて使っている)

機材

半年前から買い足した機材は2台

Ubiquiti Networks Edgerouter ER-X(日本国内)

Ubiquiti Networks Edgerouter ER-X(日本国内)

VyOSとかを触る機会を作りたいなぁと思っているところでEdgerouterを知った。

NASはvSphere環境でvmotionしたくて買った。自宅でもvMotionできると何かと便利な気がする。最近Synologyが良いと聴くけど基本的に機材の良し悪しはよくわからない事と、switchや以前から使っている無線ルーターがNETGEARなのでNASもNETGEARにした。

あと本当はNUCを1台買い足しているけれど、メモリが高騰していて買えていないので投入できていない

現状のネットワーク図

学習用途を兼ねているし、ネットワークの設計や仮想化環境の扱いはほぼ素人なので、その手の人から見たら無駄や設計ミスがあると思う。

基本的にはhomeのセグメントに接続しているmacからmyprivateのセグメントに置いたVMsshで繋いで検証とかを行っている。Edgerouterとvyosでルーティングしている

f:id:yaaamaaaguuu:20170826171333p:plain

  • 共通
    • 各セグメント共にEdgerouterがDHCP + DNS + NTPのサーバーの役割を果たしている
  • homeのセグメント
    • 今まで通り家庭用のネットワークとして使う
    • laptopとかスマホとかをwifi経由で接続する
  • managementのセグメント
    • 有線でつなぐためにあけてある
    • 冷静に考えて無駄な気がしている
  • mycloudのセグメント
    • 実際はswitchにぶら下がっている
    • 物理ホストやNAS, vCenterサーバーなどを置く所
  • myprivateのセグメント
    • 適当にVLANを切っている
    • 検証用のVMを置く所

反省と今後

vSphereの仕様上、VMの足を生やす上限は10個までとなっているので、検証で切りたいセグメントが10を超えたらリソース消費との兼ね合いでvyosをチョットずつ継ぎ足していくのかな?とか考えている。

この環境を作る途中、困ったタイミングで適当にVMKernelを作ったりしたのでipの払い出しがいい加減な所がちょこちょこある。もっとvSpereの仕様とかを把握したほうが良いはず。

またこの環境は基本冗長構成が取られてないし、各機器の監視なども特にしていないのでチョットずつ育てていこうかな?と言う気持ちになっている。

git remote がhttpsでbasic認証している時の自動化方法

前提となる話

gitのリモートには git://https:// が使える。要はsshhttpsかという違いで、諸事情によりsshを利用せずhttpsを使いたい場面がある。

sshならばパスフレーズのないdeploy keyなどを用意したら、あとはキーの取り回しをどうしたら良いか?で事が済むが、httpsかつ認証が必要なリモートリポジトリを扱う際はパスワードを入力する必要が出て来る。

httpsを利用した際の認証はbasic認証なので、 git clone をする際に認証情報を埋めたら自動化できるのだが、git remote -v で認証情報が丸見えとなる。(と言うか、自分の作った環境を先輩に見せたら丸見えの状態で非常に恥ずかしかった) ので、以下のURLで示されている方法は得策ではない。

stackoverflow.com

.netrc を利用するといちいちパスワードを入力する必要も無い。しかし通常は平文、チョット気を使うと暗号化できたりするが、何れにせよユーザーとパスワードを保存した状態となるので、様々な人が触るサーバーで .netrc を使うのは得策と言えないだろう。

加えてスクリプトから扱う専用のユーザーをリモートリポジトリのサーバーに作りたくない。リモートリポジトリ側のユーザー管理が雑になる可能性があるので、極力普段使っているユーザーの認証情報で済ませたい。

ではansibleやjenkinsなどの自動化ツールを使った際に認証付きhttpsリポジトリを扱うのはどうしたら良いか?という点に話を移すと、対話が必要なときは expect コマンドなどで頑張るのが回答の候補に上がるだろう。個人的には expect とか泥臭すぎると思うのでその方法は避けたい。

ということで今回は以下の前提で話を進める

  1. httpsかつ認証付きのリモートリポジトリ
  2. 様々な人が触る環境なので、ユーザーに紐づく状態を作りたくないという
  3. 自動化用のユーザーは作らない
  4. expectは使わない

git credential helper

環境変数経由で渡せるとansibleとかjenkinsで簡単に自動化ができるなぁと思いながら、gitを調べていたら credential helper なるものが有るのを発見した。

Git - 認証情報の保存

使いかたとか設定方法は大体上記に書いてある。

git config --global credential.helper cache

と設定すると、ファイルには設定を保存せず、ある程度の時間認証情報を保持してくれるようになるとのこと。

加えて以下を実行すると認証情報が渡せるとのこと。

git credential fill

惜しい。なにが惜しいか?というと、さらっと調べる限り、標準入力等では git credential fill に情報を渡すことはできなさそう?だった。

自動化方法

Git - 認証情報の保存 には 独自の認証情報キャッシュ という項目がある。

サクッと読むと git hook と同じように、独自コマンドを利用することが可能らしい。サンプルに乗っているRubyをパクって、環境変数を渡すようにしたら良さげ。

ということで、以下のようなスクリプトを実行権限付きで、 /usr/local/bin/git-credential-env とかで設置し、 git config --global credential.helper env とかすると、環境変数経由で認証情報を渡せるようになる。

gist.github.com

あとは、ansibleのenvironmentやjenkinsのパラメーター付きビルドで GIT_PROTOCOL, GIT_REMOTE_HOST, GIT_USER, GIT_PASSWORD を指定してやるといい感じに自動化できる。

スクリプトデバッグは以下で。

GIT_PROTOCOL=https GIT_REMOTE_HOST=github.com GIT_USER=hoge GIT_PASSWORD=fuga ./git-credential-env.sh get
protocol=https
host=github.com
username=hoge
password=fuga

注意点

  • スクリプトshebangは環境によって適度に変えてください。
  • 環境によってはcredential.helperを既に設定している可能性があるので、むやみに上書きしないように。

#builderscon に参加してきました。

builderscon.io

に参加してきました。

企業スポンサーのチケットで参加してきた形になるので、細かいレポート的なことは会社のブログで書くことになるので、自分のブログでは個人的な感想とかを書き残そうかと思います。

個人のブログとは言え、所属する会社との関係も多少あるので書いておきます

本記事での発言は個人の責任で、所属する組織を代表するものではありません。

f:id:yaaamaaaguuu:20170805203925j:plain

参加したセッション

一応buildescon2017が開催されている時間帯は全部いました。

  • 前夜祭
    • オンプレミスデータセンター撤退!
    • データストア撤退の歴史
    • PaaS完全撤退の歴史
    • ブロックストレージとの戦い、そして撤退
  • 初日
  • 2日目
    • ココまでできるmruby
    • 小さく始めるて育てるコンパイラ
    • ランチセッション【PR】エンジニアがkintoneを使うべき3つの理由 サイボウズ株式会社
    • AWS CodeBuild を使ってものすごい並列数で CI を実行しよう
    • OSS貢献超入門
    • The Evolution of PHP at Slack HQ

難しいなと思ったこと

楽しくない話しは先に書いておきます。

リアルのイベントを開催すると集客人数とかセッションごとの人の分散とかを想定しづらいのだろうなぁと思いました。前夜祭やランチセッション等で利用していた会場は若干席不足感が否めなかったです。ただ会場サイズには限りがあると思いますし、参加者全員を収容できる場所なんてなかなかないはずなので難しいなぁと思った次第です。もっとスポンサーとか集まると解消するのでしょうかね?

お昼ごはん

ランチセッションで提供されたお弁当美味しかったです。

f:id:yaaamaaaguuu:20170805204054j:plain

羨ましいなあと思ったこと

しゃもじ。

会社で振り回したかったなぁという気持ちです。

真面目な感想

まずSNSでの拡散禁止になっていた前夜祭が めちゃくちゃ おもしろかった!!! 拡散禁止になるレベルの話なので面白くないわけが無いのですが、やっぱり聞いたら面白かったです。各社 ~お腹が痛くなるような~ 苦難を乗り越えて今があるんだなぁって感じです。この様な話を聴くと、SNS拡散禁止のリアルなイベントに参加する価値がとても高いなぁと感じました。

2014,2015のyapcに参加し、1年飛んでbuildersconに参加したのですが、カルチャーが受け継がれているなぁと思いました。プログラミングを始めたのが大体2013年で、始めた当時からyapc ~ buildersconがあった身分なのですが、今年も各セッションで頑張る気持ちが高まる話が聞けて色々頑張るぞ!!!って気持ちになってます。

加えて、個人的なブログなので好き勝手書くと、PHPでwebアプリケーションを書いている人がいっぱいいて羨ましい気持ちになりました。現在のお仕事も楽しいので不満等は全く無いのですが、PHPを書くことは無く、むしろPHPを別のモノに置き換える仕事がメインで、PHPでプログラミングを覚えてきた身としては寂しさがあるのです。(PHPの話しを書いていてふと思ったのですが今年はPerlがメインな話がもしかしたら無かった?)

今後に向けて

2014年(過去のセッションの動画や資料をあさっているのでそれをカウントするともうチョット昔)から、builderscon界隈のコニュニティからは知見を頂きっぱなしになっているので、来年はLTとか本編のプロポーザルを書こうと思います。自分がやって来たことに対する知見等をコミュニティに還元できればなぁと思ってます。

しゃもじは欲しかったので、来年もそのようなサプライズがあることを期待しつつ個人スポンサーするのを忘れないようにしたい気持ちもあります。

また、所属している会社がスポンサーをさせてもらった割に、お金しかだせていないのはなんだか申し訳ない気持ちになったので来年は個人的に、スタッフとして協力も出来ればよいなぁと考えています。

要はbuilderscon最高だと思っているので、何かしらでもっと協力をさせてもらいたいなぁと思っています。

最後に

どうせブログのアカウント名をググると自分の各種アカウントしか出てこないはずなので。

ありがとうございました。

Docker Compose の fluentd ロギングドライバの設定で環境変数を使いたい時に見るページ

ロギングドライバの設定で環境変数を使いたい

個人的にはfluentd driverを使う時のタグの文字列を環境変数を利用して組み立てたかった

ドキュメントと記事

docs.docker.com

https://docs.docker.com/engine/admin/logging/log_tags/docs.docker.com

https://docs.docker.com/engine/admin/logging/fluentd/docs.docker.com

要は上記をきちんと読めば分かること(だけど自分は詰まった)

使うまでの3ステップ

  1. envファイルを読み込む
  2. --log-optenv=HOGE で利用したい環境変数を渡す
  3. --log-opttag=(.ExtraAttributes nil).HOGE の様な形で引き渡す

compose-fileに落とし込むと以下の様な感じ

env_file: .env
logging:
    driver: fluentd
    options:
        env=HOGE,HUGA
        tag="(.ExtraAttributes nil).HOGE-(.ExtraAttributes nil).HUGA"

gitリポジトリをCIするのに Jenkins Pipeline もしくは Jenkinsfileが辛いなぁと思った話

背景

  • いくつかでCIをやってみた
    • golangのプロジェクトをgitlab CI で
    • swiftのiosアプリをbitrise.ioで
    • CircleCI で Railsのアプリ
  • システムではなくソースコードをCIしたい
  • 本家?のJenkinsはやってみてなかった
    • GUIでポチポチ形式が割りと嫌
  • そう言えばJenkinsfileがあるらしい
    • 宣言的にCIの内容を書ける
    • チャレンジしてみよう

Jenkinsfileとは

jenkins.io

  • GroovyでCIのjobのステップを宣言するやつ
  • とは言えJenkins上でプログラムだと何でも動かせるので制約がいくつか付いている
    • 制限突破をするための設定があるので余り気にならない可能性はある
  • Jobの実行時にYes/Noをinputできるようにしている

辛さ

ちょっと触って詰まって調べた程度なので間違いはあるかもしれません

  • Jenkinsが想定している前提が広いこと
    • 他のCIならばgitとリポジトリに対するイベントが前提となっていると思う
  • Jenkinsfileが閉じていること
    • webhookでキックすることはできる 
      • Jenkinsfileだけではwebhookのpayloadが拾えない
      • webhookのpayloadを拾うにはJenkinsfile以外の仕組みが必要
    • 環境変数がJenkinsのGUIから設定できない
      • チャットへの通知用にtokenとかは以下を実現したい
      • 厳密にいうと環境変数へのアクセスはできるが、変数はサーバーにログインして設定する必要があるはず
  • いろいろやろうとするとJenkinsfile外との連携を考える必要が出てくる
    • Jenkinsfileの良さを殺している

雑記

冒頭で述べたように個人的にはGUIでしか設定できないのは嫌なのだが、「Jobの内部はJenkinsfile, 外部とのやり取りはGUIで設定」という現状は結局GUIでの設定に依存していてはっきり言って不便。

ソースコードをCIするだけならば、GitlabCIもしくはSaaSのCIが圧倒的に便利。前提として考えているワークフローがJenkinsとそれ以外では違いすぎる。

JenkinsはOSSだから文句を垂らすならコントリビュートする努力をしたほうが良いのだろうけど、便利な代替があるので、不便なJenkinsfileを改善する努力もするつもりが無い。

自分の観測範囲だとJenkinsはスクリプトGUIを提供するためのラッパーツールっぽくて、CIツールとしてはあまり使われていない。多分だけど自分の観測範囲外でも同じような使われかたをしているのでは?と思えるJenkinsfileの機能だと思った(Job実行時のyes/noとか)。ただ僕にとってはスクリプトをラップしたGUIとか何も有り難みが無い。結局設定されているスクリプトを見るし、直接たたいてオプションとか調べるし。調べた後はJenkinsが無駄なGUI層として残ることになる。

が、スクリプトをラップしたGUIは、スクリプトを読めない人でも安心して実行することができる利点?があるので、Jenkinsはそっちの方で生き残るのかな?

ココまで書いて、「自分が主体的に使うシーンを想定すると割りと辛い気持ちになり、他人のためにスクリプトのラッパーを用意してあげるだけならばJenkinsで良いのかも。」と思った。

pythonでslackのmessage buttons

TL;DL

api.slack.com

こんなのがあったから, 試すがてらPythonで再実装してみた.

github.com

SSLとかはLet’s Encryptとかでよしなにやってほしい。

1点注意としては、現在Firefoxからslack appの作成をしようとするとどのチームでslackアプリを作るか?という選択肢が選べない。Chromeとかだと上手くいく。

背景とかモチベーション

  • 現在の会社で所属しているチームには定型的なオペレーションがある
  • 手順は確立されているがもっと効率化, 自動化したい
    • が、最終的な対応の実施可否は人間が判断したい
      • 実行しますか?の問いにyes, noで回答させて欲しい

という背景があった上で、

  • 通知を受け取った人間がCLIで1コマンド叩く形の自動化は嫌
  • Jenkinsとかも違うなぁと思った
  • 通知と対処をシームレスに実行できれば嬉しい

そんなこんなでインタラクティブにシステムとやりとりできるslack message buttonsがいい感じに自動化のUIになってくれるのではないか?と思って試した。

個人的には以下の流れができたら最高だなと思っていた。

(一連の流れは全てslackのメッセージとしてlog化)
なにかしらの出来事 -> slackへの通知 (yes, noのbuttonの送信) -> yesを押したらスクリプト等の実行
                                                       -> noなら何もしないで終了

成果物

  • TL;DLのgithub
  • pythonにポートをした理由は所属するチーム的な理由
    • 主な利用言語にpythonがあり, nodeは使ってないから

所感

  • slack の message buttonはSSL必須
    • 試すだけなら, 単純に手間もしくは制約が多くなるだけ
      • Herokuで制約をとるか
      • Let’s Encryptで手間をかけるか
  • とはいえ、たんなるコミュニケーションツールとして、message button を用意するのは面倒だなぁと思った.
    • message button以外の(SSLが任意の)apiでなんとか賄える気がする
  • slackのUIでオペレーションをするのは厳しい
    • message button をレスポンスするアプリケーションのセキュリティ的な問題

問題点

どこまでslackを信用するかの問題。

message button appの作りとしては、以下の2つの点でアプリケーションのセキュリティを担保しようと考えている

  1. httpsでの通信
  2. slackしか知らない, slack app用の Verification Token を使う
    • アプリケーション側で Verification Token をチェックする

コレだけだと、slackから送信されてくるリクエストによく似たリクエストが送られてきたら対処のしようが無い.(特に、Verification Tokenが漏れたとかのケースを考えると…)

個人的には上記に加え, 以下の対策が取れれば良いのだけどなぁと思った。

  • slackのリクエストもとIPもしくはIP帯が公開されていること
    • slack appを動かすサーバーに対して、送信元IPでFWをかけられるようにする

送信元IPで制限がかけられればVerificationTokenが漏れましたとかなっても大丈夫なはず。

しかし、結局これでも、 万が一slackのサーバーが乗っ取られて「slackのサーバーからいろいろrequestを投げてみました(´・◡・`) 」 みたいな自体になると対処のしようが無い。

message button でアンケートが取りたいとかの、「ちょっとしたコミュニケーションの延長線上の事をやりたい」程度なら万が一message button用のアプリに不正なリクエストがあっても問題ないはずだが、「インタラクティブに実行したいオペレーション」を実行するアプリケーションに不正なリクエストがあった時のことを考えると結構厳しいはず.

なので問題点としては、以下となると思っている.

どこまでslackを信用するか

ただし、slackの問題というより、コミュニケーションツールをコミュニケーションから大幅に外れた用途で使おうと検討した自分が原因なので、slackには全く落ち度が無いと思っている.

まとめ

  • slackでサービスのオペレーションはリスクがあるのを認識できた
    • 実際に手を動かしてみたから, そこら辺に気づけたと思っている
  • 個人的考えてた用途としては厳しい