Hatena::ブログ(Diary)

IT戦士への道 このページをアンテナに追加 RSSフィード Twitter

2012-01-30

[][][]herokuのcronを使わず、定期的にmydnsにIPアドレスを通知する。

どうも。
テストが一段落し、heroku とGAE をいじっています。

題名だけではちょっとわけがわからないよ、ということで今開発しているherokuで動作するWebサービスサーバー構成を。
なぜheroku上で動作しているアプリケーションからmydns(Dynamic DNSサービス)にIPアドレスを通知しないといけないのか。

現段階ではWebサービスをリリースしていないので、ここでは仮にドメインをexsample.comと定義します。
私がやりたいことはこのexsample.comで、herokuで動作するアプリと、自宅のサーバーの2つを同じドメインで、
なおかつサブドメインで振り分けたいわけです。しかも自宅のサーバーは動的なIPで。

まず、自宅サーバーではwebサーバーが稼働しており、プロバイダから配布されているグローバルIPが動的なのでmydnsにIPアドレスを通知し、
IPアドレスが変動しても、ドメインIPアドレスの結びつきが離れないようになっています。

ドメインを取得したレジストラではmydnsのネームサーバーを指定しており、*exsample.com にリクエストが飛ぶと、
mydnsのネームサーバーに通知してある自宅のサーバーIPが返って来るようになっています。
まーここまでは普通のDDNSの使い方ですね。

開発しているサービスはheroku上で動作しているので、サブドメインごとにIPを振り分ける必要があります。
hoge.exsample.comはherokuのサーバー、その他は自宅のサーバー、のような感じ。

herokuで独自ドメインを使うには、Aレコードでherokuから指定されたIPアドレスを設定する必要があります。
ところが、mydnsでできる設定がちょっとアレ
Aレコードは、その通知してきたIPアドレスでしか設定できない模様。多分。

つまり、サブドメインで違うIPにAレコードでは振り分けられない。
そこで、mydnsに用意されているDELEGATE機能を使う。

DELEGATEは、子idを作成し、その子idに通知してきたIPアドレスをAレコードに設定することができる。

つまり、herokuで動作しているアプリケーションからmydnsに設定している子idに対してIPアドレスの通知をしなければいけない。(herokuのね
ということで通知するスクリプトなどを書いた。

まー、こんな事をしなくても*exsample.comはすべて自宅のサーバーに行くので、HTTPのHOSTを見て、hoge.exsample.comだったらherokuのアプリケーションリダイレクト
させるみたいなこともできますが、一旦自宅のサーバーの経由するので嫌ですね無駄ですね、ってことです。


で、本題。
mydnsにIPアドレスを通知するのは超簡単です。mydnsではPOP3,FTP,HTTP-BASICなどでIPを通知できます。
今回はHTTP-BASICでheroku側から通知させます。urllibを使えば一発。

問題なのは、どのタイミングで通知させるのよってことです。
herokuは固定IPですので、その固定IPをmydnsに設定していればいちいち通知する必要ないんじゃないの?って思うと思います。
しかし、mydnsの制約上、10日(だったかな?)通知がないと、IPアドレスがリフレッシュされてしまうので、接続できなくなります。

ということで、少なくとも10日に一回はheroku側からmydnsにIPアドレスを通知しなければなりません。

となると、やっぱcronかなーってなるわけです。herokuでもcronは使えます。1日一回までは無料で、それ以降は課金。
アドオンを追加するだけなので、簡単ですがクレジットカードの登録が必要です。(無料版でもね!


ですが、今回はherokuのcronは使いません。
調べてみるとherokuのcronはRubyRakefileが云々..

Ruby?

ああ、Rubyですかそうですか
RubyでもIP通知するくらいなら簡単です。
ですが、できれば開発しているサービスでは1日に2回はcronを動かしたい...、しかし1日1回以上は有料、私は学生お金が無い


じゃどうやって定期的に通知させるの?ってことですが
今回はGAEとherokuを組み合わせて無料で定期的にタスクを実行させるようにします。
GAEのcronは制限はそんなになかったような気がします(よく調べてない

仕組み

  1. GAEのcronでプログラム実行
  2. GAEがherokuで動作しているアプリケーションのcron用のurlアクセス
  3. heroku側ではそのcron用のurlアクセスがあったらタスクを実行

とすれば無料で、なおかつpythonで処理が書ける!

Flask側(heroku側)

@app.route("/dns/hogehogehugahugapiyopiyo") 
#URLはハッシュ化とかしたほうが良いでしょう

def dns():
    user = "mydns0123456"
    passwd ="hugahuga"
    url = "http://%s:%s@www.mydns.jp/login.html" % (user, passwd) 
    urllib.urlopen(url).close()

    return str("OK") 

これで通知側はおわり。
http://hoge.exsample.com/dns/hogehogehugahugapiyopiyo
アクセスがくると通知する。(hogehogeとか使いすぎて見難いけど

GAEのcron.yaml

cron:
- description: cron jobs
  url:/cron/
  schedule: every 12 hours

これで、GAE側の/cron/が12時間起きに実行される。
/cron/には一般ユーザがアクセスできないよう、app.yamllogin:adminなどとしておいたほうが良い。

GAEのmain.py

def fetch_url(url, max_times=3, sleep_sec=5):
    retry_count = 0
    success_flag = False

    while True:
        f = urlfetch.fetch(url)
        if f.status_code == 200 or retry_count >= max_times:
            success_flag = True
            break
        else:
            retry_count += 1
            time.sleep(sleep_sec)

    if success_flag:
        return "success"
    else:
        return "failed"

こんな関数を適当に作って、あとはhttp://hoge.exsample.com/dns/hogehogehugahugapiyopiyoを渡せばいいですね。

まとめ終わり!

なんか頭いい方法あったら優しく教えて下さい。

2012-01-08

明けました

年も明けたので去年のまとめと今年の目標などを正月らしく(もう過ぎてるけど

去年あったこと

去年は結構充実した気がする。
今年の目標

  • 去年かそれ以上に充実したい
  • 本を読む(積んである本が多い
  • iOSアプリリリース
  • Webアプリリリース
  • obj-c,js,javaをもっと

みたいな感じ、もっとアウトプットしないと。

2011-12-06

[]mixi scrap challenge 2011に参加してきました

株式会社mixiさんが主催している mixi Scrap & Buid challenge 2011に参加してきました。
当日の様子→Togetter

どんなイベントか

あまり詳しくは"アレ"なので"アレ"ですが、XSSを主体とした問題が多かったです。
たしか10問くらいの問題があって、時間の都合と技術力の問題で3,4問くらいしか解けなかった。

回答を聞くと「あ〜・・・」ってなったので、頭が硬いんですかね..
まぁ、いずれにせよこれからも勉強を続けたいと思います。

ちょっとした裏話みたいなのも聞けて楽しいイベントでした。
来年、再来年と続くことを期待しています。

mixiさん、参加者皆さんありがとうございました。

f:id:loutusu:20111206060926p:image
ピザ美味しかったです

2011-11-23

[]サーバーSSHログイン等があった場合にGrowlに通知を飛ばす

中国からSSHブルートフォースアタックを受けていたので、なにか通知するようなプログラムを書いてみた。

構成として、サーバーubuntu,Growlで通知が飛んでくるのはiMacとする。
f:id:loutusu:20111123105343p:image

こんな感じで、iMac側のGrowlには外から通知を受けれる設定にしておく。
SSHサーバーで稼働させるスクリプト

#!/usr/bin/env python
import os
import re
import time
import Growl

INTER_VAL = 1.0

def get_new_log_file():
    p = re.compile("^auth.log")

    log_path = "/var/log/"
    log_list = os.listdir(log_path)

    #get time stamp
    time_stamp = {}

    for ll in log_list:
        #re match
        if p.match(ll):
            time_stamp.update({os.stat(os.path.join(log_path, ll)).st_atime:ll})

    new_time_stamp = time_stamp[time_stamp.keys()[0]]

    for ts in time_stamp:
        if new_time_stamp < ts:
            new_time_stamp = ts

    return os.path.join(log_path, new_time_stamp)

def check_log(log_file):
    fp = open(log_file)
    stat = os.stat(log_file)
    fp.seek(stat[6])

    try:
        while True:
            where = fp.tell()
            line = fp.readline()
            if not line:
                time.sleep(INTER_VAL)
                fp.seek(where)
            else:
                yield line
    except:
        raise

    finally:
        fp.close()

class SSHGrowl(object):
    def __init__(self,message):
        self.g = Growl.GrowlNotifier(
             applicationName = 'GrowlPython',
             notifications = [ 'Demo' ],
             defaultNotifications = [ 0 ],
             hostname = 'ip address',
             password = 'port')

        self.g.register()
        self.message = message

    def run(self):
        for i in range(3):
            self.g.notify(noteType = 'Demo',
                title = '!! WARNING !!',
                description = self.message,
                sticky = False)

            time.sleep(0.25)

if __name__ == "__main__":
    log_file = get_new_log_file()

    for line in check_log(log_file):
        i = SSHGrowl(line).run()

bitbucket

プログラムの流れ

  1. /var/log/から正規表現でauth.logから始まるファイルを取得
  2. タイムスタンプをみて、最新っぽいauth.logのファイル名を取得
  3. ジェネレータでそのファイルが更新されたらその内容を出力
  4. Growlに通知

手抜きで、auth.logが更新されると通知されるので、ログイン、ログアウト、scpとか、なにかSSHで動きがあったときに通知される。
パースして云々すればへんなアクセスが来たときにだけ通知する、ってこともできるけどメンドイのでやってない。

ちなみにiMacに通知が飛んできた時の画像

f:id:loutusu:20111123110426p:image

かっこいいっすね!!!!!1

2011-11-20

[]「この人誰ったー」というWebサービスを作りました。

この人誰ったー


Google App Engine + Pythonで作成しました。
webapp というか、先日作成した自作のフレームワークっぽいのを利用しました。

どんなサービスなのか完結に言うと、ツイッターを利用したクイズゲームです。

一番苦戦したのはTwitterAPIを利用して、問題を作成する部分でした。
9月の下旬ごろからちまちま作って、11月19日に公開しました。

11月20日(現在)では約1万5000のユーザの方が遊んでくれたみたいで、予想していたよりも多くて嬉しい限りです。

反省点

  • 1、「Google App Engineの無料枠で済むだろww」なんて思ってたら1時間で使いきってしまい、1時間ほどサービスがダウンしてしまった。
  • 2、memcache を使ってデータストアのアクセスなどを節約した「つもり」でしたが、甘かった。データストアのReadが一番最初に上限を越してしまった。
  • 3、静的コンテンツもGoogle App Engineの同じサーバーで配信していた。

一気にリクエストが発生して、ピーク時ではGoogle App Engineインスタンス数が70近く生成されてましたので、
Google App Engineでやってなかったら普通に落ちてたのかなー...

そろそろ値上げされるみたいなので、負荷分散とかその辺の知識を増やしてVPSかなんかでまたサービス作りたいですね。