はじめに
オンプレミス環境では、システムを構築する前にサーバー調達から物理作業・OSインストールが完了しないと構築作業が進めないのですが、クラウド環境では、ボタンを押すだけでネットワークからサーバーまで短時間で準備できます。そして、最近の「Infrastructure as Code」で、ボタンも押す必要がなくなりました。設定ファイルを記述することでサーバーやネットワーク構成が自動的にプロビージョンニングされるようになり、コマンドを実行するだけで「インフラストラクチャー」ができあがってしまいます。
個人的にはこれを見てすごく興味深いので、MongoDBクラスタをコード実行で構築できるようにしたくやってみました。ちょうど@mosuke5さんのterraform
レポジトリのサンプルがあったので、サンプルを見ながらやってみました。
構成
Mongodbのレプリケーション構成では、最低レプリカ3台が必要です。そして、可用性を担保するため、それぞれのレプリカが別のスイッチに刺さる必要があります。そのため、Alibabaクラウド上では、以下の図どおりのコンポネントを作ります
コンポーネント
- VPCネットワーク。CIDR: 10.0.0.0/16
- それぞれのレプリカのプライベートネットワーク
- 10.0.0.0/19: プライマリレプリカ
- 10.0.32.0/19: セカンダリレプリカ
- 10.0.64.0/19: セカンダリレプリカ
- mongoクラスタ用のECSインスタンス
- テストのため、1CPU-1GBメモリを使います
- mongoクラスタのECSはグローバルIPを持つと外からデータストアへアクセスができてしまうため、プライベートIPしか付与しません。ソフトウェア更新やmongoインストールするために、それぞれのゾーンにSNATゲートウェイを構築します。SNATのECSがmongoクラスタへ影響しないように、別のサブネットワークにおきます。
- パブリックCIDR: 10.0.128.0/20
構築
Terraformによる自動構築
設計したコンポーネントに従ってalicloud providersを使用して実装します。
詳細のソースコードはmosuke5さんのレポジトリに参照すればよいですが、providers
は以下のふうになります。
resource "alicloud_vpc" "vpc" {
name = "mongovpc"
cidr_block = "${var.vpc_cidr}"
}
# vswitchの作成。mongo-primary
resource "alicloud_vswitch" "mongo-primary-switch" {
name = "mongo-primary-switch"
vpc_id = "${alicloud_vpc.vpc.id}"
cidr_block = "${var.mongo_primary_cidr}"
availability_zone = "${var.zones[0]}"
}
# vswitchの作成。mongo-secondary0
resource "alicloud_vswitch" "mongo-secondary0-switch" {
name = "mongo-secondary0-switch"
vpc_id = "${alicloud_vpc.vpc.id}"
cidr_block = "${var.mongo_secondary0_cidr}"
availability_zone = "${var.zones[1]}"
}
...
上記のプログラムを terraform apply --var-file="terraform.tfvars"
を実行することで、図の構成が作成されます。
レプリカ設定
terraform を実行した後では、mongodbクラスタ (サーバー、ミドルウェア)ができあがります。クラスタの設定は ansibleとかで自動化できます。ただ、今回は手動でやります。
まず、mongodbは全インターフェイスで通信できるように、bindIp
を削除し、replication
set名を設定します。設定語にmongodb
を起動します。この作業をmongodbの3台をそれぞれ行います。
[mongoadmin@mongo-primary ~]$ sudo vim /etc/mongodb.conf
net:
port: 27017
# bindIp: 127.0.0.1 # Listen to local interface only, comment to listen on all interfaces.
replication:
replSetName: testrepl
# :wq
[mongoadmin@mongo-primary ~]$ sudo systemctl start mongod.service
[mongoadmin@mongo-primary ~]$ sudo systemctl status mongod.service
● mongod.service - High-performance, schema-free document-oriented database
Loaded: loaded (/usr/lib/systemd/system/mongod.service; enabled; vendor preset: disabled)
Active: active (running) since Mon 2017-06-05 10:02:25 CST; 3s ago
Docs: https://docs.mongodb.org/manual
Process: 9504 ExecStartPre=/usr/bin/chmod 0755 /var/run/mongodb (code=exited, status=0/SUCCESS)
Process: 9502 ExecStartPre=/usr/bin/chown mongod:mongod /var/run/mongodb (code=exited, status=0/SUCCESS)
Process: 9501 ExecStartPre=/usr/bin/mkdir -p /var/run/mongodb (code=exited, status=0/SUCCESS)
Main PID: 9509 (mongod)
CGroup: /system.slice/mongod.service
└─9509 /usr/bin/mongod --quiet -f /etc/mongod.conf run
Jun 05 10:02:25 mongo-primary systemd[1]: Starting High-performance, schema-free document-oriented database...
Jun 05 10:02:25 mongo-primary systemd[1]: Started High-performance, schema-free document-oriented database.
Jun 05 10:02:25 mongo-primary mongod[9506]: about to fork child process, waiting until server is ready for connections.
Jun 05 10:02:25 mongo-primary mongod[9506]: forked process: 9509
次には、mongoシェルでreplication設定を行います。
[mongoadmin@mongo-primary ~]$ mongo
MongoDB shell version v3.4.4
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.4
Welcome to the MongoDB shell.
...
>
> rs.initiate( {
... _id : "testrepl",
... members: [
... { _id : 0, host : "10.0.26.159:27017" },
... { _id : 1, host : "10.0.43.6:27017" },
... { _id : 2, host : "10.0.91.206:27017" }
... ]
... })
{ "ok" : 1 }
> rs.status()
..
{
"_id" : 0,
"name" : "10.0.26.159:27017",
"health" : 1,
...
},
{
"_id" : 1,
"name" : "10.0.43.6:27017",
"health" : 1,
...
},
{
"_id" : 2,
"name" : "10.0.91.206:27017",
"health" : 1,
...
}
それぞれの"health": 1
となっていることを確認します。
各secondaryで当インスタンスがslaveインスタンスなことを設定します。
testrepl:SECONDARY> db.getMongo().setSlaveOk()
動作確認
testCollection
をmongo-primary
サーバーで作って、データがsecondary
へコピーされることを確認します
testrepl:PRIMARY> db.testCollection.insertOne({what: "Test mongodb", status: "ok"})
{
"acknowledged" : true,
"insertedId" : ObjectId("5934bf70cefc8e44e631908e")
}
secondaryで testCollection
をクエリしてみます。
testrepl:SECONDARY> db.testCollection.find()
{ "_id" : ObjectId("5934bf70cefc8e44e631908e"), "what" : "Test mongodb", "status" : "ok" }
おわりに
terraform
を使ってシンプルなmongoクラスタを作ってみました。terraform
を使うことで、クラウドサーバーの購入・設定のような作業を自動化できます。