おっさんのダベリ

IT系の技術的な話題が多いです

GAE版goslashサンプルで開発モードを実装してみる

goslashのサンプルをちょっと変えてみて、GAEをローカルで動かした時にデバッグしやすい修正をしてみました。

まぁGAE使っている人は知っているよ、という内容だと思います。

やりたいこと

goslashでは、Pluginを呼び出した際にSlackから渡されたresponse_urlに対してレスポンスをPOSTします。 ただし、ローカルで動かした場合にはSlackから呼び出されるわけではないため、response_urlは存在しないこともあります。 このような場合にPluginのデバッグを行うために、ローカルで動かした場合にはリクエストのレスポンスとして、送信内容を返すようにしてみたいと思います。

どうやったか

具体的なコードは以下のようになります(一部)

if appengine.IsDevAppServer() {
    // (1) 開発用サーバの場合
    cmd, _ := req.CmdArgs()
    p, ok := slashPlugins[cmd]
    if !ok {
        renderer.JSON(w, http.StatusNotFound, "cmd not found")
        return
    }

    // (2) リクエストに対するPluginの実行結果をJSONにしてレスポンスとして返す
    msg := p.Do(req)
    var jsonData bytes.Buffer
    if err := json.NewEncoder(&jsonData).Encode(&msg); err != nil {
        renderer.JSON(w, http.StatusInternalServerError, err.Error())
        return
    }
    renderer.JSON(w, http.StatusOK, jsonData.String())
} else {
    // (3) 本番環境の場合
    renderer.Text(w, http.StatusOK, slashCmd.Execute(req))
}

(1) 開発用サーバの判定

開発サーバかどうかは、appengineのIsDevAppServer() APIで判定できます。 この条件が真になった場合には、呼び出し元にレスポンスとして返す処理を実行します。

(2) リクエストに対するPluginの実行結果をJSONにしてレスポンスとして返す

ここのpはそれぞれのPluginの実装になります。 Doメソッドで、リクエストに対する個々のメッセージを作成します。 作成したメッセージはJSONデータとしてエンコードし、リクエストに対するレスポンスとして呼び出し元に返します。

(3) 本番環境の場合

もとのサンプルと同様の処理を実装しています。

動かしてみる

curlで実行した結果は次のようになりました。

$ curl -X POST -d "text=time" http://localhost:8080/v1/cmd
"{\"response_type\":\"in_channel\",\"text\":\"2016-08-08T16:31:26Z\"}\n"

ちょっとしたPluginの確認ならば、簡単にできますね。

具体的なコードは、この辺に置いときます。 https://github.com/yhanada/goslash-sample-gae

slack-slash-commandを試す

GAEやGoを使ってやりたかったことは、Slackのインテグレーションです。

昨日まででGAEをIntelliJ IDEAで動かすところまでは出来ました。 これからは、 GAE/GoでSlackの'/'(スラッシュ)コマンドサーバーを立てる

をきっかけに、

  • GAE/GoでSlackのSlach Commandを実現する
  • IntelliJ IDEAで開発しやすい環境を作る
  • 新しいPluginを作ってみる

ということを目指していきます。

さて、kyokomiさんは、SlackのSlash CommandだけではなくてBotフレームワークslackbotも公開されています。 ゆくゆくはクラウド環境で動くSlackBotも作ってみたいですね。

さて、肝心のslack slack commandは、上記のQiitaの通りにやってみるとあっさり動きました。

素晴らしいです!!

f:id:yhanada:20160729021746p:plain

IntelliJ IDEAでGoogle App Engine for Goの環境を設定する

昨日はコマンドプロンプトGoogle App Engine for Goのサンプルを起動するまで確認できました。

きっかけ

実は個人でJetbrainsのツールのライセンスを持っており、普段の仕事でもプライベートでも大活躍しています。 ということからIntelliJ IDEAでGAE for Goの環境を作ってみたいと思います。

どうやったか

JetbrainsのツールプラグインとしてGoものはだいぶ前からあるのですが、設定がよくわからなくて最初迷いました。

SDKの設定

通常のGoも入れているため、GoのSDK設定は完了しています。 しかしこのままではGAE for Goのプロジェクトは起動できません。

いろいろとググってみたところ、GoのSDKとしてGAE for Goを指定すれば良いということがわかりました。

f:id:yhanada:20160728005215p:plain

SDKの場所として昨日いれた~/go_appengineを指定します。

SDKの登録ができたら、プロジェクトで使用するSDKとしてこちらを指定します。

f:id:yhanada:20160728005335p:plain

実行するで

よっしゃこれでいけるわ、と思ったもののうまく起動できず。 EditConfigurationsでもモジュールがないぞーと言われる始末。

昨日の環境では、プロジェクト直下にhello.goがおいてあったのですが、それがまずかったようです。

f:id:yhanada:20160728005719p:plain

こんな感じで、プロジェクト配下にhelloというディレクトリを作り、その下にgoファイルを置くとモジュールとして登録できるようになりました。 ちなみにモジュール単位でSDKを選択できるらしいので、通常のGo SDKではなくてGAE for GoのSDKを指定するようにしましょう。

これでEdit Configurationsでも登録できて、Runの実行でローカルでの起動ができるようになりました。

メデタシメデタシ

Google App Engine for Goの環境を作る

きっかけ

流行りに乗ってBot作りたい。というのが理由。 Herokuとかが多そうだけど、Goを使ってとなるとGoogle App Engine(GAE)でもいいんじゃないかと思ったので、勉強も兼ねて環境を作ってみる。

インストール

まずは、SDKを入れる。 Quick StartページからSDKをダウンロードして適当な場所に置きます。

今回は、HOMEの下に置きました。

~/go_appengine

どうもpython 2.7が必要らしいけど、もともと入っていたもので良さそう。 $PATHを通してこれで準備完了。

サンプルを起動

QuickStartページにサンプルの動かし方があるので、順番にやってみる。

リポジトリのclone

サンプルのリポジトリGitHubにあるので、てけとうな場所にcloneします。

$ cd てけとうな場所
$ git clone -b part1-helloworld https://github.com/GoogleCloudPlatform/appengine-guestbook-go.git helloworld
$ cd helloworld

いまは、helloworldいいるのでこの場所で先ほどインストールしたSDKの下のgoappコマンドを以下のように呼び出します。

goapp serve .

初回起動時だけCould not read search indexesと言われますが、普通に起動します。

コンソールにも表示されていますが、3つのサーバが公開されています。

起動しているサービスを終了させるには、Ctrl-Cを押下する必要があります。

Adminサーバでは、GAEが提供するDataStoreやmemcacheなどの内容を確認したりできるため、デバッグなどで役に立ちそうです。

App Engineにデプロイする

まずは、Cloud Platform Consoleにログインし、プロジェクトを作成します。 作成したら、Project IDを控えておいて、次のようにターミナルで実行します。

$ appcfg.py -A <ProjectID> -V v1 update . 

これを実行するとデフォルトブラウザが開いて、Googleアカウントの認証処理が走ります。 ここで利用するアカウントの選択を行い、ターミナルに戻るとゴロゴロと処理が走っていることが確認できます。

Authentication successful.
01:35 AM Scanning files on local disk.
01:35 AM Cloning 4 application files.
01:35 AM Compilation starting.
01:35 AM Compilation: 1 files left.
01:35 AM Compilation completed.
01:35 AM Starting deployment.
01:35 AM Checking if deployment succeeded.
01:35 AM Deployment successful.
01:35 AM Checking if updated app version is serving.
01:35 AM Completed update of app: <ProjectID>, version: v1

上記のようにターミナルには表示されました。無事にデプロイされたようです。 このプロジェクトのURLは、次のようになります。

http://<ProjectID>.appspot.com

さらに、httpsでもアクセスできますので、セキュアなAPIも簡単に用意できますね。

プロジェクトの削除

さくさくドキュメントを読み進めていくと、「Clean upのやりかた」とか書いてますね。 リソースの状況によってはお金がかかるのを避けるために不要なプロジェクトは消しましょう。

Cloud Platform Consoleから「すべてのプロジェクトの管理」ページに遷移し、削除したいプロジェクトにチェックを入れ、右上のゴミ箱ボタンをクリックします。 これでサービスはシャットダウンされますが、すぐに削除はされないようで、しばらくは復元させることができるようです。 うっかりさんには安心ですね。

どこまで遊び倒せるか

App Engineの料金ページを見ると、課金を有効にしないかぎりはお金は掛からないようです。 ただし、上限を超えた場合には503エラーが発生します。

また、課金を有効にした場合でも一日あたりの予算を設定することが出来ます。小さなサービスで開始し、大きく成長させる場合や、ちょっとお試しの場合にも使いやすいですね。

ということで、ドキュメントの読み込みから始めるかな。

DockerでMySQL、PHP、Apacheを連携させる

きっかけ

MySQLサーバのTimeZoneがUTCで、アプリケーションのTimeZoneがAsia/Tokyoの場合に、実際のところどうなるか確認したかった。

ということでDockerの勉強も兼ねてMySQLPHP/Apacheの2種類のイメージを連携させる方法を調べたのでメモります。

いつものように先人の知恵はこちら

今回使ったイメージは公式のイメージを利用しています。

MySQLサーバ起動

上記の公式イメージを取得し、ローカルでコンテナ起動

$ docker pull mysql:5.7
$ docker run --name mysqld -e MYSQL_ROOT_PASSWORD=pass -d mysql:5.7

ここでは、ROOTのパスワードを指定しています。 パスワード以外にもいろいろと指定できますので、本格的なWebアプリケーション用に構築するならばDocker Hubや参考サイトを確認してください。

MySQLクライアントの起動

同じイメージにはMySQLのクライアントも入っているので、別のコンテナとして起動します。 --linkオプションを付けることで、先ほど起動したmysqldと連携させることが出来ます。

$ docker run --link  mysqld:mysql -it --rm mysql /bin/bash

MySQLクライアントからMySQLサーバに接続

Dockerのコンテナに入れたら以下のコマンドでMySQLサーバに接続できます。

# mysql -u root -ppass -h $MYSQL_PORT_3306_TCP_ADDR

$MYSQL_で始まる環境変数は他にも定義されいますので、envコマンドなどで確認してください。

接続できたら、てけとうなDBとテーブルを作ります。

CREATE DATABASE mydb;
USE mydb;
CREATE TABLE users(
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(32),
  created TIMESTAMP
);
INSERT INTO users(name, created) VALUES('山田', NOW());

PHPのイメージ準備

公式のphp:5.6-apacheをベースにして、PDO MySQLを追加インストールしたイメージを作成します。

FROM php:5.6-apache
RUN apt-get update && \
  docker-php-ext-install pdo_mysql

これをベースにイメージをビルドします

$ docker build -t php:5.6-apache-pdo-mysql .

PHPコンテナ起動

PHPのイメージがビルドできたら、適当なディレクトリをマウントして起動します。 今回はApacheが同梱されたイメージですが、PHPCLIを使って確認します。

$ docker run --link  mysqld:mysql -it --rm -v `pwd`:/var/www/html php:5.6-apache-pdo-mysql /bin/bash

UTCMySQLとAsia/TokyoのPHPプログラムの変換

次のようなプログラムで確認してみました。 実行すると1レコードINSERTします。createdにはNOW()で値を入れているためUTCでの時刻として表示されます。 なので、$row['created']は、UTCでの日時表記となっています。 このサンプルではDateTimeクラスを使ってUTCからAsia/Tokyoへの変換を行っています。

<?php
// default timezone=Asia/Tokyo
date_default_timezone_set('Asia/Tokyo');

// connect DB
$db = new PDO('mysql:host=mysql;dbname=mydb', 'root', 'pass');

// sample record
$db->query("INSERT INTO users(name,created) VALUES('ほげほげ', NOW())");

$st = $db->query("SELECT * FROM users");
$rows = $st->fetchAll();
foreach ($rows as $row) {
    $user = [];
    $user['name'] = $row['name'];

    // retrieve as UTC
    $dt = new DateTime($rows['created'], new DateTimeZone('UTC'));
    // set default timezone
    $dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
    $user['created'] = $dt->format('Y-m-d H:i:s');

    var_dump($user);
}

PHPから値を入れる際には、一工夫必要になる気がしますが、それは今後のお話で。

SSHのPAM認証をHookしてLinuxサーバへのログインをSlackに通知する

久しぶりに書こうと思ったら、何か別のブログを開設してしまった。 いい機会なので、Markdown形式で書くことにします。

今回のネタは、個人のサーバをさくらの専用サーバからIDCFのクラウドに移行したことと、いろいろな通知をSlackに出してみようと思ったことから始まってます。

きっかけ

Slackで遊びたいと思っていろいろ調べたところ、IDCFの踏台サーバにログインした通知を出せることがわかったので、その辺りについて書いてみます。 といってもググってみればわかることだけど。

どうやるか

Google Cloud Platform で Slack との連携を実現する 3 つの方法に書いてある、notifyのサンプルを使う。

これは、PAMフックを利用してSSHでログインしたことを検知し、SlackのWebHook APIに対してPOSTするものです。 他にもnode.jsを使う方法や、.ssh/rcを使う方法がありましたが、

  • 踏台なのでなるべく余計なものは入れたくない(踏台サーバはIDCFのS1を使っているため)
  • アカウントごとに設定が必要な物は避けたい

ということから、このPAMフックによる通知を選択しました。

ただし、PAMフックを使うにはsshdの設定で使えるようにしなければなりません。 IDCFで使っている踏台はCentOS7テンプレートから作ったので初期設定を確認したところ、/etc/sshd_configにはデフォルトでusePAM yesとなっていました。 ということで、特に悩まずにシェルスクリプトをインストールできました。

まとめ

Slack便利