2015年2月26日木曜日

Capistrano 3を使ってデプロイ作業を自動化する

テストの自動化


デプロイ作業は手動でも行えますが、

  • デプロイ手順のマニュアルを作成する必要がある
  • 手動で行うため、手順を間違えないように集中して作業しなければならない
  • デプロイ作業に時間がかかる

といったデメリットがあります。

その結果

  • デプロイ手順を間違える
  • デプロイしている暇がないから、リモートサーバー上で直接ファイルを編集する

といった問題が発生しがちです。

デプロイ作業を自動化することにより

  • 作業ミスが無くなる
  • デプロイ作業にかかる時間・労力が限りなく減る

といった恩恵を得ることが出来ます。

特に納期前などの繁忙期にデプロイが自動で行えるかどうかの違いは非常に大きいです。
時間をかけて学ぶだけの価値は間違いなくあります。

Capistrano


Capistranoとはデプロイ作業を自動化するためのフレームワークです。
Capistrano自体はrubyのフレームワークですが、デプロイ対象のプログラム言語は問いません。
phpのプログラムなどでもCapistranoによりデプロイ作業を自動化することが出来ます。
しかし非常に便利なフレームワークである一方、学習コストが高いため手を付けられずにいる方も多いのではないでしょうか?

今回はCapistranoを使用したことが無い人を対象として、デプロイ自動化手順を少しずつ説明していきたいと思います。量が多いのでいくつかに分割して少しずつ説明していきます。

Capistranoの最新バージョンは3ですが、2と3とでは設定方法が大きく異なるので使用するバージョンに注意してください。今回はCapistrano 3を使用していきます。

お題


今回はデプロイ自動化の一番の基本となる

  • リモートサーバーにソースファイルをアップロードする作業

をCapistrano 3を使って自動化することにします。

概要


  • ローカルマシン上にcapistranoをインストールする
  • ローカルマシン上のソースコードをGitHubのリモートリポジト* リ上にアップロードする
  • ローカルマシン上でデプロイコマンドを実行する
  • GitHubリポジトリ上のソースコードがリモートサーバーにアップロードされる
  • デプロイコマンドはローカルのリポジトリ上で実行します。

前提条件


  • Gitを使用したことがある
  • リモートサーバーがある
  • リモートサーバーにSSHでログインできる

作業手順


ローカルマシン

1. ローカルマシン上にプロジェクトファイルを作成します。

mkdir cap_test
cd cap_test

2. アップロードしたいソースファイルを作成します。

touch test_file

3. GitHubでリモートリポジトリを作成しpushします

git remote add origin git@github.com:{your_account}/cap_test.git
git add .
git cm
git push origin master

リモートリポジトリの名前はcap_testとしました。
{yout_account}にはGitHubのアカウント名が入ります。

4. capistranoをインストールします

bundle経由でインストールします

bundle init # Gemfileが作成される

add line Gemfile

gem 'capistrano', '~> 3.0.1'

bundle install # capistranoがインストールされる

5. capistranoの初期化

cap install # capistrano用のディレクトリ・ファイルが作成されます

6. デプロイ設定ファイルの編集

デプロイの設定のために編集するファイルは2種類あります。

  • グローバルな設定ファイル
    config/deploy.rb # デプロイ全体の設定を行います。
  • 環境ごとの設定ファイル
    config/deploy/*.rb # 環境ごとの設定を行います。

今回はstaging環境を前提としたデプロイを行います。
編集するファイルは以下の2つです。

  • config/deploy.rb
  • config/deploy/staging.rb

    deploy.rbファイルを編集

    lock '3.3.5'
    set :application, 'cap_test'
    set :repo_url, 'git@github.com:{your_account}/cap_test.git'

    deploy/staging.rbを編集する

    リモートサーバーの情報に合わせて以下を設定します

    • ホストIPアドレス
    • ホストユーザ名
    • ローカルマシン上に保存したSSHの秘密鍵

    server 'xx.xx.xx.xx', user: 'remote-server-user', roles: %w{web}
    set :ssh_options, {
    keys: %w(~/.ssh/key.pem),
    }

7. ssh-agentの設定

デプロイ時にリモートサーバー上でgit cloneが実行されますが、
このとき秘密鍵が必要になります。
リモートサーバー上に秘密鍵を設定することでもgit cloneできますが、
秘密鍵の転送・設定は厄介です。特にリモートサーバーが複数ある場合その都度設定するのは面倒です。
ssh-agentを使用することにより、ローカルマシンの秘密鍵を使用してリモートサーバー上でgit cloneすることができます。

以下のコマンドによりgit cloneに用いる鍵をssh-agentに登録します(Macの場合、OSにより方法が異なります)

ssh-add ~/key.pem

8. ローカルマシン上での設定は以上で終了です。

GitHubに最新ファイルをコミットします。

git add .
git commit
git push origin master

リモートサーバー

次にリモートサーバー上での設定を行います。
リモートサーバーにログインします。

1. リモートサーバ上にアップロード用のディレクトリを作成する

デフォルトの設定では/var/www以下にアップロードされます
もしなければ作成します。

mkdir /var/www

デプロイ時にwww以下にさらにディレクトリが作成されるので、所有者も変更しておきます。

chown remote-server-user /var/www

2. gitのインストール

デプロイ時に、リモートサーバー上でgit cloneが実行されます。
もしgitがインストールされていなければインストールしておきます。

yum -y install git

デプロイ

デプロイの準備が整いました。
ではデプロイしてみましょう。
ローカルマシンのプロジェクトディレクトリ上でデプロイコマンドを実行します。

cap staging deploy

さくっとデプロイできましたか?

2015年2月18日水曜日

ブラインドSQLインジェクション

Webシステムを攻撃する手法としてSQLインジェクションが有名です。 今回は実際に、SQLインジェクションによりパスワードを盗む方法を説明します。

ログインフォームから送信したIDとパスワードが、ソースコード内で以下のような式でクエリされるとします。 また有効なIDが1つ判明しているものとします。ユーザから送信されたIDとパスワードがDB上の値と一致していなければ、クエリの返り値は0件なのでログインすることは出来ません。

  select * from user where id='$id' AND pass='$pass'

  1. 必ずログインする
  2. パスワードに以下の文字列を入力します。

    ' OR 1 OR '
    これは以下のように展開され実行されます。
      select * from user where id='$id' AND pass='' OR 1 OR ''
    
    OR 1が挿入されることにより無条件でuserがselectされてしまいます。 クエリ結果の最初のユーザをログインユーザとする、と処理されるシステムの場合、この方法によりパスワードを知らなくても必ずログインすることができます。

  3. パスワードを見つける
  4. 上記の方法によりログインそのものは成功しますが、パスワードはまだ判明していません。
    挿入する文字列を工夫することで更にパスワードまで判明させることが出来ます。

    ' OR 1 OR '
    としていたのを
    ' OR pass like '%' OR '
    と書き換えます。 これは以下のように展開されます。
      select * from user where id='$id' AND pass='' OR pass like '%' OR ''  
    
    %は0文字以上の任意の文字列にヒットします。 この状態でもやはりログインに成功します。 次は以下の文字列をパスワードとして送信します。
    ' OR pass like 'a%' OR '
    これはpassの最初の1文字目が'a'であるもののみにヒットします。
    この結果はログイン成功 / ログイン失敗に分かれます。
    もし指定したIDのuserのpassの1文字目がaであった場合は、ログインに成功します。
    もし指定したIDのuserのpassの1文字目がaでなかった場合は、ログインに失敗します。
    これをa ~ Z, 0 ~ 9の全ての文字に対して行うことで確実にpassの最初の1文字を特定することが出来ます。
    1文字目を特定することが出来れば同じ原理で2文字目以降も特定可能です。
    もし全ての文字に対してログインが失敗した場合、次の文字はありません。

    このようにして、パスワードを盗むことができます。

2015年2月16日月曜日

Mysqlの接続について【localhost】と【127.0.0.1】の違い


ソケット(socket)ファイルとは
 ソケットファイルとはUNIXOSのプロセス間通信で使用されるファイルである。

特徴:
・ソケットファイルを用いることでサーバー/クライアント間のプロセス間通信をファイルを通じて行うことができる。
・ファイルを使用する為、サーバー/クライアントが同一ホストの場合に限られる。
・ソケットファイルを用いるとTCP/IP接続と比べ、高速な通信が可能となる。

【localhost】と【127.0.0.1】の違いについて
 localhost】はソケットファイルを使用した接続が行われるが、【127.0.0.1】では、TCP/IPを利用した接続が行われます。


では、Mysqlの接続について確認してみましょう

コマンドラインでmysqlに接続
下記のコマンドラインで設定を確認してみます
$php --ri mysqli

mysqli

MysqlI Support => enabled
Client API library version => mysqlnd 5.0.11-dev - 20120503 - $Id: f373ea5dd5538761406a8022a4b8a374418b240e $
Active Persistent Links => 0
Inactive Persistent Links => 0
Active Links => 0

Directive => Local Value => Master Value
mysqli.max_links => Unlimited => Unlimited
mysqli.max_persistent => Unlimited => Unlimited
mysqli.allow_persistent => On => On
mysqli.rollback_on_cached_plink => Off => Off
mysqli.default_host => no value => no value
mysqli.default_user => no value => no value
mysqli.default_pw => no value => no value
mysqli.default_port => 3306 => 3306
mysqli.default_socket => /tmp/mysql.sock => /tmp/mysql.sock
mysqli.reconnect => Off => Off
mysqli.allow_local_infile => On => On

上記により、デフォルトは/tmp/mysql.sockのソケットファイルを使用することが分かりました。

実際にサンプルを作成して確認してみます。サンプル 名はmysqli.phpにする。
<?php
 //定義
 $host = 'localhost';
 $username = 'fuel_dev';
 $password = 'tammulodo';
 $database = 'fuel_dev';
 $port = '3306';
 $socket = '';

 //mysqliクラスのオブジェクトを作成
 $mysqli = new mysqli($host, $username, $password, $database, $port, $socket);
 //エラーが発生したら
 if ($mysqli->connect_error){
   print("接続失敗:" . $mysqli->connect_error);
   exit();
 }
 print("接続に成功しました\n");

 //切断
 $mysqli->close();

コマンドラインで実行
$ php -f mysqli.php
接続に成功しました

上記のサンプルにより、ホスト名は【localhost】を指定し、ソケットファイルを使用してmysqlに接続することが確認出来ました。

Webブラウザからmysqlに接続
上記のサンプルをFuelPHPの実行フォルダにコーピーし、実行すると下記のエラーが発生しました。

Webサーバーからmysqlに接続する時、TCP/IP経由で接続する必要がある為、ホスト名が明示に【127.0.0.1】を指定しなければなりません。
では、上記のサンプルにホスト名を”localhost”から"127.0.0.1"に変更して、再度実行してみると、正常に接続することが出来ました。

まとめ
コマンドラインでmysqlに接続する時、ソケットファイルを使用しても、TCP/IP経由してもmysqlに接続することが出来ますが、Webブラウザからmysqlに接続する時、TCP/IP経由で接続する必要があるので、ホスト名が明示に指定しなければなりません。

2015年2月13日金曜日

BloggerでcodeのSyntax Highlighting

Bloggerに限りませんが、Blog PostでcodeをSyntax Highlightingする一つの方法は、google-code-prettifyを使用することです。

https://code.google.com/p/google-code-prettify/

他の方法としてはGithub GISTをembedすれば簡単ですが、一つのPageで多用するとPageが重くなるのでそちらを使いたい場合は適度に自重してください。

以下、Bloggerでのgoogle-code-prettifyの導入方法です。

スタイルの選択

好みのスタイルをhttp://google-code-prettify.googlecode.com/svn/trunk/styles/index.html から選択します。本ブログではSunburstを採用します。異論は認めません。 URLをコピーします。Sunburstの場合は、https://code.google.com/p/google-code-prettify/source/browse/trunk/styles/sunburst.css です。以下の手順は、Sunburstと仮定して書いています。

HTMLエディタ

Blogger Admin PanelにあるSide Menuの、Template -> Edit HTMLでHTMLエディタ画面を開きます。

スタイルの貼り付け

head tag内の末尾に以下のコードを貼り付けます。

<link rel="stylesheet" type="text/css" href="https://google-code-prettify.googlecode.com/svn/trunk/styles/sunburst.css">

templateがxmlである場合、末尾に / が必要です。

<link rel="stylesheet" type="text/css" href="https://google-code-prettify.googlecode.com/svn/trunk/styles/sunburst.css" />

スクリプトの貼付け

HTMLエディタ内のbody tagの末尾に以下のコードを貼り付けます。

<script type="text/javascript" src="https://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js"></script> <script type="text/javascript">prettyPrint();</script>

Save templateボタンを押下します。これで設定は完了です。


動作確認

preタグ

pre tagのclassにprettyprintを指定すると、syntax highlightされます。

    var logging = context.services.logging;
    logging.debug('Got request ${request.uri} .');

AndroidのListViewでSwipeRefreshLayoutを使ってみた

こんにちは。岡田です。

今回は前回に続いて、ListViewについての記事を書く。
ListViewのRefreshするUIついて、色々な方法があると思うが、
今回はListViewを下にSwipeすることでRefreshができるSwipeRefreshLayoutを使ってみた。
SwipeRefreshLayoutは、Support Library revision 19.1.0から追加された。

Dart appengineでloggingする方法

Dart appengineで、print()やlogging libraryを使用してもterminalにlogが吐かれませんでした。logが吐かれないとまともに開発することは難しいため、調査しました。

appengine packageのソースコードをチラ見する

appengine.dartをのぞいてみると、

export 'api/logging.dart';

とあり、import ‘package:appengine/appengine.dart’;したsource codeではloggingが使用可能であることが分かります。

api/logging.dartではLoggingというabstract classが定義されています。そこでは、functionとしてcritical(), error(), warning(), debug()が定義されています。明らかにLog Levelを意味しています。 LogLevel classでは、各log levelが

static const LogLevel CRITICAL = const LogLevel._('Critical', 4);

などと定義されています。


実体を探すと、appengine/src/api_impl/logging_impl.dartで、appengineのlogging機構を使用するコードになっているようです。(なにやらIssueやらTODOやら見えますね。)

つまり、Dartのappengine packageにはappengine用の独自のlogging libraryが含まれていますので、標準libraryのlogging等をimportする必要はありませんし、そして(少なくとも現在は)使うこともできないようです。

サンプルコードを検索する

そして、おもむろにサンプルコードを検索し、loggingをしている箇所を発見しました。 https://github.com/dart-lang/appengine_samples/blob/master/cloud_datastore/bin/server.dart#L97

var logging = context.services.logging;
logging.debug('Got request ${request.uri} .');

確かにappengineのloggingを利用しています。

しかし、このサンプルコードがあるサンプルプロジェクトを動かしてみても、logが吐かれません。print()デバッグもできません。

Github IssuesやGoogle Groupを検索する

関連するGithub IssuesやGoogle Groupを検索したところ、以下のスレッドを発見。

https://groups.google.com/a/dartlang.org/forum/#!searchin/cloud/log/cloud/Eyi-BfujX0U/Y8QW2kHycm8J

現在はまだ、–verbosity debug フラグを付けないとlogが表示されないようで、将来はこのフラグ無しでlogがterminalに表示されるようになるそうです。

そこで、

$ gcloud preview app run app.yaml

ではなく、

$ gcloud --verbosity debug preview app run app.yaml

として起動したところ、無事loggingとprint()の内容がterminalに表示されました。

冗長な表示内容

しかし、–verbosity debugでは他の冗長な内容が表示されすぎて辛い。なにか他の起動optionはないかと

$ gcloud preview app run --help

してみると、–log-level というoptionを発見。

しかし、–log-level=“debug” (または–log-level debug)を付加しても、自分が試した限りでは期待する動作をしてくれませんでした。

というわけで、

現状ではまだ、–verbosity debugにするしかないのかも。まあまだベータ版ですし、これからのアップデートに期待。

DartEditorなどのdebuggerとの統合ももう少し先になるようです。 http://stackoverflow.com/questions/26807828/how-can-i-debug-a-dart-appengine-app

Google App Engine Managed VM Dartを使ってみる 後編

この記事は、前回のGoogle App Engine Managed VM Dartを使ってみる の続きです。

gcloud SDKのインストール

Google cloud SDKをインストールします。 手順については、https://cloud.google.com/sdk/#Quick_Start を参照してください。 忘れずに $ gcloud auth login しましょう。

Google Cloudにsample projectを作る

WebブラウザのGoogle Developers Consoleなどから、Dart GAEの動作確認用のsample projectを作ります。 作成したら、そのsample projectのproject-idをセットします。 $ gcloud config set project

gcloudを最新版にする。

$ gcloud components update app

Sample project でhello world

ここから、Dart app engineのprojectをlocalに作成していきます。

$ mkdir helloworld && cd helloworld

app.yamlの作成

Appengineアプリケーションにはapp.yamlが必要です。以下の内容でapp.yamlファイルを作成します。

version: helloworld
runtime: custom
vm: true
api_version: 1

Dockerfileの作成

以下の内容でDockerfileファイルを作成します。

FROM google/dart-runtime

pubspec.yamlの作成

Dartアプリケーションなので、pubspec.yamlが必要です。以下の内容でpubspec.yamlファイルを作成します。

name: helloworld
version: 0.1.0
author: <your name>
dependencies:
  appengine: '>=0.2.1 <0.3.0'

dependencies sectionでappengine packageを指定しています。 忘れずに pub getしてpackageをinstallしておきましょう。

hello world コードを書く

Dart appengineは必ずbin/server.dartから起動します。 bin/ directoryを作成します。

$ mkdir bin

bin/server.dartを作成し、以下のコードを書きます。

import 'dart:io';

import 'package:appengine/appengine.dart';

main() {
  runAppEngine((HttpRequest request) {
    request.response..write('Hello, world!')
                    ..close();
  });
}

Docker imagesの取得

起動する前に、docker imagesを取得しておきます。

$ docker pull google/dart-runtime

起動

local環境でappengineを起動します。

$gcloud preview app run app.yaml

ブラウザから、http://localhost:8080にアクセスし、Hello, world!’と表示されていることを確認します。

Deployment

$ gcloud preview app deploy app.yaml
  • Dart GAEはGoogle Container Engineを使用しているため、クレジットカードを登録して課金を有効にする必要があります。 Google developers consoleから、デプロイしたurlにアクセスし、動作を確認して下さい。

まとめ

詳細な解説は、https://www.dartlang.org/server/google-cloud-platform/app-engine/run.html を参照してください。 コードサンプルは、https://github.com/dart-lang/appengine_samples にまとめられています。コードサンプルではgcloudのAPIとしてcloud_datastore、cloud_sql, memcache, modulesなどのサンプルが確認できます。

Dart app engineはベータ版であるため、シリアスなプロジェクトでの採用にはまだ慎重になるべきです。ドキュメントも未整備の部分が多いのですが、ライブラリののコードを読んで楽しめる人は挑戦してみると面白いかもしれません。AppEngineのサービスに乗ってDartでサーバーを構築できるのは大きな魅力なので、折にふれて動向を追っていければと思います。