从零搭建基于 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 命令。sudosu 功能类似,区别在于 sudo 命令需要输入当前用户的密码,su 命令需要输入要切换到的用户的密码。 sudosu 更合理一些,因为,比如如果希望某个用户切换到 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 工具软件,非常实用,其中包含了很多对开发者非常实用的功能,感兴趣可以下载尝试。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349