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 build
my 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 Nuxt
framework for SEO. If it is the first deployment or server migration, it will be troublesome. If you deploy once, the following steps are required.
- Install mysql, modify the relevant configuration files, set the encoding time zone, etc., then restart
- Download and install java, configure the java environment
- Download and install maven, configure the maven environment
- Download and install nginx, modify configuration files, design reverse proxy, etc.
- Start the spring-boot project
- Packaging vue projects,
npm install
,npm run build
etc. - start nuxt project,
npm install
,npm run start
etc
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.
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-ce
and 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-docker
folder.
docker-compose.yml
Put 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
modified docker-compose.yaml
file
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.yml
The structure is generally similar to the directory structure just now, and it is also divided into the following parts
- fame-nginx
- fame-mysql
- fame-server
- fame-admin
- fame-front
There docker-compose.yml
are a few points in this
fame-mysql
The sumfame-server
shouldrestart
be set toalways
, because there is currently no solution for Docker-compose to solve the problem of container startup. Even if it is setdepends_on
, it only controls the time when the container starts to start, and cannot control the time when the container starts to complete, so letfame-mysql
andfame-server
these two containers are setrestart
to prevent spring-boot from starting before mysql is started and failing to start with an error.fame-server
,fame-mysql
,fame-nginx
these three containers are all setvolumes
, 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-mysql
The mysql storage file of the container is also set to bevolumes
mounted 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.properties
basis 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.properties
application-docker.properties
application.properties
application-dev.properties
application-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.properties
The content is application-docker.properties
similar to the file, but the mysql and log configurations are modified according to the situation of your own development environment.
baseUrl
Dynamically configure the address of axios
Plugins are used in fame-admin
and 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-front
axios
fame-server
axios
baseUrl
-
First modify
fame-admin(Vue)
it to be compatible with manual deployment mode and Docker modefame-admin
It is based onVue CLI 3
construction. 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 bewebpack.DefinePlugin
statically 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_SECRET
will be replaced by the corresponding value. InVUE_APP_SECRET=secret
the case of , it is replaced with"sercet"
.baseUrl
Use this feature to set environment variables to dynamically set the value of Docker mode and manual deployment modefame-admin
Create a file under the directoryserver-config.js
, write the followingconst 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_URL
there , andNODE_ENV === 'production'
baseUrl is equal toVUE_APP_API_URL
the value, otherwise it islocalhost
the value.Then
axios
reference 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_URL
value, just add a step to the corresponding Dockerfile.ENV VUE_APP_API_URL http://xx.xxx.xxx.xxx
-
Then modify
fame-front(Nuxt)
it to be compatible with manual deployment mode and Docker modeSimilarly, it is a similar idea for
Nuxt
building 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
baseUrl
environment variable. If the application sets theBASE_URL
environment variable,baseUrl
the value of is equal toBASE_URL
the value of , otherwise its value ishttp://localhost:3000
.nuxt.config.js
So we just need to add code to the file as the official documentation saysmodule.exports = { env: { baseUrl: process.env.BASE_URL || 'http://localhost:3000' } }
Then write the same code as the above part in the
server-config.js
file andaxios
the configuration filefame-front/plugins/http.js
and the corresponding Dockerfile file.fame-admin
Now baseUrl
the 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-front
The 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-front
wants 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. axios
The 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 axios
the 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 baseUrl
the address of , this feature perfectly solves my problem.
server-config.js
Add the following code to the file (get the sum nuxt.config.js
of the environment variables in it )proxyHost
proxyPort
...
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 axios
add 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.yml
create an environment file in the file directory (ie project and directory) .env
and write the content
BASE_URL=http://xx.xxx.xxx.xxx
PROXY_HOST=fame-nginx
PROXY_PORT=80
This is docker-compose
the env_file
parameter, get the environment variable from the file, it can be a separate file path or list, if there is a .env
file in the same directory, it will be read by default, or you can docker-compose
set the path yourself.
.env
The value of the environment variable is already set BASE_URL
, and it can docker-compose.yml
be used directly in it. Modified docker-compose.yml
part fame-front
:
fame-front:
...
environment:
BASE_URL: ${BASE_URL}
PROXY_HOST: ${PROXY_HOST}
PROXY_PORT: ${PROXY_PORT}
...
fame-front
In this way , there are corresponding environment variables in the container BASE_URL
, PROXY_HOST
and PROXY_PORT
Nuxt can also successfully obtain and set them.
For fame-admin
containers, however, it's a little more complicated. Let's take a look at fame-admin
the 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-compose
will only take effect in the next stage, but it npm run build
is 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-compose
passed 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.yml
below fame-admin
:
fame-admin:
...
build:
context: ./
dockerfile: ./fame-docker/fame-admin/fame-admin-Dockerfile
args:
BASE_URL: ${BASE_URL} # 这里把环境变量当做ARG传给Dockerfile
...
Then fame-admin-Dockerfile
add 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