MongoDB 3.4 Sharding 觀念、安裝、與配置

Sharding 為 MongoDB 所擁有的一種資料分散處理架構,簡單的說就是將資料分片 (Shard) 儲存到不同的機器中,最常應用在大數據的案例上。在海量資料的儲存情境上,垂直擴充架構是無法滿足的,必須透過水平擴充來實現。

MongoDB Sharded Cluster 基本的架構示意如下:

在這樣的機制下,儲存進 Collection 的資料會被盡可能平均地分散到每一個 Shard 上,每一個 Shard 都是由獨立的 mongod 實體或者 Replica Set 所構成 (想了解 Replica Set 可以看看之前的文章)。上圖我們也可以看到,Sharding 機制本身是不負責備份的,在產品上線的環境中,強烈建議使用 Replica Set 來搭建 MongoDB Sharded Cluster。在這個架構中,假設我們有 1TB 的資料,可以透過 Sharding 機制將資料切分為四個 Shard,每一個 Shard 負責儲存 256G,應該很好理解。

MongoDB Sharded Cluster 運作機制

那麼 MongoDB Sharded Cluster 是如何運作的呢?我們先看看下面的架構圖:

當使用者或應用程式 (Driver) 要操作 MongoDB Sharded Cluster 時,是透過 mongos 來進行連接,mongos 扮演 Router 的角色,Router 通常由多個實體組成,可以將 Loading 分散處理,保持高可用性 (HA)。對於應用程式來說,並不需要瞭解 MongoDB Sharding 怎麼運作的,他們看到的只是一個資料庫,所以使用上基本不會有太大的差別。如下所示:

之前提到,雖然每一個 Shard 都是由一個 mongod 或 Replica Set 構成,但如果沒有透過 mongos (Router) 進行連線操作,而直接對 Shard 進行連線,那麼你看到 Collection 的資料就不會是完整的集合,而只是單一個 Shard 的資料。那麼 Cluster 是如何分配資料呢,這裡有一個很重要的角色,就是 Config Server。

Config Server

Config Server 是 MongoDB Sharding 架構中相當重要的一個角色,它存放了資料的 Metadata,包括透過 Shard Key 計算出來的索引,用來記錄每一個資料存放的 Shard 位置,好讓 Router 可以正確的 Query 資料,如果沒有這些索引,那麼每一個用來存放資料的 mongod 實體,就「純粹」只是獨立存放分散的資料,無法協同工作。Config Server 聽起來很重要對吧!?因此 Config Server 部署時必須要多台機器,在新版 3.2 之後也可以用 Replica Set 架設 Config Server。

為了將資料分散儲存,就必須找一個方法來管理 Index,我們必須將 Document 某一個欄位定義為 Shard Key,然後透過 range based partitioning 或 hash based partitioning 其中一中方式將資料分配到 chunks 中 (每一個 Chunks 預設的大小為 64MB),最後才將 Chunks 分散到不同的 Shard 上。接下來我們先看看兩種不同的 Partitioning 機制:

Range Based Sharding

這種方式就是將 Document Shard Key 欄位的值,以線性的方式進行分群,透過 Range 範圍進行切割,相近的值理所當然會被分到同一個 Chunk 中,Chunk 分配的情況會直接受到 Range 的影響(比如某一段 Range 出現頻率高,Chunk 資料就比較大)。用這種方法我們必須考慮 Shard Key 的範圍,如下所示:

Hash Based Sharding

這種方法顧名思義就是透過「雜湊函式」將我們指定的 Shard Key 欄位進行雜湊,這樣的方式資料比較容易分散到每個 Shard 中,如果資料量足夠豐富,佔用空間的分配也會比較平均。如下:

比較一下兩種方法,Range Based 實際在 Query 時,Router 可以很輕易地判斷資料的位置,然後正確地派送運算到所屬的 Shard 上。但如果要查詢的資料片段大小差異過高,且又分散在不同的 Shard 上,查詢必定會「等待」其他 Shard 處理的情況產生。來看看 Hash Based,Shard Key 透過 Hash 計算後,很容易分散在不同的 Chunk,資料分散性佳,擴充也比較容易。但是在 Query 時就必須要經過比較多的 Shard 計算,才能由 Router 返回最後的結果。兩種方法各有利弊,可以依照實際的應用情況選用。那如果想要自行實作資料分散的邏輯呢?MongoDB 當然也是支援的,就是透過 Tag 這個功能,但我沒有研究,所以就無從介紹了。有興趣可以看看官方 Tag Aware Sharding 相關介紹。

開始建置:

由於 Shard 本身沒有備援機制,因此 MongoDB 的 Shard 必須建置在 Replica Set 上,Replica Set 建置方式請參考「MongoDB Replica Set 高可用性架構搭建」這一篇文章。假設您已經建立好 Replica Set,那麼就可以繼續接著開始建置 Shard。

我們規劃原來做的mongsh1~mongsh3三台伺服器,都拿來做shard server與config server,另外mongsh1/mongsh2拿來當router。

建置 Config Server

一開始我們先建立 Config Server,在 MongoDB Sharding Cluster 架構中,Config Server 必須有三個實體以上,強烈建議部署在不同的機器上,因為 Metadata 實在太重要了。下面的範例其實是與 Replica Set 一起部署,實際上分開會比較好。慣例上我們會先透過 /etc/hosts 管理我們的主機資訊 (用 DNS 也是可以),假設要安裝 Config Server 的 Hostname 為 mong-cfg1 ~ mong-cfg3

vi /etc/hosts
10.21.1.45 mongsh1
10.21.1.46 mongsh2
10.21.1.44 mongsh3

10.21.1.45 mong-cfg1
10.21.1.46 mong-cfg2
10.21.1.44 mong-cfg3

10.21.1.45 mong-router1
10.21.1.46 mong-router2

接著建立 Config Server 要存放的 DB File Directory,命令如下:

mkdir -p /home/mongodb-cfg
sudo chown -R mongodb:mongodb /home/mongodb-cfg

三台伺服器都要做。

然後建立 MongoDB Config Server 需要的設定檔,之後再透過 mongod 來載入啟用,因為我們測試環境的 Config Server 與 Replica Set 都在其實同一台機器(正式環境建議分開或分散比較好),Port 與 PID 都要不同,Port 隔開可以方便我們部署在不同機器,然而透過不同的設定檔也會比較好進行管理。大致上的 Port 分配如下:

  • Replica Set:27019 Port
  • Config Server:27018 Port
  • Mongo Router (mongos):27017 Port

編輯 Config Server 設定檔內容如下:

vi /etc/mongod-cfgserv.conf
storage:
  dbPath: /home/mongodb-cfg
  journal:
    enabled: true
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod-cfgserv.log
sharding:
   clusterRole: configsvr
net:
  port: 27018
  bindIp: 0.0.0.0
processManagement:
  fork: true
  pidFilePath: /var/run/mongod-cfgserv.pid

上面的設定檔先不開啟認證模式,官方規定在 Production 環境最少要配置三台以上的 Config Server。透過以下 mongod 命令分別在「mong-cfg1, mong-cfg2, mong-cfg3」三台機器各別透過 mongod 指令來啟用 Config Server。

sudo mongod -f /etc/mongod-cfgserv.conf

啟動後我們也可以在 /var/log/mongodb/mongod-cfgserv.log 看到 log 資訊。

建置 MongoDB Router

再來就是建置 MongoDB Sharding Router。首先建立 mongos 要載入的設定檔,基本上跟上面 Config Server 使用的 mongod 差不多。我們這裡測試用的 Router 是跟 Config Server 放在一起,實際上可分開存放會更好。

這裡,我們就都不設定認證模式,若有意設置,可參考這篇文章

設定檔內容如下:

sudo vi /etc/mongod-router.conf
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod-router.log
sharding:
  configDB: mong-cfg1:27018,mong-cfg2:27018,mongodb-cfg-3:27018
net:
  port: 27017
  bindIp: 0.0.0.0 
processManagement:
  fork: true
  pidFilePath: /var/run/mongodb-router.pid

我們可以看到上述的設定檔有指定 Config Server 的位置,好讓 Router 啟動時可以讀取 Config Server 中的 Metadata。也需要配置到另外一台伺服器mong-router2

如同上述的設定檔,先不用啟用認證來執行 mongos,主要是為了要先在 Config Server 中建立 User 好讓後續整個認證流程可以順利串起來,這裡通常大家都會遇到最多問題,很多人最後乾脆關閉認證來配置 Sharding,這樣其實很危險,不建議關閉認證。此外由於應用程式大多是透過 27017 Port 進行連線,所以要提供連線的 Router Bind 直接啟動在標準 MongoDB Port 這樣應用程式就不用修改囉。

先啟用其中一台的 MongoDB Sharding Router,等透過 Mongo Router 設定好 Config Server 之後再啟動所有的 Rouer 即可。注意這裡是用 mongos 命令來啟動喔,如下:

sudo mongos -f /etc/mongod-router.conf

配置自動啟動config與router服務:
於10.21.1.45與10.21.1.46兩台伺服器

vi /etc/rc.local
mongod -f /etc/mongod-cfgserv.conf
mongos -f /etc/mongod-router.conf

於10.21.1.44則配置如下

vi /etc/rc.local
mongod -f /etc/mongod-cfgserv.conf

建立 Sharding Cluster

接著我們透過認證模式進入 Mongo Router 來設定 Sharding,因為後端的 Monogd 是採用Replica Set 認證模式,所以配置 Sharding 的動作一定要透過同一把 Key 啟動,才能把服務整個串起來。以下透過我們建立好的管理者帳密登入 Mongo Router 來進行配置:

mongo mongodb-router1
mongos>sh.addShard("bes/mongodb-a1:27019,mongodb-a2:27019,mongodb-a3:27019")

加入後,再檢查一下狀態:

mongos> sh.status()
--- Sharding Status --- 
  sharding version: {
	"_id" : 1,
	"minCompatibleVersion" : 5,
	"currentVersion" : 6,
	"clusterId" : ObjectId("59b4e7b564bf31ba36e4615c")
}
  shards:
	{  "_id" : "bes",  "host" : "bes/mongsh1:27019,mongsh2:27019,mongsh3:27019",  "state" : 1 }
  active mongoses:
	"3.4.8" : 2
 autosplit:
	Currently enabled: yes
  balancer:
	Currently enabled:  yes
	Currently running:  no
		Balancer lock taken at Sun Sep 10 2017 20:27:09 GMT+0800 (CST) by ConfigServer:Balancer
	Failed balancer rounds in last 5 attempts:  5
	Last reported error:  Cannot accept sharding commands if not started with --shardsvr
	Time of Reported error:  Wed Sep 13 2017 11:18:59 GMT+0800 (CST)
	Migration Results for the last 24 hours: 
		No recent migrations
  databases:
	{  "_id" : "teammerge12",  "primary" : "bes",  "partitioned" : true }

mongos> 

實務的配置上,我們會部署多台 Router 來平衡應用程式端的查詢,在應用程式中也可以指定這一群「Router」作為 Server。到這裡 MongoDB Sharding Cluster 就算是被置完成囉,實際的使用上還需要指定 Collection Shard Key 來分配 Chunk 到不同的 Shard 上,這裡請自行參考 MongoDB 官方的 Shard Key 使用介紹。

GeoIP installation for PHP on Ubuntu 16.04

Installing GeoIP Module On Apache2, Ubuntu 16.04

  1. 以下面這個指令尋找Geoip的相關程式:
    apt search geoip
    

    將會從相關的套件裡找到 libapache2-mod-geoip

  2. 進行套件安裝與更新
    apt update
    apt upgrade
    apt install geoip-bin geoip-database libapache2-mod-geoip libgeoip1
    

    新增後,會產生 /usr/share/GeoIP/ 目錄。

  3. 下載GeoIP資料庫
    cd /usr/share/GeoIP/
    ls
    rm GeoIP.dat
    wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
    gunzip GeoIP.dat.gz
    wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
    gunzip GeoLiteCity.dat.gz
    wget http://download.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz
    gunzip GeoIPASNum.dat.gz
    

    在 /usr/share/GeoIP/ 目錄裡,會有三個檔案GeoIP.dat, GeoLiteCity.dat, GeoIPASNum.dat。

  4. 更新apache2的geoip設置:nano /etc/apache2/mods-enabled/geoip.conf
    GeoIPEnable On
    GeoIPDBFile /usr/share/GeoIP/GeoIP.dat
    GeoIPDBFile /usr/share/GeoIP/GeoLiteCity.dat
    GeoIPDBFile /usr/share/GeoIP/GeoIPASNum.dat基本上就已經設定完成。
  5. 重新啟動apache2
    apachectl -t
    sudo systemctl restart apache2.service

最後做一個範本試試看:

<?php 
$geoip_country_code = getenv(GEOIP_COUNTRY_CODE);
$geoip_country_name = getenv(GEOIP_COUNTRY_NAME);
$geoip_region = getenv(GEOIP_REGION);
$geoip_city = getenv(GEOIP_CITY);
$geoip_postal_code = getenv(GEOIP_POSTAL_CODE);
$geoip_latitude = getenv(GEOIP_LATITUDE);
$geoip_longitude = getenv(GEOIP_LONGITUDE);
 
echo 'Country code: '.$geoip_country_code.'&lt;br&gt;';
echo 'Country name: '.$geoip_country_name.'&lt;br&gt;';
echo 'Region: '.$geoip_region.'&lt;br&gt;';
echo 'City: '.$geoip_city.'&lt;br&gt;';
echo 'Postal code: '.$geoip_postal_code.'&lt;br&gt;';
echo 'Latitude: '.$geoip_latitude.'&lt;br&gt;';
echo 'Longitude: '.$geoip_longitude.'&lt;br&gt;'; 
?>

大功告成!!

MongoDB Replica Set設置,採用MongoDB 3.4

安裝MongoDB 3.4 for CentOS 7

vi /etc/yum.repos.d/mongodb.repo
mongodb-org-3.4]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc

然後在執行

yum install -y mongodb-org

另外,需要將selinux(/etc/selinux/config)設定為SELINUX=Passive或Disable,詳細可參考:
https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/

MongoDB Replica Set介紹

MongoDB 算是一個很發展非常成熟的專案,大多數的系統裝起 MongoDB 應該都不會有太大的困難,今天介紹一下進階一點點的用法,Replica Set。資料庫系統做高可用 Replication (副本) 模式算是很常見的架構,在大陸稱為「主備模式」主要提供一份資料庫即時映射的副本,好讓主要的服務資料庫發生異常時可以接手工作。同時,另一個優勢就是可以進行資料備援,或者用來進行查詢。此外,當資料庫大到一定的程度,傳統的 dump 工具將很難完成任務,備援的方法就可以靠 Replication 來完成。

現在的 Open Source 系統發展得很快,官方提供的版本就早已經支援 Replication,想想以前的 PostgreSQL 在 5.x 的時候還要自己 Patch 第三方 Project 進行編譯,比起來真的省事多了。

官方有提供三種 Replica Set 架構 (Pattern),如下:

  • Three Member Replica Sets
  • Replica Sets with Four or More Members
  • Geographically Distributed Replica Sets

第一種 Three Member Replica Sets 是最常用的架構,由三個 MongoDB 節點組成,每個節點其實都是 Mongod 服務。第二種由更多的節點組成,備援能力更強,但我沒有研究,無從介紹。最後一種 Distributed Replica Sets 主要滿足異地備援的需求,這個聽起來不錯。

今天來介紹 MongoDB 最典型的副本架構,主要由三個節點構成,如下:

正常情況對 Primary 節點進行讀寫,資料會自動複製 (Replication) 到另外兩組 Secondary 節點,這些節點彼此透過 Heartbeat (每隔一段時間試探節點是否存活的方式) 檢測狀態,正常的情況每 2 秒會送出 Heartbeat,若是超過 10 秒沒有回覆,那就是 GG 了。如下:

當主要連線的節點發生故障時,會選出一台 Secondary 成為 Primary 接手工作,這樣的能力稱為 Automatic Failover,如下:

安裝與設定MongoDB Replica Set

大概知道工作原理以後,那麼我們來看看如何設定 Replica Set 架構。

開始之前我們先在 /etc/hosts 定義這三台要安裝的 hostname 與 IP Address,當然如果您想透過 DNS 服務來管理也是可以的。

vi /etc/hosts
10.21.1.45 mongsh1
10.21.1.46 mongsh2
10.21.1.44 mongsh3 

建立一個讓 Replica Set 專用的全新資料庫目錄

mkdir -p /home/mongod
chown -R mongodb:mongodb /home/mongod

三台伺服器皆需要做此設定,以方便確認資料庫的目錄以及位址。

設定 Replica Set 服務的 Mongod 設定檔(先不啟用驗證模式)

vi /etc/mongod.co
systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

storage:
  dbPath: /home/mongod
  journal:
    enabled: true

processManagement:
  fork: true  
  pidFilePath: /var/run/mongodb/mongod.pid  # location of pidfile

net:
  port: 27019
  bindIp: 0.0.0.0  

這裡我們將 Replica Set 服務設定在 27019 Port,主要是因為之後會繼續建立 Sharding 架構,刻意避開標準的 27017 Port,保留 27017 Port 給之後的 Mongo Router 使用。如果您不打算建構 Sharding,那就直接使用 27017 即可,讓應用程式端不需要修改就可以使用。修改完設定檔後記得重新啟動 mongod 服務:

systemctl start mongod

先連線進到第一台 mongod 設定管理者帳密,上述可先在第一台伺服器設置(primary)

mongo mongsh1:27019
use admin
db.createUser( {
    user: "myUserAdmin",
    pwd: "",
    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
  });
db.createUser( {
    user: "siteRootAdmin",
    pwd: "",
    roles: [ { role: "root", db: "admin" } ]
  });

也可以多新增一個admin帳號

db.createUser( {
    user: "admin",
    pwd: "",
    roles: [ { role: "root", db: "admin" } ]
  });

接著,透過以下命令建立一個 Key,並且複製到每一台機器 (mongsh1, mongsh2, mongsh3),之後每個 Mongod 實體會透過這把 Key 進行資料同步,接下來的動作比較複雜,順序不可以搞錯。很多人在這裡搞不定驗證功能,索性關閉驗證服務,這樣其實比較沒有保障,乖乖啟用驗證還是比較好的。

openssl rand -base64 741 > /home/mongod/mongodb-keyfile
chmod 600 /home/mongod/mongodb-keyfile
chown mongodb.mongodb /home/mongod/mongodb-keyfile

記得把這把 Key 放到每一台機器的 /home/mongodb-keyfile 檔案中。接下來我們就要啟用 Replica Set 與認證模式囉。再度編輯 /etc/mongod.conf 設定檔:

vim /etc/mongod.conf
security:
  keyFile: /var/lib/mongo/mongodb-keyfile
replication:
  replSetName: rs

在另外兩台伺服器可以修改設定檔為下

systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log
storage:
  dbPath: /home/mongod
  journal:
    enabled: true
processManagement:
  fork: true  
  pidFilePath: /var/run/mongodb/mongod.pid  # location of pidfile
net:
  port: 27019
  bindIp: 0.0.0.0  
security:
  keyFile: /home/mongod/mongodb-keyfile
replication:
 replSetName: bes

重新啟動 mongod 服務(另外兩台也要喔)

systemctl restart mongod

連線至剛剛的第一台 Mongod 並初始化 Replica Set,如下:

mongo mongsh1:27019
use admin
db.auth("siteRootAdmin", "");
rs.initiate()
rs.conf()

注意:正常完成時會有下面訊息出現:

接著新增另外兩台 Replica Set 節點,輸入以下命令:

rs.add("mongsh2:27019")
rs.add("mongsh3:27019")
rs.status()

注意:正常完成時會有下面訊息出現:

bes:PRIMARY> rs.status()
{
	"set" : "bes",
	"date" : ISODate("2017-09-09T14:51:02.381Z"),
	"myState" : 1,
	"term" : NumberLong(3),
	"heartbeatIntervalMillis" : NumberLong(2000),
	"optimes" : {
		"lastCommittedOpTime" : {
			"ts" : Timestamp(1504968659, 1),
			"t" : NumberLong(3)
		},
		"appliedOpTime" : {
			"ts" : Timestamp(1504968659, 1),
			"t" : NumberLong(3)
		},
		"durableOpTime" : {
			"ts" : Timestamp(1504968659, 1),
			"t" : NumberLong(3)
		}
	},
	"members" : [
		{
			"_id" : 0,
			"name" : "mongsh1:27019",
			"health" : 1,
			"state" : 1,
			"stateStr" : "PRIMARY",
			"uptime" : 4051,
			"optime" : {
				"ts" : Timestamp(1504968659, 1),
				"t" : NumberLong(3)
			},
			"optimeDate" : ISODate("2017-09-09T14:50:59Z"),
			"electionTime" : Timestamp(1504964638, 1),
			"electionDate" : ISODate("2017-09-09T13:43:58Z"),
			"configVersion" : 67972,
			"self" : true
		},
		{
			"_id" : 1,
			"name" : "mongsh2:27019",
			"health" : 1,
			"state" : 2,
			"stateStr" : "SECONDARY",
			"uptime" : 2961,
			"optime" : {
				"ts" : Timestamp(1504968659, 1),
				"t" : NumberLong(3)
			},
			"optimeDurable" : {
				"ts" : Timestamp(1504968659, 1),
				"t" : NumberLong(3)
			},
			"optimeDate" : ISODate("2017-09-09T14:50:59Z"),
			"optimeDurableDate" : ISODate("2017-09-09T14:50:59Z"),
			"lastHeartbeat" : ISODate("2017-09-09T14:51:00.676Z"),
			"lastHeartbeatRecv" : ISODate("2017-09-09T14:51:01.607Z"),
			"pingMs" : NumberLong(0),
			"syncingTo" : "mongsh1:27019",
			"configVersion" : 67972
		},
		{
			"_id" : 2,
			"name" : "mongsh3:27019",
			"health" : 1,
			"state" : 2,
			"stateStr" : "SECONDARY",
			"uptime" : 2961,
			"optime" : {
				"ts" : Timestamp(1504968659, 1),
				"t" : NumberLong(3)
			},
			"optimeDurable" : {
				"ts" : Timestamp(1504968659, 1),
				"t" : NumberLong(3)
			},
			"optimeDate" : ISODate("2017-09-09T14:50:59Z"),
			"optimeDurableDate" : ISODate("2017-09-09T14:50:59Z"),
			"lastHeartbeat" : ISODate("2017-09-09T14:51:00.676Z"),
			"lastHeartbeatRecv" : ISODate("2017-09-09T14:51:01.606Z"),
			"pingMs" : NumberLong(0),
			"syncingTo" : "mongsh1:27019",
			"configVersion" : 67972
		}
	],
	"ok" : 1
}
bes:PRIMARY> 

這樣就完成了,但如果出現有Statup誒
如果有伺服器出現:
“stateStr” : “STARTUP”,

例如:

bes:PRIMARY> rs.status()
{
	"set" : "bes",
	"date" : ISODate("2017-09-09T14:51:02.381Z"),
	"myState" : 1,
	"term" : NumberLong(3),
	"heartbeatIntervalMillis" : NumberLong(2000),
	"optimes" : {
		"lastCommittedOpTime" : {
			"ts" : Timestamp(1504968659, 1),
			"t" : NumberLong(3)
		},
		"appliedOpTime" : {
			"ts" : Timestamp(1504968659, 1),
			"t" : NumberLong(3)
		},
		"durableOpTime" : {
			"ts" : Timestamp(1504968659, 1),
			"t" : NumberLong(3)
		}
	},
	"members" : [
		{
			"_id" : 0,
			"name" : "mongsh1:27019",
			"health" : 1,
			"state" : 1,
			"stateStr" : "PRIMARY",
			"uptime" : 4051,
			"optime" : {
				"ts" : Timestamp(1504968659, 1),
				"t" : NumberLong(3)
			},
			"optimeDate" : ISODate("2017-09-09T14:50:59Z"),
			"electionTime" : Timestamp(1504964638, 1),
			"electionDate" : ISODate("2017-09-09T13:43:58Z"),
			"configVersion" : 67972,
			"self" : true
		},
		{
			"_id" : 1,
			"name" : "mongsh2:27019",
			"health" : 1,
			"state" : 0,
			"stateStr" : "STARTUP",
			"uptime" : 2961,
			"optime" : {
				"ts" : Timestamp(1504968659, 1),
				"t" : NumberLong(3)
			},
			"optimeDurable" : {
				"ts" : Timestamp(1504968659, 1),
				"t" : NumberLong(3)
			},
			"optimeDate" : ISODate("2017-09-09T14:50:59Z"),
			"optimeDurableDate" : ISODate("2017-09-09T14:50:59Z"),
			"lastHeartbeat" : ISODate("2017-09-09T14:51:00.676Z"),
			"lastHeartbeatRecv" : ISODate("2017-09-09T14:51:01.607Z"),
			"pingMs" : NumberLong(0),
			"syncingTo" : "mongsh1:27019",
			"configVersion" : 67972
		},
		{
			"_id" : 2,
			"name" : "mongsh3:27019",
			"health" : 1,
			"state" : 0,
			"stateStr" : "STARTUP",
			"uptime" : 2961,
			"optime" : {
				"ts" : Timestamp(1504968659, 1),
				"t" : NumberLong(3)
			},
			"optimeDurable" : {
				"ts" : Timestamp(1504968659, 1),
				"t" : NumberLong(3)
			},
			"optimeDate" : ISODate("2017-09-09T14:50:59Z"),
			"optimeDurableDate" : ISODate("2017-09-09T14:50:59Z"),
			"lastHeartbeat" : ISODate("2017-09-09T14:51:00.676Z"),
			"lastHeartbeatRecv" : ISODate("2017-09-09T14:51:01.606Z"),
			"pingMs" : NumberLong(0),
			"syncingTo" : "mongsh1:27019",
			"configVersion" : 67972
		}
	],
	"ok" : 1
}
#mongo localhost:27019
bes:PRIMARY> (有時候出現是bes:SECONDARY>,都可以設置)
bes:PRIMARY>use admin
bes:PRIMARY>db.auth("siteRootAdmin","")
bes:PRIMARY>var cfg={_id:"bes",members:[{_id:0,host:"mongsh1:27019"},{_id:1,host:"mongsh2:27019"},{_id:2,host:"mongsh3:27019"}]}
bes:PRIMARY>rs.reconfig(cfg)
bes:PRIMARY>rs.status()

這樣就大功告成了!!!

 

Reference:

MongoDB Replica Set 高可用性架構搭建

mongodb 副本集搭建

Git 指令

注意事項

由 project/.git/config 可知: (若有更多, 亦可由此得知)

  • origin(remote) 是 Repository 的版本
  • master(branch) 是 local 端, 正在修改的版本

平常沒事不要去動到 origin, 如果動到, 可用 git reset –hard 回覆到沒修改的狀態.

Git 新增檔案

  • git add . # 將資料先暫存到 staging area, add 之後再新增的資料, 於此次 commit 不會含在裡面.
  • git add filename
  • git add modify-file # 修改過的檔案, 也要 add. (不然 commit 要加上 -a 的參數)
  • git add -u # 只加修改過的檔案, 新增的檔案不加入.
  • git add -i # 進入互動模式

Git 刪除檔案

  • git rm filename

Git 修改檔名、搬移目錄

  • git mv filename new-filename

Git status 看目前的狀態

  • git status # 看目前檔案的狀態

Git Commit

  • git commit
  • git commit -m ‘commit message’
  • git commit -a -m ‘commit -message’ # 將所有修改過得檔案都 commit, 但是 新增的檔案 還是得要先 add.
  • git commit -a -v # -v 可以看到檔案哪些內容有被更改, -a 把所有修改的檔案都 commit

Git 產生新的 branch

  • git branch # 列出目前有多少 branch
  • git branch new-branch # 產生新的 branch (名稱: new-branch), 若沒有特別指定, 會由目前所在的 branch / master 直接複製一份.
  • git branch new-branch master # 由 master 產生新的 branch(new-branch)
  • git branch new-branch v1 # 由 tag(v1) 產生新的 branch(new-branch)
  • git branch -d new-branch # 刪除 new-branch
  • git branch -D new-branch # 強制刪除 new-branch
  • git checkout -b new-branch test # 產生新的 branch, 並同時切換過去 new-branch
  • # 與 remote repository 有關
  • git branch -r # 列出所有 Repository branch
  • git branch -a # 列出所有 branch

Git checkout 切換 branch

  • git checkout branch-name # 切換到 branch-name
  • git checkout master # 切換到 master
  • git checkout -b new-branch master # 從 master 建立新的 new-branch, 並同時切換過去 new-branch
  • git checkout -b newbranch # 由現在的環境為基礎, 建立新的 branch
  • git checkout -b newbranch origin # 於 origin 的基礎, 建立新的 branch
  • git checkout filename # 還原檔案到 Repository 狀態
  • git checkout HEAD . # 將所有檔案都 checkout 出來(最後一次 commit 的版本), 注意, 若有修改的檔案都會被還原到上一版. (git checkout -f 亦可)
  • git checkout xxxx . # 將所有檔案都 checkout 出來(xxxx commit 的版本, xxxx 是 commit 的編號前四碼), 注意, 若有修改的檔案都會被還原到上一版.
  • git checkout — * # 恢復到上一次 Commit 的狀態(* 改成檔名, 就可以只恢復那個檔案)

Git diff

  • git diff master # 與 Master 有哪些資料不同
  • git diff –cached # 比較 staging area 跟本來的 Repository
  • git diff tag1 tag2 # tag1, 與 tag2 的 diff
  • git diff tag1:file1 tag2:file2 # tag1, 與 tag2 的 file1, file2 的 diff
  • git diff # 比較 目前位置 與 staging area
  • git diff –cached # 比較 staging area 與 Repository 差異
  • git diff HEAD # 比較目前位置 與 Repository 差別
  • git diff new-branch # 比較目前位置 與 branch(new-branch) 的差別
  • git diff –stat

Git Tag

  • git tag v1 ebff # log 是 commit ebff810c461ad1924fc422fd1d01db23d858773b 的內容, 設定簡短好記得 Tag: v1
  • git tag 中文 ebff # tag 也可以下中文, 任何文字都可以
  • git tag -d 中文 # 把 tag=中文 刪掉

Git log

  • git log # 將所有 log 秀出
  • git log –all # 秀出所有的 log (含 branch)
  • git log -p # 將所有 log 和修改過得檔案內容列出
  • git log -p filename # 將此檔案的 commit log 和 修改檔案內容差異部份列出
  • git log –name-only # 列出此次 log 有哪些檔案被修改
  • git log –stat –summary # 查每個版本間的更動檔案和行數
  • git log filename # 這個檔案的所有 log
  • git log directory # 這個目錄的所有 log
  • git log -S’foo()’ # log 裡面有 foo() 這字串的.
  • git log –no-merges # 不要秀出 merge 的 log
  • git log –since=”2 weeks ago” # 最後這 2週的 log
  • git log –pretty=oneline # 秀 log 的方式
  • git log –pretty=short # 秀 log 的方式
  • git log –pretty=format:’%h was %an, %ar, message: %s’
  • git log –pretty=format:’%h : %s’ –graph # 會有簡單的文字圖形化, 分支等.
  • git log –pretty=format:’%h : %s’ –topo-order –graph # 依照主分支排序
  • git log –pretty=format:’%h : %s’ –date-order –graph # 依照時間排序

Git show

  • git show ebff # 查 log 是 commit ebff810c461ad1924fc422fd1d01db23d858773b 的內容
  • git show v1 # 查 tag:v1 的修改內容
  • git show v1:test.txt # 查 tag:v1 的 test.txt 檔案修改內容
  • git show HEAD # 此版本修改的資料
  • git show HEAD^ # 前一版修改的資料
  • git show HEAD^^ # 前前一版修改的資料
  • git show HEAD~4 # 前前前前一版修改的資料

Git reset 還原

  • git reset –hard HEAD # 還原到最前面
  • git reset –hard HEAD~3
  • git reset –soft HEAD~3
  • git reset HEAD filename # 從 staging area 狀態回到 unstaging 或 untracked (檔案內容並不會改變)

Git grep

  • git grep “te” v1 # 查 v1 是否有 “te” 的字串
  • git grep “te” # 查現在版本是否有 “te” 的字串

Git stash 暫存

  • git stash # 丟進暫存區
  • git stash list # 列出所有暫存區的資料
  • git stash pop # 取出最新的一筆, 並移除.
  • git stash apply # 取出最新的一筆 stash 暫存資料. 但是 stash 資料不移除
  • git stash clear # 把 stash 都清掉

Git merge 合併

  • Straight merge 預設的合併模式,會有全部的被合併的 branch commits 記錄加上一個 merge-commit,看線圖會有兩條 Parents 線,並保留所有 commit log。
  • Squashed commit 壓縮成只有一個 merge-commit,不會有被合併的 log。SVN 的 merge 即是如此。
  • cherry-pick 只合併指定的 commit
  • rebase 變更 branch 的分支點:找到要合併的兩個 branch 的共同的祖先,然後先只用要被 merge 的 branch 來 commit 一遍,然後再用目前 branch 再 commit 上去。這方式僅適合還沒分享給別人的 local branch,因為等於砍掉重練 commit log。

指令操作

  • git merge <branch_name> # 合併另一個 branch,若沒有 conflict 衝突會直接 commit。若需要解決衝突則會再多一個 commit。
  • git merge –squash <branch_name> # 將另一個 branch 的 commit 合併為一筆,特別適合需要做實驗的 fixes bug 或 new feature,最後只留結果。合併完不會幫你先 commit。
  • git cherry-pick 321d76f # 只合併特定其中一個 commit。如果要合併多個,可以加上 -n 指令就不會先幫你 commit,這樣可以多 pick幾個要合併的 commit,最後再 git commit 即可。

Git blame

  • git blame filename # 關於此檔案的所有 commit 紀錄

Git 還原已被刪除的檔案

  • git ls-files -d # 查看已刪除的檔案
  • git ls-files -d | xargs git checkout — # 將已刪除的檔案還原

Git 維護

  • git gc # 整理前和整理後的差異, 可由: git count-objects 看到.
  • git gc –prune
  • git fsck –full

Git revert 資料還原

  • git revert HEAD # 回到前一次 commit 的狀態
  • git revert HEAD^ # 回到前前一次 commit 的狀態
  • git reset HEAD filename # 從 staging area 狀態回到 unstaging 或 untracked (檔案內容並不會改變)
  • git checkout filename # 從 unstaging 狀態回到最初 Repository 的檔案(檔案內容變回修改前)

Git Rollback 還原到上一版

  • git reset –soft HEAD^
  • 編輯 + git add filename
  • git commit -m ‘rollback’

以下與 遠端 Repository 相關

Git remote 維護遠端檔案

  • git remote
  • git remote add new-branch http://git.example.com.tw/project.git # 增加遠端 Repository 的 branch(origin -> project)
  • git remote show # 秀出現在有多少 Repository
  • git remote rm new-branch # 刪掉
  • git remote update # 更新所有 Repository branch
  • git branch -r # 列出所有 Repository branch

抓取 / 切換 Repository 的 branch

  • git fetch origin
  • git checkout –track -b reps-branch origin/reps-branch # 抓取 reps-branch, 並將此 branch 建立於 local 的 reps-branch

刪除 Repository 的 branch

  • git push origin :heads/reps-branch
  • git push origin –delete reps-branch

Git 學習筆記 (1):安裝、選項設定、在本地使用 Git 工具

如何在NGINX+DJANGO平台設置SSL連線

於Django應用程式裡採用HTTPS對於保護用戶數據非常重要。 如果網頁應用程序具有用戶身份驗證,就是使用HTTPS的最好的理由。 否則,用戶名和密碼將通過HTTP以純文本格式傳播。 這意味著如果用戶使用公共互聯網連接,並且他登錄您的應用程序,他很容易受到駭客偷襲。

重要的是不僅安全的登錄,密碼更改和支付頁面與HTTPS,以及整個應用程序。 以下說明,將指導完成所有必要的步驟,以正確保護您的Django應用程式。

建立一個SSL證書

第一步是為您的Django應用程式獲取SSL證書。 有幾個選項:您可以生成自己的證書,您可以從Let’s Encrypt獲得一個免費的證書,或者您可以從互聯網上的許多公司購買一個證書。

在本教程中,我將使用從Namecheap註冊的Positive SSL的簡單商業SSL證書。

產生一個CSR代碼

CSR代表證書簽名請求,它是一個通常在服務器端生成的base64編碼數據。因為我們將使用Nginx作為web服務器,我們將使用openssl。

通常CSR openssl配置默認包含如下內容:

公共名稱(應頒發域名證書)
國家
州(或省)
地點(或城市)
組織
組織單位(部)
電子郵件地址

請在ssh的terminal上執行下列指令來產生CSR代碼:

openssl req -new -newkey rsa:2048 -nodes -keyout simpleacademy.key -out simpleacademy.csr

請注意,simpleacademy可以改為自己網域的名字
接著輸入相關訊息:

Country Name (2 letter code) [AU]:TW
State or Province Name (full name) [Some-State]:Taiwan
Locality Name (eg, city) []:Taipei
Organization Name (eg, company) [Internet Widgits Pty Ltd]:The Solar Systems Limited
Organizational Unit Name (eg, section) []:IT
Common Name (e.g. server FQDN or YOUR name) []:simple.academy
Email Address []:[email protected]

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:Simple is Better Than Complex

完成後就會產生兩個檔案,CSR跟KEY兩個檔案。
然後可以到NameCheap的Positive SSL來啟動有效的SSL認證金鑰。其他內容不再多做說明

安裝有效SSL金鑰

將驗證過的CSR與KEY放置到ㄧ個目錄,例如:/etc/nginx/ssl。

接著修改virtualhost的設定。

upstream simple_academy_server {
  server unix:/opt/simple_academy/run/gunicorn.sock fail_timeout=0;
}

# Redirect all non-encrypted to encrypted
server {
    server_name simple.academy;
    listen 80;
    return 301 https://simple.academy$request_uri;
}

server {
    server_name simple.academy;

    listen 443;  # <-

    ssl on;  # <-
    ssl_certificate /etc/nginx/ssl/simpleacademy_cert_chain.crt;  # <-crt所在位置
    ssl_certificate_key /etc/nginx/ssl/simpleacademy.key;  # <-key所在位置

    client_max_body_size 4G;

    access_log /opt/simple_academy/logs/nginx-access.log;
    error_log /opt/simple_academy/logs/nginx-error.log;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;  # <-這個很重要,記得加入
        proxy_set_header Host $http_host;
        proxy_redirect off;

        if (!-f $request_filename) {
            proxy_pass http://simple_academy_server;
            break;
        }
    }
}

這樣基本上就完成了!!