0
点赞
收藏
分享

微信扫一扫

使用Docker部署SpringBoot+Vue博客系统


从完成实现到现在,也断断续续的根据实际的使用情况进行更新。

只不过每次上线部署的时候都觉得有些麻烦,因为我的服务器内存太小,每次即使只更新了前台部分(fame-front)的代码,在执行​​npm build​​的时候都还必须把我的后端服务(fame-server)的进程关掉,不然会造成服务器卡死(惨啊)。

而且这个项目是前后端分离的,博客前台页面还为了SEO用了​​Nuxt​​框架,假如是第一次部署或者要服务器迁移的话,麻烦的要死啊,部署一次的话要以下步骤

  1. 安装mysql,修改相关配置文件,设置编码时区等,然后重启
  2. 下载安装java,配置java环境
  3. 下载安装maven,配置maven环境
  4. 下载安装nginx,修改配置文件,设计反向代理等
  5. 启动spring-boot项目
  6. 打包vue项目,​​npm install​​​,​​npm run build​​等
  7. 启动nuxt项目,​​npm install​​​,​​npm run start​​等

如果能够顺利的完成这七个步骤算是幸运儿了,假如中间哪个步骤报错出了问题,可能还要回头查找哪个步骤出了问题,然后又重新部署。




使用Docker部署SpringBoot+Vue博客系统_java



在这些需求面前,Docker就是解决这些问题的大杀器。无论是其虚拟化技术隔离各个容器使其资源互不影响,还是一致的运行环境,以及docker-compose的一键部署,都完美的解决了上述问题。

项目地址: ​​Fame​​

Docker和Docker-compose安装

Docker和Docker-compose的功能和使用可以看线上的一个中文文档 ​​Docker — 从入门到实践​​

下面是Centos7安装和配置Docker以及Docker-compose的shell脚本,其他操作系统可以参考修改来安装。其中Docker版本为​​docker-ce​​​,Docker-compose版本为​​1.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结束 ###


Docker化改造

改造后目录结构

先看一下改造后的项目的结构


├─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


为了不破坏原有项目的结构,无论前端还是后端的docker的相关配置文件全部提取出来,单独放在了​​fame-docker​​文件夹中。

​docker-compose.yml​​​放在项目根目录下,直接在根目录运行命令:​​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


就启动项目了,再也不用重复繁琐的步骤!

改造后的docker项目结构




使用Docker部署SpringBoot+Vue博客系统_java_02



改造后的​​docker-compose.yaml​​文件


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​​的结构和刚才目录结构大体类似,也是分以下几个部分

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

这个​​docker-compose.yml​​中有几个要点

  • ​fame-mysql​​​和​​fame-server​​​的​​restart​​​要设置为​​always​​​,因为目前Docker-compose是没有一个方案可以解决容器启动的先后的问题的。即使设置了​​depends_on​​​,那也只是控制容器开始启动的时间,不能控制容器启动完成的时间,所以让​​fame-mysql​​​和​​fame-server​​​这两个容器设置​​restart​​,防止spring-boot在mysql启动完成之前启动而报错启动失败
  • ​fame-server​​​,​​fame-mysql​​​,​​fame-nginx​​​这三个容器都设置了​​volumes​​,把容器里的logs日志文件挂载到宿主机的项目目录里,方便随时看日志文件
  • ​fame-mysql​​​容器的mysql存储文件也设置了​​volumes​​​挂载在项目目录里(​​./mysql/mysql_data:/var/lib/mysql​​),这个建议大家可以根据实际的情况设置到宿主机的其他目录里,不然不小心删除项目的话那么容器里的数据库数据也都没了

几个镜像的Dockerfile大部分都比较简单,这部分就不全部详细介绍了,可以直接去我项目中了解。

Docker化过程的困难和解决方法

spring-boot双配置切换

为了能够让spring-boot能够在开发环境和Docker环境下快速切换,需要将spring-boot的配置文件进行修改


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


在原有的​​application.properties​​​基础上增加​​application-dev.properties​​​和​​application-docker.properties​​​配置文件,把​​application.properties​​​里的数据库日志等信息分别放到​​application-dev.properties​​​和​​application-docker.properties​​这两个文件中,实现开发环境和Docker环境的快速切换。


# 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​​​的内容和​​application-docker.properties​​文件类似,只是根据自己开发环境的情况修改mysql和log配置。

动态配置axios的​​baseUrl​​地址

在​​fame-admin​​​和​​fame-front​​​中用了​​axios​​​插件,用于发起和获取​​fame-server​​​服务器的请求。在​​axios​​​要配置服务器url地址​​baseUrl​​,那么通常开发环境和Docker环境以及生产环境的url可能都不一样,每次都去修改有点麻烦。(虽然只需要配置两处,但是代码洁癖不允许我硬编码这个配置)。

  1. 先修改​​fame-admin(Vue)​​​使其兼容手动部署模式和Docker模式
    ​​​fame-admin​​​是基于​​Vue CLI 3​​​搭建的,相对于cli 2.0官方把webpack的一些配置文件都封装起来了,所以没有config和build文件夹。不过对应的官网也给了一些设置更加方便的配置参数。
    在​​​官方文档​​​中提到:
    只有以​​​VUE_APP_​​​ 开头的变量会被​​webpack.DefinePlugin​​​ 静态嵌入到客户端侧的包中。你可以在应用的代码中这样访问它们:
    console.log(process.env.VUE_APP_SECRET)
    在构建过程中,​​​process.env.VUE_APP_SECRET​​​ 将会被相应的值所取代。在​​VUE_APP_SECRET=secret​​​ 的情况下,它会被替换为​​"sercet"​​​。
    利用这个特性来设置环境变量来动态的设置Docker模式和手动部署模式的​​​baseUrl​​​的值
    在​​​fame-admin​​​目录下创建文件​​server-config.js​​​,编写以下内容
    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 }
    那么只要在环境变量中有​​​VUE_APP_API_URL​​​的值,且​​NODE_ENV === 'production'​​​,baseUrl就等于​​VUE_APP_API_URL​​​的值,否则就是​​localhost​​​的值。
    接着在​​​axios​​​配置文件中引用该文件设置
    // fame-admin/src/plugins/http.js... import serverConfig from '../../server-config' const Axios = axios.create({ baseURL: serverConfig.api + 'api/', ... }) ...
    现在只要将docker的环境变量设置一个​​​VUE_APP_API_URL​​​的值就行了,只要在对应的Dockerfile中增加一个步骤就可以了。
    ENV VUE_APP_API_URL http://​​http://xx.xxx.xxx.xxx​​
  2. 再修改​​fame-front(Nuxt)​​​使其兼容手动部署模式和Docker模式
    同样的,对于用​​​Nuxt​​​搭建​​fame-front​​​博客前台修改也是类似的思路。
    在Nuxt的​​​官方文档​​​中写到:
    Nuxt.js 让你可以配置在客户端和服务端共享的环境变量。
    例如 (​​​nuxt.config.js​​​):
    module.exports = { env: { baseUrl: process.env.BASE_URL || 'http://localhost:3000' } }
    以上配置我们创建了一个​​​baseUrl​​​ 环境变量,如果应用设定了​​BASE_URL​​​ 环境变量,那么​​baseUrl​​​的值等于​​BASE_URL​​​ 的值,否则其值为​​http://localhost:3000​​​。
    所以我们只要和官方文档说的一样,在​​​nuxt.config.js​​​文件中增加代码就可以了
    module.exports = { env: { baseUrl: process.env.BASE_URL || 'http://localhost:3000' } }
    接着在​​​server-config.js​​​文件和​​axios​​​的配置文件​​fame-front/plugins/http.js​​​以及对应的Dockerfile文件中编写和上面​​fame-admin​​部分一样的代码就可以了

现在已经把baseUrl的设置从代码的硬编码中解放出来了,但事实上我们只是把这个参数的编码从代码从转移到Dockerfile文件里了,要是想要修改的话也要去这两个文件里查找然后修改,这样也不方便。后面会解决这个问题把所有环境配置统一起来。

Nuxt在Docker中无法访问到宿主机ip问题

先要说明一点,为什么博客前端要单独去使用的Nuxt而不是和博客后台一样用Vue呢,因为博客前端有SEO的需求的,像Vue这样的对搜索引擎很不友好。

所以Nuxt的页面是服务器端渲染(SSR)的

这样就产生了问题

​fame-front​​​的页面在渲染之前必须获取到​​fame-server​​​服务器中的数据,但是每个docker容器都是互相独立的,其内部想要互相访问只能通过容器名访问。例如容器​​fame-front​​​想要访问容器​​fame-server​​​,就设置​​baseURL = fame-server​​ (fame-server是服务器的容器的container_name)。

这样设置之后打开浏览器输入网址:​​http://xx.xxx.xxx.xx可以成功...​​​,但是随便点击一个链接,就会看到浏览器提示错误无法访问到地址​​http://fame-server/...​​


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


这是必然的结果,在容器里​​http://fame-server/就是服务器...​​​,但是你本地的浏览器当然是不知道​​http://fame-server/是个什么鬼...​​,所以就浏览器就报出无法访问的错误。

什么?可是刚才不是说Nuxt是服务器渲染的页面吗,怎么又让本地浏览器报这个错误了。

原来是因为当通过浏览器链接直接访问的时候,Nuxt的确是从后端渲染了页面再传过来,但是在页面中点击链接的时候是通过Vue-Router跳转的,这时候不在Nuxt的控制范围,而是和Vue一样在浏览器渲染的,这时候就要从浏览器里向服务端获取数据来渲染,浏览器就会报错。

如何解决呢

这个问题开始的时候一直想要尝试配置Docker容器的网络模式来解决,可是都没有解决。直到后面我看​​axios​​​文档的时候才注意到​​axios​​​的代理功能,其本质是解决跨域的问题的,因为只要在​​axios​​​设置了代理,在服务端渲染的时候就会使用代理的地址,同时在浏览器访问的时候会用​​baseUrl​​ 的地址,这个特点完美解决我的问题啊。

在​​server-config.js​​​文件里增加以下代码(在​​nuxt.config.js​​​里获取环境变量里的​​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
...


然后在​​axios​​配置文件里增加代码


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

...


就可以完美的解决问题了。

Dockerfile的环境参数统一设置

在上文解决​​动态配置axios地址​​​的部分把​​baseUrl​​的设置放在了Dockerfile中,现在就再把Dockerfile中的硬编码提取出来,放到统一的配置文件中。

首先在​​docker-compose.yml​​​文件目录下(即项目跟目录)创建环境文件​​.env​​并编写一下内容


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

PROXY_HOST=fame-nginx
PROXY_PORT=80


这个是​​docker-compose​​​的​​env_file​​​参数,从文件中获取环境变量,可以为单独的文件路径或列表,如果同目录下有​​.env​​​文件则会默认读取,也可以自己在​​docker-compose​​里设置路径。

已经在​​.env​​​设置了环境变量​​BASE_URL​​​的值,就能在​​docker-compose.yml​​​里直接使用了。修改​​docker-compose.yml​​​的​​fame-front​​部分:


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


这样在​​fame-front​​​的容器里就有对应的​​BASE_URL​​​,​​PROXY_HOST​​​,​​PROXY_PORT​​环境变量,Nuxt也能够成功获取并设置。

不过对于​​fame-admin​​​容器来说就要稍微复杂一点点了。先来看一下​​fame-admin​​​容器的Dockerfile文件​​fame-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;"]


这里用了多阶段构建容器,如果直接通过​​docker-compose​​​设置环境变量只会在后面一个阶段生效,但是​​npm run build​​​是在第一个阶段执行的,所以环境变量不能应用到Vue当中。为了让环境变量在第一阶段就应用,必须要在构建的时候就把变量从​​docker-compose​​​传到​​fame-admin-Dockerfile​​​中,然后在Dockerfile中的第一阶段把这个环境变量应用到容器里。下面修改​​docker-compose.yml​​​的​​fame-admin​​部分:


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


然后在​​fame-admin-Dockerfile​​的第一阶段增加步骤


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

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

ENV VUE_APP_API_URL $BASE_URL

# 以下省略...


这样就可以在构建阶段一镜像的时候就把环境变量传入到阶段一的镜像里,让Vue里的变量生效了。

总结

现在网上很多复杂一点的项目即使用了docker-compose部署,也多少依赖shell脚本来操作,比如复制文件设置环境等,我觉得这样会降低docker-compose的意义。如果都使用了shell脚本,那不如直接不用docker-compose而全用shell来构建和启动镜像。

所以在Docker化的过程中虽然遇到一些坎坷,但坚持实现了只用docker-compose部署,以后上线和下线就及其方便了。也希望我的Docker化思路可以给其他项目做一些参考。

对比以前恐怖的步骤,现在Fame博客的上线和下线只需要两行命令,真的十分的便捷。


docker-compose up
docker-compose down


源码地址:​​doodle​​

原作者:zzzbw



使用Docker部署SpringBoot+Vue博客系统_mysql_03


举报

相关推荐

0 条评论