カテゴリー別アーカイブ: サーバー

AzureでSlackのOutgoing Webhooksを使って特定のキーワードに反応するbotをサクッと作る

Slackには外部のアプリと連携する強力なAPIが多数用意されています。

その一つであるOutgoing Webhooksを使って、特定キーワードに反応するbotを作ってみました。

こんな感じで、ラーメンに反応してランダムにラーメン画像を投げてくれます。

Outgoing Webhooks

Outgoing Webhooksは、Slackの投稿内容を外部のサーバーにPOSTできる機能です。

また、サーバーの応答内容によって、botからSlackに自動的に投稿を行うこともできます。

今回はAzure App Serviceを使ってサーバーを用意し、Slackから呼び出すような形にします。

なお、サーバーにはこんな感じのPOSTが飛ぶようです。

token=XXXXXXXXXXXXXXXXXX
team_id=T0001
team_domain=example
channel_id=C2147483705
channel_name=test
timestamp=1355517523.000005
user_id=U2147483697
user_name=Steve
text=googlebot: What is the air-speed velocity of an unladen swallow?
trigger_word=googlebot:

今回は、textキーの内容を使って投稿内容を制御したいと思います。

Azureを使う意味

  • サーバー建てるのが楽
  • メンテが楽
  • Visual Studioから簡単にデプロイできる
  • Microsoft Imagineなら無料!(重要)

とは言いつつ使いこなせてない感があり、この記事でも無理やり動かしている箇所がいくつかあります。

用意するもの

  • Visual Studio 2015
  • Python Tools for Visual Studio (PTVS)
  • 適当なMicrosoft Azure サブスクリプション

Djangoプロジェクトを作成

Visual StudioにPTVSをインストールして、Django Web Projectからプロジェクトを新規作成します。

作成したら、Python環境としてInstall in a virtual environment…を選択してください。

Azure App ServiceへアップロードするにはPython2.7か3.4が必要なため、今回はPython3.4に作成しています。

なお、実を言うとAzure App Serviceに入っているPython3.4には問題があるため、後でPython3.6をAzureのダッシュボードから入れます。

しかしAzureにデプロイするためには実行できる環境が必要なため、とりあえずインストールしておいてください。

インストールが完了したら、virtualenvにrequestsモジュールを入れます。

ソリューションエクスプローラーからPython Environmentsで先ほど作成した環境を右クリックして、「Install Python package」を選択します。

入力画面が出るので、requestsと入力してOKをクリック。

インストール出来たら、いよいよソースコードを変更していきます。

ソースの編集

編集するファイルは、以下の5つとなります。

  • app/views.py
  • app/get_keyword_image.py (新規作成)
  • your_project_name/urls.py
  • your_project_name/settings.py
  • web.config

app/views.py

以下を追加します。

from django.http.response import JsonResponse
from app.get_keyword_image import get_image_url
from django.views.decorators.csrf import csrf_exempt


@csrf_exempt
def get_image(request):
    keyword = request.POST["text"]
    if "ラーメン" in keyword and "ラーメンを検知した" not in keyword:
        if len(keyword) > 10:
            keyword = "ラーメン"
        url = get_image_url(keyword)
        return JsonResponse({"text": "ラーメンを検知した {0}".format(url)})
    else:
        return JsonResponse({})

ラーメン画像のURLを含むテキストをJSONで返すviewを定義しています。

(後でちゃんとトークンでの認証を実装しなければと思いつつ、まだやってない・・・)

app/get_keyword_image.py

import requests
import random

api_key = "your_api_key"
search_engine_id = "your_search_engine_id"
search_format = "https://www.googleapis.com/customsearch/v1" \
                "?key={0}&cx={1}&num=10&start={2}&q={3}&searchType=image"


def get_image_url(keyword):
    start_from = random.choice(range(20))
    res = requests.get(search_format.format(api_key, search_engine_id, start_from, keyword), verify=False)
    if res.status_code != 200:
        return None
    res_json = res.json()
    url = random.choice(res_json["items"])["link"]
    return url

if __name__ == "__main__":
    print(get_image_url("ラーメン"))

ちょっと雑過ぎで申し訳ないですが、ランダムに指定されたキーワードから検索して画像URLを取ってくるプログラムです。

api_keyとsearch_engine_idはこのページこのページを参考に取得してください。

your_project_name/urls.py

urlpatternsに以下のように追加してください。

urlpatterns = [
    ~中略~
    url(r'^get_image$', app.views.get_image, name='get_image'),
]

your_project_name/settings.py

以下の部分を変更してください。(ALLOWED_HOSTSにはデプロイ時のURLを指定してください)

DEBUG = False

ALLOWED_HOSTS = ["yoursite.azurewebsites.net"]

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

念のためSSLに強制的にリダイレクトする設定を追加します。

SECURE_SSL_REDIRECT = True

web.config

ソリューションエクスプローラーからプロジェクト名を右クリックして、「追加」→「既存の項目を追加」を選択し、web.configを追加してください。

web.configのhandersディレクティブを以下のように編集します。

    <handlers>
      <add name="PythonHandler" path="*" verb="*" modules="FastCgiModule" scriptProcessor="D:\home\python362x86\python.exe|D:\home\python362x86\wfastcgi.py" resourceType="Unspecified" requireAccess="Script"/>
    </handlers>

デプロイ

ソリューションエクスプローラーからプロジェクト名を右クリックして、「公開」を選択してください。

発行先をMicrosoft Azure App Serviceに設定し、適当なサブスクリプションを選択し、App Serviceを新規作成してください。

発行が終わるとブラウザが立ち上がりますが、Python3.6がインストールされていないためエラーになっています。

Python3.6のインストール

最後にPython3.6をApp Serviceにインストールします。

AzureダッシュボードからApp Serviceを選択し、拡張機能タブから「追加」を選択します。

その中からPython 3.6.2 x86を選択し、「追加」をクリックしてください。

完了したら、App Serviceのコンソールタブに移動して、以下のコマンドを実行してください。

> cd D:\home\python362x86
> python -m pip install django requests

成功すれば、サイトにアクセスできるようになっているはずです。

curlなどで https://yourapp.azurewebsites.net/get_image にPOSTしてみて、成功するか確認してください。

Slackでの設定

このページの「Slackでの設定」セクションなどを参考にして、Outgoing Webhooksを追加します。

URLには作成したサイトのURLを指定し、Trigger Wordは空にしておきます。

これでいちおうbotが動くようになります。

所感

「サクッと」という肩書きで記事を書きましたが、けっこう手順が煩雑ですね。本当に申し訳ない(

やはりサーバーを用意するのは敷居が高いですよね・・・

最後の方は力尽きてあまり丁寧に書けなかったので、適時追記をしていこうと思います。

LetsEncryptの証明書更新でError getting validation data

LetsEncryptの証明書更新でエラーが起きていたので対処。

certbot certonlyで証明書を更新する際に、以下のようなエラーが起きました。

Failed authorization procedure. sakaki333.com (tls-sni-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Error getting validation data

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: sakaki333.com
   Type:   connection
   Detail: Error getting validation data

   To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A/AAAA record(s) for that domain
   contain(s) the right IP address. Additionally, please check that
   your computer has a publicly routable IP address and that no
   firewalls are preventing the server from communicating with the
   client. If you're using the webroot plugin, you should also verify
   that you are serving files from the webroot path you provided.

エラーの内容は「サーバーには到達できたが、確認データの取得でエラーが起きました。DNSの設定とかIPアドレスが正しいかなどチェックしてください。」という感じでしょうか。

今まで普通に成功していたのでおかしいと思いDNSの設定やらルーターからの経路やらを確認しましたが、怪しい部分は発見できず。

結局何もわからず最後の手段と思い、ubuntuを更新してrebootしてみました。

# apt update
# apt upgrade
# reboot

再起動後にもう一度試すと、今度はなんと成功しました。

SSL関連のパッケージが更新された影響でしょうか。どちらにせよ、解決できてほっとしました。

それにしても、このエラー文で解決策がパッケージのアップデートとは、なかなか不親切ですね・・・

docker stackコマンドでのtips

dockerにswarm modeが追加され、KVS無しでもオーバーレイネットワークが使えるようになりました。

ただ、swarm modeでは以前のdocker-composeコマンドでオーバーレイネットワークに接続することはできず、docker stackコマンドを使います。

この辺りは別で記事を書くとして、取り急ぎ運用で困った事とその解決策(妥協策?)を書いていきます。

.env ファイルが使えない

docker-composeやdocekr stackではdocker-compose.ymlファイルにサービスの設定を書きます。

また、docker-composeコマンドはカレントディレクトリの.envファイルを読んで環境変数をセットしてくれました。

しかしdocker stackは現状では残念ながら.envファイルを読んでくれません。

そこで、以下のコマンドで暫定的に対応することにします。

# env $(cat .env | grep ^[A-Z] | xargs) docker stack deploy --compose-file docker-compose.yml stack_name

これで環境変数が展開されるみたいです。

認証が必要なregistryを使う

–with-registry-auth オプションを使うといいようです。

# docker stack deploy --with-registry-auth --compose-file docker-compose.yml stack_name

今はまだ不便なところもあるdocker stackですが、swarm modeは以前よりデプロイが簡単でとてもいいです。

今後のアップデートでどんどん使いやすくなっていくと思いますので、そのあたりはすごく期待が持てそうですね。

自己証明書でDocker registryを構築

発端

以前から自分でビルドしたイメージを保存するためにLets Encryptの証明書を使ったdocker registryを持っていましたが、以下の理由から自己証明書を使ったものに切り替えました。

  • 外部公開する必要がない
  • 自己証明書の方がお手軽に建てられる
  • ドメイン名の縛りが無いので、LAN内で複数建てられる

作業

実際の構築はほとんどこちらの記事を参考にしました。

# mkdir certs auth data
# openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key -x509 -days 365 -out certs/domain.crt
# htpasswd -Bbn user password > auth/htpasswd
# vim docker-compose.yml
registry:
  restart: always
  image: registry:2
  ports:
    - 5000:5000
  environment:
    REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
    REGISTRY_HTTP_TLS_KEY: /certs/domain.key
    REGISTRY_AUTH: htpasswd
    REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
    REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
  volumes:
    - ./data:/var/lib/registry
    - ./certs:/certs
    - ./auth:/auth
# docker-compose up -d

これで構築は完了です。

あとは各ノードに先ほど作成したdomain.crtを配置します。

これについては公式ドキュメントが参考になりました。

domain.crtを対象ノードにコピーして、/etc/docker/certs.d/{ドメイン名:ポート}/ca.crt に配置してください。

これで自己証明書のregistryを警告なしで使えるようになります。

# docker login {ドメイン名:ポート}
(ユーザー名:パスワードを入力)
# docker pull busybox
# docker tag busybox {ドメイン名:ポート}/test
# docker push {ドメイン名:ポート}/test
# docker rmi {ドメイン名:ポート}/test
# docker pull {ドメイン名:ポート}/test

以前までinsecure registryに設定を書いていましたが、こっちの方が全然楽でいいですね。

今後はこちらの方法を使っていきたいと思います。

Goでsocketに対して自前のヘッダーでPOSTする

最近Go言語で物を作っています。この言語、楽しいんですが、使いこなすのが難しそうですね・・・

というわけで、Go言語でUNIX domain socketに対して自前でヘッダー作ってPOSTしたかった話です。

簡単にできる方のnet/httpモジュールを使いたかったのですが、UNIX domain socketにファイル名から接続することはできませんでした。

そこで調べているとnetモジュールから直接ソケットに接続してデータを送る方法がありました。

ヘッダーも自分で作る必要がありますが、とりあえずこの方法でやってみようと思います。

    conn, err := net.Dial("unix", "/path/to/your/go.sock")
    if err != nil {
        log.Printf("error: %v\n", err)
        return
    }
    defer conn.Close()
    fmt.Fprintf(conn, "POST /get HTTP/1.0\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 4\r\n\r\nid=5")
    readBuf := make([]byte, 1024)
    readlen, err := conn.Read(readBuf)
    if err != nil {
        log.Printf("error: %v\n", err)
        return
    }
    fmt.Println("response: " + string(readBuf[:readlen]))

ポイントはヘッダーにContent-Type: application/x-www-form-urlencodedとContent-Length: 4を入れているところです。サーバーもGoで書いているのですが、最低限このヘッダーが必要らしく、しばらく調べ回ってしまいました・・・

参考

  • https://github.com/kaneshin/playground/blob/master/go/net/listen-unixdomainsocket/main.go
  • https://golang.org/pkg/net/
  • https://support.f5.com/kb/en-us/solutions/public/2000/100/sol2167.html
  • http://rarara.cafe.coocan.jp/cgi-bin/lng/vc/vclng.cgi?print+200606/06060021.txt

【Android】WebViewでBasic認証のダイアログが表示されない件

先日、取引先とLINEで連絡を取っていて、BASIC認証をかけたテストサイトのURL送ることがありました。

すると、連絡先の人から「見れない」というメッセージをいただきました。

おかしいと思いURLをタップしてみると、確かに401エラーが出ており、見ることができません。

リロードしても認証画面は出ず、解決手段を模索することになりました。

原因

このサイトを見ていると、どうやらBASIC認証のヘッダーがきちんと設定されていない気がします。

ただ、他のブラウザからは正常に認証画面が表示されていたので、長く使うサイトでもないですし認証さえ通せば取り急ぎは問題ないと判断しました。

解決策

このサイトを参考にして、URLに認証情報を含めました。

https://user:passwd@www.somesite.com

URLをトラッキングされると認証情報を取られる可能性がありますが、今回は認証をかけたいわけではなく公開状態にしたくないだけだったので、取り急ぎはこれで問題なしとします。

【サーバー】PT3運用に最高な省電力マザーボードを発見!

2017-04-09追記;本日Celeron J3160のページを確認したところ、VT-dがFalseとなっていました。このCPUにPCIデバイスを仮想マシンに渡す機能はありません。申し訳ありませんでした・・・

以前から省電力サーバーでPT3を運用するにあたり、いろいろと考察を行ってきましたが、少し前に理想的なマザーボードが発売されたので紹介します。

その名も「ASRock J3160DC-ITX」です。

(画像は公式へのリンクです)

とりあえず特徴だけ紹介しますが、私も暇ができ次第買って試してみたいと思います!

安くてVT-dが使える

このマザーを見かけたのは近くのドスパラでしたが、13000円でCPUも電源もついてきます。

img_20161031_144850

これだけでもかなりお買い得ですが、肝心なのはIntel VT-dが使えることです。

Intel VT-dは仮想化支援機能の1つで、PCIデバイスを仮想マシンに渡すことができます。

つまり、PT3を仮想化したマシンで使いたいときはこの機能が必須ということですね。

以前から省電力なマシンでVT-dが使えるものがないか探していましたが、そもそも古いCPUはこの機能が無効になっていることも多く、良い物に出会えずいました。

PT3を仮想マシンに渡すことのメリットですが、ドライバの更新を仮想マシン内で行うことができるので、ホストマシンをシャットダウンしなくてすみます。

私はPT3の載ったサーバーを別用途でも使っているので、この機能があるととても助かります。

オンボードCPUにAC電源付き

まず、このマザーボードは以前から同じようなシリーズがあり、省電力マザーとして知られています。

一般的にオンボードマザーのほうが個別に組むより省電力になり、ACアダプタも相まってかなりの省電力マシンになりそうです。

また、ファンレスでCPU冷却の音が出ないので、ご家庭にサーバーを置く際も重宝すると思います。

いちおう最新世代のCeleronを搭載しているので、動画変換などの重い処理をさせなければ軽快に動いてくれると思います。

あとACアダプタはマザー本体に接続するらしいですが、一応HDD用の電源もそこから伸ばせるみたいです。

地味にSATA4つ繋げられるのもポイント高いですね!

まとめ

VT-dが使える省電力格安マザーとかいう条件の厳しい物を探していましたが、ようやく本命が出たという感じです。

AC電源もPCIスロットもPT3運用にあつらえたようですし、仮想化を行わなくてもおすすめできます。

ちかごろはChinachu AirやMirakurunなどDTV界隈のツールも自由度が上がってきましたし、今後もハード面でもいろいろと調べていきたいと思います。

PT3を仮想マシンに渡したかった

やりたかったこと

PT3などの地デジチューナーはドライバをコンパイルすることで使えるようになりますが、カーネルをアップグレードするとドライバも再コンパイルが必要になります。

私のサーバー環境は割と移り変わりが激しく、カーネルも積極的にアップグレードしたいと思っていたので、安定してPT3を運用するにはOSレベルの切り離しが必要だと考えました。

そこで、我が家のサーバー(Celeron N3150搭載機)に仮想マシンを作成し、PT3を渡そうと試みました。

PT3を仮想マシンに渡すには

結論から言うと、我が家のCeleronサーバーでは仮想マシンにPT3を渡すことはできませんでした。

まず、PCIeデバイスを仮想マシンに渡すには、IOMMUという仮想化支援機能が必要になります。

これはIntel系CPUだとVT-dという機能に相当します。

VT-dを条件に検索したところ、多くのCPUは通常の仮想化支援機能(Intel VT)はサポートしていますが、VT-dをサポートしているCPUは割と少ないようです。

比較的新しいCore iシリーズやサーバー向けのCPUなど、性能の高い製品に限定して搭載されているようですね。

一方AMD系のCPUだとAMD-Viという機能に相当するようで、これは多くのCPUがサポートしているようです。

ただし、IntelもAMDもCPUだけが対応していればいいというわけではなく、マザーボードでもVT-dかAMD-Viに対応している必要があります。(AMDのCPUでやろうとするとここでひっかかることが多いかも)

マザーボードが対応しているかという情報はメーカーページにも掲載されていないことが多いので、購入する場合はベンダーに直接問い合わせるか、口コミで探すしかなさそうですね・・・(何かいい方法があったら教えてください…m(_ _)m

録画マシンに極端に性能の高いCPUを使うのもどうかと思うので、PT3の切り離しはしばらく見送りになりそうです。

結局何買えばいいの

自分のサーバーはあまりたいそうなことはしないので、省電力、低価格かつVT-dかAMD-Viをサポートしている構成を考えたいです。

まずIntel系ですが、VT-dをサポートしているCPUの中にCore i5-2500Tがあり、これはTDPが35Wと比較的低く、ちょっと古いので値段もそこまでしないはずです。

日本でも単体で売っていたようですが中古を探しても見当たらなかったので、もしかすると入手できないかもしれません・・・

TDPがちょっと高くなりますが、Celeron G3900はお値打ちでいいかもしれません。マザーボードも安く買えそうですので、コストを考えるとこれで決まりな気がします。

続いてAMD系ですが、CPUはAthronが最新版でも安く買えます。ただしマザーボードは対応しているかの情報がとても少なく、多少の賭け要素がありそうです。

ネットで探し回っていると、AM3の900シリーズのチップセットを搭載したASRock製のマザーボードなら960番台を除くすべてでAMD-Viが使えるという書き込みを発見しました。

この情報を信じるなら、ASRockの970A-G/3.1とかがよさそうですね。

Intel系で中古を漁る or TDP65WのCeleronで確実に使える構成を選ぶか、AMDで賭け的に1万5千円前後でTDPの低い構成を試してみるか、非常に悩ましい選択ですね・・・

もうちょっと調べてみて、いい感じのがあったら追記したいと思います。

Dockerでservice endpoint with name already existsと出た時

発端

ノードを再起動した後にsacleしようとしたところ、次のようなエラーが出ました。

Error response from daemon: service endpoint with name es already exists

古いコンテナを削除してもこのエラーが出る場合は、docker networkにコンテナの登録が残ってしまっている可能性が高いので、次のコマンドで削除できます。

docker network disconnect -f [ネットワーク名] [コンテナ名]

一応気になったので、実際に登録されているか調べてみました。

$ docker network inspect [network_name] | grep -n [container_name]
154                 "Name": "[container_name]",
$ docker network inspect www_default | head -n 160 | tail -n 20
(略)
            "[ID]": {
                "Name": "[container_name]",
                "EndpointID": "~~~",
                "MacAddress": "~~~",
                "IPv4Address": "~~~",
                "IPv6Address": ""
            }
(略)

上のコンテナは既にdocker rmで削除していますので、networkにゾンビのように残ってしまっていることがわかります。

あとheadとかtail使わなくてもjqとかdocker network –formatとかで綺麗に見られると思います。。

追記(2017/03/30)

swarmでノードを強制的に落とした時に同様のエラーが出ましたが、上記方法では解決しませんでした。

このような場合、落としたノードをクラスタに復帰させてからコンテナを削除する必要があるようです・・・