入门指南丨上手理解Deployment、Services和Ingress

在以前的文章中,咱们了解了Kubernetes中的基本概念,其硬件结构,不一样的软件组件(例如Pod、Deployment、StatefulSet、Services、Ingress和Persistent Volumes),并了解了如何在服务之间与外部进行通讯。前端

在本文中,咱们将了解到:node

  1. 使用MongoDB数据库建立NodeJS后端
  2. 编写Dockerfile来容器化咱们的应用程序
  1. 建立Kubernetes Deployment脚本以启动Pod
  1. 建立Kubernetes Service脚本以定义容器与外界之间的通讯接
  1. 部署Ingress Controller以请求路由
  1. 编写Kubernetes Ingress脚原本定义与外界的通讯。

图片

因为咱们的代码能够从一个节点重定向到另外一个节点(例如,一个节点没有足够的内存,因此工做将从新调度到另外一个具备足够内存的节点上),所以保存在节点上的数据容易丢失 ,意味着MongoDB 数据不稳定。在下一篇文章中,咱们将讨论数据持久性问题以及如何使用Kubernetes持久卷安全地存储咱们的持久数据。nginx

在本文中,咱们将使用NGINX做为Ingress Controller和Azure容器镜像仓库来存储咱们的自定义Docker镜像。文中编写全部脚本均可以在Stupid Simple Kubernetes git repo中找到,若有须要可访问连接获取:git

http://GitHub - CzakoZoltan08/StupidSimpleKubernetes-AKSgithub

请注意:这些脚本不限定于某个平台,所以您可使用其余类型的云提供程序或带有K3s的本地集群来实践本教程。我之因此建议使用K3s,由于它很是轻量,全部依赖项都被打包在一个小于100MB的单个二进制文件中。更重要的是,它是一种高可用的、通过CNCF认证的Kubernetes发行版,专门用于资源受限的环境中的生产工做负载。有关更多信息,您能够访问官方文档:docker

https://docs.rancher.cn/k3s/数据库

前期准备

在开始本教程以前,请确保您已安装Docker。同时也要安装kubectl。npm

Kubectl安装连接:json

https://kubernetes.io/docs/tasks/tools/#install-kubectl-on-windowssegmentfault

在本教程中使用的Kubectl命令能够在Kubectl cheat sheet(https://kubernetes.io/docs/reference/kubectl/cheatsheet/)中找到。

在本教程中,咱们将使用Visual Studio Code,但这不是必要的,你也可使用其余的编辑器。

建立可用于生产的微服务架构

将应用程序容器化

第一步,建立NodeJS后端的Docker镜像。建立镜像后,咱们会将其推送到容器镜像仓库中,在该镜像仓库中能够访问它,而且能够经过Kubernetes服务(在本例中为Azure Kubernetes Service)拉取。

The Docker file for NodeJS:  
FROM node:13.10.1  
WORKDIR /usr/src/app  
COPY package*.json ./  
RUN npm install  
# Bundle app source  
COPY . .  
EXPOSE 3000  
CMD [ "node", "index.js" ]

在第一行中,咱们须要根据要建立后端服务的镜像进行定义。在这种状况下,咱们将使用Docker Hub中13.10.1版的官方节点镜像。

在第3行中,咱们建立一个目录来将应用程序代码保存在镜像中。这将是您的应用程序的工做目录。

该镜像已经安装了Node.js和NPM,所以下一步咱们须要使用npm命令安装您的应用程序依赖项。

请注意,要安装必需的依赖项,咱们不用复制整个目录,而只需复制package.json,这使咱们能够利用缓存的Docker层。

有关高效Dockerfile的更多信息,请访问如下连接:

http://bitjudo.com/blog/2014/03/13/building-efficient-dockerfiles-node-dot-js/

在第9行中,咱们将源代码复制到工做目录中,在第11行中,将其暴露在端口3000上(若是须要,您能够选择另外一个端口,但请确保同步更改Kubernetes Service脚本。)

最后,在第13行,咱们定义了运行应用程序的命令(在Docker容器内部)。请注意,每一个Dockerfile中应该只有一个CMD指令。若是包含多个,则只有最后一个才会生效。

如今,咱们已经定义了Dockerfile,咱们将使用如下Docker命令从该Dockerfile中构建镜像(使用Visual Studio Code的Terminal或在Windows上使用CMD):

docker build -t node-user-service:dev .

请注意Docker命令末尾的小圆点,这意味着咱们正在从当前目录构建镜像,所以请确保您位于Dockerfile所在的同一文件夹中(在本例中,是repo的根文件夹)。

要在本地运行镜像,咱们可使用如下命令:

docker run -p 3000:3000 node-user-service:dev

若要将此镜像推送到咱们的Azure容器镜像仓库,咱们必须使用如下格式标记它<container-registry-login-service>/<image-name>:<tag>:,在本例中以下所示:

docker tag node-user-service:dev stupidsimplekubernetescontainerregistry.azurecr.io/node-user-service:dev

最后一步是使用如下Docker命令将其推送到咱们的容器镜像仓库中:

docker push stupidsimplekubernetescontainerregistry.azurecr.io/node-user-service:dev

使用部署脚本建立Pod

NodeJs后端

接下来,定义Kubernetes Deployment脚本,该脚本将自动为咱们管理Pod。

apiVersion: apps/v1  
kind: Deployment  
metadata:  
  name: node-user-service-deployment  
spec:  
  selector:  
    matchLabels:  
      app: node-user-service-pod  
  replicas: 3  
  template:  
    metadata:  
      labels:  
        app: node-user-service-pod  
    spec:  
      containers:  
        - name: node-user-service-container  
          image: stupidsimplekubernetescontainerregistry.azurecr.io/node-user-service:dev  
          resources:  
            limits:  
              memory: "256Mi"  
              cpu: "500m"  
          imagePullPolicy: Always  
          ports:  
            - containerPort: 3000

Kubernetes API能够查询和操做Kubernetes集群中对象的状态(例如Pod、命名空间、ConfigMap等)。如第一行中所指定,这个API的当前稳定版本为1。

在每一个Kubernetes .yml脚本中,咱们必须使用kind关键字定义Kubernetes资源类型(Pods、Deployments、Service等)。所以,你能够看到,咱们在第2行中定义了咱们想使用Deployment资源。

Kubernetes容许您向资源中添加一些元数据。这样一来,您就能够更轻松地识别、过滤和参考资源。

在第5行中,咱们定义了该资源的规范。在第8行中,咱们指定此Deployment应仅应用于标签为app:node-user-service-pod的资源中,在第9行中能够看出咱们想要建立同一Pod的3个副本。

Template(从第10行开始)定义了Pod。在这里,咱们将标签app:node-user-service-pod添加到每一个Pod。这样,Deployment将识别它们。在第16和17行中,咱们定义了应在pod内部运行哪一种Docker容器。如您在第17行中看到的那样,咱们将使用Azure容器镜像仓库中的Docker镜像,该镜像是在上一节中构建并推送的。

咱们还能够为Pod定义资源限制,避免Pod资源不足(当其中一个Pod使用全部资源而其余Pod没法使用它们时)。此外,当您为Pod中的容器指定资源请求时,调度程序将使用此信息来决定将Pod放置在哪一个节点上。当您为容器指定资源限制时,kubelet会强制执行这些限制,从而不容许运行中的容器使用超出您设置的资源限制。kubelet还至少保留该系统资源的“请求”量。请注意,若是您没有足够的硬件资源(例如CPU或内存),则永远没法调度pod。

最后一步是定义用于通讯的端口。在本例中,咱们使用端口3000。此端口号应与Dockerfile中暴露的端口号相同。

MongoDB

MongoDB数据库的Deployment脚本很是类似。惟一的区别是咱们必须指定卷挂载(数据会被保存到节点上的文件夹中)。

apiVersion: apps/v1  
kind: Deployment  
metadata:  
  name: user-db-deployment  
spec:  
  selector:  
    matchLabels:  
      app: user-db-app  
  replicas: 1  
  template:  
    metadata:  
      labels:  
        app: user-db-app  
    spec:  
      containers:  
        - name: mongo  
          image: mongo:3.6.4  
          command:  
            - mongod  
            - "--bind_ip_all"  
            - "--directoryperdb"  
          ports:  
            - containerPort: 27017  
          volumeMounts:  
            - name: data  
              mountPath: /data/db  
          resources:  
            limits:  
              memory: "256Mi"  
              cpu: "500m"  
      volumes:  
        - name: data  
          persistentVolumeClaim:  
            claimName: static-persistence-volume-claim-mongo

在本例中,咱们直接从DockerHub使用了官方MongoDB镜像(第17行)。在第24行中定义了卷安装。在讨论Kubernetes持久卷时,咱们将在下一篇文章中解释最后四行。

建立用于网络访问的服务

如今咱们已经启动了Pod,并开始定义容器之间以及与外部世界的通讯。为此,咱们须要定义一个服务。Service与Deployment之间的关系是一对一的,所以对于每一个Deployment,咱们都应该有一个Service。Deployment还能够管理Pod的生命周期,而且负责监控它们,而Service负责启用对一组Pod的网络访问。

apiVersion: v1  
kind: Service  
metadata:  
  name: node-user-service  
spec:  
  type: ClusterIP  
  selector:  
    app: node-user-service-pod  
  ports:  
    - port: 3000  
      targetPort: 3000

这个.yml脚本的重要部分是selector,它定义了如何识别要今后Service引用的Pod(由Deployment建立)。在第8行中咱们能够看到的,Selector 为app:node-user-service-pod,由于先前定义的Deployment中的Pod被标记为这样。另外一个重要的事情是定义容器端口和服务端口之间的映射。在这种状况下,传入请求将使用第10行中定义的3000端口,并将它们路由到第11行中定义的端口。

MongoDB pod的Kubernetes Service脚本很是类似。咱们只须要更新Selector和端口。

apiVersion: v1  
kind: Service  
metadata:  
  name: user-db-service  
spec:  
  clusterIP: None  
  selector:  
    app: user-db-app  
  ports:  
    - port: 27017  
      targetPort: 27017

配置外部流量

为了与外界通讯,咱们须要定义一个Ingress Controller并使用Ingress Kubernetes资源指定路由规则。

要配置NGINX ingress controller,咱们将使用能够如下连接中的脚本:

https://github.com/CzakoZoltan08/StupidSimpleKubernetes-AKS/blob/master/manifest/ingress-controller/nginx-ingress-controller-deployment.yml

这是一个通用脚本,无需修改便可应用(详细解释NGINX Ingress Controller不在本文讨论范围以内)。

下一步是定义“负载均衡器”,该负载均衡器将用于使用公共IP地址路由外部流量(云提供商提供负载均衡器)。

kind: Service  
apiVersion: v1  
metadata:  
  name: ingress-nginx  
  namespace: ingress-nginx  
  labels:  
    app.kubernetes.io/name: ingress-nginx  
    app.kubernetes.io/part-of: ingress-nginx  
spec:  
  externalTrafficPolicy: Local  
  type: LoadBalancer  
  selector:  
    app.kubernetes.io/name: ingress-nginx  
    app.kubernetes.io/part-of: ingress-nginx  
  ports:  
    - name: http  
      port: 80  
      targetPort: http  
    - name: https  
      port: 443  
      targetPort: https

如今咱们已经启动并运行了Ingress controller和负载均衡器,因而咱们能够定义Ingress Kubernetes资源来指定路由规则。

apiVersion: extensions/v1beta1  
kind: Ingress  
metadata:  
  name: node-user-service-ingress  
  annotations:  
    kubernetes.io/ingress.class: "nginx"  
    nginx.ingress.kubernetes.io/rewrite-target: /$2  
spec:  
  rules:  
    - host: stupid-simple-kubernetes.eastus2.cloudapp.azure.com  
      http:  
        paths:  
          - backend:  
              serviceName: node-user-service  
              servicePort: 3000  
            path: /user-api(/|$)(.*)  
          # - backend:  
          #     serviceName: nestjs-i-consultant-service  
          #     servicePort: 3001  
          #   path: /i-consultant-api(/|$)(.*)

在第6行中,咱们定义了Ingress Controller类型(这是Kubernetes的预约义值;Kubernetes当前支持和维护GCE和nginx controller)。

在第7行中,咱们定义了重写目标规则,在第10行中,咱们定义了主机名。

对于应该从外部访问的每一个服务,咱们应该在路径列表中添加一个条目(从第13行开始)。在此示例中,咱们仅为NodeJS用户服务后端添加了一个条目,可经过端口3000对其进行访问。/ user-api惟一标识咱们的服务,所以任何以stupid-simple-kubernetes.eastus2.cloudapp azure.com/user-api开头的请求将被路由到此NodeJS后端。若是要添加其余服务,则必须更新此脚本(请参见注释掉的代码)。

应用.yml脚本

要应用这些脚本,咱们将使用kubectl。应用文件的kubectl命令以下:

kubectl apply -f

在本例中,若是你在Stupid Simple Kubernetes repo的根文件夹中,您须要执行如下命令:

kubectl apply -f .\manifest\kubernetes\deployment.yml  
kubectl apply -f .\manifest\kubernetes\service.yml  
kubectl apply -f .\manifest\kubernetes\ingress.yml  
kubectl apply -f .\manifest\ingress-controller\nginx-ingress-controller-deployment.yml  
kubectl apply -f .\manifest\ingress-controller\ngnix-load-balancer-setup.yml

应用这些脚本后,一切准备就绪,进而咱们能够从外部调用后端(如使用Postman)。

总结

在本教程中,咱们学习了如何在Kubernetes中建立各类资源,例如Pod、Deployment、Services、Ingress和Ingress Controller。咱们使用MongoDB数据库建立了一个NodeJS后端,并使用3个pod的副本容器化并部署了NodeJS和MongoDB容器。

在下一篇文章中,咱们将了解持久保存数据的问题,并将介绍Kubernetes中的持久卷。

做者简介

Czako Zoltan,一位经验丰富的全栈开发人员,在前端,后端,DevOps,物联网和人工智能等多个领域都拥有丰富的经验。