shengyayun 10 X 10
shengyayun:~$ ssh shengyayun@tech
Welcome to shengyayun's Notebook.

shengyayun:~/blog$ cat ./tags
collectd centos Seata docker grafana kubernetes influxdb java maven manifest mongodb mysql nacos nginx php prometheus raft ssh swoole voltdb

shengyayun:~/blog$ ls post/part2
2018-09-29 voltdb的部署
2018-09-29 SSH自动断开的解决方法
2018-05-26 maven打包时在META-INF/MANIFEST.MF中添加主清单属性
2018-04-26 nginx站点访问日志
2017-12-10 mongodb的Replica Sets
2017-11-12 swoole_http_server中request的全局对象
2017-10-01 用php实现curl的data-binary
2017-09-30 grafana的部署
2017-09-30 collectd的部署
2017-09-30 influxdb的部署

shengyayun:~/blog$ curl sites.list
Google

voltdb的部署

导言

Voltdb作为分布式的内存数据库,企业版比社区版主要多提供了命令日志(事务级别的持久化日志)、不停机的弹性扩容、用于灾难恢复的数据库备份、跨数据中心的备份。

一. 下载安装

1
2
3
4
wget https://downloads.voltdb.com/technologies/server/voltdb-latest.tar.gz
tar -zxvf voltdb-latest.tar.gz
mv voltdb-community-7.8.2 /usr/local/voltdb
mkdir /data/voltdb #这个目录存放voltdb的数据

二. 系统配置

  1. voltdb不支持大页内存,终端执行以下指令,同时将它们写入/etc/rc.local文件:
    1
    2
    echo never > /sys/kernel/mm/transparent_hugepage/enabled
    echo never > /sys/kernel/mm/transparent_hugepage/defrag
  2. 将voltdb的可执行文件加入PATH:
    1
    2
    3
    4
    #更新Path
    echo 'PATH=/usr/local/voltdb/bin:$PATH' >> /etc/profile
    #终端执行以下指令立刻执行变更:
    source /etc/profile

三. 初始化和运行

1
2
voltdb init --dir /data/voltdb/ #如果以前启动过,需要强制初始化,加上 --force
voltdb start --dir /data/voltdb/

四. 测试数据插入

1 . 终端执行sqlcmd进入sql终端:

1
2
create table test(id int); #创建测试表
create procedure ping as insert into test(id) values(?); #创建存储过程

2 . 在终端通过http执行存储过程:

1
2
curl -d "Procedure=ping&Parameters=%5B0%5D" "http://localhost:8080/api/1.0/"
#其中%5B0%5D未转码前是`[0]`,所以这里等同于 `call ping (0)`

五. 常用指令

1
2
3
4
5
6
7
8
voltadmin save #立刻生成快照
voltadmin shutdown #停止集群(不要直接停止voltdb进程,应该使用voltadmin)
voltadmin shutdown —save #停止集群并生成快照
voltadmin pause #停止客户端服务,进入维护模式
voltadmin resume #退出维护模式
voltdb start --pause
voltadmin save --blocking {path} {file-prefix} #在path目录下生成file-prefix为前缀的备份文件
voltadmin restore {path} {file-prefix}

六. 使用须知

  1. 所有节点的内存大小与核数应保持一致。
  2. 每个节点的默认分区数为8,推荐分区数为:cpu核数 × 0.75 (非超线程处理器)。
  3. 每个分区有且只有一个线程读写,不存在锁冲突。分区越多并发越高,自然性能也越高。
  4. 一个表只有一个分区列,每个表通过hash分区列,把每行数据分布到不同的分区。
  5. 合理的分区列直接决定该表的增删改查性能。
  6. 如果分区表存在主键,那么主键中必须包含分区列。
  7. 分区列不能为空,且必须为TINYINTSMALLINTINTEGERBIGINTVARCHAR类型。
  8. 没有分区列的表是复制表,全部分区都会保存一份。所以虽然插入与存储成本高,但是查询成本低,适合更新较少但查询较多的小表。
  9. 存在数据的列无法添加唯一、假定唯一、主键约束。
  10. 被存储过程引用的表无法直接删除,需先删除对应存储过程。被索引和视图引用的表也无法直接删除,但是可以用DROP TABLE 表名 IF EXISTS CASCADE来级联删除。
  11. K-Safe的值代表可以保证数据不丢失的最小可以同时发生故障的服务器数。假设为K-Safe值为n,节点数为a,每个节点分区数为b,那么一条数据有n+1个拷贝,每a×b/(n+1)个分区存有一份完整的数据。
  12. 集群间每台服务器需开启NTP保证节点间时间差异不超过200ms。
  13. 存储过程返回结果不能超过50MB。
SSH自动断开的解决方法

在服务端(centos)执行以下指令:

1
2
3
echo "ClientAliveInterval 60" >> /etc/ssh/sshd_config  
echo "ClientAliveCountMax 1" >> /etc/ssh/sshd_config
service sshd restart
maven打包时在META-INF/MANIFEST.MF中添加主清单属性

发现问题:

默认使用mvn clean package生成的jar文件中的 MANIFEST.MF 是这样的:

1
2
3
4
Manifest-Version: 1.0
Created-By: Apache Maven 3.3.9
Built-By: shengyayun
Build-Jdk: 10.0.1

这种jar文件执行的时候会返回 xxx.jar中没有主清单属性

解决方法:

修改 pom.xml文件:

1
2
3
4
5
6
7
8
9
10
11
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>com.shengyayun.App</Main-Class> #划重点
</manifestEntries>
</archive>
</configuration>
</plugin>

现在重新打包生成的MANIFEST.MF中多了一行Main-Class: com.shengyayun.App

nginx站点访问日志

一. 在nginx.conf的http块中添加日志格式main2018

1
log_format main2018 '$remote_addr|$remote_user|$time_local|$request|$status|$body_bytes_sent|$http_referer|$http_user_agent|$http_x_forwarded_for|$request_time|$upstream_response_time|$upstream_addr|$upstream_status';

二. 在对应项目的server中配置该站点的访问日志:

1
access_log /var/log/www.access main2018; #使用第一步中添加的格式main2018

三. 重启nginx

1
2
nginx -t #判断配置正确
nginx -s reload

四. 查看访问日志

在服务器上执行如下指令查看最近一次访问的记录:

1
tail -n 1 /var/log/www.access | awk -F "|" '{printf "客户端地址: %s\n访问时间和时区: %s\n客户端用户名称: %s\n请求的URI和HTTP协议: %s\nHTTP请求状态: %s\n发送给客户端文件内容大小: %s\nurl跳转来源: %s\n客户端信息: %s\nHTTP的请求端真实的IP: %s\n请求的总时间: %s\nupstream响应时间: %s\nupstream地址: %s\nupstream状态: %s\n\n",$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13}

结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
客户端地址: 115.238.29.221
访问时间和时区: -
客户端用户名称: 26/Apr/2018:17:29:20 +0800
请求的URI和HTTP协议: POST /api HTTP/1.1
HTTP请求状态: 200
发送给客户端文件内容大小: 260
url跳转来源: -
客户端信息: okhttp/3.8.1
HTTP的请求端真实的IP: 115.238.29.223
请求的总时间: 0.017
upstream响应时间: 0.017
upstream地址: 127.0.0.1:9000
upstream状态: 200
mongodb的Replica Sets

导言

  1. 我使用了3台服务器来部署Replica Sets,分别为主节点ma、从节点mb、仲裁节点mc(这里的ma、mb、mc已经被我配置进了hosts,可以直接被解析为ip)。
  2. 其中主节点负责写与实时读,从节点负责非实时读(主从数据同步存在延迟)。
  3. 仲裁节点不提供数据读写支持,但会在主节点停止工作后将从节点升级为主节点。

一. 安装

新建文件**/etc/yum.repos.d/mongodb-enterprise.repo**,内容如下:

1
2
3
4
5
6
[mongodb-enterprise]
name=MongoDB Enterprise Repository
baseurl=https://repo.mongodb.com/yum/redhat/$releasever/mongodb-enterprise/3.4/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc

然后直接通过yum安装:

1
yum install mongodb-enterprise

二. 编辑配置文件

执行vi /etc/mongod.conf,配置内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#monogodb

systemLog:
destination: file #日志输出目的地
logAppend: true #当mongod/mongos重启后,是否在现有日志的尾部继续添加日志
path: /var/log/mongodb/mongod.log #日志路径

storage:
dbPath: /var/lib/mongo #mongod进程存储数据目录
indexBuildRetry: true #当构建索引时mongod意外关闭,那么再次启动重新构建索引
directoryPerDB: true #不同DB的数据存储在不同的目录中
journal:
enabled: true #开启journal日志持久存储,journal日志用来数据恢复

processManagement:
fork: true #主进程fork出一个子进程在后台执行
pidFilePath: /var/run/mongodb/mongod.pid #pid文件地址

net:
port: 27017 #默认端口

security:
authorization: enabled #是否开启用户访问控制
clusterAuthMode: keyFile #集群中members之间的认证模式
keyFile: /etc/mongodb-keyfile #keyfile的位置
javascriptEnabled: false #是否关闭server端的javascript功能


replication:
replSetName: rs0 #复制集的名称
oplogSizeMB: 8192 #replication操作日志的最大尺寸,单位:MB

三. 在全部节点上都需要进行步骤一和步骤二,全部完成后再进行以下操作

四. 配置集群间认证文件mongodb-keyfile

1 . 在主节点ma上执行openssl rand -base64 745 > /etc/mongodb-keyfile

2 . 将生成的**/etc/mongodb-keyfile**文件复制到其他节点的相同路径。

3 . 所有的节点上修改该文件的拥有者与权限:

1
2
chmod 600 /etc/mongodb-keyfile
chown mongod:mongod /etc/mongodb-keyfile

五. 在主节点进行集群初始化和管理员账号设置

1 . 编辑主节点的配置文件**/etc/mongod.conf**,将security下的authorization编辑为disabled

authorization为enable时无法创建管理员账号。

2 . 主节点执行以下指令:

1
2
systemctl start mongod.service
systemctl enable mongod.service

3 . 执行mongo进入mongo shell,然后执行以下指令:

1
2
rs.initiate()
db.createUser({user: "root",pwd: "非常复杂的超级管理员密码",roles: [ { role: "root", db: "admin" } ]});

4 . 退出mongo shell,编辑**/etc/mongod.conf**,将security下的authorization改回为enabled
5 . 执行 systemctl restart mongod.service来重启mongodb服务。

六. 启动其他节点的mongodb服务

在其他节点上启动mongodb服务:

1
2
systemctl start mongod.service
systemctl enable mongod.service

七. 把其他节点添加到集群

第五步中已经在主节点里对集群进行了初始化,然后第六步中启动了从节点和仲裁节点的mongodb服务,现在就差将它们加入集群了。

1 . 在主节点执行mongo进入mongo shell,执行以下指令完成用户认证:

1
2
use admin
db.auth('root', '非常复杂的超级管理员密码')

2 . 添加从节点

1
rs.add('mb:27017')

3 . 添加仲裁节点

1
rs.addArb('mc:27017')

八. 创建业务用的数据库与账号

1
2
use test_db
db.createUser({user:'test_user',pwd:'复杂的test_user的密码',roles:[{role:"readWrite",db:"test_db"}]})

现在,业务里可以通过连接字符串mongodb://test_user:复杂的test_user的密码@ma:27017,mb:27017/test_db来对该集群进行读写了。

swoole_http_server中request的全局对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php
/**
* http.php
*
* @author shengyayun<719048774@qq.com>
* @since 2017/11/12 18:39
*/
$http = new swoole_http_server("127.0.0.1", 9501);
$http->set(
[
'worker_num' => 2,//开启2个worker进程
'dispatch_mode' => 1,//request轮询分配给每个worker进程
]
);
$http->on('request', function ($request, $response) {
App::init($request, $response);//必须放在最前面,不然下一个request可能取到上一个request的gpc
$result = (new A())->run();//调用实现类
$response->end("子进程(" . getmypid() . ")完成请求的处理:" . $result);//request请求是在worker进程执行的
});
$http->start();
echo "主进程(" . getmypid() . ")已启动";

/**
* 存放全局对象,进程内唯一,进程内全局
*/
class App
{
/**
* @var array 进程内的全局对象GPC
*/
public static $GPC;

public static function init($request, $response)
{
foreach (['get', 'post', 'cookie', 'files'] as $attr) {
self::$GPC[$attr] = property_exists($request, $attr) ? $request->$attr : [];
}
$response->header("Content-Type", "text/html; charset=UTF-8");
}
}

/**
* 在具体业务实现中取得全局对象
*/
class A
{
public function run()
{
//获取全局gpc对象
return var_export(App::$GPC, true);
}
}
用php实现curl的data-binary

如果我想往influxdb中插入一条数据,教程告诉我可以这样:

1
curl -i -XPOST 'http://127.0.0.1:8086/write?db=metrics' -u admin:admin --data-binary 'test,host=localhost count=1'

但是现在我需要用php来实现。经过查阅网上资料,有人说可以先将test,host=localhost count=1转为stream,然后php进行curl的时候设置CURLOPT_INFILECURLOPT_INFILESIZECURLOPT_UPLOAD。这样操作下来虽然influxb虽然返回了204,但是数据并没有正确插入。

最后我使用了以下的代码实现了功能:

1
2
3
4
5
6
7
8
$ch = curl_init();//init
curl_setopt($ch, CURLOPT_TIMEOUT, 1); //一秒超时
curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:8086/write?db=metrics");
curl_setopt($ch, CURLOPT_BINARYTRANSFER, TRUE);//二进制传输
curl_setopt($ch, CURLOPT_USERPWD, '123');//权限认证
curl_setopt($ch, CURLOPT_POSTFIELDS, 'test,host=localhost count=1');//写操作只能用post
curl_exec($ch);
curl_close($ch);
grafana的部署

导言

grafana 是基于JS开发的,功能齐全的度量仪表盘和图形编辑器。我们可以通过collectd和业务代码采集数据,通过influxdb存储数据,最后通过grafana的web端进行展现。

一. 安装

1
2
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.5.2-1.x86_64.rpm
sudo yum localinstall grafana-4.5.2-1.x86_64.rpm

二. http配置修改

用vim打开文件/etc/grafana/grafana.ini,下面我们查看[server]的配置:

  • http_port默认是3000,代表它自带的http服务默认监听3000端口。
  • domain默认是localhost,结合上面的默认端口,我们可以通过http://localhost:3000来访问grafana的图形化界面。
  • root_url这个配置主要为了在使用反向代理后能正常跳转,同时邮件可以提供正确的url。

这里有个案例,我希望可以通过http://watch.langdaren.com直接访问grafana,但80端口已经被nginx占用:

  1. http_port不修改,保留默认的3000
  2. domain改为watch.langdaren.com
  3. root_url改为http://watch.langdaren.com
  4. 修改nginx配置,添加一条这样的server配置:
    1
    2
    3
    4
    5
    6
    7
    server{
      listen 80;
      server_name watch.langdaren.com;
      location / {
        proxy_pass http://127.0.0.1:3000;
      }
    }
    nginx配置修改完记得先执行nginx -t测试一下配置,然后再执行nginx -s reload进行重启。

三. smtp配置修改

配置正确的smtp,可以让grafana可以自动发送邮件,下面我使用我的腾讯邮箱进行配置。
用vim打开配置文件,下面我们查看[smtp]的配置:

  1. enabled修改为true
  2. host修改为邮件系统的发送服务器地址,这里是smtp.exmail.qq.com:465
  3. user修改为我的邮箱全名719048774@qq.com
  4. password修改为我的邮箱密码。
  5. from_address修改为邮箱全名719048774@qq.com
  6. from_name改为shengyayun

四. 管理员账号配置修改

用vim打开配置文件,下面我们查看[security]的配置:

  1. admin_user默认为admin,这个是管理员账号名,按需修改。
  2. admin_password默认为admin,这个是管理员密码,按需修改。

五. 服务运行与开机自启动

1
2
systemctl start grafana-server
systemctl enable grafana-server

六. 测试

通过第二步的http配置,就知道如何访问grafana的web端了。默认的地址是http://localhost:3000,在我的案例里的地址是http://watch.langdaren.com

七. 其他

grafana访问influxdb并创建dashboard之类操作,都是通过grafana的web端的管理员功能完成的,这里不做详细介绍,具体可以查看官网http://docs.grafana.org

collectd的部署

导言

collectd是一款性能监控程序,这里我将采用它对服务器进行监控,然后将数据写入influxdb。阅读该文档前先阅读influxdb的相关文档。

一. 安装

1
2
yum install collectd
yum install collectd-rrdtool

二. 修改配置

  1. 用vim打开/etc/collectd.conf文件。
  2. QDNLookup修改为false。如果要保留为默认的true的话,host必须与ip保持一致。
  3. 取消LoadPlugin logfile的注释,同时取消<Plugin logfile>...</Plugin>的注释,将其中File Stdout一行改为File "/var/log/collectd.log",这样collectd的日志会被单独写进这个文件里。
  4. 取消rrdtool组件的注释:
    LoadPlugin rrdtool
  5. 为了后续测试collectd功能,取消cpu组件的注释:
    LoadPlugin cpu
  6. 通过配置network组件将监控数据传给influxdb:
    1
    2
    3
    <Plugin network>
      Server "127.0.0.1" "25826"
    </Plugin>
    这里的127.0.0.1是influxdb的ip地址,25826是influxdb监听的端口号。

三. 配置influxdb的collectd插件

  1. 用vim打开/etc/influxdb/influxdb.conf文件
  2. [[collectd]]下面的enabled取消注释,并改为true
  3. [[collectd]]下面的typesdb取消注释,改为/etc/collectd.conf文件中TypesDB指向的types.db文件所在的目录
  4. 重启influxdb服务。
  5. 通过influx shell创建一个新的数据库,取名collectd。

四. 前台执行

在测试过程中发现,如果不先在前台手动执行一次collectd,直接用systemctl start collectd启动服务会导致其一直报错。执行以下指令可以让collectd在前台执行:
/usr/sbin/collectd -f
如果没有问题的话就ctrl+c结束程序。

五. 服务运行与开机自启动

1
2
systemctl start influxdb
systemctl enable influxdb

六. 测试

通过influx shell查看数据库collectd数据库,一切正常的话,里面会出现名为cpu_value的measurement。这说明collectd的cpu组件已经在不停地往influxdb写入数据了。

influxdb的部署

导言

influxdb是目前比较流行的时间序列数据库,本文只介绍如何部署influxdb,具体知识点请查阅相关资料。

一. 下载安装

1
2
wget https://dl.influxdata.com/influxdb/releases/influxdb-1.3.5.x86_64.rpm
yum localinstall influxdb-1.3.5.x86_64.rpm

二. 服务的运行和开机自启动

1
2
systemctl start influxdb
systemctl enable influxdb

三. 使用influx shell与influxdb进行交互,创建一个管理员账号

1
2
influx
create user admin with password 'admin' with all privileges to admin;

四. 修改配置文件启用权限认证

  1. 编辑/etc/influxdb/influxdb.conf文件,将[http]下的auth-enabled修改为true,然后重启服务。
  2. 以后每次登陆influxdb shell,执行指令前都需要先执行auth指令通过身份认证。

五. 修改默认端口

  1. 编辑/etc/influxdb/influxdb.conf文件,将[http]下的bind-address = ":8086"中的8086修改为8087,改完后重启服务。
  2. 换了端口后,进入influx shell的指令就变为了influx -port 8087

六. 测试

  1. 先通过influx shell执行一条创建数据库的语句:
    1
    create database testDb;
  2. 在bash中通过curl进行一次post请求:
    1
    2
    curl -i -X POST "http://localhost:8087/write?db=testDb&u=admin&p=admin" --data-binary "testMetric,host=mbp value=0.64"
    #这里的`db=testDb`就是数据库,`u=admin`和`p=admin`分别是第三步中设置的用户名和密码,`testMetric`是度量,`host=mbp`是标签,`value=0.64`是数据
  3. 在influx shell中执行查询语句:
    1
    2
    use testDb; //使用testDb数据库
    select * from testMetric; //查询度量testMetric