說明:用docker基於單服務器,虛擬多個服務器的方案,
以下是兩個config服務器,兩個分片,以及每個分片有一個副本的方案
這里關於給mongodb設置遠程密碼的問題,我采取了用兩個compser文件來解決的方式
也就是先不給mongodb配置keyfile(默認就是這樣的),任何人都可以連到mongodb,且有權限訪問和創建database。
啟動docker-compose-no-auth.yml后,創建admin賬號和密碼。
已經創建了管理員帳號了,然后再來設置mongodb需要賬號密碼訪問,其他子賬號都可以由admin去分配。
停掉之前的docker-compose-no-auth.yml
然后再次啟動docker-compose-auth.yml
這樣通過依次配置兩個compose文件來解決設置mongodb遠程連接的問題。
如果你有更好的方式,請給我留言,非常感謝!
1、安裝docker
#前往阿里雲,搜索<容器鏡像服務>開啟服務 #安裝參考阿里雲文檔 https://yq.aliyun.com/articles/110806?spm=5176.8351553.0.0.320d19912gmHZ6 #開啟鏡像加速使用阿里雲鏡像服務器 #進入阿里雲<容器鏡像服務> -- <鏡像中心> -- <鏡像加速器> #按里面的步驟添加配置
2、安裝docker-composer
#參考官方文檔https://docs.docker.com/compose/install/ sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose docker-compose --version
3、啟動docker-compose文件
首先啟動,這時還沒有設置遠程賬號密碼
docker-compose-no-auth.yml
version: '3' services: # config服務器一 config_one: container_name: config_one image: mongo:4.1.11 ports: - 27051:27019 volumes: - ./config_one/db:/data/db - ./config_one/configdb:/data/configdb command: --configsvr --replSet "rs_config" --bind_ip_all restart: always # config服務器二 config_two: container_name: config_two image: mongo:4.1.11 ports: - 27052:27019 volumes: - ./config_two/db:/data/db - ./config_two/configdb:/data/configdb command: --configsvr --replSet "rs_config" --bind_ip_all restart: always # 分片一的副本a shard_one_a: container_name: shard_one_a image: mongo:4.1.11 ports: - 27011:27018 volumes: - ./shard_one_a/db:/data/db - ./shard_one_a/configdb:/data/configdb - ./shard_one_a/backup:/data/backup command: --shardsvr --replSet "rs_shard_one_cluster" --bind_ip_all restart: always depends_on: - config_one - config_two # 分片二的副本a shard_two_a: container_name: shard_two_a image: mongo:4.1.11 ports: - 27012:27018 volumes: - ./shard_two_a/db:/data/db - ./shard_two_a/configdb:/data/configdb - ./shard_two_a/backup:/data/backup command: --shardsvr --replSet "rs_shard_two_cluster" --bind_ip_all restart: always depends_on: - config_one - config_two # 分片一的副本b shard_one_b: container_name: shard_one_b image: mongo:4.1.11 ports: - 27021:27018 volumes: - ./shard_one_b/db:/data/db - ./shard_one_b/configdb:/data/configdb - ./shard_one_b/backup:/data/backup command: --shardsvr --replSet "rs_shard_one_cluster" --bind_ip_all restart: always depends_on: - config_one - config_two # 分片二的副本b shard_two_b: container_name: shard_two_b image: mongo:4.1.11 ports: - 27022:27018 volumes: - ./shard_two_b/db:/data/db - ./shard_two_b/configdb:/data/configdb - ./shard_two_b/backup:/data/backup command: --shardsvr --replSet "rs_shard_two_cluster" --bind_ip_all restart: always depends_on: - config_one - config_two # mongos服務器一 mongos_one: container_name: mongos-one image: mongo:4.1.11 ports: - 27001:27017 volumes: - ./mongos_one/db:/data/db - ./mongos_one/configdb:/data/configdb entrypoint: mongos command: --configdb rs_config/192.168.10.188:27051,192.168.10.188:27052 --bind_ip_all depends_on: - shard_one_a - shard_two_a # mongos服務器二 mongos_two: container_name: mongos-two image: mongo:4.1.11 ports: - 27002:27017 volumes: - ./mongos_two/db:/data/db - ./mongos_two/configdb:/data/configdb entrypoint: mongos command: --configdb rs_config/192.168.10.188:27051,192.168.10.188:27052 --bind_ip_all depends_on: - shard_one_b - shard_two_b
4、配置分片,添加管理員
# 連接到任意一台配置服務器,初始化配置服副本 docker exec -it config_one /bin/bash mongo --host localhost --port 27019 rs.initiate({ _id: "rs_config", configsvr: true, members: [ { _id : 0, host : "172.19.60.174:27101" }, { _id : 1, host : "172.19.60.175:27101" } ] }); rs.status() # 連接到分片一,配置副本a、b docker exec -it shard_one_a /bin/bash mongo --host localhost --port 27018 rs.initiate({ _id: "rs_shard_one_cluster", members: [ { _id : 0, host : "172.19.60.174:27201" }, { _id : 1, host : "172.19.60.175:27201" } ] }); rs.status() # 連接到分片二,配置副本a、b docker exec -it shard_two_a /bin/bash mongo --host localhost --port 27018 rs.initiate({ _id: "rs_shard_two_cluster", members: [ { _id : 0, host : "172.19.60.174:27202" }, { _id : 1, host : "172.19.60.175:27202" } ] }); rs.status() # 連接到mongos服務器,添加分片 docker exec -it mongos-one /bin/bash mongo --host localhost --port 27017 sh.addShard("rs_shard_one_cluster/172.19.60.174:27201,172.19.60.175:27201"); sh.addShard("rs_shard_two_cluster/172.19.60.174:27202,172.19.60.175:27202"); sh.status() docker exec -it mongos-two /bin/bash mongo --host localhost --port 27017 sh.addShard("rs_shard_one_cluster/172.19.60.174:27201,172.19.60.175:27201"); sh.addShard("rs_shard_two_cluster/172.19.60.174:27202,172.19.60.175:27202"); sh.status() //為數據庫啟動分片 sh.enableSharding("test") //在需要分片的集合上對分片鍵建索引 //如果集合是空的,可以不創建索引直接進行下一步的分片會自動創建索引 //如果集合不為空,必須為分片建創建索引才行 db.test.ensureIndex({"id":1}) //設置分片鍵 //其中第一種是基於hash的分片,第二種為基於值的分片 sh.shardCollection( "test.student", { "id" : "hashed" } ) sh.shardCollection("test.teacher", { "id" : 1 } ) //查看片的狀態 sh.status(); //查看片狀態(完整版); printShardingStatus(db.getSisterDB("config"),1); //查看所有的分片服務器狀態 db.stats(); //測試分片數據 use test for (var i = 1; i <= 1000; i++) { db.student.insert( { "id" : i , "name": "student" + i } ) } //分別連接到兩個mongos, 分片一主副節點,分片二主副節點,查看是否數據都有 use test db.student.find().sort({id:1}); //生成管理員賬號 docker exec -it mongos-one /bin/bash mongo --host localhost --port 27017 use admin db.runCommand({ "createUser" : "root", "pwd" : "root^mongo", "customData" : { }, "roles" : [ { "role" : "dbAdminAnyDatabase", "db" : "admin" }, { "role" : "readWriteAnyDatabase", "db" : "admin" }, { "role" : "root", "db" : "admin" } ] });
5、生成keyfile
//生成keyfile openssl rand -base64 756 > mongo-keyfile.jks chmod 400 mongo-keyfile.jks
6、配置密鑰,使mongodb訪問需要帶密碼
帶keyfile的docker-compose-auth.yml
version: '3' services: # config服務器一 config_one: container_name: config_one image: mongo:4.1.11 ports: - 27051:27019 volumes: - ./config_one/db:/data/db - ./config_one/configdb:/data/configdb - ./mongo-keyfile.jks:/data/configdb/mongo-keyfile.jks command: --keyFile /data/configdb/mongo-keyfile.jks --configsvr --replSet "rs_config" --bind_ip_all restart: always # config服務器二 config_two: container_name: config_two image: mongo:4.1.11 ports: - 27052:27019 volumes: - ./config_two/db:/data/db - ./config_two/configdb:/data/configdb - ./mongo-keyfile.jks:/data/configdb/mongo-keyfile.jks command: --keyFile /data/configdb/mongo-keyfile.jks --configsvr --replSet "rs_config" --bind_ip_all restart: always # 分片一的副本a shard_one_a: container_name: shard_one_a image: mongo:4.1.11 ports: - 27011:27018 volumes: - ./shard_one_a/db:/data/db - ./shard_one_a/configdb:/data/configdb - ./shard_one_a/backup:/data/backup - ./mongo-keyfile.jks:/data/configdb/mongo-keyfile.jks command: --keyFile /data/configdb/mongo-keyfile.jks --shardsvr --replSet "rs_shard_one_cluster" --bind_ip_all restart: always depends_on: - config_one - config_two # 分片二的副本a shard_two_a: container_name: shard_two_a image: mongo:4.1.11 ports: - 27012:27018 volumes: - ./shard_two_a/db:/data/db - ./shard_two_a/configdb:/data/configdb - ./shard_two_a/backup:/data/backup - ./mongo-keyfile.jks:/data/configdb/mongo-keyfile.jks command: --keyFile /data/configdb/mongo-keyfile.jks --shardsvr --replSet "rs_shard_two_cluster" --bind_ip_all restart: always depends_on: - config_one - config_two # 分片一的副本b shard_one_b: container_name: shard_one_b image: mongo:4.1.11 ports: - 27021:27018 volumes: - ./shard_one_b/db:/data/db - ./shard_one_b/configdb:/data/configdb - ./shard_one_b/backup:/data/backup - ./mongo-keyfile.jks:/data/configdb/mongo-keyfile.jks command: --keyFile /data/configdb/mongo-keyfile.jks --shardsvr --replSet "rs_shard_one_cluster" --bind_ip_all restart: always depends_on: - config_one - config_two # 分片二的副本b shard_two_b: container_name: shard_two_b image: mongo:4.1.11 ports: - 27022:27018 volumes: - ./shard_two_b/db:/data/db - ./shard_two_b/configdb:/data/configdb - ./shard_two_b/backup:/data/backup - ./mongo-keyfile.jks:/data/configdb/mongo-keyfile.jks command: --shardsvr --replSet "rs_shard_two_cluster" --keyFile /data/configdb/mongo-keyfile.jks --bind_ip_all restart: always depends_on: - config_one - config_two # mongos服務器一 mongos_one: container_name: mongos-one image: mongo:4.1.11 ports: - 27001:27017 volumes: - ./mongos_one/db:/data/db - ./mongos_one/configdb:/data/configdb - ./mongo-keyfile.jks:/data/configdb/mongo-keyfile.jks entrypoint: mongos command: --keyFile /data/configdb/mongo-keyfile.jks --configdb rs_config/192.168.10.188:27051,192.168.10.188:27052 --bind_ip_all depends_on: - shard_one_a - shard_two_a # mongos服務器二 mongos_two: container_name: mongos-two image: mongo:4.1.11 ports: - 27002:27017 volumes: - ./mongos_two/db:/data/db - ./mongos_two/configdb:/data/configdb - ./mongo-keyfile.jks:/data/configdb/mongo-keyfile.jks entrypoint: mongos command: --keyFile /data/configdb/mongo-keyfile.jks --configdb rs_config/192.168.10.188:27051,192.168.10.188:27052 --bind_ip_all depends_on: - shard_one_b - shard_two_b
注意mongo-keyfile.jks放的目錄位置,最后再次啟動docker,這時候的mongodb訪問就需要賬號密碼了
docker-compose -f docker-compose-auth.yml up -d
7、安裝php mongodb擴展
擴展源碼下載地址:http://pecl.php.net/package/mongodb wget -c https://pecl.php.net/get/mongodb-1.5.4.tgz tar -zxvf mongodb-1.5.4.tgz cd mongodb-1.5.4 /www/server/php/72/bin/phpize ./configure --with-php-config=/www/server/php/72/bin/php-config make && make install vim /www/server/php/72/etc/php.ini extension = /www/server/php/72/lib/php/extensions/no-debug-non-zts-20170718/mongodb.so
8、封裝mongodb使用類
我這里開始使用的thinkphp5官方給的mongodb連接庫
參考:
https://packagist.org/packages/noprom/think-mongo-extend
https://www.kancloud.cn/manual/thinkphp5/167865
后來發現tp5官方的庫,太難用了,做不了復雜的條件查詢,於是自己寫了一個,
最后面有使用的示例,php mongodb官方有更多很詳細的復雜查詢的示例,使用文檔參考:
Connection.php
<?php namespace app\util\mongo; use MongoDB\Driver\BulkWrite; use MongoDB\Driver\Command; use MongoDB\Driver\Query; use MongoDB\Driver\Manager; class Connection { protected $config = []; protected $server = []; protected $manager; public function __construct(array $config = []) { if (!class_exists('\MongoDB\Driver\Manager')) { throw new Exception('require mongodb > 1.0'); } $this->config = config('mongo'); if (!empty($config)) { $this->config = array_merge($this->config, $config); } if (!isset($this->manager)) { $index = mt_rand(0, $this->config['master_num'] - 1); $server = $this->config['servers'][$index]; $this->server = $server; $this->manager = new Manager("mongodb://".$server['username'].":".$server['password']."@".$server['host'].":".$server['port']."/".$server['database']); } } public function query($table, $filter, $options) { $namespace = $this->server['database'].'.'.$table; $query = new Query($filter, $options); $rows = $this->manager->executeQuery($namespace, $query); $rows->setTypeMap(['root' => 'array', 'document' => 'array', 'array' => 'array']); return $rows->toArray(); } public function insert($table, $data) { $namespace = $this->server['database'].'.'.$table; $bulk = new BulkWrite(); $bulk->insert($data); $result = $this->manager->executeBulkWrite($namespace, $bulk); return $result->getInsertedCount(); } public function update($table, $filter, $data, $options = []) { $namespace = $this->server['database'].'.'.$table; $bulk = new BulkWrite(); $bulk->update($filter, $data, $options); $result = $this->manager->executeBulkWrite($namespace, $bulk); return $result->getMatchedCount(); } public function delete($table, $filter, $options = []) { $namespace = $this->server['database'].'.'.$table; $bulk = new BulkWrite(); $bulk->delete($filter, $options); $result = $this->manager->executeBulkWrite($namespace, $bulk); return $result->getDeletedCount(); } public function command($cmd) { $namespace = $this->server['database']; $rows = $this->manager->executeCommand($namespace, $cmd); $rows->setTypeMap(['root' => 'array', 'document' => 'array', 'array' => 'array']); return $rows->toArray(); } function aggregate($table, $pipeline){ $cmdOption = [ 'aggregate' => $table, 'pipeline' => $pipeline, 'cursor' => new \stdClass(), ]; $cmd = new Command($cmdOption); return $this->command($cmd); } }
Mongo.php
<?php namespace app\util\mongo; /** * Class Db * @package think * @method Query cache(mixed $key = null , integer $expire = null) static 設置查詢緩存 * @method mixed value(string $field) static 獲取某個字段的值 * @method array column(string $field, string $key = '') static 獲取某個列的值 * @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 視圖查詢 * @method mixed find(mixed $data = null) static 查詢單個記錄 * @method mixed select(mixed $data = null) static 查詢多個記錄 * @method void commit() static 用於非自動提交狀態下面的查詢提交 * @method void rollback() static 事務回滾 * @method boolean batchQuery(array $sqlArray) static 批處理執行SQL語句 * @method string getLastInsID($sequence = null) static 獲取最近插入的ID */ class Mongo { /** * @var Connection[] 數據庫連接實例 */ private static $instance = []; public static function connect($config = []) { if (empty($config)) { $config = config('mongo'); } $name = md5(serialize($config)); if (!isset(self::$instance[$name])) { self::$instance[$name] = new Connection($config); } return self::$instance[$name]; } /** * 調用驅動類的方法 * @access public * @param string $method 方法名 * @param array $params 參數 * @return mixed */ public static function __callStatic($method, $params) { return call_user_func_array([self::connect(), $method], $params); } }
MyExample.php
$cmd = [ ['$match' => ["user_id" => ['$eq' => intval($user_id)]]], ['$project' => [ "user_id" => 1, "book_author" => 1, "book_sub_type" => ['$filter' => ['input' => '$book_sub_type', 'as' => 'item', 'cond' => ['$eq' => ['$$item.sex', intval($sex)]]]], "book_type" => ['$filter' => ['input' => '$book_type', 'as' => 'item', 'cond' => ['$eq' => ['$$item.sex', intval($sex)]]]], ]], ['$project' => [ "user_id" => 1, "book_author" => ['$slice' => ['$book_author', $tag_author_size]], "book_sub_type" => ['$slice' => ['$book_sub_type', $tag_size]], "book_type" => ['$slice' => ['$book_type', $tag_size]], ]], ]; $tags = Mongo::connect()->aggregate('user_tags', $cmd);