初めてのシステムと日記

システムも日記も初めてです。

さくらVPSの初期設定とチューニング

最近いろいろ試したいことがあったので、さくらのVPSを契約してみました。
レンタルサーバーだとサーバー屋さんがサーバー設定をしてくれますが、
VPSの場合、自分で全て設定しなければなりません。
そして仕事で使っていたので知っていたのですが、本当に必要最低限のものしか入っていない。。


ここでは、自分が必要と考えた初期設定とチューニングを記述します。

■ユーザー追加

root権限で作業するのは危ないので別ユーザーを作成します。
ここではuserという名前で作成してます。

$ adduser user
$ passwd user
# パスワードを2回聞かれるので入力
Changing password for user user.
New UNIX password:
Retype new UNIX password:
passwd: all authentication tokens updated successfully.

■鍵交換

自分のPCでssh-keygenを実行します。

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/user/.ssh/id_rsa):  // 鍵の保存先
Enter passphrase (empty for no passphrase): // パスフレーズ入力 
Enter same passphrase again:  // もう一度パスフレーズを入力


~/.sshディレクトリに鍵が作成されます。

$ ls -la ~/.ssh/ 
total 24
drwx------   5 user  staff   170  1 29 11:19 .
drwxr-xr-x+ 29 user  staff   986  1 26 00:45 ..
-rw-------   1 user  staff  1675  1 29 11:19 id_rsa
-rw-r--r--   1 user  staff   404  1 29 11:19 id_rsa.pub
-rw-r--r--   1 user  staff   802  1 29 11:05 known_hosts


id_rsa.pubをVPS側に~/.ssh/authorized_keysという名前で保存します。
~/.sshディレクトリがなければ作成します。

$ mkdir ~/.ssh
$ vi ~/.ssh/authorized_keys // id_rsa.pubの中身を記述


記述が終わったらVPS側のパーミッションを変更します。

$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/authorized_keys


これで自分のPCから公開鍵を使ったSSH接続ができます。

■ファイアーウォール設定

iptablesがインストールされているか確認します。

$ yum list | grep iptables
// インストールされてる
iptables.x86_64                            1.3.5-5.3.el5_4.1           installed
iptables-ipv6.x86_64                       1.3.5-5.3.el5_4.1           installed


もしインストールされていなかったらyumでインストールします。

$ yum -y install iptables


自動起動するようになっているか確認します。

$ chkconfig --list | grep iptables
iptables       	0:off	1:off	2:on	3:on	4:on	5:on	6:off


もし自動起動するようになっていなければ設定します。

$ chkconfig iptables on 


現在のiptablesの設定を確認します。

$ iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
 
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
 
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

何も設定されていない。。


iptablesの設定ファイルを新規に作成します。
解放するポートはSSH、HTTP、FTPで、ポート番号はデフォルトにしています。
(念のためSSHのポート番号はふせておきます)

$ vi /etc/sysconfig/iptables

// 以下設定ファイルの中身
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:RH-Firewall-1-INPUT - [0:0]
-A INPUT -j RH-Firewall-1-INPUT
-A FORWARD -j RH-Firewall-1-INPUT
-A RH-Firewall-1-INPUT -i lo -j ACCEPT
-A RH-Firewall-1-INPUT -p icmp --icmp-type any -j ACCEPT
-A RH-Firewall-1-INPUT -p 50 -j ACCEPT
-A RH-Firewall-1-INPUT -p 51 -j ACCEPT
-A RH-Firewall-1-INPUT -p udp --dport 5353 -d 224.0.0.251 -j ACCEPT
-A RH-Firewall-1-INPUT -p udp -m udp --dport 631 -j ACCEPT
-A RH-Firewall-1-INPUT -p tcp -m tcp --dport 631 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# SSH, HTTP, FTP1, FTP2
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport #### -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 20 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 21 -j ACCEPT
-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT


設定が終わったらiptablesを再起動します。

$ /etc/rc.d/init.d/iptables restart
Flushing firewall rules:                                   [  OK  ]
Setting chains to policy ACCEPT: filter                    [  OK  ]
Unloading iptables modules:                                [  OK  ]
Applying iptables firewall rules:                          [  OK  ]


最後に設定が反映されているか確認します。

$ iptables -L    
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
RH-Firewall-1-INPUT  all  --  anywhere             anywhere            

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
RH-Firewall-1-INPUT  all  --  anywhere             anywhere            

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain RH-Firewall-1-INPUT (2 references)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     icmp --  anywhere             anywhere            icmp any 
ACCEPT     esp  --  anywhere             anywhere            
ACCEPT     ah   --  anywhere             anywhere            
ACCEPT     udp  --  anywhere             224.0.0.251         udp dpt:mdns 
ACCEPT     udp  --  anywhere             anywhere            udp dpt:ipp 
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ipp 
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED 
ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ssh 
ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:http 
ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ftp-data 
ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ftp 
ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:https 
REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited 

バッチリです。


■利用していないデーモンを終了

これはチューニングになります。
CentOSは多くのサービスがデフォルトで動いているため、必要ないサービスをOffにします。

$ chkconfig auditd off
$ chkconfig autofs off
$ chkconfig avahi-daemon off
$ chkconfig bluetooth off
$ chkconfig cups off
$ chkconfig firstboot off
$ chkconfig gpm off
$ chkconfig haldaemon off
$ chkconfig hidd off
$ chkconfig isdn off
$ chkconfig kudzu off
$ chkconfig lvm2-monitor off
$ chkconfig mcstrans off
$ chkconfig mdmonitor off
$ chkconfig messagebus off
$ chkconfig netfs off
$ chkconfig nfslock off
$ chkconfig pcscd off
$ chkconfig portmap off
$ chkconfig rawdevices off
$ chkconfig restorecond off
$ chkconfig rpcgssd off
$ chkconfig rpcidmapd off
$ chkconfig smartd off
$ chkconfig xfs off
$ chkconfig yum-updatesd off


サービスに関してはこちらのブログを参考にしました。

CentOSをサーバーとして活用するための基本的な設定 - さくらインターネット創業日記


■サーバー再起動

全ての設定が終わったらサーバーを再起動します。

$ reboot

これで設定が反映されます。


以下、設定前と設定後のメモリ使用量です。

設定前
$ free
             total       used       free     shared    buffers     cached
Mem:        510540     431436      79104          0      47652     307488
-/+ buffers/cache:      76296     434244
Swap:      2048276        336    2047940
設定後
$ free
             total       used       free     shared    buffers     cached
Mem:        509760     168016     341744          0      16048     119724
-/+ buffers/cache:      32244     477516
Swap:      2048276          0    2048276

約270MBぐらいメモリ使用量を減らすことが出来ました。


これで必要最低限のサーバー設定とチューニングが完了となります。

エンジニアサポート新年会2012 CROSSに参加してみた

エンジニアサポート新年会2012 CROSSに参加してきました。
はじめはスマートフォンの話だけ聞きに参加しようと思ってたのですが、
いざ会場に行くと同じ時間帯に興味深い発表がたくさん。。
結局、次世代LAMPとJavaScriptの発表に参加したのでそのことについて書きます。
(スマートフォンの話もHaskellの話も超聞きたかったOrz)


次世代LAMP CROSS これからのWebアプリケーション開発

基本的に仕事では現世代LAMP構成?で開発をしているのですが、
周りで言われているLAMPオワコンの理由がいまいち見いだせなかったこと、
また、Node.jsやMongoDBがどこまで実用レベルで使われているのかを知りたくて参加しました。

  • 現世代LAMP構成と次世代LAMP構成
    • 現世代LAMP構成:LinuxApacheMySQLPHP Perl Python
      • それぞれ言語が異なるので覚えることがたくさん→アプリ開発の壁が高い→確かに
    • 次世代LAMP構成:Liniux、NGINX、Node、MongoDB etc
      • 触ったことはないのですが基本的にJavaScriptなので触りやすいとのこと
      • 同じディレクトリにまとめられるのでgitなどでの管理が非常に楽、cloneすればそれでもう環境が出来るから
      • Nodeによってフロントエンドとバックサイドのエンジニアの距離が短くなるのでは
  • 新技術導入について
    • パネラーの方々はプライベートではNodeやMongoDBを積極的に触っている
    • では仕事ではどうか→なかなか導入できない。。
    • なぜ?→下の項目のような会社に対する説明やコストがかかるのが懸念されてしまう
      • 乗り換える理由
      • 既存の技術でダメな理由
      • 学習コスト
    • アメーバピグでNode+MongoDBを導入できた理由
      • そもそも会社が新技術導入に積極的
      • JavaMySQLRubyなど色んな選択肢があって色々検証した結果、Node+MongoDBになった
        • 新しい技術だからとか、流行だからとかではなく今回の要件に最適だったので選択した


そして今日の新年会で一番印象に残ったグリーの伊藤さんの言葉
※実際話された通りではないかもですがニュアンスは合ってるはず。。

新しくて今風で人が群がるで時代に追いついているということが重要。
Nodeのコミュニティは若い人がたくさんいて最近は活発になってきている。
新しいことがたくさん起こる所では新しいものがたくさん生まれる。
時代に追いついている、今風の流れに乗っているという感覚でモチベーションをあげていくのも重要。


現世代LAMPはいろいろ言われていますが、これはこれで残り続けていくものだと個人的には思います。
今までの実績が充分ありますし、Webアプリ開発する際の情報量や学びやすさとか考えると。
パネラーの方々も現世代LAMP構成がオワコンというよりかは、
NodeやMongoDBなどの新しい技術に触れたりすることで自分の選択肢が増えるとか、
活発なコミュニケーションが生まれるとかで次世代LAMP構成に積極的な気がしました。



JavaScript CROSS JavaScript 八面六臂 2回戦

LL Planetsは僕が初めて参加したエンジニア向けの講演で、
JavaScript 八面六臂は時間の関係で聞けなかったので非常に期待していました。

  • 最近のJavaScriptはWebアプリの開発としても選択されてきた
  • じゃあこれってエンジニアがやるのデザイナーがやるのという問題が出てきた
    • 今まで
      • デザイナー:HTMLやCSSなどの静的ページ
      • エンジニア:サーバーサイド、システムとしてのJavaScript
    • これから
      • 上記にプラスしてデザインとしてのJavaScriptがあるけどどっちがやるんだ。。
    • 今までの明確な作業役割が崩壊しつつある
    • 結論、どちらもやれるようになるべき
      • デザイナー:これからのリッチなコンテンツを作成するにはJavaScriptは必要
      • エンジニア:デザインを考えたJavaScriptの開発が必要


スマートフォンの普及、HTML5+CSS3が出てきてから今までこの辺は結構グレーゾーンな印象でしたが、
話を聞いてみると、確かに今後のことを考えるとどちらにとっても学ぶべきことだなと実感しました。


僕自身参加してみて、色んな話が聞けて刺激を受けたこと、
話についていけなかったところが結構あって悔しいこともありモチベーションはかなりあがりました。
ひとまずNode+MongoDBは触ってみたくなったので試してみます。

jQuery Mobile 1.0を試してみた

少し遅いですが、jQuery Mobile 1.0がリリースされたので試してみました。


jQuery Mobileとは
jQueryのプラグインでスマートフォン向けWebアプリケーションのフレームワークです。
スマートフォンやブラウザに対応したUIが簡単に作れます。
他のスマートフォン向けフレームワークは以下のようなものがあります。


■テンプレート作成
以下がjQuery Mobileの基本的なテンプレートになります。

<!DOCTYPE html> 
<html> 
<head> 
	<title>My Page</title> 
	<meta name="viewport" content="width=device-width, initial-scale=1"> 
	<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
	<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
	<script type="text/javascript" src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
</head> 
<body> 

<div data-role="page">
	<div data-role="header">
		<h1>My Title</h1>
	</div><!-- /header -->

	<div data-role="content">	
		<p>Hello world</p>		
	</div><!-- /content -->
</div><!-- /page -->

</body>
</html>
  • headタグ内
  • bodyタグ内
    • ページ全体をdata-role属性にpageを指定したdiv要素で囲む(ページコンテナ)
      • これがサイト上の1ページにあたる
    • 上記内のヘッダバー(data-role=“header”)と、コンテンツ部分(data-role=“content”)が基本的なページ要素(不要であれば削除可)

HTML5対応したブラウザで開くと下記のようになります。


■ヘッダーやページリンクなどを追加してみる

<body> 

<div data-role="page" id="page1" data-title="1ページ目">

	<div data-role="header">
		<a href="/index.html" data-icon="home" data-iconpos="notext" data-direction="reverse">Home</a>
		<h1>トップ</h1>
		<nav data-role="navbar">
			<ul>
				<li><a href="#page1" class="ui-btn-active">トップ</a></li>
				<li><a href="#page2" data-transition="pop">2ページ目</a></li>
				<li><a href="#page3" data-transition="flip">3ページ目</a></li>
			</ul>
		</nav>
	</div><!-- /header -->

	<div data-role="content">	
		<p>1ページ目です。</p>
		
	</div><!-- /content -->

</div><!-- /page1 -->

<div data-role="page" id="page2" data-title="2ページ目">

	<div data-role="header">
		<a href="/index.html" data-icon="home" data-iconpos="notext" data-direction="reverse">Home</a>
		<h1>2ページ目</h1>
		<nav data-role="navbar">
			<ul>
				<li><a href="#page1">トップ</a></li>
				<li><a href="#page2" class="ui-btn-active" data-transition="pop">2ページ目</a></li>
				<li><a href="#page3" data-transition="flip">3ページ目</a></li>
			</ul>
		</nav>
	</div><!-- /header -->

	<div data-role="content">	
		<p>2ページ目です。</p>
		
	</div><!-- /content -->

</div><!-- /page2 -->

<div data-role="page" id="page3" data-title="3ページ目">

	<div data-role="header">
		<a href="/index.html" data-icon="home" data-iconpos="notext" data-direction="reverse">Home</a>
		<h1>3ページ目</h1>
		<nav data-role="navbar">
			<ul>
				<li><a href="#page1">トップ</a></li>
				<li><a href="#page2"data-transition="pop">2ページ目</a></li>
				<li><a href="#page3" class="ui-btn-active" data-transition="flip">3ページ目</a></li>
			</ul>
		</nav>
	</div><!-- /header -->

	<div data-role="content">	
		<p>3ページ目です。</p>
		
	</div><!-- /content -->

</div><!-- /page3 -->

</body>
  • 全体
    • 必要なページ分、ページコンテナを追加
  • ページコンテナ内
    • id属性を追加→これがリンク先になる
    • data-title属性を追加→ページのタイトルになる(指定しなかった場合はヘッダータイトルが指定される)
  • headerタグ内
    • homeアイコンを追加
      • data-icon属性でアイコンの種類を指定
      • data-iconpos属性で アイコンの位置、もしくはテキスト非表示を指定
      • data-direction属性で戻るページ切替を指定
  • hrefタグ内
    • data-transition属性を指定してページ切替効果を指定(デフォルトはslide)

下記のようにヘッダーリンクの追加が表示でき、実際にリンクさせるとAjaxによるページ切替が確認できると思います。


■軽く触った感想

  • 簡単なコードでエレガントなUIが作れるのでエンジニアにもオススメ
  • 動的ページを作成する際、Ajaxによるページ切替だとそのままだとおそらくPHPJavaScriptを読みこまない恐れがある
    • Ajaxをオフにするか、changePage()関数による制御が必要そう→要検証
    • 公式ページの対応についてはこちら
  • jQuery Mobile、HTML5がまだ全てのブラウザをサポートしているわけではないので対応ブラウザなどは注意する必要がある

今回は最低限のページを作成しましたが、次は動的ページやフォーム周りを試してみます。

全文検索エンジンgroongaを囲む夕べ 2 #groonga に参加してみた

全文検索エンジンgroongaを囲む夕べ 2 #groongaに参加してきました。


今回はmroonga(旧groongaストレージエンジン)の話を聞きたくて参加してみました。
※以前groongaストレージエンジンを試した記事はこちら


■groonga村(株式会社クリアコード須藤さん)
今日の勉強会の位置づけやgroongaについての説明でした。

  • groonga1.2.8リリース!
  • groongaの開発者募集中
    • 興味は少しありますが、まずはソース読めですね。。
  • groongaのユーザー増加も募集中
  • 安定しているのか?
    • 実績が!!!
    • 公開可能な実績を募集中のようです
  • 類似製品との違いって
    • 類似製品一覧
    • groongaの方がよいところ
      • 即時更新
      • データをDBMSで一元管理 ※DBMS:データベースを構築、運用するための管理ソフトウェア
      • 普通のSQLで使える→既存ノウハウを使える
    • 類似製品の方がよいところ
      • 機能が豊富
      • エコシステムが充実→調べたのですがちょっと理解できなかったです。。
      • 書籍がある→先駆者が多そうなイメージ
  • groongaの外観
    • groonga(ぐるんが):全文検索エンジンライブラリ
    • rroonga(るるんが?):Rubyライブラリ
    • mroonga(むるんが?):MySQLストレージエンジン
    • nroonga(呼び方分からない。。):Node.jsライブラリ
    • groonga with PostgreSQL:PostgreSQLでgroongaを使える


■新年と収穫の祭り(有限会社未来検索ブラジル森さん)
Sennaからの開発経緯から、現在、今後開発したいものについての話でした。

  • 最初は検索エンジンとして斜め上を行ってた。
  • その後世間でも徐々に盛んに
    • twitter、real-time webの影響?→こういうところtwitterはやはりすごいですね。。
  • groongaは動的構築一筋
    • 静的構築
      • 構築が完了した時点で検索可能
      • 小さい作業領域で高速構築可能
    • 動的構築
      • 検索可能な状態を維持しながら構築
      • ランダムI/Oを抑えるために工夫が必要
    • 大量の検索と更新を同時にこなしたいので動的構築一筋
    • 静的構築 VS 動的構築について
  • 最近の動向
    • Geographical Searching
    • DBMSとの結合強化
    • 牽引の静的構築→オフラインで索引は静的構築が有利
  • 今後開発したいもの
    • カラムストアの性能強化
    • 類似文字列検索→是非!
    • 頻出パターン抽出→これも是非!
    • などなど
  • 開発者募集!!開発者募集!!開発者募集!!


■mroongaのご紹介(斯波さん)
文字通り、メジャーリリースされたmroongaの紹介です。wktk

  • 更新が混在しても高速な全文検索が可能
    • 更新時、他の更新、参照をテーブルロックでブロックすることがない
    • 並列性の高い参照、更新が可能
  • 更新が混在しても高速な位置情報検索が可能
    • 位置情報インデックスの更新性能が高い特性が加わり、全文検索同様の力を発揮
    • ただし、現在扱える位置情報は点のみ
  • 他のストレージエンジンに全文検索機能を追加することが可能
    • ラッパーモード機能(任意のストレージエンジンと連携)→今回の勉強会で一番気になった
    • 全文検索:groonga、それ以外:任意のストレージエンジンが可能→wktk
  • 主な追加機能→自分が試したver0.2でちょっと。。と思ったところがほとんど改善されていた!
    • auto_increment
    • ラッパーモード
      • create table文のストレージエンジン:groonga、コメントにCOMMENT='engine"innodb"'を記述
    • マルチカラムインデックス
      • タンデム構成(MySQL以外からもgroongaを利用)ではカラム更新時にインデックスの整合性が取れなくなるので注意
    • create/drop index
    • 位置情報index
    • 全文検索用パーサーカスタマイズ
    • rename/alter table
  • 今後の予定
    • MariaDBにバンドルされる→会場で一番盛り上がった印象ですが、盛り上がりについていけなかった。。


■Geographical Searching(株式会社ぐるなび 塩畑さん)
groongaとの歩みから経度緯度検索機能の実現についての話でした。


■groonga with PostgreSQL(フォルシア株式会社 奥野さん)
PostgreSQLのgroonga全文検索拡張や将来像についての話でした。
PostgreSQLは全く触ったことがなく。。話についていけませんでした。。
groongaを利用したライブラリα版を年内にはリリースしたいとのこと。
名前はproonga(ぷるんが)。。ですかね、流れ的に。


■mroongaベンチマーク(株式会社クリアコード須藤さん)
mroongaのベンチマークについての話でした。
ラッパーモードなど魅力的な機能が追加されたのでパフォーマンスも気になるところです。

  • 高速な全文検索機能
    • 約100万件のtweetに対して1万回のフレーズ検索にかかった時間
    • tweetは全て英語らしい
    • table構成が不明
    • mroonga > mroonga+InnoDB > MyISAM >>>> InnoDB
    • mroongaの圧勝でラッパーモードでもそんなにパフォーマンス変わらず!
  • 高速な位置情報検索機能
    • 約1100万件の番地データに対して1000回のMBRContains+ORDER BY検索にかかった時間
    • 約1100万件の番地データに対して1000回のMBRContains+ORDER BY検索にかかった時間
    • mroonga = MyISAM = mroonga+InnoDB
    • どの検索もパフォーマンスはどれも拮抗
  • 即時更新
    • 98万件のtweetが保存された状態で2万件のtweetを追加したときの更新スループット
    • mroonga >>>>> MyISAM > Sphinx >> mroonga+InnoDB = InnoDB
    • 更新に関してはラッパーモードは連携したストレージエンジンに引っ張られるとのこと

以前ver0.2で他テーブルとINNER JOINした時、パフォーマンスが著しく低下していました。
それが今回のラッパーモードによって解消されるのかが気になりました。
何十万件あるデータに紐づくテーブルをJOINすることはありえそうなのですが、、どうなんだろう。
単体テーブルで全文検索してアプリ側でfor文まわして個別に取ってきた方が速いということなのかな。


■mroongaの未サポート機能(斯波さん)

トランザクションやrepair tableが対応していないのは個人的に怖いと思いました。
復旧が大変そうな印象です。


■groonga開発予報(有限会社未来検索ブラジル矢田さん)
ダブル配列について熱く語られていた印象がw
ダブル配列は更新より参照の方が多い時に有効だそうです。
次の開発予報は対応されたらパフォーマンスがかなり向上しそうでした。

  • 開発予報
    • 頻出する索引ごをキャッシュ
    • 不安定なハッシュ表を調整
    • 見る機会の少ないデータを圧縮


今回発表された資料、Ust、Tweetのまとめは下記にあげられています。
http://groonga.org/ja/publication/#groonga-night-2

ラッパーモードは是非検証してみたいです!

Android端末での画像保存が失敗する

■起こった現象

スマートフォンサイトで待ち受け画像を表示するページを作成、

ユーザー側で画像を長押しし「画像を保存」もしくは「リンク先を保存」を押しダウンロードさせるのですが、

iPhone:サイトからダウンロードが出来、ダウンロード後に端末から表示も出来る

Android:サイトからダウンロードは出来るが、ダウンロード後に端末から表示しても何も表示されない

というAndroidのみダウンロードに失敗する現象に遭遇しました。


■待ち受け画像の表示方法

待ち受け画像までの遷移は以下のように行っています。

よくあるフォームに必要項目入力して、その後に待ち受け画像ダウンロードさせる遷移です。

待ち受け画像は直アクセスされても表示されないよう、セッションチェックをするようにしています。

また、フォームはPOSTで遷移するようにしています。

フォーム画面      // セッション開始
↓          // フォームチェック
↓          // フォーム値をセッションにセット
ダウンロード画面
↓          // セッションチェック
待ち受け画像表示  // セッション破棄
↓
待ち受け画像保存         


■原因

Android端末で画像保存をした際、アクセスログが以下のようになっていました。
※HTTPステータス、UAの部分のみ抜粋、パスは適当に変更しています。

"GET /wallpaper.jpg HTTP/1.1" 200 29669 - "[端末UA]"
"HEAD /wallpaper.jpg HTTP/1.1" 200 - "-" "-"
"GET /wallpaper.jpg HTTP/1.1" 200 29669 "-" "AndroidDownloadManager"

アクセスログを見る限り、保存をするを押すと、

  • 画像URLに再度GETでアクセスする
  • UAがAndroidDownloadManagerになっている

という処理になっていました。※2、3端末でしか確認してないので全ての端末でかは分かりません。。

そのため、画像保存でGETでアクセスした際、セッションチェックで弾かれて画像保存ができないというのが原因でした。


■対応

自分はAndroid端末のみ以下のように対応するようにしました。

トランザクションキーもセッションもなければ404

ちょっと対応が微妙。。な気がしなくもないですが。。


■AndroidDownloadManagerについて

AndroidDownloadManagerについてネットで調べてもあまり詳しくかかれてないようですが、

おそらくAndroid機のブラウザの1機能だと思われます。

※参照元 http://comile.jp/blog.php?id=1&mid=235738

実際、htc evo で確認した際はAndroidDownloadManagerではなく端末UAでアクセスを確認しました。

それでもGETでもう一度アクセスしているのでセッションチェックで弾かれますが。。


他にもまだまだiPhoneAndroidで違う挙動とか出てきそう。

メール受信でPHPを起動

Postfixにて、メール受信をトリガーにしてPHPを起動させる設定についてです。
よくある、空メールを送信して登録画面のURLを発行するなどのあれの設定です。


エイリアスを有効にする
メール受信をトリガーとしてプログラムを起動させる、
また今回はやりませんがローカルパート(@の左側)に送信されたメールを転送するなどをするため、
エイリアスを有効にします。

/etc/postfix/main.cf に以下の設定がコメントアウトされてると思うので、
コメントアウトを削除して有効にします。

/etc/postfix/main.cf
    #alias有効
    alias_maps = hash:/etc/postfix/aliases
    alias_database = hash:/etc/postfix/aliase


■バーチャルエイリアスの設定
Postfixで複数のドメインを使う場合、バーチャルエイリアスの設定が必要になります。
まず、/etc/postfix/main.cfでバーチャルエイリアスを有効にし、
バーチャルエイリアスの設定ファイル(今回は/etc/postfix/virtual)にてバーチャルエイリアスのユーザーリストを作成します。

/etc/postfix/main.cf
    #virtual_domain指定
    virtual_alias_domains = example.com
    virtual_alias_maps = hash:/etc/postfix/virtual
/etc/postfix/virtual
    #virtual_aliases設定
    register@example.com  register-example
    delete@example.com  delete-example


■トリガーの設定
上記設定にてregister@example.comを受信したらregister-exampleが、
delete@example.comを受信したらdelete-exampleが呼ばれるようになりました。
最後の/etc/postfix/aliasesに実行するプログラムを設定します。

/etc/postfix/aliases
    #トリガー設定
    register-example: | "/usr/bin/php /path/to/register.php"
    delete-example: | "/usr/bin/php /path/to/delete.php"

パイプで実行するプログラムを繋ぎダブルコーテーションで囲みます。

また、他のメールに転送する場合は転送先のメールアドレスを指定します。

    send: hoge@example.com,fuga@example.com

Gitのローカルブランチ、リモートブランチの両方を削除する方法(修正版)

http://d.hatena.ne.jp/boss_sato/20101228/1283915672

以前、上記記事にてローカルブランチ、リモートブランチを削除する方法を紹介しました。

が、紹介したリモートブランチの削除方法はローカルのみしか適用されず、

git fetch や git pull するとリモートブランチが元に戻ってしまいました。

ので、リモートブランチの削除方法を修正したものを再紹介。


■ローカルブランチの削除

$ git branch -a
* master
  test
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/test
$ git branch -d test
Deleted branch test (was 3cf312e).
$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/test

git branch -d [local_branch] で削除。


■リモートブランチの削除

// git push origin :[branch_name] で消すとリモートブランチが消せる
$ git push origin :test
Deleted remote branch origin/test (was 3cf312e).
$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

ちょっと分かりづらいですが、これでリモートブランチも消せます。