经典且简单的企业级生产环境系统架构

各位读者好久不见,很长一段时间的周末都在忙着在为新工作做准备,所以没把时间用在写文章上。今天本也有安排, 可还是想抽点时间出来写一篇文章,对这一项目的技术栈做总结。

文章正式开始之前还是想简单说一句我找工作的想法。受到开源社区以及一些Youtube程序员UP主的影响,对于下一份工作,它应该是一份我自己喜欢而且有英语工作环境的工作。快乐的coding才能做出一个真正受欢迎的程序吧 😀 而且自己也不会感觉到很疲惫。期待有一天我也能拥有一个像Redis那样受欢迎的开源项目。

以下的内容是真实企业生产环境的项目搭建与配置,由于项目面向的人群比较特殊,因此系统的使用量不高,并发量也不大,所以没有采用比较复杂的设计。默认的配置也达到了压测的要求,所以没有对配置做过多的优化。

系统架构图

系統架構.png 上图是系统架构图,是互联网公司里最常见的系统架构。这类系统往往处理并发量不高,而且业务也不复杂,所以做集群(包括应用系统集群,Redis集群,MySQL主从)的作用仅是保障它的高可用。系统里面有使用到消息队列,但要求不高,我们就直接用了Redis里的list结构来实现。简单的事情用简单的方式处理即可,对于简单的要求就没必要使用专业的MQ(RocketMQ, RabbitMQ),要不然反而增加系统的复杂性。

总体请求流向图

请求流向图.png 因为阿里云跟客户有合作协议,所以服务器资源都是来自于阿里云。通过上面的请求流向图,读者们也可以大致了解整个系统的网络架构。

详细请求流向图

详细请求流向图.png 上图中的大矩形框表示一台服务器,其中有若干矩形框代表同一台服务器里装了若干组件。所有的请求都是经由Nginx发出,包括用户浏览HTML/图片/其他静态资源,JS请求服务端,所以上图的Nginx有三个出度。这里使用Redis哨兵模式而没有使用集群模式,主要是考虑到本系统对Redis的使用仅是作为Session共享和消息队列(消息的数量比较少),总体的数据量非常少,就使用简单一些的哨兵模式。而没有考虑使用Redis Cluster模式,将数据分散到不同服务器上(通过slot)。Redis Cluster模式的其他资料读者可自行查询其他资料。

读者通过上面的3张图对系统框架有了大概的了解后,下面附上所有组件的搭建步骤,项目名为iurc。

组件部署搭建步骤与配置

1. 服务器基础环境搭建

1.1 所有服务器创建robin用户

一共有9台服务器,假设IP为10.210.111.1210.210.111.20。真实环境需要通过堡垒机登入,这里假设能直接登录服务器。

首先以root用户通过SSH工具登入服务器10.210.111.12,使用命令行创建init_server.sh脚本

首先以root用户通过SSH工具登入服务器10.210.111.12,使用命令行创建init_server.sh脚本

vim init_server.sh

# 创建robin用户
adduser -m robin
# 修改用户密码
passwd robin
# 安装telent
yum -y install telnet

创建send_all_servers.sh脚本将init_server.sh脚本分发给所有服务器

vim send_all_servers.sh

# 脚本中写入分发命令(此步骤的目的是节省创建用户的时间,但仍须登入每一台服务器执行该脚本)
scp $1 root@10.210.111.13:/root/
scp $1 root@10.210.111.14:/root/
scp $1 root@10.210.111.15:/root/
scp $1 root@10.210.111.16:/root/
scp $1 root@10.210.111.17:/root/
scp $1 root@10.210.111.18:/root/
scp $1 root@10.210.111.19:/root/
scp $1 root@10.210.111.20:/root/

执行send_all_servers.sh脚本(暂未做免密登录,执行脚本后需要输入目标服务器密码)

chmod +x ./send_all_servers.sh
bash ./send_all_servers.sh ./init_server.sh

在所有服务器执行init_server.sh脚本

chmod +x ./init_server.sh
bash ./init_server.sh

1.2 所有服务器授权robin用户

添加sudoers文件可寫權限

chmod u+w /etc/sudoers

修改sudoers文件

vim /etc/sudoers

在sudoers文件中找到如下图

image.png

在root行下方添加如下內容

robin ALL=(ALL) ALL

收回sudoers文件可写权限

chmod u-w /etc/sudoers

⚠️ 所有ECS都需要登入执行授权操作

1.3 修复服务器主机名

修改10.210.111.12服务器主机名

hostnamectl set-hostname nginx_01
reboot

建议以此修改方式,将所有主机名改为此服务器主要提供的服务,在后续方便运维。我设置的主机名如下:

IP地址系統主机名
10.210.111.12CentOS7.xnginx_01
10.210.111.13CentOS7.xnginx_02
10.210.111.14CentOS7.xredis_01
10.210.111.15CentOS7.xredis_02
10.210.111.16CentOS7.xredis_03
10.210.111.17CentOS7.xdb_01
10.210.111.18CentOS7.xdb_02
10.210.111.19CentOS7.xapp_02
10.210.111.20CentOS7.xapp_02

2. Nginx安装及配置

服务器配置

IP地址系统功能
10.210.111.12CentOS7.xNginx
10.210.111.13CentOS7.xNginx

2.1 安装Nginx

使用SSH工具以robin用戶登入服務器nginx_0110.210.111.12,下載Nginx並解壓。

cd /home/robin
wget http://nginx.org/download/nginx-1.20.1.tar.gz
tar -zxvf nginx-1.20.1.tar.gz && cd nginx-1.20.1

安装预编译环境

sudo yum update
sudo yum -y install gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel

编译安装用以下配置替換/usr/local/nginx/conf/nginx.conf裡的內容

cd /home/robin/nginx-1.20.1
./configure --prefix=/usr/local/nginx
make
sudo make install

2.2 配置Nginx

用以下配置替换/usr/local/nginx/conf/nginx.conf里的内容

worker_processes  2;

events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        
	location @router{
            rewrite ^.*$ /iurc/index.html break;
        }

        location ^~/iurc {
            root   /usr/local/nginx/html;
            try_files $uri $uri/ @router;
        }

	location ^~/iurc/api {
            proxy_pass http://iurcProxy/iurc/api/channel/http.do;
            proxy_send_timeout 1800;
            proxy_read_timeout 1800;
            proxy_connect_timeout 1800;
            client_max_body_size 2048m;
            proxy_set_header  Host              $http_host;
            proxy_set_header  X-Real-IP   $remote_addr;
            proxy_set_header  X-Real-Port       $remote_port;
            proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
        }

	location @router_admin{
            rewrite ^.*$ /iurc-admin/index.html break;
        }

        location ^~/iurc-admin {
            allow 192.168.10.0/24;
            allow 192.168.12.0/24;
            allow 192.168.30.0/24;
            allow 192.168.160.0/24;
            deny all;
            root   /usr/local/nginx/html;
            try_files $uri $uri/ @router_admin;
        }
	
        location ^~/iurc-admin/api {
            allow 192.168.10.0/24;
            allow 192.168.12.0/24;
            allow 192.168.30.0/24;
            allow 192.168.160.0/24;
            deny all;
            proxy_pass http://iurcAdminProxy/iurc/admin/api/channel/http.do;
            proxy_send_timeout 1800;
            proxy_read_timeout 1800;
            proxy_connect_timeout 1800;
            client_max_body_size 2048m;
            proxy_set_header  Host              $http_host;
            proxy_set_header  X-Real-IP   $remote_addr;
            proxy_set_header  X-Real-Port       $remote_port;
            proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
        }

        location = /50x.html {
            root   html;
        }

    }

    upstream iurcProxy {
        server 10.210.111.19:30068;
        server 10.210.111.20:30068;
    }
    
    upstream iurcAdminProxy {
        server 10.210.111.19:30070;
        server 10.210.111.20:30070;
    }
}

2.3 启动Nginx

sudo /usr/local/nginx/sbin/nginx

⚠️ 在nginx_0210.210.111.13服务器上按照步骤2.1 – 2.3安装配置Nginx

2.4 创建HTML存放目录

sudo mkdir /usr/local/nginx/html/iurc
sudo mkdir /usr/local/nginx/html/iurc-admin

若修改了/usr/local/nginx/conf/nginx.conf里的内容,可用如下命令重新启动Nginx,使新配置生效。

sudo /usr/local/nginx/sbin/nginx -s reload

3. Redis集群安装及配置(哨兵模式)

服务器配置

IP地址系统功能
10.210.111.14CentOS7.xRedis, 哨兵
10.210.111.15CentOS7.xRedis, 哨兵
10.210.111.16CentOS7.xRedis, 哨兵

3.1 安装Redis

以robin用户登入服务器redis_0110.210.111.14,下载Redis并解压。

cd /home/robin
wget https://download.redis.io/releases/redis-5.0.9.tar.gz
tar -zxvf redis-5.0.9.tar.gz && cd redis-5.0.9

安装预编译环境及安装

sudo yum update
sudo yum -y install gcc
make
sudo make install PREFIX=/usr/local/redis

⚠️ 在redis_0210.210.111.15服务器上按照步骤3.1安装配置Redis

⚠️ 在redis_0310.210.111.16服务器上按照步骤3.1安装配置Redis

3.2 配置Redis

10.210.111.14中,从redis源码目录中复制redis.conf至redis的安装目录

sudo cp /home/robin/redis-5.0.9/redis.conf /usr/local/redis/bin/ && cd /usr/local/redis/bin/

10.210.111.14中,修改redis.conf文件:

sudo vim redis.conf

daemonize no改为daemonize yes

image.png

把默认的回环地址注释掉,默认不能被其他机器访问

image.png

关闭保护模式,由yes改为no

image.png

将在10.210.111.14中修改完成的redis.conf文件分发到10.210.111.1510.210.111.16

scp /usr/local/redis/bin/redis.conf root@10.210.111.15:/usr/local/redis/bin/
# 此处会要求输入10.210.111.15的root密码

scp /usr/local/redis/bin/redis.conf root@10.210.111.16:/usr/local/redis/bin/
# 此处会要求输入10.210.111.16的root密码

10.210.111.15中,编辑redis.conf

sudo vim /usr/local/redis/bin/redis.conf
#在最后一行加入如下内容
replicaof 10.210.111.14 6379

10.210.111.16中,编辑redis.conf

sudo vim /usr/local/redis/bin/redis.conf
#在最后一行加入如下内容
replicaof 10.210.111.14 6379

3.3 配置Redis Sentinel

10.210.111.14中,安装redis-sentinel

sudo mkdir -p /usr/local/redis-sentinel
sudo cp -r /usr/local/redis/* /usr/local/redis-sentinel

10.210.111.14中,复制sentinel.conf

sudo cp /home/robin/redis-5.0.9/sentinel.conf /usr/local/redis-sentinel/bin

10.210.111.14中,修改sentinel.conf

sudo vim /usr/local/redis-sentinel/sentinel.conf

#将daemonize由no改为yes
daemonize yes
#指定多少毫秒后,主节点没有应答sentinel。此时,sentinel主观上认为主节点下线,默认30000毫秒,改为3000毫秒
sentinel down-after-milliseconds mymaster 3000

10.210.111.14的sentinel复制到10.210.111.1510.210.111.16

scp -r /usr/local/redis-sentinel/ root@10.210.111.15:/usr/local
scp -r /usr/local/redis-sentinel/ root@10.210.111.16:/usr/local

10.210.111.15中,修改redis.conf文件

vim /usr/local/redis-sentinel/bin/sentinel.conf

# 将sentinel监控的redis主节点ip改为10.210.111.14
sentinel monitor mymaster 10.210.111.14 6379 2

10.210.111.16中,修改redis.conf文件

sudo vim /usr/local/redis-sentinel/bin/sentinel.conf

# 将sentinel监控的redis主节点ip改为10.210.111.14
sentinel monitor mymaster 10.210.111.14 6379 2

3.4 设置开机启动Redis及Redis Sentinel

10.210.111.14中,添加Redis开机启动服务

sudo vim /etc/systemd/system/redis.service

# 复制粘贴以下内容
[Unit]
Description=redis-server
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis/bin/redis.conf
PrivateTmp=true

[Install]
WantedBy=multi-user.target

10.210.111.14中,添加Redis Sentinel开机启动服务

sudo vim /etc/systemd/system/redis-sentinel.service

[Unit]
Description=redis-sentinel
After=network.target

# 复制粘贴以下内容
[Service]
Type=forking
ExecStart=/usr/local/redis-sentinel/bin/redis-sentinel /usr/local/redis-sentinel/bin/sentinel.conf
PrivateTmp=true

[Install]
WantedBy=multi-user.target

配置解释如下

# [Unit]: 服务单元
# Description: 描述服务
# After: 描述服务的类别

# [Serivce]: 服务运行参数的设置
# Type=forking: 是后台运行的形式
# ExecStart: 服务的具体运行命令
# ExecReload: 服务的重启命令
# ExecStop: 服务的停止命令
# PrivateTmp=True: 表示给服务分配独立的临时空间
# ⚠️ 注意:[Service]的启动、重启、停止命令全部要求使用绝对路径

# [Install]: 运行级别下服务安装的相关设置,可设置为多用户,即系统运行级别为3

⚠️ 在10.210.111.1510.210.111.16中重复以上表示添加Redis开机启动服务添加Redis Sentinel开机启动服务步骤。

3.5 启动Redis及Redis Sentinel

10.210.111.14中,启动redis并加入开机自启动

sudo systemctl daemon-reload
sudo systemctl start redis.service 
sudo systemctl enable redis.service

10.210.111.14中,启动Redis sentinel并加入开机自启动

sudo systemctl start redis-sentinel.service 
sudo systemctl enable redis-sentinel.service

⚠️ 在10.210.111.1510.210.111.16中重复以上表示启动redis并加入开机自启动启动Redis sentinel并加入开机自启动步骤。

3.6 测试

在主Redis10.210.111.14进入redis控制台

sudo /usr/local/redis/bin/redis-cli

输入命令

info

若出现下图结果,连接了2个slaves则表示Redis主从(哨兵模式)安装成功

image.png ⚠️ 输入info命令出来的结果由于网络或者同步状态的情况,connected_slaves不一定实时为2,可以多查看几次。

4. MySQL安装及配置(主从模式)

服务器配置

IP地址系统功能
10.210.111.17CentOS7.x主數據庫
10.210.111.18CentOS7.x從數據庫

4.1 安装MySQL

以robin用户登入服务器db_0110.210.111.17,通过YUM源方式安装MySQL。

下面链接很详细的官网安装步骤,读者照里面这个步骤安装MySQL即可

dev.mysql.com/doc/refman/…

4.2 设置MySQL远程连接

设置MySQL只能两个APP SERVER10.210.111.1910.210.111.20连接

# 在MYSQL命令行中输入以下命令
GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.210.111.19' IDENTIFIED BY 'P@ssw0rd' WITH GRANT OPTION; 
GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.210.111.20' IDENTIFIED BY 'P@ssw0rd' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.210.111.14' IDENTIFIED BY 'P@ssw0rd' WITH GRANT OPTION; 
GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.210.111.15' IDENTIFIED BY 'P@ssw0rd' WITH GRANT OPTION;
FLUSH PRIVILEGES; 

⚠️ 至此已完成10.210.111.17的MySQL安装,请登录10.210.111.18参考步骤[4.1 – 4.2]10.210.111.18上安装MySQL。

4.3 设置主从同步

⚠️ 将10.210.111.17设置为主数据库,将10.210.111.18设置为从数据库

mysql -uroot -p
# 輸入密碼
CREATE USER 'slave'@'%' IDENTIFIED BY 'slaveP@ssw0rd';
GRANT ALL PRIVILEGES ON *.* TO 'slave'@"%" IDENTIFIED BY "slaveP@ssw0rd";

修改10.210.111.17的my.cnf配置文件

sudo vim /etc/my.cnf
# 加入两行内容
log-bin=mysql-bin
server-id=1
image.png

说明

# 开启二进制日志,该日志是在事务提交时写日志文件的。默认大小是1G,后面加001,002这样的后缀顺加。
log-bin=mysql-bin

# 唯一标识主机,mysql主从每个mysql实例配置都不一样就行。这个值默认是0,如果是0,主服务器拒绝任何从服务器的连接。
server-id=1

# 其他配置(不是必须配置的):

# 指定mysql的binlog日志记录哪个db,配置需要同步的数据库,可以配置多个,如果没有此配置项则同步全部。
binlog-do-db=db_001(主数据库配置)

# 配置不同步的数据库,可以配置多个。
binlog-ignore-db=mysql(主数据库配置)

# 配置binlog的格式
binlog_format=mixed

# 配置是否只读  0代表不只读,1代表只读
read-only=0

# 用于设定双主情况下自增列的ID冲突使用的,主要用来设置自增步长
auto-increament-increment=10

# 表示这台服务器的序号,从1开始,不超过auto-increament-increment
auto-increment-offset=1

重启10.210.111.17的MySQL

sudo systemctl restart mysqld

修改10.210.111.18的my.cnf配置文件

sudo vim /etc/my.cnf
# 加入两行内容
server-id=2
relay_log=mysql-relay-bin

重启10.210.111.18的MySQL

systemctl restart mysqld.service

获取 10.210.111.17MySQL的binlog信息

mysql -uroot -p
# 输入密码
show master status;
image.png

进入10.210.111.18的MySQL设置与主库同步

mysql -uroot -p
# 输入密码
change master to master_host='10.210.111.17',master_user='slave',master_password='slaveP@ssw0rd',master_log_file='mysql-bin.000001', master_log_pos=154;

⚠️ 注意以下说明

master_user和master_password是上面步骤中在主服务器上创建的用户名“slave”及其密码

master_log_file是在主服务器上查询出来的“MySQL binlog信息”中的File字段值。如上图中的mysql-bin.000001

master_log_pos是在主服务器上查询出来的“MySQL binlog信息”中的Position字段值。如上图中的154

开启10.210.111.18的从MySQL slave线程

start slave;

查看10.210.111.18的从MySQL slave状态

show slave status \G

image.png 看到红框中两个Yes,就代表从从服务器已经成功从主服务器同步数据。

4.4 主从验证

10.210.111.17主MySQL執行建庫建表操作,驗證主從模式部署成功

mysql -uroot -p
# 输入密码
create database test;
use test;
create table user_test( id int comment'ID',name VARCHAR(20) comment'名称', create_time timestamp DEFAULT now() comment'创建时间' );
insert INTO user_test value(1,"A",NOW());

10.210.111.18从MySQL查看是否有在主MySQL创建的表和数据

mysql -uroot -p
# 输入密码
show databases;
use test;
show tables;
select * from user_test;

从结果可以看出,数据已经从主MySQL同步至从MySQL,证明主从模式已经部署成功。接下来在10.210.111.17主MySQL删除测试数据即可。

drop database test;

5.静态资源同步安装

这部分的内容仅供参考,因为现在看回这个设计其实是有些问题的。当初想通过Nginx做动静分离,静态文件直接通过Nginx访问,文件备份通过“静态资源同步服务”。对于公开的静态资源当然没问题,但是如果用户上传的静态资源是不能公开的,这里就存在了很大的风险,攻击者很可能通过Nginx直接下载用户的隐私资料。

综上所述得出两条建议:

  • 对于隐私文件,有条件的话可以使用OSS服务。如果无条件可参考如下的静态资源同步服务,我认为除了我下面的服务外,应该还会有更好的选择,读者可以自行查找;
  • 对于可公开的静态资源可以采用动静分离方案,通过Nginx直接获取。对于隐私文件,切记不可通过配置Nginx直接。

服务器配置

IP地址系统功能
10.210.111.13CentOS7.xrsync服務端
10.210.111.12CentOS7.xrsync客戶端

5.1 rsync服務端

10.210.111.13安裝rsync

cd /opt
sudo yum install -y rsync

配置/etc/rsyncd.conf

# 纪录传输文件日志
transfer logging = yes
# 指定日志文件
log file = /var/log/rsyncd.log
# pid文件路径
pid file = /var/run/rsyncd.pid
# 锁路径
lock file = /var/run/rsync.lock
# 运行rsync守护进程的用户
uid = robin
# 运行rsync守护进程的组
gid = robin
# 是否允许chroot,用于提升安全性。客户端连接模块,首先chroot到模块path参数指定的目录下,chroot为yes是必须使用root权限,且不能备份path路径外的连接文件
use chroot = yes
# 忽略错误
ignore errors
# 只读
read only = no
# 设置超时时间,单位秒
timeout = 60
ignore nonreadable = yes

# 模块,可配置多个
[sync_file]
# 模块的根目录,同步目录,需要注意权限
path = /home/robin/static
# 模块认证的用户名称,可使用空格或逗号隔开多个用户名
auth users = sync
# 模块验证密码文件,可放在全局配置里
secrets file = /etc/rsyncd.secrets
# 设定白名单,可指定IP段(172.18.50.1/255.255.255.0),各IP段用空格分开
hosts allow = 10.210.111.12
hosts deny = *
# 是否允许列出模块内容
list = no

新建/etc/rsyncd.secrets,此处用robin用户作为使用同步服务的用户

# 需要使用root角色执行echo "robin:infoP@ssw0rd" > /etc/rsyncd.secrets
sudo -i
#输入密码使用root
echo "sync:123456" > /etc/rsyncd.secrets
#再转回使用普通用户
su robin

设置文件权限

sudo chmod 600 /etc/rsyncd.secrets

启动服务

sudo systemctl restart rsyncd

5.2 rsync客戶端

10.210.111.12安裝rsync

cd /opt
sudo yum install -y rsync

新建/etc/rsync.passwd,內容如下:

# 需要使用root角色执行echo "infoP@ssw0rd" > /etc/rsync.passwd
sudo -i
#输入密码使用root
echo "123456" > /etc/rsync.passwd
#再转回使用普通用户
su robin

⚠️ 客户端rsync只需要密码,此处密码填写robin用户的密码

更改权限

sudo chmod 600 /etc/rsync.passwd

安裝inotify

inotify-tools工具检测文件增加、删除、修改。

sudo yum install -y inotify-tools

编写启动脚本inotify_start.sh

cd /home/robin

脚本内容如下:

#!/bin/bash

Path=/home/robin/static
# 此處host填rsync服務器的IP
Server=10.210.111.13
User=sync
module=sync_file

/usr/bin/inotifywait -mrq --format '%w%f' -e create,close_write,delete $Path  | while read line  
do
    if [ -f $line ];then
        rsync -az $line --delete ${User}@${Server}::${module} --password-file=/etc/rsync.passwd
    else
        cd $Path &&\
        rsync -az ./ --delete ${User}@${Server}::${module} --password-file=/etc/rsync.passwd
    fi
done

测试命令

sudo rsync -avz --delete /home/robin/static/ sync@10.210.111.13::sync_file --password-file=/etc/rsync.passwd

5.3 编写服务自启动脚本

编写inotifyd.service并将其拷贝到/usr/lib/systemd/system/目录下,内容如下

[Unit]
Description=inotify rsync script
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=root
Group=root
PIDFile=/var/run/inotify.pid
ExecStart=/usr/bin/nohup /home/robin/inotify_start.sh > /home/robin/inotify.log 2>&1 &
ExecReload=
ExecStop=/usr/bin/ps -ef | /usr/bin/grep inotify | /usr/bin/grep -v grep | /usr/bin/awk '{print $2}' | /usr/bin/xargs /usr/bin/kill -9
PrivateTmp=true

[Install]
WantedBy=multi-user.target

启动服务

sudo systemctl start inotifyd.service

允许自动启动

sudo systemctl enable inotifyd.service

重新读取重启脚本

sudo systemctl daemon-reload

PS:以上是我在掘金写的文章,所以图片带水印。