こたつ&&みかん&&でーたべーす

DB 関連の話を中心に技術っぽい記事を書きます。

CockroachDB をスケールアウトするお話

今回は CockroachDB クラスタをスケールアウトする方法についてのお話です。

環境

今回は以下の環境 (Docker を利用したローカルの Insecure クラスタ) で検証しています。

Ubuntu : 19.10
Docker : 19.03.5
CockroachDB : 19.2.4
Container Image : cockroachdb/cockroach

クラスタ構築 (スケールアウト前)

まずは 3匹構成のクラスタを構築します。詳細は割愛しますが、Docker を利用したローカルクラスタの構築方法についてはこちらの記事に記載していますので、興味がある方はご参照ください。

今回は "ノード名の数字 (cockroach-N)" と "CockroachDB 内部で各ノードに割り当てられる ID" を一致させるために、意図的に "1匹目起動 -> クラスタ初期化 (cockroach init) -> 2匹目起動 -> 3匹目起動" の順序でコマンドを実行し、クラスタを構築しています。

$ sudo docker network create cockroach-net
$ sudo docker run -d \
--name=cockroach-1 \
--hostname=cockroach-1 \
--net=cockroach-net \
-p 26257:26257 -p 8080:8080 \
-v "${PWD}/cockroach-data/cockroach-1:/cockroach/cockroach-data" \
cockroachdb/cockroach:v19.2.4 start \
--insecure \
--join=cockroach-1,cockroach-2,cockroach-3
$ sudo docker exec -it cockroach-1 ./cockroach init --host=cockroach-1 --insecure
$ sudo docker run -d \
--name=cockroach-2 \
--hostname=cockroach-2 \
--net=cockroach-net \
-v "${PWD}/cockroach-data/cockroach-2:/cockroach/cockroach-data" \
cockroachdb/cockroach:v19.2.4 start \
--insecure \
--join=cockroach-1,cockroach-2,cockroach-3
$ sudo docker run -d \
--name=cockroach-3 \
--hostname=cockroach-3 \
--net=cockroach-net \
-v "${PWD}/cockroach-data/cockroach-3:/cockroach/cockroach-data" \
cockroachdb/cockroach:v19.2.4 start \
--insecure \
--join=cockroach-1,cockroach-2,cockroach-3

構築したクラスタはこんな感じになります。

$ sudo docker ps
CONTAINER ID        IMAGE                           COMMAND                  CREATED              STATUS              PORTS                                              NAMES
78c86cb0e977        cockroachdb/cockroach:v19.2.4   "/cockroach/cockroac…"   35 seconds ago       Up 35 seconds       8080/tcp, 26257/tcp                                cockroach-3
b324a970c3cf        cockroachdb/cockroach:v19.2.4   "/cockroach/cockroac…"   47 seconds ago       Up 46 seconds       8080/tcp, 26257/tcp                                cockroach-2
b1cdf52b5de4        cockroachdb/cockroach:v19.2.4   "/cockroach/cockroac…"   About a minute ago   Up About a minute   0.0.0.0:8080->8080/tcp, 0.0.0.0:26257->26257/tcp   cockroach-1
$ sudo docker exec -it cockroach-1 ./cockroach node status --host=cockroach-1 --insecure
  id |      address      |    sql_address    |  build  |            started_at            |            updated_at            | locality | is_available | is_live  
+----+-------------------+-------------------+---------+----------------------------------+----------------------------------+----------+--------------+---------+
   1 | cockroach-1:26257 | cockroach-1:26257 | v19.2.4 | 2020-03-03 10:17:05.067085+00:00 | 2020-03-03 10:18:44.108782+00:00 |          | true         | true     
   2 | cockroach-2:26257 | cockroach-2:26257 | v19.2.4 | 2020-03-03 10:17:39.956784+00:00 | 2020-03-03 10:18:47.508341+00:00 |          | true         | true     
   3 | cockroach-3:26257 | cockroach-3:26257 | v19.2.4 | 2020-03-03 10:17:51.714024+00:00 | 2020-03-03 10:18:45.761979+00:00 |          | true         | true     
(3 rows)

スケールアウト (3匹 -> 5匹)

それではさっそく、スケールアウトしてみます。

以下のコマンドで既存のクラスタに 4匹目を追加します。

$ sudo docker run -d \
--name=cockroach-4 \
--hostname=cockroach-4 \
--net=cockroach-net \
-v "${PWD}/cockroach-data/cockroach-4:/cockroach/cockroach-data" \
cockroachdb/cockroach:v19.2.4 start \
--insecure \
--join=cockroach-1,cockroach-2,cockroach-3

以上!

これだけ。本当にこれだけです。CockroachDB はとても簡単にスケールアウトできます。

cockroach node status コマンドで確認すると、クラスタが 4匹構成になっていることが確認できます。

$ sudo docker exec -it cockroach-1 ./cockroach node status --host=cockroach-1 --insecure
  id |      address      |    sql_address    |  build  |            started_at            |            updated_at            | locality | is_available | is_live  
+----+-------------------+-------------------+---------+----------------------------------+----------------------------------+----------+--------------+---------+
   1 | cockroach-1:26257 | cockroach-1:26257 | v19.2.4 | 2020-03-03 10:17:05.067085+00:00 | 2020-03-03 10:19:33.607906+00:00 |          | true         | true     
   2 | cockroach-2:26257 | cockroach-2:26257 | v19.2.4 | 2020-03-03 10:17:39.956784+00:00 | 2020-03-03 10:19:32.507973+00:00 |          | true         | true     
   3 | cockroach-3:26257 | cockroach-3:26257 | v19.2.4 | 2020-03-03 10:17:51.714024+00:00 | 2020-03-03 10:19:30.771919+00:00 |          | true         | true     
   4 | cockroach-4:26257 | cockroach-4:26257 | v19.2.4 | 2020-03-03 10:19:29.534717+00:00 | 2020-03-03 10:19:34.091829+00:00 |          | true         | true     
(4 rows)

更にノードを追加したい場合は、同じように新しいノード (cockroach-5) を起動します。

$ sudo docker run -d \
--name=cockroach-5 \
--hostname=cockroach-5 \
--net=cockroach-net \
-v "${PWD}/cockroach-data/cockroach-5:/cockroach/cockroach-data" \
cockroachdb/cockroach:v19.2.4 start \
--insecure \
--join=cockroach-1,cockroach-2,cockroach-3
$ sudo docker exec -it cockroach-1 ./cockroach node status --host=cockroach-1 --insecure
  id |      address      |    sql_address    |  build  |            started_at            |            updated_at            | locality | is_available | is_live  
+----+-------------------+-------------------+---------+----------------------------------+----------------------------------+----------+--------------+---------+
   1 | cockroach-1:26257 | cockroach-1:26257 | v19.2.4 | 2020-03-03 10:17:05.067085+00:00 | 2020-03-03 10:20:09.607054+00:00 |          | true         | true     
   2 | cockroach-2:26257 | cockroach-2:26257 | v19.2.4 | 2020-03-03 10:17:39.956784+00:00 | 2020-03-03 10:20:08.508058+00:00 |          | true         | true     
   3 | cockroach-3:26257 | cockroach-3:26257 | v19.2.4 | 2020-03-03 10:17:51.714024+00:00 | 2020-03-03 10:20:06.773181+00:00 |          | true         | true     
   4 | cockroach-4:26257 | cockroach-4:26257 | v19.2.4 | 2020-03-03 10:19:29.534717+00:00 | 2020-03-03 10:20:10.085793+00:00 |          | true         | true     
   5 | cockroach-5:26257 | cockroach-5:26257 | v19.2.4 | 2020-03-03 10:20:07.715985+00:00 | 2020-03-03 10:20:07.78451+00:00  |          | true         | true     
(5 rows)

スケールアウト (既存クラスタへのノード追加) で重要なのが --join オプションです。この --join オプションに、既存のクラスタ内で動作しているノード (接続先のノード) を指定します。

最初に構築した 3匹構成のクラスタ内の各ノードは、<hostname:26275> で他のノードからの通信及びクライアントからの通信を LISTEN しています (port 番号 "26257" はデフォルト値)。

なので、後から追加するノード (cockroach-4, cockroach-5) を起動する際の --join オプションに <hostname:[port]> を指定して起動する (明示的に port を指定しなかった場合はデフォルト値の "26257" が指定される) と、--join に指定したノードに対して接続を試行し、既存のクラスタに起動したノード (新しいノード) が追加されます。

また、--join オプションにはカンマ区切りで複数の値を指定することができます。ドキュメントを見る限り、既存クラスタ内の 3 ~ 5 台のノードを指定すると良さそうです。

この --join オプションを指定してクラスタに追加するノードを起動すれば、クラスタ接続後のデータのリバランシングやその他の処理を自動でいい感じにやってくれます。

スケールアウト時の Automatic Rebalancing

CockroachDB は、ユーザが作成したテーブルのデータを "Range" と呼ばれる単位のデータに分割し、各 Range の Replica (コピー) を 3つ保持しています (デフォルト設定の場合)。また、クラスタメタデータ等が含まれているテーブル (System Range と呼ばれている) は、デフォルトで 5つの Replica を保持しています (ただし、3匹構成のクラスタの場合は System Range の Replica も 3つ)。CockroachDB は、この Replica を各ノードにいい感じに分散配置することで、耐障害性や性能の向上を図っています。

CockroachDB をスケールアウトすると、このいい感じ分散配置されている Range (Replica) をリバランシング (新しいノードにも分配) してくれるので、その動作を検証してみます。

テストデータ作成

まずは、root ユーザで DB に接続し、テスト用のテーブル (t0, t1, t2) の作成と適当なデータの INSERT を実施します。

$ sudo docker exec -it cockroach-1 ./cockroach sql --host=cockroach-1 --insecure
#
# Welcome to the CockroachDB SQL shell.
# All statements must be terminated by a semicolon.
# To exit, type: \q.
#
# Server version: CockroachDB CCL v19.2.4 (x86_64-unknown-linux-gnu, built 2020/02/06 21:55:19, go1.12.12) (same version as client)
# Cluster ID: a75fe40c-37a4-4e13-bde1-800b30e5995a
#
# Enter \? for a brief introduction.
#
root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> CREATE TABLE t0 (id INT PRIMARY KEY, c1 INT, c2 INT, c3 INT);
CREATE TABLE

Time: 75.726718ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> CREATE TABLE t1 (id INT PRIMARY KEY, c1 INT, c2 INT, c3 INT);
CREATE TABLE

Time: 59.199631ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> CREATE TABLE t2 (id INT PRIMARY KEY, c1 INT, c2 INT, c3 INT);
CREATE TABLE

Time: 72.846164ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> INSERT INTO t0 SELECT num, (random() * 100)::int, (random() * 100)::int, (random() * 100)::int FROM generate_series(1,1500000) AS num;
INSERT 1500000

Time: 21.588598891s

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> INSERT INTO t1 SELECT num, (random() * 100)::int, (random() * 100)::int, (random() * 100)::int FROM generate_series(1,1500000) AS num;
INSERT 1500000

Time: 30.683205437s

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> INSERT INTO t2 SELECT num, (random() * 100)::int, (random() * 100)::int, (random() * 100)::int FROM generate_series(1,1500000) AS num;
INSERT 1500000

Time: 43.993299853s

root@cockroach-1:26257/defaultdb>

テーブル作成後、各テーブル (t0, t1, t2) の Range (Replica) がどのノードに配置されているかを確認します。

root@cockroach-1:26257/defaultdb> SELECT table_name, range_id, replicas FROM crdb_internal.ranges WHERE database_name = 'defaultdb';
  table_name | range_id | replicas  
+------------+----------+----------+
  t0         |       25 | {1,2,5}   
  t0         |       38 | {1,2,3}   
  t0         |       39 | {1,2,3}   
  t1         |       35 | {1,2,3}   
  t1         |       41 | {1,2,3}   
  t1         |       55 | {3,4,5}   
  t2         |       36 | {1,4,5}   
  t2         |       66 | {1,4,5}   
  t2         |       68 | {1,4,5}   
(9 rows)

Time: 5.774765ms

crdb_internal.ranges.replicas の数字は各ノードの ID です。ノード ID は cockroach node status コマンドで確認できます。今回は "ノード名の数字 (cockroach-N)" と "CockroachDB 内部で各ノードに割り当てられる ID" が一致する (cockroach-1 -> ID 1, cockroach-2 -> ID 2, cockroach-3 -> ID 3, ...) ようにクラスタを構築しているので、上記結果の場合、各テーブル (t0, t1, t2) のデータは 3つの Range に分割されており、各 Range の Replica は以下のように分散されて配置されています。

[t0 のデータ]

Range ID: 25 -> cockroach-1, cockroach-2, cockroach-5
Range ID: 38 -> cockroach-1, cockroach-2, cockroach-3
Range ID: 39 -> cockroach-1, cockroach-2, cockroach-3

[t1 のデータ]

Range ID: 35 -> cockroach-1, cockroach-2, cockroach-3
Range ID: 41 -> cockroach-1, cockroach-2, cockroach-3
Range ID: 55 -> cockroach-3, cockroach-4, cockroach-5

[t2 のデータ]

Range ID: 36 -> cockroach-1, cockroach-4, cockroach-5
Range ID: 66 -> cockroach-1, cockroach-4, cockroach-5
Range ID: 68 -> cockroach-1, cockroach-4, cockroach-5

スケールアウト (5匹 -> 7匹)

それでは、再度 CockroachDB クラスタをスケールアウトしてみます。

先程と同じように以下のコマンドでノード (6匹目, 7匹目) を追加します。

$ sudo docker run -d \
--name=cockroach-6 \
--hostname=cockroach-6 \
--net=cockroach-net \
-v "${PWD}/cockroach-data/cockroach-6:/cockroach/cockroach-data" \
cockroachdb/cockroach:v19.2.4 start \
--insecure \
--join=cockroach-1,cockroach-2,cockroach-3
$ sudo docker run -d \
--name=cockroach-7 \
--hostname=cockroach-7 \
--net=cockroach-net \
-v "${PWD}/cockroach-data/cockroach-7:/cockroach/cockroach-data" \
cockroachdb/cockroach:v19.2.4 start \
--insecure \
--join=cockroach-1,cockroach-2,cockroach-3

ノード追加後、cockroach node status コマンドでクラスタが 7匹構成になっていることを確認します。

$ sudo docker exec -it cockroach-1 ./cockroach node status --host=cockroach-1 --insecure
  id |      address      |    sql_address    |  build  |            started_at            |            updated_at            | locality | is_available | is_live  
+----+-------------------+-------------------+---------+----------------------------------+----------------------------------+----------+--------------+---------+
   1 | cockroach-1:26257 | cockroach-1:26257 | v19.2.4 | 2020-03-03 10:17:05.067085+00:00 | 2020-03-03 10:36:30.607204+00:00 |          | true         | true     
   2 | cockroach-2:26257 | cockroach-2:26257 | v19.2.4 | 2020-03-03 10:17:39.956784+00:00 | 2020-03-03 10:36:29.534801+00:00 |          | true         | true     
   3 | cockroach-3:26257 | cockroach-3:26257 | v19.2.4 | 2020-03-03 10:17:51.714024+00:00 | 2020-03-03 10:36:32.258867+00:00 |          | true         | true     
   4 | cockroach-4:26257 | cockroach-4:26257 | v19.2.4 | 2020-03-03 10:19:29.534717+00:00 | 2020-03-03 10:36:31.079381+00:00 |          | true         | true     
   5 | cockroach-5:26257 | cockroach-5:26257 | v19.2.4 | 2020-03-03 10:20:07.715985+00:00 | 2020-03-03 10:36:28.781256+00:00 |          | true         | true     
   6 | cockroach-6:26257 | cockroach-6:26257 | v19.2.4 | 2020-03-03 10:34:07.000889+00:00 | 2020-03-03 10:36:31.059838+00:00 |          | true         | true     
   7 | cockroach-7:26257 | cockroach-7:26257 | v19.2.4 | 2020-03-03 10:36:30.337069+00:00 | 2020-03-03 10:36:30.388762+00:00 |          | true         | true     
(7 rows)

スケールアウト完了 (7匹構成になっていること) を確認後、改めて先程作成した各テーブルの Rnage (Replica) がどのノードに配置されているかを確認します。(スケールアウト後のリバランシング処理には少し時間がかかるので、数分待ってから確認するとよいかもしれません)

root@cockroach-1:26257/defaultdb> SELECT table_name, range_id, replicas FROM crdb_internal.ranges WHERE database_name = 'defaultdb';
  table_name | range_id | replicas  
+------------+----------+----------+
  t0         |       25 | {1,2,5}   
  t0         |       38 | {1,3,7}   
  t0         |       39 | {1,2,7}   
  t1         |       35 | {1,2,6}   
  t1         |       41 | {2,3,6}   
  t1         |       55 | {5,6,7}   
  t2         |       36 | {1,4,7}   
  t2         |       66 | {1,4,6}   
  t2         |       68 | {4,5,6}   
(9 rows)

Time: 5.979204ms

すると、先程とは Range (Replica) の配置が変わっていることが確認できます。上記結果の場合、各 Range の Replica は以下のように配置されています。スケールアウト前と比べて、追加した cockroach-6 や cockroach-7 にも Replica が分散されていることが確認できます。

[t0 のデータ]

Range ID: 25 -> cockroach-1, cockroach-2, cockroach-5
Range ID: 38 -> cockroach-1, cockroach-3, cockroach-7
Range ID: 39 -> cockroach-1, cockroach-2, cockroach-7

[t1 のデータ]

Range ID: 35 -> cockroach-1, cockroach-2, cockroach-6
Range ID: 41 -> cockroach-2, cockroach-3, cockroach-6
Range ID: 55 -> cockroach-5, cockroach-6, cockroach-7

[t2 のデータ]

Range ID: 36 -> cockroach-1, cockroach-4, cockroach-7
Range ID: 66 -> cockroach-1, cockroach-4, cockroach-6
Range ID: 68 -> cockroach-4, cockroach-5, cockroach-6

また、Admin UI (Web UI) の Metrics タブ (http://localhost:8080/#/metrics/overview/cluster) を開くと、"Replicas per Node" の項目で各ノードが保持している Replica 数の推移をグラフで見ることができます。

今回のようにスケールアウトすると、以下のように各ノードが保持している Replica 数が変化していること (自動でリバランシングしていること) が確認できます。

[Replicas per Node] 00_admin_ui_replicas_per_node

また、表示されているグラフをマウスオーバーすると、各ノードが保持している Replica 数が表示されます。各タイミング (5匹構成, 6匹構成, 7匹構成) における各ノードが保持している Replica 数は以下のようになっています。

[5匹構成 (スケールアウト前)] 01_before_scale_out

[6匹目追加後] 02_add_6th_node

[7匹目追加後] 03_add_7th_node

スケールアウト前 (5匹構成) は各ノードが 28 ~ 31 の Replica を保持していますが、6匹構成時 (スケールアウト後) は各ノードが 23 ~ 25 の Replica を、7匹構成時は各ノードが 19 ~ 22 の Replica を保持しており、1ノードあたりの Replica 数が減少していること (クラスタ全体で Replica をある程度均等にリバランシングしていること) も確認できます。

ローカルクラスタの削除

検証後、不要な場合は各リソースを削除しておきます。

$ sudo docker rm $(sudo docker kill cockroach-1 cockroach-2 cockroach-3 cockroach-4 cockroach-5 cockroach-6 cockroach-7)
$ sudo docker network rm cockroach-net
$ sudo rm -rf ${PWD}/cockroach-data/

まとめ

CockroachDB はとてもシンプルな手順でスケールアウトすることができます。

ただし、自動で (裏で勝手に) データのリバランシング処理が走るので、スケールアウト実施時におけるクラスタ内のネットワーク帯域や各ノードのディスク I/O には注意が必要な雰囲気 (一時的に負荷が上がる可能性がありそう) です。

CockroachDB のローカルクラスタを Docker で構築するお話

今回は Docker を使ってローカル (単一ホスト上) に検証用の CockroachDB クラスタを構築する方法についてのお話です。

環境

今回は以下の環境で検証しています。

Ubuntu : 19.10
Docker : 19.03.5
CockroachDB : 19.2.4

CockroachDB のクラスタの種類について

CockroachDB のクラスタには Insecure クラスタと Secure クラスタがあり、以下の様な特徴があります。

Insecure クラスタ

  • ノード間の通信は暗号化されない (平文で通信)。
  • クライアント - サーバ間の通信も暗号化されない (平文で通信)。
  • root を含む全ての DB ユーザが認証無しで接続可能 (認証処理が無く、誰でも root で接続可能)。

Secure クラスタ

  • ノード間の通信は TLS で暗号化される。
  • クライアント - サーバ間の通信も TLS で暗号化される。
  • root ユーザでの接続にはクライアント証明書を使った認証が必要。
  • (root 以外の) 一般ユーザでの接続にはパスワード認証もしくはクライアント証明書を使った認証が必要。

クラスタ構築

それでは、それぞれのクラスタを構築してみます。今回は 3匹構成のクラスタを構築します。各手順で利用する Docker イメージは、Cockroach Labs が Docker Hub で公開している cockroachdb/cockroach を使います。

Insecure クラスタ

Insecure クラスタの場合は、各ノードを起動する際に --insecure オプションを指定します。また、Insecure クラスタの構築方法は公式ドキュメント Start Cluster in Docker にも記載されています。

Insecure クラスタの構築

1) まずは、各ノード (コンテナ) を接続させる Docker Network を作成します。

$ sudo docker network create cockroach-net

2) 作成した Docker Network を指定し、1匹目を起動します。

$ sudo docker run -d \
--name=cockroach-1 \
--hostname=cockroach-1 \
--net=cockroach-net \
-p 26257:26257 -p 8080:8080 \
-v "${PWD}/cockroach-data/cockroach-1:/cockroach/cockroach-data" \
cockroachdb/cockroach:v19.2.4 start \
--insecure \
--join=cockroach-1,cockroach-2,cockroach-3

3) 同様に、2匹目/3匹目も起動します。

$ sudo docker run -d \
--name=cockroach-2 \
--hostname=cockroach-2 \
--net=cockroach-net \
-v "${PWD}/cockroach-data/cockroach-2:/cockroach/cockroach-data" \
cockroachdb/cockroach:v19.2.4 start \
--insecure \
--join=cockroach-1,cockroach-2,cockroach-3
$ sudo docker run -d \
--name=cockroach-3 \
--hostname=cockroach-3 \
--net=cockroach-net \
-v "${PWD}/cockroach-data/cockroach-3:/cockroach/cockroach-data" \
cockroachdb/cockroach:v19.2.4 start \
--insecure \
--join=cockroach-1,cockroach-2,cockroach-3

4) 最後に cockroach init コマンドでクラスタを初期化します。

$ sudo docker exec -it cockroach-1 ./cockroach init --host=cockroach-1 --insecure

これで、Insecure クラスタの構築は完了です。cockroach node status コマンドを実行すると、構築したクラスタの情報を参照できます。

$ sudo docker exec -it cockroach-1 ./cockroach node status --host=cockroach-1 --insecure
  id |      address      |    sql_address    |  build  |            started_at            |            updated_at            | locality | is_available | is_live  
+----+-------------------+-------------------+---------+----------------------------------+----------------------------------+----------+--------------+---------+
   1 | cockroach-1:26257 | cockroach-1:26257 | v19.2.4 | 2020-02-14 11:01:07.838801+00:00 | 2020-02-14 11:01:16.888403+00:00 |          | true         | true     
   2 | cockroach-3:26257 | cockroach-3:26257 | v19.2.4 | 2020-02-14 11:01:08.106229+00:00 | 2020-02-14 11:01:12.651449+00:00 |          | true         | true     
   3 | cockroach-2:26257 | cockroach-2:26257 | v19.2.4 | 2020-02-14 11:01:08.641134+00:00 | 2020-02-14 11:01:13.190636+00:00 |          | true         | true     
(3 rows)

Built-in SQL Shell を使って Insecure クラスタに接続

Insecure クラスタ構築後、cockroach sql コマンド (Built-in SQL Shell) を使って CockroachDB (cockroach-1) に接続できます (cockroach sql にも --insecure オプションを指定します)。

$ sudo docker exec -it cockroach-1 ./cockroach sql --host=cockroach-1 --insecure

また、CockroachDB クラスタ内の各ノードには Master/Standby や Coordinator/Worker と呼ばれるような役割の分担はなく、全てのノードで Read/Write のクエリを処理できる (どのノードに接続しても Read/Write 可能な) 仕組みになっているので、cockroach-2/cockroach-3 にも同じように接続できます。

$ sudo docker exec -it cockroach-1 ./cockroach sql --host=cockroach-2 --insecure
$ sudo docker exec -it cockroach-1 ./cockroach sql --host=cockroach-3 --insecure

上記のように cockroach sql で CockroachDB に接続すると、以下のように SQL を実行できます。

$ sudo docker exec -it cockroach-1 ./cockroach sql --host=cockroach-1 --insecure
#
# Welcome to the CockroachDB SQL shell.
# All statements must be terminated by a semicolon.
# To exit, type: \q.
#
# Server version: CockroachDB CCL v19.2.4 (x86_64-unknown-linux-gnu, built 2020/02/06 21:55:19, go1.12.12) (same version as client)
# Cluster ID: 310436c0-c651-4630-aa45-b6f8c61c8760
#
# Enter \? for a brief introduction.
#
root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> SELECT version();
                                          version                                           
+------------------------------------------------------------------------------------------+
  CockroachDB CCL v19.2.4 (x86_64-unknown-linux-gnu, built 2020/02/06 21:55:19, go1.12.12)  
(1 row)

Time: 579.754µs

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> CREATE TABLE t0 (id INT PRIMARY KEY, c1 INT, c2 INT, c3 INT);
CREATE TABLE

Time: 57.580178ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> INSERT INTO t0 VALUES (1, 111, 222, 333);
INSERT 1

Time: 49.529782ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> SELECT * FROM t0;
  id | c1  | c2  | c3   
+----+-----+-----+-----+
   1 | 111 | 222 | 333  
(1 row)

Time: 2.391171ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> UPDATE t0 SET (c1, c2, c3) = (444, 555, 666) WHERE id = 1;
UPDATE 1

Time: 17.290369ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> SELECT * FROM t0;
  id | c1  | c2  | c3   
+----+-----+-----+-----+
   1 | 444 | 555 | 666  
(1 row)

Time: 2.541162ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> DELETE FROM t0 WHERE id = 1;
DELETE 1

Time: 15.74363ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> SELECT * FROM t0;
  id | c1 | c2 | c3  
+----+----+----+----+
(0 rows)

Time: 2.044183ms

root@cockroach-1:26257/defaultdb>

SQL Shell を終了する場合は、\q を実行します。

root@cockroach-1:26257/defaultdb> \q

Admin UI (Web UI) へ接続 (Insecure クラスタ)

CockroachDB にはいい感じの雰囲気の Admin UI (Web UI) があります。以下の URL (docker run 実行時に指定した port) にアクセスすると、Admin UI 画面を開くことができます。

http://localhost:8080/

00_insecure_admin_ui

Insecure クラスタの削除

Insecure クラスタを削除する場合は、各コンテナ, Docker Network, DB のデータを格納しているディレクト${PWD}/cockroach-data/ をそれぞれ削除します。

1) コンテナを削除

$ sudo docker rm $(sudo docker kill cockroach-1 cockroach-2 cockroach-3)

2) Docker Network を削除

$ sudo docker network rm cockroach-net

3) DB のデータが入ったディレクトリを削除

$ sudo rm -rf ${PWD}/cockroach-data/

Secure クラスタ

公式ドキュメントには Docker を使って Secure クラスタを構築する方法が記載されていないのですが、Start a Local Cluster (Secure)Secure Your Cluster あたりの情報を参考に証明書等を準備すれば、Secure クラスタを構築することが可能です。

Secure クラスタの場合は、事前に各ノード用の証明書等を準備し、それらを格納したディレクトリを各ノード起動時の --certs-dir オプションに指定します。

証明書作成準備

1) Docker ホスト上に、証明書等を格納するディレクトリを作成します。

$ mkdir ${PWD}/cockroach-certs
$ mkdir ${PWD}/cockroach-certs/certs
$ mkdir ${PWD}/cockroach-certs/my-safe-directory

2) 作成した ${PWD}/cockroach-certs をマウントしつつ、証明書を作成するための作業用コンテナを起動します。

$ sudo docker run -it --rm \
--name cockroach-tmp \
--hostname cockroach-tmp \
-v "${PWD}/cockroach-certs:/cockroach/cockroach-certs" \
--entrypoint /bin/bash \
cockroachdb/cockroach:v19.2.4

CA 作成 (作業用コンテナで実施)

1) cockroach cert create-ca コマンドで CA 証明書と鍵を作成します。(ここからは起動した作業用コンテナ内での作業)

root@cockroach-tmp:/cockroach# ./cockroach cert create-ca \
--certs-dir=cockroach-certs/certs \
--ca-key=cockroach-certs/my-safe-directory/ca.key

1匹目用の証明書作成 (作業用コンテナで実施)

1) 1匹目用の証明書を格納するディレクトリを作成します。(引き続きコンテナ内で作業)

root@cockroach-tmp:/cockroach# mkdir cockroach-certs/cockroach-1

2) cockroach cert create-node コマンドで 1匹目用の証明書を作成します。

root@cockroach-tmp:/cockroach# ./cockroach cert create-node \
cockroach-1 \
--certs-dir=cockroach-certs/certs \
--ca-key=cockroach-certs/my-safe-directory/ca.key

3) 作成した証明書を cockroach-certs/cockroach-1/ 配下 (1匹目用のディレクトリ) にコピーします。

root@cockroach-tmp:/cockroach# ls -l cockroach-certs/certs/
total 12
-rw-r--r-- 1 root root 1111 Feb 14 11:10 ca.crt
-rw-r--r-- 1 root root 1147 Feb 14 11:11 node.crt
-rw------- 1 root root 1675 Feb 14 11:11 node.key
root@cockroach-tmp:/cockroach# cp cockroach-certs/certs/* cockroach-certs/cockroach-1/
root@cockroach-tmp:/cockroach# ls -l cockroach-certs/cockroach-1/
total 12
-rw-r--r-- 1 root root 1111 Feb 14 11:12 ca.crt
-rw-r--r-- 1 root root 1147 Feb 14 11:12 node.crt
-rw------- 1 root root 1675 Feb 14 11:12 node.key

2匹目用の証明書作成 (作業用コンテナで実施)

1) 2匹目用の証明書を格納するディレクトリを作成します。(引き続きコンテナ内で作業)

root@cockroach-tmp:/cockroach# mkdir cockroach-certs/cockroach-2

2) cockroach cert create-node コマンドで 2匹目用の証明書を作成します (1匹目用の証明書が残っているとエラーになるので --overwrite オプションをつけて実行します)。

root@cockroach-tmp:/cockroach# ./cockroach cert create-node \
cockroach-2 \
--certs-dir=cockroach-certs/certs \
--ca-key=cockroach-certs/my-safe-directory/ca.key \
--overwrite

3) 作成した証明書を cockroach-certs/cockroach-2/ 配下 (2匹目用のディレクトリ) にコピーします。

root@cockroach-tmp:/cockroach# ls -l cockroach-certs/certs/
total 12
-rw-r--r-- 1 root root 1111 Feb 14 11:10 ca.crt
-rw-r--r-- 1 root root 1147 Feb 14 11:19 node.crt
-rw------- 1 root root 1675 Feb 14 11:19 node.key
root@cockroach-tmp:/cockroach# cp cockroach-certs/certs/* cockroach-certs/cockroach-2/
root@cockroach-tmp:/cockroach# ls -l cockroach-certs/cockroach-2/
total 12
-rw-r--r-- 1 root root 1111 Feb 14 11:19 ca.crt
-rw-r--r-- 1 root root 1147 Feb 14 11:19 node.crt
-rw------- 1 root root 1675 Feb 14 11:19 node.key

3匹目用の証明書作成 (作業用コンテナで実施)

1) 3匹目用の証明書を格納するディレクトリを作成します。(引き続きコンテナ内で作業)

root@cockroach-tmp:/cockroach# mkdir cockroach-certs/cockroach-3

2) cockroach cert create-node コマンドで 3匹目用の証明書を作成します (2匹目用の証明書が残っているとエラーになるので --overwrite オプションをつけて実行します)。

root@cockroach-tmp:/cockroach# ./cockroach cert create-node \
cockroach-3 \
--certs-dir=cockroach-certs/certs \
--ca-key=cockroach-certs/my-safe-directory/ca.key \
--overwrite

3) 作成した証明書を cockroach-certs/cockroach-3/ 配下 (3匹目用のディレクトリ) にコピーします。

root@cockroach-tmp:/cockroach# ls -l cockroach-certs/certs/
total 12
-rw-r--r-- 1 root root 1111 Feb 14 11:10 ca.crt
-rw-r--r-- 1 root root 1147 Feb 14 11:21 node.crt
-rw------- 1 root root 1679 Feb 14 11:21 node.key
root@cockroach-tmp:/cockroach# cp cockroach-certs/certs/* cockroach-certs/cockroach-3/
root@cockroach-tmp:/cockroach# ls -l cockroach-certs/cockroach-3/
total 12
-rw-r--r-- 1 root root 1111 Feb 14 11:21 ca.crt
-rw-r--r-- 1 root root 1147 Feb 14 11:21 node.crt
-rw------- 1 root root 1679 Feb 14 11:21 node.key

root ユーザ用のクライアント証明書作成 (作業用コンテナで実施)

1) root ユーザ用のクライアント証明書を格納するディレクトリを作成します。(引き続きコンテナ内で作業)

root@cockroach-tmp:/cockroach# mkdir cockroach-certs/client-root

2) cockroach cert create-client コマンドで root ユーザ用のクライアント証明書を作成します。

root@cockroach-tmp:/cockroach# ./cockroach cert create-client \
root \
--certs-dir=cockroach-certs/certs \
--ca-key=cockroach-certs/my-safe-directory/ca.key

3) 作成した証明書を cockroach-certs/client-root/ 配下 (root ユーザ用のディレクトリ) にコピーします。証明書認証で DB に接続する際には、ca.crtnode.key も必要になるため、一緒にコピーします (厳密には node.crt は不要です)。

root@cockroach-tmp:/cockroach# ls -l cockroach-certs/certs/
total 20
-rw-r--r-- 1 root root 1111 Feb 14 11:10 ca.crt
-rw-r--r-- 1 root root 1103 Feb 14 11:22 client.root.crt
-rw------- 1 root root 1679 Feb 14 11:22 client.root.key
-rw-r--r-- 1 root root 1147 Feb 14 11:21 node.crt
-rw------- 1 root root 1679 Feb 14 11:21 node.key
root@cockroach-tmp:/cockroach# cp cockroach-certs/certs/* cockroach-certs/client-root/
root@cockroach-tmp:/cockroach# ls -l cockroach-certs/client-root/
total 20
-rw-r--r-- 1 root root 1111 Feb 14 11:23 ca.crt
-rw-r--r-- 1 root root 1103 Feb 14 11:23 client.root.crt
-rw------- 1 root root 1679 Feb 14 11:23 client.root.key
-rw-r--r-- 1 root root 1147 Feb 14 11:23 node.crt
-rw------- 1 root root 1679 Feb 14 11:23 node.key

証明書の確認

1) 作業用コンテナで実行していた bash を終了します。(以降は Docker ホスト側での作業)

root@cockroach-tmp:/cockroach# exit

2) Docker ホスト側で、${PWD}/cockroach-certs/ 配下に作成した各証明書が存在してることを確認しておきます。

$ ls -lR ${PWD}/cockroach-certs/
/cockroach/cockroach-certs/:
total 24
drwxr-xr-x 2 ubuntu ubuntu 4096 Feb 14 20:22 certs
drwxr-xr-x 2 root   root   4096 Feb 14 20:23 client-root
drwxr-xr-x 2 root   root   4096 Feb 14 20:12 cockroach-1
drwxr-xr-x 2 root   root   4096 Feb 14 20:19 cockroach-2
drwxr-xr-x 2 root   root   4096 Feb 14 20:21 cockroach-3
drwxr-xr-x 2 ubuntu ubuntu 4096 Feb 14 20:10 my-safe-directory

/cockroach/cockroach-certs/certs:
total 20
-rw-r--r-- 1 root root 1111 Feb 14 20:10 ca.crt
-rw-r--r-- 1 root root 1103 Feb 14 20:22 client.root.crt
-rw------- 1 root root 1679 Feb 14 20:22 client.root.key
-rw-r--r-- 1 root root 1147 Feb 14 20:21 node.crt
-rw------- 1 root root 1679 Feb 14 20:21 node.key

/cockroach/cockroach-certs/client-root:
total 20
-rw-r--r-- 1 root root 1111 Feb 14 20:23 ca.crt
-rw-r--r-- 1 root root 1103 Feb 14 20:23 client.root.crt
-rw------- 1 root root 1679 Feb 14 20:23 client.root.key
-rw-r--r-- 1 root root 1147 Feb 14 20:23 node.crt
-rw------- 1 root root 1679 Feb 14 20:23 node.key

/cockroach/cockroach-certs/cockroach-1:
total 12
-rw-r--r-- 1 root root 1111 Feb 14 20:12 ca.crt
-rw-r--r-- 1 root root 1147 Feb 14 20:12 node.crt
-rw------- 1 root root 1675 Feb 14 20:12 node.key

/cockroach/cockroach-certs/cockroach-2:
total 12
-rw-r--r-- 1 root root 1111 Feb 14 20:19 ca.crt
-rw-r--r-- 1 root root 1147 Feb 14 20:19 node.crt
-rw------- 1 root root 1675 Feb 14 20:19 node.key

/cockroach/cockroach-certs/cockroach-3:
total 12
-rw-r--r-- 1 root root 1111 Feb 14 20:21 ca.crt
-rw-r--r-- 1 root root 1147 Feb 14 20:21 node.crt
-rw------- 1 root root 1679 Feb 14 20:21 node.key

/cockroach/cockroach-certs/my-safe-directory:
total 4
-rw------- 1 root root 1675 Feb 14 20:10 ca.key

事前に必要な証明書作成作業はここまでです。

Secure クラスタの構築

それでは、作成した証明書を利用してクラスタを構築 (各ノードを起動) します。

1) 各ノード (コンテナ) を接続させる Docker Network を作成します。

$ sudo docker network create cockroach-net

2) 作成した Docker Network を指定し、1匹目を起動します。この時、先程作成した 1匹目用の証明書が格納されたディレクトリと、root 用のクライアント証明書が格納されたディレクトリをそれぞれマウントします。

また、Secure クラスタの場合は --insecure オプションの代わりに、--certs-dir オプションを指定 (ノード用の証明書が格納さていディレクトリを指定) します。

$ sudo docker run -d \
--name=cockroach-1 \
--hostname=cockroach-1 \
--net=cockroach-net \
-p 26257:26257 -p 8080:8080 \
-v "${PWD}/cockroach-data/cockroach-1:/cockroach/cockroach-data" \
-v "${PWD}/cockroach-certs/cockroach-1:/cockroach/certs" \
-v "${PWD}/cockroach-certs/client-root:/cockroach/client-certs" \
cockroachdb/cockroach:v19.2.4 start \
--certs-dir=certs \
--join=cockroach-1,cockroach-2,cockroach-3

3) 同様に、2匹目/3匹目もそれぞれの証明書が格納されたディレクトリをマウントして起動します。

$ sudo docker run -d \
--name=cockroach-2 \
--hostname=cockroach-2 \
--net=cockroach-net \
-v "${PWD}/cockroach-data/cockroach-2:/cockroach/cockroach-data" \
-v "${PWD}/cockroach-certs/cockroach-2:/cockroach/certs" \
-v "${PWD}/cockroach-certs/client-root:/cockroach/client-certs" \
cockroachdb/cockroach:v19.2.4 start \
--certs-dir=certs \
--join=cockroach-1,cockroach-2,cockroach-3
$ sudo docker run -d \
--name=cockroach-3 \
--hostname=cockroach-3 \
--net=cockroach-net \
-v "${PWD}/cockroach-data/cockroach-3:/cockroach/cockroach-data" \
-v "${PWD}/cockroach-certs/cockroach-3:/cockroach/certs" \
-v "${PWD}/cockroach-certs/client-root:/cockroach/client-certs" \
cockroachdb/cockroach:v19.2.4 start \
--certs-dir=certs \
--join=cockroach-1,cockroach-2,cockroach-3

4) 最後に cockroach init コマンドでクラスタを初期化します。こちらのコマンドにも (--insecure オプションの代わりに) --certs-dir オプションを指定 (root ユーザ用のクライアント証明書を指定) します。

$ sudo docker exec -it cockroach-1 ./cockroach init --host=cockroach-1 --certs-dir=client-certs

これで、Secure クラスタの構築は完了です。cockroach node status コマンドでクラスタが構築されていることを確認できます。

$ sudo docker exec -it cockroach-1 ./cockroach node status --host=cockroach-1 --certs-dir=client-certs
  id |      address      |    sql_address    |  build  |            started_at            |            updated_at            | locality | is_available | is_live  
+----+-------------------+-------------------+---------+----------------------------------+----------------------------------+----------+--------------+---------+
   1 | cockroach-1:26257 | cockroach-1:26257 | v19.2.4 | 2020-02-14 11:25:37.328938+00:00 | 2020-02-14 11:25:46.396334+00:00 |          | true         | true     
   2 | cockroach-2:26257 | cockroach-2:26257 | v19.2.4 | 2020-02-14 11:25:37.681192+00:00 | 2020-02-14 11:25:42.226629+00:00 |          | true         | true     
   3 | cockroach-3:26257 | cockroach-3:26257 | v19.2.4 | 2020-02-14 11:25:38.283192+00:00 | 2020-02-14 11:25:42.827082+00:00 |          | true         | true     
(3 rows)

Built-in SQL Shell を使って Secure クラスタに接続

Secure クラスタ構築後、cockroach sql コマンド (Built-in SQL Shell) を使って、CockroachDB に接続できます (cockroach sql にも --certs-dir オプションを指定します)。

前述した通り、CockroachDB クラスタ内の各ノードには Master/Standby や Coordinator/Worker のような役割分担はないので、cockroach-1/cockroach-2/cockroach-3 いずれのノードに接続しても Read/Write のクエリを実行できます。

$ sudo docker exec -it cockroach-1 ./cockroach sql --host=cockroach-1 --certs-dir=client-certs
$ sudo docker exec -it cockroach-1 ./cockroach sql --host=cockroach-2 --certs-dir=client-certs
$ sudo docker exec -it cockroach-1 ./cockroach sql --host=cockroach-3 --certs-dir=client-certs

上記のように cockroach sql で CockroachDB に接続すると、以下のように SQL を実行できます。

$ sudo docker exec -it cockroach-1 ./cockroach sql --host=cockroach-1 --certs-dir=client-certs
#
# Welcome to the CockroachDB SQL shell.
# All statements must be terminated by a semicolon.
# To exit, type: \q.
#
# Server version: CockroachDB CCL v19.2.4 (x86_64-unknown-linux-gnu, built 2020/02/06 21:55:19, go1.12.12) (same version as client)
# Cluster ID: 7e9b40b9-1241-4066-9e94-a56c7700ae7a
#
# Enter \? for a brief introduction.
#
root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> SELECT version();
                                          version                                           
+------------------------------------------------------------------------------------------+
  CockroachDB CCL v19.2.4 (x86_64-unknown-linux-gnu, built 2020/02/06 21:55:19, go1.12.12)  
(1 row)

Time: 644.658µs

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> CREATE TABLE t0 (id INT PRIMARY KEY, c1 INT, c2 INT, c3 INT);
CREATE TABLE

Time: 57.624205ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> INSERT INTO t0 VALUES (1, 111, 222, 333);
INSERT 1

Time: 46.974669ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> SELECT * FROM t0;
  id | c1  | c2  | c3   
+----+-----+-----+-----+
   1 | 111 | 222 | 333  
(1 row)

Time: 2.4231ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> UPDATE t0 SET (c1, c2, c3) = (444, 555, 666) WHERE id = 1;
UPDATE 1

Time: 18.007125ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> SELECT * FROM t0;
  id | c1  | c2  | c3   
+----+-----+-----+-----+
   1 | 444 | 555 | 666  
(1 row)

Time: 2.303627ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> DELETE FROM t0 WHERE id = 1;
DELETE 1

Time: 17.040103ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> SELECT * FROM t0;
  id | c1 | c2 | c3  
+----+----+----+----+
(0 rows)

Time: 2.172402ms

root@cockroach-1:26257/defaultdb>

SQL Shell を終了する場合は、\q を実行します。

root@cockroach-1:26257/defaultdb> \q

Admin UI (Web UI) へ接続 (Secure クラスタ)

Secure クラスタの場合 は、(Insecure クラスタと違って) Admin UI に接続する際にパスワード認証が必要です。

また、現時点 (v19.2.4 時点) では root ユーザで Admin UI にログインすることができないので、Admin UI にログインするための一般ユーザ (非 root ユーザ) を作成する必要があります。

1) まずは、cockroach sql コマンドで (root ユーザで) DB に接続します。

$ sudo docker exec -it cockroach-1 ./cockroach sql --host=cockroach-1 --certs-dir=client-certs

2) 次に、Admin UI 接続用の一般ユーザを作成します。

root@cockroach-1:26257/defaultdb> CREATE USER cockroach_user WITH PASSWORD 'cockroach_pass';

3) 一般ユーザ (roach_user) が作成されていることを確認し、SQL Shell を終了します。

root@cockroach-1:26257/defaultdb> SHOW USERS;
    user_name     
+----------------+
  cockroach_user  
  root            
(2 rows)

Time: 1.590451ms

root@cockroach-1:26257/defaultdb>
root@cockroach-1:26257/defaultdb> \q

4) 一般ユーザ作成後、以下の URL (docker run 実行時に指定した port) にアクセスします。

https://localhost:8080/

5) すると、ログイン画面が表示されるので、先程作成したユーザ/パスワード (cockroach_user/cockroach_pass) でログインします。

01_secure_admin_ui_login

6) パスワード認証に成功すると、以下のような Admin UI が表示されます。

02_secure_admin_ui

7) ログアウトする場合は、画面左下の "LGO OUT" ボタンを選択します。

03_secure_admin_ui_logout

Secure クラスタの削除

Secure クラスタを削除する場合は、各コンテナ, Docker Network, 証明書を格納しているディレクト${PWD}/cockroach-certs/, DB のデータを格納しているディレクト${PWD}/cockroach-data/ をそれぞれ削除します。

1) コンテナを削除

$ sudo docker rm $(sudo docker kill cockroach-1 cockroach-2 cockroach-3)

2) Docker Network を削除

$ sudo docker network rm cockroach-net

3) 証明書が入ったディレクトリを削除

$ sudo rm -rf ${PWD}/cockroach-certs/

4) DB のデータが入ったディレクトリを削除

$ sudo rm -rf ${PWD}/cockroach-data/

まとめ

分散 DB である CockroachDB ですが、Docker を使うとローカル (単一ホスト) 上でクラスタを構築できました。