logo


重要链接:
「系列文章目录」

「项目源码(GitHub)」

前言

最近在一篇文章中看到一个观点:

有人认为,人在创作过程中其实会扮演两个角色,既是创作者,同时也是一个鉴赏者,会不断地对自己写出来的、创作出来的东西作一个鉴赏。

有的人一创作出来就是很好的,他就通过了自己鉴赏的标准,这种人属于天赋型选手。

有的人是创作能力比较低,刚开始创作出来的东西并不好,但他有一定的鉴赏能力,所以最终折腾来折腾去,还是能折腾出一些好东西来。

看完我悟了,显然我就是要折腾的这种人,每次写文章我都反复改很多遍。工作中也是,有时候我看到同事写的文档就觉得很别扭,到处都不通顺,有时实在看不下去了也会帮他们改改,但不免会消耗较大精力。

所以更新博客这种事吧咱也不能强求,掌握好节奏,憋了一堆干货不吐不快的时候写最好,是吧?

听懂掌声

嗯,我承认,这是一篇水文,因为部署什么的也没什么技术含量,遇到问题搜搜查查总能解决。

但这篇文章还真是非水不可,因为我们的 Web 开发知识版图还有一些大块没有填充。我们过去一直在 Windows 单机实验,与实际生产环境相差甚远,很多知识根本无法提及。现在我们要切换到 Linux、分布式 的语境下,去解决更为实际也更为复杂的问题,比如:

  • 如何实现持续集成、持续交付?
  • 如何实现数据库、缓存服务的高可用?
  • 如何实现负载均衡?
  • 如何使用容器技术快速部署项目?
  • 如何监控系统软硬件运行的整体情况?
  • 项目在生产环境下会暴漏出来哪些安全问题?这些问题如何发现,如何处理?
  • 如何应对系统崩溃、遭受攻击等突发事件?

···

这些问题暂且不展开,这一次我们先搭建一个最基础的环境:MySQL 服务器 1 台、Redis 服务器 1 台、前端(Nginx)服务器 1 台、后端(Java)服务器 1 台。操作系统均为 Linux,当然都是在虚拟机里,机器多的土豪请自便。

对应如下的部署架构:
部署架构

下面开始动手。

一、虚拟机与 CentOS 安装

首先,我们需要准备虚拟机环境。市面上的虚拟机软件比较多,Windows 平台上使用比较广泛的是 VirtualBoxVMWare Workstation 两种。

可以任选一款,但我比较习惯 VMWare Workstaion,15.X 的版本还是很好用的,如何下载以及嗯哼大家请自行百度。

至于 Linux 的版本,我倾向于 CentOS 7.x,免费且稳定,可惜最近消息说 redhat 打算近两年停止维护 CentOS,很伤,毕竟喜欢 CentOS 的人占了社区相当大的一部分。

CentOS 的安装镜像可以从华为、阿里等平台的镜像站上下载,地址是

https://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/

centos镜像
如图,有各种不同的版本,DVD 版内置了比较常用的功能,Everything 就是什么都有,Minimal 就是只有基础的功能,因为安装的时候会再次选择需要安装的组件,这里实际上是向下兼容。

镜像文件下载哪种都行,但对于生产环境来说,安装的时候最好选择最小化,以免各种组件之间存在冲突。

下载完成后,就可以创建虚拟机了。打开 VMWare,点击创建新的虚拟机, 选择典型
向导1
下一步,选择我们下载的 iso 文件
向导2
下一步,设置虚拟机的名称和位置,这里我建议你认真设置一下,因为之后虽然可以改显示的名字,但虚拟机文件的名字改起来比较麻烦。此外我们要安装多台虚拟机,整理好文件夹才能方便后期管理。
向导3
我给这台虚拟机起名叫 Pure,之后新建的虚拟机都可以从这台克隆出来,就不用重复安装的步骤了。

点击下一步,磁盘空间上限和存储方式都可以默认。

向导4
之后检查配置,勾选创建后开启此虚拟机,点击完成,系统即开始安装。进入第一个界面,可以选择直接安装或者测试媒体并安装,测试媒体并不会花费多少时间,因此可以直接回车进入。

运行一段脚本之后,即进入图形化安装界面。第一步选择语言,建议选择英语,免得之后一些组件出现适配问题。

安装1
点击 continue 继续,出现一堆配置,大部分配置会被自动完成,需要我们动手的主要是 Software Selection 和 Installation Destination,我这里是 Minimal 版,所以软件也没什么可选的,默认就是最小化。

安装2
安装位置这里必须要点一下才能继续,但也可以直接按照默认配置来,虚拟机以及帮我们分好了一块 20G 的磁盘空间。

安装3
Done 之后,点击 Begin Installation,系统和开始安装,同时这里还会提示你设置 root 密码、创建用户。至少要设置一下 root 密码才可以完成安装。

安装4
root 密码如果设置的比较简单,第一次点 done 会有提示,再点一次就可以强制完成了。

出于安全考虑,强烈不推荐设置过于简单的 root 密码。

接下来,系统将会自动继续安装,完成后,按照提示重启即可。

安装5
出现上述界面,说明安装成功。

OK,这里我们啥也不做,把虚拟机关掉,关掉,全部都关掉。

二、MySQL 服务部署

为了提供 MySQL 服务,我们需要

  • 创建一台新的虚拟机(克隆),并使用静态 IP 配置好网络
  • 安装 MySQL Server
  • 初始化数据库,配置 MySQL 的本地用户及远程访问用户

1.虚拟机克隆及网络配置

首先,我们以安装完成的纯净版虚拟机为母体,克隆出新的虚拟机,用于部署 MySQL 服务。

在虚拟机名称处点击右键-管理-克隆,下一步,选择虚拟机中的当前状态(此时我们还没有快照)

克隆1

下一步,选择创建链接克隆(硬盘空间大可随意)

克隆2

之后老一套,命名(如 wj-mysql),选择文件夹,点击完成,克隆机即创建完毕。

打开我们的克隆机,登录 root 账号。

此时可以 ping 一下百度或使用 nmcli (或 ip a)命令查看网络配置,发现网络并没有连通,原因是网卡还没有获取到 ip 地址。

网络1

虚拟机有三种网络类型,仅主机、桥接和 NAT。

  • 仅主机,即虚拟机仅能连接主机,不能访问外部网络
  • 桥接,即虚拟机与主机如同连接在同一个交换机(网桥)上,拥有相同的网段,虚拟机直接暴露给外部网络
  • NAT,即通过网络地址转换,为虚拟机分配内网 IP,让虚拟机处在主机保护之下,可以上网但无法通过外部网络直接访问

一般情况下,VMWare 默认开启的是 NAT 模式,如有需要可以在虚拟机设置中修改。 NAT 模式的网段、网关等信息可以通过 VMWare 的编辑-虚拟网络编辑器进行查看和修改。

虚拟网络编辑器
可以看到目前我的虚拟机使用的是 192.168.194.0 网段。

点击 NAT 设置,可以看到网关是 192.168.194.2。

NAT网关

这里的 VMnet8 是虚拟网卡的名称,在主机的网络适配器中也可以看到并直接配置。DHCP 默认处于开启状态,为了稳定地提供 MySQL 服务,我们需要配置静态的 IP。

在虚拟机终端中,我们需要编辑网络配置文件。

vi etc/sysconfig/network-scripts/ifcfg-ens33

网络设置
需要设置的内容,包括将 BOOTPROTO 由 dhcp 修改为 static,将 ONBOOT 由 no 修改为 yes,以及新增 IP 地址(属于 VMnet8 的子网)、子网掩码、网关,完成后保存。

接下来,为了使用域名访问网络,还需要设置 DNS。创建并编辑配置文件

vi /etc/resolv.conf

添加 nameserver ,即 DNS 地址

DNS

完成后保存,使用 service network restart 等命令重启网络服务,再测试网络,发现已经成功连通。

测试网络

2.MySQL 安装

CentOS 默认使用 yum 管理软件,我们使用 yum list | grep mysql 命令查看 MySQL 相关的包,发现并没有正经的 mysql-server。

yum list

毕竟 MySQL 现在也不是完全开源了,当然,我们还有 MariaDB,不过咱们项目既然用了 MySQL,就一条路走到黑吧。

为了能够安装 MySQL,我们需要手动去官方网站下载包含 repo 源的 rpm 文件。地址是:

https://dev.mysql.com/downloads/repo/yum/

repo
点击下载 Red Hat Enterprise Linux 7 对应的 rpm 包。

接下来,可以通过 WinSCP(下载地址 https://winscp.net/eng/download.php) 或类似的软件传入虚拟机。以 WinSCP 为例,输入主机名(IP)以及用户名和密码建立连接

winscp
在 /usr/local 下新建目录 mysql,将刚才下载的文件拖入该目录。

winscp2

上述步骤也可以在虚拟机上通过 wget 执行,更加方便一点。

获得 rpm 之后,在虚拟机中进入其所在文件夹并安装。

cd /usr/local/mysql
rpm -ivh mysql57-community-release-el7-11.noarch.rpm

安装 repo

此时在 /etc/yum.repos.d 目录下生成了两个 repo 文件。

repos
此时再执行 yum repolist all | grep mysql-community,发现有了我们想要的东西。

mysql

目前默认开启的版本就是 8.0,因此我们不需要做其它修改。如果需要下载其它版本,可以通过 yum-config-manager 设置(需要安装 yum-utils)或编辑 /etc/yum.repos.d/mysql-community.repo,修改 enable 值即可。

接下来,只需要执行

yum -y install mysql-community-server

等待安装完成即可。这里虽然我们写的是 server,但 client 也会被默认安装上。

启动 MySQL 服务

systemctl start mysqld

第一次启动成功后,MySQL 会生成一个临时初始 root 密码并写入日志里,我们可以通过 cat /var/log/mysqld.log 查看。

初始密码
获取该密码之后,即可通过本地 root 账号登录 MySQL

mysql -uroot -pFdW5COQNh9.8(your password)

登录后,可以通过如下语句修改 root 密码

ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourNewPass1!'

注意 MySQL 8.0 的密码策略比较严格,需要大写、小写、数字、符号全都有。之前我们项目配置使用了 Admin123!,这里我们为了方便可以继续沿用这一密码,但还是那句话,在实际生产环境中,强烈不建议使用如此简单的密码,而且 Web 应用尽量不要使用 root 连接数据库,不然一旦被攻击者利用,受害面积将大大增加。

3.MySQL 配置

为了让 MySQL 能被 Web 项目访问到,我们还需要进行两步操作。一是开启系统防火墙相应端口,二是在 MySQL 中对远程用户进行授权配置。

CentOS 7 默认使用 firewalld 防火墙,可以看作是 iptables 的封装。开启 MySQL 默认的 3306 端口,可以直接使用

firewall-cmd --permanent --add-service=mysql

firewall-cmd --add-port=3306/tcp --permanent

最后重启防火墙服务即可。此时我们可以通过宿主机使用 telnet 测试一下端口是否开放。

telnet 虚拟机ip 3306

telnet

可以看到成功收到回显,但此时宿主机还没有访问权限。注意这里主机的 IP 显示为 192.168.194.1,我们在宿主机的 cmd 中通过 ipconfig 查看,发现此 IP 对应的网卡是 VMnet8,也就是虚拟机在 NAT 网络模式下使用的虚拟网卡 。

host ip

我们继续进入 MySQL 中配置。首先新建一个用户 wjadmin

CREATE USER wjadmin IDENTIFIED BY 'Admin456@';

修改 mysql 数据库中的 user 表,使该用户隶属于 192.168.194.1(宿主机),修改后,该用户即可通过宿主机访问 MySQL 服务,但无法访问除 information_schema 之外的数据库。

USE mysql;
UPDATE user SET host='192.168.194.1' WHERE user='wjadmin';

创建数据库 wj

CREATE DATABASE wj;

赋予 192.168.194.1(宿主机) 上的 wjadmin 用户操作 wj 数据库所有数据表(wj.*)的所有权限(ALL PRIVILEGES)

GRANT ALL PRIVILEGES ON wj.* TO 'wjadmin'@'192.168.194.1' 

此时我们在宿主机上使用 MySQL 客户端连接虚拟机的服务,输入账号密码,发现连接成功(如果失败,可以尝试在 mysql 中 FLUSH PRIVILEGES 刷新权限)

连接 MySQL

这里我使用的是 MySQL Workbench。如果使用 Navicat,可能出现由于加密方式不一致导致拒绝连接的问题,可以参照前面的文章解决。其实现在我不是很喜欢用 Navicat 之类的第三方客户端,总觉得不那么靠谱。

在客户端中查看所有数据库,发现我们只能访问 infomation_schemawj 两个数据库,符合我们的预期。

查询数据

至此,MySQL 的安装与配置就结束了,后续搭建过程中只需把当前对宿主机开放的权限赋给后端服务器所在的虚拟机即可。

三、Redis 服务部署

同样,我们创建一个克隆,配置好网络(静态 IP)。还是先用 yum list |查找 yum 仓库里是否有 Redis,发现并没有。可以通过 yum install epel-release 命令添加 EPEL 仓库,再通过 yum install redis 安装,但这个仓库里的 Redis 版本有点低。

redis

因此,我们还是直接从官网下载包吧。首先我们通过 yum install wget 安装一下 wget,然后下载最新稳定版的 Redis 安装包 (可以在 https://redis.io/download 上查看下载地址)。

wget https://download.redis.io/releases/redis-6.0.9.tar.gz

下载完成后,我们把这个包解压到 /usr/src

tar zxvf redis-6.0.9.tar.gz -C /usr/src

为了完成编译,我们需要首先安装 gcc。CentOS 7 使用 yum install gcc 默认安装的 gcc 是 4.8.5 版本,而编译 Redis 至少需要 5.3 以上。

正常思路是手动编译安装 gcc,这个过程比较麻烦,有些依赖下载的很慢,下完了编译需要两三个小时,编译完了配置不好还是会报错。

幸亏还是有更方便的选择,我们只需使用 SCL 源安装 Developer Toolset 即可:

yum install -y centos-release-scl
yum install -y devtoolset-8-gcc devtoolset-8-gcc-c++ devtoolset-8-binutils
scl enable devtoolset-8 bash

这里我们安装了 devtoolset-8 这个版本的 gcc、gcc-c++、binutils 三个包,如果不指定则会安装 devtoolset 提供的所有的包。

最后启用 devtoolset-8 的命令是临时的,重启之后就会失效,可以把相关指令写入配置文件中:

echo "source /opt/rh/devtoolset-8/enable" >>/etc/profile

安装完成之后可以执行 gcc -v 查看一下 gcc 的版本。

gcc
8.3.1,够用了,当然官网的最新版本是 10.2.0,感兴趣的可以自己折腾一下。

ok,gcc 安装完成,我们进入之前解压的 redis-6.0.9 文件夹,依次执行编译、安装

cd /usr/src/redis-6.0.9
make
make install

这样 Redis 就安装完成了。可以直接用 redis-server 命令启动服务。

redis-server

通过 redis.conf 文件可以配置服务参数:

  • 修改 daemonize nodaemonize yes ,使 redis 能够以守护进程方式在后台运行
  • 设置 bind 参数,绑定可以访问 redis 服务的主机。默认配置是 bind 127.0.0.1 ,也就是只有本机可以访问,把这行注释掉可以使所有主机能够访问。但出于安全考虑,我们应该将其配置为后端服务器的 IP。
  • 设置 Redis 密码,仍然是在这个配置文件里,将 requirepass 一行的注释去掉,设置你想要的密码,如 requirepass Admin789#

如果图省事,可以在配置文件中把 protected-mode yes 修改为 protected-mode no,关闭保护模式。(在 yes 的情况下,必须设置密码、绑定主机才可远程访问,实际环境不推荐 no)

保存该配置文件,在启动 Redis 时按照 redis-server /path/redis.conf 格式指定该文件即可。建立另存为其它文件,把默认的配置也保存下来。

最后,别忘了 Linux 的防火墙。

四、前端服务部署

克隆,网络,前端的部署还是很简单的。

第一步,修改前端跨域访问配置(config/index.js)与请求转发地址(main.js),把原来的 localhost 替换为后端 IP。当然我们要想知道后端 IP 是多少,需要先把后端虚拟机建起来。后端同样也需要配置前端的 IP,循环依赖,不爽。

config.js:

proxyTable: {
  '/api': {
    target: 'http://192.168.194.129:8443',
    changeOrigin: true,
    pathRewrite: {
      '^/api': ''
    }
  }
}

main.js:

axios.defaults.baseURL = 'http://192.168.194.129:8443/api'

第二步npm run build,打包。

第三步,在服务器上安装 Nginx 并配置,推荐大家使用 OpenResty。

OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

OpenResty 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。

执行 yum install openresty,openresty 会安装到 /usr/local 下,进入其中的 nginx 目录找到 conf 文件,即可对 Nginx 进行配置。

这次我们不修改端口,直接用默认的 80,只配置一下 try_files,以适配 Vue-Router 的 history 模式。

location / {
     try_files $uri $uri/ /index.html;
}

接下来,把打好包的文件(dist 目录下的 staticindex.html)放入 Nginx 的 html 文件夹中。

文件位置

接下来,在 nginx 目录中执行

./sbin/nginx -p `pwd`/ -c conf/nginx.conf 

也可以在外部执行命令,替换 pwd 为 nginx 的路径即可。

通过浏览器访问 http://前端 IP,如 http://192.168.194.138,可以看到项目前端已经成功部署。(也可以直接在虚拟机执行 curl localhost 意思一下)

前端

五、后端服务部署

为了部署后端项目,首先需要安装 Java 运行环境。

1.JDK 安装

我们这次依旧使用 jar 包部署后端项目,当然也可以使用 war 包 + tomcat 的方式部署,后期优化服务器配置会更加灵活。

运行 jar 包需要 JRE,为了方便后期在服务器上修改源码并编译,也可以选择直接安装 JDK。使用 yum 安装 JDK 还是很简单的。可以先查看可以下载的版本

yum list | grep jdk

JDK
看来目前比较受欢迎的还是 JDK 1.8 和 JDK 11。那我们本着选新不选旧的原则安装 11 吧,过去虽然开发时我调到过更高的版本,但并没有用最新的特性,所以这里不影响。

执行 yum install -y java-11-openjdk.x86_64,等待安装完成后,使用 java -version 命令查看版本信息,成功回显则说明安装成功。

最后,为满足一些软件的需求,需要增加全局环境变量配置。首先使用 which java 查看 jdk 所在路径

jdk path

接下来,编辑 /etc/profile 文件,增加下面三行,注意根据上面的路径设置 JAVA_HOME 变量

export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-11.0.9.11-2.el7_9.x86_64
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

保存,使用 source /etc/profile 使其立即生效,在终端输入 $JAVA_HOME,信息正常回显,配置成功。

$JAVA_HOME

2.配置后端项目并打包

首先是数据源配置,主要是远程服务器的地址、端口和账号密码。

# MySQL 配置
spring.datasource.url=jdbc:mysql://192.168.194.128:3306/wj?characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=wjadmin
spring.datasource.password=Admin456@
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# Redis 配置
spring.redis.host=192.168.194.131
spring.redis.port=6379
spring.redis.password=Admin789#
···

可以把新建两个配置文件,分别命名为 application-prod.propertiesapplication-dev.properties ,把开发环境和生产环境中配置不同的地方区分开,再在 application.properties 中指定需要的配置,比如 spring.profiles.active=prod

然后是跨域访问,在 config/MyWebConfigurer 中修改允许跨域的地址为前端服务器地址:

 @Override
 public void addCorsMappings(CorsRegistry registry) {
     registry.addMapping("/**")
             .allowCredentials(true)
             .allowedOrigins("http://192.168.194.138")
             .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
             .allowedHeaders("*")
             .maxAge(3600);
 }

pom.xml 设置 <packaging>jar</packaging>,执行 mvn clean install,把生成的 jar 包放入后端服务器 /usr/local/bin 目录下,执行

java -jar wj-1.0.0.jar

因为我们配置了初始化数据库的脚本,数据会被自动注入。接下来就是芜湖起飞。

MD 报错了,我一看,哦,原来是数据库用户权限忘了给后端,进 mysql 服务配置一下就好了。

访问 http:192.168.194.138,测试一下功能,终于可以正常显示内容了。

测试2


这篇文章拖到了 2020 年的最后一个小时,本来我是想打游戏的,结果脑子一抽居然过来写文章了。虽然写的不算满意,但还是决定立刻发出去,给这个晚上加点仪式感,反正以后还能改不是。

朋友圈里都在总结跨年,我想我就不矫情了,想来今年确实实现了几个小目标,但现在看似乎没什么挑战性,标准定低了,也不好意思跟你们分享。

祝各位新年快乐,我排位去了,有啥事儿明年再说。

后记 - 2021 年 5 月 4 日

当初写这篇文章时以为还能继续更新下去,没想到拖了这么久,我也彻彻底底地转向了前端,实在是没有办法重拾做这个教程的感觉了。

战线这么长,有些当时所谓的新技术今天看也已经过时了。因为一些基础工具的升级,很多新读者卡在了很前面的地方,留下一句对教程难度的抱怨就转身离去了。也因为在终于厌倦了帮人 debug 后不再有问必答,被一些不太懂得为别人考虑的同学挖苦讽刺。

这些读者也让我明白了一些道理,比如把你的帮助当作理所当然的人迟早会因为你不再帮助他对你心怀怨恨,又比如有些东西能学会的人不用你教,学不会的人你教了也没用。

当然,他们只不过是极少数,在这个过程中收获的支持与认可远比负面的反馈要多的多。我只是一个平庸的开发者,性格上也没有什么闪光点,被许多素昧平生的人夸赞是我从未想过的。虽然一直以来做的不够好,但我会努力回馈这些善意。

大学毕业快四年了,我能明显感觉到自己变地更加现实、更加功利,很多事情不再单纯因为喜欢而去做,也不能因为不喜欢而不做。迷茫与自我否定,算计与误解,劳累与失望,所谓成长,确实不是一个美好、浪漫的过程。

但为什么明知前路艰辛,还是要去承担这些,甚至不惜变成自己最讨厌的人呢?因为你不去承担,总有人要替你承担。也因为这痛苦并不是没有尽头的,美好终会回归,就在你成为强者,却仍然选择做一个好人的那一刻。

又自顾自说了这么多废话,总之,《白卷》系列教程算是正式和大家告别了。其实我一开始并没有打算写这么多,只是想简单地体验一下一些框架。它是我和各位共同的作品,我对自己单方面宣布它的终结表示歉意。

我这个瓜皮作者还是会活跃下去,但可能很长一段时间都仅仅是在前端领域了。今年做了付费专栏,牵扯了很多精力,虽然数据惨淡,但也逼迫自己养成了周更的习惯。等我输出能力再强一些,会安排专栏之外的内容的。

不搞前端的各位,可能咱们也就此说再见了,希望我们短暂的交流是愉快的,如果你曾经遇到问题向我求助而我没有及时回复,希望你能够谅解,因为对一个社畜来说,每天能自己支配的时间实在是少的可怜。

对前端有兴趣的各位,希望一两年以后,我们依然能像今天一样相互成就,共同进步!

Logo

前往低代码交流专区

更多推荐