webネタ

Webエンジニアが業務に関係することをメモしていく

さくらVPSでDropbox

個人サーバーのバックアップがめんどうなので、dropboxのアカウント作ってそこに放り込むことにした。
環境はLinux。CentOS5.6(64bit)さくらVPS。

f:id:R-H:20110928074924p:image

必要なものをインストール

python2.6

sudo yum install python26
dropboxコマンドの準備

ソースをダウンロードし、権限を与えパスが通っているディレクトリへ移動。

cd ~/src
wget https://www.dropbox.com/download?dl=packages/dropbox.py
chmod u+x dropbox.py
vi dropbox.py
  一行目、#!/usr/bin/pythonを#!/usr/bin/python26に変更
mv dropbox.py ~/bin/dropbox
dropboxインストール
dropbox start -i
  To link this computer to a dropbox account, visit the following url:
  https://www.dropbox.com/cli_link?host_id=39c6****c3d04b***e6&cl=ja

ブラウザでdropboxにログインし、このurlを叩くとアカウントが紐づく。
~/Dropboxが自動で作成され、同期がはじまる。GUIの時と同じ。
なんて簡単!

確認コマンド
cd ~/Dropbox
ls -lA

ステータス
dropbox status

ヘルプ
dropbox help

プロセス
ps aux | grep dropbox

試しにディレクトリを作ってみる。

cd ~/Dropbox
mkdir test

ブラウザでアクセス
https://www.dropbox.com/home
反映されているか確認。

起動と停止
dropbox start
dropbox stop
自動起動

rootで作業。
サービスファイル作成。
変数のuserexecの値は各々設定する。

vi /etc/init.d/dropbox

                                                                                  • -
#!/bin/sh # chkconfig: 345 99 01 # description: dropbox # processname: dropbox exec="/home/ryo/bin/dropbox" user="ryo" start() { echo -n $"Starting $prog: " su -c "${exec} start" ${user} } stop() { echo -n $"Stopping $prog: " su -c "${exec} stop" ${user} } status() { su -c "${exec} status" ${user} } restart() { stop start } case "$1" in start) $1 ;; stop) $1 ;; restart) $1 ;; status) $1 ;; *) echo $"Usage: $0 {start|stop|status|restart}" exit 2 esac exit $?
                                                                                  • -

権限の付与

chmod u+x /etc/init.d/dropbox

自動起動に登録

chkconfig --add dropbox
chkconfig dropbox on
chkconfig --list dropbox
    dropbox        	0:off	1:off	2:on	3:on	4:on	5:on	6:off

これで再起動しても自動で起動する。

バックアップ

定期的にtarにして~/Dropboxに配置すば自動でバックアップされる。
自分はgitのマスターリポジトリdropboxでbackupしてます。安心。

crontab -e
  0 3 * * * tar zcfp ~/Dropbox/backu/git_master_repo.tar.gz /var/git >> /tmp/cron_dropbox.log 2>&1

gitリポジトリの作り方

gitリポジトリの作り方まとめ。

構成図はこんな感じ。ローカルにxampp置いて開発。
f:id:R-H:20110927231603p:image

マスターリポジトリ(公開リポジトリ)の作成
mkdir -p /var/git/sample.git
cd /var/git/sample.git
git --bare init

マスターリポジトリは--bareをつけてinitする。

/home/user/sampleにあるソースをマスターにするためコミット
cd /home/user/sample
git init
git add .
git commit -m 'import'
push先のリポジトリパスを指定し、push
git remote add origin file://var/git/sample.git
git push origin master

追加したリポジトリパスを見る場合。

git remote -v
pushしたら、cloneでもってくる。
cd /var/www/vhosts
git clone file:///var/git/sample.git sample

最後のsampleはディレクトリ名になる。

ローカル作業

ここはGIT GUIがおすすめ。
ローカルでclone。

git clone ssh://00.00.00.00:22/var/git/sample.git

ローカルでcommit。
ローカルでpush。

マスターリポジトリの変更をHTTP公開用リポジトリに反映
cd /var/www/vhosts/sample
git pull
さいごに

マスターリポジトリは定期的にバックアップすること。
dropboxを使うと便利。

iPhoneプッシュ通知まとめ

2011/11/14 : 追記
moruguさんに指摘頂いてdeviceTokenのPOST処理を追記しました。
連絡手段がなかったのでここにお礼として書いておきます。どもです! m(_ _)m


iPhoneアプリ開発でプッシュ通知を使ったので、まとめ。

仕組み

f:id:R-H:20110923115354j:image

1、APNsにPush通知許可の登録する。
2、APNsからデバイストークンが帰ってくる。
3、そのデバイストークンをサーバー(これは自分で用意します)に送ってDBなりに保存する。
4、サーバーからAPNsにPush通知依頼を出す。
5、APNsは登録済みの指定デバイスにPush通知を出す。
6、受け取ってAlert出すなり色々する。

実装前に準備

Push通知を行うには、iOSDeveloperCenterでPush通知用の証明書をインストールしたりしなければならない。
このフェーズは自分ではやってないので省略orz
参考サイトだけ載せておく。

実装

ここのメソッドは全てプロジェクトのdelegateクラス。

1、APNsにPush通知許可の登録する。

registerForRemoteNotificationTypesを叩くと『プッシュ送信を許可しますか?』のアラートが出て、APNsにリクエストが送られ登録される。

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window.rootViewController = self.navigationController;
[self.window makeKeyAndVisible];
    
[[UIApplication sharedApplication] 
     registerForRemoteNotificationTypes:
     (UIRemoteNotificationTypeBadge| 
      UIRemoteNotificationTypeSound|
      UIRemoteNotificationTypeAlert)];
    
return YES;
}
2、APNsからデバイストークンが帰ってくる。

レスポンスが返ってくると、didRegisterForRemoteNotificationsWithDeviceTokenメソッドがコールされる。
引数にデバイストークンがついてくる。

 - (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)devToken{ 
    NSLog(@"Success : Regist device token to APNS. (%@)", devToken);
    [self postDeviceToken:deviceTokenString]; // POST
}

APNSへのデバイス登録失敗時

 - (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)err{ 
    NSLog(@"Error : Fail Regist to APNS. (%@)", err);
}
3、そのデバイストークンをサーバー(これは自分で用意します)に送ってDBなりに保存する。
 - (void)postDeviceToken:(NSString *)deviceToken {
    
    // prepare url.
    NSString* content = [NSString stringWithFormat:@"deviceToken=%@", deviceToken];
    NSURL* url = [NSURL URLWithString:@"http://192.168.1.5:8080/registDeviceServelt"];
    
    // create instance.
    NSMutableURLRequest* urlRequest = [[NSMutableURLRequest alloc]initWithURL:url];
    [urlRequest setHTTPMethod:@"POST"];
    [urlRequest setHTTPBody:[content dataUsingEncoding:NSUTF8StringEncoding]];
    
    // post.
    NSURLResponse* response;
    NSError* error = nil;
    NSData* result = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
    if(error) NSLog(@"error = %@", error);
    
    // get result.
    NSString* resultString = [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];
    NSLog(@"%@", resultString);
    
    // release.
    [resultString release];
    [urlRequest release];
}

サーバー側でdeviceTokenを受け取り、DBに保存します。

4、サーバーからAPNsにPush通知依頼を出す。

PHPでやる場合
php-apnsというライブラリを使う。
使う前に証明書を作ったり、ルート証明書をダウンロードしないといけない。ここにやり方が載ってる。
何も考えずにコマンドを叩く^^;

Javaでやる場合
java-apnsというライブラリを使う。
証明書は、キーチェーンからPush通知用にインストールした証明書をエクスポートして使う。
サンプルコード

ApnsService service =
APNS.newService()
.withCert("/Users/*******.p12", "*****")
 .withSandboxDestination()
 .build();

String payload = APNS.newPayload().alertBody("Can't be simpler than this!").build();
String token = "638d09063fe6a73a6cb4*******************ab084e5ea8b0b390d1480f";
service.push(token, payload);
5、APNsは登録済みの指定デバイスにPush通知を出す。

ここはAPNsが勝手にやります。

6、受け取ってAlert出すなり色々する。
 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    for (id key in userInfo) {
        NSLog(@"key: %@, value: %@", key, [userInfo objectForKey:key]);
    }
}

まとめ

Push通知の仕組み自体がシンプルな上に、ライブラリも各種言語あるので簡単。
やっぱりappleはスゴイ。

iPhoneDeveloperに法人登録したまとめ

iPhone開発用に会社のアカウントを取得したときのメモです。

まず初めに

会社で法人として登録する場合、印鑑証明書か登記簿謄本のどっちか必要なので用意が必要です。
さらに審査?に時間がかかるので、今回の場合だと3週間ほどかかりました。
個人登録と同様に年間10800円かかります。

登録の流れ

流れ等は、以下のサイトを参考にして登録しました。とても参考になります。

困ったポイント

Documentation Requested for Your Program Enrollment

このようなタイトルでメールがきました。
登録のためにドキュメントが欲しいらしいのです。
が、何を送れというのが書いてありません。信じられない。。。。
しかたなくサポートにメールして聞いたところ、
申請書と印鑑証明書か登記簿謄本のどっちかを送れということでした。
申請書は、こんな感じで書式はお願いしますとご丁寧に送ってくれましたので、ワードで作って印刷しfaxしました。
faxが届くと、ちゃんと届いたと確認のメールが来るので安心です。

以下はサポートからのメールです。最初からこれ送ってこいや。

この度は iOS Developer Programへお申し込みいただき、ありがとうございます。このEメールは登録手続きに関するご案内です。

Appleでは、Program へお申し込みいただく企業について、実在確認をさせていただいております。 誠にお手数ではございますが、以下『計2点』の書類の写しをご用意いただき、FAXにて弊社までご送付ください。

1)本 Program への申請について、申請者が御社の代表取締役により権限が与えられている事を示す書面(御社本部の所在地と代表電話番号の記載、代表取締役の押印が必要です)1通。文面に関しましては、以下の例をご参照ください。
   
    ----------
    申請証明書

    ○○会社(日本語名称および英語名称)代表取締役である <代表取締役の氏名> は、Apple Developer Program の申請に関する一切について、
    当社の社員である以下の申請者に委任している事を、この書面により証明する。

    申請者氏名(Applicant Name): <申請者の氏名>(ローマ字表記を含む)
    役職(あるいは所属部署): <申請者の役職名あるいは所属部署名>

    日付
    企業本部所在地
    代表電話番号

    <代表取締役の氏名>(ローマ字表記を含む) および捺印
    ----------
    
    申請者が代表取締役である場合には、『○○会社代表取締役としての権限によりApple Developer Program へ申請する事を、この書面により証明する』といった文面で作成してください。

2)登記簿謄本(履歴事項全部証明書、又は現在事項全部証明書)、あるいは社印の印鑑証明書(本店のもの)のいずれか1通。

FAX番号:  +1-408-974-7683
※米国の番号となりますので、国際電話のダイヤル方法をご確認ください。

書類をお送りいただく際には、Enrollment ID: ******** と御社の代表電話番号を書類にお書き加えください。日本語表記のものをそのままお送りいただいて、問題ございません。

どうぞ、よろしくお願いいたします。もしご不明な点などございましたら、当方までご連絡ください。Apple Developer Programへのご参加、ありがとうございます。

OAuthの覚え書きメモ

OAuth1.0覚えたのでメモ。

1、RequestTokenを取得するリクエスト。
2、redirectしてそのサービスでログインしてリダイレクトして戻ってくる(コールバック)。
3、RequestTokenを認証(Authorized)するリクエスト。
4、AccessTokenを取得するリクエスト。
5、4で取得したtokenを使って、色々とapi叩く。

Zend_Oauth_Consumerがとても便利。OAuth1.0のみ対応。
これを使うと4のAccessTokenを取得するリクエストを送る前に、RequestTokenの認証を内部的に行ってくれる。

てかfacebookはOAuth2.0だし・・・

apacheのMaxClientを算出するスクリプトを作った

概要

apacheの設定にあるMaxClientsは、サーバースペック等から算出できるため、毎回手動で計算するのは面倒なのでスクリプトを作った。

計算方法

(サーバーメモリ量) / (httpd使用メモリ量 - httpd使用共有メモリ量) = MaxClients

psコマンドではなく、/proc/PID/smapsから取得しているためより正確。コマンドのみで算出しようと思ったが、平均値とか出すのが面倒だったためスクリプトにしてみた。でも、あくまで目安ということで。

参考

基本的にここを参考にさせていただきました。

使い方

/usr/bin/php get_max_client.php

ソースコード (get_max_client.php)

<?php

//--------------------------------------------------------------------------------------
// Usage: php get_max_client.php
// root only.
// 
// This program is used to get MaxClient of apache.
// Following step.
// 1、get all pid of httpd.
// 2、get average Rss of "/proc/$pid/smaps".
// 3、get average Shared_Clean of "/proc/$pid/smaps".
// 4、get average Shared_Dirty of "/proc/$pid/smaps".
// 5、get memory size by free command.
// 6、math max client by "memorySize / (rssAverage - (Shared_Clean + Shared_Dirty))".
// 7、print max client.
//--------------------------------------------------------------------------------------


$GLOBALS['debug'] = false;


/**
 * Dump data.
 * If $GLOBALS['debug'] is true, dump.
 *
 * @param mixd $value
 * @param string $label
 */
function debugDump ($value, $label = null) {
    if ($GLOBALS['debug']) {
        if ($label) {
            $value = $label . ' : ' . $value;
        }
        var_dump($value);
    }
}

/**
 * command execute. error handling and logging.
 * if command is error, it will end. (exit(1))
 *
 * @param string $arg
 * @return string
 */
function executeCommand ($arg)
{
    $result = `$arg 2>&1`;

    if (substr(`echo $?`, 0, 1) === '0') {
        // success
        return rtrim($result);

    } else {
        // failure
        error_log(sprintf('command failure [%s][%s]', $arg, $result));
        echo 0;
        exit(1);
    }
}


/**
 * This method is run.
 * If you want to see debug data, see this page top.
 *
 */
function run ()
{
    $httpdPidArr = getHttpdPidArr();
    $rss = getRssAverage($httpdPidArr);
    $shr = getShrAverage($httpdPidArr);
    $memorySize = getMemorySize();
    $maxClient = mathMaxClient($shr, $rss, $memorySize);

    debugDump($rss, '$rss');
    debugDump($shr, '$shr');
    debugDump($memorySize, '$memorySize');
    debugDump($maxClient, '$maxClient');
    
    echo sprintf('--------------------------------') . PHP_EOL;
    echo sprintf('memorySize / (rssAverage - shrAverage) = %dKB / (%dKB - %dKB) = %d', $memorySize, $rss, $shr, $maxClient) . PHP_EOL;
    echo sprintf('MaxClient maximum value is %d.', $maxClient) . PHP_EOL;
    echo sprintf('--------------------------------') . PHP_EOL;
}

/**
 * Get httpd pid.
 *
 * @return array
 */
function getHttpdPidArr ()
{
    $httpdPids = executeCommand('pgrep httpd');
    if (empty($httpdPids)) {
        throw new Exception("Not found httpdPids. failure pgrep httpd.");
    }
    return  explode(PHP_EOL, $httpdPids);
}

/**
 * Get rss.
 * This method is browse /proc/$pid/smaps.
 *
 * @return int
 */
function getRss ($pid)
{
    $rss = executeCommand("cat /proc/$pid/smaps | grep Rss | awk '{rss += $2} END {print rss;};'");
    if (empty($rss)) {
        throw new Exception("Rss not found. /proc/$pid/smaps not found?.");
    }
    return (int) $rss;
}

/**
 * Get rss average. 
 *
 * @return int
 */
function getRssAverage ($httpdPidArr)
{
    foreach ($httpdPidArr as $httpdPid) {
        $rssArr[] = $debug = getRss($httpdPid);
    }

    return (int) round(array_sum($rssArr) / count($rssArr));
}

/**
 * Get rss.
 * This method is browse /proc/$pid/smaps.
 *
 * @return int
 */
function getShrClean ($pid)
{
    $shrClean = executeCommand("cat /proc/$pid/smaps | grep Shared_Clean | awk '{shrc += $2} END {print shrc;};'");
    if (empty($shrClean)) {
        throw new Exception("Shared_Clean not found. /proc/$pid/smaps not found?.");
    }
    return (int) $shrClean;
}

/**
 * Get rss.
 * This method is browse /proc/$pid/smaps.
 *
 * @return int
 */
function getShrDirty ($pid)
{
    $shrDirty = executeCommand("cat /proc/$pid/smaps | grep Shared_Dirty | awk '{shrd += $2} END {print shrd;};'");
    if (empty($shrDirty)) {
        throw new Exception("Shared_Dirty not found. /proc/$pid/smaps not found?.");
    }
    return (int) $shrDirty;
}

/**
 * Get shr average.
 * (Shared_Clean + Shared_Dirty) / count
 *
 * @return int
 */
function getShrAverage ($httpdPidArr)
{
    foreach ($httpdPidArr as $httpdPid) {
        $shrClean = getShrClean($httpdPid);
        $shrDirty = getShrDirty($httpdPid);

        debugDump("pid=$httpdPid : shrClean $shrClean");
        debugDump($shrDirty, '$shrDirty');

        $shrArr[] = $shrClean + $shrDirty;
    }

    return (int) round(array_sum($shrArr) / count($shrArr));
}

/**
 * Get memory size.
 * This method is execute free command.
 *
 * @return int
 */
function getMemorySize ()
{
    $result = executeCommand("free | grep Mem | awk '{print $2;};'");
    if (empty($result)) {
        throw new Exception('memory size not found. failure free command...');
    }
    return $result;
}

/**
 * Math MaxClient.
 *
 * @param int $shr
 * @param int $rss
 * @param int $memorySize
 * @return int MaxClient
 */
function mathMaxClient ($shr, $rss, $memorySize)
{
    return (int) round($memorySize / ($rss - $shr));
}


run();