Automatic Server-Side with Etcd and Node.js
Automatic Server-Side with Etcd and Node.js
์๋น์ค์ ์๋ฒ๋ Down๋์ด์๋ ์๋๋ค. ์ค๋ น Down์ด ๋๋ค๊ณ ํ๋๋ผ๋ ๋น ๋ฅธ ์๊ฐ ์์ ๋ณต๊ตฌํ ์ ์์ด์ผํ๋ค. ์ค์ ๋ก ๋ฐ์นญ๋ ์๋น์ค ๋๋ถ๋ถ์ด ์ด๋ฌํ ์์คํ
์ ๊ฐ์ง๊ณ ์๋ค. ์๋ ํ๋ฉด, ์๋ฒ๊ฐ Down๋๋ ์๊ฐ์ด ๊ธธ์ด์ง๋ฉด ๊ธธ์ด์ง์๋ก ์ฌ์ฉ์์ Active๋ ์ค์ด๋ค๊ธฐ ๋๋ฌธ์ด๋ค. ์คํํธ์
์ ๊ฒฝ์ฐ์ ์น๋ช
์ ์ธ ๋ถ๋ถ์ด๋ค.
์ด ๊ธ์์๋ etcd ์๋ฒ๋ฅผ ๊ตฌ์ถํ ํ MongoDB ์๋ฒ๊ฐ ์ฃฝ์์ ๋ ๋ค์ ๋์ด๋ ค์ฃผ๋ Watcher Instance๋ฅผ ๊ฐ๋ฐํ๋ค. ์ด๋ฅผ ํตํ์ฌ ์๋ฒ Down์ด ์๋ ์๋ฒ๋ฅผ ๊ฐ๋ฐํด๋ณด๋ ๊ฒ์ด ๋ชฉ์ ์ด๋ค.
Apache ZooKeeper๋ก ์๋ฒ๋ฅผ ๊ตฌ์ถํ์ฌ Watcher Instance๋ฅผ ๊ฐ๋ฐํ ์ ์์ง๋ง ๋ ๊ฐ๋จํ ๊ตฌ์กฐ์ด๋ฉด์๋ ์ข์ ์ฑ๋ฅ์ ๊ฐ์ง etcd๋ฅผ ์ด์ฉํด๋ณธ๋ค. ๋จ์ ์ผ๋ก๋ ์๋ฃ๊ฐ ์ผ๋ง ์๋ค. ๊ตฌ๊ธ์ ๊ฒ์์ ํด๋ณด์๋ ์๋ฃ๋ ์ ๋ง๋ก ์ผ๋ง ์๋ค. ๋น ๋ฅด๊ฒ ๊ตฌํํ๊ธธ ์ํ๋ค๋ฉด ์๋ฃ๊ฐ ๋ง์ zk๊ฐ ์ ํฉํ๋ค..
etcd๋ Consistentํ Key-Value ์ ์ฅ์์ด๋ค.
Simple : Curl์ ์ด์ฉํ์ฌ API๋ฅผ ํ
์คํธํ ์ ์๋ค. (HTTP + JSON)
Secure : SSL์ ์ง์ํ๋ค.
Fast : ํ๋์ Instance ๋น 1000s์ ์ฐ๊ธฐ ์ฐ์ฐ์ ํ ์ ์๋ค.
Reliable : Raft๋ผ๋ ์๊ณ ๋ฆฌ์ฆ์ด ์ฌ์ฉ๋๊ณ ์๋ค.
etcd๋ Go ์ธ์ด๋ก ์์ฑ๋์ด์ก๋ค. Brew์ ๊ฐ์ ํด๋ก ์ค์นํ๋ ๊ฒ์ด ์๋ ์์ค ์ฝ๋๋ฅผ Buildํ๊ณ ์ฌ์ฉํ๋ ค๋ฉด Go ์ธ์ด๋ฅผ ์ค์นํด์ผํ๋ค.
TIP : MongoDB์ฒ๋ผ JSON Document๋ฅผ ์ ์ฅํ๊ธฐ ์ํด์๋ JSON์ String์ผ๋ก ๋ณํํด์ผํ๋ค. Node.js์์๋ JSON.Stringify๋ฅผ ์ด์ฉํ๋ฉด ๋๋ค.
1. etcd 2. Node.js 3. MongoDB (Option)
Use etcdctl, Node.js for Automatic Server-Side
etcd๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์, ์ฐ๋ฆฌ๋ Key๋ก Service๋ฅผ ํํํด์ผํ๋ฉฐ Value๋ JSON ๋ฌธ์ ํ์์ผ๋ก Service์ ํ์ํ ์ ์ฉํ ์ ๋ณด๋ค์ ํํํ ์ ์๋ค. ์๋ฅผ ๋ค๋ฉด, Hostname, Port, Pid(Process Id), createdNodeIndex ๋ฑ์ด ์กด์ฌํ๋ค.
์ด๋ฅผ ์ํด์, etcd์์๋ etcdctl๋ฅผ ์ง์ํ๋ค.
$ etcdctl set /services/db "{\"hostname\": \"127.0.0.1\", \"port\": 3000}" {"hostname": "127.0.0.1", "port": 3000} $ etcdctl ls /services /services/server $ etcdctl get /services/server {"hostname": "127.0.0.1", "port": 3000}
์ด์ Node.js๋ก ์ฝ๋๋ฅผ ์์ฑํด์ผํ๋ค. Node.js์์ etcd๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ node-etcd๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
node-etcd์ set์ ์ด์ฉํ์ฌ Key๋ฅผ ์์ฑํ ์ ์๋ค.
var Etcd = require('node-etcd'); var etcd = new Etcd(); etcd.set(key, JSON.stringify({ hostname: '127.0.0.1', port: '3000', pid: process.pid}), function () {}); Options์ { wait : true }๋ฅผ ํ๋ฉด etcdctl์ ์ด์ฉํ์ฌ watcher๋ฅผ ์คํํ ์ ์๋ค.
์์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํด๋ณด๋ฉด ์๋ฒ๋ฅผ ์คํ์์ผ๋ณธ๋ค.
$ node index { action: 'set', node: { key: '/services/db', value: '{"hostname":"127.0.0.1","port":"3000","pid":2174}', modifiedIndex: 40, createdIndex: 40 } }
์ฑ๊ณต์ ์ผ๋ก ์์ฑ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด์ ์์ฑ๋ Key์ ํด๋นํ๋ Value๋ฅผ ์ฐพ์๋ณด์. ๋งค์ฐ ๊ฐ๋จํ๋ค.
$ node index etcd.get(key, function (err, value)) { console.log(value); }
Key์ ํด๋นํ๋ Value๊ฐ ์ถ๋ ฅ๋๋ค. ์ด์ ๋ฐ๋ก config์ ์ค์ ๋ค์ ๋ถ๋ฆฌ์์ผ์ฃผ์.
config.json "mongod" : "mongod --port 20000 --dbpath /data/db/rs1--replSet master
๋ฐ๋ก Watcher.js๋ฅผ ์์ฑํ์ฌ ์๋ฒ ์ํ๋ฅผ ๋ด์ฃผ๋ ์ฝ๋๋ฅผ ์์ฑํด๋ณด๋๋ก ํ์.
var Etcd = require('node-etcd'), exec = require('child_process').exec, config = require('./config'); var etcd = new Etcd(); var api = { watch: function (key) { etcd.set(config.etcd.key1, config.mongod1, function set() { exec(config.mongod1, function (err, stdout, stderr) { setTimeout(set, 10); }); }); } } module.exports = api;
ํด๋น ์ฝ๋๋ app.js์์ ์คํํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ module.exports๋ฅผ ์์ง ์๋๋ค.
var api = require('./watcher'), config = require('config'); console.log('watcher start'); api.watch(config.etcd.key1);
sudo node app์ผ๋ก ์๋ฒ๋ฅผ ์คํ์ํจ๋ค. ๊ทธ ํ์ ps aux | grep mongod๋ฅผ ์คํํ์ฌ mongod ํ๋ก์ธ์ค๋ฅผ ํ์ธํด๋ณด์.
$ ps aux | grep mongod root 17150 0.5 0.8 2735688 35568 s002 S+ 11:30PM 0:01.93 mongod --port 20000 --dbpath /data/db/replSet1 --replSet db
์ ์์ ์ผ๋ก MongoDB ์๋ฒ๊ฐ ์๋ ์ค์ธ ๊ฒ์ ํ์ธํ ์ ์๋ค. ์ด์ ๊ฐ์ ์ ์ผ๋ก ํ๋ก์ธ์ค๋ฅผ Down ์์ผ๋ณธ ํ, MongoDB ์๋ฒ๊ฐ ๋ณต๊ตฌ๋๋ ๊ฒ์ ํ์ธํด๋ณด์.
$ sudo kill -9 [process_id] $ ps aux | grep mongod root 17892 0.7 1.2 2726472 50880 s002 S+ 11:41PM 0:00.10 mongod --port 20000 --dbpath /data/db/replSet1 --replSet db
์์ ๊ณผ์ ์ ํตํ์ฌ MongoDB ์๋ฒ๊ฐ ๋ค์ ์ ์์ ์ผ๋ก ๋ณต๊ตฌ๋๋ ๋ชจ์ต์ ํ์ธํ ์ ์๋ค. ์ด๋ฌํ ์๋ํ ์์คํ
์ ๊ตฌ์ถํ์ง์๋๋ค๋ฉด ๊ฐ๋ฐ์๋ ๋ฐค์ ์ผ์ด๋์ ์๋์ ์ผ๋ก ๋ณต๊ตฌ๋ฅผ ํด์ผํ๋ค.
์ฌ๋ฌ ๊ฐ์ ์๋ฒ๋ฅผ ๋ณต๊ตฌํ๊ธฐ ์ํด์๋ ๋ฐ๋ก ํด๋ฌ์คํฐ๋ฅผ ๊ตฌ์ฑํ๋๋ก ํ๋ค.
etcd๋ฅผ ๊ณต๋ถํ ๋ด์ฉ์ ๊ธฐ๋กํ ๊ฒ์
๋๋ค. ํน์ ํ๋ฆฐ ์ ์ด ์๋ค๋ฉด ๊ณผ๊ฐํ ๋ง์ํด์ฃผ์ธ์. ๋ค์ ๊ณ ์น๋๋ก ํ๊ฒ ์ต๋๋ค.