<file_sep>CREATE USER 'bayulaxanauser'@'%' IDENTIFIED BY '<PASSWORD>';
GRANT ALL PRIVILEGES on databasename.* to 'bayulaxanauser'@'%';
FLUSH PRIVILEGES;<file_sep># Copy the hosts list
sudo cp /vagrant/sources/hosts /etc/hosts

# Copy the new source list
sudo cp '/vagrant/sources/sources.list' '/etc/apt/'

# Update package
sudo apt update -y

# Apache Installation
sudo apt install apache2 -y
sudo ufw allow in "Apache Full"<file_sep># Implementasi MongoDB Cluster

**Nama: <NAME>**

**NRP : 05111740000020**

- [Deskripsi Tugas](#deskripsi-tugas)
  + [Dataset](#dataset)
  + [Pembagian Cluster](#pembagian-cluster)
- [Detail Cluster](#detail-cluster)
- [Environmet](#environment)
  + [Gambaran Umum](#gambaran-umum)
- [Implementasi](#implementasi)
  + [1. Konfigurasi Vagrant](#1-konfigurasi-vagrant)
  + [2. Script Provision](#2-script-provision)
  + [3. Build Vagrant](#3-build-vagrant)
  + [4. Konfigurasi Config Server](#4-konfigurasi-config-server)
  + [5. Konfigurasi Query Router](#5-konfigurasi-query-router)
  + [6. Konfugurasi Shard Server](#6-konfigurasi-shard-server)
- [Import Dataset](#import-dataset)
- [Implementasi Aplikasi CRUD](#implementasi-aplikasi-crud)
  + [Connect Database](#connect-database)
  + [Operasi READ](#operasi-read)
  + [Operasi CREATE](#operasi-create)
  + [Operasi UPDATE](#operasi-update)
  + [Operasi DELETE](#operasi-delete)
  + [Operasi AGREGASI 1](#operasi-agregasi-1)
  + [Operasi AGREGASI 2](#operasi-agregasi-2)

## Deskripsi Tugas

Dalam tugas **Implementasi MongoDB Cluster**, hal-hal yang perlu dilakukan adalah :

- Mengimplementasikan MongoDB Cluster
- Menentukan dataset yang akan digunakan
- Mengimplementasikan aplikasi Create, Read, Update dan Delete menggunakan MongoDB Cluster

**Batasan dataset :**

- Data bisa berupa CSV atau JSON.
- Jumlah data minimal 1000 data
- Dataset nantinya akan diimport ke dalam server MongoDB 

### Dataset

Dataset yang digunakan dalam impelmentasi ini adalah dataset berjudul "**Population Health Measure: Age-Adjusted Mortality Rates**" yang diambil dari situs [Data Gov](https://catalog.data.gov/dataset). Dataset lengkapnya dapat diakses melalui tautan berikut.

[**Population Health Measure: Age-Adjusted Mortality Rates**](https://catalog.data.gov/dataset/population-health-measures-age-adjusted-mortality-rates-6a2e8)

Dataset tersebut mempunyai data berjumlah 12150 dan terdiri dari 9 atribut. Berikut adalah contoh beberapa data.

![](img/Dataset_Example.png)

### Pembagian Cluster

Cluster yang diimplementasikan terdiri dari :

- 2 Config Server
- 3 Data/Shard Server
- 1 Query Router

## Detail Cluster

Cluster dijalankan secara virtual menggunakan **Virtualbox** menggunakan bantuan software provision **Vagrant**. Detail untuk masing-masing cluster disajikan dalam tabel berikut.

| Cluster         | Nama Cluster       | Alamat IP     | Sistem Operasi | Alokasi RAM |
| --------------- | ------------------ | ------------- | -------------- | ----------- |
| Config Server 1 | mongo_config_1     | 192.168.33.11 | Ubuntu-18.04   | 512 MB      |
| Config Server 2 | mongo_config_2     | 192.168.33.12 | Ubuntu-18.04   | 512 MB      |
| Shard Server 2  | mongo_shard_1      | 192.168.33.13 | Ubuntu-18.04   | 512 MB      |
| Shard Server 2  | mongo_shard_2      | 192.168.33.14 | Ubuntu-18.04   | 512 MB      |
| Shard Server 2  | mongo_shard_3      | 192.168.33.15 | Ubuntu-18.04   | 512 MB      |
| Query Router    | mongo_query_router | 192.168.33.16 | Ubuntu-18.04   | 512 MB      |

## Environment

### Gambaran Umum

- Sistem Operasi : Ubuntu 18.04
- Virtual Machine : Virtualbox
- Vagrant
- MongoDB

Pengimplementasian dilakukan sepenuhnya pada sistem operasi **Ubuntu 18.04**. Virtual Machine yang digunakan adalah **virtualbox** versi 5.2, menggunakan box **_bento/ubuntu-18.04_** dan provision menggunakan **Vagrant** versi 2.2.5. Versi **MongoDB** yang digunakan adalah versi 4.2.

## Implementasi

### 1. Konfigurasi Vagrant

Sebelum memulai mengimplementasikan MongoDB, hal pertama yang harus dilakukan adalah membuat konfigurasi file vagrant yang berisi cluster-cluster yang akan digunakan sesuai dengan deskripsi sebelumnya.

```
$ vagrant init
```

Perintah tersebut akan membuatkan file `Vagrantfile` yang nantinya akan diisi dengan konfigurasi cluster. Konfigurasi file Vagrantfile adalah sebagai berikut.

File [**Vagrantfile**](Vagrantfile)

```ruby
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|

  config.vm.define "mongo_config_1" do |mongo_config_1|
    mongo_config_1.vm.hostname = "mongo-config-1"
    mongo_config_1.vm.box = "bento/ubuntu-18.04"
    mongo_config_1.vm.network "private_network", ip: "192.168.33.11"
    
    mongo_config_1.vm.provider "virtualbox" do |vb|
      vb.name = "mongo-config-1"
      vb.gui = false
      vb.memory = "512"
    end

    mongo_config_1.vm.provision "shell", path: "provision/allhosts.sh", privileged: false
  end

  config.vm.define "mongo_config_2" do |mongo_config_2|
    mongo_config_2.vm.hostname = "mongo-config-2"
    mongo_config_2.vm.box = "bento/ubuntu-18.04"
    mongo_config_2.vm.network "private_network", ip: "192.168.33.12"
    
    mongo_config_2.vm.provider "virtualbox" do |vb|
      vb.name = "mongo-config-2"
      vb.gui = false
      vb.memory = "512"
    end

    mongo_config_2.vm.provision "shell", path: "provision/allhosts.sh", privileged: false
  end

  config.vm.define "mongo_shard_1" do |mongo_shard_1|
    mongo_shard_1.vm.hostname = "mongo-shard-1"
    mongo_shard_1.vm.box = "bento/ubuntu-18.04"
    mongo_shard_1.vm.network "private_network", ip: "192.168.33.13"
    
    mongo_shard_1.vm.provider "virtualbox" do |vb|
      vb.name = "mongo-shard-1"
      vb.gui = false
      vb.memory = "512"
    end

    mongo_shard_1.vm.provision "shell", path: "provision/allhosts.sh", privileged: false
  end

  config.vm.define "mongo_shard_2" do |mongo_shard_2|
    mongo_shard_2.vm.hostname = "mongo-shard-2"
    mongo_shard_2.vm.box = "bento/ubuntu-18.04"
    mongo_shard_2.vm.network "private_network", ip: "192.168.33.14"
    
    mongo_shard_2.vm.provider "virtualbox" do |vb|
      vb.name = "mongo-shard-2"
      vb.gui = false
      vb.memory = "512"
    end

    mongo_shard_2.vm.provision "shell", path: "provision/allhosts.sh", privileged: false
  end

  config.vm.define "mongo_shard_3" do |mongo_shard_3|
    mongo_shard_3.vm.hostname = "mongo-shard-3"
    mongo_shard_3.vm.box = "bento/ubuntu-18.04"
    mongo_shard_3.vm.network "private_network", ip: "192.168.33.15"
        
    mongo_shard_3.vm.provider "virtualbox" do |vb|
      vb.name = "mongo-shard-3"
      vb.gui = false
      vb.memory = "512"
    end

    mongo_shard_3.vm.provision "shell", path: "provision/allhosts.sh", privileged: false
  end

  config.vm.define "mongo_query_router" do |mongo_query_router|
    mongo_query_router.vm.hostname = "mongo-query-router"
    mongo_query_router.vm.box = "bento/ubuntu-18.04"
    mongo_query_router.vm.network "private_network", ip: "192.168.33.16"
    
    mongo_query_router.vm.provider "virtualbox" do |vb|
      vb.name = "mongo-query-router"
      vb.gui = false
      vb.memory = "512"
    end

    mongo_query_router.vm.provision "shell", path: "provision/allhosts.sh", privileged: false
  end

end
```

### 2. Script Provision

Konfigurasi vagrant tersebut menggunakan file provision untuk tiap host. File provision yang digunakan untuk tiap host adalah file **`allhosts.sh`**.

File [**allhosts.sh**](provision/allhosts.sh)

```bash
# Add hostname
sudo bash -c \\"echo '192.168.33.11 mongo-config-1' >> /etc/hosts\\"
sudo bash -c \\"echo '192.168.33.12 mongo-config-2' >> /etc/hosts\\"
sudo bash -c \\"echo '192.168.33.13 mongo-shard-1' >> /etc/hosts\\"
sudo bash -c \\"echo '192.168.33.14 mongo-shard-2' >> /etc/hosts\\"
sudo bash -c \\"echo '192.168.33.15 mongo-shard-3' >> /etc/hosts\\"
sudo bash -c \\"echo '192.168.33.16 mongo-query-router' >> /etc/hosts\\"

# Copy APT sources list
sudo cp /vagrant/sources/sources.list /etc/apt/
sudo cp /vagrant/sources/mongodb-org-4.2.list /etc/apt/sources.list.d/

# Add MongoDB repo key
sudo apt-get install gnupg
wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add -

# Update Repository
sudo apt-get update
# sudo apt-get upgrade -y

# Install MongoDB
sudo apt-get install -y mongodb-org

# Start MongoDB
sudo service mongod start
```

File provision tersebut digunakan untuk melakukan instalasi **MongoDB** pada tiap host ketika di-build nanti. Tiap host akan mempunyai alamat IP masing-masing sesuai dengan penjelasan sebelumnya.

### 3. Build Vagrant

Setelah file provision dibuat, maka vagrant siap untuk di-build. Build vagrant menggunakan perintah:

```
$ vagrant up --provider virtualbox
```

![](img/Vagrant_up.png)

Perintah vagrant up akan menginisiasi building virtual machine dan menjalankan script provision menggunakan box `bento/ubuntu-18.04`. Apabila belum terdapat box `bento/ubuntu-18.04`, maka vagrant akan otomatis mengunduhnya.

### 4. Konfigurasi Config Server

Setelah proses build selesai, host sudah bisa digunakan. Masuk ke dalam host masing-masing _**config server**_ dengan menggunakan perintah.

```
$ vagrant ssh mongo_config_1
$ vagrant ssh mongo_config_2
```

**Create Administrative Username**

Masuk ke dalam shell mongo pada masing-masing config server.
```
(mongo-config-#) $ mongo
```
Buat username pada mongodb.
```
mongo> use admin
mongo> db.createUser({user: "username", pwd: "<PASSWORD>", roles: [ "root"] } )
```

**Konfigurasi Config Server**

Buat file konfigurasi untuk masing-masing config server. File konfigurasi dapat dilihat pada :

- [**mongo-config-1.conf**](config/mongo-config-1.conf)
- [**mongo-config-2.conf**](config/mongo-config-2.conf)

Hal yang perlu diperhatikan adalah alamat IP harus sesuai dan ditambahkan konfigurasi khusus.

```conf
.....
net:
  port: 27019
  bindIp: <sesuai IP server>
....
....
replication:
  replSetName: configReplSet

sharding:
  clusterRole: "configsvr"
....
```

Salin file konfigurasi tadi dalam file konfigurasi mongodb pada masing-masing config server.

```
(mongo-config-1) $ sudo cp /vagrant/config/mongo-config-1.conf /etc/mongod.conf
```

```
(mongo-config-2) $ sudo cp /vagrant/config/mongo-config-2.conf /etc/mongod.conf
```

Setelah konfigurasi selesai, restart mongodb pada masing-masing server.

```
(mongo-config-#) $ sudo systemctl restart mongod
```

Pada salah satu config server, masuk kembali pada mongod.

```
(mongo-config-1) $ mongo 192.168.33.11:27019 -u mongo-admin -p --authenticationDatabase admin
```

Inisialisasi replica set dengan perintah berikut dan menambahkan anggota config server yang lain.

```json
rs.initiate(
    { _id: "configReplSet", configsvr: true, members: [
            { _id: 0, host: "192.168.33.11:27019" },
            { _id: 1, host: "192.168.33.12:27019" } 
        ] 
    }
)
```

```
mongo> rs.initiate( { _id: "configReplSet", configsvr: true, members: [ { _id: 0, host: "192.168.33.11:27019" }, { _id: 1, host: "192.168.33.12:27019" } ] } )
```

### 5. Konfigurasi Query Router

Masuk terlebih dahulu ke dalam host query router.
```
$ vagrant ssh mongo-query-router
```

**Konfigurasi Query Router**

Untuk konfigurasi query router, file konfigurasinya berbeda. Buat file konfigurasi.

- File [**mongo-query-router.conf**](config/mongo-query-router.conf)

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

# network interfaces
net:
  port: 27017
  bindIp: 192.168.33.16

sharding:
  configDB: configReplSet/192.168.33.11:27019,192.168.33.12:27019
```

Salin file konfigurasi yang telah dibuat ke dalam folder konfigurasi query router.

```
(mongo-query-router) $ sudo cp /vagrant/config/mongo-query-router.conf /etc/mongos.conf
```

**Service Query Router**

Setelah file konfigurasi, maka langkah selanjutnya adalah membuat service untuk mongos. Buatlah file service `mongos.service`.

- File [**mongos.service**](config/mongos.service)

```service
[Unit]
Description=Mongo Cluster Router
After=network.target

[Service]
User=mongodb
Group=mongodb
ExecStart=/usr/bin/mongos --config /etc/mongos.conf
# file size
LimitFSIZE=infinity
# cpu time
LimitCPU=infinity
# virtual memory size
LimitAS=infinity
# open files
LimitNOFILE=64000
# processes/threads
LimitNPROC=64000
# total threads (user+kernel)
TasksMax=infinity
TasksAccounting=false

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

### 6. Konfigurasi Shard Server

Masuk ke dalam masing-masing shard server untuk memulai konfigurasi.

```
$ vagrant ssh mongo_shard_1
$ vagrant ssh mongo_shard_2
$ vagrant ssh mongo_shard_3
```

**Create Administrative Username**

Masuk ke dalam shell mongo pada masing-masing shard server.
```
(mongo-config-#) $ mongo
```
Buat username pada mongodb.
```
mongo> use admin
mongo> db.createUser({user: "username", pwd: "<PASSWORD>", roles: [ "root"] } )
```

**Konfigurasi Shard Server**

Untuk mengkonfigurasi shard server, hal yang perlu dilakukan adalah mengubah file konfigurasi shard server, lalu menambahkan shard ke dalam mongoDB cluster kemudian mengaktifkan sharding pada level database dan level collection.

1. Mengubah file konfigurasi shard server

    Buat file konfigurasi untuk masing-masing shard server. Yang perlu diperhatikan pada masing-masing file konfigurasi adalah:

    ```conf
    bindIp: <IP masing-masing server>
    ....
    ....
    sharding:
      clusterRole: "shardsvr"
    ```

    + File [**mongo-shard-1.conf**](config/mongo-shard-1.conf)
    + File [**mongo-shard-2.conf**](config/mongo-shard-2.conf)
    + File [**mongo-shard-3.conf**](config/mongo-shard-3.conf)

    Salin file konfigurasi ke folder konfigurasi pada masing-masing shard server.

    ```
    (mongo-shard-1) $ sudo cp /vagrant/conf/mongo-shard-1.conf /etc/mongod.conf 
    ```
    
    ```
    (mongo-shard-2) $ sudo cp /vagrant/conf/mongo-shard-2.conf /etc/mongod.conf 
    ```

    ```
    (mongo-shard-3) $ sudo cp /vagrant/conf/mongo-shard-3.conf /etc/mongod.conf 
    ```

2. Menambahkan shard ke dalam mongoDB cluster.

    Di salah satu shard sever, masuk ke dalam query router.
    ```
    (mongo-shard-1) $ mongo 192.168.33.16:27017 -u mongo-admin -p --authenticationDatabase admin
    ```

    Untuk menambahkan shard, pada shell mongo jalankan perintah berikut.

    ```
    sh.addShard("192.168.33.13:27017")
    sh.addShard("192.168.33.14:27017")
    sh.addShard("192.168.33.15:27017")
    ```

3. Mengaktifkan sharding pada level database dan collection

    Pertama masuk ke dalam query router (pada salah satu shard server).
    ```
    (mongo-shard-1) $ mongo 192.168.33.16:27017 -u mongo-admin -p --authenticationDatabase admin
    ```
    Untuk mengaktifkan sharding pada level database, beralih pada salah satu database yang hendak diaktifkan shardingnya.

    ```
    mongos> use populationData
    mongos> sh.enableSharding("populationData)
    mongos> db.populationData.ensureIndex( { _id: "hashed"} )
    sh.shardCollection( "populationData.populationCollection", { "_id" : "hashed" } )
    ```

## Import Dataset

Untuk melakukan import dataset menggunakan file CSV, bisa menggunakan perintah yang sudah disediakan oleh mongoDB.

```
$ mongoimport -h [host:port] -u [username] -d [database_name] -c [collection_name] --type CSV --file [path/to/file.csv] --headerline --authenticationDatabase admin
```

Masuk pada server query router untuk menjalankan perintah tersebut.

```
$ mongoimport -h 192.168.33.16:27017 -u mongo-admin -d populationData -c populationCollection --type CSV --file /vagrant/dataset/data_real.csv --headerline --authenticationDatabase admin
```
![](img/import.png)

Data akan diimport pada database yang bersangkutan.

## Implementasi Aplikasi CRUD

![](https://upload.wikimedia.org/wikipedia/commons/3/3c/Flask_logo.svg)

![](https://upload.wikimedia.org/wikipedia/commons/f/f8/Python_logo_and_wordmark.svg)

Aplikasi CRUD yang diimplementasikan pada MongoDB Cluster adalah aplikasi API (Application Programming Interface) menggunakan bahasa pemograman Python.

Flask adalah framework web untuk Python, yang menyediakan fungsionalitas untuk membangun aplikasi web, termasuk mengelola request HTTP dan merender template. Implementasi kali ini akan menggunakan Flask untuk membuat API menggunakan bahasa pemrograman Python.

API yang dibuat berisi beberapa operasi dasar seperti:
- Create
- Read
- Update
- Delete

dan operasi agregasi yang menggunakan **count** dan **sum**.

Pengujian API menggunakan aplikasi POSTMAN.

![](https://assets.getpostman.com/common-share/postman-logo-horizontal-white.svg)

[**Implementasi API (main.py)**](API/main.py)

### Connect Database

Hal pertama yang perlu dilakukan adalah connect ke dalam database MongoDB.

```py
app = Flask(__name__)
app.secret_key = "SECRETKEY"
app.config["MONGO_URI"] = "mongodb://mongo-admin:password@192.168.33.16:27017/healthMeasure?retryWrites=false&authSource=admin"
```

### Operasi READ

```py
@app.route('/populationHealthMeasure', methods=['GET'])
def get_data():
    datas = mongo.db.populationCollection.find()
    response = dumps(datas)
    return response
```

Pada operasi read, yang dilakukan adalah untuk mendapatkan semua data. Method yang digunakan adalah `GET` dengan endpoint `/populationHealthMeasure`.

![](img/API_read.png)

### Operasi CREATE

```py
@app.route('/populationHealthMeasure', methods=['POST'])
def add_data():
    request_json = request.json
    data_uniqueid = request_json["unique_id"]
    data_3Year = request_json["3_year_period"]
    data_deathCause = request_json["cause_of_death"]
    data_Race = request_json["race"]
    data_HispanicLatino = request_json["hispanic_or_latino"]
    data_gender = request_json["gender"]
    data_ageAdjusted = request_json["age-adjusted_rate"]
    data_lowerConfidenceInterval = request_json["lower_confidence_interval"]
    data_upperConfidenceInterval = request_json["upper_confidence_interval"]

    data_id = mongo.db.populationCollection.insert({
        'unique_id' : data_uniqueid,
        '3_year_period' : data_3Year,
        'cause_of_death' : data_deathCause,
        'race' : data_Race,
        'hispanic_or_latino' : data_HispanicLatino,
        'gender' : data_gender,
        'age-adjusted_rate' : data_ageAdjusted,
        'lower_confidence_interval' : data_lowerConfidenceInterval,
        'upper_confidence_interval' : data_upperConfidenceInterval
    })

    response = jsonify('Data has added successfully. The id is {}'.format(data_id))
    response.status_code = 200
    return response
```

Operasi Create yang dilakukan adalah meng-insertkan satu data ke dalam database. Endpoint yang digunakan `/populationHealthMeasure` dan method yang digunakan adalah `POST`. Data yang dikirimkan berupa data JSON dengan atribut-atribut yang bersesuaian dan value-nya masing-masing.

![](img/API_insert.png)

### Operasi UPDATE

```py
@app.route('/populationHealthMeasure/<id>', methods=['PUT'])
def update_data(id):
    request_json = request.json
    data_id = request_json["_id"]
    data_uniqueid = request_json["unique_id"]
    data_3Year = request_json["3_year_period"]
    data_deathCause = request_json["cause_of_death"]
    data_Race = request_json["race"]
    data_HispanicLatino = request_json["hispanic_or_latino"]
    data_gender = request_json["gender"]
    data_ageAdjusted = request_json["age-adjusted_rate"]
    data_lowerConfidenceInterval = request_json["lower_confidence_interval"]
    data_upperConfidenceInterval = request_json["upper_confidence_interval"]

    mongo.db.populationCollection.update_one(
        {'_id': ObjectId(data_id['$oid']) if '$oid' in data_id else ObjectId(data_id)},
        {
            '$set' :
            {
                'unique_id' : data_uniqueid,
                '3_year_period' : data_3Year,
                'cause_of_death' : data_deathCause,
                'race' : data_Race,
                'hispanic_or_latino' : data_HispanicLatino,
                'gender' : data_gender,
                'age-adjusted_rate' : data_ageAdjusted,
                'lower_confidence_interval' : data_lowerConfidenceInterval,
                'upper_confidence_interval' : data_upperConfidenceInterval
            }
        }
    )

    response = jsonify('Data has updated successfully. The id is {}'.format(data_id))
    response.status_code = 200
    return response
```

Pada operasi update, yang dilakukan adalah memperbarui satu data dengan parameter berupa ObjectId. Data yang dikirimkan berupa data JSON dengan atribut-atribut dan value terbaru yang hendak di-update. Namun, ada satu hal yang perlu disertakan, yakni ObjectID dari data yang hendak di-update. Endpointnya adalah `/populationHealthMeasure/<id>` dengan parameter berupa ObjectId dan method yang digunakan adalah `PUT`.

![](img/API_update.png)

### Operasi DELETE

```py
@app.route('/populationHealthMeasure/<id>', methods=['DELETE'])
def delete_data(id):
    mongo.db.populationCollection.delete_one({
        '_id': ObjectId(id)
    })
    
    response = jsonify('Data has deleted successfully. The id is {}'.format(id))
    response.status_code = 200
    return response
```

Untuk operasi delete, yang dilakukan adalah menghapus data berdasarkan ObjectId. Endpoint yang digunakan adalah `/populationHealthMeasure/<id>` dengan parameter berupa ObjectId dan method yang digunakan adalah `DELETE`.

![](img/API_delete.png)

### Operasi AGREGASI 1

```py
@app.route('/populationHealthMeasure/cause_of_death', methods=['GET'])
def get_salmonela():
    result = mongo.db.populationCollection.aggregate([
        {
            "$group": {
                "_id": "$cause_of_death",
                "count": {"$sum": 1}
            } 
        }
    ])

    response = dumps(result)
    return response
```

Operasi agregrasi pertama melibatkan dua operasi yakni  **group** dan **sum**. Tujuan dari operasi ini adalah untuk menghitung berapa banyak data berdasarkan **_cause_of_death_**.

Agregasi group digunakan untuk mengelompokkan berdasarkan _cause_of_death_, kemudian dihitung jumlahnya menggunakan _sum_.

![](img/API_aggregate.png)

### Operasi AGREGASI 2

```py
# Aggregation Operation
@app.route('/populationHealthMeasure/max_upper_confidence', methods=['GET'])
def get_max_confidence():
    result = mongo.db.populationCollection.aggregate([
        {
            "$group": {
                "_id": "$cause_of_death",
                "minimum": {"$min": "$upper_confidence_interval"}
            }
        }
    ])

    response = dumps(result)
    return response
```

Operasi agregrasi kedua melibatkan dua operasi yakni  **group** dan **min**. Tujuan dari operasi ini adalah untuk mencari nilai minimal upper_confidence_interval berdasarkan **_cause_of_death_**.

![](img/API_aggregate2.png)<file_sep># Basis Data Terdistribusi

## Environment

Implementasi Basis Data Terdistribusi menggunakan metode Grup-Replication ini menggunakan tool Virtualbox dan provisioning menggunakan Vagrant. Seluruh tahapan pengerjaan dilakukan sepenuhnya pada Sistem Operasi Linux Ubuntu 18.04.

### Instalasi Vagrant dan Virtualbox

```
$ sudo apt-get update
$ sudo apt-get install vagrant
$ sudo apt-get install virtualbox
```

Cek apakah vagrant dan virtualbox sudah terinstall.
```
$ vagrant --version
$ vboxmanage --version
```

Output
```
user@user:
Vagrant 2.0.2

user@user
5.2.32_Ubuntur132056
```

## Desain Infrastruktur Basis Data

Berikut adalah desain insfrastruktur basis data yang akan diimplementasikan:

**!GAMBAR GANTI!**
![](img/Desain_Infra_BDT.jpg)

Keterangan :

### Web Server

Web server yang digunakan pada implementasi ini menggunakan komputer host (lokal), dengan IP nya sesuai komputer host. Cek ip komputer host menggunakan command:

```
$ ip addr
```
Hasil output :

![](img/ip_add.png)

### Proxy Server

Proxy server disini menggunakan virtualbox, dengan setting IP 177.5.**17**.**20** dan alokasi memori sebesar 512 MB. Tools yang digunakan adalah MySQL Proxy, yang nantinya berfungsi layaknya _Load Balancer_.

### Database Server

Terdapat 3 database server yang diimplementasikan, yang masing-masing bernama db1, db2, db3 yang masing-masing alokasi memori database server adalah 512 MB dan mempunyai IP :
- db1 192.168.127.12
- db2 172.16.58.3
- db3 192.168.127.12

## Implementasi

Tahapan-tahapan umum impelentasinya adalah sebagai berikut.
1. Membangun tiap-tiap server menggunakan vagrant.
2. Konfigurasi database server.
3. Konfigurasi proxy server.
4. Konfigurasi web server.
5. Menghubungkan aplikasi menuju proxy server.

-----------------------
## Deskripsi Aplikasi

Link [aplikasi](github.com/bayulaxana/project_bdt1)

Aplikasi yang akan digunakan untuk penerapan basis data terdistribusi menggunakan Group-Replicartion adalah aplikasi web sederhana yang dibuat menggunakan laravel. Fungsi dari aplikasi ini adalah untuk menyimpan blog-blog pribadi atau notes yang digunakan untuk keperluan pribadi saja. Cukup sederhana untuk menggambarkan bagaimana database bekerja pada web.

Aplikasi ini mencakup operasi-operasi basis data dasar seperti Create, Read, Update, dan Delete.
Berikut adalah beberapa screenshot tampilan web tersebut.

![](img/img1.png)
![](img/img2.png)
![](img/img3.png)
![](img/img4.png)
![](img/img5.png)


## Vagrant

Sebelum melakukan build virtual machine, buat terlebih dahulu file konfigurasi **Vagrantfile**.

Link [Vagrantfile](Vagrantfile)

Build menggunakan command :
```
$ vagrant up
```
Perintah `vagrant up` akan menginisiasi building virtual machine menggunakan box `bento/ubuntu-16.04`. Apabila belum terdapat box `bento/ubuntu-16.04`, maka vagrant akan otomatis mengunduhnya.

Proses ini akan membuat virtual machine dengan nama masing-masing "db1", "db2", "db3", dan "proxy", sekaligus melakukan provisioning terhadap masing-masing server untuk memperoleh library-library yang diperlukan.

File provision :
- [db1](DB_server/deployMySQL1.sh)
- [db2](DB_server/deployMySQL2.sh)
- [db3](DB_server/deployMySQL3.sh)
- [proxy](Proxy_server/deployProxySQL.sh)

Untuk memastikan apakah masing-masing server telah sukses dibuat, periksa pada direktori `.vagrant/machines`.
```
$ ls -1 .vagrant/machines
```
Akan terdapat empat direktori dengan nama masing-masing server.

![](img/gambar_machines.png)

Sampai tahap ini, masing-masing server telah berhasil dibuat.

## Database Server

Setelah semua server telah terbentuk, kini saatnya untuk mengkonfigurasi masing-masing database server. 

Sebelum mengkonfigurasi, generate terlebih dahulu UUID untuk digunakan pada grup replikasi.

```
(db1)$ uuidgen
```
Tiap-tiap database server mempunyai konfigurasi file yang hampir sama.
- [db1.cnf](DB_server/db1.cnf)
- [db2.cnf](DB_server/db2.cnf)
- [db3.cnf](DB_server/db3.cnf)

### Konfigurasi Grup-Replication

Untuk setiap file konfigurasi, aktifkan `loose-group_replication_start_on_boot = ON` agar pada saat mysql dijalankan langsung bisa mengaktifkan grup-replication.

```
...

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/

[mysqld]

# General replication settings
gtid_mode = ON
enforce_gtid_consistency = ON
master_info_repository = TABLE
relay_log_info_repository = TABLE
binlog_checksum = NONE
log_slave_updates = ON
log_bin = binlog
binlog_format = ROW
transaction_write_set_extraction = XXHASH64
loose-group_replication_bootstrap_group = OFF
loose-group_replication_start_on_boot = ON
loose-group_replication_ssl_mode = REQUIRED
loose-group_replication_recovery_use_ssl = 1

...
```
### Group sharing

Disinilah bagian konfigurasi untuk mengatur anggota-anggota dari grup-replication (database server yang lain). Isi bagian `loose-group_replication_group_name` dengan UUID yang telah digenerate, `loose-group_replication_ip_whitelist` berisi anggota masing-masing (ip) database server, `loose-group_replication_group_seeds` dengan ip diikuti port 33061.

```
# Shared replication group configuration
loose-group_replication_group_name = "f02df904-502b-4d4a-a170-2f07faf816a8"
loose-group_replication_ip_whitelist = "192.168.127.12, 172.16.58.3, 192.168.127.12"
loose-group_replication_group_seeds = "192.168.127.12:33061, 172.16.58.3:33061, 192.168.127.12:33061"
```

### Multi-primary mode

Dalam mode Multy-Primary, setiap database server mempunyai hak untuk melakukan write pada database.

```
# Single or Multi-primary mode? Uncomment these two lines
# for multi-primary mode, where any host can accept writes
loose-group_replication_single_primary_mode = OFF
loose-group_replication_enforce_update_everywhere_checks = ON
```

### Konfigurasi untuk server

Untuk masing-masing server, terdapat konfigurasi khusus spesifik.

```
# Host specific replication configuration
server_id = 1
bind-address = "192.168.127.12"
report_host = "192.168.127.12"
loose-group_replication_local_address = "192.168.127.12:33061"
```

Hal-hal yang perlu diperhatikan adalah :
- `server_id` diisi **id** masing-masing server.
- `bind-address` disii IP masing-masing server.
- `report-host` disii IP masing-masing server.
- `loose-group_replication_local_address` disii IP masing-masing server diikuti port 33061.

### Perbarui file konfigurasi MySQL

Pada masing-masing server :
```
(db1)$ sudo cp /vagrant/DB_server/db1.cnf /etc/mysql/my.cnf
```
```
(db2)$ sudo cp /vagrant/DB_server/db2.cnf /etc/mysql/my.cnf
```
```
(db3)$ sudo cp /vagrant/DB_server/db3.cnf /etc/mysql/my.cnf
```

Restart service mysql disemua server.

### Aktivasi Plugin Group-Replication

Untuk setiap server, masuk ke dalam mysql menggunakan akun `root` dan password `<PASSWORD>`, dan jalankan file `group_rep_member.sql` untuk membuat user replication. 
```
$ mysql -u root -padmin < DB_server/group_rep_member.sql
```
File [group_rep_member.sql](DB_server/group_rep_member.sql)

Sebelum memulai Grup-Replication, masuk pada **salah satu db server** (misal db1) untuk melakukan bootstraping agar masing-masing server tahu keberadaan server lain.
```
(db1)$ mysql -u root -padmin < DB_server/start_bootstrap.sql
```

File [start_bootstrap.sql](DB_server/start_bootstrap.sql)

Kemudian di server lain, inisiasi untuk memulai grup replication.
```
mysql> START GROUP_REPLICATION;
```

## Proxy Server

Proxy server berperan sebagai Load Balancer, dan tools yang digunakan adalah ProxySQL. ProxySQL sudah terinstall ketika pertama kali melakukan provisioning script `vagrant up`.

File [provisioning ProxySQL]()

Jalankan service ProxySQL terlebih dahulu.
```
(proxy)$ sudo systemctl start proxysql
```

### Konfigurasi ProxySQL Sebagai Admin

Pada server proxy, masuk ke dalam mysql dengan akun `admin` menggunakan password `<PASSWORD>` dan jalankan file `proxysql.sql`.
```
(proxy)$ mysql -u admin -padmin -h 127.0.0.1 -P 6032 < Proxy_server/proxysql.sql
```

File [proxysql.sql](Proxy_server/deployProxySQL.sh)

Pada file .sql tersebut, akan melakukan monitoring terhadap semua database server yang tersedia (semua node), sehingga ProxySQL tahu bagaimana cara untuk mendistribusikan tiap-tiap node.

```sql
INSERT INTO mysql_group_replication_hostgroups (writer_hostgroup, backup_writer_hostgroup, reader_hostgroup, offline_hostgroup, active, max_writers, writer_is_also_reader, max_transactions_behind) VALUES (2, 4, 3, 1, 1, 3, 1, 100);
```
Menambahkan database server agar diketahui oleh ProxySQL.
```sql
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (2, '192.168.127.12', 3306);
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (2, '172.16.58.3', 3306);
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (2, '192.168.127.12', 3306);
```

Agar dapat dimonitor oleh ProxySQL, maka perlu dilakukan konfigurasu tambahan pada salah satu database server.
```
(db1)$ mysql -u root -p Proxy_server/addition_to_sys.sql
```
Kemudian menambahkan user monitor baru.
```
(db1) mysql> CREATE USER 'monitor'@'%' IDENTIFIED BY 'monitorpassword';
(db1) mysql> GRANT SELECT on sys.* to 'monitor'@'%';
(db1) mysql> FLUSH PRIVILEGES;
```

Setelah itu dapat menambahkan user baru pada ProxySQL.
```sql
INSERT INTO mysql_users(username, password, default_hostgroup) VALUES ('bayulaxanauser', '<PASSWORD>', 2);
```

> User yang ditambahkan adalah `bayulaxanauser` dengan password `<PASSWORD>`

### Konfigurasi Database Server

Untuk memperbolehkan agar ProxySQL dapat berhubungan dengan database server, masing-masing database server membutuhkan user yang sama dengan user yang telah dibuat pada ProxySQL.

Dalam kasus ini, nama user yang digunakan adalah `bayulaxanauser`. Pada salah satu database server, masuk ke dalam mysql dengan user root.
```
(db1)$ mysql -u root -p
```
Kemudian eksekusi perintah dibawah.
```sql
CREATE USER 'bayulaxanauser'@'%' IDENTIFIED BY '<PASSWORD>';
GRANT ALL PRIVILEGES on databasename.* to 'bayulaxanauser'@'%';
FLUSH PRIVILEGES;
```
Tahapan konfigurasi ProxySQL sudah selesai. Untuk memastikan, lihat server-server yang terhubung dengan menjalankan perintah dibawah pada server ProxySQL (admin).

```
mysqlAdmin> SELECT hostgroup_id, hostname, status FROM runtime_mysql_servers;
```

Output
```
+--------------+-------------+---------+
| hostgroup_id | hostname    | status  |
+--------------+-------------+---------+
| 2            | 17192.168.3.11 | ONLINE  |
| 2            | 172.16.58.3 | ONLINE  |
| 2            | 192.168.127.12 | ONLINE  |
+--------------+-------------+---------+
```

## Web Server

Web server pada implementasi ini menggunakan komputer host sebagai servernya. Kemudian nantinya, komputer host akan mengarahkan koneksi database mysql menuju ProxySQL. Agar web server dapat bekerja sebagaimana mestinya, maka tools yang perlu diinstall adalah :
- Apache
- PHP

### Instalasi Apache

Install apache web server dengan perintah berikut.
```
$ sudo apt update
$ sudo apt install apache2 -y
```
Ijinkan firewall untuk menggunakan apache.
```
$ sudo ufw app info "Apache Full"
$ sudo ufw allow in "Apache Full"
```
Periksa dengan mengakses IP 127.0.0.1 pada browser. Seharusnya muncul halaman seperti gambar di bawah yang menandakan instalasi Apache sukses.

![](img/small_apache_default_1804.png)

### Instalasi PHP

Selanjutnya adalah proses instalasi PHP, yang nantinya akan digunakan sebagai backend server dan integrasi laravel.

Untuk menginstal php berserta modul-modul yang dibutuhkan oleh laravel, gunakan perintah :
```
$ sudo apt install php7.2-common php7.2-cli php7.2-gd php7.2-mysql php7.2-curl php7.2-intl php7.2-mbstring php7.2-bcmath php7.2-imap php7.2-xml php7.2-zip
```

### Instalasi Composer

Composer dibutuhkan untuk menginisiasi project laravel.

```
$ curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer
```
### Koneksi database laravel ke ProxySQL

Agar database project Laravel bisa terhubung ke ProxySQL, maka koneksi DB_HOST pada file environment laravel harus disesuaikan. Edit file .env khususnya pada bagian DB agar sesuai dengan IP ProxySQL.

```env
DB_CONNECTION=mysql
DB_HOST=172.16.31.10
DB_PORT=6033
DB_DATABASE=playground
DB_USERNAME=bayulaxanauser
DB_PASSWORD=<PASSWORD>
```

Host dari ProxySQL mempunyai IP 172.16.31.10 dan dihubungkan pada port 6033 dengan nama database `playground` dan username `bayulaxanauser`.

Untuk memulai web, bisa menggunakan command
```
$ php artisan serve
```
akan diarahkan pada `127.0.0.01:8000` untuk mengakses konten webnya.
<file_sep># Implementasi Redis Cluster

**Nama: <NAME>**

**NRP : 05111740000020**

# A. Deskripsi Tugas

## Deskripsi Umum

- Pada tugas ini, diminta untuk mengimplementasikan Redis server dan menggunakan Sentinel untuk High Availability.
- Redis server akan digunakan untuk menge-cache konten dari sebuah website Wordpress agar dapat disajikan lebih cepat.
- Kemudian juga akan membandingkan performa dari server Wordpress yang menggunakan Redis dan tidak menggunakan Redis.

## Rincian

1. Buatlah sebuah cluster Redis dengan 3 buah node. Tuliskan file konfigurasi yang digunakan.
2. Buatlah sebuah web server, kemudian:
    - Install Wordpress
    - Konfigurasi Wordpress agar menggunakan redis cache
3. Buatlah sebuah web server lagi dengan konfigurasi hardware yang sama,kemudian install Wordpress. Pada server ini tidak perlu mengaktifkan Redis cache.
4. Lakukan pengujian menggunakan JMeter untuk mengecek perbedaan load time dari dua server web tersebut. Buat kesimpulan dari pengujian yang telah dilakukan.
5. Lakukan pula pengujian untuk menunjukkan bahwa proses fail over menggunakan Redis Sentinel berhasil. Caranya dengan mematikan salah satu server Redis dan mengecek siapa master node baru yang terpilih.

## Pembagian Cluster

Pembuatan server menggunakan skema alamat IP sebagai berikut:
- Untuk IP A.B.C.D
- A dan B bebas
- C merupakan tahun angkatan
- D 3 digit NRP terakhir

### Arsitektur Cluster

![](img/Arsitektur_BDT.png)

### Detail Cluster

| Cluster             | Nama Cluster | Alamat IP   | Sistem Operasi | Alokasi RAM |
| ---------------     | ------------ | ------------| -------------- | ----------- |
| Wordpress Non Redis | wordpress_1  | 172.16.58.3 | Ubuntu-18.04   | 512 MB      |
| Wordpress Redis     | wordpress_2  | 192.168.3.11 | Ubuntu-18.04   | 512 MB      |
| Redis 1 (Master)    | redis_1      | 172.16.17.32 | Ubuntu-18.04   | 512 MB      |
| Redis 2 (Slave)     | redis_2      | 172.16.58.3 | Ubuntu-18.04   | 512 MB      |
| Redis 3 (Slave)     | redis_3      | 172.16.31.10 | Ubuntu-18.04   | 512 MB      |

# B. Environment

- Sistem Operasi : Ubuntu 18.04
- Virtual Machine : Virtualbox
- Vagrant 2.0.2
- Redis 5.0.7

Pengimplementasian dilakukan sepenuhnya pada sistem operasi **Ubuntu 18.04**. Virtual Machine yang digunakan adalah **virtualbox** versi 5.2, menggunakan box **_bento/ubuntu-18.04_** dan provision menggunakan **Vagrant** versi 2.0.2.

# C. Implementasi

## Konfigurasi Vagrant

Sebelum memulai mengimplementasikan Redis dan Web Server, hal pertama yang harus dilakukan adalah membuat konfigurasi file vagrant yang berisi cluster-cluster yang akan digunakan sesuai dengan deskripsi sebelumnya.

```
$ vagrant init
```

Perintah tersebut akan membuatkan file `Vagrantfile` yang nantinya akan diisi dengan konfigurasi cluster. Konfigurasi file Vagrantfile adalah sebagai berikut.

- [**File Vagrantfile**](Vagrantfile)

### Script Provision

Konfigurasi vagrant tersebut menggunakan file provision. Untuk web server menggunakan script provision :

- [**File wordpress_provision.sh**](provision/wordpress_provision.sh)

Sedangkan file provision untuk redis server menggunaakan script proviion :

- [**File redis_provision.sh**](provision/redis_provision.sh)

## Build Vagrant

Setelah file provision dibuat, maka vagrant siap untuk di-build. Build vagrant menggunakan perintah:

```
$ vagrant up --provider virtualbox
```

![Proses build vagrant](img/vagrantup.png)

_Proses provisioning_

![](img/vagrantup2.png)

Perintah vagrant up akan menginisiasi building virtual machine dan menjalankan script provision menggunakan box `bento/ubuntu-18.04`. Apabila belum terdapat box `bento/ubuntu-18.04`, maka vagrant akan otomatis mengunduhnya.

## File Konfigurasi Cluster Redis

Hal pertama yang akan dilakukan adalah mengkonfigurasi server redis. Cara konfigurasi untuk semua server redis sama.

Masuk ke dalam masing-masing server redis
```
$ vagrant ssh redis_1
```

_Tampilan awal_

![](img/ssh_redis1.png)

> masuk ke semua server redis

Namun sebelum memulai, ada file konfigurasi yang perlu dibuat terlebih dahulu, yakni file konfigurasi untuk redis dan redis-sentinel.

**Konfigurasi Server Redis**

- [**File konfigurasi redis1**](config/redis-1.conf)

    ```conf
    bind 172.16.17.32
    port 6379
    dir "/etc/redis"
    ```

- [**File konfigurasi redis2**](config/redis-2.conf)

    ```conf
    bind 172.16.58.3
    port 6379
    dir "/etc/redis"
    slaveof 172.16.17.32 6379
    ```

- [**File konfigurasi redis3**](config/redis-3.conf)

    ```conf
    bind 172.16.31.10
    port 6379
    dir "/etc/redis"
    slaveof 172.16.17.32 6379
    ```

**File Konfigurasi Redis Sentinel**

- [**File konfigurasi redis sentinel 1**](config/sentinel-1.conf)

    ```conf
    # Host and port we will listen for requests on
    bind 172.16.17.32
    port 26379

    sentinel monitor redis-cluster 172.16.17.32 6379 2
    sentinel down-after-milliseconds redis-cluster 5000
    sentinel parallel-syncs redis-cluster 1
    sentinel failover-timeout redis-cluster 10000
    ```

- [**File konfigurasi redis sentinel 2**](config/sentinel-2.conf)

    ```conf
    # Host and port we will listen for requests on
    bind 172.16.58.3
    port 26379

    sentinel monitor redis-cluster 172.16.17.32 6379 2
    sentinel down-after-milliseconds redis-cluster 5000
    sentinel parallel-syncs redis-cluster 1
    sentinel failover-timeout redis-cluster 10000
    ```

- [**File konfigurasi redis sentinel 3**](config/sentinel-3.conf)

    ```conf
    # Host and port we will listen for requests on
    bind 172.16.31.10
    port 26379

    sentinel monitor redis-cluster 172.16.17.32 6379 2
    sentinel down-after-milliseconds redis-cluster 5000
    sentinel parallel-syncs redis-cluster 1
    sentinel failover-timeout redis-cluster 10000
    ```

**File Service Redis**

- [**File service redis**](service/redis.service)

    ```
    [Unit]
    Description=Redis In-Memory Data Store
    After=network.target

    [Service]
    User=redis
    Group=redis
    ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf
    ExecStop=/usr/local/bin/redis-cli shutdown
    Restart=always

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

**File Service Redis Sentinel**

- [**File service redis sentinel**](service/redis-sentinel.service)

    ```
    [Unit]
    Description=Redis Sentinel
    After=network.target

    [Service]
    User=redis
    Group=redis
    ExecStart=/usr/local/bin/redis-server /etc/redis-sentinel.conf --sentinel
    ExecStop=/usr/local/bin/redis-cli shutdown
    Restart=always

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

Kemudian kita buat file script untuk nantinya dijalankan pada masing-masing server redis. Kegunaan dari file script ini adalah untuk mengaktifkan service redis dan redis sentinel pada masing-masing server redis.

- [**File script redis 1**](bash/redis1.sh)
- [**File script redis 2**](bash/redis2.sh)
- [**File script redis 3**](bash/redis3.sh)

Contoh file script :

```bash
sudo cp /vagrant/config/redis-1.conf /etc/redis/redis.conf
sudo cp /vagrant/config/sentinel-1.conf /etc/redis-sentinel.conf

sudo cp /vagrant/service/redis.service /etc/systemd/system/redis.service
sudo cp /vagrant/service/redis-sentinel.service /etc/systemd/system/redisentinel.service

sudo adduser --system --group --no-create-home redis
sudo mkdir /var/lib/redis
sudo chown redis:redis /var/lib/redis
sudo chmod 770 /var/lib/redis

sudo systemctl start redis
sudo systemctl status redis

sudo chmod 777 /etc/redis-sentinel.conf
sudo systemctl start redisentinel
sudo systemctl status redisentinel

sudo chmod -R 777 /etc/redis
sudo systemctl restart redis
sudo systemctl status redis
```

Untuk menjalankan file script, lakukan perintah berikut pada server yang bersesuaian.

```
(redis-1) $ sudo bash /vagrant/bash/redis1.sh
```

![](img/starting_redis.png)

Jika semua script telah dijalankan pada masing-masing server redis, maka sudah terinstall redis dan redis-sentinel.

![](img/install_redis.png)

Cek pada masing-masing server apakah service redis dan redis sentinel sudah berjalan atau belum.

```
(redis-1) $ sudo systemctl status redis
```

![](img/redis_status.png)

```
(redis-1) $ sudo systemctl status redisentinel
```

![](img/redisentinel_status.png)

## Konfigurasi Web Server

Setelah semua server redis telah dikonfigurasi, sekarang saatnya untuk mengkonfigurasi web server. Nantinya web server akan diinstall Wordpress, sehingga membutuhkan app-stack lain seperti :

- web server
- backend (PHP)
- database

Web server yang digunakan adalah apache2. Kemudian untuk backend menggunakan PHP dan database menggunakan MySQL.

Pada semua web server, yang perlu dilakukan adalah :

### Installasi Apache2

Proses installasi apache2 sudah dilakukan pada saat melakukan provision.

```bash
# Apache Installation
$ sudo apt install apache2 -y
$ sudo ufw allow in "Apache Full"
```

![](img/download_apache.png)

Cek dengan mengetikkan alamat IP web server pada browser. Jika berhasil maka akan muncul halaman awal dari apache.

![](img/cek_apache.png)

### Installasi PHP dan PHP Redis Extension

```
$ sudo apt install php libapache2-mod-php php-mysql php-pear php-dev -y
$ sudo a2enmod mpm_prefork && sudo a2enmod php7.2
$ sudo pecl install redis
```

![](img/install_php1.png)
![](img/install_php2.png)

Setelah PHP Redis Extension terinstall, tambahkan perintah **`extension=redis.so`** pada file **`/etc/php/7.2/apache2/php.ini`**.

![](img/redis_so.png)

### Installasi MySQL

MySQL nantinya akan digunakan sebagai database dari WordPress. Untuk melakukan instalasi MySQL, perintahnya adalah sebagai berikut.

```
$ sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password password <PASSWORD>'
$ sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password <PASSWORD>'
$ sudo apt install mysql-server -y
```

![](img/install_mysql.png)

Selanjutnya adalah mengkonfigurasi MySQL dan membuat database untuk wordpress. Pertama masuk ke dalam mysql:

```
$ mysql -u root -padmin
```

Berikut adalah syntax konfigurasinya.

```sql
CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE USER 'wordpressuser'@'%' IDENTIFIED BY 'admin';
GRANT ALL PRIVILEGES on wordpress.* to 'wordpressuser'@'%';
FLUSH PRIVILEGES;
```

![](img/install_mysql3.png)


### Installasi Wordpress

Setelah semua hal yang dibutuhkan terinstall, maka saatnya melakukan instalasi Wordpress. Untuk melakukan instalasi wordpress, lakukan perintah berikut.

```
$ cd /tmp
$ wget -c http://wordpress.org/latest.tar.gz
$ tar -xzvf latest.tar.gz
```

![](img/wget.png)

Kemudian langkah selanjutnya adalah mengubah file permission dari file/folder wordpress dan menyalin file konfigurasi wordpress.

```
$ sudo mkdir -p /var/www/wordpress
$ sudo mv wordpress/* /var/www/wordpress
```
Berikut adalah file konfigurasi untuk masing-masing web server:
- [**File config wordpress1**](wordpress_config/wp-config.php)
- [**File config wordpress2**](wordpress_config/wp-config2.php)

Pada file konfigurasi tersebut, diatur nama database sesuai setting mysql sebelumnya.

Contoh pada file [file config wordpress1](wordpress_config/wp-config.php) :

![](img/db_conn_wp.png)

```
$ sudo cp /vagrant/wp-config.php /var/www/wordpress/
$ sudo chown -R www-data:www-data /var/www/wordpress/
$ sudo chmod -R 755 /var/www/wordpress/
$ sudo systemctl restart apache2
```

Instalasi wordpress sudah selesai. Sekarang coba masuk pada wordpress untuk mendaftarkan akun. Pada browser, pergi ke alamat `<IP_web_server>/index.php`.

Ikuti langkah-langkah yang tertera.

![](img/wp-1.png)

![](img/wp-2.png)

![](img/wp-3.png)

Akan muncul halaman awal wordpress

![](img/wp-4.png)

## Mengaktifkan Redis Cache Pada Salah Satu Web Server

Web server yang menggunakan Redis Cache adalah web server dengan alamat IP 192.168.3.11 (wordpress_2). Untuk mengaktifkan Redis Cache pada wordpress, perlu dilakukan instalasi plugin **Redis Object Cache**.

Masuk pada halaman dashboard wordpress pada alamat IP tersebut.

![](img/install_redis_cache.png)

Pada halaman plugin, install plugin **Redis Object Cache**.

![](img/redisObj.png)

Tambahkan perintah berikut pada **`/var/www/wordpress/wp-config.php`**. Perintah berikut digunakan untuk menghubungkan wordpress dengan redis.

![](img/conf_redisObj.png)

> Perintah tersebut harus diletakkan tepat dibawah **`define ('DB_COLLATE', '')`**

Cek pada status Diagnostic **Redis Object Cache** apakah sudah terhubung atau belum. Jika terhubung, maka akan muncul seperti ini.

![](img/Status_Connected.png)

# <NAME>

## Pengujian Fail-Over

Pengujian fail-over dilakukan dengan mematikan service redis master atau dengan mematikan sementara service redis master. Kali ini, yang akan dilakukan adalah mematikan sementara service redis master.

Pada server redis master, ketikan perintah berikut.

```
$ redis-cli -h 172.16.17.32 -p 6379 DEBUG sleep 60
```

![](img/redis_sleep.png)

Dengan hal ini, service redi pada server master akan mati selama 60 detik.

### Cek Fail-Over

Untuk memeriksa apakah fail-over berhasil, masuk pada server redis slave (redis_2 dan redis_3), kemudian masuk pada **`redis-cli`** dan mengetikkan perintah `info replication`

_Hasil pada server redis_2_

![](img/fail_overcek.png)

_Hasil pada server redis_3_

![](img/fail_overcek1.png)

Dapat diperhatikan bahwa, redis_2 lah yang menjadi master. Dapat dilihat pada **`role:master`** di redis_2.

## Pengujian Load Test Menggunakan JMeter

### Pengujian 50 Koneksi

![](img/jmeter50.png)

### Pengujian 100 + 20 Koneksi

![](img/jmeter120.png)

### Pengujian 200 + 20 Koneksi

![](img/jmeter220.png)

### Kesimpulan Hasil Pengujian

Berdasarkan data yang telah didapatkan, didapatkan bahwa wordpress yang menggunakan Redis Object Cache cenderung lebih lambat dari pada yang tidak menggunakan Redis Object Cache. Ini dapat diperhatikan pada hasil `Average` :

- Wordpress Non Redis: 20, 19, 20
- Wordpress Redis: 27, 26, 26

Dapat dilihat bahwa hasil rata-rata untuk tiap pengujian menunjukkan lebih baik apabila tidak menggunakan redis. Kemungkinan, hal ini terjadi karena jumlah permintaan ke server redis cenderung lebih banyak dari pada jumlah permintaan (request) ke mysql sendiri. Sehingga, semakin banyaknya permintaan ke server redis, akan menyebabkan overprocess pada server. Kemungkinan lainnya adalah alokasi RAM untuk server terlalu sedikit (512 MB) yang menurut saya kurang untuk server redis.

Untuk saran kedepannya adalah dengan menambah alokasi RAM maupun Storage penyimpanaan pada redis karena redis merupakan basis data yang bersifat in-memory (menggunakan memory sebagai penyimpanan).
<file_sep>INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (2, '172.16.31.10', 3306);
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (2, '172.16.31.10', 3306);
INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (2, '192.168.127.12', 3306);

<file_sep>### EAS Basis Data Terdistribusi

# Implementasi TiDB Cluster

**Nama: <NAME>**

**NRP : 05111740000020**

## Daftar Isi

- [A. Deskripsi Tugas](#a-deskripsi-tugas)
    + [Deskripsi Umum](#deskripsi-umum)
    + [Ketentuan Pembagian IP](#ketentuan-pembagian-ip)
- [B. Pembagian Node/Server](#b-pembagian-nodeserver)
- [C. Desain Arsitektur](#c-desain-arsitektur)
- [D. Implementasi](#d-implementasi)
    + [Vagrant](#vagrant)
    + [Script Provision](#script-provision)
    + [Build Vagrant](#build-vagrant)
    + [Konfigurasi Node/Server](#konfigurasi-nodeserver)
- [E. Pemanfaat Aplikasi](#e-pemanfaatan-aplikasi)
    + [Deskripsi Aplikasi](#deskripsi-aplikasi)
    + [Operasi Create](#operasi-create)
    + [Operasi Read](#operasi-read)
    + [Operasi Update](#operasi-update)
    + [Operasi Delete](#operasi-delete)
- [F. Uji Performa](#f-uji-performa)
    + [Uji Performa Database (Sysbench)](#uji-performa-database-sysbench)
    + [Uji Performa Aplikasi (JMeter)](#uji-performa-aplikasi-jmeter)
- [G. Monitoring Grafana dan Prometheus](#g-monitoring-grafana-dan-prometheus)
    + [Instalasi Node Exporter](#instalasi-node-exporter)
    + [Instalasi Prometheus](#instalasi-prometheus)
    + [Instalasi Grafana](#instalasi-grafana)
    + [Konfigurasi Grafana](#konfigurasi-grafana)
    + [Import Dashboard Grafana](#import-dashboard-grafana)
    + [Hasil Dashboard](#hasil-dashboard)
- [H. Uji Failover](#h-uji-failover)
    + [Cek Node Leader](#cek-node-leader)
    + [Mematikan Service Leader](#mematikan-service-leader)
    + [Cek Hasil](#cek-hasil)

## A. Deskripsi Tugas

### Deskripsi Umum

- Mengimplementasikan Arsitektur Sistem Basis Data Terdistribusi menggunakan TiDB (dengan catatan jumlah PD server harus 3).
- Pemanfaatan basis data terdistribusi dalam aplikasi. Aplikasi yang dapat dipilih antara lain :
    + Aplikasi CMS (lakukan instalasi dan konfigurasi)
    + Aplikasi yang dibuat sendiri & menerapkan CRUD (Berbasis web atau API)
- Uji performa aplikasi dan basis data (minimal terdapat 3 variasi):
    + Aplikasi : menggunakan JMeter
    + Basis Data : menggunakan Sysbench
- Menambahkan monitoring dashboard menggunakan Grafana
- Dokumentasi pengerjaan yang berisi :
    + Desain arsitektur (termasuk pembagian IP)
    + Cara penggunaan aplikasi
    + Uji performa
    + Uji fail over

### Ketentuan Pembagian IP

IP pertama cluster menggunakan format A.B.**C**.**D** dimana A dan B bebas, kemudian untuk **C** merupakan **tahun angkatan** dan **D** merupakan **NRP**.

Pada implementasi ini, skema IP yang digunakan adalah sebagai berikut:
- A = 172
- B = 5
- C = 17 (tahun angkatan)
- D = 20 (NRP)

IP: **172.16.17.32**

## B. Pembagian Node/Server

Terdapat 6 node/server yang akan digunakan pada implementasi TiDB cluster kali ini. Detail masing-masing pembagiannya disajikan dalam tabel berikut.

| Node   | Sistem Operasi | Memori | Alamat IP   | Service                                         |
| ------ | -------------- | ------ | ----------- | -----------------                               |
| Node 2 | CentOS 7       | 512 MB | 172.16.17.32 | Node Exporter, TiDB, PD, Grafana dan Prometheus |
| Node 1 | CentOS 7       | 512 MB | 172.16.17.32 | Node Exporter, PD                               |
| Node 3 | CentOS 7       | 512 MB | 172.16.58.3 | Node Exporter, PD                               |
| Node 4 | CentOS 7       | 512 MB | 192.168.127.12 | Node Exporter, TiKV                             |
| Node 5 | CentOS 7       | 512 MB | 172.16.58.3 | Node Exporter, TiKV                             |
| Node 6 | CentOS 7       | 512 MB | 192.168.3.11 | Node Exporter, TiKV                             |

## C. Desain Arsitektur

Desain arsitektur yang digunakan dalam implementasi ini adalah sebagai berikut.

![](img/diagram_arsitektur.png)

Sedangkan service-service yang ada pada tiap node diperlihatkan pada gambar berikut.

![](img/diagram_arsitektur2.png)

## D. Implementasi

### Vagrant

Sebelum memulai mengimplementasikan TiDB cluster, hal pertama yang harus dilakukan adalah membuat konfigurasi file vagrant yang berisi cluster-cluster yang akan digunakan sesuai dengan deskripsi sebelumnya.

```
$ vagrant init
```

Perintah tersebut akan membuatkan file Vagrantfile yang nantinya akan diisi dengan konfigurasi node/server. Konfigurasi file Vagrantfile adalah sebagai berikut.

- [**File vagrant**](VagrantFile)

### Script Provision

File Vagrant tersebut nantinya akan menggunakan provision untuk melakukan installasi service yang dibutuhkan pada node/server. File provision dapat dilihat dalam file berikut.

- [**File provision**](provision/bootstrap.sh)

### Build Vagrant

Setelah file provision dibuat, maka vagrant siap untuk di-build. Build vagrant menggunakan perintah:

```
$ vagrant up --provider virtualbox
```

Seperti inilah proses build vagrant menggunakan file provision yang telah dibuat.

_Proses mengunduh box centos7_

Perintah vagrant up akan menginisiasi building virtual machine dan menjalankan script provision menggunakan box geerlingguy/centos7. Apabila belum terdapat box geerlingguy/centos7, maka vagrant akan otomatis mengunduhnya.

![](img/vagrant_up1.png)

_Proses menyalakan VM_
![](img/vagrant_up2.png)

_Proses provisioning_
![](img/vagrant_up3.png)

### Konfigurasi Node/Server

Setelah semua node/server berhasil di-build, saatnya kini untuk konfigurasi node/server. Masuk ke masing-masing node dengan perintah `vagrant ssh <nama_node>`. Contoh :

```
$ vagrant ssh node1
```

Node 1, 2, dan 3 berperan sebagai Placement Driver. Untuk mengaktifkan service PD pada node-node tersebut, masuk ke dalam direktori instalasi TiDB :

```
$ cd tidb-v3.0-linux-amd64
```

kemudian jalankan perintah berikut:

Pada **Node 1** :

```bash
./bin/pd-server --name=pd1 \
                --data-dir=pd \
                --client-urls="http://172.16.17.32:2379" \
                --peer-urls="http://172.16.17.32:2380" \
                --initial-cluster="pd1=http://172.16.17.32:2380,pd2=http://172.16.17.32:2380,pd3=http://172.16.58.3:2380" \
                --log-file=pd.log &
```

Pada **Node 2** :

```bash
./bin/pd-server --name=pd2 \
                --data-dir=pd \
                --client-urls="http://172.16.17.32:2379" \
                --peer-urls="http://172.16.17.32:2380" \
                --initial-cluster="pd1=http://172.16.17.32:2380,pd2=http://172.16.17.32:2380,pd3=http://172.16.58.3:2380" \
                --log-file=pd.log &
```

Pada **Node 3** :

```bash
./bin/pd-server --name=pd3 \
                --data-dir=pd \
                --client-urls="http://172.16.58.3:2379" \
                --peer-urls="http://172.16.58.3:2380" \
                --initial-cluster="pd1=http://172.16.17.32:2380,pd2=http://172.16.17.32:2380,pd3=http://172.16.58.3:2380" \
                --log-file=pd.log &
```

Sedangkan, node 4, 5, dan 6 akan berperan sebagai Server TiKV. Untuk mengaktifkan service TiKV pada node-node tersebut, masuk ke dalam direktori instalasi TiDB :

```
[node4]$ cd tidb-v3.0-linux-amd64
```

kemudian jalankan perintah berikut :

Pada **Node 4** :

```bash
./bin/tikv-server --pd="172.16.17.32:2379,172.16.17.32:2379,172.16.58.3:2379" \
                --addr="192.168.127.12:20160" \
                --data-dir=tikv \
                --log-file=tikv.log &
```

Pada **Node 5** :

```bash
./bin/tikv-server --pd="172.16.17.32:2379,172.16.17.32:2379,172.16.58.3:2379" \
                --addr="172.16.58.3:20160" \
                --data-dir=tikv \
                --log-file=tikv.log &
```

Pada **Node 6** :

```bash
./bin/tikv-server --pd="172.16.17.32:2379,172.16.17.32:2379,172.16.58.3:2379" \
                --addr="192.168.3.11:20160" \
                --data-dir=tikv \
                --log-file=tikv.log &
```

Service TiDB server sendiri akan diletakkan pada node 1. Untuk mengaktifkannya, pada node 1 (pada direktori yang sama), jalankan perintah berikut.

**Node 1**:

```bash
./bin/tidb-server --store=tikv \
                --path="172.16.17.32:2379" \
                --log-file=tidb.log &
```

## E. Pemanfaatan Aplikasi

### Deskripsi Aplikasi

Aplikasi yang akan digunakan untuk penerapan TiDB cluster adalah aplikasi web sederhana yang dibuat menggunakan framework laravel. Fungsi dari aplikasi ini adalah untuk menyimpan blog-blog pribadi atau notes yang digunakan untuk keperluan pribadi saja. Cukup sederhana untuk menggambarkan bagaimana database bekerja pada web.

Aplikasi ini mencakup operasi-operasi basis data dasar seperti Create, Read, Update, dan Delete. Berikut adalah beberapa screenshot tampilan web tersebut.

![](img/login_app.png)
![](img/read_app.png)

### Konfigurasi Laravel

Hal yang perlu dikonfigurasi pada laravel adalah koneksi databasenya. Pada file **`.env`** atur agar databse connection mengarah pada IP node1 dan pada port 4000. Sesuaikan nama databasenya.

![](img/env-conf.png)

Kemudian melakukan migrasi database.

```php
php artisan migrate
```

### Operasi Create

Operasi **CREATE** yang diperlihatkan disini adalah untuk menambahkan post baru. Tampilan formnya seperti gambar di bawah ini.

![](img/create_app1.png)

Ketika berhasil disimpan, maka akan muncul pemberitahuan seperti dibawah.

![](img/create_app2.png)

### Operasi Read

Kemudian, untuk operasi **READ**, yang dilakukan adalah untuk menampilkan post yang telah dibuat sebelumnya. Tampilannya seperti di bawah ini.

![](img/read_app1.png)

### Operasi Update

Selanjutnya, operasi **UPDATE** yang dilakukan adalah dengan mengedit isi dari post sebelumnya yang telah dibuat. Misalkan, yang diubah adalah isi postnya. Tampilan formnya seperti gambar di bawah ini.

![](img/edit_app.png)

Ketika berhasil disimpan, maka akan muncul pemberitahuan seperti dibawah.

![](img/edit_app1.png)

### Operasi Delete

Operasi **DELETE** yang dilakukan adalah dengan menghapus post sebelumnya. Saat hendak menghapus, akan ada peringatan seperti gambar di bawah.

![](img/delete_app.png)

Ketika berhasil dihapus, maka akan muncul pemberitahuan seperti dibawah.

![](img/delete_app1.png)

## F. Uji Performa

### Uji Performa Database (Sysbench)

Pengujian performa dari database dilakukan dengan menggunakan sysbench. Variasi pengujian yang diterapkan adalah dengan jumlah PD cluster (1 PD cluster, 2 PD cluster, 3 PD cluster).

**Instalasi Sysbench**

- Instal sysbench pada node PD cluster manapun dengan menjalankan perintah berikut.

    ```
    $ curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.rpm.sh | sudo bash
    $ sudo yum -y install sysbench
    ```

- Langkah selanjutnya adalah dengan mendownload file konfigurasi beserta aplikasi untuk testing tidb. Clone github :

    ```
    $ git clone https://github.com/pingcap/tidb-bench.git
    ```

**Pengujian**

- Hal pertama yang perlu dilakukan adalah mengedit file konfigurasi agar sesuai dengan koneksi tidb pada node1. Edit file konfigurasi pada file `config`.

    ```
    $ cd tidb-bench/sysbench
    $ nano config
    ```

    Buat agar menjadi seperti gambar di bawah:

    ![](img/config-sysbench.png)

- Kemudian, langkah selanjutnya adalah membuat database `sbtest` pada tidb (**Node1**). Masuk pada service mysql untuk membuat database `sbtest`.

    ```
    $ mysql -h 172.16.17.32 -P 4000 -u root
    ```

    ```
    mysql[node1]> CREATE DATABASE sbtest;
    ```

- Lakukan persiapan pengujian menjalankan perintah :

    ```
    $ ./run.sh point_select prepare 100
    ```

- Kemudian jalankan pengujian dan tunggu hingga proses selesai.

    ```
    $ ./run.sh point_select run 100
    ```

- Hasil pengujian dapat dilihat pada file log yang dihasilkan dari menjalankan perintah `run`.

**Hasil Pengujian**

Dari pengujian menggunakan sysbench pada variasi jumlah PD cluster, didapatkan hasil sebagai berikut.

| Jumlah PD Cluster | Rata-rata latency (ms) | Total query (per detik) |
| ----------------- | ---------------------- | ----------------------- |
| 1 PD Cluster      | 19.31 ms               | 5172.43 query           |
| 2 PD Cluster      | 14.80 ms               | 6756.76 query           |
| 3 PD Cluster      | 10.67 ms               | 9369.73 query           |

Hasil pengujian dapat dilihat pada file berikut.
- [Hasil 1 PD Cluster](sysbench_res/1_PD.log)
- [Hasil 2 PD Cluster](sysbench_res/2_PD.log)
- [Hasil 3 PD Cluster](sysbench_res/3_PD.log)

**Kesimpulan**

Hasil pengujian hanya dengan melihat parameter rata-rata latency dan total query per detik, menunjukkan bahwa semakin banyak PD cluster yang digunakan, maka performa database akan semakin lebih cepat. Walaupun perubahan tersebut tidak terlalu signifikan.

### Uji Performa Aplikasi (JMeter)

Pengujian performa aplikasi dilakukan dengan menggunakan bantuan aplikasi JMeter. Variasi koneksi yang digunakan adalah 100 koneksi, 500 koneksi dan 1000 koneksi. Berikut adalah hasil dari pengujian menggunakan masing-masing koneksi.

**Uji 100 Koneksi**

![](img/JMeter_100.png)

**Uji 500 Koneksi**

![](img/JMeter_500.png)

**Uji 1000 Koneksi**

![](img/JMeter_1000.png)

## G. Monitoring Grafana dan Prometheus

### Instalasi Node Exporter

- Hal pertama yang perlu dilakukan adalah menginstal node exporter. Masuk ke dalam semua node (node1 - node6), kemudian masuk pada direktori node exporter (`cd node_exporter-0.18.1.linux-amd64`). Jalankan perintah berikut.

    ```bash
    ./node_exporter --web.listen-address=":9100" \
        --log.level="info" &
    ```

    ![](img/node_exporter.png)

### Instalasi Prometheus

- Download binary dari prometheus pada **Node1**, dan extract file tersebut.

    ```bash
    $ wget https://github.com/prometheus/prometheus/releases/download/v2.2.1/prometheus-2.2.1.linux-amd64.tar.gz
    $ tar -xzf prometheus-2.2.1.linux-amd64.tar.gz
    ```

- Edit file konfigurasi pada file **prometheus.yml**. Sesuaikan isinya seperti di bawah.

    ```yml
    global:
    scrape_interval:     15s  # By default, scrape targets every 15 seconds.
    evaluation_interval: 15s  # By default, scrape targets every 15 seconds.
    # scrape_timeout is set to the global default value (10s).
    external_labels:
        cluster: 'test-cluster'
        monitor: "prometheus"

    scrape_configs:
    - job_name: 'overwritten-nodes'
        honor_labels: true  # Do not overwrite job & instance labels.
        static_configs:
        - targets:
        - '172.16.17.32:9100'
        - '172.16.17.32:9100'
        - '172.16.58.3:9100'
        - '192.168.127.12:9100'
        - '172.16.58.3:9100'
        - '192.168.3.11:9100'

    - job_name: 'tidb'
        honor_labels: true  # Do not overwrite job & instance labels.
        static_configs:
        - targets:
        - '172.16.17.32:10080'

    - job_name: 'pd'
        honor_labels: true  # Do not overwrite job & instance labels.
        static_configs:
        - targets:
        - '172.16.17.32:2379'
        - '172.16.17.32:2379'
        - '172.16.58.3:2379'

    - job_name: 'tikv'
        honor_labels: true  # Do not overwrite job & instance labels.
        static_configs:
        - targets:
        - '192.168.127.12:20180'
        - '172.16.58.3:20180'
        - '192.168.3.11:20180'
    ```

    + [**File prometheus.yml**](prometheus/prometheus.yml)

- Jalankan service prometheus dengan menjalankan perintah di bawah.

    ```bash
    prometheus
    ./prometheus \
        --config.file="./prometheus.yml" \
        --web.listen-address=":9090" \
        --web.external-url="http://172.16.17.32:9090/" \
        --web.enable-admin-api \
        --log.level="info" \
        --storage.tsdb.path="./data.metrics" \
        --storage.tsdb.retention="15d" &
    ```

### Instalasi Grafana

- Untuk menginstal grafana, download binary dan extract file tersebut (pada **Node1**).

    ```bash
    $ wget https://dl.grafana.com/oss/release/grafana-6.5.1.linux-amd64.tar.gz
    $ tar -zxf grafana-6.5.1.linux-amd64.tar.gz
    ```

- Lakukan konfigurasi grafana pada file `grafana.ini`

    ```
    $ cd grafana-6.5.1
    $ nano conf/grafana.ini
    ```

    Isi file **`grafana.ini`**

    ```ini
    [paths]
    data = ./data
    logs = ./data/log
    plugins = ./data/plugins
    [server]
    http_port = 3000
    domain = 172.16.17.32
    [database]
    [session]
    [analytics]
    check_for_updates = true
    [security]
    admin_user = admin
    admin_password = <PASSWORD>
    [snapshots]
    [users]
    [auth.anonymous]
    [auth.basic]
    [auth.ldap]
    [smtp]
    [emails]
    [log]
    mode = file
    [log.console]
    [log.file]
    level = info
    format = text
    [log.syslog]
    [event_publisher]
    [dashboards.json]
    enabled = false
    path = ./data/dashboards
    [metrics]
    [grafana_net]
    url = https://grafana.net
    ```

- Jalankan service grafana dengan menjalankan perintah berikut.

    ```bash
    ./bin/grafana-server \
        --config="./conf/grafana.ini" &
    ```

### Konfigurasi Grafana

- Langkah selanjutnya adalah mengkonfigurasi grafana. Akses pada browser dengan URL **`http://[IP-node1]:3000`**. Contoh URL **`http://172.16.17.32:3000`**. Akan muncul halaman login seperti gambar di bawah.

    ![](img/grafana_login.png)

- Masuk dengan username **`admin`** dan password **`<PASSWORD>`**. Setelah masuk, akan ada halaman dashboard untuk mengelola grafana. Tambahkan dashboard baru (lihat gambar di bawah).

    ![](img/grafana_2.png)

- Pilih **Prometheus** sebagai servicenya.

    ![](img/grafana_3.png)

- Kemudian atur agar terhubung pada IP node1 port 9090. Simpan dan test connection.

    ![](img/url_prometheus.png)

### Import Dashboard Grafana

- Kemudian import dashboard grafana untuk menjalankan monitoring. Pada halaman awal, masuk ke **Manage Dashboard**

    ![](img/to_import-grafana.png)

- Akan muncul halaman seperti gambar di bawah. Tekan import untuk mulai mengimpor.

    ![](img/grafana_import.png)

- Dashboard diimport menggunakan file JSON yang dapat diperoleh dari [link berikut](https://github.com/pingcap/tidb-ansible/tree/master/scripts). Pada implementasi ini, terdapat lima dashboard yang digunakan, yakni :

    + [pd.json](prometheus/pd.json)
    + [tidb_summary.json](prometheus/tidb_summary.json)
    + [tidb.json](prometheus/tidb.json)
    + [tikv_details.json](prometheus/tikv_details.json)
    + [tikv_summary.json](prometheus/tikv_summary.json)

### Hasil Dashboard

![](img/grafana_pdcluster.png)
![](img/grafana_tidbcluster.png)
![](img/grafana_tidbclustersummary.png)
![](img/grafana_tikvcluster.png)

## H. Uji Failover

### Cek Node Leader

Untuk melakukan proses failover, kita perlu tahu terlebih dahulu siapakah yang menjadi node leader saat ini. Hal ini dapat diketahui dengan menjalankan perintah sebagai berikut pada sembarang node Placement Driver (Node1, Node2, Node3).

Misalkan pada **node1**:
```
$ curl http://172.16.17.32:2739/pd/api/v1/members
```

maka akan muncul informasi yang berisi detail member cluster dan node yang menjadi leader saat ini. Gambar pertama menunjukkan informasi member PD cluster.

![](img/member_check.png)

Sedangkan gambar kedua menunjukkan node leader saat ini. Dapat dilihat bahwa saat ini, **node yang menjadi leader adalah node 2**.

![](img/leader_check.png)

### Mematikan Service Leader

Langkah selanjutnya adalah dengan mematikan service pada leader. Karena leader saat ini adalah node 2, maka service PD dimatikan pada node 2. Untuk mematikan service dilakukan dengan _kill_ proses dari PD cluster.

- Temukan PID dari proses service PD.

    ```
    [node2]$ ps -aux | grep pd
    ```

    ![](img/ps-aux.png)

- Matikan (kill) proses tersebut

    ```
    [node2]$ sudo kill <PID>
    ```

### Cek Hasil

Setelah proses servis PD cluser pada node 2 dimatikan, maka seharusnya sekarang leadernya sudah berganti. Untu memeriksanya, masuklah pada node PD cluster lain dengan menjalankan perintah yang sama.

```
$ curl http://172.16.17.32:2739/pd/api/v1/members
```

Hasilnya seperti gambar di bawah. Terlihat sekarang bahwa leader PD cluster sekarang dipegang oleh node 3. Dengan demikian proses uji failover telah berhasil dilakukan.

![](img/leader_result.png)<file_sep># Copy the hosts list
sudo cp /vagrant/sources/hosts /etc/hosts

# Copy the new source list
sudo cp '/vagrant/sources/sources.list' '/etc/apt/'

# Update package
sudo apt update -y

# Install Redis and JeMalloc
sudo apt-get install build-essential tcl -y
sudo apt-get install libjemalloc-dev -y

curl -O http://download.redis.io/redis-stable.tar.gz
tar xzvf redis-stable.tar.gz
cd redis-stable
make
sudo make install

#Allow Port
sudo ufw allow 6379
sudo ufw allow 26379

# Create redis directory
sudo mkdir /etc/redis

<file_sep>SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;
<file_sep># -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|

	# Configuration for Wordpress Server
  (1..2).each do |i|
    config.vm.define "wordpress_#{i}" do |node|
      node.vm.hostname = "wordpress-#{i}"
      node.vm.box = "bento/ubuntu-18.04"
      node.vm.network "private_network", ip: "172.5.17.#{19+i}"

      node.vm.provider "virtualbox" do |vb|
        vb.name = "wordpress-#{i}"
        vb.gui = false
        vb.memory = "512"
      end

      node.vm.provision "shell", path: "provision/wordpress_provision.sh", privileged: false
    end
  end

	# Configuration for Redis Server
  (1..3).each do |i|
    config.vm.define "redis_#{i}" do |node|
      node.vm.hostname = "redis-#{i}"
      node.vm.box = "bento/ubuntu-18.04"
      node.vm.network "private_network", ip: "172.5.17.#{21+i}"

      node.vm.provider "virtualbox" do |vb|
        vb.name = "redis-#{i}"
        vb.gui = false
        vb.memory = "512"
      end

      node.vm.provision "shell", path: "provision/redis_provision.sh", privileged: false
    end
  end

end<file_sep>sudo cp /vagrant/config/redis-1.conf /etc/redis/redis.conf
sudo cp /vagrant/config/sentinel-1.conf /etc/redis-sentinel.conf

sudo cp /vagrant/service/redis.service /etc/systemd/system/redis.service
sudo cp /vagrant/service/redis-sentinel.service /etc/systemd/system/redisentinel.service

sudo adduser --system --group --no-create-home redis
sudo mkdir /var/lib/redis
sudo chown redis:redis /var/lib/redis
sudo chmod 770 /var/lib/redis

sudo systemctl start redis
sudo systemctl status redis

sudo chmod 777 /etc/redis-sentinel.conf
sudo systemctl start redisentinel
sudo systemctl status redisentinel

sudo chmod -R 777 /etc/redis
sudo systemctl restart redis
sudo systemctl status redis<file_sep># Script API Python using Flask
# and flask_pymongo

from bson.json_util import dumps
from bson.objectid import ObjectId
from flask import Flask
from flask_pymongo import PyMongo
from flask import jsonify, request

app = Flask(__name__)
app.secret_key = "SECRETKEY"
app.config["MONGO_URI"] = "mongodb://mongo-admin:<PASSWORD>@192.168.33.16:27017/populationData?retryWrites=false&authSource=admin"
mongo = PyMongo(app)

# Read Operation
@app.route('/populationHealthMeasure', methods=['GET'])
def get_data():
    datas = mongo.db.populationCollection.find()
    response = dumps(datas)
    return response

# Insert Operation
@app.route('/populationHealthMeasure', methods=['POST'])
def add_data():
    request_json = request.json
    data_uniqueid = request_json["unique_id"]
    data_3Year = request_json["3_year_period"]
    data_deathCause = request_json["cause_of_death"]
    data_Race = request_json["race"]
    data_HispanicLatino = request_json["hispanic_or_latino"]
    data_gender = request_json["gender"]
    data_ageAdjusted = request_json["age-adjusted_rate"]
    data_lowerConfidenceInterval = request_json["lower_confidence_interval"]
    data_upperConfidenceInterval = request_json["upper_confidence_interval"]

    data_id = mongo.db.populationCollection.insert({
        'unique_id' : data_uniqueid,
        '3_year_period' : data_3Year,
        'cause_of_death' : data_deathCause,
        'race' : data_Race,
        'hispanic_or_latino' : data_HispanicLatino,
        'gender' : data_gender,
        'age-adjusted_rate' : data_ageAdjusted,
        'lower_confidence_interval' : data_lowerConfidenceInterval,
        'upper_confidence_interval' : data_upperConfidenceInterval
    })

    response = jsonify('Data has added successfully. The id is {}'.format(data_id))
    response.status_code = 200
    return response

# Update Operation
@app.route('/populationHealthMeasure/<id>', methods=['PUT'])
def update_data(id):
    request_json = request.json
    data_id = request_json["_id"]
    data_uniqueid = request_json["unique_id"]
    data_3Year = request_json["3_year_period"]
    data_deathCause = request_json["cause_of_death"]
    data_Race = request_json["race"]
    data_HispanicLatino = request_json["hispanic_or_latino"]
    data_gender = request_json["gender"]
    data_ageAdjusted = request_json["age-adjusted_rate"]
    data_lowerConfidenceInterval = request_json["lower_confidence_interval"]
    data_upperConfidenceInterval = request_json["upper_confidence_interval"]

    mongo.db.populationCollection.update_one(
        {'_id': ObjectId(data_id['$oid']) if '$oid' in data_id else ObjectId(data_id)},
        {
            '$set' :
            {
                'unique_id' : data_uniqueid,
                '3_year_period' : data_3Year,
                'cause_of_death' : data_deathCause,
                'race' : data_Race,
                'hispanic_or_latino' : data_HispanicLatino,
                'gender' : data_gender,
                'age-adjusted_rate' : data_ageAdjusted,
                'lower_confidence_interval' : data_lowerConfidenceInterval,
                'upper_confidence_interval' : data_upperConfidenceInterval
            }
        }
    )

    response = jsonify('Data has updated successfully. The id is {}'.format(data_id))
    response.status_code = 200
    return response

# Delete Operation
@app.route('/populationHealthMeasure/<id>', methods=['DELETE'])
def delete_data(id):
    mongo.db.populationCollection.delete_one({
        '_id': ObjectId(id)
    })
    
    response = jsonify('Data has deleted successfully. The id is {}'.format(id))
    response.status_code = 200
    return response

# Aggregation Operation
@app.route('/populationHealthMeasure/cause_of_death', methods=['GET'])
def get_cause_of_death():
    result = mongo.db.populationCollection.aggregate([
        {
            "$group": {
                "_id": "$cause_of_death",
                "count": {"$sum": 1}
            } 
        }
    ])

    response = dumps(result)
    return response

# Aggregation Operation
@app.route('/populationHealthMeasure/max_upper_confidence', methods=['GET'])
def get_max_confidence():
    result = mongo.db.populationCollection.aggregate([
        {
            "$group": {
                "_id": "$cause_of_death",
                "minimum": {"$min": "$upper_confidence_interval"}
            }
        }
    ])

    response = dumps(result)
    return response

if __name__ == "__main__":
    app.run()