set setting reset

脂肪と糖にはたらくやつ

apache で X-Forwarded-For をログに記録する

記事としてよくありますが、ちょっとハマったので記録として。

<VirtualHost *:80>
    ServerName hoge.com
    DocumentRoot "/var/www/html"
    LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined_x
    CustomLog "/var/log/httpd/access_hoge.com.log" combined_x
    ErrorLog  "/var/log/httpd/error_hoge.com.log"
</VirtualHost>

ELB 配下で使っているため、 %h は除去しました。
また、 combind_x としているのは /etc/httpd/httpd.conf にある LogFormat のデフォルト設定に上書きされてしまうことを回避するためです。

 218 #
 219 # Load config files from the config directory "/etc/httpd/conf.d".
 220 #
 221 Include conf.d/*.conf

 493 #
 494 # The following directives define some format nicknames for use with
 495 # a CustomLog directive (see below).
 496 #
 497 LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

ログは以下のようになります。

52.68.214.11 - - [10/Feb/2016:15:58:36 +0900] "GET /index.html HTTP/1.1" 200 47 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.103 Safari/537.36"

以上です。

ssh で rake spec する時に sudo password が渡せない

追記

serverspec の実行時や、ローカルPCの環境変数に sudo_password を設定すればデフォルトのままで問題ありませんでした。

Sudo password support

If you log into servers as non-root user, Serverspec add "sudo" in front of the command. You can specify sudo password like this.

$ SUDO_PASSWORD=xxxxxxxx rake spec

Or display prompt for sudo password if you run Serverspec like this.

$ ASK_SUDO_PASSWORD=1 rake spec

tutorial を先に読まないとこういうことになりますね。猛省します。

追記ここまで

.ssh/config や /etc/hosts をごにょごにょして、
デフォルトの spec_helper.rb を使って鍵認証でもパスワード認証でも ssh ログインまでできたのですが、sudo できずにエラーで終了してしまいます。

$ rake spec
(in /home/vagrant/serverspec)
/home/vagrant/.rbenv/versions/2.2.3/bin/ruby -I/home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-support-3.4.0/lib:/home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.1/lib /home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.1/exe/rspec --pattern spec/test-001/\*_spec.rb

Package "postgresql-9.3"
hoge@test-001's password:
Wrong sudo password! Please confirm your password on test-001.

Finished in 6.7 seconds (files took 0.37223 seconds to load)
1 example, 0 failures

/home/vagrant/.rbenv/versions/2.2.3/bin/ruby -I/home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-support-3.4.0/lib:/home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.1/lib /home/vagrant/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-core-3.4.1/exe/rspec --pattern spec/test-001/\*_spec.rb failed

必要な環境変数null なことが原因のようです。

if ENV['ASK_SUDO_PASSWORD']
  begin
    require 'highline/import'
  rescue LoadError
    fail "highline is not available. Try Installing it."
  end
  set :sudo_password, ask("Enter sudo password:") { |q| q.echo = failse }
else
  set :sudo_password, ENV['SUDO_PASSWORD']
end

対象は ASK_SUDO_PASSWORDSUDO_PASSWORD の両方の様で、
上記 if 文の後に puts ENV['ASK_SUDO_PASSWORD'] と、 puts ENV['SUDO_PASSWORD'] したら結果は null 。
なので、highline がインストールされているかどうかのチェックもできず落ちていた模様。

(highline が未インストールだったのでパスワード認証の時に、標準入力にパスワード丸見えになってしまった・・)

なので if 文を削って強制的に sudo password を入力させることにしました。
(環境変数を追加するのはちょっと抵抗あるので)
また、ssh が成功した時に set :request_pty, true と書くように怒られたので、追記しています。

require 'serverspec'
require 'net/ssh'

set :backend, :ssh
set :request_pty, true

begin
  require 'highline/import'
rescue LoadError
  fail "highline is not available. Try installing it."
end

set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false }

host = ENV['TARGET_HOST']

options = Net::SSH::Config.for(host)

options[:user] ||= Etc.getlogin

set :host,        options[:host_name] || host
set :ssh_options, options

これでパスワードでも鍵認証でも実行できました。が、本当にこれでいいんだろうか。。。

apache で elb からのヘルチェックをログに記録しない

apache のバージョン

# httpd -V
Server version: Apache/2.2.15 (Unix)
Server built:   Aug 13 2013 17:29:28
Server's Module Magic Number: 20051115:25
Server loaded:  APR 1.3.9, APR-Util 1.3.9
Compiled using: APR 1.3.9, APR-Util 1.3.9
Architecture:   64-bit
Server MPM:     Prefork
  threaded:     no
    forked:     yes (variable process count)
Server compiled with....
 -D APACHE_MPM_DIR="server/mpm/prefork"
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_SYSVSEM_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=128
 -D HTTPD_ROOT="/etc/httpd"
 -D SUEXEC_BIN="/usr/sbin/suexec"
 -D DEFAULT_PIDLOG="run/httpd.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_LOCKFILE="logs/accept.lock"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="conf/mime.types"
 -D SERVER_CONFIG_FILE="conf/httpd.conf"

ELB-HealthChecker/1.0 からのアクセスログ

***.***.***.*** - - [11/Dec/2015:14:36:42 +0900] "GET /check.html HTTP/1.1" 200 20 "-" "ELB-HealthChecker/1.0" 373 ***.***.***.***

これをログに記録させないようにします。

config

<VirtualHost *:80>
    DocumentRoot /var/www/html
    ServerName hoge.com
 
    SetEnvIf Request_URI "^/check.html$" dontlog
    CustomLog /var/log/httpd/vhost-access.log combined_customized env=!dontlog
    ErrorLog /var/log/httpd/vhost-error.log
</VirtualHost>

CustomLogenv=!dontlog が肝ですね。 反映は reload で OK です。

PostgreSQL で UPDATE REPLACE する時にちょっと怖かった

こんな感じのテーブルがあるとします。

select count(*) from table ;

 count
------
 1000

a が含まれているレコードが 10 あるとします。

select count(*) from table where column like '%a%';

 count
------
    10

replace してみます。

BEGIN;

UPDATE 
    table 
SET
    column = REPLACE(column, 'a', 'b');

UPDATE 1000

a にマッチするのは 10 件のはずなのに、全件 UPDATE しています。

select count(*) from table where column like '%a%';

 count
------
     0

目的は果たせているようですが、他に影響でていたらいやなので慌てて rollback します。

select count(*) from table where column like '%a%';
 count
------
    10

結論としては普通に WHERE 句を指定していないから全件走査するようでした。(そりゃそうか)

BEGIN;

UPDATE
    table
SET
    column = REPLACE(column, 'a', 'b')
WHERE
    column like '%a%';

UPDATE 10

WHERE句なしの時は全カラム走査して、検索条件にマッチすれば更新するという挙動のようです。
UPDATE 1000 と表示されても実際に影響があったのは a が含まれている行のみでした。

Serverspec のセットアップ (rbenv / bundler)

Serverspec を使うために ruby の環境をセットアップします。 環境は CentOS 7 です。 O'REILLY を参考に進めていきます。

O'Reilly Japan - Serverspec

rbenv のインストール

rbenv は ruby のバージョン切替ができるツールです。 以下の手順でセットアップします。

$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile
$ rbenv --version
rbenv 0.4.0-192-g825de5d

インストールできる ruby のバージョン確認

$ rbenv install --list

ruby のインストール

今回は 2.2.3 をインストールします。

$ rbenv install 2.2.3
Downloading ruby-2.2.3.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/df795f2f99860745a416092a4004b016ccf77e8b82dec956b120f18bdc71edce
Installing ruby-2.2.3...
BUILD FAILED (CentOS Linux 7 using ruby-build 20151028-11-gbd22205)

Inspect or clean up the working tree at /tmp/ruby-build.20151119040352.4827
Results logged to /tmp/ruby-build.20151119040352.4827.log

Last 10 log lines:
The Ruby openssl extension was not compiled.
The Ruby readline extension was not compiled.
The Ruby zlib extension was not compiled.
ERROR: Ruby install aborted due to missing extensions
Try running `yum install -y openssl-devel readline-devel zlib-devel` to fetch missing dependencies.

Configure options used:
  --prefix=/home/vagrant/.rbenv/versions/2.2.3
  LDFLAGS=-L/home/vagrant/.rbenv/versions/2.2.3/lib
  CPPFLAGS=-I/home/vagrant/.rbenv/versions/2.2.3/include

エラーになってしまいました。パッケージが足りないようです。

$ sudo yum install -y openssl-devel readline-devel zlib-devel

もう一度インストールします。

$ rbenv install 2.2.3
Downloading ruby-2.2.3.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/df795f2f99860745a416092a4004b016ccf77e8b82dec956b120f18bdc71edce
Installing ruby-2.2.3...
Installed ruby-2.2.3 to /home/vagrant/.rbenv/versions/2.2.3

できました!
インストールした ruby を local 環境に設定します。

$ rbenv local 2.2.3
$ rbenv local
2.2.3
$ ruby --version
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]

bundler のインストール

Gemfile に記載したパッケージをインストールしてくれる bundler をインストールします。

$ gem install bundler

bundler を使って Serverspec をインストール

serverspec ディレクトリを作成して作業ディレクトリとします。 また、Gemfile を用意します。

$ pwd
/home/vagrant/serverspec
$ cat Gemfile
source 'https://rubygems.org'
gem 'serverspec'

インストールします。

$ bundle install
Fetching gem metadata from https://rubygems.org/.......
Fetching version metadata from https://rubygems.org/..
Resolving dependencies...
Installing diff-lcs 1.2.5
Installing multi_json 1.11.2
Installing net-ssh 2.9.2
Installing net-scp 1.2.1
Installing net-telnet 0.1.1
Installing rspec-support 3.4.0
Installing rspec-core 3.4.1
Installing rspec-expectations 3.4.0
Installing rspec-mocks 3.4.0
Installing rspec 3.4.0
Installing rspec-its 1.2.0
Installing sfl 2.2
Installing specinfra 2.44.2
Installing serverspec 2.24.3
Using bundler 1.10.6
Bundle complete! 1 Gemfile dependency, 15 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.

インストールできました。

サンプルの生成

postgresql の構築テストを行いたいので、target host name を test-db としてみます。

$ serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: n
Input target host name: test-db
 + spec/
 + spec/test-db/
 + spec/test-db/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec

spec の配下にホストごとのディレクトリが生成され、それぞれにテストコードがあるようです。
ホストごとでは大規模になったときに大変とどこかに書いてあった気がします。。
とりあえずはこれで進めてみようと思います。

一旦ここまで。

bash でよく使うやつ

桁指定で表示する

-bash ~$ i=1
-bash ~$ printf "%02d" $i
01-bash ~$

改行させるなら

-bash ~$  printf "%02d\n" $i
01

整数の加算

expr とさらばできる

-bash ~$  i=0
-bash ~$  ((i++))
-bash ~$  echo $i
1

C 言語っぽい for 文がかける

$  for (( i=0; i<3; i++)) do echo $i; done
0
1
2

正規表現でマッチングできる

in 句のように使える

-bash ~$  a=abc
-bash ~$  [[ $a =~ c ]] && echo "matched"
matched
-bash ~$  [[ $a =~ d ]] && echo "matched"
-bash ~$

リスト

# リストの作成
-bash ~$  i=("a" "b" "c")

# リストの展開
-bash ~$  echo ${i[0]}
a
-bash ~$  echo ${i[1]}
b
-bash ~$  echo ${i[2]}
c
-bash ~$  echo ${i[@]}
a b c
-bash ~$  echo ${i[*]}
a b c

# 要素の数
-bash ~$  echo ${#i[@]}
3

# 要素の追加
-bash ~$  i+=("d")
-bash ~$  echo ${i[@]}
a b c d
-bash ~$  echo ${#i[@]}
4

引数を順番に処理する

$  cat shift_test.sh
#!/bin/bash

echo $1
echo $#
shift 
echo $1
echo $#

$  sh shift_test.sh 1 2
1
2
2
1

引数がなくなるまでループする

$  cat shift_test.sh
#!/bin/bash

for (( i=0; $#>0; ))
do
    echo $1
    shift
done

echo "exit" && exit 0

$  sh shift_test.sh 1 2 3 4 5
1
2
3
4
5
exit

i=0 がないと shift_test.sh: line 3: syntax error: arithmetic expression required と怒られる

ユーザからの入力があるまでスクリプト実行を待機する

$  cat shift_test.sh
#!/bin/bash

for (( i=0; $#>0; ))
do
    echo $1
    shift
    if [ $# = 1 ]; then
        read -p "this is last loop. hit enter key to continue."
    fi
done

echo "exit" && exit 0

$  sh shift_test.sh  1 2 3 4 5
1
2
3
4
this is last loop. hit enter key to continue.
5
exit

中断するときは ctrl + c

関数名を表示する

$  cat func_test.sh
#!/bin/bash


function mytest()
{
    echo ${FUNCNAME}
}

mytest

$  sh func_test.sh
mytest

printf で文字列を置換する

$ a=test
$ printf '%s\n' $a
test

ファイルの行番号を表示する

以下の様な txt を用意して、

$  cat raw.txt
nikko nikko ni-
anata no heart ni
niko niko ni-
egao todokeru
yazawa niko niko-
nikoni- tte yonde
love niko

while ループでまわす

$ cat raw.txt | while read line; do printf '%2d %s\n' ${lineno} "${line}"; (( lineno++ )) ; done
 1 nikko nikko ni-
 2 anata no heart ni
 3 niko niko ni-
 4 egao todokeru
 5 yazawa niko niko-
 6 nikoni- tte yonde
 7 love niko

実行中のファイルの行番号を表示する

$  cat raw.sh
#!/bin/bash

echo ${LINENO}
echo ${LINENO}
echo ${LINENO}
echo ${LINENO}

function mytest()
{
    echo ${FUNCNAME} ${LINENO}
}

echo ${LINENO}
echo ${LINENO}

mytest

echo ${LINENO}
echo ${LINENO}

$  sh raw.sh
3
4
5
6
13
14
mytest 10
18
19

PostgreSQL を暖気する

postgresqllinux においてファイルキャッシュを信用するので、 データファイルを cat してあげると自然とデータがメモリに乗ります。

メモリ大容量時代が到来していますので、このようなニーズもあるのかなと。

postgresql の再起動後などに以下のようなコマンドを実行することで、全てのデータファイルをメモリに乗せることができます。

find "${PG_DATA}" -name "*" -exec cat {} \;

また、特定のテーブルを除いて暖気したい要求がありましたのでスクリプトを書いてみました。

#!/bin/bash
# warming_up_database.sh

set -e

PG_HOST="host"
PG_USER="user"
PG_PORT="5432"
PG_DATABASE="user_table"
PG_DATA="db_cluster_dir"
PG_CONN="-U ${PG_USER} -H ${PG_HOST} -p ${PG_PORT} -d ${PG_DATABASE}"

warming_up_database_with_exclude_file()
{
    # generate exclude string
    for (( i=1; $#>0; ))
    do
        target=$1
        # need to do without password, using .pgpass or pg_hba.conf
        data_file_name=$(/usr/pgsql-9.3/bin/oid2name ${PG_CONN} -t ${target} -q | awk {'print $1'})

        if [ -z "${data_file_name}" ]; then
            read -p "no datafile -> $1 . if you want continue, hit Enter key."
            break
        fi

        if [ $i = 1 ]; then
            exclude=("-name ${data_file_name}")
        else
            exclude+=(" -o -name ${data_file_name}")
        fi

        shift
    done

    # warming up !
    [ -n "${exclude[*]}" ] && time find ${PG_DATA} ${exclude[@]} -prune -o -print -exec cat {} \; > /dev/null 2>&1 \
    || echo "${FUNCNAME} failed." && exit 1
}

warming_up_database_all()
{
    time find "${PG_DATA}" -name "*" -exec cat {} \; > /dev/null 2>&1 \
    || echo "${FUNCNAME} failed." && exit 1
}

print_usage()
{
    echo "Usage:"
    echo "do all warming up => $0 --all"
    echo "do warming up with exclude file => $0 --exclude exclude_table_name"
    exit 1
}

case $1 in
    --help|*)
        print_usage
        ;;
    --exclude)
        if [ $# -lt 2 ]; then
            print_usage
            exit 1
        fi
        shift
        warming_up_database_with_exclude_file $@
        exit 0
        ;;
    --all)
        warming_up_database_all
        exit 0
        ;;
esac