Elixir/Phoenix: Distillery を利用した tarball 作成と配備

『Elixir/Phoenix 初級④: バリデーション』のサンプルアプリ NanoPlanner を本番環境(Ubuntu 18.04 Server LTS)にセットアップして、公開する手順を紹介します。

デプロイメントツールとしてDistilleryを使用します。

前提条件

  • Ubuntu 18.04 Server LTS
  • kuroda ユーザー (sudo 権限あり)
  • phoenix ユーザー (sudo 権限あり)

Erlang と Elixir のインストール

kuroda ユーザーで Ubuntu Server にログインして、次のコマンド群を順に実行。

$ wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
$ sudo dpkg -i erlang-solutions_1.0_all.deb
$ sudo apt-get update
$ sudo apt-get -y install esl-erlang
$ sudo apt-get -y install elixir
$ elixir --version

次のような結果が出ることを確認する。

Erlang/OTP 21 [erts-10.1.3] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [hipe]

Elixir 1.7.4 (compiled with Erlang/OTP 20)

Node.js のインストール

引き続き、kuroda ユーザーとして次のコマンドを実行。

$ sudo apt install nodejs npm

PostgreSQL のインストールとセットアップ

引き続き、kuroda ユーザーとして次のコマンド群を順に実行。

$ sudo apt-get -y install postgresql
$ sudo -u postgres createuser -d phoenix -P

パスワードの入力を求められるので、phoenix と2回入力。

さらに次のコマンドを実行。

$ sudo -u postgres createdb --owner phoenix nano_planner_prod

Nginx のインストールと設定

kuroda ユーザーとして次のコマンドを実行。

$ sudo apt-get -y install nginx

テキストエディタ/etc/nginx/sites-available ディレクトリに新規ファイル nano_planner を作成

upstream phoenix {
  server 127.0.0.1:4000;
}

server {
  listen 80;
  server_name example.com;

  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $http_host;
  proxy_set_header X-Cluster-Client-Ip $remote_addr;

  location / {
    proxy_pass  http://phoenix;
    allow all;
  }
}

ただし、example.com の部分は、Ubuntu Server に割り当てられた実際のホスト名で置き換える。

kuroda ユーザーとして次のコマンド群を順に実行。

$ cd /etc/nginx/sites-enabled
$ sudo ln -s ../sites-available/nano_planner .
$ sudo systemctl reload nginx

NanoPlanner のセットアップ

ここから先は、phoenix ユーザーで Ubuntu Server にログインして、次のコマンド群を順に実行。

ソースコードの取得から npm run deploy まで

$ git clone -b master4-deploy https://github.com/oiax/nano_planner.git
$ cd nano_planner
$ cp config/skel/prod.secret.exs config/
$ cd assets
$ npm install
$ npm run deploy
$ cd ..

tarballの作成

$ mix deps.get --only prod
$ MIX_ENV=prod mix compile
$ MIX_ENV=prod mix phx.digest
$ MIX_ENV=prod mix release

リリースの配備

$ mkdir -p ~/app/releases/0.1.0
$ cd _build/prod/rel/nano_planner/releases/0.1.0
$ cp nano_planner.tar.gz ~/app/releases/0.1.0
$ cd ~/app/releases/0.1.0
$ tar -xf nano_planner.tar.gz -C ~/app

データベースの初期化

$ cd ~/nano_planner
$ MIX_ENV=prod mix ecto.migrate
$ MIX_ENV=prod mix run priv/repo/seeds.exs

NanoPlanner の起動

$ cd ~/app
$ bin/nano_planner start

4000番ポートで待ち受けるプロセスの存在を確認。

$ lsof -i:4000

次のような結果が出ることを確認する。

COMMAND    PID    USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
beam.smp 16931 phoenix   15u  IPv6  52028      0t0  TCP *:4000 (LISTEN)

以上で作業完了です。ブラウザで http://example.com にアクセスすれば、NanoPlanner の画面が出てくるはずです。ただし、example.com の部分は、Ubuntu Server に割り当てられた実際のホスト名で読み替えてください。

GCP/Ubuntu: Cloud SQL Proxy のセットアップ(追記あり)

先々月にGCP: 自動スケーリング時における Google Cloud SQL ホワイトリストの管理という記事を書きました。その続きです。

GCPVMインスタンスからCloud SQLインスタンスへ接続するのにCloud SQL Proxyを使えばうまく行くんじゃないか、という考察で終わっていました。

結論から言えばその通りでした。


Ubuntu 18.04(64bit版)におけるセットアップ手順の概要は、次の通りです。

  1. VMインスタンスの設定で「すべての Cloud API に完全アクセス権を許可」を選ぶ。
  2. VMインスタンスsshでログインして、cloud_sql_proxy をインストールする。
  3. cloud_sql_proxy のためのSystemd起動スクリプトを設置する。

これで、Cloud SQL インスタンスPostgreSQL なら localhost:5432 で接続できるようになります。とても簡単です。

なお、GCPドキュメントには「下位互換性のない方法で変更される可能性があり、 は、SLA または非推奨ポリシーの対象ではありません。」と書いてあります(2018年9月9日現在)。採用は自己責任でお願いします。


以下、詳しい説明です。

VMインスタンスの設定

VMインスタンスの「アクセス範囲」に関する設定を「デフォルトのアクセスを許可」から「すべての Cloud API に完全アクセス権を許可」に変更します。

cloud_sql_proxy のインストール

VMインスタンスsshでログインして、cloud_sql_proxy をインストールします。

$ wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O cloud_sql_proxy
$ sudo chown root:root cloud_sql_proxy
$ sudo chmod +x cloud_sql_proxy
$ sudo mv cloud_sql_proxy /usr/local/bin

起動スクリプトの設置

テキストエディタで新規ファイル /etc/systemd/system/cloud-sql-proxy.service を以下の内容で作成します。

[Unit]
Description=Cloud SQL Proxy
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/cloud_sql_proxy -dir=sqlproxy -instances=<ID>=tcp:5432
TimeoutSec=300
Restart=always

[Install]
WantedBy=multi-user.target

ただし、<ID> の部分は Cloud SQL インスタンスの接続名で置き換えてください。GCP コンソールで Cloud SQL インスタンスの「概要」タブを見れば、「このインスタンスに接続」と書いてあるボックスの中に記載されています。具体的には

foo:asia-northeast1:bar

のような形式の文字列です。foo がプロジェクト ID で、bar が Cloud SQL インスタンス ID です。

なお、5432 の部分は接続に使用するポート番号です。適宜変更しても構いません。

サービスの起動

VM インスタンスのターミナルで次のコマンドを実行し、Cloud SQL Proxy サービスを起動します。

$ sudo systemctl start cloud-sql-proxy.service

サービスの状態を確認します。

$ sudo systemctl status cloud-sql-proxy.service

正常に動いていれば、次のようなログが表示されます。

Started Cloud SQL Proxy.
Listening on sqlproxy/...
Ready for new connections

失敗した場合、例えば次のようなログが表示されます。

Failed to start Cloud SQL Proxy.

テキストエディタ/etc/systemd/system/cloud-sql-proxy.service を開き、インスタンスの接続名などが誤っていないかどうかを確認し、訂正してください。

次のコマンドを順に実行し、Cloud SQL Proxy サービスを起動し直します。

$ sudo systemctl daemon-reload
$ sudo systemctl start cloud-sql-proxy.service

接続の確認

psql コマンドで接続してみます。

$ psql -h localhost --port 5432 -U postgres postgres

サービスの有効化

正常にサービスが起動できるようになったら Cloud SQL Proxy サービスを有効化します。

$ sudo systemctl enable cloud-sql-proxy.service

直接 PostgreSQL に接続する場合とのパフォーマンスの違い(追記)

私の環境で実測したところ、直接 PostgreSQL に接続した場合に比べて Cloud SQL Proxy 経由で接続した場合、Rails アプリケーションのパフォーマンスが1割〜2割程度落ちる、という現象を観察しました。

ここで「パフォーマンス」は、レスポンス時間を指します。具体的に言えば、平均0.63秒が平均0.69秒になりました。いろいろと条件を変えて負荷試験を繰り返しましたが、レスポンス時間が1割〜2割増加するという事実は変化しませんでした。

Cloud SQL Proxy は設定を楽にしてくれますが、2018年9月の段階では、多少のデメリットもあるようです。

GCP: 自動スケーリング時における Google Cloud SQL ホワイトリストの管理

次のような条件でRuby on Railsアプリケーションを運用することにした。

セットアップの過程で少し困ったのが、PostgreSQL インスタンスに接続できるIPアドレスのリスト(ホワイトリスト)をどう管理するか、という点である。

なぜ困るか。次の3点に集約される。

  1. マネージドインスタンスグループに属する VM インスタンスPostgreSQL インスタンスは別のネットワークにある。
  2. 自動スケーリングによって起動される VM インスタンスの IP アドレスは事前にわからない。
  3. 自動スケーリングによって起動された VM インスタンスに割り当てられた IP アドレスは、インスタンス停止後に他のインスタンスにより再利用される。

アプリケーションサーバーと同じネットワーク内に PostgreSQL サーバーを自前で立てて動かす場合、そのネットワーク(例えば、192.168.0.0/24)を pg_hba.conf に記載してやればいいのだが、その方法は使えない。

VM インスタンスの起動時にその IP アドレスをホワイトリストに登録し、停止時に抹消する必要がある。

この状況を打開する方法はふたつある。ひとつは、VM gcloud コマンドで VM インスタンスの起動時にその IP アドレスをホワイトリストに登録し、停止時に抹消するという方法、もうひとつは、Cloud SQL Proxyを利用する方法である。

今回は、前者の方法について解説しよう。


前提条件

対象となる GCP プロジェクトに関して、以下の準備作業を済ませる。

  • GCP コンソールの[APIとサービス]→[ライブラリ]から「Cloud SQL Admin API」が有効化してある。
  • 「Cloud SQL Admin」権限を持つ GCP のサービスアカウントを作成してある。

以下の条件を満たす VM インスタンスを用意する。

  • サーバー OS は Ubuntu 18.04
  • Ruby 2.4 以上がインストール済み
  • 上記サービスアカウントの資格情報(JSONデータ)を /root/gcp_sa_credentials.json に保存してある。

VM インスタンスの起動・停止時に実行する Ruby スクリプトの作成

/root/bin/ ディレクトリに次のような内容のファイル vm-instance-startup.rb を作成する。ただし、5 行目の pg-instance-0 の部分は、自分の Google Cloud SQL インスタンスの名前(id)で置き換えること。

#!/usr/bin/env ruby

require "json"

SQL_INSTANCE_ID = "pg-instalnce-0"
GCLOUD_COMMAND = "/snap/bin/gcloud sql instances"
KEY_FILE_PATH = "/root/gcp_sa_credentials.json"

system("/snap/bin/gcloud auth activate-service-account --key-file=#{KEY_FILE_PATH}")

api_url = "http://metadata.google.internal/computeMetadata/v1" +
  "/instance/network-interfaces/0/access-configs/0/external-ip"

external_ip = `/usr/bin/curl -s #{api_url} -H "Metadata-Flavor: Google"`

sql_instance_description = `#{GCLOUD_COMMAND} describe #{SQL_INSTANCE_ID} --format=json`
description_hash = JSON.parse(sql_instance_description)

networks = description_hash["settings"]["ipConfiguration"]["authorizedNetworks"].map do |e|
  e["value"]
end

unless networks.include?(external_ip)
  networks << external_ip
  joined = networks.join(",")

  system("#{GCLOUD_COMMAND} patch #{SQL_INSTANCE_ID} --quiet --authorized-networks=#{joined}")
end

また、同じディレクトリに次のような内容のファイル vm-instance-shutdown.rb を作成する。

#!/usr/bin/env ruby

require "json"

SQL_INSTANCE_ID = "pg-00"
GCLOUD_COMMAND = "/snap/bin/gcloud sql instances"
KEY_FILE_PATH = "/root/gcp_sa_credentials.json"

system("/snap/bin/gcloud auth activate-service-account --key-file=#{KEY_FILE_PATH}")

api_url = "http://metadata.google.internal/computeMetadata/v1" +
  "/instance/network-interfaces/0/access-configs/0/external-ip"

external_ip = `/usr/bin/curl -s #{api_url} -H "Metadata-Flavor: Google"`

sql_instance_description = `#{GCLOUD_COMMAND} describe #{SQL_INSTANCE_ID} --format=json`
description_hash = JSON.parse(sql_instance_description)

networks = description_hash["settings"]["ipConfiguration"]["authorizedNetworks"].map do |e|
  e["value"]
end

if networks.include?(external_ip)
  networks.delete(external_ip)
  joined = networks.join(",")

  system("#{GCLOUD_COMMAND} patch #{SQL_INSTANCE_ID} --quiet --authorized-networks=#{joined}")
end

ふたつの Ruby スクリプトの内容はほぼ同じである。末尾の6行だけが異なっている。

そして、これらの Ruby ファイルの実行可能フラグを立てておく。

$ chmod u+x /root/bin/*.rb

Systemdの設定ファイルを作成

ディレクト/etc/systemd/system/whitelist-registry.service という名前のファイルを次のような内容で作成する(名前は何でもよい)。ただし、このファイルには実行可能フラグを立てないこと。

[Unit]
Description=Google Cloud SQL Whitelist Registry

[Service]
Type=oneshot
ExecStart=/root/bin/vm-instance-startup.rb
ExecStop=/root/bin/vm-instance-shutdown.rb
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

そして、このサービスを有効化する。

$ sudo systemctl enable whitelist-registry.service

動作確認

サービスの起動を試す。

$ sudo systemctl start whitelist-registry.service

GCP コンソールで Cloud SQL インスタンスの「承認」タブで、この VM インスタンスの IP アドレスが「承認済みネットワーク」に加わっていれば OK である。

サービスの停止を試す。

$ sudo systemctl stop whitelist-registry.service

GCP コンソールで Cloud SQL インスタンスの「承認」タブで、この VM インスタンスの IP アドレスが「承認済みネットワーク」から消えていれば OK である。

最後に VM インスタンス自体を再起動したり、停止したりして、動作確認する。

考察

今回解説した方法の懸念点は、Systemd の停止スクリプトが実行されないまま VM インスタンスが異常終了したときに、その IP アドレスがホワイトリストに残ってしまう、ということである。データベース自体にパスワードが設定されていればそれほど危険ではないものの、気持ち悪さは残る。

Cloud SQL Proxy を使えばこの懸念は解消されるだろうが、正直に言えば、私自身がまだ試せていない。近いうちにレポートを書きたいと思う。

『Elixir/Phoenix初級③: フォーム構造体とチェンジセット』(OIAX BOOKS)本日発売開始

拙著『Elixir/Phoenix初級③: フォーム構造体とチェンジセット』(OIAX BOOKS)が本日発売になりました。価格は税込み1,944円。206ページです。

f:id:tkrd:20180404093239j:plain

残念ながら全国の書店には並ばないので、AmazonかOIAX BOOKSのウェブストアからお買い求めください。Kindle版もあります(税込み1,080円)。

前巻(『Elixir/Phoenix初級②)の出版からちょうど1年経過しました。もともとの計画では2017年夏には出そうと思っていたのですが、ソフトウェア開発の仕事が忙しくなってしまって、なかなか執筆時間を確保できませんでした。

前巻まではElixir 1.3とPhoenix 1.2を採用していたのですが、本巻の内容はElixir 1.6とPhoenix 1.3に基づきます。Phoenix 1.3になって、Webアプリケーションのディレクトリ構造と用語法が大きく変わりました。概要を知りたい方は、@shufo 氏のPhoenix 1.3のディレクトリ構造とContext - Qiitaを参照してください。

Phoenixの「チェンジセット」はとても面白い概念です。本巻ではそれほど深いところまでは解説できていないのですが、チェンジセットを使って実際にプログラムを書いてみれば、関数型言語でWebアプリケーションを開発する楽しさの一端が見えてくると思います。

著述スタイルは、前巻を踏襲しています。具体的なWebアプリケーション例として簡易予定表管理システムNanoPlannerを作りつつ、必要に応じてElixirの文法や各種関数の説明を加えていく、というスタイルです。初心者でも迷わないように、コマンドの実行やソースコードの変更については、できるかぎり省略せずに記述しています。プログラミング経験の長い方には、少し冗長に見えるかもしれません。


さて、本書は今月の22日に開かれる技術書典4に出品されます。私の会社(株式会社オイアクス)も参加を申し込んでいたのですが、残念ながら落選してしまいました。そこで、知人のブースを間借りさせていただくことにしました。「き09」の「神Excel(カミエクセル)」です。少し割引があるので、よろしくお願いいたします。

Windows Subsystem for Linux は Rails 入門環境には(多分)向かない【追記あり】

私は、Ruby on Rails の入門書の著者あるいは初心者向け講習会の講師として、Microsoft Windows というプラットフォームの取り扱いに悩まされてきました。

プログラミング入門者・初心者の間では Windows が主流ですが、プロの Rails エンジニアの多くは macOSLinux を使っています。RubyRails が急速に進化を遂げる中で Windows が取り残される、という状況がどうしても起きます。そのため、WindowsRails 開発環境を構築する過程でさまざまなトラブルが発生しがちです。

2014年刊行の実践Ruby on Rails 4 現場のプロから学ぶ本格Webプログラミングでは、VagrantVirtualBox を用いて CentOS または Ubuntu をインストールする方法を採用しました。

しかし、2015年刊行の改訂3版基礎 Ruby on Railsでは、初版・第2版と同様にRubyInstaller for Windowsが配布しているインストーラを使用することにしました。本書はプログラミング自体を初めて体験しようとする人々に読まれているので、Vagrant は少々ハードルが高いと考えられたためです。

さて、この文章を書いているのは2018年3月18日です。まもなく Ruby on Rails 5.2 がリリースされようという時期に当たります。『基礎Ruby on Rails』の改訂作業を始めようとしています。

私は普段の Ruby on Rails の開発業務で UbuntumacOS を主に使っていて、Windows 業界の動きはあまり追いかけていないのですが、Windows 10 上で UbuntuopenSUSE などの Linux ディストリビューションを動かせるという情報は目にしていました。そして、次の『基礎Ruby on Rails』の改訂では、この技術を利用できるといいなと漠然と期待を抱いてきました。

最初の発表当時、この技術は Bash on Windows と呼ばれていましたが、その後 Windows Subsytem for Linux (WSL) が正式名称となりました。

昨晩私は WSL を初めて試してみました。まず、コントロールパネルで WSL を有効にし、Microsoft Store から Ubuntu (無償)をダウンロードし、インストールしました。UbuntuRails の開発環境を構築するのはやや複雑な過程ではありますが、何度もやっていることなので難しくはありませんでした。

しかし、Rails のインストールが終わってから重大な問題に気付きました。Windows 上にインストールされたソフトウェアから WSL 上のファイルを操作できないのです。つまり、Windows 用のテキストエディタRailsソースコードを編集することはできません。Ubuntu 上のテキストエディタである nano とか vim とかを使うのなら問題はありません。でも、私はプログラミング入門者・初心者に nano や vim を勧める気にはなりません。AtomVisual Studio Code などのモダンなテキストエディタを使ってもらいたいです。これらなら初心者でもすぐに使い始められます。書籍の第1章や講習会の初日をテキストエディタの練習に費やしたくはありません。

もう一点、私の WSL に対する印象を悪くしたのは、Ubuntu のセットアップやライブラリ群のインストール・更新に予想外の時間がかかったことです。私のネットワーク環境や実施した時間帯がよくなかったのかもしれませんが、VirtualBoxUbuntu をインストールするのに比べてかなり進みが遅かったのです。

私は、WSL を採用することで環境構築の時間が短縮できると期待していたので、この結果にはかなり失望させられました。

以上のようなわけで、残念ながら『改訂4版基礎Ruby on Rails』で WSL を採用するのは難しそうだという結論になりました。

[追記] 2018-03-19

この文章をポストした翌日、もう少し可能性を探ってみようと思い立ちました。

基本的なアイデアは、「Windows側の特定のフォルダ X とWSL側の特定のフォルダ Y を自動で同期する」というものです。これができるのなら、Windows 側のエディタで X 上のファイルを編集し、WSL上で Rails サーバーを起動し、Windows 側のブラウザでアクセスする、という仕組みが可能になります。

手動でふたつのフォルダを同期するのは難しくありません。WSLのシェル上で

$ rsync -a /mnt/c/Users/kuroda/rails/ ~/rails/

というコマンドを実行すれば、Windows 上の kuroda ユーザーのホームディレクトリにある rails フォルダでの変更内容が WSL 側の ~/rails ディレクトリに反映されます。

しかし、ふたつのフォルダを自動で同期するとなると、話は複雑になります。Linux には lsyncd という便利なツールがあり、これをデーモン(daemon)として常駐させておけば、フォルダ同士が自動で同期されます。

しかし、現行の WSL は「デーモン」に対応していないのです。

Microsoftによる2017年12月4日の発表によれば、デーモンをサポートする方向で WSL の開発が進んでいるようで、2018 年春に予定されている次の Windows 10 のアップデートで実現するかもしれません。

けれども、『改訂4版基礎Ruby on Rails』の刊行も2018年初夏を予定しているので、原稿の準備や動作確認のことを考えるととても間に合わないそうもありません。

やはり、現時点では「Windows Subsystem for LinuxRails 入門環境には(多分)向かない」という結論になってしまいます。

[追記] 2018-04-15

Qiitaで@stkdevさんが書かれた記事RailsをWindows Subsystem for Linuxで動かしてみるを読んで、私が重大な見落としをしていたことに気付きました。

Windows側からWSL側のファイルを操作できないのですが、逆は可能なのです。つまり、RailsアプリのソースコードWindows側の適当なフォルダ上で作成すれば、Windows側のテキストエディタで読み書きできるし、WSL側でサーバーとして起動することもできる、というわけです。

まったく目が曇っていました。

具体的な手順は、こんな感じになります。Windowsユーザーの名前を kuroda とすれば、まず Windows 側のホームディレクトリ直下に rails というフォルダを作成します。それは、WSL側からは /mnt/c/Users/kuroda/rails として見えるので、WSLのシェル上で

$ ln -s /mnt/c/Users/kuroda/rails ~/rails

のようにシンボリックリンクを設定できます。続いて、WSLのシェル上で、Railsアプリを生成します。

$ cd ~/rails
$ rails new foo_bar

多分、以上の考え方で問題ないでしょう。『改訂4版基礎Ruby on Rails』の原稿を書き直す方向で検討したいと思います。

macOS/Homebrewで複数バージョンのPostgreSQLを共存させる方法(2018年版)

複数の PostgreSQL クラスタ(インスタンス)を起動するで書いたように Ubuntu 環境では、普通に apt-get でインストールするだけで複数バージョンのPostgresqlを共存させる状況が整います。

しかし、macOS/Homebrew 環境ではちょっとした準備作業が必要となります。以下、その概略を説明します。

【重要な注意】 以下の手順では、いずれのコマンドも sudo を付けずに実行してください。

【参考資料】 Macで複数のバージョンのPostgresqlを共存させる -- itmammoth 氏による 2016 年 3 月の記事。

インストール済みの PostgreSQL を無効化

まず、現在の環境に PostgreSQL がインストールされているかどうかを確認します。

$ brew search postgresql

リストの中にある postgresql の右に ✔ アイコンが付いていればインストール済みです。次のコマンドで無効化します。

$ brew unlink postgresql

リポジトリの追加

リポジトリ petere/homebrew-postgresql を追加します。

$ brew tap petere/homebrew-postgresql

postgresql-common のインストール

$ brew install postgresql-common

PostgreSQL のインストール

インストール可能な PostgreSQL のバージョン番号は brew search postgresql コマンドで調べられます。表示されたリストの中に petere/postgresql/postgresql@X という項目があれば、バージョン X の PostgreSQL がインストール可能です。

例として、バージョン 9.6 と 10 をインストールしてみましょう。

$ brew install postgresql-9.6
$ brew install postgresql-10

もう一度 brew search postgresql コマンドを実行し、petere/postgresql/postgresql@9.6 および petere/postgresql/postgresql@10 の右側に ✔ アイコンが付いていることを確認してください。

クラスタの作成

ここから先は Ubuntu 環境の場合とほぼ同じです。ただし、データディレクトリ、設定ファイルの置き場所、データベース postgres の owner など、細かな違いがあります。繰り返しになりますが、すべてのコマンドを sudo なしで実行してください(重要)。

さて、PostgreSQL 用語の「クラスタ」とは、ひとつの PostgreSQL サーバーインスタンスによって管理される複数のデータベースの集合体を意味します。ひとつの macOS 上で複数個のクラスタを同時に稼働させることができます。

PostgreSQL 9.6 のクラスタ mainbackup を作成します。

$ pg_createcluster 9.6 main
$ pg_createcluster 9.6 backup

さらに、PostgreSQL 10 のクラスタ main を作成します。

$ pg_createcluster 10 main

合計で 3 個のクラスタが作られたことになります。クラスタの状態を確認します。

$ pg_lsclusters

すると、次のような結果が表示されます(一行が長いので一部省略しています)。

Ver Cluster Port Status Owner  Data directory...
9.6 main    5433 down   kuroda /usr/local/var...
9.6 backup  5434 down   kuroda /usr/local/var...
10  main    5435 down   kuroda /usr/local/var...

3番目の Port 列に着目してください。それぞれのクラスタに対応するサーバーインスタンスがどのポートを listen するかが書かれています。なお、5番目の Owner 列には、現在 macOS にログイン中のユーザー名が表示されます。

クラスタの起動

PostgreSQL 9.6 のクラスタ main を起動します。

$ pg_ctlcluster 9.6 main start

クラスタの状態を確認します。

$ pg_lsclusters

すると、次のような結果が表示されます。

Ver Cluster Port Status Owner  Data directory...
9.6 main    5433 online kuroda /usr/local/var...
9.6 backup  5434 down   kuroda /usr/local/var...
10  main    5435 down   kuroda /usr/local/var...

1 行目の Satus 列の値が down から online に変化しています。

同様に、他の 2 個のクラスタも起動します。

$ pg_ctlcluster 9.6 backup start
$ pg_ctlcluster 10 main start

psql で接続確認

PostgreSQL 10 のクラスタ mainpsql で接続してみましょう。

$ psql --port=5435 postgres

--port オプションでポート番号を、接続先データベースとして postgres を指定しています。初期状態で存在する唯一のデータベースが postgres です。オーナーは pg_createcluster を実行したユーザーです。

次のように出力されれば、成功です。

psql (10.2 (Homebrew petere/postgresql))
Type "help" for help.

postgres=#

クラスタの停止

PostgreSQL 9.6 のクラスタ main を停止するには、次のコマンドを実行します。

$ pg_ctlcluster 9.6 main stop

ポート番号などの変更

クラスタの設定ファイルは /usr/local/etc/postgresql ディレクトリの下にあります。PostgreSQL 10 のクラスタ main の設定ファイルは、/usr/local/etc/postgresql/10/main ディレクトリにあります。

ポート番号を変更したい場合は、エディタで postgresql.conf を開き、port = で始まる行を探して値を書き換えてください。設定を反映させるには、クラスタの再起動が必要です。

PostgreSQL: template1 のエンコーディングを UTF-8 に変更する

PostgreSQL におけるデータベース作成とは、既存のデータベースの複製を作ることです。デフォルトでは template1 という名前のデータベースが複製元となります。

さて、PostgreSQLのインストール直後に template1エンコーディングSQL_ASCII になっていることがあります。

postgres=# \l
                                    List of databases
     Name     |  Owner   | Encoding  |   Collate   |    Ctype    |   Access privileges   
--------------+----------+-----------+-------------+-------------+-----------------------
 postgres     | postgres | SQL_ASCII | C           | C           | 
 template0    | postgres | SQL_ASCII | C           | C           | =c/postgres          +
              |          |           |             |             | postgres=CTc/postgres
 template1    | postgres | SQL_ASCII | C           | C           | 

Web アプリケーションのバックエンドとして使う場合、エンコーディングとして UTF-8 を採用することが多いので、このままだと不便です。

psql のコンソールで次のステートメントを発行すれば、template1エンコーディングUTF-8 に(ついでに、ロケールをに ja_JP に)変更できます。

UPDATE pg_database SET datistemplate = FALSE WHERE datname = 'template1';
DROP DATABASE template1;
CREATE DATABASE template1 WITH TEMPLATE = template0 ENCODING = 'UTF8'
  LC_COLLATE = 'ja_JP.UTF-8' LC_CTYPE = 'ja_JP.UTF-8';
UPDATE pg_database SET datistemplate = TRUE WHERE datname = 'template1';

結果は、この通り:

postgres=# \l
                                    List of databases
     Name     |  Owner   | Encoding  |   Collate   |    Ctype    |   Access privileges   
--------------+----------+-----------+-------------+-------------+-----------------------
 postgres     | postgres | SQL_ASCII | C           | C           | 
 template0    | postgres | SQL_ASCII | C           | C           | =c/postgres          +
              |          |           |             |             | postgres=CTc/postgres
 template1    | postgres | UTF8      | ja_JP.UTF-8 | ja_JP.UTF-8 |