apt: バージョン指定してElixirをインストールする方法

Ubuntu/Debianにaptを利用してElixirをインストールする方法は、ここに書いてある通りです:

https://elixir-lang.jp/install.html#unix-%E7%B3%BB

ただし、これをそのまま実行すると最新のElixirがインストールされます。バージョン指定してElixirをインストールするための方法を紹介します。

Erlang solutionsのリポジトリを追加して、Erlangをインストールします。

$ 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 install esl-erlang

ここで、インストール可能なバージョンのリストを調べます。

$ apt policy elixir
elixir:
  インストールされているバージョン: 1.11.2-1
  候補:               1.12.0-1
  バージョンテーブル:
     1.12.0-1 999
        999 http://binaries.erlang-solutions.com/debian focal/contrib amd64 Packages
     1.11.4-1 999
        999 http://binaries.erlang-solutions.com/debian focal/contrib amd64 Packages
 *** 1.11.2-1 999
        999 http://binaries.erlang-solutions.com/debian focal/contrib amd64 Packages
        100 /var/lib/dpkg/status
     1.11.1-1 999
        999 http://binaries.erlang-solutions.com/debian focal/contrib amd64 Packages
     1.10.4-1 999
        999 http://binaries.erlang-solutions.com/debian focal/contrib amd64 Packages
     1.10.3-1 999
        999 http://binaries.erlang-solutions.com/debian focal/contrib amd64 Packages
     1.10.2-1 999
        999 http://binaries.erlang-solutions.com/debian focal/contrib amd64 Packages
     1.9.1.dfsg-1.3 500
        500 http://jp.archive.ubuntu.com/ubuntu focal/universe amd64 Packages

現在、1.11.2-1 がインストールされていて、最新版は 1.12.0-1 です。このまま sudo apt-get upgrade を実行すると最新版がインストールされます。

1.11.4-1 をインストールします。

$ sudo apt-get install elixir=1.11.4-1

Elixirのバージョンを固定します。

$ sudo apt mark hold elixir

こうしておくと、sudo apt-get upgrade で自動的にアップグレードされないようになります。

Ubuntu 18.04/20.04: フリーズ/クラッシュ問題対応(作業記録)解決済み

会社で購入したデスクトップPCにUbuntu 18.04 LTSとUbuntu 20.04 LTSをインストールしてみたところ、いくつか深刻な不具合に遭遇しました。一部は解決し、一部は未解決です。情報共有のため作業メモを残します。

マシンは@Sycomという会社から購入しました。省スペースPCというカテゴリのものです。すでに複数回ここから買っていて、これまで大きな問題はありませんでした。

問題は5点あります。

  1. フリーズする。タイミングはいろいろ。ログイン画面で起こったり、しばらく使ってから起こったりする。
  2. クラッシュする。突然、スクリーンが真っ黒になりPCが再起動する。
  3. クラッシュする。黒い画面に切り替わり、「Kernel panic」などのメッセージを表示して停止する。
  4. モニターを2台接続しても一方が認識されない。
  5. ディスプレイポートとDVI端子で接続したモニターの解像度を変更できない。

やったこと

amd-disable-c6 を導入

Ubuntu 18.04を Ryzenで使用時に定期的にフリーズする。という記事を見て、やってみました。効果なし。

スワップファイルのサイズを16GBに増やす

Ask ubuntuに投稿されたUbuntu 20.04 LTS freezing about every 10-15 secondsという質問への回答を見て実施。効果あり。問題1と2が解消された模様。

【追記】その後、問題2が解消されていないことが判明。

ただし、Ubuntu 20.04 LTSで新たに問題3が発生するようになりました。

Linuxカーネルを最新の5.8系にアップグレード

Ubuntu Linuxのカーネルをaptでアップグレードするを見て実施。効果あり。問題4と問題5が解消されました。問題3はPCを一晩放置した結果発生したので、効果のほどはまだわかりません。

アップグレード前のLinuxカーネルは5.4系でした。Linux 5.8は約3ヶ月前(2020年8月5日)にリリースされたばかりのもの。

マシンスペック

Kernel panicが発生したときのメッセージに「x300m-stx」という文字があったので調べてみると、ASRock DeskMini X300M-STXはつい最近(2020年10月)発売されたばかりものでした。Linuxカーネルをアップグレードして問題が解消された事実と整合します。

追記1 (2020-10-26)

Ubuntu 20.04がインストールされている別のマシンも若干調子が悪い(ときどきウィンドウ内が再描画されなくなって、黒い部分が残る)ので、Linuxカーネルを最新の5.8系(5.8.0.23)にアップグレードしてみたところ、接続している3台のモニター(HDMI接続2、DisplayPort接続1)のうちDisplayPort接続のモニターしか映らなくなるという問題が発生しました。現在は、Linuxカーネルを5.4系に戻しています。

真新しいLinuxカーネルにアップグレードするのには、それなりのリスクが伴うようです。

「別のマシン」のスペックは以下の通り:

追記2 (2020-10-26)

実際に運用してみたところ、Ubuntu 18.04でもUbuntu 20.04でも問題2(使用中に突然クラッシュして再起動)が解消されていないことが判明しました。

追記3 (2020-10-26)

Ubuntu 20.04 の Kernel を 5.9.1 にアップグレードしてみましたが、効果なし。

追記4 (2020-10-26)

https://bbs.archlinux.org/viewtopic.php?id=245608 の情報に基づき、以下の設定変更をしました。

  • sudo vim /etc/default/grubGRUB の設定ファイルを開き、GRUB_CMDLINE_LINUX_DEFAULTprocessor.max_cstate=1 を追加。
  • sudo update-grub コマンドを実行して、再起動。

ログイン後すぐにクラッシュする現象は解消された模様。現在、様子見。2時間ほど使ってみましたが、フリーズもクラッシュも起きていません。ついにやったか…。

追記5 (2020-10-27)

昨日の半日使用でフリーズもクラッシュも発生しませんでした。解決済みと判断します。

追記6 (2020-10-28)

不思議なことに同じハードウェア構成の2台のPCのうち、片方は追記5までの対策(ただし、amd-disable-c6を除く)をしても問題は解決しませんでした。ログインから1分も経たないうちにクラッシュして再起動が始まってしまいます。

そこで、ブート時にキーボードからF2を押してUEFI設定画面を開き、CPU関連の設定でEnable/Disableを切り替えられるものをすべてDisableにしてみたところ、安定して動くようになりました。

また暇のあるときに、ひとつひとつEnableしてどの設定が問題を引き起こしているのかを特定したいと思います。

追記7 (2020-10-28)

どうやら、UEFI設定画面のCPU Configurationにある、PSS SupportをDisableにすれば安定するようです。

調べたところ、これはACPI(電源管理に関する規格)に関連する設定項目でした。

追記4でGRUBの設定に追加した processor.max_cstate=1 も省電力に関するものでした。電源関係が弱点なのでしょうかね。

Docker + Rails 6 + PostgreSQL による Web アプリケーション開発の始め方(Windows版)

要旨

  • Docker Desktop for Windowsを用いてRuby on Rails 6.0によるWebアプリケーション開発を行う手順をできるかぎり簡潔にまとめた。
  • Docker Composeを用いてデータベースサーバーのコンテナとWebサーバーのコンテナを管理する方法についても解説する。

この記事は、macOSUbuntuを対象にしたDocker + Rails 6 + PostgreSQL による Web アプリケーション開発の始め方Windows向けに書き直したものです。一部、内容に重複があります。

対象OS

Windows 10 Pro (32bit) および Windows 10 Home では Docker Desktop for Windows が動作しません。

注意事項

この記事は2019年2月16から18日の間に書かれています。Ruby on Rails 6.0のリリースが予定されているのは2019年4月末です。現時点での最新版である Rails 6.0.0.beta1 に基づいています。今後、内容に訂正が必要となる可能性が十分にあります。

はじめに

この記事ではDocker Desktop for Windowsと呼ばれるソフトウェア(以下、「Docker」と呼ぶ)を用いてRuby on Rails 6の開発環境を構築する手順を紹介します。Dockerをインストールする対象のOS(ホストOS)はWindows 10 Pro (64bit)とします。

ゲストOSにはAlpine Linuxを採用しました。インストールに必要なストレージ容量が非常に小さいことが特長です。

Dockerを用いて構築されたゲストOSの環境はコンテナと呼ばれます。この記事では、データベースサーバー(PostgreSQLサーバー)のためのコンテナとWebサーバー(Railsアプリケーション)のためのコンテナ、計2個のコンテナを作ります。これらのコンテナをうまく組み合わせるためにDocker Composeと呼ばれるソフトウェアを使用します。

コンテナを実行するのに必要なファイルシステムディレクトリ、ファイル、メタ情報の集合体)をイメージと呼びます。イメージはWebサービスDocker Hubを通じて配布されます。

また、この記事では設定ファイル等の取得にGitを使用します。あらかじめGit for Windowsをインストールしておいてください。

Docker Hubアカウントの取得

Docker自体のインストーラやDockerイメージを取得するためには、Docker Hubのアカウントが必要となります。メールを受け取れるアドレスがあれば、無料で取得できます。未取得の方は、次のURLを訪問して登録してください。

https://hub.docker.com/signup 

Docker Hubにログインしてから次の手順に進んでください。

Docker Desktop for Windowsのインストール

https://hub.docker.com/editions/community/docker-ce-desktop-windows から Docker Desktop for Windowsインストーラをダウンロードしてインストールしてください。Dockerと同時にDocker Composeもインストールされます。

@sikkim 氏がQiitaに書いた記事 https://qiita.com/sikkim/items/bfc79561b16b2c13caba に詳しいインストール手順が載っていますので、参考にしてください。

※ DockerToolboxという名前の類似のソフトウェアが存在しますが、別物です。混同しないでください。

【重要な注意】 VirtualBox との共存

  • Docker for WindowsをインストールするとVirtualBoxが動作しなくなるケースがあります。
  • Docker for Windowsを使用する場合、Windows 10の仮想化システム「Hyper-V」を有効にする必要がありますが、バージョン6未満のVirtualBoxはこの「Hyper-V」と共存できません。
  • 2018年末にリリースされたVirtualBox 6.0は「Hyper-V」と共存できるとされていますが、ハードウェアにより動作する場合と動作しない場合あるようです。

【参考】

Dockerへのログイン

Docker Desktop for Windowsのインストールが完了した後、いったんWindowsからサインアウトする必要があります。サインインすると、DockerのWelcomeウィンドウが現れるので、DockerHubのIDとパスワードでログインします。

Docker Compose用の設定ファイル群の取得

コマンドプロンプト(cmd.exe)を開き、以下のコマンドを実行します。

> git clone https://github.com/oiax/rails6-compose.git
> cd rails6-compose
> cp docker-compose.win.yml docker-compose.override.yml

ボリュームの作成

> docker volume create --name pgdata

ボリュームの作成に関する注記

  • 複数の Rails アプリケーション開発プロジェクトを並行して進める場合、上記コマンドの pgdata の部分をそれぞれ別の名前、例えば pgdata1pgdata2 で置き換える必要があります。
  • その場合、テキストエディタdocker-compose.override.yml を開き、pgdata と書かれている箇所を pgdata1 あるいは pgdata2 で書き換えてください。

コンテナ群の構築・起動

> docker pull oiax/rails6-deps:latest
> docker-compose up -d

オプション -d により、コンテナ群が detached mode で起動します。すなわち、コンテナ群がバックグランドで動き始めます。

ターミナルに次のようなメッセージが表示されれば OK です。

Starting rails6compose_db_1 ... 
Starting rails6compose_db_1 ... done
Starting rails6compose_web_1 ... 
Starting rails6compose_web_1 ... done

Web コンテナにログイン

> docker-compose exec web bash

※ ログアウトするには exit コマンドを実行するか、Ctrl-D キーを入力してください。

コンテナ群の停止

> docker-compose stop

コンテナ群の破棄

> docker-compose down

ボリュームの破棄

> docker volume rm pgdata

Docker + Rails 6 + PostgreSQL による Web アプリケーション開発の始め方

要旨

  • Dockerを用いてRuby on Rails 6.0によるWebアプリケーション開発を行う手順をできるかぎり簡潔にまとめた。
  • Docker Composeを用いてデータベースサーバーのコンテナとWebサーバーのコンテナを管理する方法についても解説する。

対象OS

Windows 10 ProでもDockerを用いてRails開発を行うことができますが、用語や手順で相違点が多いので今回は対象外とします。

【更新】 Windows編を書きました。

注意事項

この記事は2019年2月16から18日の間に書かれています。Ruby on Rails 6.0のリリースが予定されているのは2019年4月末です。現時点での最新版である Rails 6.0.0.beta1 に基づいています。今後、内容に訂正が必要となる可能性が十分にあります。

はじめに

この記事ではDockerと呼ばれるソフトウェアを用いてRuby on Rails 6の開発環境を構築する手順を紹介します。Dockerをインストールする対象のOS(ホストOS)は、macOSUbuntu 18.04 (64bit)とします。

ゲストOSにはAlpine Linuxを採用しました。インストールに必要なストレージ容量が非常に小さいことが特長です。

Dockerを用いて構築されたゲストOSの環境はコンテナと呼ばれます。この記事では、データベースサーバー(PostgreSQLサーバー)のためのコンテナとWebサーバー(Railsアプリケーション)のためのコンテナ、計2個のコンテナを作ります。これらのコンテナをうまく組み合わせるためにDocker Composeと呼ばれるソフトウェアを使用します。

コンテナを実行するのに必要なファイルシステムディレクトリ、ファイル、メタ情報の集合体)をイメージと呼びます。イメージはWebサービスDocker Hubを通じて配布されます。

また、この記事では設定ファイル等の取得にGitを使用します。ホストOSに合わせて、最新のGitをインストールしてください。

Docker Hubアカウントの取得

Docker自体のインストーラやDockerイメージを取得するためには、Docker Hubのアカウントが必要となります。メールを受け取れるアドレスがあれば、無料で取得できます。未取得の方は、次のURLを訪問して登録してください。

https://hub.docker.com/signup 

Docker Hubにログインしてから次の手順に進んでください。

Docker/Docker Composeのインストール

macOSの場合

https://hub.docker.com/editions/community/docker-ce-desktop-mac から Docker Desktop for Macインストーラをダウンロードしてインストールしてください。Dockerと同時にDocker Composeもインストールされます。

Ubuntuの場合

ターミナルで以下のコマンド群を順に実行し、Dockerをインストールします。

$ sudo apt-get update
$ sudo apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
$ sudo apt-get update
$ sudo apt-get install -y docker-ce docker-ce-cli containerd.io

詳しくは https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-using-the-repository を参照してください。

さらに、以下のコマンド群を順に実行し、Docker Composeをインストールします。

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" \
    -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

1.23.2 の部分は、Docker Composeのバージョン番号です。

詳しくは、https://docs.docker.com/compose/install/#install-compose を参照してください。

$ sudo gpasswd -a $USER docker

Ubuntuからログアウトし、ログインし直します。

docker login

ターミナルで次のコマンドを実行し、Docker Hubにログインします。

$ docker login

Docker Compose用の設定ファイル群の取得

$ git clone https://github.com/oiax/rails6-compose.git
$ cd rails6-compose

コンテナ群の構築・起動

$ setup.sh
$ docker-compose up -d

オプション -d により、コンテナ群が detached mode で起動します。すなわち、コンテナ群がバックグランドで動き始めます。

ターミナルに次のようなメッセージが表示されれば OK です。

Starting rails6compose_db_1 ... 
Starting rails6compose_db_1 ... done
Starting rails6compose_web_1 ... 
Starting rails6compose_web_1 ... done

【参考】 setup.sh の中身

setup.sh は次のような bash スクリプトです。

#!/bin/bash
set -eu

docker pull oiax/rails6-deps:latest

case "$OSTYPE" in
  darwin*)
    docker-compose build web
    ;;
  linux*)
    docker-compose build --build-arg UID=$(id -u) --build-arg GID=$(id -g) web
    ;;
  *)
    echo "Unknown OS Type: $OSTYPE"
    ;;
esac

まず、docker pull コマンドでベースとなるイメージの最新版を取得しています。

続いて、docker-compose build コマンドで Web コンテナを構築します。このとき、ホスト OS とコンテナの間でソースコード等を共有するときに、ファイルやディレクトリの所有権に関する問題が発生しないように --build-arg オプションを不可しています。この問題は Linux 版の Docker で発生しますが、macOS 版では発生しません。

参考資料:

コンテナ群の状態の確認

$ docker-compose ps

コンテナ群が正常に起動していれば次のような結果が出力されます。

       Name                      Command              State           Ports
------------------------------------------------------------------------------------
rails6compose_db_1    docker-entrypoint.sh postgres   Up      5432/tcp
rails6compose_web_1   /bin/sh                         Up      0.0.0.0:3000->3000/tcp 

Webコンテナへのログイン

$ docker-compose exec web bash

develユーザーとしてAlpine Linuxにログインします。パスワードなしでsudoを実行する権限が与えてあります。

Rails 開発に最低限必要と思われるパッケージ群は入っていますが、もし足りないパッケージがあれば apk コマンドでインストールしてください。

Railsアプリケーションの開発の要点

  • rails コマンドや bundler コマンドを用いた各種作業は、Webコンテナにログインして行います。
  • ソースコードの編集はホストOS側のテキストエディタで行います。

https://github.com/oiax/rails6-compose/blob/master/RAILS.md に具体的な操作例がありますので、参考にしてください。

Rails アプリケーションの起動

ホスト OS のターミナルで、次のコマンドを実行します。

$ docker-compose exec web bash -c 'cd /apps/myapp; bin/webpack-dev-server'

さらに別のターミナルを開き、次のコマンドを実行します。

$ docker-compose exec web bash -c 'cd /apps/myapp; bin/rails s'

コンテナの停止

$ docker-compose stop

コンテナの破棄

$ docker-compose down

おわりに

私がこの記事を書こうと思ったきっかけは、2014年刊行の拙著『実践Ruby on Rails 4 現場のプロから学ぶ本格Webプログラミング』の改訂版を出したいという打診をインプレスの編集者から受けたことです。

この本ではVagrantを使って開発環境を構築しましたが、改訂版ではDockerを利用したいと考えました。原稿を書き始める前にブログ記事を書いて知識をアップデートしておこう、ということです。あわよくば読者の方からフィードバックをもらって間違いを潰しておきたい、という意図もあります。

この記事を書くにあたっては、Dockerの公式ドキュメントにある「Quickstart: Compose and Rails」(以下、「Quickstart」と呼びます)を参考にしました。

https://docs.docker.com/compose/rails/

ただし、両者の間には重要な相違点があります。Quickstartでは docker-compose up コマンドの結果、自動的にRailsアプリケーションが起動しますが、私の記事では手動で起動しなければなりません。私の手順の方が少し面倒ですが、わかりやすさを優先しました。

この記事で紹介しているのは、あくまでDockerを用いて手早くRails開発環境を構築する方法です。構築した開発環境の設定をチームで共有したり、実運用(本番)環境にRailsアプリケーションをデプロイしたりするのは、また別の話になります。

この記事についての質問は、ブログ上のコメント欄の他にメールでも受け付けます。hermes@oiax.jp までご連絡ください。

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 22 [erts-10.4.1] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [hipe]

Elixir 1.8.2 (compiled with Erlang/OTP 20)

Node.js のインストール

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

$ curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
$ sudo apt-get install nodejs

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