从零搭建基于 Java 的服务器生产环境

之前的文章介绍了我最近开发的一款工具软件 移动工具箱,然而安装包放在华为应用市场上面,如果不安装华为应用市场就无法下载软件,无奈,只能自己从零搭建一个服务器来进行应用的宣传和下载。搭建一个服务器是很简单的,这里我们将服务器的要求提高一些,以对标 “生产” 的标准进行要求,要做的东西比较多,故作此文,加以记录。

首先说下环境,腾讯云,CentOS 7.5 64 位,服务器采用 Java 完成,具体来说就是 Springboot. 大致罗列下要做的事情:

  • 介绍服务器远程连接方式
  • 进行基础的服务器安全配置
    • 禁止 root 用户直接登录服务器
    • 关闭 22 登录端口
    • 警惕空密码账户
    • 设置密码过期时间
    • 设置密码复杂度要求
    • 禁止 ping 服务器
    • 最重要的,防止 rm -rf
  • 搭建生产服务器环境
    • 安装 ftp 服务
    • 连接 ftp 服务器
    • 安装 JDK
    • 搭建 Springboot 环境
  • 发布应用
    • 在服务器中启动 Springboot 程序
    • 指定虚拟机启动参数
    • 编写服务器启动脚本程序
    • 配置域名

1、介绍服务器远程连接方式

对于腾讯云,一种连接方式是直接使用腾讯云提供的在线的 webshell 进行登录,这种登录方式提供的是 ssh 登录。假如我们进行 ftp 登录来上传文件的话比较不方便。腾讯云的 webshell 还是比较好用的,不过一般我们推荐使用 Xshell 进行 ssh 登录,使用 xftp 进行 ftp 登录。可以到我的公众号后台回复 【服务器连接软件】 获取我打包好的软件。

拿到一个新的服务器,我们要做的第一件事情就是到控制台里去重置密码。对于密码,为了系统安全起见,应该选择比较复杂的口令,例如最好使用 8 位长的口令,口令中包含有大写、小写字母和数字,不应该包含单词,应该与姓名、生日等不相同。

按照上面的介绍重置密码之后就可以进行登录了,这里使用 Xshell 进行登录,登录方式比较简单,此处略过。

2、进行基础的服务器安全配置

2.1 禁止 root 用户直接登录服务器

为啥要禁止 root 登录呢?首先,root 这个用户是众所周知的,使用 root 用户增加了被攻击的风险。其次,root 权限比较高,不论是服务器拥有者还是服务器的协同开发都不应该直接使用 root 操作服务器。使用 root 用户,一旦出现操作意外,就可能会给服务器带来致命的危害。

禁止 root 登录之前先添加一个新的管理员用户,

# Step 1: 创建新用户 admin
adduser the_xxx_admin
# Step 2: 修改用户密码,密码也应该满足上述要求哦
passwd the_xxx_admin
# Step 3: 赋予该用户 su 执行权限
gpasswd -a the_xxx_admin wheel

这里增加了一个名为 the_xxx_admin (用户名不要太大众化)的管理员用户,并将其用户组设置为 wheel. wheel 在 linux 中是一个特殊的用户组,这个组被设计用来解决 su 指令的授权问题。也就是只有在 wheel 组里面的成员才能使用 su 切换到 root 用户。如果一个用户不在 wheel 组里面,即使使用 su 指令并正确地输入了密码也无法切换到 root 用户。wheel 用户组不仅具有 su 指令的权限,也具有 sudo 的权限,可以通过浏览 /etc/sudoers 文件来了解。

增加了新的用户之后并赋予 sudo 权限之后,我们就可以禁止 root 用户登录了:

  1. 修改 /etc/ssh/sshd_config 文件将 #PermitRootLogin yes 修改为 PermitRootLogin no;
  2. 使用 service sshd restart 重启 ssh 服务并验证登录效果即可。

此处修改文件的时候可以使用 vim /etc/ssh/sshd_config 命令,然后输入 /PermitRootLogin 定位到 #PermitRootLogin yes 所在的一行,输入 a 进入编辑模式,修改完毕之后使用 :wq! 退出即可。

重启之后再使用 root 进行登录,立刻显示 “服务器拒绝 ssh 连接”,然后使用新增的用户进行登录即可。

2.2 关闭 22 登录端口

主要是因为 22 端口登录已经是众所周知的了,如果我们使用其它端口实现 22 端口的功能可以减少被攻击的风险。修改登录端口需要修改 /etc/ssh/sshd_config 文件。在该文件中,我们先新增一个端口,推荐使用 10000 号以上的端口。注意这里应该先保留 22 端口,设置完毕并验证新的端口可以登录之后再删除 22 端口。还有需要注意的地方,验证新的接口是否可以登录之前一定要先打开防火墙,防火墙关闭的时候端口可以直接使用,而一旦防火墙打开,而你又没有使用防火墙打开该端口,那么防火墙可能会直接屏蔽该端口,导致你无法登录自己的服务器。

这里直接使用 vim 编辑 /etc/ssh/sshd_config 文件的时候应该会出现 Permission Denied。此时,可以通过 sudo 命令来完成,

sudo vim /etc/ssh/sshd_config

与 sudo 类似的还有 su 命令。sudo 和 su 功能类似,区别在于 sudo 命令需要输入当前用户的密码,su 命令需要输入要切换到的用户的密码。 sudo 比 su 更合理一些,因为,比如如果希望某个用户切换到 root 用户,使用 su 的方式要求执行者必须知道 root 账户的密码,密码被很多人知道显然不安全。而 sudo 只要求确认当前执行者的身份,如果当前执行的用户在 sudoers 中被授予过访问 root 的权限,则它可以切换到 root 用户,这样我们既赋予了该用户切换到 root 的权限,又无需告知该他 root 账户的密码。此外,我们还可以通过在 sudoers 文件中进行配置来限制用户的权限,记录更多的用户日志等。

进入编辑模式之后找到 Port 22 并在下面新增一行 Port 10022:

Port 22
Port 10022

然后,我们需要按照上述管理端口的逻辑开放 10022 端口。然后,重启 ssh 服务:

systemctl restart sshd # 或者 service sshd restart

如果验证登录成功,再将上面的 Port 22 注释掉,重启 ssd 服务然后再使用 22 端口登录,验证是否已经禁止。

此时,我再使用 22 端口进行登录,提示 Connection failed,表明禁止 22 端口登录成功。

2.3 警惕空密码账户

空密码账户会增加系统的安全风险。使用如下指令查询:

cat /etc/shadow | awk -F: '($2==""){print $1}'

另外,可以通过修改 ssh 配置文件 /etc/ssh/sshd_config 来禁止空密码账户进行登录:

PermitEmptyPasswords no

2.4 设置密码过期时间

设置和查询密码有效期会用到指令 chage,该指令格式如下:

chage [参数] [数值]

常用参数:

  1. -m : 密码可更改的最小天数。为零时代表任何时候都可以更改密码。
  2. -M : 密码保持有效的最大天数。
  3. -W : 用户密码到期前,提前收到警告信息的天数。
  4. -E : 帐号到期的日期。过了这天,此帐号将不可用。
  5. -d : 上一次更改的日期。
  6. -I : 停滞时期。如果一个密码已过期这些天,那么此帐号将不可用。
  7. -l : 例出当前的设置。由非特权用户来确定他们的密码或帐号何时过期。

一般的使用示例如下。

首先查询用户密码的有效期:

chage -l admin

显示信息如下:

Last password change                                    : Apr 07, 2020
Password expires                                        : never
Password inactive                                       : never
Account expires                                         : never
Minimum number of days between password change          : 0
Maximum number of days between password change          : 99999
Number of days of warning before password expires       : 7

我们可以使用 chage 指令来修改密码的过期时间,比如使用如下指令将密码的最大有效期设置为 99 天:

chage -M 99 admin

2.5 设置密码复杂度要求

有两种方式来设置密码复杂度。

一种方式是修改 /etc/login.defs 文件,这里面几个比较重要的选项。可以通过修改选项的值来设置对密码对要求:

PASS_MAX_DAYS   90  #密码最长过期天数
PASS_MIN_DAYS   80  #密码最小过期天数
PASS_MIN_LEN    10  #密码最小长度
PASS_WARN_AGE   7   #密码过期警告天数

另外一个方法是,修改 /etc/pam.d/system-auth 文件。编辑该文件,在下面这行进行配置即可:

password required pam_pwquality.so dcredit=-1 ucredit=-1 ocredit=-1 lcredit=0

配置的可选参数可以参考 /etc/security/pwquality.conf 文件。常用的参数如下:

  1. retry=N:定义登录/修改密码失败时,可以重试的次数;
  2. Difok=N:定义新密码中必须有几个字符要与旧密码不同。但是如果新密码中有1/2以上的字符与旧密码不同时,该新密码将被接受;
  3. minlen=N:定义用户密码的最小长度;
  4. dcredit=N:定义用户密码中必须包含多少个数字;
  5. ucredit=N:定义用户密码中必须包含多少个大写字母;
  6. lcredit=N:定义用户密码中必须包含多少个小写字母;
  7. ocredit=N:定义用户密码中必须包含多少个特殊字符(除数字、字母之外);

2.6 禁止 ping 服务器

禁止 ping 后,不让别人通过域名 ping 到你的 ip. 禁用后,你在 ping 自己的域名会给你返回服务商的 IP 并提示超时,这样你就可以减少 IP 暴露,增加一点安全。

实现的方式是:编辑 /etc/sysctl.conf 里面配置,如果没有添加上下面一行配置:

net.ipv4.icmp_echo_ignore_all=1

然后使用如下命令使配置生效:

sysctl -p

这样就禁止了对服务器的 ping 操作。如果要解除,只需要将上面的 1 换成 0 即可。

配置完毕之后在本地的命令行里输入 ping 你的ip 地址,如果显示请求超时,则说明配置成功。

2.7 最重要的,防止 rm -rf

rm -rf 这个梗想必大家都了解,这节我们不是要教你如何在服务器上面执行该操作,来观察它带来的喜剧效果,而是如何避免该操作带来的负面影响。需要注意的地方:下面的操作会使用 source 命令,这个指令也是万分危险的指令,必须慎重! 这个指令用来修改类似于 windows 中的环境变量,假如 source 出现问题,可能会导致几乎所有的命令都无法使用,非常危险。

实现的原理是对 rm 命令进行改写,将其关联到一个自定义脚本。首先,我们在 ~ 目录下面一个隐藏的 .trash 文件和 .tools 文件夹,

# 创建 .trash 文件夹
mkdir .trash
# 创建 .tools 文件夹
mkdir .tools
# 检查创建结果
ls -al

然后,在 .tools 文件夹中创建一个名称为 remove.sh 的脚本,编辑其内容如下:

#!/bin/sh
trash_dir=~/.trash/`date +%Y%m%d`

if [ ! -d ${trash_dir} ] ;then
    mkdir -p ${trash_dir}
fi

for i in $*
do
    suffix=`date "+%H%M%S"`
    if [ ! -d "${i}" ]&&[ ! -f "${i}" ];then    # 首先判断是否是合法的文件或者文件夹
        if [[ "${i}" != "-rf" && "${i}" != "-f" ]];then    # 这里对-rf进行处理,因为mv指令后面没有-rf,-f参数
            echo "[${i}] do not exist"
        fi
    else
        file_name=`basename $i`   # 取得文件名称
        mv ${i} ${trash_dir}/${file_name}_${suffix}_${RANDOM}
        echo "[${i}] delete completed"
    fi
done

这里简单解释下这个脚本文件的内容吧。这个脚本文件按照文件名拼接的规则,在文件名中增加了日期信息(用来对同名文件进行区别),然后使用 mv 命令将指定对文件夹移动到 ~/.trash 目录下面。应该注意这里对 -rf 参数的处理。之前也看过一些博客的脚本,没有对这两个参数做处理,导致 mv 命令无法执行。

然后,我们修改 ~/.bashrc 文件,在末尾追加下面一行代码。将 rm 命令关联到指定的 Shell 文件:

alias rm='sh ~/.tools/remove.sh'

然后,使用如下命令来使我们对修改生效,

source ~/.bashrc

最后,在用户根目录下面创建文件夹,并使用 rm -rf 文件夹 指令测试效果。

这里本质上是把要删除的目录移动到了 .trash 目录下面。然后根据操作的日期对移除对文件夹进行管理。这无疑地会导致我们的文件夹越来越大,所以,我们要定期对该文件夹进行删除。这里,我们使用脚本来实现这个目标。

这里我们创建一个定时任务来执行删除操作。在 .tools 文件夹下面添加一个脚本文件 clean.sh ,编辑内容如下。其作用是找到回收站中修改日期大于 3 天的文件,执行真正的删除操作:

#!/bin/sh
trashdir=~/.trash
find ${trashdir} -mtime +3 -exec 'rm' -rf {} \;

然后,我们将该任务添加到 crontab 任务中。

使用 crontab -e 命令进入定时任务编辑界面,在最后面加入

0 3 * * * sh ~/.tools/clean.sh     #每天 3:00 执行清理回收站的脚本

使用 service crond restart 重启 crontab 服务,使用 crontab -l 命令如果可以看到刚才添加的那段话,则证明添加成功。这里使用了一段 cron 表达式,Linux 的 cron 表达式和 Springboot 中的表达式起始单位有些不同,需要注意下。

另外,注意到上文中需要使用 source ~/.bashrc 才能使配置的 bash 文件生效。当用户重新登录的时候也必须这么做才能使我们的配置再次生效。作为程序员,这当然是无法容忍的。于是,我们可以用下面的方法来解决这个问题,来让每次登录服务器之后我们的配置自动生效,

# 编辑 .profile 文件
vim .profile

# 在文件中增加如下的配置
if [ -s ~/.bashrc ]; then
    source ~/.bashrc;
fi

# 或者直接增加下面的代码也行,不过简单粗暴了点
source ~/.bashrc

这样配置之后,每次启动重新登录的时候我们的配置就可以自动生效了。

最后的一个问题,如果用户目录下面不存在 .bashrc 或者 .profile 文件,那么可以从 /etc/skel 文件下面将其拷贝到用户目录下面,然后再做上面的配置即可。

3、搭建生产服务器环境

上面的配置已经做到了安全性的基本要求。下面着手搭建服务器生产环境。既然是基于 Java 的生产环境,那么要求安装的东西不多,一个必备的 ftp 服务,一个 JDK 环境即可。当然,正式的话还有数据库 MySQL, Redis,以及常用的中间件 MQ、ES 等。这里我们先搭建一个基础的 Java 环境,能运行起 Springboot 即可,所以 JDK 就够了。

3.1 安装 ftp 服务

Ftp 还是比较重要的,我一般使用 ftp 上传要发布的 jar 包。另外,个别软件的安装包比较大,而且存在网络连接问题,导致下载速率比较低,因此,有时候会选择本地下载完成安装包之后通过 ftp 上传到服务器再进行安装。所以,ftp 是非常重要的一个环节。

先安装 vsftpd,

yum install -y vsftpd

安装之后会生成 /etc/vsftpd/ 和 /var/ftp/,其中

  1. /etc/vsftpd/ 目录下面包含四个文件:

    1. ftpusers 指定了哪些用户不能访问 ftp 服务
    2. user_list 当 vsftpd 里 userlist_deny=NO 时,只允许这里的用户访问 ftp 服务;当 vsftpd 里 userlist_deny=YES(默认) 时,不允许这里的用户 访问 ftp 服务
    3. vsftpd.conf 是 vsftpd 的核心配置文件
    4. vsftpd_conf_migrate.sh 是 vsftpd 操作的一些变量和设置脚本
  2. /var/ftp/ 是匿名访问 ftp 服务器的时候能够访问的目录

使用 vim vsftpd.conf 编辑配置文件,比较重要的配置项说明:

# 是否允许匿名登录,默认 YES,为了安全起见,我们应该将其关闭
anonymous_enable=NO
# 是否允许本地账号(系统账号)登录
local_enable=YES
# 是否运行上传操作,如果要运行上传那么就要开启这个配置
write_enable=YES
# 是否以独立运行的方式监听服务,ftp 服务的运行模式,NO 时表示 xinetd 模式;YES 时表示 standlone 模式
listen=NO
# 控制 user_list 的功能,允许 user_list 列表用户登录ftp
userlist_enable=YES
# 控制 user_list 的功能,不允许 user_list 列表用户登录ftp
userlist_deny=YES
# 这个指令应该谨慎!!因为当其设置为 YES 的时候,虽然可以登录服务器,但是登录的 ftp 用户可以访问上级目录
chroot_local_user=NO
# 不受限制的用户列表,在 vsftpd 目录下面建立 chroot_list 文件,写入指定的用户名即可
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd/chroot_list
allow_writeable_chroot=YES # 这句必须有

其他配置还可以选择:

# 地址:设置监听f t p服务的ip地址,默认监听所有IP地址
listen_address=IP
# 设置监听ftp服务的端口号
listen_port=21
# 允许下载权限
download_enable=YES
# 用户切换进入目录时显示 “.message”文件(如果已存在)的内容
dirmessage_enable=YES
# 启用xferlog日志,默认记录到 /var/log/xferlog
xferlog_enable=YES
# ftp日志格式
xferlog_std_format=YES
# 数据连接端口号默认20
connect_from_port_20=YES
# 禁止被动模式连接;默认允许被动模式连接
pasv_enable=NO
# 起始端口号
pasv_max_port=21600
# 结束端口号
pasv_min_port=21700
# 开启PAM验证
pam_service_name=vsftpd
# 限制多个客户端同时连接(0为无限制)
max_clients=0
# 允许相同IP地址访问ftp连接次数(0为无限制)
max_per_ip=0
# ftp的访问控制列表提升安全性使用
tcp_wrappers=YES

配置完成之后就可以使用以下命令开启服务:

service vsftpd start

另外还需要配置 系统的安全模块 SELinux:

# Step 1: 打开 SELinux 文件进行编辑:
vim /etc/sysconfig/selinux

# Step 2: 将 SELINUX=1 修改为 disabled

然后,我们需要添加一个独立的用户来进行 ftp 访问,并且限制 ftp 用户只能进行 ftp 连接,不能进行服务器登录,不应该将 ftp 用户和登录用户混为一谈,以此来进一步保障服务器的安全性:

# Step 1: 添加用户,语法是 useradd [-mMnr][-c <备注>][-d <登入目录>][-e <有效期限>][-f <缓冲天数>][-g <群组>][-G <群组>][-s <shell>][-u <uid>][用户帐号]
# 用户登录终端设为 /sbin/nologin,即使之不能登录系统,只能访问 ftp
useradd ftpuser -d /home/ftpuser -s /sbin/nologin

# Step 2: 修改用户密码
passwd ftpuser

# Step 3: 设置用户权限,/path/you/set 为刚刚设定的该用户 ftp 的根目录
chown -R ftpuser /home/ftpuser

# Step 4: 再到 /etc/vsftpd 目录下新建(或者修改) chroot_list 文件 并添加允许访问的用户

# Step 5: 重启 ftp 服务
service vsftpd restart

# Step 6: 若出现无法上传或者读取目录情况,设置之前指定的目录的读写权限
chmod 777 /home/ftpuser

3.2 连接 ftp 服务器

1. 远程连接 ftp

在 Mac 上面,可以使用 finder 连接 ftp 服务器,缺点是只能下载不能上传。参考文章 《Mac自带FTP工具用法》 进行连接即可,另外还可以使用 iterm2 连接。iterm2 是 Mac 上的终端神奇。其安装和配置可以参考 《MAC终端神器iterm2——告别黑白》,然后需要在 Mac 上面安装 ftp,参考 《Mac ftp 命令安装即使用》。至于 Windows 上面,直接使用 Xftp 即可。可以到我的公众号后台回复 【服务器连接软件】 获取我打包好的软件。

2. 连接 ftp 服务器的问题

用 xftp 连接提示无法打开,无法显示远程文件夹:选择 “属性->选项->将使用被动模式” 选项去掉即可。

3.3 安装 JDK

需要注意:Java SDK 和 ElasticSearch 等其他的中间件的版本对应关系,一般来说 JDK 8+ 可以满足大部分需求。

到官方网站 JDK 官方下载地址 下载 JDK 即可。在官网下载之前需要先进行用户登录,获取到下载链接之后使用 wget 命令进行下载即可:

wget http://download.oracle.com/otn-pub/java/jdk/8u171-b11/512cd62ec5174c3487ac17c61aaa89e8/jdk-8u171-linux-x64.tar.gz?AuthParam=1531155951_4e06a4d17c6c1dbfb8440352e19dd2ae

注意下载后缀中包含 AuthParam 参数,如果链接不包含这个参数,下载之后可能无法使用。

如果从官网下载速度比较慢,可以尝试从华为镜像下载:华为 JDK 镜像。

进行安装:

# Step 1: 创建安装目录
mkdir /usr/local/java/

# Step 2: 解压至安装目录
tar -zxvf jdk-8u171-linux-x64.tar.gz -C /usr/local/java/

设置环境变量:

# Step 1:打开 profile 编辑模式
vim /etc/profile

# Step 2:在 profile 文件末尾追加环境变量
export JAVA_HOME=/usr/local/java/jdk1.8.0_171
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

# Step 3:使 profile 生效
source /etc/profile

# Step 4:添加软链接
ln -s /usr/local/java/jdk1.8.0_171/bin/java /usr/bin/java

# Step 5: 检测是否安装成功
java -version

3.4 搭建 Springboot 环境

如果仅仅是提供一个下载服务的话,还是比较简单的,直接使用 Springboot 的 web 和模板引擎构建一个 html 页面,apk 放到 static 目录下即可。也有问题需要注意下,即如果这样简简单单地搭建的一个服务是不安全的,因为没有做限流处理。而一般我会使用 Redis 做限流,但是这样就要介绍如何安装 Redis 以及 Redis 的各种安全配置等……所以,目前先不做,当前能够展示应用详情,并且能够给用户提供下载渠道即可。

4、发布应用

4.1 在服务器中启动 Springboot 程序

Springboot 应用开发完毕,直接在项目根目录执行 mvn install 编译打包即可。打出来的包,通过 ftp 上传到服务器。

对于 jar 包,如果想要在后台启动,可以使用如下指令,

nohup java -jar target/包名.jar --spring.profiles.active=lt &

也就是使用 nohup 并且末尾追加了一个 &。如果不使用 nohup 只追加一个 &,也可以后台启动,但是这种方式在 ssh 会话结束之后程序就会停止。

按照上面 nohup 的启动方式,会在当前目录下面生成一个名为 nohup.out 的文件,终端的内容会被写入到这个文件当中。使用这种启动方式的时候终端不会显示启动过程,所以我们只能通过获取 nohup.out 的内容来判断程序是否启动成功。动态获取文件的内容可以使用指令 tail -f nohup.out 来获取。当然,如果希望将输出人日志重定向到某个具体的文件或者不输出日志(重定向到 /dev/null)也是可以的。比如下面的命令用来将日志重定向到 catalina.out 文件:

nohup java -jar xxx.jar > catalina.out  2>&1 &

查看 Springboot 工程的进程使用如下命令,

ps aux | grep "java -jar" | grep -v "grep"

上述显示的内容中包含了进程的 pid,如果要杀掉某个进程,直接输入下面的命令即可:

kill -9 pid

4.2 指定虚拟机启动参数

1. 远程连接虚拟机

首先,我们需要远程连接上我们的应用来观察虚拟机的状况。不论是排查线上问题还是项目启动之前虚拟机参数调整,这个都比较重要。常用的连接工具有 jconsole 和 jvisualvm 两个。让我们的虚拟机可以被远程监控,需要我们做小小的配置。

首先,我们需要修改虚拟机远程连接的密码。进入 jre 安装目录下面的 management 目录中,比如我的 jdk1.8.0_181/jre/lib/management。然后,复制 jmxremote.password.template 并将其重命名为 jmxremote.password,然后注释掉最后两行的注释,这里的 monitorRole 就是默认的远程连接的登录账号,紧随其后的就是登录密码。需要把默认的密码修改掉,增加服务器的安全性。

然后,我们需要在项目的启动参数中增加几个参数来配置并启用远程连接,比如我的设置了之后的参数如下:

nohup java -jar -Djava.rmi.server.hostname=23.123.122.31 -Dcom.sun.management.jmxremote.port=11162 -Dcom.sun.management.jmxremote.rmi.port=11163 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true $portal_app_jar > portal.out 2>&1 &

这里我们的参数配置及其含义如下:

  1. java.rmi.server.hostname:远程连接的时候的 ip,就是你的服务器的真实 ip 地址;
  2. com.sun.management.jmxremote.port:远程连接的时候的端口,不能和应用的端口相同;
  3. com.sun.management.jmxremote.rmi.port:远程 rmi 连接的时候的端口,不能和应用的端口相同;
  4. com.sun.management.jmxremote.ssl:是否使用 ssl 连接方式;
  5. com.sun.management.jmxremote.authenticate:是否启用身份认证功能,这个应该启用,否则就别人不需要输入密码就可以直接连接并查看你的虚拟机了。

这样配置完成之后开发上面填写的两个端口,然后重新启动应用。

本地远程连接虚拟机的时候使用 jconsole 和 jvisualvm 皆可。这两个可执行文件放在 jdk 的 bin 目录下面。双击打开之后直接连接即可。

2. 应用启动参数配置

首先是一些官方的参考资料

常用的参数:

-XX:MetaspaceSize=128m (元空间默认大小)
-XX:MaxMetaspaceSize=128m (元空间最大大小)
-Xms1024m (堆默认大小也是最小的大小)
-Xmx1024m (堆最大大小)
-Xmn256m (新生代大小)
-Xss256k (棧最大深度大小)
-XX:SurvivorRatio=8 (新生代分区比例 8:2)
-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器)
-XX:+PrintGCDetails (打印详细的GC日志)

具体取值多少要根据自己的应用的类型、并发量和服务器环境综合分析,可以结合上面的一些文档为自己的程序指定参数。

4.3 编写服务器启动脚本程序

当然了,指定服务器启动参数还是比较繁琐的,所以,通常我们会编写启动脚本,然后通过执行脚本来启动和终止程序。下面是一段参考代码,

#!/bin/bash
# 程序运行脚本程序
# 作者:王守恒
# 定义要启动的程序的 Jar 文件,不指定 Jar 文件的版本
app_jar=box.jar

# 脚本文件使用说明
usage() {
echo "|====================================================================="
echo "|                           脚本文件用法说明                         |"
echo "|====================================================================="
echo "|  sh 脚本名称.sh [start|stop|restart|status] [prev]                 |"
echo "|                                                                    |"
echo "|                               参数说明                             |"
echo "|                                                                    |"
echo "|  start: 启动程序                                                   |"
echo "|  stop: 停止程序                                                    |"
echo "|  restart: 重新启动程序                                             |"
echo "|  status: 程序状态                                                  |"
echo "|                                                                    |"
echo "|  prev: 可选参数,如果指定该参数,则会将程序的运行日志输出到指定    |"
echo "|     的文件中,否则不输出,建议项目正式发布的时候不启用该选项       |"
echo "|====================================================================="
}

# 程序启动函数
# 函数体内使用 $ 获取的参数是函数调用的时候传入的参数,
# 而不是整个脚本被调用的时候传入的参数
start() {
  # 运行portal程序
  vm_opt='-XX:NewSize=216m -XX:MaxNewSize=216m -XX:SurvivorRatio=8 -Xms1024m -Xmx2048m -XX:+PrintGCDetails -Djava.rmi.server.hostname=你的ip -Dcom.sun.management.jmxremote.port=你的端口 -Dcom.sun.management.jmxremote.rmi.port=你的端口 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true'
  if [[ $1 == "prev" ]]; then
    # 预览
    nohup java -jar $vm_opt $app_jar > box.out 2>&1 &
    echo "box[prev] start succeed"
  else
    nohup java -jar $vm_opt $app_jar > /dev/null &
    echo "box app start succeed"
  fi  
}

# 程序停止函数
stop() {
# 获取程序的pid并kill之
is_exist $app_jar
if [ $? -eq "0" ]; then
  kill -9 $pid
  echo "Box was killed with pid ${pid}"
else
  echo "Box is NOT running" 
fi
}

# 程序状态函数
status() {
is_exist $app_jar
if [ $? -eq "0" ]; then
  echo "Box is running with pid ${pid}"
else
  echo "Box is NOT running" 
fi
}

# 程序重新启动函数
restart() {
  stop $app_jar
  start $app_jar
}

# 判断指定的进程存不存在
is_exist() {
pid=`ps -ef | grep $1|grep -v grep | awk '{print $2}'`
if [ -z "${pid}" ]; then
  return 1
else
  return 0
fi
}

# 根据用户输入参数选择执行的函数
case "$1" in
"start")
start $2
;;
"stop")
stop
;;
"status")
status
;;
"restart")
restart
;;
*)
usage
;;
esac

# End
# =========================================================================

这里我区分了预览和正式环境,因为有时候应用启动之前就因为某些配置错误无法启动,此时如果使用正式环境的话,日志会按照我们上述的配置被丢掉,从而无法配查问题。可以通过启动的时候指定一个 prev 参数将启动过程中的日志重定向到当前的目录下的指定文件中。这里主要有几个功能吧:启动、重启、判断是否启动以及指令说明。

4.4 配置域名

按照上面的配置我们已经可以可以使用 ip 地址进行访问了,但是我们还是要配置一下域名。这里我使用的是之前的一个子域名。当然,除了配置域名之外还要配置一下 CDN,配置了 CDN 之后的访问逻辑如下:

box.meiyan.tech -> CDN 域名 -> CDN 域名指定的 ip 地址或者 ip 地址加端口号

配置 CDN 比直接使用域名映射到 ip 地址的方式还有一个好处,就是在 ping 指定的域名的时候不会直接暴露 ip 地址,这样可以阻挡一步分流量攻击。

总结

这里介绍了搭建服务器和服务器安全配置相关的知识,也涉及到了一些 linux 指令,因篇幅的原因,上面仅仅介绍了一部分,更无法详细进行介绍。此外,我写了完整的十几篇文章,从基础的 linux 指令到各种常用的中间件安全配置等详细的知识,感兴趣的话关注下吧。这里提到的 移动工具箱 是我最近开发的一款 Android 工具软件,非常实用,其中包含了很多对开发者非常实用的功能,感兴趣可以下载尝试。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容