Use Docker to deploy Spring-Boot+Vue blog system

Source address: doodle

Original address: Using Docker to deploy Spring-Boot+Vue blog system


At the beginning of this year, I completed the implementation of my own Fame blog system, and I also made a blog post Spring-boot+Vue = Fame wrote a summary of the blog as a record and introduction. From the completion of the implementation to the present, it has been updated intermittently according to the actual usage.

It's just that I feel a little troublesome every time I deploy it online, because my server memory is too small. Even if I only update the code of the front-end (fame-front) every time, I have to update npm buildmy back-end service during execution. The process of (fame-server) is turned off, otherwise the server will be stuck (miserable).

And this project is separated from the front and back ends. The front page of the blog also uses a Nuxtframework for SEO. If it is the first deployment or server migration, it will be troublesome. If you deploy once, the following steps are required.

  1. Install mysql, modify the relevant configuration files, set the encoding time zone, etc., then restart
  2. Download and install java, configure the java environment
  3. Download and install maven, configure the maven environment
  4. Download and install nginx, modify configuration files, design reverse proxy, etc.
  5. Start the spring-boot project
  6. Packaging vue projects, npm install, npm run buildetc.
  7. start nuxt project, npm install, npm run startetc

If you can successfully complete these seven steps, you are lucky. If there is an error in any of the steps in the middle, you may have to go back to find which step has the problem, and then redeploy.

I choose to die

In the face of these needs, Docker is a big killer to solve these problems. Whether its virtualization technology isolates each container so that its resources do not affect each other, or the consistent operating environment, and the one-click deployment of docker-compose, it perfectly solves the above problems.

Project address: Fame

Docker and Docker-compose installation

The functions and use of Docker and Docker-compose can be seen on the online Chinese document Docker - from entry to practice

The following is the shell script for Centos7 to install and configure Docker and Docker-compose. Other operating systems can refer to the modification to install. The Docker version is docker-ceand the Docker-compose version is1.22.0

#!/bin/sh

### 更新 ###
yum -y update

### 安装docker ###
# 安装一些必要的系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加软件源信息
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 更新 yum 缓存
sudo yum makecache fast
# 安装 Docker-ce
sudo yum -y install docker-ce
# 启动docker并设置为开机启动(centos7)
systemctl  start docker.service
systemctl  enable docker.service
# 替换docker为国内源
echo '{"registry-mirrors": ["https://registry.docker-cn.com"],"live-restore": true}' > /etc/docker/daemon.json
systemctl restart docker
# 安装dokcer-compose
sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
# 安装命令补全工具
yum -y install bash-completion
curl -L https://raw.githubusercontent.com/docker/compose/$(docker-compose version --short)/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose
### 安装docker结束 ###

Dockerized transformation

Directory structure after transformation

Let's take a look at the structure of the remodeled project

├─Fame
│  │  .env                            // docker-compose环境参数配置文件
│  │  docker-compose.yml              // docker-compose文件
│  ├─fame-docker
│  │  │  fame-front-Dockerfile        // fame-front的Dockerfile文件
│  │  │  fame-server-Dockerfile       // fame-server的Dockerfile文件
│  │  │  
│  │  ├─fame-admin
│  │  │      fame-admin-Dockerfile    // fame-admin的Dockerfile文件
│  │  │      nginx.conf               // fame-admin的nginx服务器配置文件
│  │  │      
│  │  ├─fame-mysql
│  │  │      fame-mysql-Dockerfile    // mysql的Dockerfile文件
│  │  │      mysqld.cnf               // mysql的配置文件mysqld.cnf
│  │  │      
│  │  └─fame-nginx
│  │          nginx-Dockerfile        // 整个项目的nginx服务器的Dockerfile文件
│  │          nginx.conf              // 整个项目的nginx的配置文件
│  │          
│  ├─fame-admin   // 博客管理后台,基于Vue+elementui
│  ├─fame-front   // 博客前端,基于Nuxt
│  └─fame-server  // 博客服务端,基于spring-boot

In order not to destroy the structure of the original project, the relevant configuration files of both the front-end and the back-end docker are all extracted and placed in a separate fame-dockerfolder.

docker-compose.ymlPut it in the project root directory and run the command directly in the root directory:docker-compose up -d

[root@localhost Fame]# docker-compose up -d
Starting fame-front ... 
Starting fame-admin ... 
Starting fame-front ... done
Starting fame-admin ... done
Starting fame-nginx ... done

Just start the project and never repeat the tedious steps again!

The transformed docker project structure

fame-structure

modified docker-compose.yamlfile

version: '3'
services: 
  fame-nginx:
    container_name: fame-nginx
    build:
     context: ./
     dockerfile: ./fame-docker/fame-nginx/nginx-Dockerfile
    ports:
      - "80:80"
    volumes:
     - ./logs/nginx:/var/log/nginx
    depends_on:
      - fame-server
      - fame-admin
      - fame-front
  
  fame-mysql:
   container_name: fame-mysql
   build: 
     context: ./
     dockerfile: ./fame-docker/fame-mysql/fame-mysql-Dockerfile
   environment:
     MYSQL_DATABASE: fame
     MYSQL_ROOT_PASSWORD: root
     MYSQL_ROOT_HOST: '%'
     TZ: Asia/Shanghai
   expose:
      - "3306"
   volumes:
     - ./mysql/mysql_data:/var/lib/mysql
   restart: always

  fame-server:
    container_name: fame-server
    restart: always
    build: 
     context: ./
     dockerfile: ./fame-docker/fame-server-Dockerfile
    working_dir: /app
    volumes:
      - ./fame-server:/app
      - ~/.m2:/root/.m2
      - ./logs/fame:/app/log
    expose:
      - "9090"
    command: mvn clean spring-boot:run -Dspring-boot.run.profiles=docker -Dmaven.test.skip=true
    depends_on:
      - fame-mysql

  fame-admin:
   container_name: fame-admin
   build: 
    context: ./
    dockerfile: ./fame-docker/fame-admin/fame-admin-Dockerfile
    args:
      BASE_URL: ${BASE_URL}
   expose:
      - "3001"

  fame-front:
   container_name: fame-front
   build: 
    context: ./
    dockerfile: ./fame-docker/fame-front-Dockerfile
   environment:
      BASE_URL: ${BASE_URL}
      PROXY_HOST: ${PROXY_HOST}
      PROXY_PORT: ${PROXY_PORT}
   expose:
      - "3000"

docker-compose.ymlThe structure is generally similar to the directory structure just now, and it is also divided into the following parts

  1. fame-nginx
  2. fame-mysql
  3. fame-server
  4. fame-admin
  5. fame-front

There docker-compose.ymlare a few points in this

  • fame-mysqlThe sum fame-servershould restartbe set to always, because there is currently no solution for Docker-compose to solve the problem of container startup. Even if it is set depends_on, it only controls the time when the container starts to start, and cannot control the time when the container starts to complete, so let fame-mysqland fame-serverthese two containers are set restartto prevent spring-boot from starting before mysql is started and failing to start with an error.
  • fame-server, fame-mysql, fame-nginxthese three containers are all set volumes, and the logs log file in the container is mounted to the project directory of the host machine, so that the log file can be viewed at any time.
  • fame-mysqlThe mysql storage file of the container is also set to be volumesmounted in the project directory ( ./mysql/mysql_data:/var/lib/mysql). This suggestion can be set to other directories of the host according to the actual situation. Otherwise, if the project is accidentally deleted, the database data in the container will also be gone.

Most of the Dockerfiles of several images are relatively simple. This part will not be introduced in detail. You can go directly to my project to learn about it.

Difficulties and Solutions of Dockerization Process

spring-boot dual configuration switch

In order to enable spring-boot to quickly switch between the development environment and the Docker environment, the configuration file of spring-boot needs to be modified

└─fame-server
		...                   
        │  └─resources
        │      │  application-dev.properties
        │      │  application-docker.properties
        │      │  application.properties

Add and configure files on the application.propertiesbasis of the original , and put the database logs and other information in the two files respectively, so as to realize the rapid switching between the development environment and the Docker environment.application-dev.propertiesapplication-docker.propertiesapplication.propertiesapplication-dev.propertiesapplication-docker.properties

# application.properties文件
#端口号
server.port=9090
#mybatis
mybatis.type-aliases-package=com.zbw.fame.Model
#mapper
mapper.mappers=com.zbw.fame.util.MyMapper
mapper.not-empty=false
mapper.identity=MYSQL

#mail
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true

#默认properties
spring.profiles.active=dev
# application-docker.properties文件
#datasource
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://fame-mysql:3306/fame?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root

#log
logging.level.root=INFO
logging.level.org.springframework.web=INFO
logging.file=log/fame.log

application-dev.propertiesThe content is application-docker.propertiessimilar to the file, but the mysql and log configurations are modified according to the situation of your own development environment.

baseUrlDynamically configure the address of axios

Plugins are used in fame-adminand to initiate and get server requests. When configuring the server url address , usually the url of the development environment, the Docker environment and the production environment may be different, and it is a bit troublesome to modify it every time. (Although only two configurations are required, code cleanliness does not allow me to hardcode this configuration).fame-frontaxiosfame-serveraxiosbaseUrl

  1. First modify fame-admin(Vue)it to be compatible with manual deployment mode and Docker mode

    fame-adminIt is based on Vue CLI 3construction. Compared with cli 2.0, some configuration files of webpack are officially encapsulated, so there are no config and build folders. However, the corresponding official website also gives some configuration parameters that are more convenient to set.

    It is mentioned in the official documentation :

    Only variables VUE_APP_starting with will be webpack.DefinePluginstatically embedded in the client-side package. You can access them in your app's code like this:

    console.log(process.env.VUE_APP_SECRET)
    

    During the build process, it process.env.VUE_APP_SECRETwill be replaced by the corresponding value. In VUE_APP_SECRET=secretthe case of , it is replaced with "sercet".

    baseUrlUse this feature to set environment variables to dynamically set the value of Docker mode and manual deployment mode

    fame-adminCreate a file under the directory server-config.js, write the following

    const isProd = process.env.NODE_ENV === 'production'
    const localhost = 'http://127.0.0.1:9090/'
    const baseUrl = process.env.VUE_APP_API_URL || localhost
    const api = isProd ? baseUrl : localhost
    export default {
      isProd,
      api
    }
    

    Then as long as VUE_APP_API_URLthere , and NODE_ENV === 'production'baseUrl is equal to VUE_APP_API_URLthe value, otherwise it is localhostthe value.

    Then axiosreference the file settings in the configuration file

    // fame-admin/src/plugins/http.js
    ...
    import serverConfig from '../../server-config'
    
    const Axios = axios.create({
      baseURL: serverConfig.api + 'api/',
     ...
    })
    
    ...
    

    Now just set the docker environment variable to a VUE_APP_API_URLvalue, just add a step to the corresponding Dockerfile.

    ENV VUE_APP_API_URL http://xx.xxx.xxx.xxx
    
  2. Then modify fame-front(Nuxt)it to be compatible with manual deployment mode and Docker mode

    Similarly, it is a similar idea for Nuxtbuilding a blog front desk modification.fame-front

    In the official documentation of Nuxt it is written:

    Nuxt.js lets you configure environment variables that are shared between client and server.

    For example ( nuxt.config.js):

    module.exports = {
      env: {
        baseUrl: process.env.BASE_URL || 'http://localhost:3000'
      }
    }
    

    The above configuration creates an baseUrlenvironment variable. If the application sets the BASE_URLenvironment variable, baseUrlthe value of is equal to BASE_URLthe value of , otherwise its value is http://localhost:3000.

    nuxt.config.jsSo we just need to add code to the file as the official documentation says

    module.exports = {
     env: {
       baseUrl: process.env.BASE_URL || 'http://localhost:3000'
     }
    }
    

    Then write the same code as the above part in the server-config.jsfile and axiosthe configuration file fame-front/plugins/http.jsand the corresponding Dockerfile file.fame-admin

Now baseUrlthe settings have been liberated from the hard coding of the code, but in fact we just transferred the coding of this parameter from the code to the Dockerfile file. If you want to modify it, you must go to these two files to find and then It is not convenient to modify it. This problem will be solved later to unify all environment configurations.

Nuxt cannot access the host ip in Docker

First of all, I want to explain why the front end of the blog should use Nuxt alone instead of using Vue like the back end of the blog, because the front end of the blog has SEO requirements, and Vue is very unfriendly to search engines.

So Nuxt pages are server-side rendered (SSR)

This creates a problem

fame-frontThe page must obtain the data in the server before rendering fame-server, but each docker container is independent of each other, and the internal access to each other can only be accessed through the container name. For example, if the container fame-frontwants to access the container fame-server, it is set baseURL = fame-server (fame-server is the container_name of the server's container).

After this setting, open the browser and enter the URL: http://xx.xxx.xxx.xx can successfully access the page, but if you click on a link, you will see the browser prompt that the error cannot be accessed to the address http://fame- server/ ...

vendor.e2feb665ef91f298be86.js:2 GET http://fame-server/api/article/1 net::ERR_CONNECTION_REFUSED

This is an inevitable result. In the container, http://fame-server/ is the address of the server, but of course your local browser does not know what the ghost address http://fame-server/ is, so the browser just An unreachable error is reported.

What? But didn't you just say that Nuxt is a server-rendered page, why did the local browser report this error again?

It turns out that when accessing directly through the browser link, Nuxt does render the page from the backend and then pass it over, but when clicking the link on the page, it jumps through Vue-Router, which is not under the control of Nuxt at this time. , but it is rendered in the browser like Vue. At this time, it is necessary to obtain data from the browser to the server for rendering, and the browser will report an error.

How to solve it

At the beginning of this problem, I always wanted to try to configure the network mode of the Docker container to solve it, but it did not solve it. axiosThe proxy function, which I didn’t notice until I read the document later axios, is essentially to solve the problem of cross-domain, because as long as axiosthe proxy is set, the address of the proxy will be used when rendering on the server side, and at the same time when the browser accesses I will use baseUrlthe address of , this feature perfectly solves my problem.

server-config.jsAdd the following code to the file (get the sum nuxt.config.jsof the environment variables in it )proxyHostproxyPort

...
const localProxy = {
  host: '127.0.0.1',
  port: 9090
}
const baseProxy = {
  host: process.env.proxyHost || localProxy.host,
  port: process.env.proxyPort || localProxy.port
}
exports.baseProxy = isProd ? baseProxy : localProxy
...

Then axiosadd the code to the configuration file

// fame-front/plugins/http.js
const Axios = axios.create({
  proxy: serverConfig.baseProxy
 ...
})
    
...

It can perfectly solve the problem.

Unified setting of environment parameters of Dockerfile

动态配置axios地址In the part solved above baseUrl, the settings are placed in the Dockerfile, and now the hard-coded in the Dockerfile is extracted and placed in a unified configuration file.

First docker-compose.ymlcreate an environment file in the file directory (ie project and directory) .envand write the content

BASE_URL=http://xx.xxx.xxx.xxx

PROXY_HOST=fame-nginx
PROXY_PORT=80

This is docker-composethe env_fileparameter, get the environment variable from the file, it can be a separate file path or list, if there is a .envfile in the same directory, it will be read by default, or you can docker-composeset the path yourself.

.envThe value of the environment variable is already set BASE_URL, and it can docker-compose.ymlbe used directly in it. Modified docker-compose.ymlpart fame-front:

fame-front:
 ...
 environment:
  BASE_URL: ${BASE_URL}
  PROXY_HOST: ${PROXY_HOST}
  PROXY_PORT: ${PROXY_PORT}
  ...

fame-frontIn this way , there are corresponding environment variables in the container BASE_URL, PROXY_HOSTand PROXY_PORTNuxt can also successfully obtain and set them.

For fame-admincontainers, however, it's a little more complicated. Let's take a look at fame-adminthe Dockerfile file of the container firstfame-admin-Dockerfile

# build stage
FROM node:10.10.0-alpine as build-stage

#中间一些操作省略...

RUN npm run build

# production stage
FROM nginx:1.15.3-alpine as production-stage

COPY ./fame-docker/fame-admin/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

A multi-stage build container is used here. If you set the environment variable directly, it docker-composewill only take effect in the next stage, but it npm run buildis executed in the first stage, so the environment variable cannot be applied to Vue. In order for the environment variable to be applied in the first stage, the variable must be docker-composepassed to the container at build time fame-admin-Dockerfile, and then the environment variable must be applied to the container in the first stage in the Dockerfile. Modified part docker-compose.ymlbelow fame-admin:

 fame-admin:
   ...
   build: 
    context: ./
    dockerfile: ./fame-docker/fame-admin/fame-admin-Dockerfile
    args:
      BASE_URL: ${BASE_URL} # 这里把环境变量当做ARG传给Dockerfile
   ...

Then fame-admin-Dockerfileadd steps in the first stage of

# build stage
FROM node:10.10.0-alpine as build-stage

ARG BASE_URL # 必须申明这个ARG才能从docker-compose里获取

ENV VUE_APP_API_URL $BASE_URL

# 以下省略...

In this way, the environment variables can be passed into the image of phase one when the image of phase one is built, so that the variables in Vue can take effect.

Summarize

Many complex projects on the Internet now use docker-compose to deploy, but also rely on shell scripts to operate, such as copying files to set up the environment, etc. I think this will reduce the meaning of docker-compose. If shell scripts are used, it is better to use shell instead of docker-compose to build and start images.

Therefore, although we encountered some bumps in the process of Dockerization, we insisted that only docker-compose deployment was used, and it would be very convenient to go online and offline in the future. I also hope that my Dockerization ideas can give some reference to other projects.

Compared with the previous terrifying steps, the online and offline of Fame blog now only requires two lines of commands, which is really convenient.

docker-compose up
docker-compose down
{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324107318&siteId=291194637