容器化单页面应用中Nginx反向代理与Kubernetes部署

在《容器化单页面应用中RESTful API的访问》一文中,我介绍了一个在容器化环境中单页面应用访问后端服务的完整案例。这里我将继续使用这个案例,介绍一下容器化单页面应用部署的另一个场景:将Nginx的职责独立出来。

注:这里单页面应用是值一个包含前端页面、后端服务以及后台数据库的一个完整应用系统,这样符合微服务模式对于服务的定义。不过为了介绍简单,文章案例不使用后台数据库,而是将数据“写死”在后端服务中。

继续回顾一下上篇文章中的案例,我们有两个服务:前端单页面应用(client),以及后端基于ASP.NET Core Web API的RESTful服务(service),案例代码地址是:https://github.com/daxnet/name-list。在这个案例中,前端单页面应用运行在Nginx容器中,这里的Nginx同时还承担了反向代理的角色,用以将前端页面发出的RESTful API请求正确地转发到ASP.NET Core Web API上。

如果整个系统只有这一个单页面应用,那么这么做是简单且合理的;但如果一个系统包含多个单页面应用,或者说一个系统包含一个前端页面与多个后台服务,那么,将Nginx反向代理的职责加到这个前端页面的容器上,明显是不合理的。为什么不合理?因为一个系统有可能不仅仅有基于Web的UI,而且还有可能会有移动客户端,比如Andriod或者iOS的前端,甚至直接暴露API以供外部系统集成。如果运行前端页面的容器还兼职做反向代理的话,这些访问请求都将发送到前端单页面应用的服务器(容器)上,这样就会对前端应用造成压力。

因此,一个更好的做法是,将Nginx的反向代理职责从前端页面所运行的Nginx容器中独立出来。拓扑结构如下图所示:

640?wx_fmt=png

我们将从以下几个方面对前文所述案例进行配置调整:

  • 简化前端应用的Nginx配置

  • Nginx反向代理容器的创建

  • 调整docker-compose.yml文件

简化前端应用的Nginx配置

在之前的案例中,前端应用的Nginx配置中还包含了反向代理的配置,这部分内容现在可以拿掉了,于是,前端应用的Nginx配置就非常简单了,只需要使用默认的静态页面服务配置即可,例如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

events {

    worker_connections 1024;

}

http {

    server {

      listen        80;

      server_name   localhost;

      include  /etc/nginx/mime.types;

      location / {

        root /usr/share/nginx/html;

        index  index.html  index.htm;

      }

    }

}

因此,在docker中完成前端页面的编译之后,将所有的资源复制到/usr/share/nginx/html下即可。前端Dockerfile如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

FROM nginx AS base

WORKDIR /app

EXPOSE 80

FROM node:10.16.0-alpine AS build

RUN npm install -g @angular/cli@8.0.3

WORKDIR /src

COPY . .

RUN npm install

RUN ng build --prod --output-path /app

FROM base AS final

COPY --from=build /app /usr/share/nginx/html

COPY --from=build /src/nginx.conf /etc/nginx/nginx.conf

CMD ["nginx", "-g", "daemon off;"]

Nginx反向代理容器的创建

下一步就是创建一个Nginx反向代理的容器,基本思路是将反向代理配置到nginx.conf文件中,然后基于Nginx容器镜像,将nginx.conf文件复制到容器中即可。nginx.conf文件内容如下:

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

events {

    worker_connections 1024;

}

http {

    server {

      listen        80;

      server_name   localhost;

      include  /etc/nginx/mime.types;

      location / {

        root /usr/share/nginx/html;

        index  index.html  index.htm;

      }

      location /app {

      }

      location ~ ^/name-service/(.*)$ {

        rewrite ^ $request_uri;

        rewrite ^/name-service/(.*)$ $1 break;

        return 400;

      }

    }

    upstream namelistsvc {

      server namelist-service:5000;

    }

    upstream namelistcli {

      server namelist-client:80;

    }

}

上面定义了两个upstream,分别对应应用程序的前端和后端,然后根据不同的路径规则分别将请求路由到不同的服务器上。在Dockerfile中,只需要将该配置文件复制到Nginx的配置路径下即可:

1

2

3

FROM nginx

COPY nginx.conf /etc/nginx/nginx.conf

CMD ["nginx", "-g", "daemon off;"]

调整docker-compose.yml文件

我们需要相应地调整docker-compose.yml文件,以便能够方便地将这些服务运行起来。docker-compose.yml文件非常简单:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

version: '3'

services:

  namelist-service:

    image: daxnet/namelist-service

  namelist-client:

    image: daxnet/namelist-client

  namelist-nginx:

    image: daxnet/namelist-nginx

    ports:

      - 80:80

    links:

      - namelist-service

      - namelist-client

对于namelist-service和namelist-client两个服务,我们没有指定TCP端口,因为这两个服务无需暴露出来,namelist-nginx服务会通过容器链接(links)由docker的DNS来解析这两个服务并在子网内部访问。

下面我们测试一下整个应用程序,使用下面的命令分别编译docker镜像,注意:编译前先进入client或service项目的根目录下:

1

2

$ docker build -t daxnet/namelist-client .

$ docker build -t daxnet/namelist-service .

然后,使用docker-compose up命令,启动所有服务,并使用浏览器访问Nginx反向代理服务的/app路径,得到如下结果:

640?wx_fmt=png

目前无需纠结上图中最后一个c415….是什么,它只不过是当前服务端机器的机器名称,在接下来Kubernetes部署阶段,我们会通过实验来验证namelist-service服务在Kubernetes中的伸缩性。

接下来,我们将name-list案例部署到Kubernetes上。在这里,我会使用Minikube来演示。Minikube是一套Kubernetes的最小集群,它只包含一个节点,但对于我们学习和实验来说已经够用。安装Minikube过程也不是特别容易,尤其是在国内的网络环境中,我推荐使用阿里云提供的相关资源以及使用Oracle Virtual Box来作为Minikube的虚拟化环境,这样安装过程最简单。我的Minikube是安装在Ubuntu 18.04的Linux机器上。

首先需要编写Kubernetes的部署描述文件,可以使用Kubernetes官方的Kompose工具,它能够帮助我们很方便地从docker-compose.yml文件生成Kubernetes的部署描述文件。对于name-list而言,我们已经有docker-compose.yml文件了,因此,使用Kompose工具一键生成即可:

1

$ kompose convert -o k8s.deployment.yaml

这条命令会将所有的部署脚本(包括deployment,service等)输出到同一个yaml文件中,如果不使用-o参数,那么就会分别输出到不同的文件中。但这都不是重点。重点是,我们还需要对生成的yaml文件进行一些修改。

第一个需要修改的地方是,要将namelist-nginx的service类型指定为NodePort,这样我们才可以使用Node IP来访问我们的应用程序。Minikube不支持LoadBalancer类型的service,因此,在访问应用程序之前,我们需要获取Node IP。在上文中我提到,namelist-service和namelist-client无需暴露端口出来,因为Nginx反向代理会将外部请求转发到这两个服务上。然而,由于没有暴露可访问的TCP端口,Kompose并不会对这两个服务产生service的定义,这就需要我们自己添加到所产生的k8s.deployment.yaml文件中,只不过我们不需要指定service的类型,因为我们不需要直接访问它们。

准备好部署文件之后,我们需要使用docker push命令,将namelist的三个docker镜像推送到Docker Hub上。Minikube默认会从Docker Hub上拉取镜像进行部署。这一步我就不多做说明了。

接下来,使用下面的命令将namelist应用部署到Kubernetes上:

1

$ kubectl apply -f k8s.deployment.yaml

部署完成后,查看deployments、services和pods:

640?wx_fmt=png

然后,使用kubectl cluster-info命令以获得Node IP:

640?wx_fmt=png

在浏览器中使用Node IP和Node Port来访问namelist应用程序:

640?wx_fmt=png

现在,将namelist-service扩展到2个实例:

640?wx_fmt=png

在浏览器中,反复刷新页面,可以看到,页面上显示的机器名在变化,证明Kubernetes将API访问请求重定向到不同的namelist-service服务实例:

640?wx_fmt=png

640?wx_fmt=png

本文介绍了在namelist案例中,将Nginx反向代理职责从前端容器中独立出来的设计与实现,并介绍了Kubernetes部署的基本步骤和注意事项。基于namelist案例还可以继续扩展,比如使用HELM打包Kubernetes应用,今后有机会我会继续介绍。

本文源代码可以参考:https://github.com/daxnet/name-list/tree/k8s-deployment。

原文:https://sunnycoding.cn/2019/07/27/reverse-proxy-in-containerized-spa-and-kubernetes-deployment/ 


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

640?wx_fmt=jpeg


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/314703.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[蓝桥杯2019初赛]迷宫-DFS、BFS两种方法

迷宫问题的最短路&#xff0c;加最小字典序 迷宫文件maze.txt传送门 作者写的2019年B组蓝桥杯解集 . . . DFS的版本 #include<iostream> #include<cstring> using namespace std; const int ax[4]{0,0,1,-1}; const int ay[4]{1,-1,0,0}; const char dir[5]{R,L…

2021牛客暑期多校训练营7 xay loves trees dfs序 + 主席树

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 给你两棵树&#xff0c;让你在第一棵树选一条最长的深度递增的链&#xff0c;链上每一个点在第二棵树上都不互为祖先。 n≤3e5n\le3e5n≤3e5 思路&#xff1a; 之前做过差不多的题传送门。 之前哪个题是不…

xxl-job dotnet core executor执行器开源

DotXxlJob[(github)https://github.com/xuanye/DotXxlJob][https://github.com/xuanye/DotXxlJob] xxl-job的dotnet core 执行器实现&#xff0c;支持XXL-JOB 2.01 XXL-JOB概述[XXL-JOB][1]是一个轻量级分布式任务调度平台&#xff0c;其核心设计目标是开发迅速、学习简单、轻量…

2019年第十届蓝桥杯 C / C ++省赛 B 组真题题解

A: 组队 输入数据 编号 号位1 2 3 4 51 97 90 0 0 0 2 92 85 96 0 0 3 0 0 0 0 93 4 0 0 0 80 86 5 89 83 97 0 0 6 82 86 0 0 0 7 0 0 0 87 90 8 0 97 96 0 0 9 0 0 89 0 0 10 95 99 0 0 0 11 0 0 96 97 0 12 0 0 0 93 98 13 94 91 0 0 0 14 0 83 87 0 0 15 …

HDU - 7054 Yiwen with Formula 分治拆位FFT + dp + 费马小定理降幂

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 求aaa的所有子序列的和的乘积。 思路&#xff1a; 看到suma≤1e5sum_a\le1e5suma​≤1e5&#xff0c;这应该会给我们提示&#xff0c;但是我没看到。 我们可以记cntxcnt_xcntx​表示和为xxx的子序列有cnt…

使用Redis创建分布式锁

点击上方蓝色字关注我们~在本文中&#xff0c;我们将讨论如何在.NET Core中使用Redis创建分布式锁。当我们构建分布式系统时&#xff0c;我们将面临多个进程一起处理共享资源&#xff0c;由于其中只有一个可以一次使用共享资源&#xff0c;因此会导致一些意外问题&#xff01;我…

ASP.NET Core Web Api之JWT VS Session VS Cookie(二)

本文我们来探讨下JWT VS Session的问题&#xff0c;我们可直接抛出问题&#xff1a;使用客户端存储的JWT比服务端维持Session更好吗&#xff1f; 既然要比较JWT VS Session&#xff0c;那我们就得知道为何需要JWT和Session&#xff0c;它们共同是为了解决什么问题呢&#xff1f…

P4721 【模板】分治 FFT

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 写一下式子&#xff0c;发现每个fif_ifi​只有左边的fff对他有影响&#xff0c;所以考虑分治FFTFFTFFT来解决这个问题。 先递归左边&#xff0c;让后计算对右边贡献&#xff0c;再递归右边…

程序员生活之路--来自程序员爸爸的一封信

亲爱的孩子&#xff1a;当你看到爸爸这封信的时候&#xff0c;说明你已经长大了或者已经会玩微信公众号了&#xff0c;当然爸爸还是希望你长大了&#xff0c;并不希望你那么小就天天抱着手机刷微信。我写这个文章的时候正直盛夏&#xff0c;现在外边正是37度的高温&#xff0c;…

C#上位机与欧姆龙PLC的通信05---- HostLink协议

1、介绍 Hostlink协议是欧姆龙PLC与上位机链接的公开协议。上位机通过发送Hostlink命令&#xff0c;可以对PLC进行I/O读写、可以对PLC进行I/O读写、改变操作模式、强制置位/复位等操作。由于是公开协议&#xff0c;即便是非欧姆龙的上位设备&#xff08;软件&#xff09;&…

Codeforces Round #737 (Div. 2) D. Ezzat and Grid 线段树动态开点

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 比较套路的一个题&#xff0c;我们维护一个dp[i]dp[i]dp[i]表示到了第iii行能保留的区间最多是多少。 转移比较明显&#xff1a;dp[i]max(dp[j])dp[i]max(dp[j])dp[i]max(dp[j]) 其中jjj能…

从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件

标题&#xff1a;从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件 作者&#xff1a;Lamond Lu地址&#xff1a;https://www.cnblogs.com/lwqlun/p/11260750.html源代码&#xff1a;https://github.com/lamondlu/DynamicPlugins前情回顾•从零开始实现ASP…

SP1043 GSS1 - Can you answer these queries I 猫树

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 猫树是一种可以O(nlogn)O(nlogn)O(nlogn)预处理&#xff0c;O(1)O(1)O(1)查询的数据结构。预处理的信息应该满足可合并的性质&#xff0c;与线段树pushuppushuppushup的原理相同&#xff0…

动手造轮子:基于 Redis 实现 EventBus

动手造轮子&#xff1a;基于 Redis 实现 EventBusIntro上次我们造了一个简单的基于内存的 EventBus&#xff0c;但是如果要跨系统的话就不合适了&#xff0c;所以有了这篇基于 Redis 的 EventBus 探索。本文的实现是基于 StackExchange.Redis 来实现。RedisEventStore 实现既然…

最小生成树KrusKal算法(并查集)

洛谷p1111链接 克鲁斯卡尔算法的思路就是由森林变成树的过程&#xff0c;其中最主要的就是贪心和并查集的应用。 我们知道链接n个点需要n-1条边&#xff0c;这就满足的最后生成的是一颗树&#xff0c;而不是一个环。在这n-1条边的选择上我们又要尽可能的让边的权重小&#xff0…

#6278. 数列分块 2 分块 + 块内二分

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 真 调一晚上血压上来了。 考虑第一个操作&#xff0c;块内打个标记&#xff0c;其他的暴力查询即可。 考虑第二个操作&#xff0c;讲块内元素排序之后&#xff0c;直接二分查询。 注意修改…

使用腾讯云提供的针对Nuget包管理器的缓存加速服务

继阿里巴巴开源镜像站&#xff08;https://opsx.alibaba.com/&#xff09;、华为云镜像站点&#xff08;https://mirrors.huaweicloud.com/ &#xff09;之后&#xff0c;腾讯也已于近日上线了类似的服务&#xff0c;官方名称为腾讯云软件源&#xff08;Tencent Open Source Mi…

最小生成树Prime算法

洛谷p1546链接 Prime算法的核心也是贪心&#xff0c;但是不同的就是&#xff0c;它是一直维护一颗树&#xff0c; 直到变成一颗最小生成树&#xff0c; #include<bits/stdc.h> using namespace std; const int maxn 110; const int inf 0x3f3f3f3f; int maze[maxn][m…

#6284. 数列分块 8 分块

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 乍一看貌似没有什么东西能维护块内同一个数的个数&#xff0c;但是通过第六感可以发现每次操作后区间都会被推成一个数&#xff0c;那么我们分个块&#xff0c;让后块内打个标记&#xff0…

最短路弗洛伊德(Floyd)算法加保存路径

弗洛伊德算法大致有点像dp的推导 dp[i][j] min(dp[i][k] dp[k][j], dp[i][j]), 其中 i 是起始点&#xff0c;j 是终止点。k是它们经过的中途点。 通过这个公式不断地更新dp[i][j],得到最短路径长。 我们先定义两个矩阵&#xff0c;minpath[i][j],表示的是从 i 到 j 当前得到的…