当前位置:   article > 正文

【Docker】前后端分离项目 Vue+Gin 容器化部署 | docker-compose 部署 | 部署 nginx 通过域名访问 | 权限管理系统_vue3 nginx docker-compose配置

vue3 nginx docker-compose配置

前言

项目地址:https://gitee.com/Cauchy_AQ/rbac

项目前端使用 vue3 并且由 vite 构建,后端采用 gin 框架,搭建了一个简易的权限管理系统(rbac)。本节对该项目进行容器化部署。

前后端不完全独立

代码地址:https://gitee.com/Cauchy_AQ/rbac/tree/master/

前后端部署不完全独立,指通过 vue 的 build 构建后,返回一个静态页面 index.html,然后将构建后的文件放置后端项目中,并由后端返回该页面。

docker 部署

由于项目需要使用 mysql 和 redis 两个数据库,所以除了本体项目会使用一个 docker 镜像构建,数据库也会分别独立使用 docker 容器部署。

mysql

在后端项目中,mysql 的配置如下:

mysql:
  host: mysql57
  port: 3306
  user: root
  password: root
  db: wms
  max_open_conns: 100
  max_idle_conns: 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

指定 mysql 的主机名是 mysql57,端口 3306,用户 root,密码 root,数据库 wms。

除了上述配置外,项目还需要初始化构建数据,sql 文件在后端项目中有,需要在构建 mysql 镜像时一并导入执行数据库的初始化工作。

在这里插入图片描述

如上图,在本地 /home/cauchy/docker/mysql 文件夹下,将后端项目中的 sql 文件拷贝过来 rbac.sql,并且编写 Dockerfiler 文件:

FROM mysql:5.7
COPY rbac.sql /docker-entrypoint-initdb.d/
  • 1
  • 2

容器内目录 /docker-entrypoint-initdb.d/ 下的 .sql 文件会被检测到,并自动执行。所以在构建镜像时,将 sql 文件拷贝至该目录下。

docker build -t mysql57:1.0 .
docker run -itd -v /home/cauchy/docker/mysql/data:/var/lib/mysql --name mysql57 -e MYSQL_ROOT_PASSWORD=root --network rbac mysql57:1.0
  • 1
  • 2

执行上述语句构建 mysql57:1.0 镜像,并且 -v 将容器的 /var/lib/mysql 目录挂载到本地 data 目录,--name 指定运行容器名称 mysql57,-e MYSQL_ROOT_PASSWORD=root 指定 root 用户的密码,--network 指定容器关联的网络 rbac

三个容器会绑定在同一个网络 rbac,用于网络通信,这样在项目容器中的配置文件指定 mysql 的 host 时,使用 mysql57 就能成功发现容器并正常通信了。

如果上述执行失败,可先创建网络:docker network create rbac,再去执行上述 shell 指令。


redis

项目中使用的 redis 配置如下:

redis:
  host: redis7
  port: 6379
  password: redis
  db: 0
  • 1
  • 2
  • 3
  • 4
  • 5

指定 redis 主机名是 redis7,端口 6379,密码 redis,数据库 0。

同理构建 redis:

docker run -itd -v /home/cauchy/docker/redis/data:/data --network rbac --name redis7 redis:7.0 --requirepass redis
  • 1

直接采用拉取的 redis:7.0 镜像构建 redis7 容器,--requirepass 指定需要认证的密码,容器的 /data 目录挂载到本地 data 目录。

如果没有镜像,可先拉取镜像:docker pull redis:7.0,再去执行上述 shell 指令。


rbac

该项目在部署前还需调整一下相关配置:

  1. 前端构建
  • vite.config.ts 文件调整
server: {
	 // 监听地址
	 host: "127.0.0.1",
	 // 端口号
	 port: 80,
	 // 服务启动时是否自动打开浏览器
	 open: true,
	 // 允许跨域
	 cors: true,
	 // 自定义代理规则
	 proxy: {},
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

修改 host 为服务器的 ip 地址

  • .env.development 文件调整
ENV = 'development'
VITE_APP_BASE_URL = 'http://127.0.0.1:8080'
  • 1
  • 2

修改 VITE_APP_BASE_URL 为后续实际访问服务的 url,即 http://服务器ip:服务监听端口,本项目配置端口在 conf/conf.yaml 中是 8080

  • npm run build 构建前端项目

构建成功后会生成一个 dist 目录,直接拷贝到后端的 static 目录下。

  1. 后端返回

在后端中,routers/routers.go 添加如下代码:

r.LoadHTMLGlob("static/dist/*.html")
r.Static("/assets", "./static/dist/assets")

r.GET("/", func(c *gin.Context) {
	if strings.HasSuffix(c.Request.RequestURI,".js") {
	        c.Header("Content-Type", "application/javascript; charset=utf-8")
	} else {
	        c.Header("Content-Type", "text/html; charset=utf-8")
	}
	c.HTML(http.StatusOK, "index.html", nil)
})

r.NoRoute(func(c *gin.Context) {
	if strings.HasSuffix(c.Request.RequestURI, ".js") {
		c.Header("Content-Type", "application/javascript; charset=utf-8")
	} else {
		c.Header("Content-Type", "text/html; charset=utf-8")
	}
	c.HTML(http.StatusOK, "index.html", nil)
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

LoadHTMLGlob 加载静态页面,Static 指定访问静态文件 js、css 时去哪个实际目录访问,GET("/", ...) 所有访问都会返回 index.html 页面,NoRoute 路由不存在时也返回 index.html 页面。由于获取前端界面需要 http 请求发给后端,后端会返回对应的资源文件,即上述的 js、css 文件,那么这些上行的路由在实际的路由中是不存在的,所以在此需要指定不存在路由是也同样返回界面,而不是报错。


对源码做上述修改后,就可以来构建基础镜像了。

后端的源码上传到服务器 /home/cauchy/docker/rbac 上:

在这里插入图片描述

编写 Dockerfile 文件:

FROM golang:1.22-alpine AS builder

ENV GO111MODULE=on \
    GOPROXY=https://goproxy.cn,direct \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64

WORKDIR /build

COPY . .

RUN go mod tidy
RUN go build -o rbac .


FROM scratch

WORKDIR /app

COPY ./conf /app/conf
COPY ./static /app/static
COPY --from=builder /build/rbac .

ENTRYPOINT ["/app/rbac"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

这里采用分阶段构建,实际运行仅需要一个二进制文件和相关配置文件。

先拉取 golang:1.22-alpine 镜像提供 go 的基础环境,ENV 中指定了程序需要的的环境变量,WORKDIR 指定工作目录,COPY 拷贝当前宿主机 Dockerfile 文件所在的同级所有文件到容器中工作目录下 /buildRUN 执行构建 gin 项目的指令,最终生成一个二进制文件 rbac。接下来拉取 scratch 镜像作为最终运行程序的环境,拷贝上一个容器中的二进制文件 rbac,源码下的配置文件夹 conf,前端构建后的文件夹 static,到这个小镜像的工作目录下,然后 ENTRYPOINT 指定执行这个二进制文件即可。

执行构建指令:

docker build -t rbac:1.0 .
docker run -itd -p 8080:8080 --network rbac --name rbac rbac:1.0
  • 1
  • 2

创建 rbac:1.0 镜像,构建容器 rbac,开放宿端口 8080 映射为容器内的 8080 端口。(服务器需要放行这个端口)

至此,三个 docker 容器,依此部署,成功部署该项目。

在这里插入图片描述


docker compose 部署

使用 docker compose 管理编排多个容器,按照依赖顺序构建容器服务。

首先需要在项目源码(后端)中添加新文件:docker-compose.yml,并编写如下内容:

version: "3.8"

services:

  mysql57:
    #build:
    #  context: ../mysql/
    #  dockerfile: Dockerfile
    image: mysql:5.7
    environment:
      - "MYSQL_ROOT_PASSWORD=root"
    volumes:
      - mysqlData:/var/lib/mysql
      - ./rbac.sql:/docker-entrypoint-initdb.d/rbac.sql
    networks:
      - rbac
    restart: always

  redis7:
    image: redis:7.0
    command: redis-server --requirepass redis
    restart: always
    volumes:
      - redisData:/data
    networks:
      - rbac

  rbac:
    build: ./
    command: sh -c "sleep 10; ./wait-for.sh mysql57:3306 redis7:6379 -- ./rbac"
    restart: on-failure
    ports:
      - "8080:8080"
    depends_on:
      - mysql57
      - redis7
    networks:
      - rbac


networks:
  rbac:

volumes:
  mysqlData:
  redisData:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

详细的 docker compose 模板文件可参考:https://vuepress.mirror.docker-practice.com/compose/

上述文件详细说明:

  • version:当前 docker compose file 的版本,可以查看 docker 引擎的版本(docker compose version),然后对照该表 这里,一般默认 3.8 即可
  • services:描述一组服务,必须写
  • build:在启动容器之前,根据 Dockerfile 构建镜像, 然后根据构建的镜像启动容器。该命令下的 context 指定 Dockerfile 所在的上下文目录位置
  • mysql57redis7rbac:都代表服务的名称,docker-compose 文件中服务名称都是唯一的。要指定容器的名称,可以使用 container_name 模板命令,指定特定名称,每个服务器容器名称唯一
  • image:指定启动容器使用的镜像
  • environment:给容器启动指定环境变量,env_file 模板命令也可以指定某个文件作为环境变量载入
  • volumes:指定宿主机与容器目录或文件的映射,但是在 docker compose 中不会主动创建未存在的目录,可以通过上述代码中最后的声明命令声明要挂载的目录
  • networks:指定启动容器使用网桥,同样未存在的 network 也需要声明
  • restart:指定当前容器的重启策略,always 表示总是运行,on-failure 表示失败后重启,详细可参考 这里
  • command:覆盖容器启动后默认执行的命令
  • depends_on:服务启动依赖的其他服务,但是当前服务不会等待依赖的服务完全启动之后才启动
  • ports:宿主机和容器的端口映射

可以看到,rbac 服务 command 命令中使用了 sleep 10; 还用了 ./wait-for.sh。按理执行这个脚本文件,会在启动 rbac 容器前先启动好 mysql、redis,但是我不知道为什么行不通,所以索性强制休眠 10 秒,等待另两个服务完成构建。

这个脚本文件参考:https://github.com/Eficode/wait-for,用于解决上述 depends_on 遗留的问题,帮助服务更好的完成按依赖顺序构建。

当然执行完这个 docker-compose.yml 文件,如配置中所示会构建一个新镜像,rbac 服务中 build: ./,即会在当前同级目录下找到 Dockerfile 文件构建,我们重新来看一下这个文件。当前目录文件如下所示:

在这里插入图片描述

Dockerfile:与之前分步构建时大致相同,不过换了一个镜像(不是 scratch 这个小镜像了),因为 wait-for.sh 脚本文件需要依赖 netcat,需要借助 apt-get 环境下载。(前面说了,我没使用成功,不能正常的等待依赖服务执行,所以如果不想使用的也可以换个能正常执行 sleep 的小镜像就行,不必用 ubuntu:latest 这个偏大的镜像)

FROM golang:1.22-alpine AS builder

ENV GO111MODULE=on \
    GOPROXY=https://goproxy.cn,direct \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64

WORKDIR /build

COPY . .

RUN go mod tidy
RUN go build -o rbac .


FROM ubuntu:latest

WORKDIR /app

COPY ./wait-for.sh .
COPY ./conf /app/conf
COPY ./static /app/static
COPY --from=builder /build/rbac .

RUN set -eux; \
    apt-get update; \
    apt-get install -y \
        --no-install-recommends \
        netcat; \
        chmod 755 wait-for.sh;

# ENTRYPOINT ["/app/rbac"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

注释 # ENTRYPOINT ["/app/rbac"],因为在 docker-compose.yml 中执行完 sleep 命令后,会去执行这个二进制文件,注意到文件中的 -- ./rbac

至此,通过 docker compose 部署本项目也成功了。


部署 nginx

由于要通过域名访问后端项目地址,那么可以通过加一个 nginx 容器,做一层正向代理。

第一步首先要进行域名解析:

  • 服务器的管理后台添加域名解析

在这里插入图片描述

  • 域名管理解析网站主机 IP

在这里插入图片描述

  • 防火墙开放 80 端口:
sudo apt install firewalld
sudo firewall-cmd --zone=public --add-port=80/tcp --permanent
sudo firewall-cmd --reload
  • 1
  • 2
  • 3

docker-compose.yml 中添加一个 nginx 镜像:

nginx125:
  image: nginx:1.25-alpine
  ports:
    - "80:80"
  networks:
    - rbac
  volumes:
    - ./nginx.conf:/etc/nginx/nginx.conf
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

nginx.conf 配置如下:

user root;

worker_processes  2;

error_log /error.log;

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://172.18.0.5:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_redirect off;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

这里域名 server_name 自行更换,proxy_pass 代理地址这里使用 docker inspect xxx 查看 rbac 这个 docker 的 ip,若换成自己的服务器 ip 或 localhost 也许也行,自行实验。

重新一键部署 docker compose up 即可通过域名访问,或是直接使用服务器 ip 访问。


前后端独立部署

代码地址:https://gitee.com/Cauchy_AQ/rbac/tree/rbac/

前面部署是通过后端程序返回前端静态页面完成的,也可在不修改源代码的情况下(之前部署需要在后端添加返回前端界面的代码),独立部署前后端,并且也通过 docker-compose.yml 一键部署。

在前端 frontend 文件夹下,需要自行修改 .env.developmentvite.config.ts,并且添加 Dockerfile 和一个 nginx.conf 文件。

  • .env.development:配置后端的地址 VITE_APP_BASE_URL = http://121.xxx.xxx.7:8080,后端 ip + 监听的端口
  • vite.config.tsserver 模块配置 host 为后端主机地址

nginx.conf

user root;

worker_processes  4;

error_log /error.log;

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;
        server_name example.com;

		root /usr/share/nginx/html;
		index index.html index.htm;
	
		include mime.types;
		default_type  application/octet-stream;
	
		location / {
		    try_files $uri $uri/ /index.html;	
		}
	
		location /assets/ {
	    	    alias /usr/share/nginx/html/assets/;
	    	    expires 1h;
		}
	
	    location ^~ /api/ {
	        proxy_pass http://121.xxx.xxx.7:8080;
	        proxy_set_header Host $host;
	        proxy_set_header X-Real-IP $remote_addr;
	        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	        proxy_set_header REMOTE-HOST $remote_addr;
	    }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

Dockerfile

FROM node:latest AS builder

WORKDIR /app

COPY package*.json ./

RUN npm i 

COPY . .

RUN npm run build


FROM nginx:1.25-alpine

COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=builder /app/dist /usr/share/nginx/html

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

即在前端中,需要单独配置一个 docker,并使用 nginx 部署,上述配置不详细解释了。

在后端 backend 文件夹下,也需要单独配置一个 docker:

FROM golang:1.22-alpine AS builder

ENV GO111MODULE=on \
    GOPROXY=https://goproxy.cn,direct \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64

WORKDIR /build

COPY . .

RUN go mod tidy
RUN go build -o rbac .

FROM alpine:latest

WORKDIR /app

COPY ./conf /app/conf
COPY --from=builder /build/rbac .

RUN echo "https://mirrors.tuna.tsinghua.edu.cn/alpine/latest-stable/main" > /etc/apk/repositories && \
    echo "https://mirrors.tuna.tsinghua.edu.cn/alpine/latest-stable/community" >> /etc/apk/repositories
RUN apk add --no-cache busybox
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

目录结构如下:

|- backend
	|- Dockerfile
	|- ***
|- frontend
	|- Dockerfile
	|- nginx.conf
	|- ***
|- docker-compose.yml
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里仍然直接在一台服务器上部署,通过 docker compose 一键部署,配置如下:

version: "3.8"

services:

  mysql57:
    image: mysql:5.7
    environment:
      - "MYSQL_ROOT_PASSWORD=root"
    volumes:
      - mysqlData:/var/lib/mysql
      - ./backend/rbac.sql:/docker-entrypoint-initdb.d/rbac.sql
    networks:
      - rbac
    restart: always

  redis7:
    image: redis:7.0
    command: redis-server --requirepass redis
    restart: always
    volumes:
      - redisData:/data
    networks:
      - rbac

  rbac:
    build: ./backend
    command: sh -c "sleep 10; ./rbac"
    restart: on-failure
    ports:
      - "8080:8080"
    depends_on:
      - mysql57
      - redis7
    networks:
      - rbac

  vue:
    build: ./frontend
    ports:
      - "80:80"
    restart: always


networks:
  rbac:

volumes:
  mysqlData:
  redisData:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

可以看到,vue 这个 docker 容器(实际容器名称 rbac-vue-1)不在 networkrbac 的网络中。前端界面通过 nginx 代理缓存静态资源返回,所有 rpc 通信会统一前缀 /api 并转到后端对应接口返回响应数据。这里图个方便直接全部丢在一个 docker-compose.yml 一键生成所需容器,并自动化部署。前端这个 nginx 容器独立出去也是可以的。

到此,第一个前后端项目部署就完成了,这次记录并未涉及很复杂的操作。我没使用 SSL 证书,只是自己简单购买了一个域名,写了一个前后端小项目,快速的部署了一下。文章如有错误,烦请指正。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/991243
推荐阅读
相关标签
  

闽ICP备14008679号