传统的Vue项目部署:

  1. webpack 打包为静态资源。
  2. 将静态资源部署到Nginx。
  3. 使用Nginx作为Web服务器。
  4. Nginx可能还需要作为后端的接口的静态代理。

对应的配置文件为

server {
    listen       80;
    server_name  localhost;
    charset utf-8;
    access_log  /var/log/nginx/host.access.log  main;

    absolute_redirect off;

    location /{
        proxy_pass http://localhost:8080; 
    }
    location /manage {
        alias  /unisign-manage-control;
        index  index.html index.htm;
    }
}

在单web应用的场景中缺失已经能够满足我们的需求。

上面部署的一定问题:

  1. 在微服务的模型中所有流量都需要从网关进行控制,上面的这段配置中,静态资源走了nginx那么久不经过网关了,那么我们在网关上的控制策略无效了。
  2. 在上面的nginx配置文件中显示的指定的某个ip,这是很不好的,因此后端的实例可能会因为弹性伸缩具有多个实例,当然这种时候你可以通过域名的dns服务来解决,但是这是有隐形成本的,如果之前系统中没有dns的服务发现组件那么还需要搭建一套基于dns服务发现的系统,增加了系统复杂度。
  3. 由于使用采用nginx部署,也就是所它并不是一个可以被服务发现到的应用,也就没办法进行服务的发现和治理。
  4. 容器化时,后端的代理问题很难解决,因为运行在容器中的应用,它将会得到一个容器内部网络的IP,如果不通过dns等策略很难以解决。

那么如何微服务化?

有两种方案:

  1. 作为springboot 的静态资源
  2. sidecar

springboot 代理静态资源

起一个SpringBoot项目,将webpack打包好的vue项目作为静态资源放到resources/static目录中,然后将该项目注册到eureka注册中心,通过在网关上配置指定的路由访问该服务即可。

这种做法实际上也就是使用到tomcat的静态资源代理服务,然后在增加上服务发现,实现起来比较简单

方案1

sidecar

Spring Cloud的Sidecar方案提供对非JVM的微服务提供注册发现和服务治理的功能。
方式2

通过上面的方式就可以不用每次更新前端项目之后,重新编译整个jar包,并且对于前端的开发人员来说部署方式和原来一直,只是nginx不在负责代理后端服务,前端项目需要稍作该做增加一个静态资源用于健康检查,这样由于sidecar部分提供了服务的注册功能,这样网关服务就可以服务发现前端的项目并做代理。

准备

Demo源码: github . https://github.com/Trisia/static-resource/tree/master/dist
示例静态html文件 index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello</title>
</head>
<body>
    <h1>This is Main page</h1>
</body>
</html>

除了静态文件之外为了支持健康检查,还需要返还一个jsonhealth.json

{
    "status": "UP"
}

前端

Springboot 静态代理服务编写

  • springboot 2.0.7-RELEASE
  • springcloud Finchley.SR2

Demo源码: github . https://github.com/Trisia/static-proxy

pom.xml 引入依赖

 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
 </dependency>
<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
</dependency>

配置application.yaml文件

spring:
  application:
    name: static-proxy
  resources:
    static-locations: file:${PROXY_PATH:/www/html}

server:
  port: 777

eureka:
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    service-url:
      defaultZone: ${REGISTER_SERVER_URL:http://172.22.203.11:8761/eureka/}

上面配置重点关注spring.resources.static-locations这个就是制定外部静态资源的位置,此处使用了一个环境变量PROXY_PATH如果系统中没有设置该环境变量,那么将使用/www/html作为静态资源的目录。该目录下的所有文件将会被映射到/**路径下。此处使用环境变量是为容器化做准备。

注意:在windows环境中开发时,/www/html中的/是项目所处磁盘的根目录,如果项目在D盘的某个文件夹中,那/www/html就是D:\www\html

我们将准备过程中的文件放置到/www/html目录下,启动项目,访问接口就可以获取页面。
测试结果

可以在微服务的网关中加入下面配置

zuul:
  routes:
    static-proxy: /ui/**

通过微服务中的网关服务加上这个服务的名称也能够访问到这个页面
微服务访问

容器化

由于这个代理服务的功能:提供静态资源代理服务的注册和发现、代理静态资源。

对于代理服务来说变动较少可以作为基础镜像,然后在前端项目中使用基础镜像构建

基础镜像

首先在项目根目录创建docker目录,然后编写Dockerfile

FROM openjdk:8-jre

RUN mkdir -p /www/html && \
    echo "OK" > /www/html/index.html

ADD target/static-proxy-1.0.0-SNAPSHOT.jar /app.jar

ENTRYPOINT ["java", "-jar", "-Duser.timezone=GMT+8", "/app.jar"]

到项目根目录构建基础镜像

docker build -f docker/Dockerfile -t static-proxy:1.0.0-SNAPSHOT .

运行测试基础镜像

docker run -it -p 777:777 static-proxy:1.0.0-SNAPSHOT

测试结果
static-proxy:1.0.0-SNAPSHOT 基础镜像功能 符合预期

应用镜像

我们在我们静态文件的目录下创建一个Dockerfile

这里还是使用准备阶段生成的那个静态页面来模拟vue项目webpack最终打包完成的结果

Demo 源码: github . https://github.com/Trisia/static-resource

项目结构

Dockerfile 需要使用到上一步构建的基础镜像

FROM static-proxy:1.0.0-SNAPSHOT

ENV PROXY_PATH=/www/html REGISTER_SERVER_URL=http://172.22.203.11:8761/eureka/

COPY dist/* /www/html/

设置一些必要的环境变量,然后复制静态文件,启动指令将会从基础镜像中继承得到。

构建镜像

docker build -f docker/Dockerfile -t static-proxy:1.0.0-SNAPSHOT .

运行项目

docker run -it -p 777:777 app:v1

测试结果如下
符合预期
符合预期,你还可以从通过网关服务的代理进行访问,结果也是一样的。


sidecar

服务编写

Demo代码: github

结合操作系统安装 nginx Nginx instal http://nginx.org/en/linux_packages.html

然后 nginx的代理配置为frontend.conf,放置于/etc/nginx/conf.d/目录下

server {
    listen       7776;
    server_name  localhost;

    location / {
        root   /home/static-resource;
        index  index.html index.htm;
    }
}

重新启动nginx

systemctl restart nginx 
# 或
service nginx restart

新建一个springboot项目
版本说明:

  • springboot 2.0.7.RELEASE
  • springcloud Finchley.SR2

pom.xml 引入依赖

 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
 </dependency>
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-netflix-sidecar</artifactId>
 </dependency>

eureka-client是用于注册发现的,sidecar是sidecar的代理。

在启动类上增加上@EnableSidecar

@EnableSidecar
@SpringBootApplication
public class StaticSidecarApplication {
    public static void main(String[] args) {
        SpringApplication.run(StaticSidecarApplication.class, args);
    }
}

配置 resources/application.yaml

server:
  port: 7777
spring:
  application:
    name: static-sidecar
eureka:
  client:
    service-url:
      defaultZone: http://172.22.203.11:8761/eureka

sidecar:
  port: 7776                                     # 本地的 nginx 服务端口
  health-uri: http://localhost:7776/health.json  # 健康检查

Eureka 注册中心需要自己准备哦。

除了上面springboot和springcloud的常规配置之外,主要是sidecar的配置

  1. 由于我们将会把sidecar和nginx部署到同一台机器上,所以此处不用指令nginx服务的后台地址,只需填写本地nginx启动的端口。
  2. 为了能让eureka进行服务治理,sidecar需要知道nginx这边的服务状态是否可用,因此需要访问之前在前端项目中预先准备好的健康检查的文件health.json,所需要手动指明健康检查的路径。

启动sidecar服务,就可以完成对本地7776端口的代理并且能够从注册中心中发现该服务。

sidecar就是一个zuul,我们从启动的日志打印和spring-cloud-netflix-sidecar都可以看出来。
zuul依赖

从注册中心中可以看到服务已经注册成功
服务注册

好了现在可以通过网关加上服务名称,访问到我们静态index.html页面。

网关服务需要自己准备。

我们可以在网关部分对该服务做个映射

zuul:
  routes:
    STATIC-SIDECAR: /ui/**

操作结果

容器化

分析:

  • 由于我们这个sidecar项目的作用是对前端项目的代理和服务发现,除此之外项目中没有任何其他的逻辑,项目本身只要构建一次基本就不需要调整,对于这种类型的服务我个人倾向于把它打包到基础镜像中去。
  • 但是在我们这个sidecar项目中任然有部分是需要调整的,比如说注册中心的地址,因此需要将该部分会变的量在yaml文件中配置为从环境变量中获取。

静态资源的镜像制作分为:

  1. 制作基础镜像
  2. 制作应用镜像
制作基础镜像

修改后的的yaml 为

server:
  port: 7777
spring:
  application:
    name: static-sidecar
eureka:
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    service-url:
      defaultZone: ${REGISTER_SERVER_URL:http://172.22.203.11:8761/eureka/}


sidecar:
  port: 7776                                     # 本地的 nginx 服务端口
  health-uri: http://localhost:7776/health.json  # 健康检查

使用IP和端口注册服务。

在sidecar项目的目录创建一个docker文件夹,用于存放构建docker所需要的相关文件。

首先放入之前提到的index.htmlhealth.json到test-resource目录下

然后是 nginx的文件代理如下 default.conf

server {
    listen       7776;
    server_name  localhost;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}

目录结构

然后是Dockerfile

FROM nginx:1.17.3-alpine
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \
      apk add openjdk8-jre-base && \
      rm -rf /var/cache/apk/*

COPY docker/test-resource/*  /usr/share/nginx/html/
COPY docker/default.conf /etc/nginx/conf.d/default.conf

ADD target/static-sidecar-1.0.0-SNAPSHOT.jar /app.jar

ENTRYPOINT sh -c 'nginx && java -jar /app.jar'

首先选择一个nginx镜像作为基础镜像,然后安装jre运行环境,接下来将测试代理用的静态文件复制到nginx代理的默认位置,然后使用自定义的代理配置覆盖默认配置文件。

接下来加入sidecar文件。

使用后台运行nginx并启动sidecar jar包。这样项目就启动起来了。

容器其中尽可能不要启动两个进程,因为容器的模型的推荐使用单进程,如果要多进程如上述场景推荐还是使用 supervisor,此处为了简单使用&& 一同运行。

如何构建镜像

切换目录到sidecar项目的根目录,然后运行docker build命令构建镜像

docker build -f docker/Dockerfile -t static-sidecar:1.0.0-SNAPSHOT .

测试,以主机网络模式运行并映射端口

docker run -it -p 7777:7777 --network host static-sidecar:1.0.0-SNAPSHOT

项目启动后通过网关我们可以得到相同的结果
测试结果
这样基础镜像就已经制作完成

制作应用镜像

制作镜像的过程也就是把类似于vue项目中webpack打包出来的dist复制到指定位置然后运行就可以。(把Dockfile放置于vue项目根目录)

注意一定要在项目中放置一个返还值为{"statue":"UP"}的json静态文件并可以访问,他将会用于健康检查,否则项目运行可能会报错,检查不通过。

FROM static-sidecar:1.0.0-SNAPSHOT
COPY dist/* /usr/share/nginx/html/
ENTRYPOINT sh -c 'nginx && java -jar /app.jar'

在vue项目根目录运行构建

docker build -t your_app_name:version .

your_app_name:version替换为应用名称和版本

和上一步一样可以打完包之后测试一下。

致谢

[1]. doc . spring clould netifix . https://cloud.spring.io/spring-cloud-netflix/multi/multi__polyglot_support_with_sidecar.html
[2]. docker(11):alpinelinux安装openjre . freewebsys . csdn . https://blog.csdn.net/freewebsys/article/details/53744348
[3]. Run multiple services in a container . docker . doc . https://docs.docker.com/config/containers/multi-service_container/
[4]. Spring Boot静态资源访问和配置全解析小 .洋人最happy . csdn . https://blog.csdn.net/u010358168/article/details/81205116
[5]. Docker running nginx plus jar. stackoverflow . answered objectuser . https://stackoverflow.com/questions/30270779/docker-running-nginx-plus-jar
[6]. How do I use Spring Boot to serve static content located in Dropbox folder?. Dave Syer . stackoverflow

Logo

前往低代码交流专区

更多推荐