/tmp/hoge

雑な記録

PostgreSQLをレプリ構成で立てるまでの記録

あらすじ

こんな便利な時代に自前でPostgreSQLを立てる必要が出た。悲しきオンプレのさだめよ。 当方PostgreSQL素人なのでいちから構築手順をメモしていく。

要件

ミドルウェアが利用するPostgreSQLでそんなにパフォーマンスも容量もいらない。 でも、ちゃんと冗長化していて欲しいし、障害時にはフェイルオーバして欲しい。そんな感じ。

全体像

とりあえずPostgreSQLの同期レプリケーションでアクティブ/スタンバイの構成を作りたい。

使用バージョン

インストール手順

yumレポジトリをインストールするrpmパッケージの入手

ここにある。

PostgreSQL RPM Repository (with Yum)

以下のコマンドにてpostgresqlのレポジトリがyum.repoに追加される。

yum install -y https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm

postgresql-serverのインストール

そして実際のpostgresqlサーバのインストール。

yum install -y postgresql96-server

データベースディレクトリの初期化

サービスを起動するまえにデータベースディレクトリが必要なそうな。データベースあるある。

/usr/pgsql-9.6/bin/postgresql96-setup initdb

/var/lib/pgsql/9.6/data/を覗くと色々ファイルができていた。

サービス起動

初期化後にサービスを立ち上げるとpostgresqlが立ちあがる。

# systemctl start postgresql-9.6
# systemctl status postgresql-9.6
● postgresql-9.6.service - PostgreSQL 9.6 database server
   Loaded: loaded (/usr/lib/systemd/system/postgresql-9.6.service; disabled; vendor preset: disabled)
   Active: active (running) since 水 2017-10-04 04:40:31 UTC; 5s ago
  Process: 28155 ExecStartPre=/usr/pgsql-9.6/bin/postgresql96-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
 Main PID: 28161 (postmaster)
   CGroup: /system.slice/postgresql-9.6.service
           ├─28161 /usr/pgsql-9.6/bin/postmaster -D /var/lib/pgsql/9.6/data/
           ├─28163 postgres: logger process   
           ├─28165 postgres: checkpointer process   
           ├─28166 postgres: writer process   
           ├─28167 postgres: wal writer process   
           ├─28168 postgres: autovacuum launcher process   
           └─28169 postgres: stats collector process   

10月 04 04:40:31 test-pg-1 systemd[1]: Starting PostgreSQL 9.6 database server...
10月 04 04:40:31 test-pg-1 postmaster[28161]: < 2017-10-04 04:40:31.246 UTC > LOG:  redirecting log output to logging collector process
10月 04 04:40:31 test-pg-1 postmaster[28161]: < 2017-10-04 04:40:31.246 UTC > HINT:  Future log output will appear in directory "pg_log".
10月 04 04:40:31 test-pg-1 systemd[1]: Started PostgreSQL 9.6 database server.

おKおK

各種設定

一旦、Postgresqlサーバを止めて各種設定を確定させて行く。前述したとおりパフォーマンス要件は厳しく無いので、セキュリティやloggingの設定を主に注意してやっていく。

が、ここで問題が発生して、どうもpostgresqlの設定ファイルはinitdbで作成するディレクトリの/var/lib/pgsql/9.6/data/の下にできるそうな。 個人的には設定ファイルは/etc/pgsqlの下におかれていて欲しいなぁ。。。と思っていたと所、以下のドキュメントを発見した。

PostgreSQL: Documentation: 9.6: File Locations

これを読む限り、config_fileパラメータを起動時に渡してやれば諸々上手くいくそうだ。systemdのユニットファイルをいじれば良さそう。

以下は個々のconfigファイルについて

接続周り

listen_addresses = '*'

全部のIPで待ち受けますよ的なおなじみのやつ。

WAL/レプリケーション

wal_level                 = replica
max_wal_senders           = 5
max_replication_slots     = 2
track_commit_timestamp    = on
wal_lebel
max_wal_senders
  • walをレプリスレーブに送信するプロセス数
  • 再接続に備えて、実際のスレーブ数より少し多めにすると良いらしい
max_replication_slots
  • レプリスレーブがどこまでwalを読んだか?という情報を管理するためのスロット数
  • レプリ台数にする必要がある
  • また、一度上げてから下げようとするとエラーになるらしい
  • 同期レプリの場合いらない気もするけども、とりあえず設定

マスタ固有

synchronous_standby_names = '1 (pg2, pg3)'

同期レプリするための設定なので、レプリ先がいない状態で反映すると動かなくなる

スレーブ固有

hot_standby = on

スタンバイ側でクエリを許可

ロギング設定

log_destination = 'stderr'
logging_collector = on
log_directory = '/var/log/pg_log'
log_filename = 'postgresql-%a.log'
log_truncate_on_rotation = on
log_rotation_age = 1d
log_line_prefix = '< %m > '
log_timezone = 'UTC'

ほとんどデフォルトでコメントアウトされていなかったパラメータそのまま。 log_directoryだけ、/var/log下に書き込まれるとうにへんこうした、、、がlog_destinationが'stderr'なので本当に吐かれるかは疑問。 log_destinationをstderrのままにしているのはsystemdのjournaldにログを監視してもらうため。

クライアント接続設定

datestyle = 'iso, mdy'
timezone = 'UTC'
lc_messages = 'en_US.UTF-8'
lc_monetary = 'en_US.UTF-8'
lc_numeric = 'en_US.UTF-8'
lc_time = 'en_US.UTF-8'
default_text_search_config = 'pg_catalog.english'

デフォルトでコメントアウトされていなかったパラメータそのまま。

パフォーマンス設定

何度も言うが、パフォーマンス要件は厳しく無い。 基本的なパフォーマンスの設定だけ抑えておけると良いのだが、何か良い記事は無いかとあさってるといかのページにたどり着いた。

pgtune.leopard.in.ua

なにやらHW状態に合わせて設定を生成してくれるらしい。これを使って実運用で問題出たら都度チューニングしていこう。 検証環境に合わせて生成すると以下の感じになった。

max_connections              = 1024
shared_buffers               = 1GB
effective_cache_size         = 3GB
work_mem                     = 1MB
maintenance_work_mem         = 256MB
min_wal_size                 = 1GB
max_wal_size                 = 2GB
checkpoint_completion_target = 0.7
wal_buffers                  = 16MB
default_statistics_target    = 100

ディレクトリとパス設定

include_dir = 'conf.d'

コンフィグファイルは分割してincludeできるみたいなので、よしなにincludeさせる。

認証ファイル設定

ホスト認証周りの設定はpg_hba.confで行うようだ。 とりあえず、社内DCにいるサーバにはhogeユーザでhogeデータベースにパスワード指定でログイン可能なようにゆるく許可を与える。 あと検証なので、レプリもゆるく開けている。

# TYPE  DATABASE        USER            ADDRESS                 METHOD
local   all             all                                     peer
host    sameuser        hoge            192.168.0.0/16          md5
host    replication     postgres        192.168.0.0/16          trust

一瞬、このファイルを設定変更した際、リスタートせんとアカンのん??と思ったが、SIGHUPでリロードしてくれるらしい。ですよねー。良かった。

systemdの設定

個別の設定ファイルをロードさせるためにsystemdのUnitファイルを変更する。

[Unit]
Description=PostgreSQL 9.6 database server
After=syslog.target
After=network.target

[Service]
Type=notify

User=postgres
Group=postgres
PermissionsStartOnly=true

Environment=PGDATA=/var/lib/pgsql/9.6/data/
Environment=PGCONFIG=/etc/pgsql/

OOMScoreAdjust=-1000

ExecStartPre=-/usr/pgsql-9.6/bin/postgresql96-setup initdb
ExecStartPre=/usr/pgsql-9.6/bin/postgresql96-check-db-dir ${PGDATA}
ExecStart=/usr/pgsql-9.6/bin/postmaster \
          -c config_file=${PGCONFIG}/postgresql.conf \
          -c hba_file=${PGCONFIG}/pg_hba.conf \
          -c ident_file=${PGCONFIG}/pg_ident.conf \
          -D ${PGDATA}
ExecReload=/bin/kill -HUP $MAINPID
KillMode=mixed
KillSignal=SIGINT
 
TimeoutSec=300

[Install]
WantedBy=multi-user.target

ExecStartPreでinitdbを行うように変更した。あと、configを読む箇所を変えたいので起動時パラメータでコンフィグのロード先を指定している。

最終的なファイル構成

こんな感じになった。

  • /etc/systemd/system/postgresql.service
[Unit]
Description=PostgreSQL 9.6 database server
After=syslog.target
After=network.target

[Service]
Type=notify

User=postgres
Group=postgres
PermissionsStartOnly=true

Environment=PGDATA=/var/lib/pgsql/9.6/data/
Environment=PGCONFIG=/etc/pgsql/

OOMScoreAdjust=-1000

ExecStartPre=-/usr/pgsql-9.6/bin/postgresql96-setup initdb
ExecStartPre=/usr/pgsql-9.6/bin/postgresql96-check-db-dir ${PGDATA}
ExecStart=/usr/pgsql-9.6/bin/postmaster \
          -c config_file=${PGCONFIG}/postgresql.conf \
          -c hba_file=${PGCONFIG}/pg_hba.conf \
          -c ident_file=${PGCONFIG}/pg_ident.conf \
          -D ${PGDATA}
ExecReload=/bin/kill -HUP $MAINPID
KillMode=mixed
KillSignal=SIGINT
 
TimeoutSec=300

[Install]
WantedBy=multi-user.target
  • /etc/pgsql/postgresql.conf
listen_addresses = '*'
include_dir      = 'conf.d'
  • /etc/pgsql/conf.d/replication.conf
wal_level                 = replica
max_wal_senders           = 5
max_replication_slots     = 2
track_commit_timestamp    = on
  • /etc/pgsql/conf.d/master.conf
synchronous_standby_names = '1 (pg2, pg3)'
  • /etc/pgsql/conf.d/slave.conf
hot_standby = on
  • /etc/pgsql/conf.d/logging.conf
log_destination = 'stderr'
logging_collector = on
log_directory = '/var/log/pg_log'
log_filename = 'postgresql-%a.log'
log_truncate_on_rotation = on
log_rotation_age = 1d
log_line_prefix = '< %m > '
log_timezone = 'UTC'
  • /etc/pgsql/conf.d/client.conf
datestyle = 'iso, mdy'
timezone = 'UTC'
lc_messages = 'en_US.UTF-8'
lc_monetary = 'en_US.UTF-8'
lc_numeric = 'en_US.UTF-8'
lc_time = 'en_US.UTF-8'
default_text_search_config = 'pg_catalog.english'
  • /etc/pgsql/conf.d/pgtune.conf
max_connections              = 1024
shared_buffers               = 1GB
effective_cache_size         = 3GB
work_mem                     = 1MB
maintenance_work_mem         = 256MB
min_wal_size                 = 1GB
max_wal_size                 = 2GB
checkpoint_completion_target = 0.7
wal_buffers                  = 16MB
default_statistics_target    = 100
  • /etc/pgsql/pg_hba.conf
# TYPE  DATABASE        USER            ADDRESS                 METHOD
local   all             all                                     peer
host    sameuser        hoge            192.168.0.0/16          md5
host    replication     postgres        192.168.0.0/16          trust
  • /etc/pgsql/pg_ident.conf



使わないので空ファイル

再起動

ファイル巻き終わったらsystemdを更新して既存のデータディレクトリも消して再起動を行う。

systemctl daemon-reload
rm -rf /var/lib/pgsql/9.6/data/*
systemctl restart postgresql

この状態だと起動はするけど同期レプリケーションがいないので書き込みができない。 なのでスレイブを作成してきどうさせる必要がある。

レプリケーションスレイブの作成

同じ設定をアクティブと同様に巻く。 撒いた後はbg_backupコマンドを使ってSlave用のデータディレクトリを構成する。

su -c "pg_basebackup -R -D /var/lib/pgsql/9.6/data/ -h 192.168.0.1 -p 5432 -U postgres" postgres

バックアップを取った後、recovery.confを編集する必要がある。 色々調べたけど、このファイルはDataディレクトリにないといけないみたいだ。。。皆デプロイどうしてんだろう?

standby_mode = 'on'
primary_conninfo = 'user=postgres host=192.168.0.1 port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres application_name=pg2'

application_name=pg2を追加する必要がある。

設定が終わったら起動させる。

systemctl start postgresql.service

アクティブのレプリケーション状態の確認

su -c 'psql -c "SELECT * FROM pg_stat_replication" -x' postgres
-[ RECORD 1 ]----+------------------------------
pid              | 30960
usesysid         | 10
usename          | postgres
application_name | pg2
client_addr      | 192.168.0.2
client_hostname  | 
client_port      | 51696
backend_start    | 2017-10-05 04:58:13.243582+00
backend_xmin     | 
state            | streaming
sent_location    | 0/7000060
write_location   | 0/7000060
flush_location   | 0/7000060
replay_location  | 0/7000060
sync_priority    | 1
sync_state       | sync

同期レプリされてるっぽいぞ。 おkおk。

終わりに

postgresqlの設定をイジってレプリが組めるところまで実施した。 本番デプロイに向けて、後はフェイルオーバーをコントロールするサーバと同期レプリカの候補サーバがもう一台あったほうが良いだろう。

kyoto tycoonのDocker Imageを作る

あらすじ

私の所属する会社は未だバリバリにkyoto tycoon(kt)というkvsを使っている。 このktをいっちょDocker化したるか!と思ったら色々ハマったので備忘兼ねて記録を残す。

すでにDockernizeしてる人がいた

ネットを漁ると、すでにDocker化している方がいた。

qiita.com

先人の知恵に感謝しつつも、上記のDockerfileを覗くと、alpineのバージョンが3.4で固定されいる。 怪訝に思いながらも、それ以外に、会社で使用するkyototycoonはluaスクリプトによる拡張を有効化する必要があっため、大部分を参考にしつつ自前でDockerfile書くことにした。 ついでにalpineも最新の3.5で作ることにした。 自前で書く、といってもluaのパッケージを追加して、make前のconfigureで--enable-luaオプションを付加するだけだろうと思っていた。

alpine3.5でビルドが通らない問題

いざDockerfileを書いてbuildしようしたが、alpine3.5だと以下のようなエラーが出てmakeでこけてしまった。

g++ -c -I. -I/usr/local/include -I/usr/local/include -DNDEBUG -D_GNU_SOURCE=1 -D_FILE_OFFSET_BITS=64 -D_REENTRANT -D__EXTENSIONS__ -D_MYZLIB -D_MYGCCATOMIC -D_KC_PREFIX="\"/usr/local\"" -D_KC_INCLUDEDIR="\"/usr/local/include\"" -D_KC_LIBDIR="\"/usr/local/lib\"" -D_KC_BINDIR="\"/usr/local/bin\"" -D_KC_LIBEXECDIR="\"/usr/local/libexec\"" -D_KC_APPINC="\"-I/usr/local/include\"" -D_KC_APPLIBS="\"-L/usr/local/lib -lkyotocabinet -lz -lstdc++ -lrt -lpthread -lm -lc \"" -std=c++11 -march=native -m64 -g -O2 -Wall -fPIC -fsigned-char -g0 -O2 -Wno-unused-but-set-variable -Wno-unused-but-set-parameter kcdbext.cc
In file included from kcdbext.cc:16:0:
kcdbext.h: In member function 'char* kyotocabinet::IndexDB::get(const char*, size_t, size_t*)':
kcdbext.h:1281:14: error: cannot convert 'bool' to 'char*' in return
       return false;
              ^~~~~
make: *** [Makefile:76: kcdbext.o] Error 1

luaの部分がおかしいのかと思いきや、alpine3.4だと上手くいく。 コンパイルでエラーが出ているので、alpine3.4と3.5のg++とgccのバージョンを確認すると、alpine3.4のほうがgccのバージョンが5系でalpine3.5のほうがgccが6系となっていた。 デフォルトのコンパイルオプションとかが変わったのかな?とか思いながら、ググっていると以下の記事にたどり着いた。

mag.osdn.jp

どうやらgccの6系は標準で利用するc++の規格が変わってしまったようだ。 ktは古いプログラムなので、上手くコンパイルできないのであろう。

解決策

というわけでDockerfile内でsedを使って強引にMakefileを書き換えて、c++98を使ってbuildするようにすれば、makeが通るようになった。

sed -i -e "s/CXXFLAGS =/CXXFLAGS = -std=c++98/" ./Makefile && \
sed -i -e "s/CFLAGS =/CFLAGS = -std=c++98/" ./Makefile
余談

色々なDockefileを見てるとsedで強引にファイルを書き換えているものを良く見るが、個人的にこれはイマイチな気がしている。 しかし、他にいい方法も思いつかないので、安易にsedっている。 Dockerfileでshell芸よろしくゴリゴリ書くの、みんな満足しているのだろうか?? 個人的には何かキッティングツールが欲しいと思うのだが、いまいちPackerとかがDockerのビルドに使われてる感を感じないんだなぁ。

まとめ

  • 古いミドルウェアを使い続けるのはやめましょう。
  • どうしてもbuildをする必要のあるときは、gccのバージョンと利用するcの規格に注意する。

今回作ったDockerImageは以下にアップした。

https://hub.docker.com/r/kanga333/kyototycoon/