• 容器化单页面应用中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容器中独立出来。拓扑结构如下图所示: image

    对案例的调整

    我们将从以下几个方面对前文所述案例进行配置调整:
    • 简化前端应用的Nginx配置
    • Nginx反向代理容器的创建
    • 调整docker-compose.yml文件
     

    简化前端应用的Nginx配置

    在之前的案例中,前端应用的Nginx配置中还包含了反向代理的配置,这部分内容现在可以拿掉了,于是,前端应用的Nginx配置就非常简单了,只需要使用默认的静态页面服务配置即可,例如:
    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如下:
    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文件内容如下:
    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 {
              proxy_pass http://namelistcli/;
          }
    
          location ~ ^/name-service/(.*)$ {
            rewrite ^ $request_uri;
            rewrite ^/name-service/(.*)$ $1 break;
            return 400;
            proxy_pass http://namelistsvc/$1;
          }
        }
    
        upstream namelistsvc {
          server namelist-service:5000;
        }
    
        upstream namelistcli {
          server namelist-client:80;
        }
    }
    上面定义了两个upstream,分别对应应用程序的前端和后端,然后根据不同的路径规则分别将请求路由到不同的服务器上。在Dockerfile中,只需要将该配置文件复制到Nginx的配置路径下即可:
    FROM nginx
    COPY nginx.conf /etc/nginx/nginx.conf
    CMD ["nginx", "-g", "daemon off;"]

    调整docker-compose.yml文件

    我们需要相应地调整docker-compose.yml文件,以便能够方便地将这些服务运行起来。docker-compose.yml文件非常简单:
    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项目的根目录下:
    $ docker build -t daxnet/namelist-client .
    $ docker build -t daxnet/namelist-service .
    然后,使用docker-compose up命令,启动所有服务,并使用浏览器访问Nginx反向代理服务的/app路径,得到如下结果: image 目前无需纠结上图中最后一个c415….是什么,它只不过是当前服务端机器的机器名称,在接下来Kubernetes部署阶段,我们会通过实验来验证namelist-service服务在Kubernetes中的伸缩性。

    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工具一键生成即可:
    $ 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上:
    $ kubectl apply -f k8s.deployment.yaml
    部署完成后,查看deployments、services和pods: Screenshot from 2019-07-27 20-26-07 然后,使用kubectl cluster-info命令以获得Node IP: Screenshot from 2019-07-27 20-27-48 在浏览器中使用Node IP和Node Port来访问namelist应用程序: Screenshot from 2019-07-27 20-29-52 现在,将namelist-service扩展到2个实例: Screenshot from 2019-07-27 20-33-07 在浏览器中,反复刷新页面,可以看到,页面上显示的机器名在变化,证明Kubernetes将API访问请求重定向到不同的namelist-service服务实例: Screenshot from 2019-07-27 20-42-08 Screenshot from 2019-07-27 20-42-20

    总结

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

    源代码

    本文源代码可以参考:https://github.com/daxnet/name-list/tree/k8s-deployment
  • 相关阅读:
    Yarn的运行原理(执行流程)
    Hive 表类型简述
    hive 表类型
    Hadoop小知识点总结1
    电商交易项目案例
    乘用车辆和商用车辆销售数据分析
    搜狗搜索日志分析系统
    mysql group by 对多个字段进行分组
    group by
    输出GPLT
  • 原文地址:https://www.cnblogs.com/daxnet/p/12941770.html
Copyright © 2020-2023  润新知