Pre and post deployment of spring Vue + boot project

Posted by wayz1229 on Sun, 02 Jan 2022 21:15:13 +0100

preface

This blog post mainly wants to connect the basic processes and key points of front-end and back-end development and server deployment. It does not involve very deep things in the development content and technology used, but although the sparrow has all the small and five internal organs, it is completely convenient for students who are not very familiar with some aspects to get started quickly.

  • Applicable population: front-end development, back-end development, server operation and maintenance
  • Development language: Java, JavaScript
  • Development framework: StringBoot, vue2 0
  • Server deployment: Linux, Docker, Nginx

1, Backend project development and deployment

We quickly develop and deploy the simplest say hello project to provide a back-end API interface.
The function of this interface is very simple. It returns hello, {msg} content according to the passed parameter msg.

1. Back end development

  1. Java code

SayHelloMain.java

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

SayHelloController.java

@RestController
public class SayHelloController {
    @GetMapping("/say-hello")
    public Object sayHello(String msg) {
        Map<String, Object> result = new LinkedHashMap<>(3, 0.95F);
        result.put("code", "0000");
        result.put("data", "hello, " + msg);
        return result;
    }
}
  1. configuration file

application.yml

server:
  port: 80
  servlet:
    context-path: /test

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>ace.gjh</groupId>
  <artifactId>api-say-hello</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>api-say-hello</name>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.1.RELEASE</version>
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>

    <!-- SpringBoot web -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

  1. Interface call example
GET http://localhost/test/say-hello?msg=world

Response:

{
    "code": "0000",
    "data": "hello, world"
}

2. Back end deployment

After the backend project is compiled and packaged, the generated file api-say-hello-1.0-snapshot jar

2.1 making docker image

Dockerfile script:

FROM lapierre/java-alpine:8
MAINTAINER ACE_GJH
ENV WORK_DIR /opt/deploy/say-hello
RUN mkdir -p $WORK_DIR
WORKDIR $WORK_DIR
ENV JAR_NAME api-say-hello-1.0-SNAPSHOT.jar
COPY ./$JAR_NAME $WORK_DIR
EXPOSE 80
ENV JAVA_OPT="-Xmx32m -Xms32m -Xmn16g"
CMD java $JAVA_OPT -jar $JAR_NAME

Run the docker image build command:

docker build -t say-hello:v1.0 .

2.2 start docker container

docker run --name say-hello -d say-hello:v1.0

Note that no port of the server is mapped when the container is started, so it cannot be accessed through the external port of the server, but only between containers.

2, nginx deployment

Nginx is the focus of this deployment. The back-end service has been deployed, and nginx is responsible for the reverse proxy of the back-end API and the release of the front-end project.

1. nginx configuration

  1. Get the ip address of the back-end service

Here, "say hello" refers to the name of the backend service container just deployed. In this step, you can query the ip address of the back-end container on the docker network, so that the nginx container can access the back-end services through the docker network.

docker inspect say-hello | grep '"IPAddress"'
  1. Writing nginx configuration files

Only one virtual service can be established in the configuration file. The / api matching rule of location can determine whether to access the back-end service, and nginx will intercept the uri behind the / api and redirect it to the real back-end service.

nginx.conf

# User group
user nginx;
# The working process can be set to the number of CPU cores by default
worker_processes auto;
# Error log storage directory
error_log /var/log/nginx/error.log;
# pid file storage location
pid /run/nginx.pid;
# Load module configuration file
include /usr/share/nginx/modules/*.conf;
# Maximum number of concurrent connections per worker process (IO multiplexing)
events {
    worker_connections 1024;
}

# http protocol
http {
    # File extension and type mapping table
    include   mime.types;
    # Default file type: byte stream
    default_type  application/octet-stream;
    # Enable gzip compression
    gzip on;
    gzip_min_length 1k;
    gzip_buffers    4 16k;
    gzip_http_version 1.0;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;
    gzip_vary on;

    # Establish virtual service
    server {
        listen 80;
        server_name  localhost;
        
        # Match the url of the back-end API for reverse proxy
        location /api {
            rewrite ^/api/(.*)$ /$1 break; #Get the uri after / api for redirection
            proxy_pass http://172.17.0.2:80/; #  Actual backend service address
        }
    }

}

2. Start nginx container

docker run --name nginx -d -p 18080:80 -v $(pwd)/deploy/log:/var/log/nginx -v $(pwd)/deploy/nginx.conf:/etc/nginx/nginx.conf nginx:1.21.4

Access effect:

GET http://192.168.56.101:18080/api/test/say-hello?msg=gjh
{
    "code": "0000",
    "data": "hello, gjh"
}

3, Front end project development and deployment

The blogger is the first time to develop the front-end project, so the front-end development part is recorded in detail. I hope the front-end students can make more corrections.

1. Front end development

1.1 pulling vue basic frame

Follow the npm method of this tutorial to install: Rookie tutorial: Vue JS installation

npm -v
npm install -g cnpm --registry=https://registry.npmmirror.com
# Global installation Vue cli
$ cnpm install --global vue-cli
# Initialize new project
$ vue init webpack show-hello

 Project name show-hello
? Project description a test project
? Author ACE_GJH
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Set up unit tests Yes
? Pick a test runner karma
? Setup e2e tests with Nightwatch? Yes
? Should we run `npm install` for you after the project has been created? (recommended) no
cd show-hello
cnpm install
cnpm run dev

Browser access http://localhost:8080/ You can see the vue classic logo

1.2 configuring vue agents

Because it is a front-end and back-end separation project, you need to configure the back-end service agent to prevent cross domain problems.
I use vue2 In version 0, the agent is configured as follows:
In config / index The proxyTable attribute is found in the. JS file. The modification is as follows

    proxyTable: {
      '/api': {
        target: 'http://192.168.56.101:18080',
        changeOrigin: true,
        pathRewrite: {
          // Note that this will be / API / test / say hello? msg=China
          // Replace with http://192.168.56.101:18080/api/test/say-hello?msg=China
          '^/api': '/api'
        }
      }
    }

The purpose of this step is to resolve the uri at the beginning of / api in the axios request, which host address to access, and replace / api in the original uri with / api as a new uri to access the real host.

1.3 configuring axios

  1. Installing axios
cnpm install axios
  1. Introducing axios

In main JS to declare axios as a global variable

import axios from 'axios'
Vue.prototype.$axios = axios

1.4 development components

  1. Project directory
  2. Main components

App.vue

<template>
  <div id="app">
    <div id="btn">
      <router-link to="/"><button>home page</button></router-link>
      <router-link to="/luffy"><button>Monkey D Luffy</button></router-link>
      <router-link to="/zoro"><button>Sauron</button></router-link>
    </div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
#btn {
  text-align: center;
  margin-bottom: 50px;
}
#btn button {
  width: 80px;
  margin-left: 10px;
  margin-right: 10px;
}
</style>
  1. Home page component

Home.vue

<template>
  <div class="hello">
    <img src="../assets/onepiece.jpeg">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'Home',
  data: function () {
    return {
      msg: 'Welcome to the age of great navigation!'
    }
  }
}
</script>
  1. Other components

HelloLuffy

<template>
  <div class="hello">
    <img src="../assets/Luffy.jpeg">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloLuffy',
  data: function () {
    return {
      msg: null
    }
  },
  created () {
    this.getMsg()
  },
  methods: {
    getMsg () {
      return this.$axios.get('/api/test/say-hello?msg=Luffy')
        .then(response => {
          console.log('response', response)
          this.msg = response.data.data
        })
    }
  }
}

</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
div img {
  width: 200px;
  height:200px;
}
</style>

HelloZoro.vue

<template>
  <div class="hello">
    <img src="../assets/Zoro.jpeg">
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloZoro',
  data: function () {
    return {
      msg: null
    }
  },
  created () {
    this.getMsg()
  },
  methods: {
    getMsg () {
      return this.$axios.get('/api/test/say-hello?msg=Zoro')
        .then(response => {
          console.log('response', response)
          this.msg = response.data.data
        })
    }
  }
}

</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
div img {
  width: 200px;
  height:200px;
}
</style>

1.5 configuring routing

router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import HelloLuffy from '@/components/HelloLuffy'
import HelloZoro from '@/components/HelloZoro'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/luffy',
      name: 'luffy',
      component: HelloLuffy
    },
    {
      path: '/zoro',
      name: 'zoro',
      component: HelloZoro
    }
  ]
})

2. Front end deployment

2.1 packaged items

cnpm run build

2.2 modify nginx configuration on the server

This step adds the / opt/nginx/frontend directory in the container as the root directory of the front-end project

    # Establish virtual service
    server {
        listen 80;
        server_name  localhost;
        
        # Front end project access
        location / {
            # Define the root directory location of the front-end project;
            root /opt/nginx/frontend; 
            index index.html;
            error_page 404 /index.html;
        }

        # Match the url of the back-end API for reverse proxy
        location /api {
            rewrite ^/api/(.*)$ /$1 break; #Get the uri after / api for redirection
            proxy_pass http://172.17.0.2:80/; #  Actual backend service address
        }
    }

2.3 modify nginx container startup command

This step adds a parameter to mount the front-end root directory / opt/nginx/frontend in the container to the server/ deploy/frontend directory. When starting, pay attention to stop and delete the containers started before.

docker run --name nginx -d -p 18080:80 -v $(pwd)/deploy/frontend:/opt/nginx/frontend -v $(pwd)/deploy/log:/var/log/nginx -v $(pwd)/deploy/nginx.conf:/etc/nginx/nginx.conf nginx:1.21.4

2.4 deploying front-end projects

Copy all files in the packed dist folder to the directory of the project root directory at the front end of the mapping container.

frontend/
├── index.html
└── static
    ├── css
    ├── img
    └── js

3. Page effect

  1. home page
  2. Switch to other pages
    Click the "Luffy" button to jump to the Luffy page.

    Click the "Solon" button to jump to the Solon page.

4, Upgrade: how to deploy multiple front-end projects?

1. Problem analysis

As we saw earlier, there is only one index in the root directory of the front-end project HTML and static, if we follow this deployment method, when we access through IP + port number, we can only access one front-end project. If we need to deploy multiple front-end projects, we also need to add ports and nginx configurations. Can we access different items by prefixing URIs?
The answer is yes. We can synchronize "fiddling" between the request url and the server project file path to ensure that the url path requested by the front end is consistent with the actual file path. For example, for the project one, the original static resource url was / static/css/xxx. Now there is a layer of file directory one on the server, so the current static resource url can be changed to / one/static/css/xxx.

frontend/
└── one
    ├── index.html
    └── static
        ├── css
        ├── img
        └── js

2. Problem solving

The solution only needs to modify three places on the basis of the previous one.
Suppose the uri access prefix of project 1 is / one.

2.1 modifying resource paths for front-end projects

In config / index JS, change the assetsPublicPath property from / to / one /, and note that there is "/" after "one".
Explain: the request path prefix of static resources is spliced by assetsPublicPath+assetsSubDirectory. The default is / static, but now it has become / one/static.

2.2 create a new subdirectory from the root directory

You need to create a one folder under the root directory of the front-end project. Previously, frontend was equivalent to the packaged dist folder. Now frontend is equivalent to the parent directory of dist.
Previous Directory:

frontend/
├── index.html
└── static
    ├── css
    ├── img
    └── js

Current directory:

frontend/
└── one
    ├── index.html
    └── static
        ├── css
        ├── img
        └── js

2.3 access url

Previous access url: http://192.168.56.101:18080
Current access url: http://192.168.56.101:18080/one/ Yes, there is a /, which is determined by nginx's location matching and conversion rules, otherwise there will be problems.

It's OK to jump to the page:

3. Add: does the routing mode have an impact?

The default hash mode is used in our front-end projects. Will it be affected if we change to history mode? Let's try it together.

3.1 modify routing mode

In router / index JS.

3.2 deployment testing

The project of history mode is packaged and deployed to the server as project 2 for access.
Sure enough, there was no display except for the three buttons.

3.3 solution

I put the mouse pointer on the "Luffy" button, and I saw its jump address in the lower left corner of the browser:

Compared with the hash mode, the history mode removes / # /, but there is less / two in front of the jump address, so it should be very simple to solve this problem.
Add base request prefix to the route:

Access again after deployment:

Click the "Luffy" button to jump to the page:

Click the "Solon" button to jump to the page:

Perfect!

Summary

Through this development and deployment, the use of front-end Vue and back-end SpringBoot framework, nginx configuration and docker container deployment are connected in series. It also studies how to deploy multiple front-end projects on one port through the cooperation of front-end projects and nginx. At the same time, we also see the difference between hash and history routing modes.
The development and deployment process of this time is also full of harvest, which is the so-called "no one can do everything in the day, no contribution will be made to the sea".

Topics: Java Docker Nginx Spring Boot Vue.js