Nginx应用与运维——Nginx在Kubernetes中的应用(一)

Nginx在Kubernetes中的应用

Kubernetes简称k8s,是Google开源的分布式容器管理系统,它的核心功能是如何自动化部署、扩展和管理运行于容器中的应用软件,实现对容器的部署、网络管理、负载调度、节点集群和资源的扩缩容等自动化管理功能。Kubernetes v1.0版本发布于2015年7月,虽然发布时间不长,但在IT技术领域产生了很大影响,被誉为"云时代的Linux"​。Kubernetes支持Docker、Rocket和Hyper-v容器引擎,其中Docker容器引擎是基于Go语言开发的,它基于宿主机中操作系统上的进程级别虚拟化技术,直接利用宿主机的系统资源,比虚拟系统级别的虚拟化技术少了虚拟系统的中间层调用,具有资源占用低、镜像体积小、加载速度快等优点。

在Kubernetes系统中的服务对外发布方案中,使用了基于Nginx的应用层代理、负载方案,基于Nginx的高稳定路由代理、模块化、可编程等特性,使Kubernetes对集群中运行于容器中的应用程序具有了更加灵活的应用层,可提供对外访问的管理能力。Kubernetes目前仍处于活跃开发状态,功能迭代频繁,不同版本间会存在一些差异。本章将以运行于CentOS 7操作系统,基于Docker容器引擎的Kubernetes最新版本v1.15介绍Nginx在Kubernetes集群中的集成应用。

1、Kubernetes简介

1.1、Kubernetes架构简述

Kubernetes是分布式容器管理系统,它提供了对容器快速部署、网络规划、负载调度及宿主机节点自动化更新和维护的管理机制,使容器自动化按照用户期望的方式运行。与大多数分布式系统一样,Kubernetes集群由主节点(Master)和多个从节点(Node)组成,集群中运行多个应用组件,是计算、存储、网络资源的集合,为运行的各种应用提供资源管理、调度和维护等功能。Kubernetes架构如图所示。

具体说明如下。

  • Kubernetes集群中被操作控制的资源称为资源对象,如Pod、Node、Service都被看作资源对象。
  • kubectl是Kubernetes的命令行管理工具,该工具与Web UI一样,可以通过Kuber-netes主节点的接口服务(API Server)查看及进行创建、删除或更新资源对象等操作。
  • Kubernetes主节点的核心组件包括接口服务(APIServer)、调度服务(Scheduler)、控制管理服务(Controller Manager)和存储服务(etcd)。
  • 接口服务提供了资源对象操作的统一入口,提供认证、授权、访问控制等功能,以REST API方式对外提供服务,允许各类组件创建、删除、更新或监视资源。
  • 调度服务按照编排的调度策略将运行容器(Pod)根据集群资源和状态选择合适的Node进行创建。
  • 控制管理服务负责维护整个集群的状态,包括滚动更新、自动扩缩容、故障检测等。
  • 存储服务用于存储整个集群各种配置及资源实例的状态,实现配置共享和服务发现等功能。

一个Kubernetes集群可包括多个Node节点,每个Node节 点上运行了网络、代理及管理组件。Kubernetes节点架构如图所示。

具体说明如下。

  • kube-proxy负责为Pod提供网络代理和负载均衡等功能。
  • Kubernetes并未提供专门的网络组件实现网络功能,目前常用的是flannel,它通过CNI(Container NetworkInterface,容器网络接口)方式与Kubernetes集成,提供网络功能。
  • kubelet是运行在每个节点上的节点代理服务,可以实现每个节点上的Pod管理及监控,并接收主节点组件下发的各种管理任务。
  • Pod是Kubernetes中最基本的管理单位,是在Docker容器上的一层封装,一个Pod可包含多个容器,可以实现内部运行容器的资源共享。
  • CRI(Container Runtime Interface,容器运行时接口)是Kubernetes中用来与底层容器(如Docker)进行通信的接口,可对容器执行启停等操作。

1.2、Kubernetes相关术语

Kubernetes提供了容器运行时各种资源自动化调度与管理的强大功能,为应对各种复杂环境的自动化管理操作,Kubernetes系统中定义了很多术语。本章涉及的术语可分为资源对象、资源控制器、资源配置和管理工具4类。

1.2.1、资源对象

资源对象指Kubernetes中所有被管理的资源,如Pod、节点、服务都属于资源对象。可以通过接口服务对Kubernetes集群中的所有资源对象进行增、删、改、查操作,资源对象相关数据被持久化保存在存储服务中。

Kubernetes中被管理的资源对象会因版本不同而变化,本章涉及的资源对象如下。

  • (1)Pod
    Pod是Kubernetes中的核心资源,每个Pod可包含多个容器,每个运行的Pod由一个名为pause的沙盒容器(Sandbox Container,也称基础容器)与一个或多个应用容器组成。基础容器为Pod中的应用容器提供如下功能。
    • 共享PID命名空间,Pod中的应用程序可以查看彼此的进程ID。
    • 共享网络命名空间,Pod中的不同容器共同使用一个IP和端口范围。
    • 共享IPC命名空间,Pod中的不同容器的应用可以使用SystemV IPC或POSIX消息队列进行通信。
    • 共享UTS命名空间,Pod中的所有容器共享同一个主机名及共享Pod级别定义的存储卷(Volume)。
  • (2)Node
    Node是指Kubernetes集群中运行Pod的宿主机,每个Node都会运行节点代理服务(kubelet),负责各Node运行Pod容器的管理、监控,并向主节点汇报运行容器的状态,同时接收并执行主节点下发的任务。通过管理工具可对Node资源进行添加、删除及隔离等操作。
  • (3)标签
    标签(Label)是与资源对象关联的键值对,用以标识资源实例的特征,方便用户对资源实例通过自定义标识进行归类。标签键(key)的长度最多63个字符,必须以字母或数字为开始和结束的字符,中间可以有"_""-""."做连接符。标签键值(value)的长度最多63个字符,必须以字母或数字为首字符,也可以为空。每个资源实例与标签是多对多的关系,可以在资源实例初始时定义标签,也可以动态添加或删除。
  • (4)注解
    注解(Annotation)也是与资源对象关联的键值对,用以对资源实例的内置属性进行描述。注解键值对不能用于资源实例的标识及选择,但资源实例被描述的属性数据可以被管理工具或系统扩展使用。注解键值对可以使用标签不允许的字符,可用于资源实例运行时的外部设置,使资源实例在注解值不同时实现不同的运行状态。
  • (5)服务
    服务(Service)定义了由多个具有相同服务名称标签的Pod组成的虚拟网络集群,其负责虚拟集群内Pod的负载均衡和自动发现,每个服务会被分配一个全局唯一固定的虚拟IP(Cluster IP),Kubernetes集群内的所有应用都可以通过Cluster IP与这个服务实现TCP通信。
  • (6)端点
    资源对象端点(Endpoint)表示一个由Pod IP和端口组成的可被访问的网络访问点,是构成Service的基础单位,每个Service负责实现端点列表中端点的负载均衡和网络请求转发。
  • (7)配置映射
    配置映射(ConfigMap)提供了一种类似于配置中心的配置使用方法。应用容器的内容是在制作镜像时打包好的,如果要修改容器的内容,通常都需要重新制作镜像。日常使用中,会遇到很多只简单修改应用配置文件的需求,通过配置映射将配置变量或文件存储在存储服务etcd中,应用容器可以在运行的系统中以环境变量或挂载文件的方式使用这些配置变量。
  • (8)命名空间
    命名空间(Namespace)是Kubernetes集群用于对资源进行管理的逻辑集合,不同命名空间的资源实例逻辑间是彼此隔离的。不同命名空间可通过设定资源配额、网络策略、RBAC策略进行资源实例的管控。网络策略需要网络插件的支持,如Flannel并没有提供网络策略的支持,所以无法实现网络隔离。
1.2.2、资源控制器

资源控制器用以实现每个资源对象的具体操作,每个资源对象都由对应的控制器进行管理和控制。Kubernetes通过各种控制器跟踪和对比存储服务中已保存资源实例的期望状态与当前集群中运行的资源实例的实际状态的差异来实现自动控制和纠错。

Kubernetes通过管理控制服务来管理资源对象,管理控制服务由一系列资源控制器(Controller)组成,本章涉及的控制器有如下几种。

  • (1)副本控制器
    副本控制器(Replication Controller, RC)与进程管理器类似,用以监控集群中所有节点上的Pod,并确保每个Pod都有设定数量的副本在运行。如果运行的Pod数量大于设定的数量,则关闭多余的Pod;反之,则启用足够数量的新Pod。
  • (2)副本集
    副本集(Replica Set, RS)在RC原有功能的基础上提供了更多的增强工具,它主要被部署控制器(DeploymentController)作为协调Pod创建、删除和更新使用。RS也被称为下一代副本控制器,官方已经推荐使用部署控制器管理RS(而不是RC)。
  • (3)部署控制器
    部署控制器用来管理无状态应用,它通过资源对象Deployment的配置与RS组合来管理Pod的多个副本,确保Pod按照资源配置描述的状态运行。部署控制器完成资源对象Deployment实例的创建过程,由RS协助实现Pod副本的创建,并随时监控Deployment资源实例的部署状态,当部署状态不稳定时,可将Pod回滚到之前的Deployment资源实例版本。
  • (4)DaemonSet控制器
    DaemonSet控制器可确保以该模式部署的Pod应用,在集群中的每个Node上都有一个Pod副本在运行,如果集群中增加了新的Node,也会自动在该Node创建该应用的Pod副本,常用来部署全局使用的日志采集、监控、系统管理等容器应用。
  • (5)StatefulSet控制器
    StatefulSet控制器是用来管理有状态应用的,能够保证其管理的Pod的每个副本在整个生命周期中名称不变。通常每个Pod在被删除重建或重启后名称(PodName和HostName)都会变化,而StatefulSet控制器可以使Pod副本相关信息不变,也可以按照固定的顺序启动、更新或删除。StatefulSet控制器通常用来解决有状态服务的管理和维护。
  • (6)端点控制器
    端点控制器(Endpoint Controller)负责与服务对应端点列表的生成和维护,监听服务及其对应Pod的变化。服务被创建或修改时,端点控制器根据服务信息获得其所有Pod的IP和端口信息,并创建或更新同名的端点对象列表。当服务被删除时,同名的端点列表也会被删除。kube-proxy服务通过获取每个服务对应的端点列表,实现服务的负载均衡和数据转发配置。
  • (7)服务控制器
    服务控制器(Service Controller)是属于Pod应用对外发布服务的一个接口控制器,可通过ClusterIP、NodePort、LoadBalancer、ExternalName和externalIPs方式实现Pod应用的对外服务访问。服务控制器监听资源对象服务的变化,当服务是LoadBalancer类型时,确保外部的云平台上对该服务对应的LoadBalancer实例被相应地创建、删除及路由转发表的更新。
1.2.3、资源配置

资源配置是由用户编写来描述资源实例期望状态的Yaml格式数据或文本,每个资源对象被通过对应的资源接口创建或修改资源实例,并通过资源控制器使资源实例按照资源配置文件中描述的期望状态运行。

本章涉及资源配置的资源接口版本(apiVersion)、资源类型(kind)、元数据(metadata)、规范(spec)四个部分。

  • (1)资源接口版本

    因Kubernetes本身也在快速迭代,所以Kubernetes每次更新一个版本时,就会为被改变内容的资源接口创建一个新的版本,所以在编写资源配置时,需要先声明被操作资源接口的版本,以确保所描述的操作内容可被正常解析和执行。可使用如下命令查看当前Kubernetes集群接口服务支持的接口版本。

    bash 复制代码
    kubectl api-versions
  • (2)资源类型

    资源类型用以声明需要操作的资源类型名称,资源类型包括一个或多个可被操作的资源对象,常见的资源类型有Service、Deployment、Pod、Ingress。可使用如下命令查看当前Kubernetes集群接口服务可操作的资源对象名称和所属资源类型。

    bash 复制代码
    kubectl api-resources
  • (3)元数据

    元数据用以对当前操作的资源实例进行标识,元数据可以包括实例名称(name)、实例所在命名空间(namespace)、实例标签(label)、实例注解(Annotation)等信息。

  • (4)规范

    规范用以描述被操作的资源实例在Kubernetes集群中的执行规范和被期望达成的状态。

    资源配置样例如下。

    创建名为nginx-svc的服务资源实例,将资源实例nginx-svc以NodePort类型对外开放端口30080,对应的Service端口为8080, Pod端口为80。

    bash 复制代码
    apiVersion: v1          # 调用资源接口版本为v1
    kind: Service           # 资源类型为Service
    metadata:
        name: nginx-svc # 资源实例名称为nginx-svc
        namespace: webapps      # 资源实例所属命名空间为webapps
        labels:
            app: nginx-svc      # 资源实例的标签为nginx-svc
    spec:
        type: NodePort          # 服务类型为NodePort
        ports:
        - port: 8080            # 服务的端口为8080
          nodePort: 30080       # NodePort对外开放的端口为30080
          targetPort: 80        # Pod应用的端口为80
        selector:
            app: nginx-web      # 服务用于筛选对应Pod的标签名为nginx-web
1.2.4、管理工具

管理工具是用于与Kubernetes交互来实现资源对象操作的执行程序,资源配置文件就是通过管理工具提交给Kubernetes接口服务完成相关资源对象操作的。

  • (1)集群部署工具kubeadm

    kubeadm是Kubernetes官方推荐的部署工具之一,可以实现Kubernetes集群容器化的快速部署。Master节点只需执行kubeadm init即可完成Master组件的自动化部署,Node节点只需执行kubeadm join即可完成加入指定Kubernetes集群的操作。执行kubeadm init命令时自动执行如下动作。

    • 1)系统环境检查。
    • 2)生成Master token。
    • 3)生成自签名的CA和Client证书。
    • 4)生成kubeconfig用于kubelet服务连接API server。
    • 5)初始化并启动kubelet服务。
    • 6)为Master各组件生成静态Pod配置(Static Podmanifests)并创建Pod应用,Master组件运行命名空间为kube-system。
    • 7)配置RBAC。
    • 8)添加kube-proxy和CoreDNS附加服务。

    该命令的其他参数如下。

    bash 复制代码
    # 初始化主节点
    kubeadm init
    
    # 查看token
    kubeadm token list
    
    # 重新生成token
    kubeadm token generate
    
    # 清空kubeadm设置
    kubeadm reset
  • (2)资源管理工具kubectl

    kubectl是Kubernetes的资源管理客户端程序,可以通过Kubernetes Master的接口服务(API Server)查看及进行创建、删除或更新资源对象等操作。通常建议在非Master节点主机运行或在Master节点上以非root权限用户运行。当Master节点被kubeadm初始化成功后,会提示将/etc/kubernetes/admin.conf复制到kubectl控制机或非root用户的Home目录中。该命令的其他参数如下。

    bash 复制代码
    # 查看节点状态
    kubectl get nodes
    
    # 查看集群状态
    kubectl get cs
    
    # 查看所有事件
    kubectl get events --all-namespaces
    
    # 查看所有Pod
    kubectl get pods --all-namespaces -o wide
    
    # 查看所有服务
    kubectl get services --all-namespaces -o wide
    
    # 扩缩容,将以deployment部署方式部署的Pod资源实例nginx的副本数设定为3
    kubectl scale --replicas=3 deployment/nginx
    
    # 编辑配置,编辑资源对象Service实例名为nginx的资源配置
    kubectl edit service/nginx

    为了更方便地扩展资源管理工具的功能,kubectl通过插件机制允许开发者以独立文件的形式发布自定义的kubectl子命令。kubectl插件可以使用任意语言开发,可以是一个Bash或Python的脚本,也可以是其他语言开发编译的二进制可执行文件,只要最终将脚本或二进制可执行文件以kubectl-为前缀命名,并存放到/root/.krew/bin/目录中即可。使用kubectl plugin list命令可以查看有哪些插件。krew是kubectl插件的管理器,使用krew可以轻松查找、安装和管理kubectl插件。krew本身也是一个kubectl插件。krew相关的命令如下。

    bash 复制代码
    # 安装kubectl插件krew
    curl -fsSLO "https://storage.googleapis.com/krew/v0.2.1/krew.{tar.gz,yaml}"
    
    tar zxvf krew.tar.gz
    ./krew-linux_amd64 install --manifest=krew.yaml --archive=krew.tar.gz
    echo "export PATH=\"\${KREW_ROOT:-\$HOME/.krew}/bin:\$PATH\"" >>/etc/profile
    source /etc/profile
    
    # 更新插件列表
    kubectl krew update
    
    # 查看插件列表
    kubectl krew list
  • (3)应用部署工具Helm

    Helm并非官方提供的工具,而是Deis公司(已被微软收购)开发的用于Kubernetes下应用部署、更新、卸载的管理工具。Helm类似于Linux操作系统中的包管理工具,如CentOS下使用的yum。Helm让Kubernetes的用户可以像安装软件包一样,轻松查找、部署、升级或卸载各种应用。Helm的工作逻辑如图所示。

    关于Helm的几点说明如下。

    • Helm管理的安装包被称为Chart。
    • Chart存储在远端的Charts仓库(Repository)。
    • Tiller是Helm的服务端,以Pod方式部署在Kubernetes中,负责接收Helm客户端的控制命令,解析Chart并调用接口服务完成应用的部署和配置。

    常用的Helm命令如下。

    bash 复制代码
    # 初始化Helm
    helm init
    
    # 查看当前安装的应用
    helm list
    
    # 安装应用
    helm install --namespace kubeapps --name kubeapps bitnami/kubeapps
    
    # 删除应用
    helm delete --purge kubeapps

1.3、Kubernetes集群部署

Kubernetes集群支持多种方式部署,kubeadm是Kubernetes官方提供的用于快速部署Kubernetes集群的工具,本节将使用kubeadm实现Kubernetes集群样例的快速部署。部署规划如表所示。

1.3.1、系统初始化

分别在Master和Node主机进行系统初始化,此处使用的操作系统版本为CentOS 7.2。

bash 复制代码
# 关闭setenforce
setenforce 0
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config

# 关闭默认防火墙
systemctl stop firewalld
systemctl disable firewalld

# 配置hosts,实现本地主机名解析
echo "10.10.4.17 vm417centos-master.kube
10.10.4.26 vm426centos-node01.kube" >> /etc/hosts

# 配置系统内核参数,因网桥工作于数据链路层,数据默认会直接经过网桥转发,为避免iptables的FORWARD
# 设置失效,需要启用bridge-nf机制
cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
vm.swappiness=0
EOF

# 使内核参数配置生效
sysctl --system

# 关闭交换内存,如果不关闭,kubelet服务将无法启动
swapoff -a

# 安装docker-ce, Kubernetes与Docker存在版本兼容问题,Kubernetes最新版本v1.15,最高支持
# Docker 18.09版本,所以需要安装指定的Docker版本
yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce-18.09.0-3.el7 docker-ce-cli-18.09.0-3.el7 containerd.io-1.2.0-3.el7 ebtables ethtool
systemctl enable docker
systemctl start docker

# 优化Docker cgroup驱动,Kubernetes文档指出,使用systemd作为init system的Linux系统中,
# cgroup driver为systemd模式可以确保服务器节点在资源紧张时的稳定性
yum install -y systemd
cat >/etc/docker/daemon.json<<EOF
{
    "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
systemctl restart docker

# 查看确认
docker info | grep Cgroup

# 配置kubernetes yum源,用以安装Kubernetes基础服务及工具,此处使用阿里云镜像仓库源
cat > /etc/yum.repos.d/kubernetes.repo <<EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
EOF

# 安装Kubernetes基础服务及工具
yum install -y kubeadm kubelet kubectl kompose kubernetes-cni
systemctl enable kubelet.service
1.3.2、部署Master节点

Master节点理论上只需要接口服务、调度服务、控制管理服务、状态存储服务,但kubeadm以Pod形式部署Master组件,所以在Master节点主机上仍需要部署kubelet服务,kubeadm在初始化时会自动对kubelet服务进行配置和管理。

bash 复制代码
# 设置主机名,kubeadm识别主机名时有严格的规范,主机名中需要有"-"或"."
hostnamectl --static set-hostname vm417centos-master.kube

# 使用kubeadm初始化Master节点,建议使用阿里云镜像仓库
kubeadm init  --pod-network-cidr=172.172.0.0/16 \       # 设置Pod网段IP为172.172.0.0/16
              --image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \                                         # 设置从阿里云镜像仓库下载
              --kubernetes-version v1.15.1  # 下载Kubernetes的v1.15.1版本

Master节点初始化成功后,会提示成功并输出token和discovery-token-ca-cert-hash,用于将Node加入所指定Master的Kubernetes集群。Kubernetes本身并没有集成网络功能,需要单独安装网络插件实现Kubernetes集群中Pod的网络功能,此处安装网络组件Flannel。

bash 复制代码
# 初始化kubectl配置,建议在非root或单独的管理机上配置kubectl管理环境
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
source ~/.bash_profile

# 获取网络组件Flannel的资源配置文件
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

# 修改Pod网段IP为自定义的172.172.0.0/16
sed -i "s#10.244.0.0/16#172.172.0.0/16#g" kube-flannel.yml

# 创建应用
kubectl apply -f kube-flannel.yml

网络组件安装后,可以在网络接口上看到cni0和flannel.1,如图所示。

用如下命令可以查看主节点运行Pod的状态。

bash 复制代码
kubectl get pods --all-namespaces -o wide
1.3.3、部署Node
bash 复制代码
# 设置主机名,kubeadm识别主机名时有严格的规范,主机名中需要有"-"或"."
hostnamectl --static set-hostname vm426centos-node01.kube

# 加入Kubernetes集群
kubeadm join 10.10.4.17:6443 --token rk1zux.esj6fnjz3xlms3rv \
    --discovery-token-ca-cert-hash sha256:f8371d489b9f67f630199a03754ceffa83d850f06db039a60fc9b170c20e5826

# 在Master节点通过命令查看节点状态
kubectl get nodes
1.3.4、部署kubernetes-dashboard

kubernetes-dashboard是Kubernetes社区中一个很受欢迎的项目,它为Kubernetes用户提供了一个可视化的Web前端,通过Web前端可以查看当前集群的各种信息,为用户管理维护Kubernetes集群提供帮助。

bash 复制代码
# 获取资源配置文件
wget https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml

# 修改镜像仓库为阿里云仓库
sed -i "s/k8s.gcr.io/registry.cn-hangzhou.aliyuncs.com\/google_containers/g" kubernetes-dashboard.yaml

# 设置端口映射方式为NodePort,映射端口为31443
sed -i '/spec:/{N;s/  ports:/  type: NodePort\n&/g}' kubernetes-dashboard.yaml
sed -i "/targetPort: 8443/a\      nodePort: 31443" kubernetes-dashboard.yaml

# 部署Pod应用
kubectl apply -f kubernetes-dashboard.yaml

kubernetes-dashboard有Kubeconfig和Token两种认证登录方式,此处选择Token方式认证登录。此处Kubernetes的资源类型---服务账户(Service Account)创建admin-user账户并授权为Cluster-Role的管理角色。

bash 复制代码
# 创建admin-user账户及授权的资源配置文件
cat>dashboard-adminuser.yml<<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
    name: admin-user
    namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
    name: admin-user
roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kube-system
EOF

# 创建资源实例
kubectl create -f dashboard-adminuser.yml

# 获取账户admin-user的Token用于登录
kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')

kubernetes-dashboard的Pod运行成功后,可以在浏览器上通过集群中的任意Node IP和31443端口访问kubernetes-dashboard,通过Token登录后就可以通过Web界面进行Kubernetes集群的管理和维护。

1.3.5、部署管理工具Helm

Helm客户端程序需要使用Kubernetes管理工具kubectl,所以要先确认安装Helm主机的kubectl可用,如果不可用则需要先安装。

  • (1)安装kubectl
    配置样例如下:

    bash 复制代码
    # 配置Kubernetes安装源
    cat > /etc/yum.repos.d/kubernetes.repo <<EOF
    [kubernetes]
    name=Kubernetes
    baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
    enabled=1
    gpgcheck=0
    EOF
    
    # 安装kubectl
    yum install -y kubectl
    
    # 初始化配置目录
    mkdir -p $HOME/.kube
    
    # 将Master节点主机的文件/etc/kubernetes/admin.conf复制到kubectl控制机
    scp Master:/etc/kubernetes/admin.conf $HOME/.kube/config
  • (2)安装Helm
    配置样例如下:

    bash 复制代码
    # 下载Helm客户端
    wget https://get.helm.sh/helm-v2.14.2-linux-amd64.tar.gz
    tar -zxvf helm-v2.14.2-linux-amd64.tar.gz
    mv linux-amd64/helm /usr/sbin/
    mv linux-amd64/tiller /usr/sbin/
    helm help
    
    # 添加阿里云仓库
    helm repo add aliyun-stable https://acs-k8s-ingress.oss-cn-hangzhou.aliyuncs.com/charts
    helm repo update
    
    # 将Tiller应用安装到Kubernetes集群并使用阿里云的charts仓库
    helm init --upgrade -i registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.14.2 --stable-repo-url https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
    
    # 添加Tiller授权
    kubectl create serviceaccount --namespace kube-system tiller
    kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
    kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template": {"spec":{"serviceAccount":"tiller"}}}}'
  • (3)安装Helm的Web管理工具Kubeapps
    Kubeapps是Helm的Web化管理工具,提供了比命令行更丰富的应用安装说明和更便捷的安装方式。

    bash 复制代码
    # 添加bitnami的charts仓库
    helm repo add bitnami https://charts.bitnami.com/bitnami
    
    # 安装Kubeapps,命名为kubeapps,所属命名空间为kubeapps
    helm install --namespace kubeapps --name kubeapps bitnami/kubeapps
    
    # 创建Kubeapps账号
    kubectl create serviceaccount kubeapps-operator
    kubectl create clusterrolebinding kubeapps-operator --clusterrole=cluster-admin --serviceaccount=default:kubeapps-operator
    
    # 创建服务,提供NodePort类型的访问端口30080
    cat>kubeapps-service.yml<<EOF
    apiVersion: v1
    kind: Service
    metadata:
        name: kubeapps-svc
        namespace: kubeapps
        labels:
            app: kubeapps
    spec:
        type: NodePort
        ports:
        - port: 8080
          nodePort: 30080
        selector:
            app: kubeapps
    EOF
    
    # 在集群中创建资源实例
    kubectl create -f kubeapps-service.yml
    
    # 获取登录token
    kubectl get secret $(kubectl get serviceaccount kubeapps-operator -o jsonpath= '{.secrets[].name}') -o jsonpath='{.data.token}' | base64 --decode

在浏览器上通过端口30080就可以访问应用Kubeapps。

1.4、Kubernetes网络通信

计算机间的信息和数据在网络中必须按照数据传输的顺序、数据的格式内容等方面的约定或规则进行传输,这种约定或规则称作协议。各种网络协议分布于不同的网络分层中,网络分层分为OSI七层模型和TCP/IP五层模型两种。TCP/IP五层模型分别是应用层、传输层、网络层、链路层和物理层,其中应用层对应于OSI七层模型中的会话层、表示层、应用层,这也是二者的区别。计算机网络数据是按照协议规范,采用分层的结构由发送端自上而下流动到物理层,再从物理层在网络分层中自下而上流动到接收端的应用层完成数据通信。网络分层中,高层级的应用模块仅利用低层级应用模块提供的接口和功能,低层级应用模块也仅使用高层级应用模块传来的参数响应相关操作,层次间每个应用模块都可能被提供相同功能的应用模块替代。Kubernetes网络通信也遵守TCP/IP五层模型的定义,通过不同的资源对象在相应的层级提供相应的模块功能。Kubernetes资源对象在相应的网络层级与传统网络设备模块的对照表如表所示。

1.4.1、Docker网络模式

Kubernetes是基于容器的管理系统,其使用的Docker容器版本的Pod由多个Docker容器组成,因此为便于理解Pod的网络通信方式,应首先了解Docker自有的网络模式。Docker容器有如下4种常见的网络模式。

  • 主机模式(host)。该模式下,因为容器与宿主机共享网络命名空间(network name-space, netns),所以该容器中可以共享使用宿主机的所有网卡设备。使用者可以通过访问宿主机IP,访问容器中运行应用的所有网络端口。主机模式下网络传输效率最高,但宿主机上已经存在的网络端口无法被容器使用。
  • 无网卡模式(none)。该模式下,容器中只有环回(Lookback, lo)接口,运行在容器内的应用仅能使用环回接口实现网络层的数据传输。
  • 桥接模式(bridge)。该模式下,容器内会被创建Veth(Virtual ETHernet)设备并接入宿主机的桥接网络,通过宿主机的桥接网络,容器内部应用可与宿主机及宿主机中接入同一桥接设备的其他容器应用进行通信。
  • Macvlan网络模式(macvlan),当宿主机的网络存在多个不同的VLAN时,可以通过该模式为容器配置VLAN ID,使该容器与宿主机网络中同一VLAN ID的设备实现网络通信。

Docker容器间可以通过IP网络、容器名解析、joined容器3种方式实现通信。IP网络是在网络联通的基础上通过IP地址实现互访通信。容器名解析是在网络联通的基础上,由Docker内嵌的DNS进行容器名解析实现的互访通信方式,同一主机桥接模式的容器间需要启动时,可使用--link参数启用这一功能。joined容器方式可以使多个容器共享一个网络命名空间,多个容器间通过环回接口直接通信,这种方式容器间传输效率最高。

1.4.2、Pod内容器间的数据通信

Pod是由多个Docker容器以joined容器方式构成的,多个容器共享由名为pause的容器创建的网络命名空间,容器内的进程彼此间通过环回接口实现数据通信。环回接口不依赖链路层和物理层协议,一旦传输层检测到目的端地址是环回接口地址,数据报文离开网络层时会被返回给本机的端口应用。这种模式传输效率较高,非常适用于容器间进程的频繁通信。

1.4.3、同节点的Pod间数据通信

每个Pod拥有唯一的IP和彼此隔离的网络命名空间,在Linux系统中,Pod间跨网络命名空间的数据通信是通过Veth设备实现的。Veth设备工作在链路层,总是成对出现,也被称为Veth-pair设备。在网络插件是Flannel的虚拟网络结构中,Flannel在被Kubernetes触发、接收到相关Pod参数时,会为Pod创建Veth设备并分配IP, Veth设备一端是Pod的eth0接口,一端是Node节点中网络空间名为default的Veth虚拟接口。Flannel在初始安装时,创建了网桥设备cni0,网络空间default中创建的Veth虚拟接口都被加入网桥设备cni0中,相当于所有的Pod都被接入这个虚拟交换机中,在同一虚拟交换机中的Pod实现了链路层的互联并进行网络通信。工作原理如图所示。

可用如下命令查看当前节点服务器的网络命名空间和网桥信息。

bash 复制代码
# 查看系统中的网络命名空间
ls /var/run/docker/netns

# 查看每个命名空间的网络接口信息
nsenter --net=/var/run/docker/netns/default ifconfig -a

# 查看网桥信息
brctl show
1.4.4、跨主机的Pod间数据通信

由CoreOS使用Go语言开发的Flannel实现了一种基于Vxlan(Virtual eXtensible Local Area Network)封装的覆盖网络(Overlay Network),将TCP数据封装在另一种网络包中进行路由转发和通信。

Vxlan协议是一种隧道协议,基于UDP协议传输数据。Flannel的Vxlan虚拟网络比较简单,在每个Kubernetes的Node上只有1个VTEP(Vxlan Tunnel Endpoint)设备(默认为flannel.1)​。Kubernetes集群中整个Flannel网络默认配置网段为10.244.0.0/16,每个节点都分配了唯一的24位子网,Flannel在Kubernetes集群中类似于传统网络中的一个三层交换设备,每个Node节点的桥接设备通过VTEP设备接口互联,使运行在不同Node节点中不同子网IP的容器实现跨Node互通。

可用如下命令查看当前节点服务器的arp信息。

bash 复制代码
# 本地桥arp表
bridge fdb

bridge fdb show dev flannel.1
1.4.5、Pod应用在Kubernetes集群内发布服务

Kubernetes通过副本集控制器能够动态地在集群中任意创建和销毁Node,因为每个Node被分配的子网范围不同,所以Pod IP也会随之变化。Flannel构建的虚拟网络使得集群中的每个Pod在网络上已经实现互联互通,由于Pod IP变化的不确定性,运行在Pod中的应用服务无法被其他应用固定访问。为使动态变化IP的Pod应用可以被其他应用访问,Kubernetes通过标签筛选的形式将具有相同指定标签的一组Pod定义为Service,每个Service的Pod成员信息通过端点控制器在etcd中保存及更新。Service为Pod应用提供了固定的虚拟IP和端口实现固定访问,使得集群内其他Pod应用可以访问这个服务。

Service是四层(TCP/UDP over IP)概念,其构建了一个有固定ClusterIP(集群虚拟IP, Virtual IP)和Port的虚拟集群,每个节点上运行的kube-proxy进程通过主节点的接口服务监听资源对象Service和Endpoint内Pod列表的变化。kube-proxy默认使用iptables代理模式,其通过对每个Service配置对应的iptables规则,在集群中的Node主机上捕获到达该Service的ClusterIP和Port的请求,当捕获到请求时,会将访问请求按比例随机分配给Service中的一个Pod,如果被选择的Pod没有响应(取决于readinessprobes的配置)​,则自动重试另一个Pod。Service访问逻辑如图所示。

  • kube-proxy根据集群中Service和Endpoint资源对象的状态初始化所在节点的iptables规则。
  • kube-proxy通过接口服务监听集群中Service和Endpoint资源对象的变化并更新本地的iptables规则。
  • iptables规则监听所有请求,将对应ClusterIP和Port的请求使用随机负载均衡算法负载到后端Pod。

kube-proxy在集群中的每个节点都会配置集群中所有Service的iptables规则,iptables规则设置如下。

  • kube-proxy首先是建立filter表的INPUT规则链和nat表的PREROUTING规则链,将访问节点的流量全部跳转到KUBE-SERVICES规则链进行处理。
  • kube-proxy遍历集群中的Service资源实例,为每个Service资源实例创建两条KUBE-SERVICES规则。
  • KUBE-SERVICES中一条规则是将访问Service的非集群Pod IP交由KUBE-MARK-MASQ规则标记为0x4000/0x4000,在执行到POSTROUTING规则链时由KUBE-POSTROUTING规则链对数据流量实现SNAT。
  • KUBE-SERVICES中另一条规则将访问目标是Service的请求跳转到对应的KUBE-SVC规则链。
  • KUBE-SVC规则链由目标Service端点列表中每个Pod的处理规则组成,这些规则包括随机负载均衡策略及会话保持(Session Affinity)的实现。
  • KUBE-SVC每条规则链命名是将服务名+协议名按照SHA256算法生成哈希值后通过base32对该哈希值再编码,取编码的前16位与KUBE-SVC作为前缀组成的字符串。
  • KUBE-SEP每个Pod有两条KUBE-SEP规则,一条是将请求数据DNAT到Pod IP,另一条用来将Pod返回数据交由KUBE-POSTROUTING规则链实现SNAT。
  • KUBE-SEP每条规则链命名是将服务名+协议名+端口按照SHA256算法生成哈希值后通过base32对该哈希值再编码,取编码的前16位与KUBE-SEP为前缀组成的字符串。

Service的负载均衡是由iptables的statistic模块实现的。statistic模块的random模式可以将被设定目标的请求数在参数probability设定的概率范围内分配,参数设定值在0.0~1.0之间,当参数设定值为0.5时,表示该目标有50%的概率分配到请求。kube-proxy遍历Service中的Pod列表时,按照公式1.0/float64(n-i)为每个Pod计算概率值,n是Pod的总数量,i是当前计数。当有3个Pod时,计算值分别为33%、50%、100%,3个Pod的总流量负载分配分别为33%、35%、32%。

Service也支持会话保持功能,是应用iptables的recent模块实现的。recent允许动态创建源地址列表,并对源地址列表中匹配的来源IP执行相应的iptables动作。recent模块参数如表所示。

配置Service会话保持,只需在Service中进行如下配置即可。

bash 复制代码
spec:
    sessionAffinity: ClientIP
    sessionAffinityConfig:
        clientIP:
            timeoutSeconds: 10800

kube-proxy实现Service的方法有4种,分别是userspace、iptables、IPVS和winuser-space。iptables只是默认配置,因kube-proxy的其他实现方式非本书重点,此处不深入探讨。

1.4.6、Pod应用在Kubernetes集群外发布服务

Service实现了Pod访问的固定IP和端口,但ClusterIP并不是绑定在网络设备上的,它只是kube-proxy进程设定的iptables本地监听转发规则,只能在Kubernetes集群内的节点上进行访问。Kubernetes系统默认提供两种方式实现Pod应用向集群外发布服务,一种是基于资源对象Pod的hostPort和hostNetwork方式,另一种是基于资源对象Service的NodePort、Load-Balancer和ExternalIPs方式。

  • (1)hostPort方式

    hostPort方式相当于创建Docker容器时使用-p参数提供容器的端口映射,只能通过运行容器的Node主机IP进行访问,属于资源对象Pod的运行方式,不支持多个Pod的Service负载均衡等功能。资源配置如下:

    bash 复制代码
    apiVersion: v1
    kind: Pod
    metadata:
        name: apps
        labels:
            app: web
    spec:
        containers:
        - name: apps
          image: apache
          ports:
            - containerPort: 80
              hostPort: 8080
  • (2)hostNetwork方式

    hostNetwork方式相当于创建Docker容器时以主机模式为网络模式的Pod运行方式,该方式运行的容器与所在Node主机共享网络命名空间,属于资源对象Pod的运行方式,不支持多个Pod的Service负载均衡等功能。资源配置如下:

    bash 复制代码
    apiVersion: v1
    kind: Pod
    metadata:
        name: nginx-web
        namespace: default
        labels:
            run: nginx-web
    spec:
        hostNetwork: true
        containers:
        - name: nginx-web
          image: nginx
            ports:
            - containerPort: 80
  • (3)NodePort方式

    NodePort方式是在集群中每个节点监听固定端口(NodePort)的访问,外部用户对任意Node主机IP和NodePort的访问,都会被Service负载到后端的Pod,全局NodePort的默认可用范围为30000~32767。NodePort方式访问逻辑如图所示。

    • kube-proxy初始化时,会对NodePort方式的Service在iptables nat表中创建规则链KUBE-NODEPORTS,用于监听本机NodePort的请求。
    • 外部请求访问节点IP和端口(NodePort)后,被iptables规则KUBE-NODEPORTS匹配后跳转给对应的KUBE-SVC规则链执行负载均衡等操作。
    • 选定Pod后,请求被转发到选定的Pod IP和目标端口(targetPod)。

    NodePort方式的资源配置如下:

    bash 复制代码
    apiVersion: v1
    kind: Service
    metadata:
        name: nginx-web
        namespace: default
        labels:
            run: nginx-web
    spec:
        type: NodePort
        ports:
        - nodePort: 31804
          port: 8080
            protocol: TCP
            targetPort: 8080
  • (4)LoadBalancer方式

    LoadBalancer方式是一种Kubernetes自动对外发布的解决方案,该方案是将外部负载均衡器作为上层负载,在创建Service时自动与外部负载均衡器互动,完成对Kubernetes Service负载均衡创建的操作,将Service按照外部负载均衡器的负载策略对外提供服务。该方案依赖外部负载均衡器的支持,阿里云、腾讯云等的容器云都提供了对这个方案的支持。资源配置如下:

    bash 复制代码
    apiVersion: v1
    kind: Service
    metadata:
        name: nginx-web
        namespace: default
        labels:
            run: nginx-web
    spec:
        type: LoadBalancer
        ports:
        - port: 8080
          protocol: TCP
          targetPort: 8080
    • 不同的外部负载均衡器需要有对应的负载均衡控制器(Loadbalancer Controller)。
    • 负载均衡控制器通过接口服务实时监听资源对象Service的变化。
    • LoadBalancer类型的Service被创建时,Kubernetes会为该Service自动分配Node-Port。
    • 当监听到LoadBalancer类型的Service创建时,负载均衡控制器将触发外部负载均衡器(LoadBalancer)创建外部VIP、分配外部IP或将现有节点IP绑定NodePort端口添加到外部负载均衡器的负载均衡池,完成负载均衡的配置。
    • 当外部用户访问负载均衡器的外部VIP时,外部负载均衡器会将流量负载到Kubernetes节点或Kubernetes集群中的Pod(视外部负载均衡器的功能而定)。
    • 不能与NodePort方式同时使用。
  • (5)ExternalIPs方式

    ExternalIPs方式提供了一种指定外部IP绑定Service端口的方法,该方法可以指定节点内某几个节点IP地址或绑定外部路由到节点网络的非节点IP对外提供访问。Kubernetes通过ExternalIPs参数将被指定的IP与Service端口通过iptables监听,其使用与Service一致的端口,相较于NodePort方式配置更加简单灵活。由于是直接将Service端口绑定被路由的IP对外暴露服务,用户需要将整个集群对外服务的端口做好相应的规划,避免端口冲突。资源配置如下:

    bash 复制代码
    spec:
        externalIPs:
        - 192.168.1.101
        - 192.168.1.102
        ports:
        - name: http
          port: 80
          targetPort: 80
          protocol: TCP
        - name: https
          port: 443
          targetPort: 443
          protocol: TCP
    • ExternalIPs设置的IP可以是集群中现有的节点IP,也可以是上层网络设备路由过来的IP。kube-proxy初始化时,会对ExternalIPs方式的Service在iptables nat表中创建规则链KUBE-SERVICES,用于访问ExternalIPs列表中IP及Service port请求的监听。
    • 外部或本地访问ExternalIPs列表中IP及port的请求被匹配后,跳转给对应的KUBE-SVC规则链执行负载均衡等操作。
1.4.7、Service中Pod的调度策略

Kubernetes系统中,Pod默认是按照资源策略随机部署的,虽然用户可对调度策略进行一定的调整,但Pod的调度策略同样对Pod通信存在一定的影响,相关调度策略有如下两种。

  • (1)部署调度策略(Affinity)

    Kubernetes集群中的Pod被随机调度并创建在集群中的Node上。在实际使用中,有时需要考虑Node资源的有效利用及不同应用间的访问效率等因素,也需要对这种调度设置相关期望的策略。主要体现在Node与Pod间的关系、同Service下Pod间的关系、不同Service下Pod间的关系这3个方面。Node与Pod间的关系可以使用nodeAffinity在资源配置文件中设置,在设置Pod资源对象时,可以将Pod部署到具有指定标签的集群Node上。Pod间的关系可通过podAntiAffinity的配置尽量把同一Service下的Pod分配到不同的Node上,提高自身的高可用性,也可以把互相影响的不同Service的Pod分散到不同的集群Node上。对于Pod间访问比较频繁的应用,可以使用podAffinity配置,尽量把被配置的Pod部署到同一Node服务器上。

  • (2)流量调度策略(externalTrafficPolicy)

    Service的流量调度策略有两种,分别是Cluster和Local。

    Cluster是默认调度策略,依据iptables的随机负载算法,将用户请求负载均衡分配给Pod,但该方式会隐藏客户端的源IP。Local策略则会将请求只分配给请求IP主机中该Service的Pod,而不会转发给Service中部署在其他Node中的Pod,这样就保留了最初的源IP地址。但该方式不会对Service的Pod进行负载均衡,同时被访问IP的Node主机上如果没有该Service的Pod,则会报错。Local策略仅适用于NodePort和LoadBalancer类型的Service。

    Kubernetes中通过Service实现Pod应用访问,在流量调度策略的Cluster调度策略下,对一个Service的访问请求会被随机分配到Service中的任意Pod,即便该Service与发出请求的Pod在同一Node有可提供服务的Pod,也不一定会被选中。在Kubernetes计划的1.16版本中增加了服务拓扑感知的流量管理功能,设计了新的Pod定位器(PodLocator),实现了服务的拓扑感知服务路由机制,使得Pod总能优先使用本地访问的策略找到最近的服务后端,这种拓扑感知服务使本地访问具有更广泛的意义,包括节点主机、机架、网络、机房等,这样可以有效地减少网络延迟,提高访问效率及安全性,更加节约成本。

相关推荐
苏三的开发日记1 小时前
百度网盘p2p加速文件存储位置修改记录
运维
LabVIEW开发2 小时前
Anritsu 2602A:频谱仪的几种测量怎么自动跑
运维·服务器·labview·labview知识·labview功能·labview程序
2401_868534782 小时前
存储系统规划设计
运维·服务器·智能路由器
螺蛳粉 螺蛳粉2 小时前
第一篇:Keepalived 高可用实战:VIP 漂移与 Nginx 主备切换完整指南
运维·nginx·负载均衡·keepalived·高可用
荣合技术服务2 小时前
VMware ESXi 虚拟化平台服务器虚拟机数据恢复服务
linux·运维·服务器
爱吃香菜的初学者2 小时前
十三.Linux——信号量
linux·运维·服务器·开发语言
吴声子夜歌2 小时前
Nginx应用与运维——Nginx在Kubernetes中的应用(三)
运维·nginx·kubernetes
小小龙学IT3 小时前
ROS2 安装完全指南(Ubuntu 版):从零开始到跑通第一个节点本文
linux·运维·ubuntu
Ruiery3 小时前
Linux 6.6内核 CPU 深度解析(十二):SMT 进阶 — core scheduling 与共享算力的负载平衡
linux·运维·服务器