一、引言

最近公司在海外上云服务器,作者自己也搞了云服务器去搭建部署系统,方便了解整体架构和系统的生命周期,排查解决问题可以从原理侧进行分析实验。虽然用的云不是同一个,但是原理都是相通的。

二、选型

作者选用的是腾讯云,没别的原因,就是便宜加牌子大。

阿里云肯定是更好一些的,不管是服务售后还是服务器内核和操作系统都是比较活跃的,毕竟作者以前公司用的就是阿里云,了解一些。

不过以前都是和运维沟通排查问题,作者自己只能看到一些服务器监控和运维的截图,这对于了解整个云服务架构体系的生命流程是不太友好的。

操作系统选择了CentOS7.6-Docker20,毕竟linux的底层是必须的,目前的容器环境也是服务的基础。

基本参数如下,没必要太好

  • CPU – 2核 内存 – 4GB
  • 系统盘 – SSD云硬盘 60GB管理快照
  • 流量包 – 500GB/月(带宽:5Mbps)

三、系统搭建部署

作者准备把springboot用Maven打包,jar包拿到docker容器运行

3.1、后端

后端是SpringBoot,SSM框架,代码就不贴了,作者写了个小程序给家里人用的。

3.2、打包

这一步很麻烦,打出来的包很小,作者当时还没意识到问题,本地java -jar运行一下,报错了。显示no main manifest attribute, in /**.jar,问了一下chatGpt,这就很扯,做了这么久的SpringBoot,他里面的application怎么可能不是字段设置为主类的呢。

作者有找了网上的一些文章,有的说是打包的时候没有设置入口类,pom的build重新设置一下。

org.apache.maven.pluginsmaven-compiler-plugin3.8.11.81.8org.apache.maven.pluginsmaven-jar-plugin3.2.0true**.Application

然后出现了新的错误:Caused by: java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)

跟chatgpt拉扯了许久,还是没找到正确的pom,又找了朋友看以前做的项目,其实很简单的po,完全依赖spring自动打包发现

org.springframework.bootspring-boot-maven-plugin

打包之后有60几M大小,之前只有几百k,所以很明显了,再次本地运行java -jar,显示成功,通过Localhost也可以访问里面的接口

3.3 部署

部署系统jar之前,需要先把数据库部署好,不然根本运行不起来。

1、部署mysql

这里安装的的是mysql5.7,习惯了这个版本

下载MySQL 5.7的Docker镜像:docker pull mysql:5.7
下载完成后,可以运行以下命令来创建并运行MySQL容器:docker run –name mysql-e MYSQL_ROOT_PASSWORD= -p 3306:3306 -d mysql:5.7
创建一个名为`mysql`的容器,并将MySQL的默认端口3306映射到主机的3306端口。将“替换密码。
检查MySQL容器是否正在运行:docker ps -a
如果看到`mysql`容器正在运行,说明MySQL已经成功安装并运行在Docker中。

如果MySQL容器未运行,启动它:docker start mysql

登录到MySQL:docker exec -it mysql mysql -u root -p

检查MySQL服务器的运行状态:service mysql status
启动它:service mysql start

作者在这中间还走了一些弯路,一开始不想设置用户名密码,结果导致他自动生成了一串,作者还不知道密码是什么,又进去修改密码,最后还生成了两个root账号,一个权限是*一个是localhost,导致后面jar运行出了问题。

2、部署服务

腾讯云界面上有个SFTP的文件可视化,作者一开始不知道,还准备搞市面上的一些文件传输软件,毕竟jar包需要传输到远程服务器。

首先要创建一个Dockerfile,这个文件没有后缀的,在服务器窗口建好文件夹之后

输入:viDockerfile

从vi界面搭建i编辑,自动创建文件,写完内容之后点击esc退出编辑状态,再点击:wq退出文件并且保存。

Dockerfile里面内容:

# 基础镜像使用javaFROM openjdk:8-jdk-alpine# VOLUME 指定了临时文件目录为/tmp。# 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmpVOLUME /tmpADD test-provider-service-0.0.1.jar /test-provider-service.jarENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/test-provider-service.jar"]

1. `FROM openjdk:8-jdk-alpine`:指定使用OpenJDK 8的Alpine Linux作为基础镜像。

2. `VOLUME /tmp`:指定将主机的`/var/lib/docker`目录与容器的`/tmp`目录进行挂载,用作临时文件目录。

3. `ADD test-provider-service-0.0.1.jar /test-provider-service.jar`:将当前目录下的`test-provider-service-0.0.1.jar`文件复制到容器的根目录,并将其重命名为`test-provider-service.jar`。

4. `ENTRYPOINT [“java”,”-Djava.security.egd=file:/dev/./urandom”,”-jar”,”/test-provider-service.jar”]`:指定容器启动时要执行的命令。这里是运行Java应用程序的命令,使用`java`命令执行`test-provider-service.jar`文件。

然后把jar传入服务器,jar要和Dockerfile在一个文件夹里面。

创建docker镜像:docker build -t **.

这个.是指使用当前文件夹,所以要先cd到jar所在的文件夹,**就是指容器的名字。

运行容器:docker run -d -p 5006:5006**

用的是宿主机5006端口,容器的5006也是作者springboot用的端口,默认springboot是用8080的,这个要在配置文件设一下

3、问题排查解决

3.1、root拒绝

docker ps -a看一下容器状态,诶嘿,退出来了exit,说明jar有问题,但是docker logs ** 有看不到日志,那就加个日志打印。

public class Application {public static void main(String[] args) {try{SpringApplication.run(Application.class, args);} catch (Throwable t) {System.out.println(t);throw t;}}}

先得把之前的容器和镜像删除,然后重新生成,删除容器需要用容器id或者名称,在docker ps -a中可以看到:docker rm **

删除镜像:docker rmi **

爆出来的是:access denied for user ‘root’@’ip’ (using password: YES)

一开始还以为是配置错了账号密码,在本地又跑了一下,但是很明显没有问题,

看了一下mysql,在MySQL的user表中,每个用户都有一个唯一的组合键,由用户名(User)和主机名(Host)组成。这意味着即使用户名相同,只要主机名不同,就会被视为不同的用户。我猜测的是root这个%会拦截所有的root访问,因为在本地用workBench去链接显示的也是拒绝。但是%的root不是我设置的密码,所以得加一个新的用户密码。

root | localhost
root | %

创建一个具有相同权限的新用户,但允许从任何主机访问MySQL服务器:

CREATE USER ‘new_user’@’%’ IDENTIFIED BY ‘password’;GRANT ALL PRIVILEGES ON *.* TO ‘new_user’@’%’ WITH GRANT OPTION;

3.2、链接mysql失败

再次运行镜像爆出了不一样的错误:

Unsatisfied dependency expressed through bean property 'sqlSessionFactory'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]: Unsatisfied dependency expressed through method 'sqlSessionFactory' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [com/alibaba/druid/spring/boot/autoconfigure/DruidDataSourceAutoConfigure.class]: Invocation of init method failed; nested exception is com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

很明显是mysql的链接问题,但是为什么会出现这种问题,问一下chatgot

说的很官方,没有什么实际作用,但是原理是相同的,要么是驱动程序问题,要么是网络问题,如果是驱动程序应该不可能出现本地运行成功,服务器失败。

那么就要思考服务器的docker容器和本地运行到底有什么区别,作者想到的是mysql和服务jar运行在两个容器当中,本地访问服务器的mysql是通过公网ip,那么在服务器jar里面的配置是使用127.0.0.1(localhost)去链接mysql,那么是不是不能直接使用localhost去链接。

因为云服务器的容器不一定在同一个宿主机上面,即使在同一个宿主机,分配端口维度也可能是容器,这个就看每个云厂商怎么设置的规则了。

带着这样的猜测,作者把配置文件改为使用内网ip,结果成功了。

四、总结

通过自己安装部署云服务器上的系统,作者对于整个系统架构有了更多了解,实践出真知,对于技术工具实验和底层原理排查都有帮助。