为了给小学期实验做准备,这几天提前准备试验了一下使用 docker 容器部署SpringBoot+Vue前后端分离的项目。中间踩坑无数,特此记录一下,仅供学习和参考。
首先需要配置一下需要的 docker 环境,总体来讲是需要 docker engine 和 docker registry 。搭建环境网上有很多教程,这里不再赘述。
之后,需要在前后端分别添加 Dockerfile,而前端除了需要 Dockerfile 以外,还需要 Nginx 的配置文件——姑且命名为 default.conf 。
一、准备配置文件
下面列出了具体的文件。
(1)后端 Dockerfile
FROM openjdk:8
ADD /target/blog-1.0-SNAPSHOT.jar app.jar
EXPOSE 11451
CMD java -jar app.jar
执行这个 Dockerfile 的前提是需要在本地先 package 完毕,/target 目录下有对应 jar 包。
(2)前端 Dockerfile
# build stage
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# production stage
FROM nginx:stable-alpine as production-stage
RUN rm /etc/nginx/conf.d/default.conf
COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY --from=build-stage /app/default.conf /etc/nginx/nginx.conf
EXPOSE 13141
CMD ["nginx", "-g", "daemon off;"]
(3)前端 Nginx 代理配置文件
worker_processes 1
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 13141;
server_name 10.134.136.71;
root /usr/share/nginx/html;
location / {
# root html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /dev-api {
# rewrite ^/b/(.*)$ /$1 break;
proxy_pass http://10.134.136.71:11451/api/v1;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
当前端的 Dockerfile 执行时,会构建一个 Vue 项目的镜像。
构建时,会先运行 npm build 指令,生成目标 dist 目录;紧接着,会清除镜像中 Nginx 原有的配置文件,并将我们本地的“/dist”目录和“default.conf”两项分别拷贝至我们刚刚创建的镜像中,同时暴露13141端口;最后,执行重启镜像 Nginx 的指令。
这便是整个构建 Docker 镜像的过程。
二、运行
Docker 相关的指令
# docker 构建指令
docker build -f {Dockerfile 名称} -t {镜像名称}
# 查看当前仓库中的镜像
docker images
# 运行镜像
docker run -d --name={镜像名称} -p 13141:13141 {容器名称}
# 停止运行镜像
docker stop {镜像名称/ID}
# 删除镜像
docker rmi {镜像名称/ID}
# 删除容器
docker rm {容器名称/ID}
# 查看运行中的容器
docker ps
# 查看所有容器(包括停止运行的)
docker ps -a
# 进入容器内部
docker exec -it {容器名称/容器ID} /bin/bash
docker exec -it {容器名称/容器ID} /bin/sh
# 查看 docker 容器的日志
docker logs --since 30m {容器ID}
三、问题汇总
(1)Nginx 前后端应该完全对应,否则会出现 Nginx 400/50x 等相关错误。
首先,端口要对应。后端的 yml 文件为
因此后端 Dockerfile 开放的端口保证为 11451。前端的 default.conf 文件中代理地址则也应该保持一致。
至于为什么是“/dev-api”,这个是前端统一配置的 api 格式。通俗来讲,Nginx 会把带有“/dev-api”的请求代理到对应的接口上。如下图所示:
最后,前端的 Dockerfile 也相应地开放一个端口 13141, 用于留给 Nginx 监听,当 Nginx 监听到有请求访问这个端口时,就会进入到既定的 root 目录下的 index 界面中,并触发代理,转发到对应的后端接口地址。因此,Nginx 对应的配置如下图所示。
(2)build 后的前端制品在 Docker 镜像中的挂载目录引发的一系列问题
将前端的制品构建镜像时,我们要指定挂载目录。在前端 Dockerfile 中通过 COPY 指令操作了这一过程,如下图。
下表是一般情况下,宿主机和镜像目录的挂载对应关系。
宿主机 | 容器 | 用途 |
---|---|---|
/app/dist | /usr/share/nginx/html | 资源路径 |
/app/default.conf | /etc/nginx/nginx.conf | nginx的默认配置文件就在此处 |
(3)Docker 运行指令中的端口映射
构建时的指令:
docker run -d --name={镜像名称} -p 13141:13141 {容器名称}
“-p”参数规定了宿主机和容器之间的映射关系。一般来讲,“-p port1:port2”中,port1 是宿主机的端口,port2 是容器的端口。具体可以通过“docker ps”指令进行查看。注意,在宿主机的某个指定端口上只可以绑定一个容器。
可以通过以下指令查看端口映射的信息。
# 查看容器所有映射端口
docker port {容器名/容器ID}
# 查看容器内某个端口映射到哪里
docker port {容器名/容器ID} {容器端口号}
(4)Docker 启动后闪退
如果出现此类问题,建议查看 docker 日志,来具体分析。查看日志的指令如下:
# 查看 docker 容器的日志
docker logs --since 30m {容器ID}
(5)Dockerfile 中启动 Nginx 的指令
我们可以看到,在 Vue 项目的 Dockerfile 中,最后一行是:
# 指令1
CMD ["nginx", "-g", "daemon off;"]
为什么不是通用的启动指令(如下):
# 指令2
CMD service nginx start
这是因为容器只对主进程服务,当主进程退出的时候,容器也会退出。而 CMD 在使用 shell 格式的话,真实的命令会被包装为 sh -c 参数的形式执行。
结合以上两点,可以看出,启动 Nginx 的时候,主进程其实是 sh,而容器是因为 sh 这个主进程产生的,当该条命令执行完毕之后,sh 作为主进程会退出,容器自然也会退出,因此导致 Nginx 启动不了。所以需要使用指令1进行包装启动。
(6)运行Vue Docker 镜像报错:“worker_processes;指令不允许使用nginx”
造这个错误的是容器找不到“default.conf”中的指令导致的。但是为什么会找不到呢?很有可能是“default.conf”这个文件被 COPY 到错误的目录下了。
请注意:worker_processes以及 Nginx 配置文件中的所有指令,仅在配置的顶层(/etc/nginx/nginx.conf)有效,因此,请检查一下,是否将“default.conf”文件 COPY 到了“/etc/nginx/conf.d/default.conf”目录下。