三台树莓派+云服务器反代实现负载均衡记录

2018-02-02 11:42:23

前段时间,突然发现去年买的三台树莓派已经默默的在角落吃了大半年的灰了,想起我当年买的初心,不禁十分惭愧,于是决定使用云服务器作反代,负载三台树莓派研究一番。

首先肯定是要为树莓派装lnmp环境了,过程就略过了,于是三台树莓派的环境如下:

  • PHP 7.1.7
  • MYSQL 5.5.56
  • NGINX 1.12.2

首先我们需要在本地,注意是本地而非树莓派部署laravel框架,执行以下命令

composer create-project laravel/laravel shumeipai --prefer-dist

安装完成后,确保laravel可正常跑起来之后,就可以开始负载之旅了。

首先把代码上传到 github 或者 gitee,因为需要用到他们的WebHooks,这样我们就可以本地修改代码以后,push时,直接触发三台树莓派的git pull,全自动完成代码的更新了。

代码上传过程略,我选择上传到gitee,毕竟可以免费创建私有仓库啊
接下来就开始配置 WebHooks
首先我们需要确定树莓派上的nginx用户是谁,这个一般都写在nginx.conf配置文件中,我的nginx user为www,可能有的使用的是www-data。
然后nginx的root文件夹下执行

sudo -Hu www ssh-keygen -t rsa

这个是为www用户生成ssh公钥和私钥,密码最好设置为空,这样后面就会少折腾许多事情。

执行的时候,说是/home/www/.ssh文件夹不存在,于是要执行

sudo mkdir -p /home/www/.ssh
sudo chown -R www.www /home/www/.ssh

建立相应的文件夹,然后可生成公钥,以及私钥文件,接着我们需要把树莓派的公钥文件部署到gitee上面,这样我们就有权限执行git pull拉取代码了。

我们打开gitee中新上传的laravel代码目录,在上面找到最后一个Settings,点击进入,如图

然后点击Deploy keys setting > Add Key添加我们的公钥,如图

添加成功以后,我们在树莓派的web目录中,执行

sudo -Hu www git clone git@gitee.com:xxxx/xxxx.git
cd xxxx
sudo -Hu www .env.example .env
sudo -Hu www composer install
sudo -Hu php artisian key:generate

如果不出意外的话,就完成了一台树莓派的代码拉取,其余的树莓派操作相同。这样三台树莓派的代码就部署好了。现在我们需要完成 WebHooks 相应的功能

点击WebHooks,提交我们三台树莓派的通知post地址,如图所示

因为我是有公网ip的,虽然是动态的,但是也是可以使用的。如果没有公网ip的话,只能考虑nat(内网穿透)这类操作了。为了安全考虑,WebHooks是需要我们设置密码的,它会在post过来的json中,以明文的格式返回给你,如下

{
    "before": "fb32ef5812dc132ece716a05c50c7531c6dc1b4d", 
    "after": "ac63b9ba95191a1bf79d60bc262851a66c12cda1", 
    "ref": "refs/heads/master", 
    "user_id": 13,
    "user_name": "123", 
    "user": {
      "name": "123",
      "username": "test123",
      "url": "https://gitee.com/oschina"
    }, 
    "repository": {
        "name": "webhook", 
        "url": "http://git.oschina.net/oschina/webhook", 
        "description": "", 
        "homepage": "https://gitee.com/oschina/webhook"
    }, 
    "commits": [
        {
            "id": "ac63b9ba95191a1bf79d60bc262851a66c12cda1", 
            "message": "1234 bug fix", 
            "timestamp": "2016-12-09T17:28:02 08:00", 
            "url": "https://gitee.com/oschina/webhook/commit/ac63b9ba95191a1bf79d60bc262851a66c12cda1", 
            "author": {
                "name": "123", 
                "email": "123@123.com", 
                "time": "2016-12-09T17:28:02 08:00"
            }
        }
    ], 
    "total_commits_count": 1, 
    "commits_more_than_ten": false, 
    "project": {
        "name": "webhook", 
        "path": "webhook", 
        "url": "https://gitee.com/oschina/webhook", 
        "git_ssh_url": "git@gitee.com:oschina/webhook.git", 
        "git_http_url": "https://gitee.com/oschina/webhook.git", 
        "git_svn_url": "svn://gitee.com/oschina/webhook", 
        "namespace": "oschina", 
        "name_with_namespace": "oschina/webhook", 
        "path_with_namespace": "oschina/webhook", 
        "default_branch": "master"
    }, 
    "hook_name": "push_hooks", 
    "password": "pwd"
}

所以说我们判断,如果post过来的password和我们设置的password一致的话,就可以认为是gitee发送过来的,于是我们的密码强度需要设置的高一点。不过github中是可以进行验签的,安全性个人感觉要高不少。但密码设置复杂点,而且不泄露的话,我认为还是问题不大的。

于是我们需要开发一个laravel的gitee通知接口,笔者的代码可以放出来参考一下

路由

Route::group(['prefix' => 'notice','namespace' => '\Api'],function ($router)
{

    $router->post('gitee','NoticeController@gitee');

});

Controller

<?php

namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class NoticeController extends Controller
{

    public function gitee(Request $request)
    {
        $json = $request->getContent();

        $data = json_decode($json);

        if($data->password = 'xxxxx')
        {
            shell_exec('/usr/local/bin/gitee');
        }
    }
    //
}

/usr/local/bin/gitee脚本代码

#!/bin/bash
cd /home/wwwroot/shumeipai/
git pull
composer install

也可以后脚本直接写在php代码里,看个人习惯吧。

测试一下,一下就成功了。这样我们就完成集群代码的自动同步。接下来需要搞定的是mysql的复制问题,于是我们在三台树莓派的mysql中,建立shumeipai数据库

create database shumeipai character set utf8;

接着我们需要指定一台当做主服务器,其余为从服务器
三台树莓派的内网ip分别为

  • 192.168.2.110
  • 192.168.2.111
  • 192.168.2.112

于是我们设置为 192.168.2.110为主数据库,实际环境中,需要视情况设置主服务器,很要的。
在主数据库中
首先一定要开启bin-log日志
设置 binlog_format=mixed
server-id 要设置好,有唯一性(重要)

从服务器照上面一样设置

在主服务器中创建从服务器的授权账号登陆

GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO card2@'192.168.2.111' IDENTIFIED BY 'xxx';
GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO card3@'192.168.2.112' IDENTIFIED BY 'xxx';

主服务器为从服务器开启防火墙端口

sudo iptables -I INPUT -s 192.168.2.111 -p tcp --dport 3306 -j ACCEPT
sudo iptables -I INPUT -s 192.168.2.112 -p tcp --dport 3306 -j ACCEPT

在mysql中执行

mysql>show master status;

我们会得到bin-log文件名,以及Position号,记录下来,然后设置从数据库

在两个从数据库的mysql中分别执行

配置从服务器Slave

192.168.31.7:

change master to master_host="192.168.2.111",master_user='card2',master_password='xxx',master_log_file='mysql-bin.000001',master_log_pos=308;

192.168.31.8:

master_host="192.168.2.112",master_user='card3',master_password='xxx',master_log_file='mysql-bin.000001',master_log_pos=308;

启动从服务器复制功能 start slave;
检查从服务器复制功能状态

mysql> show slave statusG

至此,就完成了数据库的主从复制功能。接下来我们需要在云服务器中配置nginx反代,相关配置文件如下

upstream backend{
    ip_hash;
    server xxx.55555.io:xxx;
    server xxx.55555.io:xxx;
    server xxx.55555.io:xxx;
}
server {
        listen 80 ;
        server_name shumeipai.muzilong.cn;
        location / {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
             #禁用缓存
            proxy_buffering off;
            proxy_pass http://backend;
        }

        error_log /var/log/nginx/shumeipai_error.log;
        access_log /var/log/nginx/shumeipai_access.log;
    }

其中的域名为花生壳域名啦,于是我们启动nginx服务器,成功的反代了三台树莓派了。至于ip_hash是做什么的,是根据返问者的ip地址,固定分配一台可用的服务器供反问者访问。主要是解决session的不一致问题,但如果你有专一的session存储方案的话,如redis,memcache,mongoDb等,可以配置为其它的访问方式,如weight等,我个人还是比较喜欢weight的,比较可控,但树莓派硬件受限,只能舍弃了。

运行一段时间过后,发现连接超时,打不开网站。查询日志文件发现,原来nginx启动的时候,会自动把域名解析成ip然后执行,当ip发生变化的时候,dns是无法更新的,google了一下,暂时没有什么好的解决方案,暂时只能通过脚本自行解决

脚本如下

#!/bin/sh  
ADDR=nosay.55555.io
TMPSTR=`ping ${ADDR} -s 1 -c 1 | grep ${ADDR} | head -n 1 | cut -d'(' -f 2 | cut -d')' -f1`
CURRENT_IP=`cat /home/nosay/docker/muzilong/logs/shumeipai/ip`
CURRENT_DATE=$(date +%Y/%m/%d\ %H:%M);
if [ "$TMPSTR" != "$CURRENT_IP" ];then
    echo ${CURRENT_DATE}'>>>>>>>>The old ip is '${CURRENT_IP}',The Current ip is '${TMPSTR}',now let us change it' >> /home/nosay/docker/muzilong/logs/shumeipai/log
    cd /home/nosay/docker/muzilong
    /usr/local/bin/docker-compose stop nginx
    /usr/local/bin/docker-compose up -d nginx
    echo ${TMPSTR}>/home/nosay/docker/muzilong/logs/shumeipai/ip
fi

具体是当检测到ip地址和保存的ip地址发生变化时,就重启nginx服务器,然后丢到crontab中,设置好检测频率即可。

接着我们需要搞读写分离了,我们希望所有的写操作落在主数据库上,然后读操作落在本地,这个laravel解决起来不要太简单,只需要在mysql配置文件中稍做修改即可。

mysql' => [
            'read' => [
                'host' => '127.0.0.1',
            ],
            'write' => [
//                'host' => 'mysql',
                'host' => 'card1',
            ],

从配置文件中,我们可以看到读host为本地,而写host设置为card1

至于card1是什么鬼,那是在三台树莓派中的hosts文件中设置了

card1 192.168.2.110

也许有人跳出来问了一句,你直接设置192.168.2.110不就好了,何必设置为card1呢,其实我也不想啊,但是设置192.168.2.110在laravel中无法连接数据库啊,google半天也没找到答案,你若是知道,可以发邮件告知一下,感谢。

这样就完成了一个比较简单的负载均衡demo