タイトル

Need for Answer

2014年3月27日木曜日

AWSと消費税

AWSを利用している皆さん、『東京リージョンにおけるAWSのサービス料金請求プロセスの明確化に関する御案内』というメール来てますよね!(2014年3月時事ネタ)

AWSの請求って『税金』の部分が見えないから意識しないですよね。意識しても取られてますが!ということで調べてみました。

…実は『AWS の請求に関するよくある質問』を読むとあっさり解決してしまうのですが、簡単に言うと『「既定のお支払い方法」に関連づけられた住所』に基づいた税金がかかります。

つまり東京リージョン以外のEC2とかを使っていても、税率が変わるとかは無いということですね。といっても日本では『売上税』は取られないのですが!

あと消費税について記載のある部分は『Amazon EC2 よくある質問』ですね。表示価格は消費税込みです。

2014年3月20日木曜日

MySQL冗長化モデル

以前作成したMySQLの冗長化モデルをご紹介。
  • MySQL
  • LVS
  • iptables

を利用しています。

Master及びSlaveそれぞれの冗長化及びスケールについて、参考になる部分もあるかと。運用実績も1年以上経過し、稼働中の障害復旧もできているので大丈夫なんじゃないかな-と思います。


2014年3月19日水曜日

『大事なことはタグに書く』的なお話

AWS EC2運用をしていると、『AWS運用に関する変数』をどうやって収納するか悩みますよね!OSのユーザー権限とも違うし、テキストで置いたらセキュリティ的にも…とか思うと夜も寝られません。

ということで、『EC2のタグに書いてしまえばいいじゃない!』という発想もあるよ!というお話です。

色々利点は有るのですが、一番の利点は『AWS Management Console上で確認・変更できる』ということじゃないでしょうか。

awscli+jqのサンプルコードを書いてみました。


# タグをつける
# 同一Keyに実行すると、上書きされる
aws ec2 create-tags --resources `curl -s http://169.254.169.254/latest/meta-data/instance-id` --tags "Key=TEST,Value=TESTValue"

# タグの値を取得
aws ec2 describe-tags --filters "Name=resource-id,Values=`curl -s http://169.254.169.254/latest/meta-data/instance-id`"|jq -r ‘.Tags[] | select(.Key=="TEST")|.Value’ ⇒ TESTValue  or null

# タグを削除
# keyが存在しなくてもreturnはtrue
aws ec2 delete-tags --resources `curl -s http://169.254.169.254/latest/meta-data/instance-id` --tags "Key=TEST"

2014年3月18日火曜日

Amazonの買い物をGoogleCalendarへ登録するChrome拡張機能

Amazon.co.jpの買い物って、いつ届くか忘れますよね!
過去からのテロ攻撃って怖いですね。

…ということで、amazonの商品お届け予定日をGoogleCalendarに登録するChrome拡張機能を作りました!

Amazon2GoogleCalendar

Google Chrome拡張機能をインストールすると、Amazon.co.jpの『注文履歴を見る』ページにGoogleCalendarへ登録するボタンが追加されます。使い方簡単。



Amazonが公式にwebcal対応してくれればいいのにな…。

2014年3月17日月曜日

AWSのアクセスキーとシークレットキーを運用する

AWSより「AWS アカウントアクセスキーの管理方法に関する重要な変更」というメールを貰った皆さん!IAMできちんと運用してないですよ、と注意されたようなものですね!

…ということでAWSのアクセスキーとシークレットキーってどうやって運用するのが良いの?というお話。結論を先に言うと、『権限が限定されたAWSアクセスキーを作る』ということです。

以下本題。

セキュリティの根本的な考え方として、『WEBに公開しているシステムは、実行ユーザーで出来る全てのことをやられる可能性が有る』というのが有ります。

仮にApacheをApacheユーザーで実行していたとしたら、コンソールでApacheユーザーにスイッチした時に出来る事は全て出来る、と考えます。『ブラウザからアクセスしているから大丈夫』とか『ベーシック認証かけてるから大丈夫』なんて何の防御力も無いです。

つまり、『実行ユーザーには最低限の権限だけを与えて、ハックされても問題ないようにする』ということです。『入られない』ではなく、『入られても問題ない』を中心に考えよう、ということです。

この考えはAWSのユーザー管理も一緒です。つまり『AWSアクセスキーとシークレットキーが漏れても、権限を絞っておけば被害が抑えられる』と考えます。


これを実運用に落とし込みます。AWSは権限を、『IAMで作成するユーザー単位』で定義します。つまり、『このユーザーはAWSのどの機能まで使えるのか?』ということです。

新規作成されたAWSアカウントは『ルートアカウント』と呼ばれ、AWSへのアクセス権限がフルにあるということです。(Administrator・rootをイメージして頂ければよいかと)

その上でIAMでユーザーを新規作成し、画面に出てきたAWSアクセスキーとシークレットキーを控えておきます。

そのアカウントに最低限の権限を付与します。(S3にだけアクセスできる、とか)

あとはそのキーを利用すれば、最低限のセキュリティは守られるという事になります!

『入られる事を前提』と考えますと、『入り口を限定すること』『定期的にキーを変更すること』といった運用が必要、という事にもなりますね。

2014年3月16日日曜日

Gmailで繰り返し来るメールを自動削除する

定期配信されるメールって有りますよね。
そういうメールって、最新のもの以外はいらなかったりしませんか?

ということで、『決まった題名のメールを1通だけ残して、あとはアーカイブ』をGmailで自動化しよう!というお話です。

とりあえず使うものはGmailとGoogle Apps Scriptになります。簡単に説明すると『GmailをメンテナンスするJavaScript』をGoogleのサービスで定期実行させる、ということです。こんなことも無料で出来るんですよ。

以下ソース。

var TITLE="メールのタイトル"

// メイン処理
function Main(){
  var TMPOBJ = STRinMailTitle(TITLE);
  ArcOldMail(TMPOBJ);
}

// 文字列が含まれたスレッドオブジェクト配列を返却
function STRinMailTitle(STR){
  var RETOBJ = [];
  
  var thds = GmailApp.getInboxThreads();
  for(var n in thds){
    var thd = thds[n];
    var subject = thd.getMessages()[0].getSubject();
    if (subject.indexOf(STR,0) != -1){
      RETOBJ.push(thd);
    }
  }
  return RETOBJ;
}

// スレッド配列を処理する
function ArcOldMail(OBJ){
  
  var TMPOBJ=OBJ;
  
  // 配列先頭を削除
  TMPOBJ.shift();
  // 先頭以外は既読・アーカイブする
  GmailApp.markThreadsRead(TMPOBJ);
  // アーカイブ
  GmailApp.moveThreadsToArchive(TMPOBJ);
}

上記ソースをGoogleドライブのスクリプトを新規作成して、仕込んで下さい。

次に1行目の「メールのタイトル」部分を、『定期メンテナンスしたいメールのタイトル』に書き換えてください。部分一致でも大丈夫です。

あとは、Main()を時間トリガーに仕込みます。

可読性を上げるために処理を削りましたが、『1通目だけラベルをつける』『メール本文で判定』とか色々なことが出来ますので、改造してみてはいかがでしょうか?

2014年3月14日金曜日

なぜELB経由のEC2にGlobal IPを割り当てられないのか?

ELBでアクセスしているEC2。AWSインフラ構築者が一度は聞く相談として、『ELBで公開しているWEBに、固定Global IPも割り当てたいんだけど?』が有ります…よね。

なんでみんな気軽に『Elastic IPをEC2に割り振ればいいんじゃないの?』って言うんでしょう!

『出来ない』と答えるにしても、説明面倒ですよね!
そんな悲しい歴史を断ち切るために、わかりやすく解説しました!

ELB経由でアクセスする、通常パターンを図解してみました。


ここでは簡単に『EC2は、外部(0.0.0.0/0)から来た通信を、ELBにのみ打ち返している』と思ってくれればいいです。

さて、このEC2にElastic IPを割り当てましょう。そして外部からアクセスするとどうなるでしょうか?(NGパターンですね。)


これが『通信できない』状態です。つまり『EC2はElastic IPから来た通信を、ELBに打ち返している』のです。外部からの入り口が2つになっても、EC2がどの入口にレスポンスを返却すれば良いのかは判ってないのです。

そしてデフォルトの通信経路、ELBに向かって通信を返します。受け取ったELBは返却先が判らないので、通信が迷子になるのです。説明おしまい。

※解決方法は色々あるので説明省略!

2014年3月13日木曜日

AWS EC2バックアップを定期的に取得する方法(awscli+jq版)

AWS EC2のストレージ信頼度は高いです。が、人為ミスによるコンテンツ紛失はどうにもなりません。…EC2をバックアップしたいという需要はクラウドでも一緒ですね。

先人の作り上げたShellScriptをawscli+jqで移植しよう!というお話です。STOP!車輪の再発明!

長いので使い方を先に説明しますと、『all_createsnapshot.sh』をどこかのEC2で実行すると、タグ付けられたEC2-Instanceを全部バックアップします!cronに登録しておくと良いと思います!

前置き終了以下解説。

awscli』と『jq』のインストールが必須です。
jqはパスの通った所に置いて下さい。

まずはShellScriptから説明します。

2つのShellScriptは同一ディレクトリに設置して下さい。awscliの設定ファイル設置場所をハードコードしておりますので、各自ご修正ください。

2つのShellScriptの役割はこんな感じです。
  • all_createsnapshot.sh ⇒ バックアップ対象のEC2のInstance-IDをリスト化
  • createsnapshot.sh ⇒ Instanse-IDを渡すとSnapshotを取得。過去分は削除。
ログは『標準出力』『/var/log/message』の2つに出力されます。

ShellScriptはパラメータを渡して実行する事ができます。
通常は自動取得されますので、設定の必要は無いです。

all_createsnapshot.sh
パラメータ 内容 未指定時
-h ヘルプを表示 表示しない
-c AWS_CONFIG_FILEの場所を指定
(外部から引き渡すときに使用)
Shell内設定を使用

createsnapshot.sh
パラメータ 内容 未指定時
-h ヘルプを表示 表示しない
-c AWS_CONFIG_FILEの場所を指定
(外部から引き渡すときに使用)
Shell内設定を使用
-i SnapShotを取得したいInstanceID 実行中のインスタンス
-g SnapShotを保存する世代数 5

#!/bin/bash
#
# All Instances CreateSnapshot script

CONFIG_FILE=/root/aws/account.txt

opterror(){
    echo "Usage: $prog -c [config]" 1>&2
    echo "             -h HELP"
    exit 1
}

while getopts c:h OPT
do
  case $OPT in
    "h" ) HELP=1 ;;
    "c" ) CONFIG="$OPTARG" ;;
      * ) opterror
  esac
done

# TimeWatch Start ----------------------------
START_TIME=`date +%s`
TMPTXT="$0 Start EC2 SnapShot JOB..."
echo "${TMPTXT}" && logger -t $0 -i "${TMPTXT}"

# Exec ---------------------------------------
test ! -z $HELP   && opterror
test ! -z $CONFIG && CONFIG_FILE=${CONFIG}

export AWS_CONFIG_FILE=${CONFIG_FILE}
script_dir=$(cd $(dirname $BASH_SOURCE); pwd)

# Exec Failed Check --------------------------
test -z ${AWS_CONFIG_FILE} && echo "Not Set AWS_CONFIG_FILE ..." && exit 1
test ! -f ${script_dir}/createsnapshot.sh && echo "Not Find createsnapshot.sh ..." && exit 1

# Loop Tagged Instances
aws ec2 describe-instances --filters "Name=tag-key,Values=backup" "Name=tag-value,Values=ON"|jq -r ".Reservations[].Instances[].InstanceId"| while read INSTANCE_ID
do
    GENERATION=`aws ec2 describe-instances --instance-id=${INSTANCE_ID} |jq -r '.Reservations[].Instances[].Tags[]|select(.Key== "generation").Value'`
    test -z ${GENERATION} && GENERATION=5

    TMPTXT="Backup INSTANCE is ${INSTANCE_ID} and Generation is ${GENERATION}"
    echo "${TMPTXT}" && logger -t $0 -i "${TMPTXT}"

    if [ ! -z $CONFIG ];then
        ${script_dir}/createsnapshot.sh -i ${INSTANCE_ID} -g ${GENERATION} -c $CONFIG
    else
        ${script_dir}/createsnapshot.sh -i ${INSTANCE_ID} -g ${GENERATION}
    fi
done

# TimeWatch STOP -----------------------------
END_TIME=`date +%s`

SS=`expr ${END_TIME} - ${START_TIME}`

HH=`expr ${SS} / 3600`
SS=`expr ${SS} % 3600`
MM=`expr ${SS} / 60`
SS=`expr ${SS} % 60`

TMPTXT="$0 exec Total Time => ${HH}:${MM}:${SS}"
echo "${TMPTXT}" && logger -t $0 -i "${TMPTXT}"

#!/bin/bash
#
# CreateSnapshot script
# using awswcli + jq

CONFIG_FILE=/root/aws/account.txt
export LANG=C

#=====================================================
opterror(){
    echo "Usage: $0 -i [instance_id] -g [backup generation] -c [config]" 1>&2
    echo "          -h HELP"
    echo "          if Not Defined instance_id, take Backup Myself."
    exit 1
}

while getopts i:g:c:h OPT
do
  case $OPT in
    "h" ) HELP=1 ;;
    "i" ) EC2_INSTANCE_ID="$OPTARG" ;;
    "g" ) EBS_GENERATION_COUNT="$OPTARG" ;;
    "c" ) CONFIG_FILE="$OPTARG" ;;
      * ) opterror
  esac
done

#------------------------------------------------------
test ! -z $HELP && opterror
test ! -z $CONFIG && CONFIG_FILE=${CONFIG}

export AWS_CONFIG_FILE=${CONFIG_FILE}

test -z $EC2_INSTANCE_ID && EC2_INSTANCE_ID=`curl -s http://169.254.169.254/latest/meta-data/instance-id`
test -z $EBS_GENERATION_COUNT && EBS_GENERATION_COUNT=5
#======================================================

EBS_VOLUME_ID=`aws ec2 describe-volumes|jq --arg hoge ${EC2_INSTANCE_ID} -r '.Volumes[]|select(.Attachments[].InstanceId ==$hoge)|.VolumeId'`

# Create SnapShot --------------------
NOW=`date +"%Y/%m/%d %k:%M:%S"`
DESCRIPT_TXT="AutoBackup ${EC2_INSTANCE_ID} created at ${NOW}"
aws ec2 create-snapshot --volume-id ${EBS_VOLUME_ID} --description "${DESCRIPT_TXT}" >/dev/null 2>&1

# Error Catch ------------------------
RESULT=$?
TMPTXT="${EC2_INSTANCE_ID} create snapshot ${EBS_VOLUME_ID} "

if [ ${RESULT} -ne 0 ];then
  TMPTXT="${TMPTXT} => failed..."
else
  TMPTXT="${TMPTXT} => Success!!"
fi

echo "${TMPTXT}" && logger -t $0 -i "${TMPTXT}"

# List & Sort Delete SnapShots -------
SNAPSHOTS=(`aws ec2 describe-snapshots |jq --arg hoge ${EBS_VOLUME_ID} -c '.Snapshots[] | select(.VolumeId==$hoge) | [ .StartTime, .SnapshotId, .Description ]'| grep "AutoBackup" |sort -t "," -k 1,1| awk -F'[,"]' '{print $5}'`)
SNAPSHOTS_LINE=${#SNAPSHOTS[*]}

CNT=`expr ${SNAPSHOTS_LINE} - ${EBS_GENERATION_COUNT}`

# Exit -------------------------------
if [ ${CNT} -lt 0 ];then
  exit 0
fi

# Delete OLD SnapShot ----------------
for ((i = 0; i < ${CNT}; i++)){
  aws ec2 delete-snapshot --snapshot-id ${SNAPSHOTS[i]} >/dev/null 2>&1

  RESULT=$?
  TMPTXT="${SNAPSHOTS[i]} delete"

  if [ ${RESULT} -ne 0 ];then
    TMPTXT="${TMPTXT} => failed..."
  else
    TMPTXT="${TMPTXT} => Success!!"
  fi

  echo "${TMPTXT}" && logger -t $0 -i "${TMPTXT}"
}

次に、実際の使用方法を説明します。

バックアップ取得対象のEC2にTagを付けます。「Key:backup Value:ON」のものをバックアップ対象とします。「Key:generation Value:[任意の数値]」をつけると、その世代分以降のバックアップは削除されます。未指定時は5世代です。


あとはどこかのEC2で『all_createsnapshot.sh』を実行すれば、タグ付けされたEC2のSnapShotが取得されます。

なおバックアップ削除は『Descriptionに「AutoBackup」という文字列が含まれている物のみ』対象にします。手動取得したSnapshotsが勝手に消去されることは無いのでご安心下さい。

参照元:
【AWS】クラウド時代のバックアップ管理術  ~tagを活用したsnapshotの世代管理~
Amazon EBSのスナップショット(バックアップ)を取得しつつ世代管理も行うスクリプト



2014年3月11日火曜日

AWSアカウントを跨いでAMIをコピーしたい!

AWSでアカウントを跨いでAMIをコピーしたい…ありますよね!

『アカウント間でのAMI共有』は機能提供されているので問題無いのですが、共有されたAMIはコピー出来ません。グレーアウトしてますね。

実はOwnerが自分のAMIの物しかコピー出来ないのです。



AWS Management Consoleで出来ないなら、awscliだ!…とコマンドを発行してみましょう。
# aws ec2 copy-image --source-region ap-northeast-1 --source-image-id ami-******** --name AMI_Copy_Test
{
    "ImageId": "ami-********"
}
コマンドプロンプトでは成功したかのようなステータスが返ってきますが、確認してみるとこんなステータスになってます。


ということで解決方法。
  1. 共有されたAMIを元に、新規インスタンス起動
  2. 起動インスタンスから、Create Image
あんまりスマートじゃない気がしますね…

2014年3月10日月曜日

New RelicのApdexって何?

New Relicをお使いの皆さん、Apdexって何?という疑問…あると思います!
Wikipediaにも説明がありますが、2行で要約。

何割の人がレスポンススピードに満足しているか?』です。
0~1の範囲になり、1がみんな満足です。

以下New Relicのロジックを翻訳してみます。
『サーバー応答』と『ブラウザ』でのApdexがありますが、違いは後述。

まず『レスポンスに満足する秒(T)』を自己定義します。NewRelicのデフォルトは0.5秒です。
Applications>Settings>Application>App server>Apdex Tで設定可能です。

次にレスポンス速度毎に3分類し、アクセス回数を集計します。
『不満』はTの4倍超の時間です。(4倍はApdexの決まり)

満足(Satisfied)⇒0.5秒以下
容認(Tolerated)⇒0.5~2秒の間
不満(Frustrated)⇒2秒超

3つの数字が出ますので、これを計算式に代入するとApdexスコアが出ます。
Apdexスコア = (満足数 + 容認数 / 2) / アクセス合計数



上記計算式のNewRelicリンクも貼っておきます。

この値が0.7を下回るとエラーとなります。
アラート閾値を変更したい時は、Tools>Alert policiesで設定できます。

次に『サーバー応答』と『ブラウザ』の違いを説明します。
要約すると『レスポンスを計測する範囲の違い』です。

『サーバー応答』のレスポンスは、『ユーザーリクエストがサーバーに到着~サーバーがコンテンツを応答まで』になります。
サーバー⇒エンドユーザー間のネットワーク到達時間は考慮されていません

それに対して『ブラウザ』はユーザーがリクエストを発行してから、ドキュメントがロードされるまでの時間になります。
つまりネットワーク到達時間も考慮に入れている事になります。

これはNewRelicエージェントがJavaScriptを自動的に埋め込んで計測しています。
httpドキュメントの最下部にJavaScriptが自動添付されているのが確認できますね。

2014年3月7日金曜日

monitの罠

monit使ってますか!

プロセス監視やhtmlファイル改ざんチェック、ftpでディレクトリアップロードにアップロードされた時アクションするとか、アイデア次第で色々使えますね。非常に便利。

…実は設定ファイルを書くときに、注意しなければならない罠があるのです。
check process ntpd with pidfile /var/run/ntpd.pid
    start program = "/etc/init.d/ntpd start"
    stop program = "/etc/init.d/ntpd stop"
    if 5 restarts within 5 cycles then timeout

上記設定ファイルは問題が有ります。何が問題か?といいますと、『設定ファイルの末尾が空白改行』なのです。監視プロセスが落ちても再起動しません。『~timeout』の後に有る改行を削除すれば大丈夫です。

とりあえずmonit5.5だとなってたので、皆さん注意しましょうね、というお話でした。

2014年3月6日木曜日

素晴らしきヒィッツカラルド

横長のjpgやpngファイルを半分分割して、連番名にしてくれるツールを作りました。
本とかパンフレットを見開きスキャンした時に使うことを想定しています。
要.Net frameworks 4.5。

素晴らしきヒィッツカラルド

  1. エクスプローラーから対象ファイルをドラッグして下さい。(フォルダは対応しません)
  2. 横長のファイルのみ自動判別し、編集対象にします。それ以外は無視します。
  3. 『右開き』『左開き』を指定することが出来ます。
  4. jpgは圧縮率を指定することが出来ます。
  5. ファイル名接頭詞を指定することが出来ます。
  6. 連番は『1』から始まります。
  7. 処理中に1・2を変更する事が出来ます。各人の使用モラルに任せます。
  8. 作業ディレクトリ配下に『Edit』というフォルダを作ります。作業時には、その中にあるファイルを一旦全消去します。

【2014/03/25:追記】 拡張子の大文字・小文字判定が甘かったので修正

AWS CloudWatchの値をfluentdに流す方法

AWSのcloudwatchは2週間以上前のデータを見ることが出来ません。要件次第では自力ハウジングする必要があります。先人に感謝しつつ実現しましょう、というお話。

概要は、『fluentdでShellScriptを定期的にkickし、返り値をtsv形式で受け取る』という感じ。fluentdで受け取ったあとは、outputプラグインにお任せしましょう。

まずはShellScript。『aws cloudwatch get-metric-statistics』で取得した値を、tsv形式で吐き出すスクリプトです。今回はSNS通信の送信・失敗数をカウントすることにしました。

get-metric-statisticsは該当する値が無いとnullを返すので、『0』を代入する必要が有ります。これくらいしか悩む所ありません。
#!/bin/bash
export AWS_CONFIG_FILE=[適当なパス]

if [ $# -ne 1 ]; then
  LASTSEC=300
else
  LASTSEC=$1
fi

LASTSEC_CMD="$LASTSEC seconds ago"

NOW=`date +"%Y/%m/%dT%I:%M:%SJST"`
LASTDAY=`date -d "$LASTSEC_CMD" +" %Y/%m/%dT%I:%M:%SJST"`

# DEBUG -----------------------------------
#LASTDAY=`date -d '5 days ago' +"%Y/%m/%dT%I:%M:%SJST"`

# Get CloudWatch DATA ---------------------
SEND=`/usr/bin/aws cloudwatch get-metric-statistics --namespace "AWS/SNS" --metric-name "NumberOfMessagesPublished" --start-time $LASTDAY --end-time $NOW --period $LASTSEC --statistics "Sum" --dimensions Name=Application,Value=********** --output text | awk 'NR==2 {print $2}'| sed -e "s/.0//g"`

FAILED=`/usr/bin/aws cloudwatch get-metric-statistics --namespace "AWS/SNS" --metric-name "NumberOfNotificationsFailed" --start-time $LASTDAY --end-time $NOW --period $LASTSEC --statistics "Sum" --dimensions Name=Application,Value=********** --output text| awk 'NR==2 {print $2}'| sed -e "s/.0//g"`
# null check ------------------------------
if [ -z $SEND ]; then
  SEND="0"
fi
if [ -z $FAILED ]; then
  FAILED="0"
fi

# echo TSV Format -------------------------
echo -e "$SEND\t$FAILED"

このShellScriptをfluentdからkickしてもらいます。ShellScriptから吐き出されたtsvにはkey、tagがないので、ここで定義します。起動間隔もここで定義できます。
<source>
  type exec
  command [shellのパス]
  keys [key名1],[key名2]
  tag [タグ名]
  run_interval 300s
</source>
fluentdに投げ込んだ後は、S3に送るなりmongoDBに喰わせるなりご自由に!