patorashのブログ

方向性はまだない

aws-sdk-s3を使いつつ、timecopで時間をずらす

ストレージを伴うテストのためにminioを使うようにしようとしたのですが、設定をしただけではうまく動きませんでした。

timecopを使っているところで、aws-sdkがエラーを起こしました。timecopは時間を固定したり過去・未来に移動したりするライブラリです。

https://github.com/travisjeffery/timecop

エラーメッセージは以下のようなものでした。

Aws::S3::Errors::RequestTimeTooSkewed: The difference between the request time and the server's time is too large.

このメッセージでググるとわかるのが、アプリケーションサーバとS3のサーバ(minio)の間で時刻が離れすぎているから起きる例外でした。timecopで3年前とかに移動しているため、当然起きました…😩

解決方法

解決方法は、aws-sdk-s3から時刻の確認をするときだけ、Time.nowが現在の時刻を返せばいいというアプローチでした。

参考にしたのは以下のページです。

qiita.com

このページの、例外に備えよう、という見出しのところに書いてあります。

まんまでは動かない

しかし、記事自体が3年前のものなためか、aws-sdkのバージョンが変わり、まんまでは動かなくなっていました。

# aws-sdk-s3では動かないので注意
class Time
  class << self
    def now_wrap
      if (caller || []).first.match('aws-sdk')
        now_without_mock_time # return real time
      else
        mock_time || now_without_mock_time # return mock time or real time
      end
    end
    alias_method :now, :now_wrap
  end
end

修正

置き換えなければならない箇所の呼び出し元のパス情報が、aws-sdkではなく、aws-sigを含むものに変わっていたので、修正しました。

class Time
  class << self
    def now_wrap
      # p caller.first if caller.first.match('aws') # awsという文字列でなんとなくあたりを付けた
      # すると、aws-sigというものを発見
      if (caller || []).first.match('aws-sig') # aws-sdkをaws-sigに変更
        now_without_mock_time # return real time
      else
        mock_time || now_without_mock_time # return mock time or real time
      end
    end
    alias_method :now, :now_wrap
  end
end

修正後、テストを実行したところ、timecopを使っている箇所でもテストが成功しました👍

他に試したこと

最初は、エラーメッセージから、アプリケーションサーバとS3サーバの時刻の比較をしないで済むオプションがあるのではないか?と考え、S3クライアントにそれっぽいものを設定してみたのですが、結局ダメでした。

s3_client = Aws::S3::Client.new(
    access_key_id: Rails.application.credentials.aws_access_key_id,
    secret_access_key: Rails.application.credentials.aws_secret_access_key,
    region: Rails.application.credentials.aws_region,
    correct_clock_skew: false, # default true
    retry_mode: 'standard', # default 'legacy'
)

UNIXという考え方を読んだ

タイトルと本の見た目から、なんとなく難しそうだからと読んでなかったんじゃないかと思うんだけれど、お薦めの本として紹介されていたので読んでみた。感想としては、もっと若い時に読んでおくべき本だったなぁ~と思った。私、もうオッサンなので…。いや、オッサンだと学びが少ないとかではなくて、こういう考え方を若いうちから取り入れておけば、作ってきたアプリケーションやライブラリにもこの考え方を適用することができただろうから、柔軟なものを作ることができただろうになぁ…というやつです。

そして、この考え方を知っていれば、なぜこのライブラリはこんな作りになっているのか?とか、どうしてシェルだとパイプを使ってゴニョゴニョせにゃならんのか?という理由もわかる。わかると「便利だな」と思うんだけれど、わからないと「なんでこんなに組み合わせなければならないんだ!?」と思ってしまって苦手意識がついてしまうんじゃなかろうか?まぁ実際に苦手意識はかなりある。

効率よりも移植性を優先する

最も読んでよかったなと思ったところは「効率よりも移植性を優先する」というところだった。綺麗なコードや効率のよい処理のことはよく意識するけれど、移植性についてはあまり意識していなかったかもしれない。この章は示唆に富んでいて、耳が痛い。「プログラムを速くすることに時間をかけない」とか、「最も効率のよい方法は、ほとんどの場合移植性に欠ける」とか。

「来年になればもっと速いハードウェアが出てくるから、そこでもそのプログラムが実行できれば勝手に速くなる(意訳)」と書かれていた。本が書かれた当時はムーアの法則がめちゃくちゃ効いてた頃だから、それは納得なのだが、とはいえ現代においてもそれは言えるのかなと思う。クラウドにおいても、移植性が高ければ、スペックの高いインスタンスを立てればいいわけだ(いわゆる金の弾丸で解決するやつ)。

すべてのプログラムをフィルタにする

この章も、なるほどなと思った。もっと標準入出力(stdin, stdout)を使って、処理の結果をどうするかはユーザーに委ねたほうがいいなと思った。そうすれば、ユーザーの判断で様々な処理を組み合わせることができる。結果をファイルに出したいのであれば、 > hoge.txt とすればいいわけだし。

処理の成果物はなんとなくファイルにしたほうがいいよなぁ~と思っていた節がある。でもまぁそれをどうするかはユーザに委ねるべき。

pecoを使って処理をしたいときがあって、そのために標準出力に出す、ということはしていた。

フィルタにするためには、標準出力に出すことと、標準入力から受け取れるようにすること。こういうのが理解できると、gulpとかでstreamを使って処理を順々にしていくのはこの考え方なんやなってのがわかる。webpackのプラグインも同じ。だから、処理の流れの途中にやりたいことを差し込める。

他の学び

梃子の効果なども面白かった。他の考え方(移植性を重視とか)の部分と切り離せない部分はあるが、Cで書くよりもシェルスクリプトにすれば、大概の環境で動くし速度も速い。汎用的なものにしておけば、どこでも動くから再利用性が高まる。

90%の解を目指す、とか、部分の総和は全体よりも大きい、のところもウーンと唸りながら読んだ。疎結合のプログラムの組み合わせでアプリケーションを作るのと、単体のプログラムでアプリケーションを組んだ時の話では、マイクロサービスとモノシリックなサービスを連想した。マイクロサービスはUNIXの考え方に基づいたものなのだなと思った(マイクロサービスアーキテクチャに対する知識はほぼないけれど…)。

疎結合にしておけば、より効率のよいプログラム実装が組めれば、そこだけを差し替えることが可能になる。不要になれば外すこともできる。しかしでかいプログラムになっていると、外せない…。今も絶賛それで苦しんでいるところがある…。

感想

感想、この記事の最初に書いちゃってるんだけれど、この考え方を最初から知っていれば、なんでUNIXLinuxの環境はこういう書き方なんだろうか?とか、シェルわけわからん~みたいな苦手意識は薄まるのではないか?と思う。そして、UNIX的考え方を持っていれば、ライブラリを作るときにも汎用的に作ることができそう。この考え方はコンピュータで仕事をする上でベースとなっているし、普段利用しているものはこういう思想で作られているのだなとわかっておいたほうが絶対にいい。早い段階で知っておくべきもんだなと思うので、もっと布教していきたい。

UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

  • 作者:Mike Gancarz
  • 発売日: 2001/02/01
  • メディア: 単行本

OSS-DB Gold Ver.2.0不合格体験記

OSS-DB Gold Ver.2.0を受けてきたのですが、落ちました😭完全に準備不足でしたが、まぁまぁ問題集が解けるようになってきていたので、運が良ければ合格するんじゃないかな?と思っていたのですが、蓋を開けてみると56点という超微妙なレベルで(ちなみに合格ラインは70点)、あと5問は正解しないと合格しませんでした…。

なんで落ちたのかとか、そのあたりを考察しておいて、これから受験する人へのヒントや再受験するであろう自分自身の今後の試験勉強にも活かそうと思います。

OSS-DB Goldについて

OSS-DB Goldは、PostgreSQLの運用・設計・パフォーマンスチューニング・障害対応などに対して深い知識があるかどうかを問われる試験です。普通の開発者であれば、OSS-DB Silverを取得していれば十分かと思います。GoldはDBA(データベース管理者)の領域を問われる試験なので、実際に運用している等の経験がないと難しいです(落ちた私が言うのもなんですが…)

使用教材

主に使ったのは、OSS-DB Gold認定教材と、内部構造本と、Webのサンプル問題でした。あとはVMにインストールしたPostgreSQLで実際に設定したりSQLを実行したりして試したりなど…。

内部構造本はめちゃくちゃ勉強になるので4周くらい流し読みはしていました。まぁまぁ分かった気になっていただけに、落ちたのはショック…😥この本は最高なので私が悪いのです。

LPI-Japan OSS-DB Gold 認定教材 PostgreSQL 高度技術者育成テキスト

LPI-Japan OSS-DB Gold 認定教材 PostgreSQL 高度技術者育成テキスト

  • 作者:河原 翔
  • 発売日: 2014/10/27
  • メディア: オンデマンド (ペーパーバック)

逆に微妙なのが、OSS-DB Gold認定教材のほうで、これはまぁVer.1.0対応の本なので仕方がないのです…。因みに買ったのはVer.2.0が出る前で、Goldを受けようかなと本腰を入れようとしたらVer.2.0がアナウンスされてしまい(2019年4月頃だったはず)、今更1.0を受けてもあかんな…と思い、受験を断念してました。それでまぁいつか2.0の本が出るだろうと思っていたら、全然出ないのでとりあえず受けてみたという感じです。 この本は受験対策の講座のパワポ資料とその解説っぽい感じで、勉強にはなるのですが、同じことがページの上下で書いてあったりしてやや冗長…。しかし、問題集が付いているので、その点は評価できます!でも問題のバリエーションがやはりVer.1.0の頃のものなので少ないかなと思います。

早くVer.2.0対応版を出してください!😣

oss-db.jp

Webのサンプル問題は2周くらいやって、正答率7割くらいにまではなってたので、正答率7割で解説読んで勉強したから次は8割くらい取れるだろうからイケるに違いないという謎理論で受験に臨みました。

勉強期間

勉強期間は1か月半ですが、家だと全然集中して勉強できなかったりして(子供が寝ない…)、会社で勉強したり、試験前は妻が協力してくれて外出した先で勉強したり等していました。合計時間はわかりません…。

実際に受験してどうだったか

認定教材・内部構造本・Web問題でも見たことのないような問題が結構出てきて、全然わからないものがまぁまぁ出ました。とはいえ、25問くらいは見たことあるようなものばかりだったかと思います。

私が苦手なのが、答えを複数選ぶ問題なのですが、1つはわかるんだけれど、あと1つが2択まで絞り込めるんだけどわからない…というやつ。結局こういうのをいくつも落としてたんだろうなぁ…。

なぜ落ちたか?

正直なところ、問題集に頼り過ぎました(Web問題も含む)。問題集の内容を押さえていれば合格できるんじゃないかなと思い、問題集を中心にして勉強していました。まぁ本格的に勉強する時間があまりとれなかったからという言い訳をしておきます…。

どう勉強すればいいと思ったか?

今思い返すと、試験の出題範囲になっているキーワード類を元にPostgreSQLの公式ドキュメントを熟読するべきだったと思います。

oss-db.jp

全然見たことないキーワードの問題めっちゃ出た…と思っていたのですが、試験概要にはキーワード、ちゃんと書いてあるんですよ。そのあたりを放置して内部構造本でなんとなく全体的に押さえれば、なんとかなるんちゃうかなと高を括っていた面がありました。実際にどんな問題が出たかは書けないのですけど、これから受ける人はこの試験概要に書いてあるキーワードを、一通りちゃんと調べておいたほうがいいです。かなり多いけど!

落ちたけれど勉強してよかったこと

パフォーマンスチューニングや、実行計画の読み解き方等は結構わかるようになったかと思います。まぁ内部構造本を見ながらになるとは思いますけど…。しかし、実行計画の結合の仕方の種類について等はちゃんと勉強することができてよかったなと思います。どの結合が速いか、ソートのためにインデックスを貼るべきか等も、理由がわかるようになりました。期待した結合が選ばれてないようだったらWORKMEM増やしてみては?とか言えるようになりましたし。

再受験するか?

先ほど書いたように、試験範囲に含まれるキーワードを全部調べ直してから、再受験しようと思ってます。ただ、10月にDBスペシャリストを受ける予定なので、ひとまずは頭を切り替えてそれの対策に臨みます。その試験が終わった後にでも、再挑戦しようと思います。まぁ年末くらい?

コンテナのTimezoneは統一しよう

Railsアプリの開発中に、ちょっと物によっては時間がかかる処理があったので、ActiveJobに処理を移行させたのですが、perform_nowperform_laterでデータを保存した時のcreated_at等が9時間ずれる現象が発生しました。

結論から書くと…

結論は、rails serverしているコンテナのTimezoneは'Asia/Tokyo'になっていたのですが、ActiveJobを処理するresqueを動かしているコンテナのTimezoneが未設定でした。

未設定だと、UTCになるのですが、Railsアプリケーション的には

config.active_record.default_timezone = :local
config.time_zone = 'Tokyo'

となっていたので、Time.zone.nowなどをActiveJobで行ってもずれていることもなく…。データ保存後に再取得してcreated_atを表示しても正しい値を出しているのに、rails sをしているコンテナ側からアクセスすると時間が9時間前になっているという状況で本当に参りました。

対処

docker-compose.ymlのresqueのところの環境変数TZに値を設定しました。

services:
  # 略
  rails:
    # 略
    environment:
      TZ: "/usr/share/zoneinfo/Asia/Tokyo"
      # 略

  resque:
    # 略
    environment:
      TZ: "/usr/share/zoneinfo/Asia/Tokyo"
      # 略

PITRを試してみた

PITRとは、ポイント・イン・タイム・リカバリーと言って、指定した時間まで遡ってリカバリすることができます。pg_dumpだとそのときのスナップショットになるので、実際に稼働しているサービスが突然落ちた時などに、直前まで復旧することができませんが、PITRならば、直前まで復旧することができます!ただし、設定方法と復旧作業が複雑です。

本を読みながらやってみました。

基本方針

試してみたいだけなので、まずはpgbenchで作ったデータベースを対象にやってみることにしました。

  1. pg_start_backupを実行
  2. rsyncでデータベースクラスタをバックアップする
  3. pg_stop_backupを実行する
  4. データを沢山登録する
  5. 時間を空ける(ここまでリカバリさせるため)
  6. 更にデータを登録する
  7. dbを停止させる
  8. リカバリしてみる

postgresql.confを編集

まず、postgresql.confを編集しました。 archive_modeをonにすることで、WAL(Write Ahead Log)を作成させます。WALは、データ更新系のログで、DBに反映する前に作られるやつです。リカバリ時には、ベースバックアップからバックアップ時点まで戻し、そこからWALを再適用することで復旧させていきます。しかし、WALはある程度までしか保存されず、一定数になると削除されていきます。それを消される前にアーカイブするためにコマンドを指定するのが、archive_commandです。cpコマンドでWALアーカイブを保存します。本来は、データベースクラスタと異なるディスク上に保存しておくべきものですが、試したいだけなので同じディスクに置いてます。

archive_mode = on
archive_command = 'cp %p /var/lib/pgsql/archivedir/%f'

設定後、postgresqlを再起動させました。設定ファイルの再読み込みだけでもよかったかも…。

pg_ctl restart

PITRやっていく

まずデータ投入

最初にデータ投入しときましょう。pgbenchでやります。デフォルトの初期化です。pgbench_accountsに10万件登録されます。

pgbench -i benchdb

pg_start_backupを実行

次にバックアップの開始のラベルを作成します。これは結構時間がかかるときもあります。

psql -c "SELECT pg_start_backup(now()::text)"

ベースバックアップを保存する

ベースバックアップは、rsyncを使うのが確実だそうです。pg_walディレクトリ以下にWALが作成されるのですが、どうせ後でコピーするので、ベースバックアップからは外します。また、postmaster.pidがあるとpostgresqlが起動しない場合など、トラブルになるかもしれないので、これも除外します。

 rsync -av --delete --exclude=pg_wal/* --exclude=postmaster.pid $PGDATA/* /var/lib/pgsql/backup

pg_stop_backupを実行

ベースバックアップを取り終えたので、pg_stop_backupを実行します。

 psql -c "select pg_stop_backup()"

これでバックアップ体制は出来上がってるので、データを登録していっても大丈夫なはず。

データを登録する

pgbenchで作られたテーブルに、データを登録していきます。大量にデータを登録するときに便利なgenerate_seriesを使います。10万1件目から20万件登録します。

INSERT INTO pgbench_accounts (aid, bid, abalance, filler) SELECT i, 1, 0, '' FROM generate_series(100001, 200000) AS i;

これを行うことで、大量のWALが発生します。収まらなくなったWALはarchive_commandで指定したコマンドの通りにコピーされていってます。

時間を空ける

空白時間を作ります。この時間をが何時なのかを記憶しておきます。

-bash-4.2$ psql -c "select now()"
              now
-------------------------------
 2020-08-16 17:59:10.852806+00
(1 row)

戻す時間の目安を2020-08-16 18:00:00 UTCとすることにしました。2~3分待ちます。

更にデータを登録する

20万1件目から30万件登録します。これで更にWALが作られます。

INSERT INTO pgbench_accounts (aid, bid, abalance, filler) SELECT i, 1, 0, '' FROM generate_series(200001, 300000) AS i;

postgresqlを停止する

killで止めたほうが本当の障害ぽさがあるとは思いますが、普通に止めました。

pg_ctl stop

これでリカバリを実行する条件が整いました。

リカバリする

WALをコピーする

まずは、落ち着いてWALをコピーします。WALアーカイブに移動していないものを取り出します。

cp -r ${PGDATA}/pg_wal /tmp/

元のデータベースクラスタを別名で残す

次に、元のデータベースクラスタを別名で残しておきます。消してしまうと、リカバリが失敗したときに問題の調査ができなくなるので、残しましょう。

mv ${PGDATA} ${PGDATA}.temp

ベースバックアップから復元する

rsyncでベースバックアップをデータベースクラスタを復元します。

rsync -av /var/lib/pgsql/backup/* ${PGDATA}

これでベースバックアップ時点までは戻せますが、まだWALがないので後で登録されたものが存在しない状態になります。

WALを戻す

さきほどtmpにコピーしたWALを戻します。

cp /tmp/pg_wal/* ${PGDATA}/pg_wal/

recovery.confを作成

recovery.confを作っていきます。最小限の設定は、restore_commandでWALアーカイブを取ってくる命令を書いておくことです。

vi ${PGDATA}/recovery.conf

内容は以下の通り。まずは、DBを停止させる直前まで全部戻すほうを確認します。

restore_command = 'cp /var/lib/pgsql/archivedir/%f %p'

PostgreSQLを起動

これで、後はPostgreSQLを起動すると、recovery.confがあるため、リカバリーモードで起動し、WALをWALアーカイブから順々に適用していくようになります。

pg_ctl start

これだと、全てのWALが適用されるため、pgbench_accountsには、30万件のデータが復元されます。確認してみます。

benchdb=# select aid from pgbench_accounts order by aid desc limit 5;
  aid
--------
 300000
 299999
 299998
 299997
 299996
(5 rows)

バッチリです!

指定時刻まで復元する

ところで時間を空けておいたので、今度はそこまでの復元をやってみましょう。postgresqlを停止して、データベースクラスタを削除してまた復元作業をやります。一部はもうやってるので端折ります。

pg_ctl stop
rm -rf /var/lib/pgsql/data # もうdata.tempにバックアップしてあるので先ほど復元したものは消す
rsync -av /var/lib/pgsql/backup/* ${PGDATA} # もう一度ベースバックアップを持ってくる
cp /tmp/pg_wal/* ${PGDATA}/pg_wal/ # もう一度WALを持ってくる
vi ${PGDATA}/recovery.conf

recovery.confの内容は、以下の通り。recovery_target_timeで、適用させたいところまでの時刻を指定します。この時間までであれば、データは30万件ではなく、20万件のはずです。

restore_command = 'cp /var/lib/pgsql/archivedir/%f %p'
recovery_target_time = '2020-08-16 18:00:00 UTC'

PostgreSQLを起動します。

pg_ctl start

では、psqlで確認してみます。

benchdb=# select aid from pgbench_accounts order by aid desc limit 5;
  aid
--------
 200000
 199999
 199998
 199997
 199996
(5 rows)

20万が最後のaidになってました!完璧です!

まとめ

手順が少々複雑なので、とりあえず試しておいたほうがやっぱりいいなと思いました。 本当にPITRをやろうと思ったら、WALのディレクトリや、WALアーカイブディレクトリは外部ストレージに保存するべきだし、バックアップも多重化する必要があるので、もっと複雑になるかと思います。でも決まった時間に戻すことができるので、オペレーションミスでうっかりデータを削除してしまっても、PITRならば、戻せます。マネージドデータベースを使えない場合はやっておく価値あるでしょう。

pgbenchでチューニング前後でベンチマークを取ってみた

PC

  • Lenovo L380 yoga
  • OS: Windows 10 Pro
  • バージョン: 2004
  • CPU: Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz 1.99GHz (4コア8スレッド)
  • メモリ: 32GB

PostgreSQLサーバ

Hyper-VVM上に構築した。

pgbenchを実行した環境

PostgreSQLサーバを複製して作成。ネットワーク越しにpgbenchを実行するための環境。

チューニングした内容

PGTuneで生成したものを利用する。

pgtune.leopard.in.ua

ALTER SYSTEMコマンドのほうを利用する。これを使うと、postgresql.confを編集せずに、postgresql.auto.confが更新され、次回からこれが反映される。しかし、実行しただけでは反映されていないのでPostgreSQLの再起動が必要。

# DB Version: 11
# OS Type: linux
# DB Type: web
# Total Memory (RAM): 1 GB
# CPUs num: 1
# Connections num: 100
# Data Storage: ssd

ALTER SYSTEM SET
 max_connections = '100';
ALTER SYSTEM SET
 shared_buffers = '256MB';
ALTER SYSTEM SET
 effective_cache_size = '768MB';
ALTER SYSTEM SET
 maintenance_work_mem = '64MB';
ALTER SYSTEM SET
 checkpoint_completion_target = '0.7';
ALTER SYSTEM SET
 wal_buffers = '7864kB';
ALTER SYSTEM SET
 default_statistics_target = '100';
ALTER SYSTEM SET
 random_page_cost = '1.1';
ALTER SYSTEM SET
 effective_io_concurrency = '200';
ALTER SYSTEM SET
 work_mem = '1310kB';
ALTER SYSTEM SET
 min_wal_size = '1GB';
ALTER SYSTEM SET
 max_wal_size = '4GB';
項目 チューニング前 チューニング後
max_connections 100 100
shared_buffers 128MB 256MB
effective_cache_size 4GB 768MB
maintenance_work_mem 64MB 64MB
checkpoint_completion_target 0.5 0.7
wal_buffers 4MB 7864kB
default_statistics_target 100 100
random_page_cost 4 1.1
effective_io_concurrency 1 200
work_mem 4MB 1310kB
min_wal_size 80MB 1GB
max_wal_size 1GB 4GB
max_worker_processes 8 8
max_parallel_workers_per_gather 2 2
max_parallel_workers 8 8

実行した条件

pgbench -h 192.168.100.2 -p 5432 -U postgres -c 30 -t 1000 -N benchdb

クライアント数を30、トランザクション数を1,000とした。また、実際にWebアプリを想定して頻繁に更新は起きないだろうということで、-Nオプションを追加して実施した。

結果発表!

単位はTPS(including connections establishing)です。

回数 チューニング前 チューニング後
1 806 813
2 775 820
3 789 846
4 751 806
5 753 834
平均 774 823

平均すると50TPSくらい性能が向上した。823 / 774 * 100 = 106.33...なので、6.3%くらいの性能向上だった。

考察

一応、チューニングによって性能向上はしたけれど、劇的という感じには思えなかった。やはりCPUコア数やメモリの量を少なくしているのが響いているのか? CPUコア数を増やしたり、メモリ量を調整してまたチャレンジしたい。

あとpgbenchの他のオプションについてイマイチまだ理解できていないので使ってない。PostgreSQLのslackとかで質問してみるかなぁ…。(Slackをあまり使ってないのでなんとなく苦手意識がある)

もう一つ仮想環境を作って、そこからpgbenchを実行してみた

Hyper-VAmazon Linux2を立ち上げていて、前回はそこでpgbenchを実行してみていた。

patorash.hatenablog.com

pgbenchでtwitterで検索していたら、pgbenchを実行するホストとPostgreSQLが動いているホストが同じ場合、pgbenchにCPUを使われるのでもっと正確なベンチマークが取れないという話を見て、確かになぁ…と思った次第で、ちょっととりあえずもう一台VMを作って、そこから実行してみようと思った。

Hyper-Vの仮想環境を複製する

複製するのは、特にこだわりを持たなければ簡単だった。こだわりというのは、仮想環境のハードドライブをどこに置くか、というところだ。そこさえ気にしなければ、簡単。

複製の準備

まず、複製対象のVMをオフにします。

f:id:patorash:20200813024059p:plain
VMをオフにする。

次に、VMのイメージをエクスポートします。場所は適当に。私はOneDriveにバックアップされると面倒なので、ホームディレクトリ直下に置いた。

f:id:patorash:20200813024703p:plain

これで準備は完了です。

複製する

複製は、先ほどエクスポートしたイメージをインポートするだけですが、インポートウィザードで多少修正します。まずはインポートを選択します。

f:id:patorash:20200813024841p:plain

先ほどエクスポートしたイメージのディレクトリを選択します。

f:id:patorash:20200813025101p:plain

どんどん進めていき、インポートの種類のところで、「仮想マシンをコピーする(新しい一意なIDを作成する)」を選択します。

f:id:patorash:20200813025258p:plain

他は変更なく進めて、最後の「仮想ハードディスクを保存するフォルダを選択します」のところで、デフォルトと違うフォルダを選択します。元のままだと、複製元のVMと同じ名前のハードディスク名になってしまうため、既にあるので作れないと怒られてしまいます。このフォルダに置きたい!というこだわりがなければ、別名のフォルダを作ってしまい、そこに置きましょう。

f:id:patorash:20200813025822p:plain

あとは、確認画面が出るので、問題なければインポートを実行します。VMのインポートなので、そこそこ時間はかかります。

コピーが終わっていれば、終了です。

両方とも立ち上げてみましょう。複製元のVMに固定IPを振っていなければ、問題なく動くかと思いますが、振っている場合は、複製先のVMは他のIPを振りましょう。

もう一台のVMからPostgreSQLに接続する

複製元をプライマリ、複製先をセカンダリと呼ぶことにします。(まだレプリケーションとかしてないけど)

postgresql.confとpg_hba.confを修正していないため、セカンダリからプライマリに接続することができません。以下のようにエラーになります。

[ec2-user@postgres ~]$ psql -h 172.23.59.9 -p 5432 -U postgres benchdb
psql: could not connect to server: Connection refused
        Is the server running on host "172.23.59.9" and accepting
        TCP/IP connections on port 5432?

postgresql.confを修正して、TCP/IP接続可能にする

プライマリ側のpostgresql.confを修正します。場所は/var/lib/pgsql/data/postgresql.confでした。listen_addressesの行のコメントアウトを外し、全てのIPを許可するために*に、portの行のコメントアウトを外しました。

#------------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#------------------------------------------------------------------------------

# - Connection Settings -

listen_addresses = '*'          # what IP address(es) to listen on;
                                        # comma-separated list of addresses;
                                        # defaults to 'localhost'; use '*' for all
                                        # (change requires restart)
port = 5432                             # (change requires restart)

change requires restartと書いてあるので、postgresqlを再起動します。

sudo systemctl restart postgresql

pg_hba.confを修正する

ホストベース認証の設定ファイルであるpg_hba.confを修正します。デフォルトだとlocalhostしか許可していません。 今回は、同一サブネットからの接続は全てtrustにしました(勉強用なので!) 最後の行を追加しています。

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
host    all             all             ::1/128                 trust
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     trust
host    replication     all             127.0.0.1/32            trust
host    replication     all             ::1/128                 trust
host    all             all             172.23.48.0/20          trust

ちなみにサブネットマスクの計算は以下のサイトを使いました。

note.cman.jp

pg_hba.confの修正に関しては、再起動は不要なので設定を再読み込みさせます。

pg_ctl reload

接続してみます。

[ec2-user@postgres ~]$ psql -h 172.23.59.9 -p 5432 -U postgres benchdb
psql (11.5)
Type "help" for help.

benchdb=#

できました!

セカンダリからpgbenchを実行する

では、実行してみましょう。

[ec2-user@postgres ~]$ pgbench -h 172.23.59.9 -p 5432 -U postgres benchdb
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 1
number of threads: 1
number of transactions per client: 10
number of transactions actually processed: 10/10
latency average = 6.123 ms
tps = 163.323153 (including connections establishing)
tps = 179.586074 (excluding connections establishing)

特にまだチューニングもしていないし、pgbench側のオプションも付けていないのですが、TPSが163という結果になりました。

ちなみにプライマリで実行してみた結果はというと…

-bash-4.2$ pgbench -h 127.0.0.1 -p 5432 -U postgres benchdb
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 1
query mode: simple
number of clients: 1
number of threads: 1
number of transactions per client: 10
number of transactions actually processed: 10/10
latency average = 3.132 ms
tps = 319.238007 (including connections establishing)
tps = 388.040694 (excluding connections establishing)

TPSが319なので、流石に違うホストからのベンチマークよりは速いのかなというところ。とはいえ、これがCPUを食い合ってる状態なので、微妙!

まだ全然チューニングもしていない状態だし、pgbenchのオプションの最適な値もわかっていないけれど、とりあえず今日はここまで。眠い。