CentOS7 / MongoDBでシャーディング

今更というネタでもありますが物理的に3台のマシンを使ってMongoDBのクラスタを組んだのでメモ
シャーディングとレプリカセットの組み合わせです

## 各ノードのIPアドレスとポートの例

### IPアドレス

|ノード名| db1 | db2 | db3 |
|:-:|:-:|:-:|:-:|
| IPアドレス | 192.168.0.11  | 192.168.0.12  | 192.168.0.13  |



### 各サービス毎の利用ポート

|ノード名| db1 | db2 | db3 |
|:-:|:-:|:-:|:-:|
| config  | 27018  | 27018  | 27018  |
| mongos  | 27017  | 27017  | 27017  |
| shard1  | 27021  | 27021  | 27021* |
| shard2  | 27022* | 27022  | 27022  |
| shard3  | 27023  | 27023* | 27023 |

- レプリカセットのshard(x)の後ろに*があるのはArbiter(実データは持たずプライマリノードの選挙権だけを持つ)の意味
- 各ノードではconfig/shard1/shard2/shard3の4つのmongodプロセスとmongosプロセスが1つ動いてることになる
- クライアントアプリを各ノードのローカルに配布した場合、mongodb://localhost:27017/your_database で接続できる
- **configサーバのレプリカセットにArbiterは存在できない**




## 3台で同じ設定すんのめんどくせー

Ansibleで自動化したり、ディスクや仮想マシンのコピーができるクラウドサービスとか使うと捗ります
私もとあるイベント行ったときにもらったさくらのクラウドの無料枠でやりました



## OS側の設定

### MongoDBのインストール
[この辺](http://qiita.com/khirose/items/14a2aea0a96564c9c74c)を参考にしてください(自演)


### /etc/hostsの設定

IP直打ちだとノードがお亡くなりになったときに困ることも出てくるのでhostsで名前で引けるように追記して設定

```text:/etc/hosts
192.168.0.11 db1
192.168.0.12 db2
192.168.0.13 db3
```


### firewalldの設定
各ノード同士で接続できるようにポートをあけます
今回はスイッチの下で直接インターネットにはつながないマシンなのでガバガバ設定

```bash
firewall-cmd --permanent  --zone=public --add-port=27017/tcp
firewall-cmd --permanent  --zone=public --add-port=27018/tcp
firewall-cmd --permanent  --zone=public --add-port=27021/tcp
firewall-cmd --permanent  --zone=public --add-port=27022/tcp
firewall-cmd --permanent  --zone=public --add-port=27023/tcp
firewall-cmd --reload
```


## データ領域用のレプリカセットを作成する

### データディレクトリの作成

デフォルトの場所とはちょっと違うところに各シャード用のデータベースのディレクトリを掘っておきます
プロセスを実行するユーザ mongod をオーナーにしてください

```bash
mkdir /data
mkdir /data/log
mkdir /data/mongoc
mkdir /data/shard1
mkdir /data/shard2
mkdir /data/shard3

chown mongod:mongod /data/log
chown mongod:mongod /data/mongoc
chown mongod:mongod /data/shard1
chown mongod:mongod /data/shard2
chown mongod:mongod /data/shard3

```

### pidファイル置き場の作成

既にあれば不要

```bash
mkdir /var/run/mongodb
chown mongod:mongod /var/run/mongodb
```

### データ領域用の設定ファイル

せっかくのMongoDB 3.2なのでWiredTigerで作ります
1つ作ってしまえばあとはコピペでほぼ大丈夫
各パラメータ(特にcacheSizeGBあたり)はサーバスペックに合わせて調整が必要

```yaml:/etc/shard1.conf
# shard1.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /data/log/shard1.log
  logRotate: rename

# Where and how to store data.
storage:
  dbPath: "/data/shard1"
  engine: wiredTiger
  directoryPerDB: true
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
      statisticsLogDelaySecs: 0
    collectionConfig:
      blockCompressor: "snappy"
    indexConfig:
      prefixCompression: true
  journal:
    enabled: true

# how the process runs
processManagement:
  fork: true  # fork and run in background
  pidFilePath: /var/run/mongodb/shard1.pid  # location of pidfile

# network interfaces
net:
  port: 27021

#security:

#operationProfiling:

replication:
   replSetName: "shard1"

sharding:
  clusterRole: "shardsvr"

## Enterprise-Only Options

#auditLog:

#snmp:
```


```yaml:/etc/shard2.conf
# shard2.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /data/log/shard2.log
  logRotate: rename

# Where and how to store data.
storage:
  dbPath: "/data/shard2"
  engine: wiredTiger
  directoryPerDB: true
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
      statisticsLogDelaySecs: 0
    collectionConfig:
      blockCompressor: "snappy"
    indexConfig:
      prefixCompression: true
  journal:
    enabled: true

# how the process runs
processManagement:
  fork: true  # fork and run in background
  pidFilePath: /var/run/mongodb/shard2.pid  # location of pidfile

# network interfaces
net:
  port: 27022

#security:

#operationProfiling:

replication:
   replSetName: "shard2"

sharding:
  clusterRole: "shardsvr"

## Enterprise-Only Options

#auditLog:

#snmp:
```

```yaml:/etc/shard3.conf
# shard3.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /data/log/shard3.log
  logRotate: rename

# Where and how to store data.
storage:
  dbPath: "/data/shard3"
  engine: wiredTiger
  directoryPerDB: true
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
      statisticsLogDelaySecs: 0
    collectionConfig:
      blockCompressor: "snappy"
    indexConfig:
      prefixCompression: true
  journal:
    enabled: true

# how the process runs
processManagement:
  fork: true  # fork and run in background
  pidFilePath: /var/run/mongodb/shard3.pid  # location of pidfile

# network interfaces
net:
  port: 27023

#security:

#operationProfiling:

replication:
   replSetName: "shard3"

sharding:
  clusterRole: "shardsvr"

## Enterprise-Only Options

#auditLog:

#snmp:
```

### systemdの設定

yumでインストールするとCentOS7の場合でも/etc/init.d/*なスクリプトを経由することになるようなのですが、ここはCentOS7とsystemdの流儀に従ってsystemdのサービスを定義してみます
これも1個作ってコピペしてましたがスクリプト化するのがいちばん手っ取り早いかもしれない

```ini:/lib/systemd/system/shard1.service
[Unit]
Description=High-performance, schema-free document-oriented database

[Service]
User=mongod
Group=mongod
Environment="OPTIONS=--quiet -f /etc/shard1.conf"
ExecStart=/usr/bin/mongod $OPTIONS
PIDFile=/var/run/mongodb/shard1.pid

[Install]
WantedBy=multi-user.target
```


```ini:/lib/systemd/system/shard2.service
[Unit]
Description=High-performance, schema-free document-oriented database

[Service]
User=mongod
Group=mongod
Environment="OPTIONS=--quiet -f /etc/shard2.conf"
ExecStart=/usr/bin/mongod $OPTIONS
PIDFile=/var/run/mongodb/shard2.pid

[Install]
WantedBy=multi-user.target
```


```ini:/lib/systemd/system/shard3.service
[Unit]
Description=High-performance, schema-free document-oriented database

[Service]
User=mongod
Group=mongod
Environment="OPTIONS=--quiet -f /etc/shard3.conf"
ExecStart=/usr/bin/mongod $OPTIONS
PIDFile=/var/run/mongodb/shard3.pid

[Install]
WantedBy=multi-user.target
```

設定が終わったら

```bash
systemctl daemon-reload
```

で設定ファイルの更新を反映させて起動

```bash
systemctl start shard1.service
systemctl start shard2.service
systemctl start shard3.service
```

### データ領域のレプリカセット

各レプリカセットをMongoDB上で設定します

```bash:shard1用のレプリカセット
mongo db1:27021

shard1 > config = {
_id: 'shard1',
members: [
  {_id: 0, host: 'db1:27021'},
  {_id: 1, host: 'db2:27021'},
  {_id: 2, host: 'db3:27021', arbiterOnly: true}
]};

shard1 > rs.initiate(config)
```


```bash:shard2用のレプリカセット
mongo db2:27022

shard2 > config = {
_id: 'shard2',
members: [
  {_id: 0, host: 'db2:27022'},
  {_id: 1, host: 'db3:27022'},
  {_id: 2, host: 'db1:27022', arbiterOnly: true}
]};

shard2 > rs.initiate(config)
```

```bash:shard3用のレプリカセット
mongo db3:27023

shard3 > config = {
_id: 'shard3',
members: [
  {_id: 0, host: 'db3:27023'},
  {_id: 1, host: 'db1:27023'},
  {_id: 2, host: 'db2:27023', arbiterOnly: true}
]};

shard3 > rs.initiate(config)
```

データ置き場はこれで3つのパーティション、各パーティションはPrimaryとSecondary2台のディスク+Arbiterの構成になりました


## configサーバの設定

configサーバ用の設定ファイルを作成して

```yaml:/etc/mongoc.conf
# mongoc.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /data/log/mongoc.log

# Where and how to store data.
storage:
  dbPath: /data/mongoc
  engine: wiredTiger
  directoryPerDB: true
  wiredTiger:
    engineConfig:
      cacheSizeGB: 1
      directoryForIndexes: true
      statisticsLogDelaySecs: 0
    collectionConfig:
      blockCompressor: "snappy"
    indexConfig:
      prefixCompression: true
  journal:
    enabled: true

# how the process runs
processManagement:
  fork: true  # fork and run in background
  pidFilePath: /var/run/mongodb/mongoc.pid  # location of pidfile

# network interfaces
net:
  port: 27018

#security:

#operationProfiling:

replication:
   oplogSizeMB: 64
   replSetName: mongoc

sharding:
  clusterRole: configsvr

## Enterprise-Only Options

#auditLog:

#snmp:
```

サービス設定を書いて

```ini:/lib/systemd/system/mongoc.service
[Unit]
Description=High-performance, schema-free document-oriented database

[Service]
User=mongod
Group=mongod
Environment="OPTIONS=--quiet -f /etc/mongoc.conf"
ExecStart=/usr/bin/mongod $OPTIONS
PIDFile=/var/run/mongodb/mongoc.pid

[Install]
WantedBy=multi-user.target
```
systemdに認識させてプロセスを起動

```bash
systemctl daemon-reload
systemctl start mongoc.service
```

次にレプリカセットを構成

```bash:configサーバ用のレプリカセット
mongo db1:27018

mongoc > config = {
_id: 'mongoc',
configsvr: true,
members: [
  {_id: 0, host: 'db1:27018'},
  {_id: 1, host: 'db2:27018'},
  {_id: 2, host: 'db3:27018'}
]};

mongoc > rs.initiate(config)
```
**configsvr: true** を指定しているのがミソ
私はこれを忘れて小一時間ハマってました


## ルータ(mongos)
基本手順は同じ
ただプロセスの実行ファイル名がmongodではなくmongosになったりmongos自体は実データを持たないプロセスなのでstorageの設定が不要、代わりにconfigDBで設定を持つレプリカセットを指定するという設定ファイルの違いがある

```yaml:/etc/mongos.conf
# mongos.conf

# for documentation of all options, see:
#   http://docs.mongodb.org/manual/reference/configuration-options/

# where to write logging data.
systemLog:
  destination: file
  logAppend: true
  path: /data/log/mongos.log

# how the process runs
processManagement:
  fork: true  # fork and run in background
  pidFilePath: /var/run/mongodb/mongos.pid  # location of pidfile

# network interfaces
net:
  port: 27017

#security:

#operationProfiling:

replication:
  localPingThresholdMs: 15

sharding:
  autoSplit: true
  configDB: mongoc/db1:27018,db2:27018,db3:27018

## Enterprise-Only Options

#auditLog:

#snmp:
```




```ini:/lib/systemd/system/mongos.service
[Unit]
Description=High-performance, schema-free document-oriented database

[Service]
User=mongod
Group=mongod
Environment="OPTIONS=--quiet -f /etc/mongos.conf"
ExecStart=/usr/bin/mongos $OPTIONS
PIDFile=/var/run/mongodb/mongos.pid

[Install]
WantedBy=multi-user.target
```
そして起動

```bash
systemctl daemon-reload
systemctl start mongos.service
```

エラーがなければこれで構成までは終わり


## データを実際にシャーディング環境に載せるには

どっかの端末でmongosに接続する
今回の設定例ならシェルから

```bash
mongo
```

だけでつながるはず
プロンプトが

```bash
mongos>
```

になってれば正解

### データベースをシャーディング環境に教える

```bash
mongos> sh.enableSharding('dbname')
```

### その上でコレクションを指定してシャーディングさせる

どのシャードに格納するかを決定させるキーを指定すること
キーは予めインデックスを作っておいてください

```bash
mongos> sh.shardCollection('collection-name', { someIndex : 1 })
```
















## 自動起動にも対応するなら

```bash
systemctl enable shard1.service
systemctl enable shard2.service
systemctl enable shard3.service
systemctl enable mongoc.service
systemctl enable mongos.service
```