re:annkara

日々学んだことを書き留めておく。

redis-cli を弄って単一の Redis クラスターを構築する

ほとんどないとは思うが*1、シングル構成のRedisクラスタが欲しくなった際に、以下のようにコマンドを実行してもエラーが発生する。

annkara@annkara:~/src/redis-5.0.5/src$ ./redis-cli --cluster create 127.0.0.1:6379
[ERR] Wrong number of arguments for specified --cluster sub command

どうやら、redis-cli コマンド内で引数のチェックをしているようなので、チェックを回避するようにして単一マスターノードのクラスタが構築できるか試してみる。

対象の Redis のバージョンは 5.0.5 を利用しており、ソースコードは公式サイトからダウンロードしてきている。

redis.io

とりあえず、エラーメッセージを出力している個所を探してみる。

2050 static clusterManagerCommandProc *validateClusterManagerCommand(void) {
2051     int i, commands_count = sizeof(clusterManagerCommands) /
2052                             sizeof(clusterManagerCommandDef);
2053     clusterManagerCommandProc *proc = NULL;
2054     char *cmdname = config.cluster_manager_command.name;
2055     int argc = config.cluster_manager_command.argc;
2056     for (i = 0; i < commands_count; i++) {
2057         clusterManagerCommandDef cmddef = clusterManagerCommands[i];
2058         if (!strcmp(cmddef.name, cmdname)) {
2059             if ((cmddef.arity > 0 && argc != cmddef.arity) ||
2060                 (cmddef.arity < 0 && argc < (cmddef.arity * -1))) {
2061                 fprintf(stderr, "[ERR] Wrong number of arguments for "
2062                                 "specified --cluster sub command\n");
2063                 return NULL;
2064             }
2065             proc = cmddef.proc;
2066         }
2067     }
2068     if (!proc) fprintf(stderr, "Unknown --cluster subcommand\n");
2069     return proc;
2070 }

2059行目か2060行目のチェックに該当してエラーメッセージが出力されていそう。
チェック処理についてもう少し理解を深めるために、clusterManagerCommandDef型を見てみる。

2006 typedef struct clusterManagerCommandDef {
2007     char *name;
2008     clusterManagerCommandProc *proc;
2009     int arity;
2010     char *args;
2011     char *options;
2012 } clusterManagerCommandDef;
2013
2014 clusterManagerCommandDef clusterManagerCommands[] = {
2015     {"create", clusterManagerCommandCreate, -2, "host1:port1 ... hostN:portN",
2016      "replicas <arg>"},
2017     {"check", clusterManagerCommandCheck, -1, "host:port",
2018      "search-multiple-owners"},
2019     {"info", clusterManagerCommandInfo, -1, "host:port", NULL},
2020     {"fix", clusterManagerCommandFix, -1, "host:port",
2021      "search-multiple-owners"},
2022     {"reshard", clusterManagerCommandReshard, -1, "host:port",
2023      "from <arg>,to <arg>,slots <arg>,yes,timeout <arg>,pipeline <arg>,"
2024      "replace"},
2025     {"rebalance", clusterManagerCommandRebalance, -1, "host:port",
2026      "weight <node1=w1...nodeN=wN>,use-empty-masters,"
2027      "timeout <arg>,simulate,pipeline <arg>,threshold <arg>,replace"},
2028     {"add-node", clusterManagerCommandAddNode, 2,
2029      "new_host:new_port existing_host:existing_port", "slave,master-id <arg>"},
2030     {"del-node", clusterManagerCommandDeleteNode, 2, "host:port node_id",NULL},
2031     {"call", clusterManagerCommandCall, -2,
2032         "host:port command arg arg .. arg", NULL},
2033     {"set-timeout", clusterManagerCommandSetTimeout, 2,
2034      "host:port milliseconds", NULL},
2035     {"import", clusterManagerCommandImport, 1, "host:port",
2036      "from <arg>,copy,replace"},
2037     {"help", clusterManagerCommandHelp, 0, NULL, NULL}
2038 };

clusterManagerCommandDef は構造体で、2014行目で定義されている clusterManagerCommands 配列を見てみると、Redis クラスタを操作するコマンド名や関数ポインタ、引数の数などを保持するための構造体であることがわかる。

arity という英単語を初めて見たのだけど、関数が受け取る引数の数の事を言うらしい。今回は create コマンドなので、-2 となっている。
改めて条件文にもどってみると、2060行目のチェック処理に該当することがわかる。 つまり、Redis クラスタを構成する際には、3台以上のノードの IP:PORT 番号が必要となる。クラスタと言っているので当たり前といえば当たり前だが。。。

## redis-cli --cluster create 127.0.0.1:6379 のコマンドを実行するとき
## cmddef.arity = -2
## argc = 1 (create コマンドに渡される引数の数)2055行目の int argc = config.cluster_manager_command.argc の値
2059 if ((cmddef.arity > 0 && argc != cmddef.arity) ||
2060     (cmddef.arity < 0 && argc < (cmddef.arity * -1))) {

なので、1ノードの設定でも通せるように修正し、もう一度コンパイルして、コマンドを実行してみる。

annkara@annkara:~/src/redis-5.0.5/src$ ./redis-cli --cluster create 127.0.0.1:6379
*** ERROR: Invalid configuration for cluster creation.
*** Redis Cluster requires at least 3 master nodes.
*** This is not possible with 1 nodes and 0 replicas per node.
*** At least 3 nodes are required.

Redis クラスタは最低3台のマスターノードが必要だと至極まっとうなエラーが返される。
どうやら他にもチェックをしている個所があるらしい。。。

4578 /* Cluster Manager Commands */
4579
4580 static int clusterManagerCommandCreate(int argc, char **argv) {

        /* 省略 */

4628     if (masters_count < 3) {
4629         clusterManagerLogErr(
4630             "*** ERROR: Invalid configuration for cluster creation.\n"
4631             "*** Redis Cluster requires at least 3 master nodes.\n"
4632             "*** This is not possible with %d nodes and %d replicas per node.",
4633             node_len, replicas);
4634         clusterManagerLogErr("\n*** At least %d nodes are required.\n",
4635                              3 * (replicas + 1));
4636         return 0;
4637     }

clusterManagerCommandCreate関数できっちりとマスターノード台数のチェックがされているのでこれも回避する。

リトライするととりあえず成功する。

annkara@annkara:~/src/redis-5.0.5/src$ ./redis-cli --cluster create 127.0.0.1:6379
>>> Performing hash slots allocation on 1 nodes...
Master[0] -> Slots 0 - 16383
M: c4ba607158e6288e4a07e19f748dc210be0cd1fc 127.0.0.1:6379
   slots:[0-16383] (16384 slots) master
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join

>>> Performing Cluster Check (using node 127.0.0.1:6379)
M: c4ba607158e6288e4a07e19f748dc210be0cd1fc 127.0.0.1:6379
   slots:[0-16383] (16384 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

値の設定・取得くらいの簡単な確認しかしてないけど、とりあえず単一マスターのRedisクラスタを構築することができた。

結局自分の環境では3台のマスターノードを準備して、クラスタ構成を構築したので実際には運用してないけど、とりあえずのメモ書きとして残しておく。

*1:私の場合はRedisクライアントがRedisクラスタしか対応しておらず、かつ検証環境でもとりあえず稼働するRedisクラスタがあればよいという状況だった。