At the beginning of this year, I completed the implementation of my own Fame blog system. At that time, I also did a blog post spring-boot +Vue = Fame writing a summary as a record and introduction. From the completion of the implementation to now, also intermittently according to the actual use of the update.

It was a bit of a hassle every time I went live because my server memory was so small that I had to shut down my back end fame-server process during the NPM build, even if I only updated the fame-front part of the code.

And this project is before and after the end of the separation, blog front page also used Nuxt framework for SEO, if it is the first deployment or server migration, trouble to die ah, deployment once if the following steps

  1. Install mysql, modify the related configuration files, set the encoding time zone, and then restart
  2. Download and install Java and configure the Java environment
  3. Download and install Maven and configure the Maven environment
  4. Download and install Nginx, modify configuration files, design reverse proxies, etc
  5. Start the spring-boot project
  6. Package the VUE project,npm install.npm run buildEtc.
  7. Launch the NUXT project,npm install.npm run startEtc.

If you are lucky enough to complete these seven steps successfully, you may have to go back and find the wrong step and redeploy if one of the steps fails.

In front of these needs, Docker is the big killer to solve these problems. Its virtualization technology isolates each container so that its resources do not affect each other, its consistent operating environment, and docker-compose’s one-click deployment perfectly solve the above problems.

Project address: Fame

Docker and Docker-compose installation

Docker and docker-compose feature and use can be viewed online in a Chinese document Docker- from getting started to practice

The following is a shell script for Centos7 to install and configure Docker and Docker-compose. Docker version is Docker-CE, docker-compose version is 1.22.0

#! /bin/sh

#Update # # # # #
yum -y update

### Install docker
#Install the necessary system tools
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
#Add software source information
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
#Update the YUM cache
sudo yum makecache fast
#Install the Docker - ce
sudo yum -y install docker-ce
#Start Docker and set it to boot (centos7)
systemctl  start docker.service
systemctl  enable docker.service
#Replace docker with a domestic source
echo '{"registry-mirrors": ["https://registry.docker-cn.com"],"live-restore": true}' > /etc/docker/daemon.json
systemctl restart docker
#Install dokcer - composeSudo 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#Install command completion tool
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
### Install docker end ###
Copy the code

Docker reformed

Directory structure after transformation

Take a look at the structure of the revamped project

├─Fame │ ├. Env // ├─Fame │ ├─Fame │ ├─ ├─Fame │ ├─ for docker-compose Fame - front - Dockerfile / / fame - front Dockerfile file │ │ │ fame - server - Dockerfile / / fame - server Dockerfile file │ │ │ │ │ ├ ─ fame - admin │ │ │ fame - admin - Dockerfile / / fame - admin Dockerfile file │ │ │ nginx. Conf / / fame - admin nginx server configuration file │ │ │ │ │ ├─ ├─fame-mysql │ ├─fame-mysql │ ├─fame-mysql -Dockerfile └ ─ fame - nginx │ │ nginx Dockerfile / / nginx server Dockerfile across the entire project file │ │ nginx. Conf / / the whole project nginx configuration file │ │ │ ├ ─ fame - admin / / ├─ ├─ crime-server, Vue+elementui │ ├─ crime-server, Nuxt │ ├─ crime-server, spring-bootCopy the code

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

Docker-compose. Yml: docker-compose up -d: docker-compose up -d: 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
Copy the code

Start the project, don’t repeat the tedious steps!

Docker project structure after transformation

After transforming thedocker-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: The '%'
     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"
Copy the code

The structure of docker-comemage. yml is similar to that of docker-comemage. yml

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

There are a few points in docker-comemage.yml

  • fame-mysqlandfame-servertherestartTo set up foralways, because currently docker-compose does not have a solution that can solve the problem of the sequence of container startup. Even if it’s set updepends_onThat only controls when the container starts, not when it finishes, so letfame-mysqlandfame-serverThese two container SettingsrestartTo prevent spring-boot from being started before mysql is started
  • fame-server.fame-mysql.fame-nginxAll three containers are setvolumes, mount the logs log file in the container to the project directory of the host machine, convenient to see the log file at any time
  • fame-mysqlThe container’s mysql storage file is also setvolumesMount in the project directory (./mysql/mysql_data:/var/lib/mysql), this suggestion can be set according to the actual situation of the host machine in another directory, otherwise accidentally delete the project so that the database data in the container are also gone

Most of the dockerfiles of several mirrors are relatively simple, this part will not be introduced in detail, you can directly go to my project to understand.

Difficulties and solutions of docker-ization process

Switch between spring-Boot dual-configuration

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... ├ ─ garbage ─resources │ ├ ─ class-de.properties │ ├ ─ class-de.properties │ ├ ─ class-de.properties │ ├ ─ class-de.propertiesCopy the code

Add application-dev.properties and application-docker.properties configuration files to the application. Properties database logs in application-dev.properties and application-docker.properties files respectively. Fast switch between development environment and Docker environment.

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. The mail. The properties. The mail. The SMTP. Starttls. Required = true # default properties spring. Profiles. The active = devCopy the code
# # application - docker. The properties file datasource spring. The datasource. The 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.logCopy the code

The contents of application-dev.properties are similar to the application-docker.properties file, except that mysql and log configurations are modified to suit your development environment.

Dynamically configure axiOSbaseUrladdress

The AXIos plug-in is used in fame-Admin and fame-Front to initiate and get requests from fame-Server servers. In Axios, you need to configure the server URL baseUrl, so usually the development environment and Docker environment as well as the production environment url may be different, and it is a bit cumbersome to change each time. (I only need to configure two, but code cleanliness doesn’t allow me to hard-code this configuration).

  1. Fame-admin (Vue) was first modified to be compatible with manual deployment mode and Docker mode

    Fame-admin is based on Vue CLI 3, which is used to package webpack configuration files, so there is no config and build folder. However, the corresponding official website also provides some more convenient configuration parameters.

    In the official document:

    Only variables starting with VUE_APP_ are statically embedded in client-side packages by webpack.definePlugin. You can access them in your application code like this:

    console.log(process.env.VUE_APP_SECRET)
    Copy the code

    During the build process, process.env.vue_app_secret will be replaced with the corresponding value. In the case of VUE_APP_SECRET=secret, it is replaced with “sercet”.

    Use this feature to set environment variables to dynamically set the baseUrl values for Docker mode and manual deployment mode

    Create the file server-config.js under fame-admin and 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
    }
    Copy the code

    So as long as there is VUE_APP_API_URL in the environment variable and NODE_ENV === ‘production’, baseUrl is equal to VUE_APP_API_URL, otherwise localhost.

    This file setting is then referenced in the AXIOS configuration file

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

    Now all you need to do is set the docker environment variable to VUE_APP_API_URL and add a step to the corresponding Dockerfile.

    ENV VUE_APP_API_URL http://xx.xxx.xxx.xxx
    Copy the code
  2. Fame-front (Nuxt) was modified to be compatible with manual deployment mode and Docker mode

    The same idea applies to creating fame-Front with Nuxt.

    The official Nuxt documentation reads:

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

    For example (nuxt. Config. Js) :

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

    The above configuration creates a baseUrl environment variable. If the application sets the BASE_URL environment variable, the value of baseUrl is equal to the value of BASE_URL, otherwise it is http://localhost:3000.

    So we just add the code to the nuxt.config.js file as the official documentation says

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

    Then write the same code as above in the fame-admin section in the server-config.js file and axios configuration file fame-front/plugins/http.js and the corresponding Dockerfile

And now we havebaseUrlThis frees the code from hard coding, but in fact we just move the parameter encoding from the code to the Dockerfile. If we want to change it, we have to look it up in these two files and change it, which is not convenient. This will be addressed later by unifying all environment configurations.

Nuxt cannot access host IP in Docker

First to explain a point, why the blog front end to use Nuxt alone rather than the same as the blog background with Vue, because the blog front end has SEO needs, like Vue is very unfriendly to search engines.

So Nuxt’s pages are server-side rendered (SSR)

And that creates a problem

Fame-front pages must retrieve data from fame-Server before rendering, but each Docker container is independent of each other and can only be accessed internally by the container name. For example, if fame-front wants to access fame-server, set baseURL = fame-server(fame-server is container_name of the server container).

After this setting, open the browser and enter the url: xx.xxx.xx. xx can be successfully accessed to the page, but casually click a link, you will see the browser prompt error can not access the address http://fame-server/…

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

As a result, http://fame-server/ is the server address in the container, but of course your local browser does not know what http://fame-server/ is, so the browser will report an inaccessible error.

What? Nuxt is a web page rendered by the server.

Nuxt does render the page from the back end when accessed directly through the browser link. However, when a link is clicked on the page, it is routed through vue-Router, which is not under Nuxt’s control. It is rendered in the browser like Vue. In this case, the browser needs to fetch data from the server to render, and the browser will report an error.

How to solve it

At the beginning, I tried to configure the network mode of Docker container to solve this problem, but it was not solved. It wasn’t until later when I looked at the Axios documentation that I noticed that axios’ proxy functionality was cross-domain in nature, because as long as the proxy is set up in Axios, the server will use the proxy’s address for rendering and the browser will use the baseUrl address for accessing. This feature solved my problem perfectly.

Add the following code to the server-config.js file (get proxyHost and proxyPort from the environment variables in nuxt.config.js)

. 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
...
Copy the code

Then add the code to the AXIOS configuration file

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

You can solve the problem perfectly.

Unified setting of Dockerfile environment parameters

In the section above dealing with dynamically configuring the AXIos address, we put the baseUrl Settings in the Dockerfile. Now we extract the hard code from the Dockerfile and put it into a unified configuration file.

Env = docker-comemage. yml = docker-comemage. yml = docker-comemage. yml = docker-comemage. yml = docker-comemage. yml = docker-comemage. yml

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

PROXY_HOST=fame-nginx
PROXY_PORT=80
Copy the code

The env_file argument for docker-compose will fetch the environment variables from the docker-compose file, either as a separate file path or a list. If there is a. Env file in the same directory, the path will be read by default, or you can set the path to the docker-compose file yourself.

Env has set the value of the environment variable BASE_URL to be used directly in docker-comemess.yml. Modify docker-comemage. yml fame-front section:

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

Copy the code

In this case, there are the corresponding BASE_URL,PROXY_HOST, and PROXY_PORT environment variables in the fame-front container, and Nuxt can be successfully fetched and set.

However, the fame-admin container is a little more complicated. Let’s take a look at the fame-admin-dockerfile of the fame-admin container

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

# some intermediate operations omitted...

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;"]
Copy the code

The multistage build container is used here. Setting environment variables directly via docker-compose will only take effect in the next phase, but the NPM run build is executed in the first phase, so environment variables cannot be applied to Vue. In order for the environment variable to be applied in the first phase, it is necessary to transfer the variable from docker-compose to fame-admin-dockerfile at build time, and then apply the environment variable to the container in the first phase of the Dockerfile. Docker-comemage. yml: docker-comemage. yml: docker-comemage. yml: docker-comemage. yml

 fame-admin:
   .
   build: 
    context: . /
    dockerfile: ./fame-docker/fame-admin/fame-admin-Dockerfile
    args:
      BASE_URL: ${BASE_URL} Pass the environment variable as ARG to Dockerfile
   .
Copy the code

Then add steps to the first stage of fame-admin-Dockerfile

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

ARG BASE_URL Docker-compose must declare the ARG for docker-compose

ENV VUE_APP_API_URL $BASE_URL

# below omit...

Copy the code

This allows the environment variables to be passed into the phase 1 image during the build phase 1 image, making the variables in Vue work.

conclusion

At present, many complicated projects on the Internet even use docker-compose deployment, but also rely on shell scripts to operate, such as copying files to set up the environment. I think this will reduce the significance of Docker-compose. If you’re using shell scripts, you might want to skip docker-compose and start the image with a shell instead.

Therefore, in the process of docker-oriented, although we encountered some difficulties, we persisted in implementing only docker-compose deployment, which made it extremely convenient to go online and offline in the future. I also hope that my dockerization ideas can be used for reference for other projects.

The Fame blog is now up and down in just two lines of command, compared to the dreaded steps of the past.

docker-compose up
docker-compose down
Copy the code

Source address :doodle

Use Docker to deploy the spring-boot +Vue blogging system