Docker Compose 部署前后端分离应用
部署前后端分离应用
容器化 Abp 应用
关于 Abp 应用的容器化,其实和普通的 ASP.NET Core 应用差不多,大家可以参考我此前的文章。
唯一需要注意的是:因为 Abp 解决方案中有多个项目,在 publish 过程中需要手动指定启动项目,例如:
# 其余内容请参考上述文章
# 修改 RUN dotnet publish -c Release -o /app 为以下内容
RUN dotnet publish ./src/YourProjectName.Web.Host/YourProjectName.Web.Host.csproj -c Release -o /app
使用 sql 文件应用迁移
在使用 EF Core 进行 Code First 开发的时候,我们往往都习惯在 VS 的控制台中使用Update-Database完成迁移。但在实际部署过程中,考虑开发环境可能无法直接与部署所用主机相连,我们可以通过导出 sql 文件的形式来完成迁移。
在解决方案的根目录下打开命令行:
dotnet ef migrations script -p .\src\YourProjectName.EntityFrameworkCore\ -o .\init.sql
然后,将该 init.sql 文件挂载到 mysql 的镜像的初始化目录当中:
version: "3"
services:
mysql:
# ...
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
这样,就会在 mysql 容器启动时,自动完成迁移。
使用外部网络桥接数据库
在我们的 API 应用中,会在启动阶段检测数据库中是否正确配置了基础信息,这就带来一个棘手的问题:当所用数据库比如说 MySQL,也是通过同一个 Docker Compose 部署的时候,会有启动延迟,从而使得数据库还未来得及应用迁移(Apply Migrations),即便在配置文件中配置了depends_on也无法避免。
depends_on只影响容器启动的顺序,而此处 MySQL 的启动延迟是在容器启动后发生的。
在此前,我一直是通过脚本或者额外的代码来为 API 应用增添启动延迟重试的功能,但显然这对生产环境来说并不合适。
其实,我们可以来分析一下几个要求:
- MySQL 必须在 API 应用启动前,完成初始化或迁移。
- 不应为此给 API 应用引入额外的逻辑。
- API 应用的更新不应该影响数据库。
所以,最终还是决定将数据库独立出来,单独用一个 Docker Compose 来进行部署,可这也带来一个新的问题:API 应用无法确定数据库所在的子网 IP,或者说两者甚至不在同一子网中。
那么,自然得想办法将数据库重新加入 API 应用所在的子网,通过查找资料,Docker Compose 已经为我们提供了这一功能,即使用外部网络。
这里又可以多说一句,在我们使用 Docker Compose 部署的时候,其默认会为我们创建一个子网,并将我们的定义的服务添加到该子网中。而这个网络是直接由 Docker Compose 管理的,在
up时创建,在down时销毁,因此不符合我们需要将多个 Docker Compose 定义的服务加入同一子网的要求。
手动创建一个虚拟子网:
docker network create xxx
在配置文件中定义该网络,并将服务分别添加到该网络:
# db.yml
version: "3"
services:
mysql:
# ...
networks:
- xxx
networks:
# 定义该网络为外部网络
xxx:
external: true
# 同理
# docker-compose.yml
version: "3"
services:
api:
# ...
networks:
- xxx
networks:
xxx:
external: true
然后,在部署的时候,我们只需要在第一次部署时,先等等 MySQL 已经完成初始化,再启动 API 应用即可。且对此后的 CI/CD 过程,我们也只需要关注 API 应用镜像的更新(需要修改数据库表结构除外)。
使用 Nginx 部署 SPA 应用
我们的前端都是以 SPA 应用来构建的,也就是说只需发布静态文件即可。这里我们就使用 Nginx 作为 Web 服务器。
值得注意的也就以下三点:
- 挂载 Web 根目录和配置文件
- 开启反向代理
- 开启伪静态
挂载目录和文件已经是老生常谈了,这里就不再赘述,在 docker-compose.yml 中配置以下两行即可:
version: "3"
services:
web:
# ...
volumes:
- ./html:/usr/share/nginx/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
接下来是开启反向代理,这里我们所请求的 API 都是以/api为前缀的相对路径,因此只需要在 nginx.conf 中配置:
server {
# ...
location /api {
proxy_pass http://api;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
}
}
可能细心的人有发现这里将请求代理给了 api 这个域名,其实也就是我们所定义的 api 服务。那为了让这种写法有效,自然别忘了将 web 服务和 api 服务添加到同一子网,可以是之前的外部子网,或者再新建一个內部子网也行。
配置到这里,其实已经是可以用了,但在你刷新页面的时候不时会出现 404 错误。这是因为 SPA 应用的路由并不对应真正文件的路径,我们需要将对应的请求指向我们真正的文件,也就是伪静态。
例如,你的应用发布在 Web 根目录下,其主页面名为 index.html,可以如下配置:
server {
# ...
location / {
try_files $uri $uri/ /index.html;
}
}
结语
最终,你所有的配置文件应该如下:
docker-compose.yml
version: "3"
services:
api:
container_name: xxx_api
image: xxx:api
ports:
- "21021:80"
volumes:
# 这里映射日志目录
- ./App_Data:/app/App_Data
environment:
- ConnectionStrings:Default=server=mysql;userid=root;pwd=xxx;port=3306;database=xxx;Charset=utf-8;
# 跨域控制
- App:ServerRootAddress=http://xxx.xxx:21021
- App:ClientRootAddress=http://xxx.xxx:8000
- App:CorsOrigins=http://xxx.xxx:8000,http://xxx.xxx:21021
networks:
- xxx
web:
container_name: xxx_web
image: nginx
ports:
- "8000:80"
volumes:
- ./html:/usr/share/nginx/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
networks:
- xxx
networks:
xxx:
external: true
db.yml
version: "3"
services:
mysql:
container_name: xxx_mysql
image: mysql:8.0
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=xxx
- MYSQL_DATABASE=xxx
volumes:
- ./mysql:/var/lib/mysql
- ./charset.cnf:/etc/mysql/conf.d/charset.cnf
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- xxx
networks:
xxx:
external: true
nginx.conf
server {
listen 80;
# gzip config
gzip on;
gzip_min_length 1k;
gzip_comp_level 9;
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
root /usr/share/nginx/html;
location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://api;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Docker Compose 部署前后端分离应用的更多相关文章
- 使用 Nginx 部署前后端分离项目,解决跨域问题
前后端分离这个问题其实松哥和大家聊过很多了,上周松哥把自己的两个开源项目部署在服务器上以帮助大家可以快速在线预览(喜大普奔,两个开源的 Spring Boot + Vue 前后端分离项目可以在线体验了 ...
- Nginx部署前后端分离服务
飘过... 一,安装Nginx 二,配置nginx 一般nginx配置文件在etc目录下 另,如何找nginx.conf配置文件: 在前后端分离端项目里,前端的代码会被打包成为纯静态文件.使用 Ngi ...
- linux --- 部署前后端分离项目
vue + uwsgi +nginx 部署前后端分离项目 准备项目 1.将前端vue项目包和后端django项目包上传服务器,通过lrzsz,直接从windows拖进linux中 2.解压缩操作 前端 ...
- centos7部署前后端分离项目的过程
概述 本文主要讲解在安装了centos7的Linux主机中部署前后端分离项目的过程. 前端项目名为:vue_project:后端项目名为:django_project. 将这两个项目放在/opt/wh ...
- 在centos7.6上部署前后端分离项目Nginx反向代理vue.js2.6+Tornado5.1.1,使用supervisor统一管理服务
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_102 这一次使用vue.js+tornado的组合来部署前后端分离的web项目,vue.js不用说了,前端当红炸子鸡,泛用性非常广 ...
- docker+nginx+redis部署前后端分离项目!!!
介绍本文用的经典的前后端分离开源项目.项目的拉取这些在另一篇博客!!! 其中所需要的前后端打包本篇就不做操作了!!不明白的去看另一篇博客!!! 地址:http://www.cnblogs.com/ps ...
- 海纳百川无所不容,Win10环境下使用Docker容器式部署前后端分离项目Django+Vue.js
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_179 随着现代化产品研发的不断推进,我们会发现,几乎每个产品线都会包含功能各异的服务,而且服务与服务之间存在也会存在着错综复杂的依 ...
- 在Linux上从零开始部署前后端分离的Vue+Spring boot项目
最近做了一个前后端分离的商城项目来熟悉开发的整个流程,最后希望能有个正式的部署流程,于是试着把项目放在云服务器上,做了一下发现遇到了不少问题,借此记录一下整个部署的过程. 使用的技术栈如标题所说大体上 ...
- vue+uwsgi+nginx部署前后端分离项目
前后端分离,vue前端提供静态页面,且可以向后台发起get,post等restful请求. django后台提供数据支撑,返回json数据,返回给vue,进行数据页面渲染 后端 创建虚拟环境 解决dj ...
随机推荐
- Ubuntu cd
查看目录文件内容 ./ or filename/file.* cd 返回用户主目录 ~,,,,,/home/user cd ..不管用 cd / 返回用户根目录 root
- 复制带随机指针的链表 · Copy List with Random Pointer
[抄题]: 给出一个链表,每个节点包含一个额外增加的随机指针可以指向链表中的任何节点或空的节点. 返回一个深拷贝的链表. [思维问题]: [一句话思路]: 完完全全地复制,否则不好操作. 1-> ...
- 【转】从源码浅析MVC的MvcRouteHandler、MvcHandler和MvcHttpHandler
原文:http://www.cnblogs.com/jeffwongishandsome/archive/2012/01/08/2316521.html 熟悉WebForm开发的朋友一定都知道,Pag ...
- ueditor使用注意
问题1:后端配置项没有正常加载,上传插件不能正常使用! 我用的是开发版,1.4.3.3 .Net版 网上查了很多,后来发现只是配置的问题而已. 1.在根目录下:config.json 其中有Img上传 ...
- 前端学习之JavaScript
JavaScript的历史 1992年Nombas开发出C-minus-minus(C--)的嵌入式脚本语言(最初绑定在CEnvi软件中).后将其改名ScriptEase.(客户端执行的语言) Net ...
- 团队项目:二次开发--v.2.1--软件工程
原先代码,对于基本对象的Get,Set方法构造函数等方法与实现基本功能的方法统一放到了一起,容易造成代码不清晰,别人比较难阅读的情况.而且其中代码冗余比较多. 改进代码,进行了层次的分析,将基本对象与 ...
- [SoapUI] 各种日期计算
import java.util.*; import java.text.SimpleDateFormat; // current date String dateNew = today() // t ...
- Spring.NET学习笔记7——依赖对象的注入(基础篇) Level 200
1.person类 public class Person { public string Name { get; set; } public int Age { g ...
- 由已打开的文件读取数据---read
头文件:#include<unistd.h> 函数原型:ssize_t read(int fd,void *buf,size_t count); 参数说明:fd:文件描述符 buf:存放读 ...
- 2018.09.07 codeforces311B. Cats Transport(斜率优化dp)
传送门 斜率优化dp好题. 对于第i只猫,显然如果管理员想从出发开始刚好接到它,需要在t[i]=h[i]−dist(1,i)" role="presentation" s ...