文章目录

1、Kubernetes介绍

1.1 什么是Kubernetes

  • Kubernetes是一个可移植的、可扩展的、开源的平台,用于管理容器化的工作负载和服务,它促进了声明式配置和自动化。它有一个庞大的、快速增长的生态系统。Kubernetes的服务、支持和工具可以广泛使用。

  • Kubernetes这个名字源于希腊语,意思是舵手或飞行员。K8s作为一个缩写词,是计算“K”和“s”之间的八个字母的结果。谷歌于2014年开放Kubernetes项目。Kubernetes结合了谷歌超过15年的大规模运行生产负载的经验,以及来自社区的最佳品种的想法和实践。

 

1.2 容器化的发展历程

在这里插入图片描述

  • 传统部署时代(Traditional Deployment era):早期,APP之间运行在物理服务器上,无法为其中的应用程序定义资源边界,这就导致了资源分配 问题。例如,如果一个物理服务器上运行着多个应用程序,可能会出现一个应用程序占用大部分资源的情况下,从而影响其它程序的性能。如果一个应用程序发生了内存泄漏,最终可能会耗尽服务器的资源,从而导致所有应用程序都挂掉。解决方案是在不同的物理机器上运行每个应用程序。但是这样的话,会导致资源得不到充分利用,而且维护组织很多物理服务器的成本很高,因此这种方式无法扩展。

  • 虚拟化部署时代(Virtualized Deployment era):一种解决方案就是使用虚拟化。它允许在一个物理服务器的CPU上运行多个虚拟机(vm)。虚拟化运行应用程序在虚拟机之间隔离,一个应用程序的信息不能被另一个应用程序自由访问,从而提供了一定安全性。虚拟化可以更好地利用物理服务器中的资源,有着更好的伸缩性,因为可以轻松地添加或更新应用程序,降低硬件成本。使用虚拟化,可以将一组物理资源表示为一次性虚拟机的集群。每个VM都是一台完整的机器,在虚拟硬件之上运行所有组件,包括自己的操作系统,这样也会导致一部分的资源消耗。

  • 容器化部署时代(Container Deployment era):容器类似于vm,但是它们有宽松的隔离属性,可以在应用程序之间共享操作系统(OS)。因此,容器被认为是轻量级的。与vm类型,容器有自己的文件系统、共享CPU、内存、进程空间等。由于它们与底层基础设施解耦,因此可以跨云和OS发行版进行移植。

使用容器的优点

  • 应用程序创建和部署更敏捷:与使用vm相比,容器映像创建更加简单和高效。
  • 持续开发、集成和部署:通过快速高效的回滚,提供可靠和频繁的容器映像构建和部署。
  • 可观察性:不仅显示操作系统级别的信息和指标,还显示应用程序状态和其它信号。
  • 跨平台、测试和生产环境的一致性:在笔记本电脑上和云中运行的环境是一样的。
  • 云和操作系统发行版的可移植性:可在Ubuntu、RHEL、CoreOS、内部部署、主要的公共云以及其它任何地方运行。
  • 以应用程序为中心的管理:将抽象级别从在虚拟硬件上运行操作系统提升到使用逻辑资源在操作系统上运行应用程序。
  • 松散耦合、分布式、弹性、自由的微服务:应用程序被分解为更小、独立的部分,可以动态地部署和管理,而不是在一台大型、单一用途的机器上运行一个庞大的堆栈。
  • 资源隔离:可预测的应用性能。
  • 资源利用:效率高、密度大。

 

1.3 为什么需要K8S以及它能做什么

        容器是捆绑和运行应用程序的好方法。在生产环境中,我们需要管理运行应用程序的容器,并确保没有停机时间。例如,一个容器出现故障,则需要启动另一个容器,如果由系统自动发现来处理那不是更简单吗?这就是K8S拯救我们的方式。K8S为我们提供了一个能够弹性地运行分布式系统的框架。它负责应用程序的扩展和故障转移,提供部署模式等等。

K8S可以提供的服务

  • 服务发现和负载均衡(Service discovery and load balancing):K8s可以使用DNS名称或它们的IP地址开放容器。如果一个容器的流量很大,K8s能够负载均衡和分配网络流量,从而使部署稳定。
  • 存储编排(Storage orchestration):K8s运行我们自动挂载选择的存储系统,如本地存储、公共云提供商等。
  • 自动部署和回滚(Automated rollouts and rollbacks):我们可以使用K8s自动化地为我们的部署创建新的容器,删除现有的容器,并将它们所有的资源采用到新容器中。
  • 自动装箱(Automatic bin packing):在K8s的集群中,告诉K8s每个容器需要多少CPU和内存,K8s可以将容器安装到节点上,以充分利用资源。
  • 自愈(Self-healing):K8s会重新启动挂掉的容器,替换容器。
  • 机密和配置管理(Secret and configuration management):K8s允许存储和管理敏感信息,如密码OAuth令牌和SSH密钥。在部署和更新机密文件和应用程序配置时无需重新构建容器映像。

 

2、Kubernetes简介

2.1 Kubernetes简介

        Kubernetes是一个全新的基于容器技术的分布式架构领先方案,是谷歌严格保密十几年的秘密武器–Borg系统的一个开源版本,于2014年9月发布第一个版本,2015年7月发布第一个正式版本。

        Kubernetes本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。目的是实现资源管理的自动化。

 

2.2 Kubernetes组件

        一个Kubernetes集群主要由控制节点(Master)工作节点(Node)构成,每个节点上都会安装不同的组件。

Master:集群的控制平面,复制集群的决策:

ApiServer:资源操作的唯一入口,接受用户输入的命令,提供认证、授权、API注册和发现等机制。

Scheduler:负责集群资源调度,安装预定的调度策略将Pod调度到相应的Node节点上。

ControllerManager:负责维护集群的状态,比如程序部署安排、故障检测、自动扩展、滚动更新等。

Etcd:负责存储集群中各种资源对象的信息。

Node:集群的数据平面,负责为容器提供运行环境:

Kubelet:负责维护容器的生命周期,即通过控制Docker来创建、更新、销毁容器。

KubeProxy:负责提供集群内部的服务发现和负载均衡。

Docker:负责节点上容器的各种操作。

在这里插入图片描述

下面,以部署一个nginx服务来说明Kubernetes系统各个组件调用关系:

  1. 首先要明确,一旦Kubernetes环境启动成功后,master和node都会将自身的信息存储到etcd数据库中。

  2. 一个nginx服务的安装请求会首先被发送到master节点的ApiServer组件。

  3. ApiServer组件会调用Scheduler组件来决定到的应该把这个服务安装到哪个node节点上,在此时,它会从Etcd读取各个node节点的信息,然后按照一定的算法进行选择,并将结果告知ApiServer。

  4. ApiServer调用ControllerManager去调度Node节点安装Nginx服务。

  5. Kubelet接收到指令后,会通知Docker,然后由Docker来启动一个nginx的Pod,Pod是Kubernetes的最小操作单元,容器必须跑在Pod中。

  6. 至此,一个nginx服务就运行了,如果需要访问nginx,就需要通过Kube-proxy来对Pod产生访问的代理,这样,外界用户就可以访问集群中的nginx服务了。

2.3 Kubernetes中的概念

Master:集群控制节点,每个集群需要至少一个master节点负责集群的管控。

Node:工作负载节点,由master分配工作到这些node节点上,然后node节点上的docker负责容器的运行。

Pod:K8s最小控制单元,容器都是运行在Pod中的,一个Pod中可以有一个或多个容器。

Controller:控制器,通过它来实现对Pod的管理,比如启动Pod、停止Pod、伸缩Pod的数量等等。

Service:Pod对外服务的统一入口,下面可以维护同一类的多个Pod。

Label:标签,用来对Pod进行分类,同一类Pod会拥有相同的标签。

NameSpace:命名空间,用来隔离Pod的运行环境。

在这里插入图片描述

 

3、环境搭建

        本次环境搭建需要安装三台centos服务器(一主二从),然后在每台服务器中分别安装docker、kubeadm、kubelet、bubectl程序。

3.1 安装虚拟机

使用的虚拟机配置如下:

名称IP地址操作系统配置
Master192.168.226.168centos72颗CPU 2G内存 50G硬盘
Node1192.168.226.169centos72颗CPU 2G内存 50G硬盘
Node2192.168.226.170centos72颗CPU 2G内存 50G硬盘

        

3.2 环境初始化

1)检查操作系统版本

# 此方式下安装kubernetes集群要求centos7版本在7.5或之上
[root@master ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

2)主机域名解析

为了方便后面集群的直接调用,在这配置一下主机名解析。

# 编辑三台服务器的/etc/hosts文件,添加下面内容,ip和主机名要换成自己的
192.168.226.168 master
192.168.226.169 node1
192.168.226.170 node2

3)时间同步

Kubernetes要求集群中的节点时间必须精确一致,这里直接使用chronyd服务从网络同步时间。

# 启动chronyd
systemctl start chronyd

# 设置chronyd服务开机自启
systemctl enable chronyd

# 查看时间
date

4)禁用iptables和firewalld服务

Kubernetes和Docker在运行中会产生大量的iptables规则,为了不让系统规则与他们混淆,直接关闭系统的规则。

# 关闭firewalld服务
systemctl stop firewalld
systemctl disable firewalld

# 关闭iptables服务
systemctl stop iptables
systemctl disable iptables

5)禁用selinux

selinu是linux系统下的一个安全服务,如果不关闭他,在安装集群中会产生各种奇葩问题

# 编辑 /etc/selinux/config文件,修改SELINUX的值为disabled
SELINUX=disabled
# 修改完后需要重新启动linux,可以在执行完后面操作后统一重启

6)禁用swap分区

swap分区指的是虚拟内存分区,它的作用是在物理内存使用完之后,将磁盘空间虚拟成内存在使用。

启用swap设备会对系统的性能产生非常负面的影响,因此Kubernetes要求每个节点都要禁用swap设备。

但是如果因为某些原因确实不能关闭swap分区,就需要在集群安装过程中通过明确的参数进行配置说明。

# 编辑分区配置文件 /etc/fstab ,注释掉swap分区一行
# 注意修改后要重启linux
/dev/mapper/centos-root /                       xfs     defaults        0 0
UUID=1b419f2d-536b-4728-a6d2-c8b9d7ea85f0 /boot                   xfs     defaults        0 0
#/dev/mapper/centos-swap swap                    swap    defaults        0 0

7)修改linux内核参数

# 修改linux的内核参数,添加网桥过滤和地址转发功能
# 编辑/etc/sysctl.d/kubernetes.conf文件,添加如下配置
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1

# 重新加载配置
sysctl -p

# 加载网桥过滤模块
modprobe br_netfilter

# 查看网桥过滤模块是否加载成功
lsmod | grep br_netfilter

[root@master ~]# lsmod | grep br_netfilter
br_netfilter           22256  0 
bridge                151336  1 br_netfilter

8)配置ipvs功能

在kubernetes中service有两种代理模型,一种基于iptables的,一种基于ipvs的

两者比较的话,ipvs的性能明显要高一些,但是如果要使用它,需要手动载入ipvs模块

# 1.安装ipset和ipvsadm
yum install -y ipset ipvsadm

# 2.添加需要加载的模块写入脚本
cat <<EOF > /etc/sysconfig/modules/ipvs.modules
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF

# 3.为脚本文件添加执行权限
chmod +x /etc/sysconfig/modules/ipvs.modules

# 4.执行脚本文件
/bin/bash /etc/sysconfig/modules/ipvs.modules

# 5.查看对应的模块是否加载成功
lsmod | grep -e ip_vs -e nf_conntrack_ipv4

[root@master ~]# lsmod | grep -e ip_vs -e nf_conntrack_ipv4
nf_conntrack_ipv4      15053  0 
nf_defrag_ipv4         12729  1 nf_conntrack_ipv4
ip_vs_sh               12688  0 
ip_vs_wrr              12697  0 
ip_vs_rr               12600  0 
ip_vs                 145458  6 ip_vs_rr,ip_vs_sh,ip_vs_wrr
nf_conntrack          139264  2 ip_vs,nf_conntrack_ipv4
libcrc32c              12644  3 xfs,ip_vs,nf_conntrack

9)重启linux

reboot
# 重启后查看selinux
[root@master ~]# getenforce
Disabled

# 查看swap分区情况,swap全是0代表已经关掉了
[root@master ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:           3770         271        3174          11         324        3271
Swap:             0           0           0

至此环境初始化已经完成了!

        

3.3 Docker安装

# 1.更新镜像源
$ yum -y update

# 2.安装所需的软件包。yum-utils 提供了 yum-config-manager ,并且 device mapper 存储驱动程序需要 device-mapper-persistent-data 和 lvm2。
$ sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2
  
# 3.使用阿里云镜像源
$ sudo yum-config-manager \
    --add-repo \
    http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 4.安装最新版本的 Docker Engine-Community 和 containerd
$ sudo yum install docker-ce docker-ce-cli containerd.io

# 5.添加配置文件
# Docker在默认情况下使用的Cgroup Driver为cgroupfs,而kubernetes推荐使用systemd来代替cgroupfs
mkdir /etc/docker
cat <<EOF > /etc/docker/daemon.json
{
  "exec-opts":["native.cgroupdriver=systemd"],
  "registry-mirrors": ["https://kn0t2bca.mirror.aliyuncs.com"]
}
EOF

# 6.启动Docker
$ sudo systemctl restart docker

# 7.设置docker开机自启
$ sudo systemctl enable docker 

# 8.运行hello-world测试是否安装成功
$ docker run hello-world

        

3.4 安装Kubernetes组件

# 由于Kubernetes的镜像源在国外,速度比较慢,这里切换成国内的镜像源
# 编辑/etc/yum.repos.d/kubernetes.repo,添加下面的配置

[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
        https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
        
# 安装kubeadm、kubelet和kubectl
yum install -y kubelet kubeadm kubectl 

# 配置kubelet的cgroup
# 编辑/etc/sysconfig/kubelet,添加如下配置
KUBELET_CGROUP_ARGS="--cgroup-driver=systemd"
KUBE_PROXY_MODE="ipvs"

# 设置kubelet开机自启
systemctl enable kubelet

        

3.5 准备集群镜像

# 在安装kubernetes集群之前,必须要提前准备好集群所需的镜像,所需镜像可以通过下面命令查看
kubeadm config images list

# 下载镜像
# 此镜像在k8s的仓库中,由于网络原因,无法连接,下面提供了一种代替方案
# 我们从阿里云拉取镜像,然后给镜像打上k8s官方的tag,这样他就不会从k8s仓库中拉取了
# 在shell中输入下面命令即可
images=(
	kube-apiserver:v1.23.1
	kube-controller-manager:v1.23.1
	kube-scheduler:v1.23.1
	kube-proxy:v1.23.1
	pause:3.6
	etcd:3.5.1-0
	coredns:v1.8.6	
)

for imageName in ${images[@]}; do
	docker pull registry.aliyuncs.com/google_containers/$imageName
	docker tag registry.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName
	docker rmi registry.aliyuncs.com/google_containers/$imageName
done

docker pull registry.aliyuncs.com/google_containers/coredns:v1.8.6
docker tag registry.aliyuncs.com/google_containers/coredns:v1.8.6 k8s.gcr.io/coredns/coredns:v1.8.6
docker rmi registry.aliyuncs.com/google_containers/coredns:v1.8.6

# 执行完毕后可以查看镜像
[root@master ~]# docker images
REPOSITORY                                        TAG       IMAGE ID       CREATED        SIZE
k8s.gcr.io/kube-apiserver                         v1.23.1   b6d7abedde39   3 weeks ago    135MB
k8s.gcr.io/kube-proxy                             v1.23.1   b46c42588d51   3 weeks ago    112MB
k8s.gcr.io/kube-controller-manager                v1.23.1   f51846a4fd28   3 weeks ago    125MB
k8s.gcr.io/kube-scheduler                         v1.23.1   71d575efe628   3 weeks ago    53.5MB
k8s.gcr.io/etcd                                   3.5.1-0   25f8c7f3da61   2 months ago   293MB
k8s.gcr.io/coredns                                v1.8.6    a4ca41631cc7   3 months ago   46.8MB
registry.aliyuncs.com/google_containers/coredns   v1.8.6    a4ca41631cc7   3 months ago   46.8MB
hello-world                                       latest    feb5d9fea6a5   3 months ago   13.3kB
k8s.gcr.io/pause 

        

3.6 集群初始化

下面的操作只需在master节点上执行即可

# 创建集群, 第一个版本可能需要改成你自己的版本,最后一个是master节点的ip地址,需要改成自己的
kubeadm init \
--kubernetes-version=v1.23.1 \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/12 \
--apiserver-advertise-address=192.168.226.168   

# 显示下面这句话说明运行成功
Your Kubernetes control-plane has initialized successfully!

# 然后执行如下命令
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

其它节点加入集群

# 在其他节点上加入集群
# 在master节点上使用下面的命令可以查看加入集群的命令
kubeadm token create --print-join-command

# 然后复制命令,在其它节点上执行
[root@master ~]# kubeadm token create --print-join-command
kubeadm join 192.168.226.168:6443 --token iopvlm.1fdztldlifhh7ej9 --discovery-token-ca-cert-hash sha256:1ce8f02b795807f4c16532da3e1ea2b798e01e6580a59b419abd825d90af8a7d 

# 在master节点上查看加入的节点
[root@master ~]# kubectl get nodes
NAME     STATUS     ROLES                  AGE   VERSION
master   NotReady   control-plane,master   12m   v1.23.1
node1    NotReady   <none>                 21s   v1.23.1
node2    NotReady   <none>                 14s   v1.23.1

状态显示NotReady,是因为网络插件还没有安装,下面安装网络插件
        

3.7 安装网络插件

K8s支持多种网络插件,比如flannel、calico、canal等,任选一种使用即可,在此选择flannel

下面操作依旧只需在master节点执行即可,插件使用的是DaemonSet的控制器,它会在每个节点上都运行。

# 获取flannel的配置文件
# 配置文件在下面

# 使用配置文件启动flannel
kubectl apply -f kube-flannel.yml

# 稍等片刻,再次查看集群节点的状态
kubectl get nodes

flannel配置文件

---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: psp.flannel.unprivileged
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
    seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
    apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
    apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
spec:
  privileged: false
  volumes:
  - configMap
  - secret
  - emptyDir
  - hostPath
  allowedHostPaths:
  - pathPrefix: "/etc/cni/net.d"
  - pathPrefix: "/etc/kube-flannel"
  - pathPrefix: "/run/flannel"
  readOnlyRootFilesystem: false
  # Users and groups
  runAsUser:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  # Privilege Escalation
  allowPrivilegeEscalation: false
  defaultAllowPrivilegeEscalation: false
  # Capabilities
  allowedCapabilities: ['NET_ADMIN', 'NET_RAW']
  defaultAddCapabilities: []
  requiredDropCapabilities: []
  # Host namespaces
  hostPID: false
  hostIPC: false
  hostNetwork: true
  hostPorts:
  - min: 0
    max: 65535
  # SELinux
  seLinux:
    # SELinux is unused in CaaSP
    rule: 'RunAsAny'
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
rules:
- apiGroups: ['extensions']
  resources: ['podsecuritypolicies']
  verbs: ['use']
  resourceNames: ['psp.flannel.unprivileged']
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: flannel
subjects:
- kind: ServiceAccount
  name: flannel
  namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: flannel
  namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: kube-flannel-cfg
  namespace: kube-system
  labels:
    tier: node
    app: flannel
data:
  cni-conf.json: |
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-flannel-ds
  namespace: kube-system
  labels:
    tier: node
    app: flannel
spec:
  selector:
    matchLabels:
      app: flannel
  template:
    metadata:
      labels:
        tier: node
        app: flannel
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values:
                - linux
      hostNetwork: true
      priorityClassName: system-node-critical
      tolerations:
      - operator: Exists
        effect: NoSchedule
      serviceAccountName: flannel
      initContainers:
      - name: install-cni
        image: quay-mirror.qiniu.com/coreos/flannel:v0.13.1-rc1
        command:
        - cp
        args:
        - -f
        - /etc/kube-flannel/cni-conf.json
        - /etc/cni/net.d/10-flannel.conflist
        volumeMounts:
        - name: cni
          mountPath: /etc/cni/net.d
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      containers:
      - name: kube-flannel
        image: quay.io/coreos/flannel:v0.13.1-rc1
        command:
        - /opt/bin/flanneld
        args:
        - --ip-masq
        - --kube-subnet-mgr
        resources:
          requests:
            cpu: "100m"
            memory: "50Mi"
          limits:
            cpu: "100m"
            memory: "50Mi"
        securityContext:
          privileged: false
          capabilities:
            add: ["NET_ADMIN", "NET_RAW"]
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        volumeMounts:
        - name: run
          mountPath: /run/flannel
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      volumes:
      - name: run
        hostPath:
          path: /run/flannel
      - name: cni
        hostPath:
          path: /etc/cni/net.d
      - name: flannel-cfg
        configMap:
          name: kube-flannel-cfg

        

3.8 服务部署

接下来在k8s集群中部署一个nginx程序,测试下集群是否正常工作。

# 部署nginx
kubectl create deployment nginx --image=nginx:1.14-alpine

# 暴露端口
kubectl expose deployment nginx --port=80 --type=NodePort

# 查看服务状态
[root@master ~]# kubectl get pods,service
NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-7cbb8cd5d8-hhnmg   1/1     Running   0          2m11s

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        68m
service/nginx        NodePort    10.96.52.227   <none>        80:30041/TCP   31m

# 最后在电脑上访问nginx
# 输入master主机的ip和上面最后一条中的30041端口

在这里插入图片描述

        

3.9 可能产生的问题

使用 kubectl get pod -n kube-system可以查看系统pod的启动情况:

[root@master k8s]# kubectl get pods -n kube-system
NAME                             READY   STATUS                  RESTARTS      AGE
coredns-64897985d-6w7vv          0/1     ImagePullBackOff        0             44s
coredns-64897985d-b577t          0/1     ImagePullBackOff        0             44s
etcd-master                      1/1     Running                 0             22h
kube-apiserver-master            1/1     Running                 0             22h
kube-controller-manager-master   1/1     Running                 2 (17h ago)   22h
kube-flannel-ds-amd64-dkh9p      1/1     Running                 7 (21h ago)   21h
kube-flannel-ds-amd64-hx9v4      1/1     Running                 7 (21h ago)   21h
kube-flannel-ds-amd64-m684w      1/1     Running                 7 (21h ago)   21h
kube-flannel-ds-b9xc9            0/1     Init:ImagePullBackOff   0             21h
kube-flannel-ds-pft6v            0/1     Init:ImagePullBackOff   0             21h
kube-flannel-ds-t22gs            0/1     Init:ImagePullBackOff   0             21h
kube-proxy-4kg5z                 1/1     Running                 0             22h
kube-proxy-c4xqw                 1/1     Running                 0             22h
kube-proxy-rkdf5                 1/1     Running                 0             22h
kube-scheduler-master            1/1     Running                 2 (17h ago)   22h

        可以看到coredns和flannel都没有启动成功,状态是ImagePullBackOff,说明镜像拉取失败,但是明明本地已经下载了镜像。看来应该是标签名不一致,所以它又去拉取,而且没有成功,因此我们需要把本地的镜像打一个新的标签。

使用 kubectl get pod -n kube-system -o wide可以查看详情:

[root@master k8s]# kubectl get pods -n kube-system -o wide
NAME                             READY   STATUS                  RESTARTS      AGE   IP                NODE     NOMINATED NODE   READINESS GATES
coredns-64897985d-6w7vv          0/1     ImagePullBackOff        0             92s   10.244.2.10       node2    <none>           <none>
coredns-64897985d-b577t          0/1     ImagePullBackOff        0             92s   10.244.1.6        node1    <none>           <none>
etcd-master                      1/1     Running                 0             22h   192.168.226.168   master   <none>           <none>
kube-apiserver-master            1/1     Running                 0             22h   192.168.226.168   master   <none>           <none>
kube-controller-manager-master   1/1     Running                 2 (17h ago)   22h   192.168.226.168   master   <none>           <none>
kube-flannel-ds-amd64-dkh9p      1/1     Running                 7 (21h ago)   21h   192.168.226.170   node2    <none>           <none>
kube-flannel-ds-amd64-hx9v4      1/1     Running                 7 (21h ago)   21h   192.168.226.169   node1    <none>           <none>
kube-flannel-ds-amd64-m684w      1/1     Running                 7 (21h ago)   21h   192.168.226.168   master   <none>           <none>
kube-flannel-ds-b9xc9            0/1     Init:ImagePullBackOff   0             21h   192.168.226.170   node2    <none>           <none>
kube-flannel-ds-pft6v            0/1     Init:ErrImagePull       0             21h   192.168.226.168   master   <none>           <none>
kube-flannel-ds-t22gs            0/1     Init:ImagePullBackOff   0             21h   192.168.226.169   node1    <none>           <none>
kube-proxy-4kg5z                 1/1     Running                 0             22h   192.168.226.169   node1    <none>           <none>
kube-proxy-c4xqw                 1/1     Running                 0             22h   192.168.226.170   node2    <none>           <none>
kube-proxy-rkdf5                 1/1     Running                 0             22h   192.168.226.168   master   <none>           <none>
kube-scheduler-master            1/1     Running                 2 (17h ago)   22h   192.168.226.168   master   <none>           <none>

通过查看,发现node1和node2的coredns没有启动成功,master和两个node的flannel都没有启动成功。

使用 kubectl describe pod kube-flannel-ds-b9xc9 -n kube-system | grep Image:来查看需要的镜像名。

[root@master k8s]# kubectl describe pod kube-flannel-ds-b9xc9 -n kube-system | grep Image:
    Image:         quay-mirror.qiniu.com/coreos/flannel:v0.13.1-rc1
    Image:         quay.io/coreos/flannel:v0.13.1-rc1

然后使用docker tag 原标签名 目的标签名来修改标签名即可。

然后这些没有启动的pod就会重启,或者也可以删掉pod,删掉后会重新启动,命令为:

kubectl delete pod pod名 -n kube-system

 

4、资源管理

4.1 资源管理介绍

        在Kubernetes中,所有内容都被抽象为资源,用户需要通过操作资源来管理Kubernetes。

​ Kubernetes的本质就是一个集群系统,用户可以在集群中部署各种服务,所谓的部署服务,其实就是在Kubernetes集群中运行一个个的容器,并将指定的程序跑在容器中。

​ Kubernetes的最小管理单元是Pod而不是容器,所以只能将容器放在Pod中,而Kubernetes一般也不会直接管理Pod,而是通过pod控制器来管理Pod的。

​ Pod可以提供服务之后,就需要考虑如何访问Pod中的服务,Kubernetes提供了Service资源实现这个功能。

​ 当然,如果Pod中程序的数据需要持久化,Kubernetes还提供了各种存储系统。

在这里插入图片描述

学习Kubernetes的核心,就是学习如何对集群的Pod、Pod控制器、Service、存储等各种资源进行操作。

        

4.2 Yaml语法介绍

        YAML是一个类似XML、JSON的标记性语言。它强调以数据为中心,并不是以标识语言为重点。因为YAML本身的定义比较简单,号称"一种人性化的数据格式语言"。

YAML的语法比较简单,主要有下面几个:

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格(高版本可以)
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • '#'表示注释

YAML支持以下几种数据类型:

  • 纯量:单个的不可再分的值
  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hash)/ 字典(dictionary)
  • 数组:一组按次序排列的值,又称为序列(sequence)/ 列表(list)
# 纯量,就是指的一个简单的值,字符串、布尔、整数、浮点数、Null、时间、日期
# 布尔类型
c1: true
# 整型
c2: 123456
# 浮点类型
c3: 3.14
# null类型
c4: ~ # 使用~表示null
# 日期类型
c5: 2019-11-11 # 日期类型必须使用ISO 8601格式,即yyyy-MM-dd
# 时间类型
c6: 2019-11-11T15:02:31+08.00 # 时间类型使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区
# 字符串类型
c7: haha # 简单写法,直接写值,如果字符串中间有特殊符号,必须使用双引号或单引号包裹
c8: line1
    line2 # 字符串过多的情况可以折成多行,每一行都会转换成一个空格
# 对象
# 形式一(推荐):
xudaxian:	
	name: kunkun
	age: 30
# 形式二(了解):
xuxian: { name: jige, age: 30 }
# 数组
# 形式一(推荐):
address:
	- 江苏
	- 浙江
# 形式二(了解):
address: [江苏,上海]

小提示:

​ 1 书写yaml切记 :后面要加一个空格

​ 2 如果需要将多段yaml配置放在一个文件中,中间要用---分割

        

4.3 资源管理方式

  • 命令式对象管理:直接使用命令去操作Kubernetes资源

    kubectl run nginx-pod --image=nginx:1.17.1 --port=80

  • 命令式对象配置:通过命令配置和配置文件去操作Kubernetes资源

    kubectl create/patch -f nginx-pod.yaml

  • 声明式对象配置:通过apply命令和配置文件去操作Kubernetes资源

    kubectl apply -f nginx-pod.yaml

类型操作对象适用环境优点缺点
命令式对象管理对象测试简单只能操作活动对象,无法审计、跟踪
命令式对象配置文件开发可以审计、跟踪项目大时,配置文件多,操作麻烦
声明式对象配置目录开发支持目录操作意外情况下难以调试
4.3.1 命令式对象管理

kubectl 命令

        kubectl是Kubernetes集群的命令行工具,通过它能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。kubectl命令的语法如下:

kubectl [command] [type] [name] [flags]

command:指定要对资源进行的操作,例如create、get、delete

type:指定资源类型,比如deployment、pod、service

name:指定资源的名称,名称大小写敏感

flags:指定额外的可选参数

# 查看所有pod
kubectl get pod

# 查看某个pod
kubectl get pod pod_name

# 查看某个pod,以json格式展示结果
kubectl get pod pod_name -o json
·操作·

        Kubernetes运行对资源进行多种操作,可以通过kubectl --help来查看详细的操作命令

查用操作如下:

① 基本命令

命令翻译命令作用
create创建创建一个资源
edit编辑编辑一个资源
get获取获取一个资源
patch更新更新一个资源
delete删除删除一个资源
explain解释展示资源文档

② 运行和调试

命令翻译命令作用
run运行在集群中运行一个指定的镜像
expose暴露暴露资源为Service
describe描述显示资源内部信息
logs日志输出容器在Pod中的日志
attach缠绕进入运行中的容器
exec执行执行容器中的一个命令
cp复制在Pod内外复制文件
rollout首次展示管理资源的发布
scale规模扩(缩)容Pod的数量
autoscale自动调整自动调整Pod的数量

③ 高级命令

命令翻译命令作用
apply应用通过文件对资源进行配置
label标签更新资源上的标签

④ 其它命令

命令翻译命令作用
apply应用通过文件对资源进行配置
label标签更新资源上的标签
·资源类型·

        Kubernetes中所有内容都抽象为资源,可以通过下面的命令进行查看:

kubectl api-resources

常用资源如下:

① 集群级别资源

资源名称缩写资源作用
nodesno集群组成部分
namespacesns隔离Pod

② Pod资源

资源名称缩写资源作用
Podspo装载容器

③ Pod资源控制器

资源名称缩写资源作用
replicationcontrollersrc控制Pod资源
replicasetsrs控制Pod资源
deploymentsdeploy控制Pod资源
daemonsetsds控制Pod资源
jobs控制Pod资源
cronjobscj控制Pod资源
horizontalpodautoscalershpa控制Pod资源
statefulsetssts控制Pod资源

④ 服务发现资源

资源名称缩写资源作用
servicessvc统一Pod对外接口
ingressing统一Pod对外接口

⑤ 存储资源

volumeattachments存储
persistentvolumespv存储
persistentvolumeclaimspvc存储

⑥ 配置资源

资源名称缩写资源作用
configmapscm配置
secrets配置

下面以一个namespace / pod的创建和删除简单演示下命令的使用:

# 创建一个namespace, namespace可以缩写为ns
[root@master ~]# kubectl create namespace dev
namespace/dev created

# 查看namespace
[root@master ~]# kubectl get ns
NAME              STATUS   AGE
default           Active   20h
dev               Active   40s
kube-node-lease   Active   20h
kube-public       Active   20h
kube-system       Active   20h

# 在此namespace下创建并运行一个nginx的pod,-n 用来指定命名空间
[root@master ~]# kubectl run pod --image=nginx:1.17.1 -n dev
pod/pod created

# 查看新创建的pod
[root@master ~]# kubectl get pod -n dev
NAME   READY   STATUS    RESTARTS   AGE
pod    1/1     Running   0          36s

# 删除指定的pod
[root@master ~]# kubectl delete pod pod -n dev
pod "pod" deleted

# 删除指定的namespace
[root@master ~]# kubectl delete ns dev
namespace "dev" deleted
4.3.2 命令式对象配置

命令式对象配置就是使用命令和配置文件一起来操作Kubernetes

1)创建一个nginxpod.yaml,内容如下:

apiVersion: v1
kind: Namespace
metadata: 
  name: dev

---

apiVersion: v1
kind: Pod
metadata: 
  name: nginxpod
  namespace: dev
spec:
  containers:
  - name: nginx-containers
    image: nginx:1.17.1

2)执行create命令,创建资源

[root@master k8s]# kubectl create -f nginxpod.yaml 
namespace/dev created
pod/nginxpod created

此时发现创建了两个资源对象,分别是namespace和pod

3)执行get命令查看资源:

[root@master k8s]# kubectl get -f nginxpod.yaml
NAME            STATUS   AGE
namespace/dev   Active   93s

NAME           READY   STATUS    RESTARTS   AGE
pod/nginxpod   1/1     Running   0          93s

这样就显示了两个资源对象的信息

4)执行delete命令,删除资源

[root@master k8s]# kubectl delete -f nginxpod.yaml 
namespace "dev" deleted
pod "nginxpod" deleted

此时发现两个资源对象被删除了

​ 总结:

        命令式对象配置的操作资源,可以简单的认为:命令 + yaml配置文件(里面是命令需要的各种参数)

4.3.3 声明式对象配置

声明式对象配置根命令式对象配置很相似,但是它只有一个命令apply

# 首先执行一次kubectl apply -f nginxpod.yaml文件,发现创建了资源
[root@master k8s]# kubectl apply -f nginxpod.yaml 
namespace/dev created
pod/nginxpod created

# 再次执行,发现资源没有变动
[root@master k8s]# kubectl apply -f nginxpod.yaml 
namespace/dev unchanged
pod/nginxpod unchanged

总结:

        其实声明式对象配置就是使用apply描述一个资源的最终状态(在yaml中定义)

        使用apply操作资源:

                如果资源不存在,就创建,相当于 kubectl create

                如果资源已存在,就更新,相当于 kubectl patch

扩展:kubectl可以在node节点上运行吗?

        kubectl的运行是需要进行配置的,它的配置文件是$HOME/.kube,如果相要在node节点运行此命令,需要将master上的.kube文件复制到node节点上,即在master节点上执行下面操作:

scp -r ~/.kube/ node1:~/

使用推荐:三种方式应该怎么用?

创建/更新资源: 使用声明式对象配置 kubectl apply -f xxx.yaml

删除资源: 使用命令式对象配置kubectl delete -f xxx.yaml

查询资源: 使用命令式对象管理 kubectl get / describe 资源名称

 

5、实战入门

本章节将介绍如何在Kubernetes集群中部署一个nginx服务,并且能够对其访问。

5.1 Namespace

        Namespace是Kubernetes系统中的一种非常重要的资源,它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离

        默认情况下,Kubernetes集群中所有的Pod都是可以互相访问的。但在实际中,可能不想让两个Pode之间互相访问,那么此时就可以将两个Pod划分到不同的Namespace中。Kubernetes通过将集群内部的资源分配到不同的Namespace中,可以形成逻辑上,以方便不同组的资源进行隔离使用和管理。

        可以通过Kubernetes的授权机制,将不同的Namespace交给不同的租户进行管理,这样就实现了多租户的资源隔离。此时还能结合Kubernetes的资源配额机制,限定不同租户能占用的资源,例如CPU的使用量、内存使用量等等,来实现租户可用资源的管理。

在这里插入图片描述

Kubernetes在集群启动后,会默认创建几个Namespace

# namespace 也可以缩写为 ns
[root@master k8s]# kubectl get namespace            
NAME              STATUS   AGE
default           Active   46h
kube-node-lease   Active   46h
kube-public       Active   46h
kube-system       Active   46h

下面来看Namespace资源的具体操作:

查看

# 1.查看所有的namespace
[root@master k8s]# kubectl get ns
NAME              STATUS   AGE
default           Active   46h
dev               Active   41m
kube-node-lease   Active   46h
kube-public       Active   46h
kube-system       Active   46h

# 2.查看指定的ns
[root@master k8s]# kubectl get ns kube-system
NAME          STATUS   AGE
kube-system   Active   46h

# 3.指定输出格式 命令:kubectl get ns ns名称 -o 格式参数
# 格式参数有很多,比较常见的是 wide、json、yaml
[root@master k8s]# kubectl get ns default -o json
{
    "apiVersion": "v1",
    "kind": "Namespace",
    "metadata": {
        "creationTimestamp": "2022-01-06T13:14:23Z",
        "labels": {
            "kubernetes.io/metadata.name": "default"
        },
        "name": "default",
        "resourceVersion": "207",
        "uid": "5a0e80e3-24de-4e83-8748-7b5e777dd4c7"
    },
    "spec": {
        "finalizers": [
            "kubernetes"
        ]
    },
    "status": {
        "phase": "Active"
    }
}

# 4.查看ns详情 命令: kubectl describe ns ns名称
[root@master k8s]# kubectl describe ns default
Name:         default
Labels:       kubernetes.io/metadata.name=default
Annotations:  <none>
Status:       Active

No resource quota.

No LimitRange resource.

创建

# 创建namespace
[root@master k8s]# kubectl create ns test
namespace/test created

删除

# 删除namespace
[root@master k8s]# kubectl delete ns test
namespace "test" deleted

配置方式

首先准备一个yaml文件:ns-dev.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: dev

然后就可以执行对应的创建和删除命令了:

创建 kubectl create -f ns-dev.yaml

删除 kubectl delete -f ns-dev.yaml

        

5.2 Pod

        Pod是Kubernetes集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。

        Pod可以认为是容器的封装,一个Pod中可以存在一个或多个容器。

在这里插入图片描述

Kubernetes在集群启动之后,集群中的各个组件也都是以Pod的方式运行的。可以通过下面的命令查看:

[root@master k8s]# kubectl get pod -n kube-system
NAME                             READY   STATUS    RESTARTS      AGE
coredns-64897985d-hgmnd          1/1     Running   0             24h
coredns-64897985d-wrqs4          1/1     Running   0             24h
etcd-master                      1/1     Running   0             47h
kube-apiserver-master            1/1     Running   0             47h
kube-controller-manager-master   1/1     Running   2 (41h ago)   47h
kube-flannel-ds-amd64-dkh9p      1/1     Running   7 (46h ago)   46h
kube-flannel-ds-amd64-hx9v4      1/1     Running   7 (46h ago)   46h
kube-flannel-ds-amd64-m684w      1/1     Running   7 (46h ago)   46h
kube-flannel-ds-b9xc9            1/1     Running   0             46h
kube-flannel-ds-pft6v            1/1     Running   0             46h
kube-flannel-ds-t22gs            1/1     Running   0             46h
kube-proxy-4kg5z                 1/1     Running   0             46h
kube-proxy-c4xqw                 1/1     Running   0             46h
kube-proxy-rkdf5                 1/1     Running   0             47h
kube-scheduler-master            1/1     Running   2 (41h ago)   47h

创建并运行

Kubernetes没有提供单独允许Pod的命令,都是通过Pod控制器来实现的

# 命令格式: kubectl run (pod控制器名称) [参数]
# --image 指定Pod的jingx
# --port 指定端口
# --namespace 指定命名空间
[root@master k8s]# kubectl run nginx-test --image=nginx:1.17.1 --port=80 --namespace=dev
pod/nginx-test created

查看Pod信息

# 查看pod基本信息
[root@master k8s]# kubectl get pod -n dev
NAME                     READY   STATUS    RESTARTS   AGE
nginx-6fdfb7f689-ht222   1/1     Running   0          40m
nginx-6fdfb7f689-q2wjx   1/1     Running   0          40m
nginx-6fdfb7f689-xklcw   1/1     Running   0          40m
nginx-test               1/1     Running   0          54s

# 查看详细信息
[root@master k8s]# kubectl get pod -n dev -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
nginx-6fdfb7f689-ht222   1/1     Running   0          41m   10.244.1.15   node1   <none>           <none>
nginx-6fdfb7f689-q2wjx   1/1     Running   0          41m   10.244.2.14   node2   <none>           <none>
nginx-6fdfb7f689-xklcw   1/1     Running   0          41m   10.244.1.14   node1   <none>           <none>
nginx-test               1/1     Running   0          76s   10.244.1.16   node1   <none>           <none>

# 查看某个pod的更详细信息,最下面记录了Pod的启动日志,可以用此来排错
[root@master k8s]# kubectl describe pod nginx-test -n dev
Name:         nginx-test
Namespace:    dev
Priority:     0
Node:         node1/192.168.226.169
Start Time:   Sat, 08 Jan 2022 20:23:54 +0800
Labels:       run=nginx-test
Annotations:  <none>
Status:       Running
IP:           10.244.1.16
IPs:
  IP:  10.244.1.16
Containers:
  nginx-test:
    Container ID:   docker://d8419946e6348a3f5297ec3b30675e9aa27639c853d68b5a4e9bd334ef709924
    Image:          nginx:1.17.1
    Image ID:       docker-pullable://nginx@sha256:b4b9b3eee194703fc2fa8afa5b7510c77ae70cfba567af1376a573a967c03dbb
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sat, 08 Jan 2022 20:23:55 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-fnf59 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-fnf59:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  2m20s  default-scheduler  Successfully assigned dev/nginx-test to node1
  Normal  Pulled     2m20s  kubelet            Container image "nginx:1.17.1" already present on machine
  Normal  Created    2m20s  kubelet            Created container nginx-test
  Normal  Started    2m20s  kubelet            Started container nginx-test

删除指定的Pod

# 删除指定pod
[root@master k8s]# kubectl delete pod nginx-test -n dev
pod "nginx-test" deleted

# 此时,显示删除成功,但是再次查询,发现可能新产生了一个
# 如果是旧版本,可以会产生一个新的,新版本则不会产生。
# 说明,新版本使用run命令创建的pod没有Pod控制器。
# 在旧版本中,使用run命令会产生一个Pod控制器,该控制器会监控Pod的状态,一旦Pod被删除,Pod控制器
# 就会创建一个新的Pod

# 查看Pod控制器
kubectl get deploy -n dev

配置操作

创建一个pod-nginx.yaml,内容如下

    apiVersion: v1
    kind: Pod
    metadata: 
      name: nginx-test
      namespace: dev
    spec:
      containers:
      - image: nginx:1.17.1
        imagePullPolicy: IfNotPresent
        name: pod
        ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP

然后就可以执行对应的创建和删除命令了:

创建: kubectl create -f pod-nginx.yaml

删除: kubectl delete -f pod-nginx.yaml

        

5.3 Label

        Label是Kubernetes系统中的一个重要概念。它的作用就是在资源上添加标识,用来对他们进行区分和选择。

Label的特定:

  • 一个Label会以key/value键值对的形式附加到各种对象上,如Node、Pod、Service等
  • 一个资源对象可以定义任意数量的Label,同一个Lable也可以被添加到任意数量的资源对象上去。
  • Label通常在资源对象定义时确定,当然也可以在对象创建后动态添加或删除。

可以通过Label实现资源的多维分组,以便灵魂、方便地进行资源分配、调度、配置、部署等管理工作。

一些常用的Label示例如下:

  • 版本标签:“version”:“release”, “version”:“stable” …
  • 环境标签:“environment”:“dev”, “environment”:“test”, “environment”:“pro” …
  • 架构标签:“tier”:“frontend”, “tier”:“backend” …

标签定义完毕后,还要考虑到标签的选择,这就要用到Label Selector,即:

  • Label用于给某个资源对象定义标识

  • Label Selector用于查询和筛选具有某些标签的资源对象

当前有两种Label Selector:

  • 基于等式的Label Selector

    name=slave:选择所有包含Label中key="name"且value="slave"的对象

    env!=production:选择所有包含Label中的key="env"且value不等于"production"的对象

  • 基于集合的Label Selector

    name in (master, slave):选择所有包含Label中的key="name"且value="master"或"slave"的对象

    name not int (frontend):选择所有包含Label中的key="env"且value不等于"frontend"的对象

标签的选择条件可以使用多个,此时将多个Label Selector进行组合,使用逗号分隔即可,例如:

    name=slave, env!=production

​    name not in (frontend), env!=production

命令格式

# 为pod资源打标签
[root@master k8s]# kubectl label pod nginx-test version=1.0 -n dev
pod/nginx-test labeled

# 为pod资源更新标签
[root@master k8s]# kubectl label pod nginx-test version=2.0 -n dev --overwrite
pod/nginx-test labeled

# 查看标签
[root@master k8s]# kubectl get pod nginx-test -n dev --show-labels
NAME         READY   STATUS    RESTARTS   AGE   LABELS
nginx-test   1/1     Running   0          14m   version=2.0

# 筛选标签
[root@master k8s]# kubectl get pod -n dev -l version=2.0 --show-labels
NAME         READY   STATUS    RESTARTS   AGE   LABELS
nginx-test   1/1     Running   0          15m   version=2.0
[root@master k8s]# kubectl get pod -n dev -l version!=2.0 --show-labels
NAME                     READY   STATUS    RESTARTS   AGE   LABELS
nginx-6fdfb7f689-ht222   1/1     Running   0          70m   pod-template-hash=6fdfb7f689,run=nginx

# 删除标签
[root@master k8s]# kubectl label pod nginx-test -n dev version-
pod/nginx-test unlabele

配置方式

apiVersion: v1
kind: Pod
metadata: 
  name: nginx-test
  namespace: dev
  labels:
    version: "3.0"
    env: "test"
spec:
  containers:
  - image: nginx:1.17.1
    imagePullPolicy: IfNotPresent
    name: pod
    ports:
    - name: nginx-port
      containerPort: 80
      protocol: TCP

然后就可以执行对应的更新命令了: kubectl apply -f pod-labeled-nginx.yml

        

5.4 Deployment

        在Kubernetes中,Pod是最小的控制单元,但是Kubernetes很少直接控制Pod,一般都是通过Pod控制器来完成的。Pod控制器用于Pod的管理,确保Pod资源符合预期的状态,当Pod的资源出现故障时,会尝试进行重启或重建Pod。

        在Kubernetes中Pod控制器的种类有很多种,本章节只介绍一种:Deployment。

在这里插入图片描述

命令操作

# 命令格式: kubectl create deployment deployment名称 [参数]
# --image 指定pod的镜像
# --port 指定端口
# --replicas 指定副本数量
# --namespace 指定namespace
[root@master k8s]# kubectl create deploy nginx --image=nginx:1.17.1 --port=80 --replicas=3 --namespace=dev
deployment.apps/nginx created

# 查看创建的pod
[root@master k8s]# kubectl get pod -n dev
NAME                     READY   STATUS    RESTARTS   AGE
nginx-78bc4f47cf-6tdr7   1/1     Running   0          24s
nginx-78bc4f47cf-bxwp2   1/1     Running   0          24s
nginx-78bc4f47cf-srm7m   1/1     Running   0          24s

# 查看deployment
[root@master k8s]# kubectl get deploy -n dev
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   3/3     3            3           70s

# UP-TO-DATE:成功升级的副本数量
# AVAILABLE:可以副本数量
[root@master k8s]# kubectl get deploy -n dev -o wide
NAME    READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES         SELECTOR
nginx   3/3     3            3           2m16s   nginx        nginx:1.17.1   app=nginx

# 查看deployment的详细信息
[root@master k8s]# kubectl describe deploy nginx -n dev
Name:                   nginx
Namespace:              dev
CreationTimestamp:      Sat, 08 Jan 2022 21:04:09 +0800
Labels:                 app=nginx
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=nginx
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx:1.17.1
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-78bc4f47cf (3/3 replicas created)
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  2m45s  deployment-controller  Scaled up replica set nginx-78bc4f47cf to 3
 
 
# 删除deployment
[root@master k8s]# kubectl delete deploy nginx -n dev
deployment.apps "nginx" deleted

配置操作

创建一个deploy-nginx.yaml,内容如下:

apiVersion: apps/v1
kind: Deployment
metadata: 
  name: nginx
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      run: nginx
  template:
    metadata: 
      labels:
        run: nginx
    spec:
      containers:
      - image: nginx:1.17.1
        name: nginx
        ports:
        - containerPort: 80
          protocol: TCP

然后使用create或apply命令来创建,delete命令来更新即可

        

5.5 Service

通过上节课的学习,已经能够利用Deployment来创建一组Pod来提供高可用性的服务。

虽然每个Pod都会分配一个单独的IP,然而却存在如下两问题:

  • Pod IP会随着Pod的重建发生变化
  • Pod IP仅仅是集群内可见的虚拟IP,外部无法访问

这样对于访问这个服务带来了难度。因此,Kubernetes设计了Service来解决这个问题。

Service可以看作是一组同类Pod对外访问的接口。借助Service,应用可以方便地实现负载均衡和服务发现。

在这里插入图片描述

操作一:创建集群内部可访问的Service

# 暴露Service,service可以缩写为svc
[root@master k8s]# kubectl expose deploy nginx --name=svc-nginx --type=ClusterIP --port=80 --target-port=80 -n dev
service/svc-nginx exposed

# 查看Service
[root@master k8s]# kubectl get svc -n dev
NAME        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
svc-nginx   ClusterIP   10.111.174.153   <none>        80/TCP    22s

# 这里产生了一个CLUSTER-IP,这就是Service的IP,在Service的生命周期中,这个地址是不会变的
# 可以通过这个IP访问当前Service对应的Pod
[root@master k8s]# curl 10.111.174.153:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

操作二:创建集群外部也可访问的Service

# 上面创建的Service的type类型为ClusterIP,这个IP地址只能集群内部访问
# 如果需要创建外部也可访问的Service,需要修改type为NodePort
[root@master k8s]# kubectl expose deploy nginx --name=svc-nginx1 --type=NodePort --port=80 --target-port=80 -n dev
service/svc-nginx1 exposed

# 此时查看,会发现出现了NodePort类型的Service,而且有一对port(80:30248)
[root@master k8s]# kubectl get svc -n dev
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
svc-nginx    ClusterIP   10.111.174.153   <none>        80/TCP         4m58s
svc-nginx1   NodePort    10.99.100.168    <none>        80:30248/TCP   20s

# 接下来就可以通过集群外的主机访问节点IP:30248服务了
# 可以通过任意一个节点的IP:30248来访问

在这里插入图片描述

配置方式

创建一个svc-nginx.yaml的文件,内容如下:

apiVersion: v1
kind: Service
metadata:
  name: svc-nginx
  namespace: dev
spec:
  clusterIP: 10.97.241.231
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: nginx
  type: ClusterIP

然后就可以执行对应的创建和删除命令了:

创建 kubectl create -f svc-nginx.yaml

删除 kubectl delete -f svc-nginx.yaml

小结:至此,已经学习了Namespace、Pod、Deployment和Service资源的基本操作,有了这些操作,就可以在Kubernetes集群中实现一个简单的服务部署和访问了,但是如果想要更好地使用Kubernetes,就需要深入学习这几种资源地细节和原理。

 

6、Pod详解

本章节将详细介绍Pod资源的各种配置和原理。
        

6.1 Pod介绍

6.1.1 Pod结构

在这里插入图片描述

每个Pod中都可以包含一个或多个容器,这些容器可以分为两类:

  • 用户程序所在的容器,数量可多可少
  • Pause容器,这是每个Pod都会有的一个根容器,它的作用有两个:
    • 可以以它为凭据,评估整个Pod的健康状态。
    • 可以在根容器上设置Ip地址,其它容器使用此IP,以实现Pod内部的网络通信

这里是Pod内部的通讯,Pod之间的通讯采用虚拟二层网络技术来实现,我们当前环境用的是Flannel。

6.1.2 Pod定义

下面是Pod的资源清单:

apiVersion: v1         # 必选,版本号,例如v1
kind: Pod              # 必选,资源类型
metadata:              # 必选,元数据
  name: <string>       # 必选,pod名称
  namespace: <string>      # pod所属命名空间,默认为"default"
  labels:                  # 自定义标签列表
    - name: <string>         
spec:                  # 必选,pod中容器的详细定义
  containers:          # 必选,pod中容器列表
  - name: <string>     # 必选,容器名称
    image: <string>    # 必选,容器的镜像名称
    imagePullPolicy: [Always|Never|IfNotPresent]      # 获取镜像的策略
    commange: [<string>]  # 容器的启动命令列表,如不指定,使用打包时使用的启动命令
    args: [<string>]      # 容器的启动命令参数列表
    workingDir: <string>    # 容器的工作目录
    volumeMounts:          # 挂载到容器内部的存储卷配置
    - name: <string>        # 引用pod定义的共享存储卷的名称,需用volumes[]部分定义的卷名
      mountPath: <string>     # 存储卷在容器内mount的绝对路径,应少于512字符
      readOnly: <bool>          # 是否为只读模式
    ports:                 # 需要暴露的端口号列表
    - name: <string>       # 端口的名称
      containerPort:       # 容器需要监听的端口号
      hostPort: <int>      # 容器所在主机需要监听的端口号,也就是映射端口,默认与容器相同
      protocol: <string>   # 端口协议,支持TCP和UDP,默认TCP
    env:                   # 容器运行前需设置的环境变量列表
    - name: <string>       # key
      value: <string>      # value
    resources:             # 资源限制和请求的限制
      limits:              # 资源限制的设置
        cpu: <string>      # cpu的限制,单位为core数量,将用于docker run --cpu-shares参数
        memory: <string>    # 内存限制,单位为M,G,Mi或Gi,将用于docker run --memory参数
      requests:             # 资源请求的设置
        cpu: <string>       # cpu请求,容器启动的初始可用数量
        memory: <string>    # 内存请求,容器启动的初始可用大小
    lifecycle:             # 生命周期钩子
      postStart:             # 容器启动后立即执行此钩子,如果执行失败会根据重启策略进行重启
      preStop:               # 容器终止前执行此钩子,无论结果如何,容器都会终止
    livenessProbe:           # 对pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器
      exec:                  # 对pod容器内检查方式设置为exec
        command: [<string>]  # exec方式需要指定的命令或脚本
      httpGet:               # 对pod内各个容器的健康检查方式设置为httpGet
        path: <string>
        port: <int>
        host: <string>
        scheme: <string>
        HttpHeaders:
        - name: <string>
          value: <string>
      tcpSocket:            # tcpSocket方式
        port: <int>
      initialDelaySecondes: 0        # 容器启动完成后首次探测的时间,单位s
      timeoutSeconds: 0              # 对容器健康检查探测等待响应的超时时间,单位s,默认1s
      periodSeconds: 0               # 对容器监控检查的定期探测时间设置,单位s,默认10s一次
      successThreshold: 0
      failureThreshold: 0
  restartPolicy: [Always|Never|OnFailure]     # pod的重启策略
  nodeName: <string>                  # 设置nodeName表示将该pod调度到指定名称的node节点上
  nodeSelector: <object>              # 设置NodeSelector表示将该pod调度到包含这个label的node上
  imagePullSecrets:           # 拉取镜像时使用的secret名称,以key:secretKey格式指定
  - name: <string>
  hostNetwork: <bool>           # 是否采用主机模式,默认为false,如果设置为true,表示使用宿主机网络
  volumes:                   # 在该pod上定义共享存储卷列表
  - name: <string>           # 共享存储卷名称
    emptyDir: {}             # 类型为emptyDir的存储卷,与Pod同生命周期的一个临时目录,为空值。
    hostPath: <string>       # 类型为hostPath的存储卷,表示挂载pod所在宿主机的目录
      path: <string>         # pod所在宿主机目录
    secret:                  # 类型为secret的存储卷,挂载集群与定义的secret对象到容器内部
      secretName:
      items:
      - key: <string>
        path: <string>
    configMap:               # 类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
      name: <string>
      items:
      - key: <string>
        path: <string>
# 小提示
# 在这里,可以通过一个命令来查看每种资源的可配置项
# kubectl explain 资源类型                 查看某种资源可以配置的一级属性
# kubectl explain 资源类型.属性             查看属性的子属性
[root@master k8s]# kubectl explain pod
KIND:     Pod
VERSION:  v1

DESCRIPTION:
     Pod is a collection of containers that can run on a host. This resource is
     created by clients and scheduled onto hosts.

FIELDS:
   apiVersion   <string>
     APIVersion defines the versioned schema of this representation of an
     object. Servers should convert recognized schemas to the latest internal
     value, and may reject unrecognized values. More info:

   kind <string>
     Kind is a string value representing the REST resource this object
     represents. Servers may infer this from the endpoint the client submits
     requests to. Cannot be updated. In CamelCase. More info:

   metadata     <Object>
     Standard object's metadata. More info:

   spec <Object>
     Specification of the desired behavior of the pod. More info:

   status       <Object>
     Most recently observed status of the pod. This data may not be up to date.
     Populated by the system. Read-only. More info:
[root@master k8s]# kubectl explain pod.metadata
KIND:     Pod
VERSION:  v1

RESOURCE: metadata <Object>

DESCRIPTION:
     Standard object's metadata. More info:

     ObjectMeta is metadata that all persisted resources must have, which
     includes all objects users must create.

FIELDS:
   annotations  <map[string]string>
   clusterName  <string>
   creationTimestamp    <string>
   deletionGracePeriodSeconds   <integer>
   deletionTimestamp    <string>
   finalizers   <[]string>
   generateName <string>
   generation   <integer>
   managedFields        <[]Object>
   name <string>
   namespace    <string>
   ......

在Kubernetes中基本所有的资源的一级属性都是一样的,主要包括5部分:

  • apiVersion <string> 版本,由k8s内部定义,版本号可用kubectl api-version 查询到
  • kind <string> 类型,由k8s内部定义,版本号可用kubectl api-resources 查询到
  • metadata <object> 元数据,主要是资源标识和说明,常用的有name、namespace、labels等
  • spec <object> 描述,这是配置中最重要的一部分,里面是对各种资源配置的详细描述
  • status <object> 状态信息,里面的内容不需要定义,由k8s自己生成

在上面的属性中,spec是接下来研究的重点,继续看下它的常见子属性:

containers \<[]object\> 容器列表,用于定义容器的详细信息

nodeName \<map\> 根据nodeName的值将pod调度到指定的node节点上

nodeSelector \<string\> 根据nodeSelector中定义的信息选择将该pod调度到包含这些label的node上

hostNetwork \<bool\> 是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络

volumes \<[]object\> 存储卷,用于定义pod上面挂载的存储信息

restartPolicy \<string\> 重启策略,表示pod在遇到故障时候的处理策略

        

6.2 Pod配置

本小节主要来研究pod.spec.containers属性,这也是pod配置中最为关键的一项配置。

[root@master k8s]# kubectl explain pod.spec.containers
KIND:     Pod
VERSION:  v1

RESOURCE: containers <[]Object>

DESCRIPTION:
     List of containers belonging to the pod. Containers cannot currently be
     added or removed. There must be at least one container in a Pod. Cannot be
     updated.

     A single application container that you want to run within a pod.

FIELDS:
   args <[]string>
   command      <[]string>
   env  <[]Object>
   envFrom      <[]Object>    
   image        <string>  
   imagePullPolicy      <string>    
   lifecycle    <Object> 
   livenessProbe        <Object>    
   name <string> -required-     
   ports        <[]Object>   
   readinessProbe       <Object>    
   resources    <Object>     
   securityContext      <Object>     
   startupProbe <Object>     
   stdin        <boolean>   
   stdinOnce    <boolean>   
   terminationMessagePath       <string>   
   terminationMessagePolicy     <string>    
   tty  <boolean>    
   volumeDevices        <[]Object>   
   volumeMounts <[]Object>   
   workingDir   <string>   
6.2.1 基本配置

创建pod-base.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata: 
  name: pod-base
  namespace: dev
  labels:
    user: mage
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  - name: busybox
    image: busybox:1.30

上面定义了一个比较简单pod的配置,里面有两个容器:

  • nginx::1.17.1版本的镜像构建
  • busybos:1.30版本的镜像构建,busybox是一个小巧的linux命令集合
# 创建pod
[root@master k8s]# kubectl create -f pod-base.yml 
pod/pod-base created

# 查看pod状态
# READY 1/2 : 表示当前pod中有两个容器,其中一个准备就绪,一个未就绪
# RESTARTS : 重启次数,因为有一个容器故障了,pod一直在重启试图恢复它
[root@master k8s]# kubectl get pod -n dev
NAME          READY   STATUS             RESTARTS      AGE
pod-base      1/2     CrashLoopBackOff   4 (26s ago)   2m3s

# 可用通过describe查看详情
# 此时已经运行起来了一个基本pod,虽然它暂时有问题
[root@master k8s]# kubectl describe pod pod-base -n dev
6.2.2 镜像拉取

imagePullPolicy用于设置镜像拉取策略,k8s支持三种拉取策略:

  • Always:总是从远程仓库拉取镜像
  • IfNotPresent:本地有则使用本地的,本地没有就拉取远程仓库的
  • Never:只使用本地镜像,从不去远程仓库拉取,本地没有就报错

默认值说明:

  如果镜像tag为具体版本号,默认策略是:IfNotPresent
  
  如果镜像tag为:lastest,默认策略是Always
6.2.3 启动命令

        在前面的案例中,有一个问题没有解决,就是busybox容器一直没有启动成功,那么到底是什么原因呢?

        对Docker比较了解的就会知道,busybox容器在启动时,容器内没有进程在运行,因此Docker就会停止该容器,解决方案就是让其一直运行一个程序,这就可以用command来配置。

创建pod-command.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata: 
  name: pod-command
  namespace: dev
  labels:
    user: mage
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh", "-c", "touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt;sleep3;done;"]

command,用于在pod中的容器初始化完毕后运行一个命令。

# 创建pod
[root@master k8s]# kubectl create -f pod-command.yml 
pod/pod-command created

# 查看pod状态
# 此时发现pod中的两个容器都在运行
[root@master k8s]# kubectl get pod -n dev
NAME          READY   STATUS             RESTARTS        AGE
pod-command   2/2     Running            0               17s

# 进入pod中的busybox容器,查看文件内容
# kubectl exec pod名称 -n 命名空间 -it -c 容器名称 /bin/sh 在容器内部执行命令
# 使用这个命令就可以进入某个容器的内部,然后进行相关操作了
[root@master k8s]# kubectl exec pod-command -n dev -it -c busybox /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # tail -f /tmp/hello.txt 
12:41:34
12:41:34

特别说明:

	通过上面发现command已经可以完成启动命令和传递参数的功能,为什么还要提供一个args选项,用于传递参数?其实和Docker有点关系,kubernetes中的command和args两个参数其实是为了实现覆盖Dockerfile中的ENTRYPOINT的功能:
  • 如果command和args均没有写,那么用Dockerfile的配置。

  • 如果command写了,但是args没有写,那么Dockerfile默认的配置会被忽略,执行注入的command。

  • 如果command没有写,但是args写了,那么Dockerfile中配置的ENTRYPOINT命令会被执行,使用当前args的参数。

  • 如果command和args都写了,那么Dockerfile中的配置会被忽略,执行command并追加上args参数。

6.2.4 环境变量

创建pod-evn.yaml文件,内容如下

apiVersion: v1
kind: Pod
metadata:
  name: pod-env
  namespace: dev
  labels:
    user: xudaxian
spec:
  containers:
    - name: nginx # 容器名称
      image: nginx:1.17.1 # 容器需要的镜像地址
      imagePullPolicy: IfNotPresent # 设置镜像拉取策略
    - name: busybox # 容器名称
      image: busybox:1.30 # 容器需要的镜像地址
      command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt;sleep 3;done;"]
      env:
        - name: "username"
          value: "admin"
        - name: "password"
          value: "123456"

env:环境变量,用于在Pod中的容器设置环境变量。

6.2.5 端口设置

创建pod-ports.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-ports
  namespace: dev
  labels:
    user: xudaxian
spec:
  containers:
    - name: nginx                      # 容器名称
      image: nginx:1.17.1              # 容器需要的镜像地址
      imagePullPolicy: IfNotPresent    # 设置镜像拉取策略
      ports:
        - name: nginx-port      # 端口名称,如果执行,必须保证name在Pod中是唯一的
          containerPort: 80     # 容器要监听的端口 (0~65536)
          protocol: TCP         # 端口协议
6.2.6 资源配额
  • 容器中的程序要运行,肯定会占用一定的资源,比如CPU和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量的资源,导致其他的容器无法运行。针对这种情况,kubernetes提供了对内存和CPU的资源进行配额的机制,这种机制主要通过resources选项实现,它有两个子选项:

    • limits:用于限制运行的容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启。
    • requests:用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动。
  • 可以通过上面的两个选项设置资源的上下限。

  • 创建pod-resoures.yaml文件,内容如下

apiVersion: v1
kind: Pod
metadata:
  name: pod-resoures
  namespace: dev
  labels:
    user: xudaxian
spec:
  containers:
    - name: nginx                          # 容器名称
      image: nginx:1.17.1                   # 容器需要的镜像地址
      imagePullPolicy: IfNotPresent          # 设置镜像拉取策略
      ports:             # 端口设置
        - name: nginx-port    # 端口名称,如果执行,必须保证name在Pod中是唯一的
          containerPort: 80    # 容器要监听的端口 (0~65536)
          protocol: TCP        # 端口协议
      resources:            # 资源配额
        limits:             # 限制资源的上限
          cpu: "2"          # CPU限制,单位是core数
          memory: "10Gi"    # 内存限制
        requests:           # 限制资源的下限
          cpu: "1"          # CPU限制,单位是core数 
          memory: "10Mi"    # 内存限制
  • cpu:core数,可以为整数或小数。
  • memory:内存大小,可以使用Gi、Mi、G、M等形式。

6.3 Pod生命周期

我们一般将pod对象从创建至死亡的这段时间范围称为pod的生命周期,它包含下面的过程:

  • pod创建过程
  • 运行初始化容器(init container)过程
  • 运行主容器(main container)过程
    • 容器启动后钩子(post start)、容器终止前钩子(pre stop)
    • 容器的存活性探测(liveness probe)、就绪性探测(readiness probe)
  • pod终止过程

在这里插入图片描述

在整个生命周期中,pod会出现5中状态,分别如下:

  • 挂起(Pending):apiServer已经创建了pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中
  • 运行中(Running):pod已经被调度至某节点,并且所有容器都已经被kubelet创建完成
  • 成功(Succeeded):pod中所有的容器都已经成功终止并且不会被重启
  • 失败(Failed):所有的容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态
  • 未知(Unkonwn):apiServer无法正常获取到pod对象的状态信息,通常由网络通信失败导致
6.3.1 创建和终止

pod的创建过程

  1. 用户通过kubectl或其它api客户端提交需要创建的pod信息给apiServer
  2. apiServer开始生成pod对象的信息,并将信息存入etcd,然后返回确认信息至客户端
  3. apiServer开始反映etcd中的pod对象的变化,其它组件使用watch机制来跟踪检查apiServer上的变动
  4. schedule发现有新的pod对象需要创建,开始为pod分配主机并将结果信息更新至apiServer
  5. node节点上的kubelet发现有pod调度过来,尝试调用docker启动容器,并将结果送至apiServer
  6. apiServer将收到的pod状态信息存入etcd中

在这里插入图片描述

pod的终止过程

  1. 用户向apiServer发送删除pod对象的命令
  2. apiServer中的pod对象信息会随着时间的推移而更新,在宽限期内(默认30s),pod被视为dead
  3. 将pod标记为terminating状态
  4. kubelet在监控到pod对象转为terminating状态的同时启动pod关闭过程
  5. 端点控制器监控到pod对象的关闭行为时将其从所有匹配到此端点的service资源的端点列表中移除
  6. 如果当前pod对象定义了preStop钩子处理器,则在其标记为terminating后即会以同步的方式启动执行
  7. pod对象中的容器进程收到停止信号
  8. 宽限期结束后,若pod中还存在仍在运行的进程,那么pod对象会收到立即终止的信号
  9. kubelet请求apiServer将此pod资源的宽限期设置为0从而完成删除操作,此时pod对于用户已不可见
6.3.2初始化容器

初始化容器是在pod的主容器启动之前要运行的容器,主要做一些容器的前置工作,它具有两大特征:

  1. 初始化容器必须运行完成直至结束,若初始化容器运行失败,那么k8s需要重启它直到成功完成
  2. 初始化容器必须按照定义的顺序执行,当且仅当一个成功后,后面的一个才能运行

初始化容器有很多的应用场景,下面列出的两个是最常见的几个:

  • 提供主容器镜像中不具备的工具程序或自定义代码
  • 初始化容器要先于应用容器串行启动并运行完成,因此可用于延后应用容器的启动直至依赖的条件得到满足

接下来做一个案例,模拟下面这个需求:

	假设以主容器运行nginx,但是要求在运行nginx之前要先能够连接上mysql和redis所在服务器

	为了简化测试,事先规定好mysql(192.168.226.201)和redis(192.168.226.202)服务器的地址

创建pod-initcontainer.yaml,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-initcontainer
  namespace: dev
spec:
  containers:
  - name: main-container
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
  initContainers:
  - name: test-mysql
    image: busybox:1.30
    command: ['sh', '-c', 'until ping 192.168.226.201 -c 1; do echo waiting for mysql...;sleep 2;done;']
  - name: test-redis
    image: busybox:1.30
    command: ['sh', '-c', 'until ping 192.168.226.202 -c 1; do echo waiting for redis...;sleep 2;done;']
# 创建pod
[root@master k8s]# kubectl create -f pod-initcontainer.yml 
pod/pod-initcontainer created

# 查看pod状态
# 发现pod卡在启动第一个初始化容器过程中,后面的容器不会运行
[root@master k8s]# kubectl get pod -n dev
NAME                READY   STATUS             RESTARTS          AGE
pod-initcontainer   0/1     Init:0/2           0                 73s

[root@master k8s]# kubectl describe pod pod-initcontainer -n dev
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  99s   default-scheduler  Successfully assigned dev/pod-initcontainer to node2
  Normal  Pulled     97s   kubelet            Container image "busybox:1.30" already present on machine
  Normal  Created    97s   kubelet            Created container test-mysql
  Normal  Started    97s   kubelet            Started container test-mysql
  
# 动态查看pod
[root@master k8s]# kubectl get pod pod-initcontainer -n dev -w
NAME                READY   STATUS     RESTARTS   AGE
pod-initcontainer   0/1     Init:0/2   0          2m36s
pod-initcontainer   0/1     Init:1/2   0          5m28s
pod-initcontainer   0/1     PodInitializing   0          5m29s
pod-initcontainer   1/1     Running           0          5m30s

# 接下来新开一个shell,为当前服务器新增两个ip,观察pod的变化
[root@master ~]# ifconfig ens33:1 192.168.226.201 netmask 255.255.255.0 up
[root@master ~]# ifconfig ens33:2 192.168.226.202 netmask 255.255.255.0 up
6.3.3 钩子函数

钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。

Kubernetes在主容器启动之后和停止之前提供了两个钩子函数:

  • post start:容器创建后执行,如果失败了会重启容器
  • pre stop:容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作

钩子处理器支持使用下面三种方式定义动作:

Exec命令:在容器内执行一次命令

......
  lifecycle:
    postStart:
      exec:
        command:
        - cat
        - /tmp/healthy
......

TCPSocket:在当前容器尝试访问指定的socket

......
  lifecycle:
    postStart:
      tcpSocket:
        port: 8080
......

HTTPGet:在当前容器中向某url发起http请求

......
  lifecycle:
    postStart:
      httpGet:
        path: /         # URL地址
        port: 80        # 端口号
        host: 192.168.226.168     # 主机地址
        scheme: HTTP       # 支持的协议HTTP或HTTPS
......

接下来以exec的方式为例,演示下钩子函数的使用,创建pod-hook-exec.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata: 
  name: pod-hook-exec
  namespace: dev
spec:
  containers:
  - name: main-container
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
    lifecycle:
      postStart:      # 在容器启动后执行一个命令,修改nginx的默认首页内容
        exec:
          command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]
      preStop:         # 在容器停止前停止nginx服务
        exec:
          command: ["/user/sbin/nginx", "-s", "quit"]
# 创建pod
[root@master k8s]# kubectl create -f pod-hook-exec.yml 
pod/pod-hook-exec created

# 查看pod
[root@master k8s]# kubectl get pod pod-hook-exec -n dev -o wide
NAME            READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-hook-exec   1/1     Running   0          57s   10.244.1.26   node1   <none>           <none>

# 访问nginx服务
[root@master k8s]# curl 10.244.1.26:80
postStart...
6.3.4 容器探测

        容器探测用于检测容器中的实例是否正常工作,是保障业务可用性的一种传统机制。如果经过探测,实例的状态不符合预期,那么k8s就会把问题实例“摘除”,不承担业务流量。k8s提供了两种探针来实现容器探测,分别是:

  • liveness probes:存活性探针,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启容器
  • readiness probes:就绪性探针,用于检测应用实例当前是否可以接受请求,如果不能,k8s不会转发流量

livenessProbe决定是否重启容器,readinessProbe决定是否将请求转发给容器

上面两种探针目前均支持三种探测方式:

Exec命令:在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常

......
  livenessProbe:
    exec:
      command:
      - cat
      - /tmp/healthy
......

TCPSocket:将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常

......
  livenessProbe:
    tcpSocket:
      port: 8080
......

HTTPGet:调用容器内Web应用的URL,如果返回的状态码在200到399之间,则认为程序正常,否则不正常

......
  livenessProbe:
    httpGet:
      path: /         # URL地址
      port: 80        # 端口号
      host: 192.168.226.168     # 主机地址
      scheme: HTTP       # 支持的协议HTTP或HTTPS
......

下面以livenessProbe为例,做几个演示:

方式一:Exec

创建pod-liveness-exec.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-exec
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      exec:
        command: ["/bin/cat", "/tmp/hello.txt"]  # 执行一个查看文件的命令
# 创建pod
[root@master k8s]# kubectl create -f pod-liveness-exec.yml 
pod/pod-liveness-exec created

# 查看pod详情
[root@master k8s]# kubectl describe pod pod-liveness-exec -n dev
Events:
  Type     Reason     Age               From               Message
  ----     ------     ----              ----               -------
  Normal   Scheduled  36s               default-scheduler  Successfully assigned dev/pod-liveness-exec to node1
  Normal   Pulled     7s (x2 over 36s)  kubelet            Container image "nginx:1.17.1" already present on machine
  Normal   Created    7s (x2 over 36s)  kubelet            Created container nginx
  Normal   Started    7s (x2 over 36s)  kubelet            Started container nginx
  Warning  Unhealthy  7s (x3 over 27s)  kubelet            Liveness probe failed: /bin/cat: /tmp/hello.txt: No such file or directory
  Normal   Killing    7s                kubelet            Container nginx failed liveness probe, will be restarted
  
# 观察上面的信息就会发现nginx容器启动之后就进行了健康检查
# 检查失败后,容器被kill掉,然后尝试重启容器(这是重启策略,后面详解)
# 稍等一会后,在观察pod信息,就可以看到RESTARTS不再是0,而是一直增长
[root@master k8s]# kubectl get pod pod-liveness-exec -n dev
NAME                READY   STATUS             RESTARTS      AGE
pod-liveness-exec   0/1     CrashLoopBackOff   4 (10s ago)   2m40s

方式二:TCPSocket

创建pod-liveness-tcpsocket.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-tcpsocket
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      tcpSocket:
        port: 8080       # 尝试访问8080端口
# 创建pod
[root@master k8s]# kubectl create -f pod-liveness-tcpsocket.yml 
pod/pod-liveness-tcpsocket created

# 查看pod详情
[root@master k8s]# kubectl describe pod pod-liveness-tcpsocket -n dev
Events:
  Type     Reason     Age               From               Message
  ----     ------     ----              ----               -------
  Normal   Scheduled  32s               default-scheduler  Successfully assigned dev/pod-liveness-tcpsocket to node2
  Normal   Pulled     3s (x2 over 31s)  kubelet            Container image "nginx:1.17.1" already present on machine
  Normal   Created    3s (x2 over 31s)  kubelet            Created container nginx
  Normal   Started    3s (x2 over 31s)  kubelet            Started container nginx
  Warning  Unhealthy  3s (x3 over 23s)  kubelet            Liveness probe failed: dial tcp 10.244.2.22:8080: connect: connection refused
  Normal   Killing    3s                kubelet            Container nginx failed liveness probe, will be restarted
  
# 查看pod信息,可以看到已经重启了两次
[root@master k8s]# kubectl get pod pod-liveness-tcpsocket -n dev
NAME                     READY   STATUS    RESTARTS      AGE
pod-liveness-tcpsocket   1/1     Running   2 (14s ago)   74s

方式三:HTTPGet

创建pod-liveness-httpget.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-httpget
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 801
    livenessProbe:
      httpGet:
        scheme: HTTP
        port: 80
        path: /hello
# 创建pod
[root@master k8s]# kubectl create -f pod-liveness-httpget.yml 
pod/pod-liveness-httpget created

# 查看pod详情,访问返回的状态码为404
[root@master k8s]# kubectl describe pod pod-liveness-httpget -n dev
Events:
  Type     Reason     Age               From               Message
  ----     ------     ----              ----               -------
  Normal   Scheduled  24s               default-scheduler  Successfully assigned dev/pod-liveness-httpget to node2
  Normal   Pulled     23s               kubelet            Container image "nginx:1.17.1" already present on machine
  Normal   Created    23s               kubelet            Created container nginx
  Normal   Started    23s               kubelet            Started container nginx
  Warning  Unhealthy  5s (x2 over 15s)  kubelet            Liveness probe failed: HTTP probe failed with statuscode: 404
  
# 查看pod信息
[root@master k8s]# kubectl get pod pod-liveness-httpget -n dev
NAME                   READY   STATUS    RESTARTS     AGE
pod-liveness-httpget   1/1     Running   2 (7s ago)   67s

至此,已经使用livenessProbe演示了三种探测方式,但是查看livenessProbe的子属性,会发现除了这三种方式,还有一些其它的配置,在这里解释一下:

[root@master k8s]# kubectl explain pod.spec.containers.livenessProbe
FIELDS:
   exec <Object>   
   grpc <Object>
   httpGet      <Object>    
   initialDelaySeconds  <integer>       # 容器启动后等待多少秒执行第一次探测    
   timeoutSeconds       <integer>       # 探测超时时间,默认1s,最小1s
   periodSeconds        <integer>       # 执行探测的频率,默认10s,最小1s
   failureThreshold     <integer>       # 连续探测失败多少次才被认定失败。默认3,最小值1
   successThreshold     <integer>       # 连续探测成功多少次才被认定为成功。默认1
   tcpSocket    <Object>     
   terminationGracePeriodSeconds        <integer>    
  
6.3.5 重启策略
	在上一节中,一旦容器探测出现了问题,k8s就会对容器所在的pod进行重启,其实这是由pod的重启策略决定的,pod的重启策略有三种,分别如下:
  • Always:容器失效时,自动重启该容器,这也是默认值
  • OnFailure:容器终止运行且退出码不为0时重启
  • Never:不论状态如何,都不重启该容器

        重启策略适用于pod对象中所有的容器,首次需要重启的容器,将在需要时立即重启,随后再次需要重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长为10s、20s、40s、80s、160s和300s,300s是最大延迟时长。

创建pod-restartPolicy.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: pod-restartpolicy
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      httpGet:
        path: /hello
        scheme: HTTP
        port: 80
  restartPolicy: Never       # 设置重启策略为Never
# 创建pod
[root@master k8s]# kubectl create -f pod-restartPolicy.yml 
pod/pod-restartpolicy create

# 查看pod详情
[root@master k8s]# kubectl describe pod pod-restartpolicy -n dev
Events:
  Type     Reason     Age               From               Message
  ----     ------     ----              ----               -------
  Normal   Scheduled  25s               default-scheduler  Successfully assigned dev/pod-restartpolicy to node1
  Normal   Pulled     25s               kubelet            Container image "nginx:1.17.1" already present on machine
  Normal   Created    25s               kubelet            Created container nginx
  Normal   Started    25s               kubelet            Started container nginx
  Warning  Unhealthy  6s (x2 over 16s)  kubelet            Liveness probe failed: HTTP probe failed with statuscode: 404
  
# 查看pod信息,发现STATUS为Completed,多次查看发现RESTARTS也一直是0
[root@master k8s]# kubectl get pod pod-restartpolicy -n dev
NAME                READY   STATUS      RESTARTS   AGE
pod-restartpolicy   0/1     Completed   0          43s

        

6.4 Pod调度

	在默认情况下,一个Pod在哪个节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不能满足需求,因为很多情况下,我们想控制某些Pod到某些节点上,那么应该怎么做呢?这就要了解K8S对Pod的调度规则,K8S提供了四大调度方式:
  • 自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出
  • 定向调度:NodeName、NodeSelector
  • 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
  • 污点(容忍)调度:Taints、Toleration
6.4.1 定向调度
	定向调度,指定是利用在pod上声明NodeName或者NodeSelector,以此将Pod调度到期望的node节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标node不存在,也会向上面进行调度,只不过pod运行失败而已。

NodeName

	NodeName用于强制约束将Pod调度到指定Name的Node节点上。这种方式,其实是直接跳过Scheduler的调度逻辑,直接将Pod调度到指定名称的节点上。

接下来,实验一些,创建pod-nodename.yaml文件:

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodename
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
  nodeName: node1
# 创建pod
[root@master k8s]# kubectl create -f pod-nodename.yml 
pod/pod-nodename created

# 查看pod信息,确实调度到了node1节点上
[root@master k8s]# kubectl get pod pod-nodename -n dev -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-nodename   1/1     Running   0          53s   10.244.1.29   node1   <none>           <none>

# 接下来删除pod,然后将yml中的nodeName改为node3(并没有node3节点)
# 发现pod被调度到了node3,但是由于没有node3,所以pod是挂起状态
kubectl delete -f pod-nodename.yml
kubectl create -f pod-nodename.yml
[root@master k8s]# kubectl get pod pod-nodename -n dev -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP       NODE    NOMINATED NODE   READINESS GATES
pod-nodename   0/1     Pending   0          17s   <none>   node3   <none>           <none>

NodeSelector

	NodeSelector用于将pod调度到添加了指定标签的node节点上。它是通过K8S的label-selector机制实现的,也就是说,在pod创建之前,会由Scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,然后将node调度到目标节点,该匹配规则是强制约束。

接下来实验一下:

1.首先分别为node节点添加标签

kubectl label node node1 nodeenv=pro
kubectl label node node2 nodeenv=test

2.创建pod-nodeselector.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeselector
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  nodeSelector:
    nodeenv: pro              # 指定pod调度到具有nodeenv=pro标签的节点上
# 创建pod
[root@master k8s]# kubectl create -f pod-nodeselector.yml 
pod/pod-nodeselector created

# 查看pod信息,pod被调度到了node1上
[root@master k8s]# kubectl get pod pod-nodeselector -n dev -o wide
NAME               READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-nodeselector   1/1     Running   0          26s   10.244.1.30   node1   <none>           <none>
6.4.2 亲和性调度
	上一节,介绍了两种定向调度的方式,使用起来非常方便,但是也有一定的问题,那就是如果没有满足条件的Node,那么Pod将不会被运行,即使在集群中还有可用Node列表也不行,这就限制了它的使用场景。

	基于上面的问题,K8S还提供了一种亲和性调度(Affinity)。它在NodeSelector的基础之上进行了扩展,可用通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。

Affinity注意分为三类:

  • nodeAffinity(node亲和性):以node为目标,解决pod可以调度到哪些node的问题
  • podAffinity(pod亲和性):以pod为目标,解决pod可以和哪些已存在的pod部署在同一个node中的问题。
  • podAntiAffinity(pod反亲和性):以pod为目标,解决pod不能和哪些已存在pod部署在同一个node中的问题。

关于亲和性和反亲和性使用场景的说明:

亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用尽可能的靠近,这样可以减少因网络通信而带来的性能损耗。

反亲和性:当应用采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性。

NodeAffinity

首先来看一下NodeAffinity的可配置项:

pod.spec.affinity.nodeAffinity
  requiredDuringSchedulingIgnoredDuringExecution:  # Node节点必须满足指定的所有规则才可以,相当于硬限制
    nodeSelectorTerms:       # 节点选择列表
      matchFields:           # 按节点字段列出的节点选择器要求列表  
      matchExpressions:      # 按节点标签列出的节点选择器要求列表(推荐)
        key:         # 键
        values:      # 值
        operator:           # 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
  preferredDuringSchedulingIgnoredDuringExecution:  # 优先调度到满足指定的规则的Node,相当于软限制 (倾向)     
    preference:            # 一个节点选择器项,与相应的权重相关联
      matchFields:         # 按节点字段列出的节点选择器要求列表
      matchExpressions:    # 按节点标签列出的节点选择器要求列表(推荐)
        key:           # 键
        values:         # 值
        operator:              # 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt  
    weight:                # 倾向权重,在范围1-100。
- matchExpressions:
	- key: nodeenv               # 匹配存在标签的key为nodeenv的节点
	  operator: Exists   
	- key: nodeenv               # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点
	  operator: In    
      values: ["xxx","yyy"]
    - key: nodeenv               # 匹配标签的key为nodeenv,且value大于"xxx"的节点
      operator: Gt   
      values: "xxx"

接下来首先演示一下requiredDuringSchedulingIgnoredDuringExecution,

创建pod-nodeaffinity-required.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-required
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:           # 亲和性设置
    nodeAffinity:     # node亲和性设置
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:         # 配置env的值在["xxx", "yyy"]中的标签
          - key: nodeenv
            operator: In
            values: ["xxx", "yyy"]
# 创建pod
[root@master k8s]# kubectl create -f pod-nodeaffinity-required.yaml 
pod/pod-nodeaffinity created

# 查看pod状态(运行失败)
[root@master k8s]# kubectl get pod pod-nodeaffinity-required -n dev -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
pod-nodeaffinity-required   0/1     Pending   0          5s    <none>   <none>   <none>           <none>

# 查看pod详情
[root@master k8s]# kubectl describe pod pod-nodeaffinity-required -n dev
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  40s   default-scheduler  0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match Pod's node affinity/selector.
  
# 接下来删除该pod
# 将配置文件中的values改为["pro", "yyy"],再次运行
kubectl delete -f pod-nodeaffinity-required.yaml
kubectl create -f pod-nodeaffinity-required.yaml

# 然后查看pod状态,发现在node1上运行
[root@master k8s]# kubectl get pod pod-nodeaffinity-required -n dev -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-nodeaffinity-required   1/1     Running   0          30s   10.244.1.31   node1   <none>           <none>

接下来在演示一下preferredDuringSchedulingIgnoredDuringExecution

创建pod-nodeaffinity-preferred.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-preferred
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:                # 亲和性设置
    nodeAffinity:          # 设置node亲和性
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: nodeenv
            operator: In
            values: ["xxx", "yyy"]
# 创建pod
[root@master k8s]# kubectl create -f pod-nodeaffinity-preferred.yaml 
pod/pod-nodeaffinity-preferred created

# 查看pod状态(运行成功)
[root@master k8s]# kubectl get pod pod-nodeaffinity-preferred -n dev -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-nodeaffinity-preferred   1/1     Running   0          31s   10.244.2.24   node2   <none>           <none>

NodeAffinity规则设置的注意事项:

  • 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能运行在指定的Node上
  • 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可
  • 如果一个nodeSelectorTerms中有多个matchExpression,则一个节点必须满足所有的才能匹配成功
  • 如果一个pod所在的node在pod运行期间其标签发生了改变,不再符合该pod节点亲和性需求,则系统将忽略此变化

PodAffinity

PodAffinity主要实现以一个运行的Pod为参照,让新创建的Pod跟参照pod在一个拓扑域中的功能。

首先来看一下PodAffinity的可配置项:

pod.spec.affinity.podAffinity
  requiredDuringSchedulingIgnoredDuringExecution:     # 硬限制
    namespaces:                                # 指定参照pod的namespace
    topologyKey:                               # 指定调度作用域
    labelSelector:                             # 标签选择器
      matchExpressions:                       # 按节点标签列出的节点选择器要求列表(推荐)
        key:           # 键
        values:        # 值
        operator:         # 关系符 支持In, NotIn, Exists, DoesNotExist.
      matchLabels:        # 指多个matchExpressions映射的内容  
  preferredDuringSchedulingIgnoredDuringExecution:       # 软限制    
    podAffinityTerm:           # 选项
      namespaces:
      topologyKey:
      labelSelector:
        matchExpressions: 
          key:         #  键  
          values:       # 值  
          operator:
        matchLabels: 
    weight:           # 倾向权重,在范围1-1

topologyKey用于指定调度时作用域,例如:

	如果指定kubernetes.io/hostname,那就是以Node节点为区分范围

	如果指定beta.kubernetes.io/os,则是以Node节点的操作系统类型来区分

接下来演示一下requiredDuringSchedulingIgnoredDuringExecution

1)首先创建一个参照Pod,pod-podaffinity-target.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-podaffinity-target
  namespace: dev
  labels:
    podenv: pro           # 设置标签
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  nodeName: node1         # 将目标pod明确指定到node1上
# 启动目标pod
[root@master k8s]# kubectl create -f pod-podaffinity-target.yaml 
pod/pod-podaffinity-target created

# 查看pod状况
[root@master k8s]# kubectl get pod pod-podaffinity-target -n dev -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-podaffinity-target   1/1     Running   0          19s   10.244.1.32   node1   <none>           <none>

2)创建pod-podaffinity-required.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-podaffinity-required
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: podenv
            operator: In
            values: ["xxx", "yyy"]
        topologyKey: kubernetes.io/hostname

上面配置表达的意思是:新pod必须要与拥有标签podenv=xxx或podenv=yyy的pod在 同一node上,显然现在没有这样的pod,接下来运行测试一下:

# 创建pod
[root@master k8s]# kubectl create -f pod-podaffinity-required.yaml 
pod/pod-podaffinity-required created

# 查看pod状态,发现未运行
[root@master k8s]# kubectl get pod pod-podaffinity-required -n dev -o wide
NAME                       READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
pod-podaffinity-required   0/1     Pending   0          24s   <none>   <none>   <none>           <none>

# 查看详细信息
[root@master k8s]# kubectl describe pod pod-podaffinity-required -n dev 
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  76s   default-scheduler  0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match pod affinity rules.
  Warning  FailedScheduling  10s   default-scheduler  0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match pod affinity rules.
  
# 接下来删除该pod将values改为["pro", "yyy"],再次运行
kubectl delete -f pod-podaffinity-required.yaml 
kubectl create -f pod-podaffinity-required.yaml

# 查看pod状态,发现运行在node1上
[root@master k8s]# kubectl get pod pod-podaffinity-required -n dev -o wide
NAME                       READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-podaffinity-required   1/1     Running   0          22s   10.244.1.33   node1   <none>           <none>

PodAntiAffinity

PodAntiAffinity主要实现以运行的pod为参照,让新创建的pod跟参照pod不在一个区域的功能。

它的配置方式和选项与PodAffinity是一样的,在这里不做详细解释,直接做一个测试案例:

1)继续使用上个案例中的目标pod

2)创建pod-podantiaffinity-required.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-podantiaffinity-required
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: podenv
            operator: In
            values: ["pro"]
        topologyKey: kubernetes.io/hostname

上面配置表达的意思是:新建pod必须要与拥有标签podenv=pro的pod不在同一个node上。

# 创建pod
[root@master k8s]# kubectl create -f pod-podantiaffinity-required.yaml 
pod/pod-podantiaffinity-required created

# 查看pod状态,发现pod运行在node2上
[root@master k8s]# kubectl get pod pod-podantiaffinity-required -n dev -o wide
NAME                           READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-podantiaffinity-required   1/1     Running   0          27s   10.244.2.25   node2   <none>           <none>
6.4.3 污点和容忍

污点(Taints)

	前面的调度方式都是站在pod的角度上,通过在pod上添加属性,来确定pod是否要调度到指定的node上,其实我们也可以站在node的角度上,通过在node上添加`污点`属性,来决定是否允许pod调度过来。

	node被设置上污点之后就和pod之间存在了一种相斥的关系,进而拒绝pod调度进来,甚至可以将已存在的pod驱逐出去。

污点的格式为:key=value:effect,key和value是污点的标签,effect描述污点的作用,支持下面三个选项:

  • PreferNoSchedule:k8s将尽量避免把pod调度到具有该污点的node上,除非没有其它节点可调度
  • NoSchedule:k8s将不会把pod调度到具有该污点的node上,但不会影响当前node上已存在的pod
  • NoExecute:k8s将不会把pod调度到具有该污点的node上,同时也会将node上已存在的pod驱离

在这里插入图片描述

使用kubectl设置和去除污点的命令如下:

# 设置污点
kubectl taint node node1 key=value:effect

# 去除污点
kubectl taint node node1 key:effect-

# 去除所有污点
kubectl taint node node1 key-

接下来演示一下污点的效果:

  1. 准备节点node1(为了演示效果更加明显,暂时停止node2节点)
  2. 为node1节点设置一个污点:tag=wudian:PreferNoSchedule,然后创建pod1
  3. 修改node1节点设置一个污点:tag=wudian:NoSchedule,然后创建pod2
  4. 修改node1节点设置一个污点:tag=wudian:NoExecute,然后创建pod3
# 关闭node2虚拟机
# 查看集群节点
[root@master k8s]# kubectl get node
NAME     STATUS     ROLES                  AGE     VERSION
master   Ready      control-plane,master   3d22h   v1.23.1
node1    Ready      <none>                 3d22h   v1.23.1
node2    NotReady   <none>                 3d22h   v1.23.1

# 给node1设置污点
[root@master k8s]# kubectl taint node node1 tag=wudian:PreferNoSchedule 
node/node1 tainted

# 创建pod1
[root@master k8s]# kubectl run nginx --image=nginx -n dev
pod/nginx created

# 查看pod状态
[root@master k8s]# kubectl get pod -n dev
NAME                           READY   STATUS      RESTARTS   AGE
nginx                          1/1     Running     0          31s

# 为node1修改污点为NoSchedule
[root@master k8s]# kubectl taint node node1 tag:PreferNoSchedule-
node/node1 untainted
[root@master k8s]# kubectl taint node node1 tag=wudian:NoSchedule
node/node1 tainted

# 创建pod2
[root@master k8s]# kubectl run nginx2 --image=nginx:1.17.1 -n dev
pod/nginx2 created

# 查看pod状态,发现nginx2是挂起状态
[root@master k8s]# kubectl get pod -n dev
NAME                           READY   STATUS      RESTARTS   AGE
nginx                          1/1     Running     0          2m34s
nginx2                         0/1     Pending     0          11s

# 查看pod2的详细信息
[root@master k8s]# kubectl describe pod nginx2 -n dev
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  45s   default-scheduler  0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 1 node(s) had taint {node.kubernetes.io/unreachable: }, that the pod didn't tolerate, 1 node(s) had taint {tag: wudian}, that the pod didn't tolerate.
  
# 为node1修改污点为NoExecute
[root@master k8s]# kubectl taint node node1 tag:NoSchedule-
node/node1 untainted
[root@master k8s]# kubectl taint node node1 tag=wudian:NoExecute
node/node1 tainted

# 启动pod3
[root@master k8s]# kubectl run nginx3 --image=nginx:1.17.1 -n dev
pod/nginx3 created

# 查看pod状态,三个pod都没有运行
[root@master k8s]# kubectl get pod -n dev
NAME                           READY   STATUS        RESTARTS   AGE
nginx3                         0/1     Pending       0          75s

小提示:

  使用kubeadm搭建的集群,默认就会为master节点添加一个污点标记,所以pod就不会调度到该节点上

容忍(Toleration)

	上面介绍了污点的作用,我们可以在node上添加污点用于拒绝pod调度上来,但是如果就是想将一个pod调度到一个有污点的node上去,这时候应该怎么做呢?这就要用到`容忍`。

在这里插入图片描述

污点就是拒绝,容忍就是忽略,Node通过污点拒绝pod调度上去,pod通过容忍忽略拒绝

下面先通过一个案例看下效果:

  1. 上一节已经在node1节点上打上了NoExecute的污点,此时pod是调度不上去的
  2. 本小节,可以通过给pod添加容忍,然后将其调度上去

创建pod-toleration.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: pod-toleration
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  tolerations:            # 添加容忍
  - key: "tag"            # 要添加的污点的key
    operator: "Equal"     # 操作符
    value: "wudian"       # 容忍的污点的value
    effect: "NoExecute"   # 添加容忍的规则,这里必须和标记的污点规则相同
# 创建pod
[root@master k8s]# kubectl create -f pod-toleration.yaml 
pod/pod-toleration created

# 查看pod状态
[root@master k8s]# kubectl get pod pod-toleration -n dev -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-toleration   1/1     Running   0          23s   10.244.1.36   node1   <none>           <none>

下面看一下容忍的详细配置:

[root@master k8s]# kubectl explain pod.spec.tolerations
FIELDS:
   effect       <string>       # 对应污点的effect,空值意味着匹配所有影响
   key  <string>               # 对应要容忍的污点的键,空意味着匹配所有的键
   operator     <string>       # key-value的运算符,支持Equal和Exists(默认) 
   tolerationSeconds    <integer>   # 容忍时间,当effect为NoExecute时生效,表示pod在node上的停留时间
   value        <string>            # 对应要容忍的污点的值

 

7、Pod控制器

本章节主要介绍各种Pod控制器的详细使用。

        

7.1 Pod控制器介绍

在Kubernetes中,按照Pod的创建方式可以将其分为两类:

  • 自主式Pod:K8S直接创建出来的Pod,这种Pod删除后就没有了,也不会重建
  • 控制器创建的Pod:通过控制器创建的Pod,这种Pod删除了之后还会自动重建

什么是Pod控制器

  Pod控制器就是管理Pod的中间层,使用了Pod控制器之后,我们只需要告诉Pod控制器,想要多少个什么样的Pod就可以了,它就会创建出满足条件的Pod并确保每一个Pod处于用户期望的状态,如果Pod在运行中出现故障,控制器会基于指定策略重启或者重建Pod。

在Kubernetes中,有很多类型的Pod控制器,每种都有自己的适合的场景,常见的有下面这些:

  • ReplicationController:比较原始的Pod控制器,已经被废弃,由ReplicaSet替代
  • ReplicaSet:保证指定数量的Pod运行,并支持Pod数量变更,镜像版本变更
  • Deployment:通过控制ReplicaSet来控制Pod,并支持滚动升级、版本回退
  • Horizontal Pod Autoscaler:可以根据集群负载自动调整Pod的数量,实现削峰填谷
  • DaemonSet:在集群中的指定Node上都运行一个副本,一般用于守护进程类的任务
  • Job:它创建出的Pod只要完成任务就立即退出,用于执行一次性任务
  • CronJob:它创建的Pod会周期性地执行,用于执行周期性任务
  • StatefulSet:管理有状态应用

        

7.2 ReplicaSet(RS)

	ReplicaSet的主要作用是**保证一定数量的Pod能够正常运行**,它会持续监听这些Pod的运行状态,一旦Pod发生故障,就会重启或重建。同时它还支持对**Pod数量的扩缩容**和**镜像版本的升级**。

在这里插入图片描述

ReplicaSet的资源清单:

apiVersion: apps/v1          # 版本号
kind: ReplicaSet             # 类型
metadata:                    # 元数据
  name:                      # rs名称
  namespace:                 # 所属命名空间
  labels:                    # 标签
    controller: rs          
spec:
  replicas: 3                # Pod副本数量
  selector:                  # 选择器,通过它指定该控制器管理哪些Pod
    matchLabels:             # Labels匹配规则
      app: nginx-pod
    matchExpressions:         
      - {key: app, operator: In, values: [nginx-pod]}
   template:                 # 模板,当副本数量不足时会根据下面的模板创建Pod副本
     metadata:
       labels:
         app: nginx-pod
     spec:
       containers:
       - name: nginx
         image: nginx:1.17.1
         prots:
         - containerPort: 80

在这里面,需要了解的配置项就是spec下面几个选项:

  • replicas:指定副本数量,其实就是当前rs创建出来的Pod的数量,默认为1
  • selector:选择器,它的作用是建立Pod控制器和Pod之间的关联关系,采用的是Label Selector机制,在Pod模板上 定义Label,在控制器上定义选择器,就可以表明当前控制器能管理哪些Pod了
  • template:模板,就是当前控制器创建Pod使用的模板,里面其实就是前一章学过的Pod的定义

创建ReplicaSet

创建pc-replicaset.yaml:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: pc-replicaset
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
# 创建rs
[root@master k8s]# kubectl create -f pc-replicaset.yaml 
replicaset.apps/pc-replicaset created

# 查看rs
# DESIRED:期望副本数量
# CURRENT:当前副本数量
# READY:已经准备好提供服务的副本数量
[root@master k8s]# kubectl get rs pc-replicaset -n dev -o wide
NAME            DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES         SELECTOR
pc-replicaset   3         3         3       26s   nginx        nginx:1.17.1   app=nginx-pod

# 查看当前控制器创建的Pod
# 这里发现控制器创建出来的Pod的名称是在控制器名称后面拼接了-xxxxx随机码
[root@master k8s]# kubectl get pod -n dev -o wide
NAME                  READY   STATUS    RESTARTS   AGE     IP            NODE    NOMINATED NODE   READINESS GATES
pc-replicaset-h72ms   1/1     Running   0          2m27s   10.244.2.29   node2   <none>           <none>
pc-replicaset-w7vdw   1/1     Running   0          2m27s   10.244.2.27   node2   <none>           <none>
pc-replicaset-xgc2w   1/1     Running   0          2m27s   10.244.2.28   node2   <none>           <none>

扩缩容

# 1.编辑rs的副本数量,修改spec:replicas:6,然后wq保存即可
[root@master k8s]# kubectl edit rs pc-replicaset -n dev
replicaset.apps/pc-replicaset edited

# 查看rs
[root@master k8s]# kubectl get rs pc-replicaset -n dev -o wide
NAME            DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES         SELECTOR
pc-replicaset   6         6         6       4m23s   nginx        nginx:1.17.1   app=nginx-pod

# 2.通过命令实现
# 使用scale命令实现扩缩容,后面--replicas=n直接指定目标数量即可
[root@master k8s]# kubectl scale rs pc-replicaset -n dev --replicas=2
replicaset.apps/pc-replicaset scaled

# 查看rs
[root@master k8s]# kubectl get rs pc-replicaset -n dev -o wide
NAME            DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES         SELECTOR
pc-replicaset   2         2         2       6m    nginx        nginx:1.17.1   app=nginx-pod

镜像升级

# 1.编辑rs的容器镜像为nginx:1.17.2
[root@master k8s]# kubectl edit rs pc-replicaset -n dev
replicaset.apps/pc-replicaset edited

# 查看rs
[root@master k8s]# kubectl get rs pc-replicaset -n dev -o wide
NAME            DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES         SELECTOR
pc-replicaset   2         2         2       7m46s   nginx        nginx:1.17.2   app=nginx-pod

# 2.使用命令来修改镜像
# kubectl set image rs rs名称 容器=镜像版本 -n namespace
[root@master k8s]# kubectl set image rs pc-replicaset nginx=nginx:1.17.1 -n dev
replicaset.apps/pc-replicaset image updated

# 查看rs,发现镜像变为了1.17.1
[root@master k8s]# kubectl get rs pc-replicaset -n dev -o wide
NAME            DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES         SELECTOR
pc-replicaset   2         2         2       9m14s   nginx        nginx:1.17.1   app=nginx-pod

删除ReplicaSet

# 使用kubectl delete命令会删除此rs以及它管理的Pod
# 在kubernetes删除rs前,会将rs的replicas调整为0,等待所有的pod被删除后,再执行rs对象的删除
[root@master k8s]# kubectl delete rs pc-replicaset -n dev
replicaset.apps "pc-replicaset" deleted

# 如果希望仅仅删除rs对象(保留pod),可以使用kubectl delete 命令时添加--cascade=false选项(不推荐使用)
kubectl delete rs pc-replicaset -n dev -cascade=false

# 也可以使用yaml直接删除
kubectl delelte -f pc-replicaset.yaml

        

7.3 Deployment(Deploy)

	为了更好的解决服务编排问题,kubernetes在v1.2版本开始,引入了Deployment控制器。值得一提的是,这种控制器并不直接管理Pod,而是通过管理ReplicaSet来间接管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。所以Deployment比ReplicaSet功能更加强大。

在这里插入图片描述

Deployment主要功能有下面几个:

  • 支持ReplicaSet的所有功能
  • 支持发布的停止、继续
  • 支持版本滚动更新和版本回退

Deployment的资源清单:

apiVersion: apps/v1         # 版本号
kind: Deployment            # 类型
metadata:                   # 元数据
  name:                     # rs名称
  namespace:                # rs所属命名空间
  labels:                   # 标签
    controller: deploy
spec:
  replicas: 3                    # 副本数量
  revisionHistoryLimit: 3        # 保留历史版本数量,默认10
  paused: false                  # 暂停部署,默认false
  progressDeadlineSeconds: 600   # 部署时间(s),默认600
  strategy:                      # 策略
    type: RollingUpdata          # 滚动更新策略
    rollingUpdate:               # 滚动更新
      maxSurge: 30%              # 最大额外可以存在的副本数,可以为百分比或整数
      maxUnavailable: 30%        # 最大不可用状态的Pod的最大值,可以为百分比或整数
  selector:                      # 选择器
    matchLabels:                 # labels匹配规则
      app: nginx-pod
    matchExpressions:
      - {key: app, operator: In, values: [nginx-pod]}
  template:                      # 模板,当副本数量不足时,会根据下面的模板创建Pod
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80

创建Deployment

创建pc-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pc-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
# 创建deployment
# --record=true 记录每次版本的变化,在新版本被废弃了
[root@master k8s]# kubectl create -f pc-deployment.yaml --record=true
Flag --record has been deprecated, --record will be removed in the future
deployment.apps/pc-deployment created

# 查看deployment
# UP-TO-DATE:最新版本的pod的数量
# AVAILABLE:当前可用的pod数量
[root@master k8s]# kubectl get deploy pc-deployment -n dev
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
pc-deployment   3/3     3            3           2m26s

# 查看rs
# 发现rs的名称是在deployment的名字后面添加了一个10位数的随机串
[root@master k8s]# kubectl get rs -n dev
NAME                       DESIRED   CURRENT   READY   AGE
pc-deployment-6f7f65b46d   3         3         3       3m19s

# 查看pod
[root@master k8s]# kubectl get pod  -n dev
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-6f7f65b46d-4sgxp   1/1     Running   0          3m37s
pc-deployment-6f7f65b46d-qvrh8   1/1     Running   0          3m37s
pc-deployment-6f7f65b46d-rb8xh   1/1     Running   0          3m38s

扩缩容

# 变更副本数量为5个
[root@master k8s]# kubectl scale deploy pc-deployment --replicas=5 -n dev
deployment.apps/pc-deployment scaled

# 查看deployment
[root@master k8s]# kubectl get deploy pc-deployment -n dev
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
pc-deployment   5/5     5            5           4m56s

# 查看pod
[root@master k8s]# kubectl get pod -n dev
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-6f7f65b46d-4sgxp   1/1     Running   0          5m13s
pc-deployment-6f7f65b46d-6crdw   1/1     Running   0          40s
pc-deployment-6f7f65b46d-7lghr   1/1     Running   0          40s
pc-deployment-6f7f65b46d-qvrh8   1/1     Running   0          5m13s
pc-deployment-6f7f65b46d-rb8xh   1/1     Running   0          5m14s

# 通过编辑改变pod副本数量为3个
[root@master k8s]# kubectl edit deploy pc-deployment -n dev
deployment.apps/pc-deployment edited

# 查看pod
[root@master k8s]# kubectl get pod -n dev
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-6f7f65b46d-4sgxp   1/1     Running   0          6m16s
pc-deployment-6f7f65b46d-qvrh8   1/1     Running   0          6m16s
pc-deployment-6f7f65b46d-rb8xh   1/1     Running   0          6m17s

镜像更新

Deployment支持两种镜像更新策略:重建更新滚动更新(默认),可用通过strategy进行配置。

strategy:                 # 指定新的Pod替换旧的Pod的策略,支持两个属性:
  type:                   # 指定策略类型,支持两种策略
    Recreate:             # 重建更新,在创建出新的Pod之前会先杀掉所有已存在的Pod
    RollingUpdate:        # 滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本的Pod
  rollingUpdate:          # 当type为RollingUpdate时生效,用于为RollingUpdate设置参数,支持两个属性
    maxUnavailable:       # 用来指定在升级过程中不可用Pod的最大数量,默认为25%
    maxSurge:             # 用来指定在升级过程中可用超过期望的Pod的最大数量,默认为25%

重建更新

1)编辑pc-deployment.yaml,在spec节点下添加更新策略

spec:
  strategy:
    type: Recreate

2)创建deploy进行验证

# 应用新的yaml
[root@master k8s]# kubectl apply -f pc-deployment.yaml 
deployment.apps/pc-deployment configured

# 先打开另一个shell窗口,动态观察更新过程
kubectl get pod -n dev -w

# 变更镜像
[root@master k8s]# kubectl set image deploy pc-deployment nginx=nginx:1.17.2 -n dev
deployment.apps/pc-deployment image updated

# 查看pod的更新过程,先是将三个Pod全部停止,然后再启动新的Pod
[root@master ~]# kubectl get pod -n dev -w
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-6f7f65b46d-4sgxp   1/1     Running   0          17m
pc-deployment-6f7f65b46d-qvrh8   1/1     Running   0          17m
pc-deployment-6f7f65b46d-rb8xh   1/1     Running   0          17m
pc-deployment-6f7f65b46d-rb8xh   1/1     Terminating   0          17m
pc-deployment-6f7f65b46d-qvrh8   1/1     Terminating   0          17m
pc-deployment-6f7f65b46d-4sgxp   1/1     Terminating   0          17m
pc-deployment-6f7f65b46d-rb8xh   0/1     Terminating   0          17m
pc-deployment-6f7f65b46d-rb8xh   0/1     Terminating   0          17m
pc-deployment-6f7f65b46d-rb8xh   0/1     Terminating   0          17m
pc-deployment-86f4996797-5ltvh   0/1     Pending       0          0s
pc-deployment-86f4996797-5ltvh   0/1     Pending       0          0s
pc-deployment-86f4996797-p8kch   0/1     Pending       0          0s
pc-deployment-86f4996797-5ltvh   0/1     ContainerCreating   0          1s
pc-deployment-86f4996797-xtfhp   0/1     ContainerCreating   0          2s
pc-deployment-86f4996797-p8kch   0/1     ContainerCreating   0          2s
pc-deployment-86f4996797-5ltvh   1/1     Running             0          22s
pc-deployment-86f4996797-p8kch   1/1     Running             0          23s
pc-deployment-86f4996797-xtfhp   1/1     Running             0          38s

滚动更新

1)编辑pc-deployment.yaml,修改spec节点下的属性:

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxUnavailable: 25%
    maxSurge: 25%

2)创建deploy进行验证

# 应用修改后的yaml
[root@master k8s]# kubectl apply -f pc-deployment.yaml 
deployment.apps/pc-deployment configured

# 先打开另一个shell窗口,动态观察更新过程
kubectl get pod -n dev -w

# 变更镜像
[root@master k8s]# kubectl set image deploy pc-deployment nginx=nginx:1.17.3 -n dev
deployment.apps/pc-deployment image updated

# 查看pod的更新过程,可以看到,先启动了新的Pod,然后杀死一个Pod,然后再这样,直到所有的Pod更新完成
[root@master ~]# kubectl get pod -n dev -w
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-6f7f65b46d-2hqs5   1/1     Running   0          46s
pc-deployment-6f7f65b46d-6kpzr   1/1     Running   0          45s
pc-deployment-6f7f65b46d-9nvbj   1/1     Running   0          48s

pc-deployment-79f7d88458-r64zr   0/1     Pending   0          0s
pc-deployment-79f7d88458-r64zr   0/1     ContainerCreating   0          0s
pc-deployment-79f7d88458-r64zr   1/1     Running             0          24s
pc-deployment-6f7f65b46d-9nvbj   1/1     Terminating         0          79s

pc-deployment-79f7d88458-b2zd9   0/1     Pending             0          0s
pc-deployment-79f7d88458-b2zd9   0/1     ContainerCreating   0          0s
pc-deployment-6f7f65b46d-9nvbj   0/1     Terminating         0          80s
pc-deployment-79f7d88458-b2zd9   1/1     Running             0          1s
pc-deployment-6f7f65b46d-2hqs5   1/1     Terminating         0          78s

pc-deployment-79f7d88458-n2sx6   0/1     Pending             0          0s
pc-deployment-79f7d88458-n2sx6   0/1     ContainerCreating   0          0s
pc-deployment-79f7d88458-n2sx6   1/1     Running             0          1s
pc-deployment-6f7f65b46d-2hqs5   0/1     Terminating         0          79s
pc-deployment-6f7f65b46d-6kpzr   1/1     Terminating         0          78s

滚动更新的过程:

在这里插入图片描述

镜像更新中rs的变化:

# 查看rs,发现原来的rs依旧存在,只是Pod的数量变为了0,而后又产生了一个新的rs,Pod数量为3
# 其实这就是deployment能够进行版本回退的奥妙所在,后面会详细解释
[root@master k8s]# kubectl get rs -n dev
NAME                       DESIRED   CURRENT   READY   AGE
pc-deployment-6f7f65b46d   0         0         0       30m
pc-deployment-79f7d88458   3         3         3       7m16s
pc-deployment-86f4996797   0         0         0       13m

版本回退

deployment支持版本升级过程中的暂停、继续功能以及版本回退等诸多功能,下面具体来看:

kubectl rollout:版本升级相关功能,支持下面的选项:

  • status 显示当前升级状态
  • history 显示升级历史记录
  • pause 暂停版本升级过程
  • resume 继续已经暂停的版本升级过程
  • restart 重启版本升级过程
  • undo 回滚到上一版本(可以使用–to-revision回滚到指定版本)
# 查看当前升级状态
[root@master k8s]# kubectl rollout status deploy pc-deployment -n dev
deployment "pc-deployment" successfully rolled out

# 查看升级历史记录
[root@master k8s]# kubectl rollout history deploy pc-deployment -n dev
deployment.apps/pc-deployment 
REVISION  CHANGE-CAUSE
2         kubectl create --filename=pc-deployment.yaml --record=true
3         kubectl create --filename=pc-deployment.yaml --record=true
4         kubectl create --filename=pc-deployment.yaml --record=true
# 可以发现有三次版本记录,说明完成升级过两次

# 版本回滚
# 这里直接使用--to-revision回滚到版本2,如果省略这个选项,就是回退到上个版本
# 先查看一下nginx版本
[root@master k8s]# kubectl get deploy pc-deployment -n dev -o wide
NAME            READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES         SELECTOR
pc-deployment   3/3     3            3           36m   nginx        nginx:1.17.3   app=nginx-pod

# 回滚
[root@master k8s]# kubectl rollout undo deploy pc-deployment --to-revision=2 -n dev
deployment.apps/pc-deployment rolled back

# 再次查看版本
[root@master k8s]# kubectl get deploy pc-deployment -n dev -o wide
NAME            READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES         SELECTOR
pc-deployment   3/3     3            3           37m   nginx        nginx:1.17.2   app=nginx-pod

# 查看rs,发现第三个rs中有三个Pod运行
# 其实deployment之所以可以实现版本的回滚,就是通过记录下rs来实现的
# 一旦想回滚到哪个版本,只需要将当前版本pod数量降为0,然后将回滚版本的Pod提升为目标数量就可以了
[root@master k8s]# kubectl get rs -n dev
NAME                       DESIRED   CURRENT   READY   AGE
pc-deployment-6f7f65b46d   0         0         0       38m
pc-deployment-79f7d88458   0         0         0       15m
pc-deployment-86f4996797   3         3         3       20m

金丝雀发布

	Deployment支持更新过程中的控制,如“暂停”或“继续”更新操作。

	比如有一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧版本。然后再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳定地按期望的方式运行。确定没问题后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。
# 更新deployment的版本,并配置暂停deployment
[root@master k8s]# kubectl set image deploy pc-deployment nginx=nginx:1.17.4 -n dev && kubectl rollout pause deploy pc-deployment -n dev
deployment.apps/pc-deployment image updated
deployment.apps/pc-deployment paused

# 观察更新状态
[root@master k8s]# kubectl rollout status deploy pc-deployment -n dev
Waiting for deployment "pc-deployment" rollout to finish: 1 out of 3 new replicas have been updated...

# 监控更新的过程,可以看到已经新增了一个资源,但是并未按照预期的状态去删除一个旧的资源,就是因为使用了pause暂停命令
[root@master k8s]# kubectl get rs -n dev
NAME                       DESIRED   CURRENT   READY   AGE
pc-deployment-6f7f65b46d   0         0         0       46m
pc-deployment-79f7d88458   0         0         0       22m
pc-deployment-86f4996797   3         3         3       28m
pc-deployment-cf7c57879    1         1         1       103s

[root@master k8s]# kubectl get pod -n dev
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-86f4996797-64m7h   1/1     Running   0          9m
pc-deployment-86f4996797-bgq8z   1/1     Running   0          9m3s
pc-deployment-86f4996797-swghv   1/1     Running   0          9m1s
pc-deployment-cf7c57879-w5m8v    1/1     Running   0          2m5s

# 确保更新的pod没问题了,继续更新
[root@master k8s]# kubectl rollout resume deploy pc-deployment -n dev
deployment.apps/pc-deployment resumed

# 查看最后的更新情况
[root@master k8s]# kubectl get rs -n dev -o wide
NAME                       DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES         SELECTOR
pc-deployment-6f7f65b46d   0         0         0       48m     nginx        nginx:1.17.1   app=nginx-pod,pod-template-hash=6f7f65b46d
pc-deployment-79f7d88458   0         0         0       24m     nginx        nginx:1.17.3   app=nginx-pod,pod-template-hash=79f7d88458
pc-deployment-86f4996797   0         0         0       30m     nginx        nginx:1.17.2   app=nginx-pod,pod-template-hash=86f4996797
pc-deployment-cf7c57879    3         3         3       3m42s   nginx        nginx:1.17.4   app=nginx-pod,pod-template-hash=cf7c57879

[root@master k8s]# kubectl get pod -n dev
NAME                            READY   STATUS    RESTARTS   AGE
pc-deployment-cf7c57879-f77gw   1/1     Running   0          87s
pc-deployment-cf7c57879-r7x2f   1/1     Running   0          89s
pc-deployment-cf7c57879-w5m8v   1/1     Running   0          4m14s

删除Deployment

# 删除deployment,其下的rs和pos也将被删除
[root@master k8s]# kubectl delete -f pc-deployment.yaml 
deployment.apps "pc-deployment" deleted

        

7.4 Horizontal Pod Autoscaler(HPA)

	在前面的课程中,我们可以通过手动执行`kubectl scale`命令实现Pod扩缩容,但是这显然不符合K8S的定位目标--自动化、智能化。K8S期望可以通过检测Pod的使用情况,实现Pod数量的自动调整,于是就产生了HPA这种控制器。

	HPA可以获取每个Pod的资源利用率,然后和HPA中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现Pod的数量的调整。其实HPA与之前的Deployment一样,也属于一种K8S资源对象,它通过追踪分析目标Pod的负载变化情况,来确定是否需要针对性地调整目标Pod的副本数。

在这里插入图片描述

1.安装metrics-server

metrics-server可以用来收集集群中的资源使用情况

# 安装git
yum install -y git

# 获取metrics-server
git clone -b v0.3.6 https://github.com/kubernetes-incubator/metrics-server
git clone -b v0.5.1 https://github.com/kubernetes-incubator/metrics-server

# 修改deployment,注意修改的是镜像和初始化参数
cd /root/metrics-server/deploy/1.8+/
vim metrics-server-deployment.yaml
# 按照图中添加或修改下面选项
hostNetwork: true
image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP

# 修改完成后,可能还是会出现错误,需要修改其中配置文件中的一个版本配置项,执行下面命令
sed -i 's#apiregistration.k8s.io/v1beta1#apiregistration.k8s.io/v1#' metrics-apiservice.yaml

在这里插入图片描述

# 安装metrics-server
[root@master 1.8+]# kubectl apply -f ./

# 查看pod运行情况
[root@master 1.8+]# kubectl get pod -n kube-system
NAME                              READY   STATUS    RESTARTS        AGE
metrics-server-6b7d7c4c4f-wm48k   1/1     Running   0               17m

# 使用kubectl top node 查看资源使用情况
[root@master 1.8+]# kubectl top node    
NAME     CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
master   219m         10%    1645Mi          44%
node1    39m          1%     505Mi           29%   
node2    49m          2%     467Mi           27%

[root@master 1.8+]# kubectl top pod -n kube-system  
NAME                              CPU(cores)   MEMORY(bytes)
coredns-64897985d-9lbdj           1m           11Mi  
coredns-64897985d-jgj74           2m           10Mi 
etcd-master                       15m          302Mi 
......

至此,metrics-server安装完成

2.准备deployment和service

创建 hpa-deployment.yaml文件

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hpa-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        resources:
          requests: 
            cpu: 100m
# 创建deployment
[root@master 1.8+]# kubectl create -f hpa-deployment.yaml 
deployment.apps/hpa-deployment created

# 创建service
[root@master 1.8+]# kubectl expose deploy hpa-deployment --type=NodePort --port=80 -n dev
service/hpa-deployment exposed

# 查看
[root@master 1.8+]# kubectl get deploy,pod,svc -n dev
NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/hpa-deployment   3/3     3            3           81s

NAME                                 READY   STATUS    RESTARTS   AGE
pod/hpa-deployment-c789d6d7f-ddg6b   1/1     Running   0          81s
pod/hpa-deployment-c789d6d7f-nv9gq   1/1     Running   0          81s
pod/hpa-deployment-c789d6d7f-nw48s   1/1     Running   0          81s

NAME                     TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/hpa-deployment   NodePort   10.96.58.136   <none>        80:31343/TCP   31s

3.部署HPA

创建pc-hpa.yaml

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: pc-hpa
  namespace: dev
spec:
  minReplicas: 1     # 最小pod数量
  maxReplicas: 10    # 最大pod数量
  targetCPUUtilizationPercentage: 3     # cpu使用率指标,在这为了测试,设为3%
  scaleTargetRef:                       # 指定要控制的deployment信息
    apiVersion: apps/v1
    kind: Deployment
    name: hpa-deployment
# 创建hpa
[root@master k8s]# kubectl create -f pc-hpa.yaml 
horizontalpodautoscaler.autoscaling/pc-hpa created

# 查看hpa
[root@master k8s]# kubectl get hpa -n dev
NAME     REFERENCE                   TARGETS        MINPODS   MAXPODS   REPLICAS   AGE
pc-hpa   Deployment/hpa-deployment   <unknown>/3%   1         10        3          17s

4.使用压测工具访问nginx来查看pod、deploy、hpa的信息

在这个地方踩了很多坑,可能是k8s跟metrics-server和版本不兼容,我的k8s是1.23版本,目前还没有解决,先不管这个了。

        

7.5 DaemonSet(DS)

	DaemonSet类型的控制器可以保证集群中的每一台(或指定)节点上都运行一个副本,一般适用于日志收集、节点监控等场景。也就是说一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建。

在这里插入图片描述

DaemonSet控制器的特定:
  • 每当向集群中添加一个节点时,指定的Pod副本也将添加到该节点上
  • 当节点从集群中移除时,Pod也就被当垃圾回收了

下面来看DaemonSet的资源清单:

apiVersion: apps/v1                 # 版本号
kind: DaemonSet                     # 类型
metadata:                           # 元数据
  name:                             # rs名称
  namespace:                        # 所属命名空间
  labels:                           # 标签
    controller: daemonset
spec:
  revisionHistoryLimit: 3           # 保留历史版本数
  updateStrategy:                   # 更新策略
    type: RollingUpdate             # 滚动更新
    rollingUpdate:
      maxAvailable: 1               # 最大不可用状态的Pod的最大值,可以为百分比或整数
  selector:                         # selector,通过它指定该控制器管理哪些pod
    matchLabels:                    # labels匹配规则
      app: nginx-pod
    matchExpression:
      - {key: app, operator: In, values: [nginx-pod]}
  template:                         # 模板,当副本数量不足时,会根据下面的模板创建pod副本
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
    

创建pc-daemonset.yaml:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: pc-daemonset
  namespace: dev
spec:
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
# 创建ds
[root@master k8s]# kubectl create -f pc-daemonset.yaml 
daemonset.apps/pc-daemonset created

# 查看ds
[root@master k8s]# kubectl get ds -n dev
NAME           DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
pc-daemonset   2         2         2       2            2           <none>          3m42s

# 查看pod
[root@master k8s]# kubectl get pod -n dev
NAME                             READY   STATUS    RESTARTS   AGE
pc-daemonset-kwprp               1/1     Running   0          4m16s
pc-daemonset-zq992               1/1     Running   0          58s

# 删除daemonset
[root@master k8s]# kubectl delete -f pc-daemonset.yaml 
daemonset.apps "pc-daemonset" deleted

        

7.6 Job

        Job,主要负责批量处理(一次要处理指定数量任务)短暂的一次性(每个任务运行一次就结束)任务。Job特点如下:

  • 当Job创建的Pod执行成功时,Job将记录成功结束的Pod数量
  • 当成功结束的Pod达到指定的数量时,Job将完成执行

在这里插入图片描述

Job的资源清单文件:

apiVersion: batch/v1              # 版本号
kind: Job                         # 类型
metadata:
  name:                           # Job名称
  namespace:                      # 所属命名空间
  labels:                         # 标签
    controller: job
spec:
  completions: 1                  # 指定Job需要成功运行pod的次数。默认值:1
  parallelism: 1                  # 指定Job在任一时刻应该并发运行Pod的数量。默认值:1
  activeDeadlineSeconds: 30       # 指定Job可运行的时间期限,超时时间还未结束,系统将尝试进行终止
  backoffLimit: 6                 # 指定Job失败后进行重试的次数,默认:6
  manualSelector: true            # 是否可以使用selector选择器选择pod,默认:false
  selector:                       # 选择器,通过它指定该控制器管理哪些Pod
    matchLabels:
      app: counter-pod
    matchExpression:
      - {key: app, operator: In, values: [counter-pod]}
  template:
    metadata:
      labels:
        app: counter-pod
    spec:
      restartPolicy: Never        # 重启策略只能设置为Never或OnFailure
      containers:
      - name: counter
        image: busybox:1.30
        command: ["/bin/sh", "-c", "for i in {1..9};do echo $i;sleep 2;done"]

关于重启策略设置的说明:

	如果指定OnFailure,则Job会在Pod出现故障时重启容器,而不是创建Pod,failed次数也不变

	如果指定Never,则Job会在Pod出现故障时创建新的Pod,并且故障Pod不会消失,也不会重启,failed次数加1

	如果指定为Always,就意味着一直重启,意味着Job任务会重复执行了,当然不对,所以不能设置为Always

创建pc-job.yaml:

apiVersion: batch/v1
kind: Job
metadata:
  name: pc-job
  namespace: dev
spec:
  manualSelector: true
  selector:
    matchLabels:
      app: counter-pod
  template:
    metadata:
      labels:
        app: counter-pod
    spec:
      restartPolicy: Never
      containers:
      - name: counter
        image: busybox:1.30
        command: ["/bin/sh", "-c", "for i in {9..1};do echo $i;sleep 2;done"]
# 创建job
[root@master k8s]# kubectl create -f pc-job.yaml 
job.batch/pc-job created

# 动态查看job
[root@master ~]# kubectl get job -n dev -w
NAME     COMPLETIONS   DURATION   AGE
pc-job   0/1                      0s
pc-job   0/1           0s         0s
pc-job   0/1           12s        12s
pc-job   1/1           12s        12s

# 动态查看pod
[root@master ~]# kubectl get pod -n dev -w
NAME                             READY   STATUS    RESTARTS   AGE
pc-job-64b76                     0/1     Pending   0          0s
pc-job-64b76                     0/1     ContainerCreating   0          0s
pc-job-64b76                     1/1     Running             0          10s
pc-job-64b76                     0/1     Completed           0          12s

# 删除job
[root@master k8s]# kubectl delete job pc-job -n dev
job.batch "pc-job" deleted

        

7.7 CronJob(CJ)

	CronJob控制器以Job控制器资源为其管控对象,并借助它管理Pod资源对象,Job控制器定义的作业任务在其控制器资源创建后便会立即执行,但CronJob可以以类似Linux操作系统的周期性任务作业计划的方式控制其运行**时间点**和**重复运行**的方式。也就是说,CronJob可以在特定的时间点反复去运行Job任务。

在这里插入图片描述

CronJob的资源清单文件:

apiVersion: batch/v1          # 版本号
kind: CronJob                 # 类型
metadata:
  name:
  namespace:
  labels:
    controller: cronjob
spec:
  schedule:                    # cron格式的作业调度运行时间点,用于控制任务在什么时间执行
  concurrencyPolicy:           # 并发执行策略,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业
  failedJobHistoryLimit:       # 为失败的任务执行保留的历史记录数,默认1
  successfulJobHistoryLimit:   # 为成功任务执行保留的历史记录数,默认3
  startingDeadlineSeconds:     # 启动作业错误的超时时长
  jobTemplate:                 # Job控制器模板,用于为cronjob控制器生成job对象
    metadata:
    spec:
      completions: 1
      parallelism: 1
      activeDeadlineSeconds: 1
      backoffLimit: 30
      manualSelector: true
      selector:
        matchLabels:
          app: counter-pod
        matchExpression:
          - {key: app, operator: In, values: [counter-pod]}
      template:
        metadata:
          name:
          namespace:
        spec:
           restartPolicy: Never
           containers:
           - name: counter
             image: busybox:1.30
             command: ["/bin/sh", "-c", "for i in {9..1};do echo $i;sleep 2;done"]

需要解释的几个选项:

schedule:cron表达式,用于指定任务的执行时间

*/1      *      *     *      *
<分钟>  <小时>  <>  <月份>  <星期> 

分钟 值从 059
小时 值从 023
日  值从 131
月  值从 112
星期 值从 060表示星期日
多个时间可以用逗号隔开;范围可以用字符给出;*可以作为通配符;/表示每...

concurrencyPolicy:
Allow: 允许Job并发允许(默认)
Forbid:禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行
Replace:替换,取消当前正在运行的作业并用新作业替换它

创建pc-cronjob.yaml

apiVersion: batch/v1          # 版本号
kind: CronJob                 # 类型
metadata:
  name: pc-cronjob
  namespace: dev
  labels:
    controller: cronjob
spec:
  schedule: "*/1 * * * *"     # 每分钟执行一次任务                
  jobTemplate:               
    metadata:
    spec:
      template:
        spec:
           restartPolicy: Never
           containers:
           - name: counter
             image: busybox:1.30
             command: ["/bin/sh", "-c", "for i in {9..1};do echo $i;sleep 3;done"]
# 创建cronjob
[root@master k8s]# kubectl create -f pc-cronjob.yaml 
cronjob.batch/pc-cronjob created

# 动态查看cronjob
# 可以看到每隔一分钟就会运行一个job
[root@master ~]# kubectl get cj -n dev -w
NAME         SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
pc-cronjob   */1 * * * *   False     0        <none>          0s
pc-cronjob   */1 * * * *   False     1        0s              25s
pc-cronjob   */1 * * * *   False     0        7s              32s
pc-cronjob   */1 * * * *   False     0        7s              32s
pc-cronjob   */1 * * * *   False     1        0s              85s
pc-cronjob   */1 * * * *   False     0        4s              89s

# 动态查看job
[root@master ~]# kubectl get job -n dev -w
NAME                  COMPLETIONS   DURATION   AGE
pc-cronjob-27365083   0/1                      0s
pc-cronjob-27365083   0/1           0s         0s
pc-cronjob-27365083   0/1           7s         7s
pc-cronjob-27365083   1/1           7s         7s
pc-cronjob-27365084   0/1                      0s
pc-cronjob-27365084   0/1           0s         0s
pc-cronjob-27365084   0/1           4s         4s
pc-cronjob-27365084   1/1           4s         4s

# 动态查看pod
[root@master ~]# kubectl get pod -n dev -w
NAME                        READY   STATUS    RESTARTS   AGE
pc-cronjob-27365083-t7krw   0/1     Pending   0          0s
pc-cronjob-27365083-t7krw   0/1     ContainerCreating   0          0s
pc-cronjob-27365083-t7krw   1/1     Running             0          4s
pc-cronjob-27365083-t7krw   0/1     Completed           0          7s
pc-cronjob-27365084-97rdd   0/1     Pending             0          0s
pc-cronjob-27365084-97rdd   0/1     ContainerCreating   0          0s
pc-cronjob-27365084-97rdd   1/1     Running             0          1s
pc-cronjob-27365084-97rdd   0/1     Completed           0          4s

7.8 StatefulSet

还没看

 

8、Service详解

本章节主要介绍Kubernetes的流量负载组件:Service和Ingress。

        

8.1 Service介绍

	在K8S中,Pod是应用程序的载体,我们可以通过Pod的IP来访问应用程序,但是Pod的IP地址不是固定的,这也就意味着不方便直接采用Pod的IP对服务进行访问。

	为了解决这个问题,K8S提供了Service资源,Service会对提供同一个服务的多个Pod进行聚合,并且提供一个统一的入口地址。通过访问Service的入口地址就能访问到后面的Pod服务。                                                                                                               

在这里插入图片描述

	Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个kube-proxy服务进程。当创建Service的时候会通过apiServer向Etcd写入创建的Service信息,而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将新的Service信息转换成对应的访问规则。

在这里插入图片描述

# 10.96.0.10:53  是service提供的访问入口
# 当访问这个入口的时候,可以发现后面有三个Pod的服务在等待调用
# kube-proxy会基于rr(轮询)的策略,将请求分发到其中一个Pod上去
# 这个规则会同时在集群内的所有节点上都生成,所以在任何一个节点上访问都可以
[root@master ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn      
TCP  10.96.0.10:53 rr
  -> 10.244.0.2:53                Masq    1      0          0         
  -> 10.244.0.3:53                Masq    1      0          0              

kube-proxy目前支持三种工作模式:

userspace 模式

	userspace模式下,kube-proxy会为每一个Service创建一个监听端口,发向Cluster IP的请求被iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据LB算法选择一个提供服务的Pod并和其建立连接,以将请求转发到Pod上。

	该模式下,kube-proxy充当了一个四层负载均衡器的角色。由于kube-proxy运行在userspace中,在进行转发处理时会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低。

在这里插入图片描述

iptables 模式

	iptables模式下,kube-proxy为Service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP。

	该模式下kube-proxy不承担四层负载均衡器的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试。

在这里插入图片描述

ipvs 模式

	ipvs模式和iptables模式类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对于iptables转发效率更高。除此之外,ipvs支持更多的LB算法。

在这里插入图片描述

# 此模式必须安装ipvs内核模块,否则会降级为iptables,在前面的环境搭建中已经按照了ipvs
# 开启ipvs
# 编辑cm文件,在文件中查找mode选项,改为ipvs
[root@master ~]# kubectl edit cm kube-proxy -n kube-system

# 重启kube-proxy组件
[root@master ~]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system

# 查看ipvs规则
[root@master ~]# ipvsadm -Ln 

在这里插入图片描述

        

8.2 Service类型

Service的资源清单文件:

apiVersion: v1
kind: Service
metadata:
  name: service
  namespace: dev
spec:
  selector:            # 标签选择器,用于确定Service代理哪些Pod
    app: nginx
  type:                # Service类型,指定Service的访问方式
  clusterIP:           # 虚拟服务的IP地址
  sessionAffinity:     # session亲和性,支持ClientIP、None两个选项
  ports:               # 端口信息
    - protocol: TCP
      port: 3017           # Service端口
      targetPort: 5003     # Pod端口
      nodePort: 31122      # 主机端口

ClusterIP:默认值,它是K8S系统自动分配的虚拟IP,只能在集群内部访问

NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务

LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境支持

ExternalName:把集群外部的服务引入集群内部,直接使用

        

8.3 Service使用

8.3.1 实验环境准备

在使用Service之前,首先利用Deployment创建出3个Pod,注意要为Pod设置app=nginx-pod的标签

创建deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pc-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
# 创建pod
[root@master service_test]# kubectl create -f deployment.yaml 
deployment.apps/pc-deployment created

# 查看pod
[root@master service_test]# kubectl get pod -n dev -o wide
NAME                             READY   STATUS    RESTARTS   AGE     IP            NODE    NOMINATED NODE   READINESS GATES
pc-deployment-6756f95949-8frqw   1/1     Running   0          3m51s   10.244.1.47   node1   <none>           <none>
pc-deployment-6756f95949-cnnww   1/1     Running   0          3m51s   10.244.2.62   node2   <none>           <none>
pc-deployment-6756f95949-mmpsm   1/1     Running   0          3m51s   10.244.2.63   node2   <none>           <none>

# 为了方便后面的测试,修改下三台nginx的index.html页面(三台修改的ip地址不一致)
# kubectl exec -it pc-deployment-6756f95949-8frqw -n dev /bin/sh
# echo "10.244.1.47" > /usr/share/nginx/html/index.html

# 修改完成后,访问测试
[root@master service_test]# curl 10.244.1.47:80
10.244.1.47
[root@master service_test]# curl 10.244.2.62:80
10.244.2.62
[root@master service_test]# curl 10.244.2.63:80
10.244.2.63
8.3.2 ClusterIP类型的Service

创建service-clusterip.yaml:

apiVersion: v1
kind: Service
metadata:
  name: service-clusterip
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: 10.97.97.97    # Service的ip如果不写,默认会生成一个
  type: ClusterIP
  ports:
  - port: 80               # Service端口
    targetPort: 80         # Pod端口
# 创建Service
[root@master service_test]# kubectl create -f service-clusterip.yaml 
service/service-clusterip created

# 查看Service
[root@master service_test]# kubectl get svc -n dev
NAME                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service-clusterip   ClusterIP   10.97.97.97   <none>        80/TCP    10s

# 查看Service的详细信息
[root@master service_test]# kubectl get svc -n dev
NAME                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service-clusterip   ClusterIP   10.97.97.97   <none>        80/TCP    10s
[root@master service_test]# kubectl describe svc service-clusterip -n dev
Name:              service-clusterip
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.97.97.97
IPs:               10.97.97.97
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.47:80,10.244.2.62:80,10.244.2.63:80
Session Affinity:  None
Events:            <none>

# 查看ipvs的映射规则
[root@master service_test]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.97.97.97:80 rr
  -> 10.244.1.47:80               Masq    1      0          0         
  -> 10.244.2.62:80               Masq    1      0          0         
  -> 10.244.2.63:80               Masq    1      0          0         

# 多次访问 10.97.97.97:80 观察效果,发现请求是轮询分发到每个Pod的
[root@master service_test]# curl 10.97.97.97:80
10.244.2.63
[root@master service_test]# curl 10.97.97.97:80
10.244.2.62
[root@master service_test]# curl 10.97.97.97:80
10.244.1.47
[root@master service_test]# curl 10.97.97.97:80
10.244.2.63
[root@master service_test]# curl 10.97.97.97:80
10.244.2.62
[root@master service_test]# curl 10.97.97.97:80
10.244.1.47

Endpoint

	Endpoint是K8S中的一个资源对象,存储在Etcd中,用来记录一个Service对应的所有Pod的访问地址,它是根据Service配置文件中selector描述产生的。

	一个Service由一组Pod组成,这些Pod通过Endpoints暴露出来,Endpoints是实现实际服务的端点集合。换句话说,Service和Pod之间的联系是通过Endpoints实现的。

在这里插入图片描述

[root@master service_test]# kubectl get endpoints -n dev -o wide
NAME                ENDPOINTS                                      AGE
service-clusterip   10.244.1.47:80,10.244.2.62:80,10.244.2.63:80   70m

负载分发策略

对Service的访问被分发到了后端的Pod上去,目前K8S提供了两种负载分发策略:

  • 如果不定义,默认使用的kube-proxy的策略,比如随机、轮询
  • 基于客户端地址的会话保持模式,即来自同一个客户端发起的所有请求都会转发到固定的一个Pod上,此模式在spec中添加sessionAffinity:ClientIP选项来使用
# 查看ipvs的映射规则(rr:轮询)
[root@master service_test]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.97.97.97:80 rr
  -> 10.244.1.47:80               Masq    1      0          0         
  -> 10.244.2.62:80               Masq    1      0          0         
  -> 10.244.2.63:80               Masq    1      0          0         
  
# 循环访问测试
[root@master service_test]# while true;do curl 10.97.97.97:80;sleep 3;done;
10.244.2.63
10.244.2.62
10.244.1.47
10.244.2.63
10.244.2.62
10.244.1.47

# 修改分发策略 --- sessionAffinity: ClientIP
# 修改service-clusterip.yaml文件,添加sessionAffinity: ClientIP
# 应用修改后的文件
[root@master service_test]# kubectl apply -f service-clusterip.yaml 
service/service-clusterip configured

# 查看ipvs规则,可以看到为 rr persistent 10800,persistent表示持久,10800为秒数
[root@master service_test]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.97.97.97:80 rr persistent 10800
  -> 10.244.1.47:80               Masq    1      0          0         
  -> 10.244.2.62:80               Masq    1      0          0         
  -> 10.244.2.63:80               Masq    1      0          0         

# 循环访问测试
[root@master service_test]# while true;do curl 10.97.97.97:80;sleep 3;done;
10.244.2.62
10.244.2.62
10.244.2.62
10.244.2.62
10.244.2.62

# 删除Service
[root@master service_test]# kubectl delete -f service-clusterip.yaml 
service "service-clusterip" deleted
8.3.3 HeadLiness类型的Service
	在某些场景中,开发人员可能不想使用Service提供的负载均衡,而希望自己来控制负载均衡策略,针对这种情况,K8S提供了HeadLiness Service,这类Service不会分配ClusterIP,如果想要访问Service,只能通过Service的域名进行查询。

创建service-headliness.yaml:

apiVersion: v1
kind: Service
metadata:
  name: service-headliness
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None               # 将clusterIP设置为None,即可创建Headliness Service
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80
# 创建Service
[root@master service_test]# kubectl create -f service-headliness.yaml 
service/service-headliness created

# 查看Service,发现没有分配CLUSTER-IP
[root@master service_test]# kubectl get svc service-headliness -n dev -o wide
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service-headliness   ClusterIP   None         <none>        80/TCP    63s   app=nginx-pod

# 查看Service详情
[root@master service_test]# kubectl describe svc service-headliness -n dev 
Name:              service-headliness
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                None
IPs:               None
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.47:80,10.244.2.62:80,10.244.2.63:80
Session Affinity:  None
Events:            <none>

# 查看域名的解析情况
[root@master service_test]# kubectl exec -it pc-deployment-6756f95949-8frqw -n dev /bin/sh
# cat /etc/resolv.conf
nameserver 10.96.0.10
search dev.svc.cluster.local svc.cluster.local cluster.local

[root@master service_test]# dig @10.96.0.10 service-headliness.dev.svc.cluster.local
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.2.62
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.47
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.2.63
8.3.4 NodePort类型的Service
	在之前的样例中,创建的Service的IP地址只有集群内部才可以访问,如果希望将Service暴露给集群外部使用,那么就要使用到另一种类型的Service,称为NodePort类型。NodePort的工作原理其实就是将Service的端口映射到Node的一个端口上,然后就可以通过`NodeIP:NodePort`来访问Service了。

在这里插入图片描述

创建service-nodeport.yaml:

apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: dev
spec:
  selector:
    app: nginx-pod
  type: NodePort
  ports:
  - port: 80
    targetPort: 80           
    nodePort: 30001    # 指定绑定的node的端口(默认取值范围:30000-32767),如果不指定,会默认分配
# 创建Service
[root@master service_test]# kubectl create -f service-nodeport.yaml 
service/service-nodeport created

# 查看Service
[root@master service_test]# kubectl get svc service-nodeport -n dev -o wide
NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE   SELECTOR
service-nodeport   NodePort   10.98.208.175   <none>        80:30001/TCP   26s   app=nginx-pod

# 接下来可以在浏览器中输入 主机IP:30001 来访问了

在这里插入图片描述

8.3.5 LoadBanancer类型的Service
	LoadBanlancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBanlancer会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境支持,外部服务发送到这个设备的请求,会被设备负载之后转发到集群中。

在这里插入图片描述

由于缺失必要设备,就没法演示了。

8.3.6 ExternalName类型的Service

在这里插入图片描述

创建service-externalname.yaml

apiVersion: v1
kind: Service
metadata:
  name: service-externalname
  namespace: dev
spec:
  type: ExternalName
  externalName: www.baidu.com      # 改成ip地址也可以
# 创建service
[root@master service_test]# kubectl create -f service-externalname.yaml 
service/service-externalname created

# 域名解析
[root@master service_test]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local
www.baidu.com.          30      IN      CNAME   www.a.shifen.com.
www.a.shifen.com.       30      IN      A       182.61.200.7
www.a.shifen.com.       30      IN      A       182.61.200.6

        

8.4 Ingress介绍

	在前面的章节中已经提到,Service对集群之外暴露服务的主要方式有两种:NodePort和LoadBalancer,但是这两种方式都有一定的缺点:
  • NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多时,这个缺点就愈发明显
  • LB方式的缺点是每个Service都需要一个LB,浪费、麻烦,并且需要K8S之外的设备支持

        基于这种现状,K8S提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。工作机制大致如下图所示:

在这里插入图片描述

	实际上,Ingress相当于一个7层的负载均衡器,是K8S对反向代理的一个抽象,它的工作原理类似于Nginx,可以理解成**在Ingress中建立诸多映射规则,Ingress Controller通过监听这些配置规则并转化为Nginx的反向代理配置,然后对外部提供服务**。在这里有两个核心概念:

Ingress:K8S中的一个对象,作用是定义请求如何转发到Service的规则

Ingress Controller:具体实现反向代理及负载均衡的程序,对Ingress定义的规则进行解析,根据配置的规则来实现请求转发,实现的方式有很多,比如Nginx、Contour、Haproxy等

Ingress的工作原理如下(以Nginx为例):

  1. 用户编写Ingress规则,说明哪个域名对应K8S集群中的哪个Service
  2. Ingress控制器动态感知Ingress服务规则的变化,然后生成一段对应的Nginx配置
  3. Ingress控制器会将生成的Nginx配置写入到一个运行着的Nginx服务中,并动态更新
  4. 到此为止,其实真正在工作的就是一个Nginx了,内部配置了用户定义的请求转发规则

在这里插入图片描述

8.5 Ingress使用

8.5.1 环境准备

搭建Ingress环境

# 创建文件夹
[root@master k8s]# mkdir ingress-controller
[root@master k8s]# cd ingress-controller/
# 粘贴下面的配置到ingress-nginx.yaml文件中
# 创建:kubectl create -f filename.yaml

# 查看pod
[root@master ingress-controller]# kubectl get pod -n ingress-nginx
NAME                                   READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-2ktm9   0/1     Completed   0          4m23s
ingress-nginx-admission-patch-sddq9    0/1     Completed   2          4m23s
ingress-nginx-controller-lhzlp         1/1     Running     0          4m23s
ingress-nginx-controller-wnscj         1/1     Running     0          4m23s

# 查看svc
[root@master ingress-controller]# kubectl get svc -n ingress-nginx
NAME                                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
ingress-nginx-controller-admission   ClusterIP   10.100.16.181   <none>        443/TCP   7m23s
apiVersion: v1
kind: Namespace
metadata:
  name: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx

---
# Source: ingress-nginx/templates/controller-serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx
  namespace: ingress-nginx
automountServiceAccountToken: true
---
# Source: ingress-nginx/templates/controller-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
---
# Source: ingress-nginx/templates/clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
  name: ingress-nginx
rules:
  - apiGroups:
      - ''
    resources:
      - configmaps
      - endpoints
      - nodes
      - pods
      - secrets
    verbs:
      - list
      - watch
  - apiGroups:
      - ''
    resources:
      - nodes
    verbs:
      - get
  - apiGroups:
      - ''
    resources:
      - services
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - networking.k8s.io
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ''
    resources:
      - events
    verbs:
      - create
      - patch
  - apiGroups:
      - networking.k8s.io
    resources:
      - ingresses/status
    verbs:
      - update
  - apiGroups:
      - networking.k8s.io
    resources:
      - ingressclasses
    verbs:
      - get
      - list
      - watch
---
# Source: ingress-nginx/templates/clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
  name: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx
subjects:
  - kind: ServiceAccount
    name: ingress-nginx
    namespace: ingress-nginx
---
# Source: ingress-nginx/templates/controller-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx
  namespace: ingress-nginx
rules:
  - apiGroups:
      - ''
    resources:
      - namespaces
    verbs:
      - get
  - apiGroups:
      - ''
    resources:
      - configmaps
      - pods
      - secrets
      - endpoints
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ''
    resources:
      - services
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - networking.k8s.io
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - networking.k8s.io
    resources:
      - ingresses/status
    verbs:
      - update
  - apiGroups:
      - networking.k8s.io
    resources:
      - ingressclasses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ''
    resources:
      - configmaps
    resourceNames:
      - ingress-controller-leader
    verbs:
      - get
      - update
  - apiGroups:
      - ''
    resources:
      - configmaps
    verbs:
      - create
  - apiGroups:
      - ''
    resources:
      - events
    verbs:
      - create
      - patch
---
# Source: ingress-nginx/templates/controller-rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx
  namespace: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-nginx
subjects:
  - kind: ServiceAccount
    name: ingress-nginx
    namespace: ingress-nginx
---
# Source: ingress-nginx/templates/controller-service-webhook.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller-admission
  namespace: ingress-nginx
spec:
  type: NodePort
  ports:
    - name: http
      port: 80
      targetPort: 80
      protocol: TCP
    - name: https
      port: 443
      targetPort: 443
      protocol: TCP
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/component: controller
---
# Source: ingress-nginx/templates/controller-deployment.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/instance: ingress-nginx
      app.kubernetes.io/component: controller
  revisionHistoryLimit: 10
  minReadySeconds: 0
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/component: controller
    spec:
      hostNetwork: true
      dnsPolicy: ClusterFirst
      containers:
        - name: controller
          image: registry.cn-beijing.aliyuncs.com/kole_chang/controller:v1.0.0
          imagePullPolicy: IfNotPresent
          lifecycle:
            preStop:
              exec:
                command:
                  - /wait-shutdown
          args:
            - /nginx-ingress-controller
            - --election-id=ingress-controller-leader
            - --controller-class=k8s.io/ingress-nginx
            - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
            - --validating-webhook=:8443
            - --validating-webhook-certificate=/usr/local/certificates/cert
            - --validating-webhook-key=/usr/local/certificates/key
            - --watch-ingress-without-class=true
          securityContext:
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            runAsUser: 101
            allowPrivilegeEscalation: true
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: LD_PRELOAD
              value: /usr/local/lib/libmimalloc.so
          livenessProbe:
            failureThreshold: 5
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              protocol: TCP
            - name: webhook
              containerPort: 8443
              protocol: TCP
          volumeMounts:
            - name: webhook-cert
              mountPath: /usr/local/certificates/
              readOnly: true
          resources:
            requests:
              cpu: 100m
              memory: 90Mi
      nodeSelector:
        kubernetes.io/os: linux
      serviceAccountName: ingress-nginx
      terminationGracePeriodSeconds: 300
      volumes:
        - name: webhook-cert
          secret:
            secretName: ingress-nginx-admission
---
# Source: ingress-nginx/templates/controller-ingressclass.yaml
# We don't support namespaced ingressClass yet
# So a ClusterRole and a ClusterRoleBinding is required
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: nginx
  namespace: ingress-nginx
spec:
  controller: k8s.io/ingress-nginx
---
# Source: ingress-nginx/templates/admission-webhooks/validating-webhook.yaml
# before changing this value, check the required kubernetes version
# https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#prerequisites
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: admission-webhook
  name: ingress-nginx-admission
webhooks:
  - name: validate.nginx.ingress.kubernetes.io
    matchPolicy: Equivalent
    rules:
      - apiGroups:
          - networking.k8s.io
        apiVersions:
          - v1
        operations:
          - CREATE
          - UPDATE
        resources:
          - ingresses
    failurePolicy: Fail
    sideEffects: None
    admissionReviewVersions:
      - v1
    clientConfig:
      service:
        namespace: ingress-nginx
        name: ingress-nginx-controller-admission
        path: /networking/v1/ingresses
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ingress-nginx-admission
  namespace: ingress-nginx
  annotations:
    helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade
    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: admission-webhook
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ingress-nginx-admission
  annotations:
    helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade
    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: admission-webhook
rules:
  - apiGroups:
      - admissionregistration.k8s.io
    resources:
      - validatingwebhookconfigurations
    verbs:
      - get
      - update
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: ingress-nginx-admission
  annotations:
    helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade
    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: admission-webhook
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ingress-nginx-admission
subjects:
  - kind: ServiceAccount
    name: ingress-nginx-admission
    namespace: ingress-nginx
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: ingress-nginx-admission
  namespace: ingress-nginx
  annotations:
    helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade
    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: admission-webhook
rules:
  - apiGroups:
      - ''
    resources:
      - secrets
    verbs:
      - get
      - create
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ingress-nginx-admission
  namespace: ingress-nginx
  annotations:
    helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade
    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: admission-webhook
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-nginx-admission
subjects:
  - kind: ServiceAccount
    name: ingress-nginx-admission
    namespace: ingress-nginx
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: ingress-nginx-admission-create
  namespace: ingress-nginx
  annotations:
    helm.sh/hook: pre-install,pre-upgrade
    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: admission-webhook
spec:
  template:
    metadata:
      name: ingress-nginx-admission-create
      labels:
        helm.sh/chart: ingress-nginx-4.0.1
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/version: 1.0.0
        app.kubernetes.io/managed-by: Helm
        app.kubernetes.io/component: admission-webhook
    spec:
      containers:
        - name: create
          image: registry.cn-beijing.aliyuncs.com/kole_chang/kube-webhook-certgen:v1.0
          imagePullPolicy: IfNotPresent
          args:
            - create
            - --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc
            - --namespace=$(POD_NAMESPACE)
            - --secret-name=ingress-nginx-admission
          env:
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
      restartPolicy: OnFailure
      serviceAccountName: ingress-nginx-admission
      nodeSelector:
        kubernetes.io/os: linux
      securityContext:
        runAsNonRoot: true
        runAsUser: 2000
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: ingress-nginx-admission-patch
  namespace: ingress-nginx
  annotations:
    helm.sh/hook: post-install,post-upgrade
    helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
  labels:
    helm.sh/chart: ingress-nginx-4.0.1
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.0.0
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: admission-webhook
spec:
  template:
    metadata:
      name: ingress-nginx-admission-patch
      labels:
        helm.sh/chart: ingress-nginx-4.0.1
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/instance: ingress-nginx
        app.kubernetes.io/version: 1.0.0
        app.kubernetes.io/managed-by: Helm
        app.kubernetes.io/component: admission-webhook
    spec:
      containers:
        - name: patch
          image: registry.cn-beijing.aliyuncs.com/kole_chang/kube-webhook-certgen:v1.0
          imagePullPolicy: IfNotPresent
          args:
            - patch
            - --webhook-name=ingress-nginx-admission
            - --namespace=$(POD_NAMESPACE)
            - --patch-mutating=false
            - --secret-name=ingress-nginx-admission
            - --patch-failure-policy=Fail
          env:
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
      restartPolicy: OnFailure
      serviceAccountName: ingress-nginx-admission
      nodeSelector:
        kubernetes.io/os: linux
      securityContext:
        runAsNonRoot: true
        runAsUser: 2000

准备Service和Pod

为了后面的实验比较方便,创建如下图所示的模型:

在这里插入图片描述

创建tomcat-nginx.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
        
---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tomcat-pod
  template:
    metadata:
      labels:
        app: tomcat-pod
    spec:
      containers:
      - name: tomcat
        image: tomcat:8.5-jre10-slim
        ports:
        - containerPort: 8080
        
---

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80
    
---

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
  namespace: dev
spec:
  selector:
    app: tomcat-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 8080
    targetPort: 8080
# 创建模型
[root@master service_test]# kubectl create -f tomcat-nginx.yaml 
deployments.apps/nginx-deployment created
deployment.apps/tomcat-deployment created
service/nginx-service created
service/tomcat-service created

# 查看deploy、pod和svc
[root@master service_test]# kubectl get deploy -n dev
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment    3/3     3            3           2m50s
tomcat-deployment   3/3     3            3           2m8s

[root@master service_test]# kubectl get pod -n dev
NAME                                 READY   STATUS    RESTARTS   AGE
nginx-deployment-6756f95949-5tmw4    1/1     Running   0          3m25s
nginx-deployment-6756f95949-jc69z    1/1     Running   0          3m25s
nginx-deployment-6756f95949-n6n8t    1/1     Running   0          3m25s
tomcat-deployment-76bccfb47c-5sph9   1/1     Running   0          2m43s
tomcat-deployment-76bccfb47c-7r2wp   1/1     Running   0          2m43s
tomcat-deployment-76bccfb47c-g95jq   1/1     Running   0          2m43s

[root@master service_test]# kubectl get svc -n dev
NAME                   TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)    AGE
nginx-service          ClusterIP      None         <none>          80/TCP     2m19s
tomcat-service         ClusterIP      None         <none>          8080/TCP   2m19s
8.5.2 Http代理

创建ingress-http.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-http
  namespace: dev
spec:
  rules:
  - host: nginx.xmg.com
    http:
      paths:
      - path: /
        pathType: Exact
        backend:
          service:
            name: nginx-service
            port: 
              number: 80
  - host: tomcat.xmg.com
    http:
      paths:
      - path: /
        pathType: Exact
        backend:
          service:
            name: tomcat-service
            port: 
              number: 8080
# 创建ingress
[root@master service_test]# kubectl create -f ingress-http.yaml 
ingress.networking.k8s.io/ingress-http created

# 查看ingress
[root@master service_test]# kubectl get ing -n dev
NAME           CLASS    HOSTS                          ADDRESS                           PORTS   AGE
ingress-http   <none>   nginx.xmg.com,tomcat.xmg.com   192.168.226.169,192.168.226.170   80      26s

# 查看ingress详情
[root@master service_test]# kubectl get ing -n dev
NAME           CLASS    HOSTS                          ADDRESS                           PORTS   AGE
ingress-http   <none>   nginx.xmg.com,tomcat.xmg.com   192.168.226.169,192.168.226.170   80      26s
[root@master service_test]# kubectl describe ing ingress-http -n dev
Name:             ingress-http
Labels:           <none>
Namespace:        dev
Address:          192.168.226.169,192.168.226.170
Default backend:  default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
Rules:
  Host            Path  Backends
  ----            ----  --------
  nginx.xmg.com   
                  /   nginx-service:80 (10.244.1.58:80,10.244.1.59:80,10.244.2.65:80)
  tomcat.xmg.com  
                  /   tomcat-service:8080 (10.244.1.60:8080,10.244.2.66:8080,10.244.2.67:8080)
Annotations:      <none>
Events:
  Type    Reason  Age                From                      Message
  ----    ------  ----               ----                      -------
  Normal  Sync    51s (x2 over 61s)  nginx-ingress-controller  Scheduled for sync
  Normal  Sync    51s (x2 over 61s)  nginx-ingress-controller  Scheduled for sync
  
# 然后在windows的 C:\Windows\System32\drivers\etc\hosts中添加规则:
# ip和域名需要改成自己的
192.168.226.168 nginx.xmg.com
192.168.226.168 tomcat.xmg.com

# 查看svc
[root@master ingress-controller]# kubectl get svc -n ingress-nginx
NAME                                 TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller-admission   NodePort   10.100.16.181   <none>        80:30586/TCP,443:31694/TCP   52m

# 然后再浏览器中输入 nginx.xmg.com:30586来访问:

在这里插入图片描述

输入 tomcat.xmg.com来访问tomcat(不知为何,图片加载不了,但是已经成功了):

在这里插入图片描述

8.5.3 Https代理

创建证书

# 生成证书
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=xmg.com"

# 创建密钥
kubectl create secret tls tls-secret --key tls.key --cert tls.crt

创建ingress-https.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-https 
  namespace: dev
spec:
  tls:
    - hosts:
      - nginx.xmg.com
      - tomcat.xmg.com
      secretName: tls-secret     # 指定密钥
  rules:
  - host: nginx.xmg.com
    http:
      paths:
      - path: /
        pathType: Exact
        backend:
          service:
            name: nginx-service
            port: 
              number: 80
  - host: tomcat.xmg.com
    http:
      paths:
      - path: /
        pathType: Exact
        backend:
          service:
            name: tomcat-service
            port: 
              number: 8080
# 创建ingress,没有成功
[root@master service_test]# kubectl create -f ingress-https.yaml 
Error from server (InternalError): error when creating "ingress-https.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": failed to call webhook: Post "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s": x509: certificate is valid for ingress.local, not ingress-nginx-controller-admission.ingress-nginx.svc

# 网上找了解决方案
# 使用下面的命令查看 webhook
[root@master service_test]# kubectl get validatingwebhookconfigurations ingress-nginx-admission
NAME                      WEBHOOKS   AGE
ingress-nginx-admission   1          76m

# 删除 ingress-nginx-admission
[root@master service_test]# kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
validatingwebhookconfiguration.admissionregistration.k8s.io "ingress-nginx-admission" deleted

# 再次创建ingress(成功了)
[root@master service_test]# kubectl create -f ingress-https.yaml 
ingress.networking.k8s.io/ingress-https created

# 查看ingress详情
[root@master service_test]# kubectl describe ing ingress-https -n dev
Name:             ingress-https
Labels:           <none>
Namespace:        dev
Address:          192.168.226.169,192.168.226.170
Default backend:  default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
TLS:
  tls-secret terminates nginx.xmg.com,tomcat.xmg.com
Rules:
  Host            Path  Backends
  ----            ----  --------
  nginx.xmg.com   
                  /   nginx-service:80 (10.244.1.58:80,10.244.1.59:80,10.244.2.65:80)
  tomcat.xmg.com  
                  /   tomcat-service:8080 (10.244.1.60:8080,10.244.2.66:8080,10.244.2.67:8080)
Annotations:      <none>
Events:
  Type    Reason  Age                    From                      Message
  ----    ------  ----                   ----                      -------
  Normal  Sync    3m48s (x2 over 4m28s)  nginx-ingress-controller  Scheduled for sync
  Normal  Sync    3m48s (x2 over 4m28s)  nginx-ingress-controller  Scheduled for sync

# 查看svc
[root@master service_test]# kubectl get svc -n ingress-nginx
NAME                                 TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller-admission   NodePort   10.100.16.181   <none>        80:30586/TCP,443:31694/TCP   80m

# 使用 https://域名:31694来访问

 
在这里插入图片描述

 
在这里插入图片描述

9、数据存储

在前面已经提到,容器的生命周期可能很短,会被频繁地创建和销毁。那么容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下时不乐意看到的。为了持久化保存容器中的数据,K8S引入了Volume的概念。

Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被Pod中的多个容器挂载到具体的文件目录下,K8S通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储。Volume的生命周期不与Pod中单个容器的生命周期相关,当容器终止或者重启时,Volume中的数据也不会丢失。

  • kubernetes的Volume支持多种类型,比较常见的有下面的几个:

    • 简单存储:EmptyDir、HostPath、NFS。
    • 高级存储:PV、PVC。
    • 配置存储:ConfigMap、Secret。

9.1 基本存储

9.1.1 EmptyDir
  • EmptyDir是最基础的Volume类型,一个EmptyDir就是Host上的一个空目录。

  • EmptyDir是在Pod被分配到Node时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为kubernetes会自动分配一个目录,当Pod销毁时,EmptyDir中的数据也会被永久删除。

  • EmptyDir的用途如下:

    • 临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留。
    • 一个容器需要从另一个容器中获取数据的目录(多容器共享目录)。
  • 接下来,通过一个容器之间的共享案例来使用描述一个EmptyDir。

  • 在一个Pod中准备两个容器nginx和busybox,然后声明一个volume分别挂载到两个容器的目录中,然后nginx容器负责向volume中写日志,busybox中通过命令将日志内容读到控制台。

在这里插入图片描述

创建Pod

创建volume-emptydir.yaml文件,内容如下

apiVersion: v1
kind: Pod
metadata:
  name: volume-emptydir
  namespace: dev
spec:
  containers:
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
      volumeMounts:       # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx
        - name: logs-volume
          mountPath: /var/log/nginx
    - name: busybox
      image: busybox:1.30
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件
      volumeMounts: # 将logs-volume挂载到busybox容器中的对应目录,该目录为/logs
        - name: logs-volume
          mountPath: /logs
  volumes: # 声明volume,name为logs-volume,类型为emptyDir
    - name: logs-volume
      emptyDir: {}

创建Pod

kubectl create -f volume-emptydir.yaml

查看Pod

kubectl get pod volume-emptydir -n dev -o wide

访问Pod中的Nginx

curl 10.244.2.2

查看指定容器的标准输出

kubectl logs -f volume-emptydir -n dev -c busybox
9.1.2 HostPath
  • 我们已经知道EmptyDir中的数据不会被持久化,它会随着Pod的结束而销毁,如果想要简单的将数据持久化到主机中,可以选择HostPath。
  • HostPath就是将Node主机中的一个实际目录挂载到Pod中,以供容器使用,这样的设计就可以保证Pod销毁了,但是数据依旧可以保存在Node主机上。
    在这里插入图片描述
    在这里插入图片描述

创建Pod

  • 创建volume-hostpath.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
  name: volume-hostpath
  namespace: dev
spec:
  containers:
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
      volumeMounts:       # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx
        - name: logs-volume
          mountPath: /var/log/nginx
    - name: busybox
      image: busybox:1.30
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件
      volumeMounts: # 将logs-volume挂载到busybox容器中的对应目录,该目录为/logs
        - name: logs-volume
          mountPath: /logs
  volumes:                         # 声明volume,name为logs-volume,类型为hostPath
    - name: logs-volume
      hostPath:
        path: /root/logs
        type: DirectoryOrCreate    # 目录存在就使用,不存在就先创建再使用

type的值的说明:

  • DirectoryOrCreate:目录存在就使用,不存在就先创建后使用。
  • Directory:目录必须存在。
  • FileOrCreate:文件存在就使用,不存在就先创建后使用。
  • File:文件必须存在。
  • Socket:unix套接字必须存在。
  • CharDevice:字符设备必须存在。
  • BlockDevice:块设备必须存在。

创建Pod

kubectl create -f volume-hostpath.yaml

查看Pod

kubectl get pod volume-hostpath -n dev -o wide

访问Pod中的Nginx

curl 10.244.2.3

在Node节点中找到HostPath映射的目录中的文件

进入到Pod所在节点,查看HostPath映射的目录中的文件

ls /root/logs

9.2 NFS

9.2.1 概述
  • HostPath虽然可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到别的Node节点上,又会出现问题,此时需要准备单独的网络存储系统,比较常用的是NFS和CIFS。
  • NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样,无论Pod在节点上怎么转移,只要Node和NFS的对接没有问题,数据就可以成功访问。

在这里插入图片描述

9.2.2 搭建NFS服务器
  • 首先需要准备NFS服务器,这里为了简单,直接在Master节点做NFS服务器。
  • 在Master节点上安装NFS服务器
yum install -y nfs-utils rpcbind

# 准备一个共享目录:
mkdir -pv /root/data/nfs

# 将共享目录以读写权限暴露给192.168.226.0/24网段中的所有主机
vim /etc/exports
/root/data/nfs 192.168.18.0/24(rw,no_root_squash)

# 修改权限
chmod 777 -R /root/data/nfs

# 加载配置
exportfs -r

# 启动nfs服务
systemctl start rpcbind
systemctl enable rpcbind
systemctl start nfs
systemctl enable nfs

# 在Master节点上测试是否挂载成功
showmount -e 192.168.226.100

在Node节点上都安装NFS服务器,目的是为了Node节点可以驱动NFS设备

# 在Node节点上安装NFS服务,不需要启动
yum -y install nfs-utils

# 在Node节点测试是否挂载成功
showmount -e 192.168.226.100

# 高可用备份方式,在所有节点执行如下的命令:
mount -t  nfs 192.168.18.100:/root/data/nfs /mnt
9.2.3 创建Pod

创建volume-nfs.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: volume-nfs
  namespace: dev
spec:
  containers:
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80
      volumeMounts:            # 将logs-volume挂载到nginx容器中对应的目录,该目录为/var/log/nginx
        - name: logs-volume
          mountPath: /var/log/nginx
    - name: busybox
      image: busybox:1.30
      imagePullPolicy: IfNotPresent
      command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件
      volumeMounts:           # 将logs-volume挂载到busybox容器中的对应目录,该目录为/logs
        - name: logs-volume
          mountPath: /logs
  volumes:                    # 声明volume
    - name: logs-volume
      nfs:
        server: 192.168.18.100 # NFS服务器地址
        path: /root/data/nfs # 共享文件路径

创建Pod

kubectl create -f volume-nfs.yaml

查看Pod

kubectl get pod volume-nfs -n dev

查看NFS服务器上的共享目录

ls /root/data/nfs

9.3 高级存储

9.3.1 PV和PVC概述
  • 前面我们已经学习了使用NFS提供存储,此时就要求用户会搭建NFS系统,并且会在yaml配置nfs。由于kubernetes支持的存储系统有很多,要求客户全部掌握,显然不现实。为了能够屏蔽底层存储实现的细节,方便用户使用,kubernetes引入了PV和PVC两种资源对象。
  • PV(Persistent Volume)是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下PV由kubernetes管理员进行创建和配置,它和底层具体的共享存储技术有关,并通过插件完成和共享存储的对接。
  • PVC(Persistent Volume Claim)是持久化卷声明的意思,是用户对于存储需求的一种声明。换言之,PVC其实就是用户向kubernetes系统发出的一种资源需求申请。

在这里插入图片描述

  • 使用了PV和PVC之后,工作可以得到进一步的提升:

    • 存储:存储工程师维护。
    • PV:kubernetes管理员维护。
    • PVC:kubernetes用户维护。
9.3.2 PV

PV是存储资源的抽象,下面是PV的资源清单文件:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  nfs: # 存储类型,和底层正则的存储对应
    path:
    server:
  capacity: # 存储能力,目前只支持存储空间的设置
    storage: 2Gi
  accessModes: # 访问模式
    -
  storageClassName: # 存储类别
  persistentVolumeReclaimPolicy: # 回收策略

pv的关键配置参数说明:

  • 存储类型:底层实际存储的类型,kubernetes支持多种存储类型,每种存储类型的配置有所不同。

  • 存储能力(capacity):目前只支持存储空间的设置(storage=1Gi),不过未来可能会加入IOPS、吞吐量等指标的配置。

  • 访问模式(accessModes):

    • 用来描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
      • ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载。
      • ReadOnlyMany(ROX):只读权限,可以被多个节点挂载。
      • ReadWriteMany(RWX):读写权限,可以被多个节点挂载。
    • 需要注意的是,底层不同的存储类型可能支持的访问模式不同。
  • 回收策略( persistentVolumeReclaimPolicy):

    • 当PV不再被使用之后,对其的处理方式,目前支持三种策略:
      • Retain(保留):保留数据,需要管理员手动清理数据。
      • Recycle(回收):清除PV中的数据,效果相当于rm -rf /volume/*
      • Delete(删除):和PV相连的后端存储完成volume的删除操作,常见于云服务器厂商的存储服务。
    • 需要注意的是,底层不同的存储类型可能支持的回收策略不同。
  • 存储类别(storageClassName):PV可以通过storageClassName参数指定一个存储类别。

    • 具有特定类型的PV只能和请求了该类别的PVC进行绑定。
    • 未设定类别的PV只能和不请求任何类别的PVC进行绑定。
  • 状态(status):一个PV的生命周期,可能会处于4种不同的阶段。

    • Available(可用):表示可用状态,还未被任何PVC绑定。
    • Bound(已绑定):表示PV已经被PVC绑定。
    • Released(已释放):表示PVC被删除,但是资源还没有被集群重新释放。
    • Failed(失败):表示该PV的自动回收失败。

准备工作环境

# 创建目录
mkdir -pv /root/data/{pv1,pv2,pv3}

# 授权
chmod 777 -R /root/data

# 修改/etc/exports文件
vim /etc/exports
/root/data/pv1     192.168.18.0/24(rw,no_root_squash) 
/root/data/pv2     192.168.18.0/24(rw,no_root_squash) 
/root/data/pv3     192.168.18.0/24(rw,no_root_squash)

# 重启nfs服务
systemctl restart nfs

创建PV

创建pv.yaml文件,内容如下:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  nfs: # 存储类型吗,和底层正则的存储对应
    path: /root/data/pv1
    server: 192.168.18.100
  capacity: # 存储能力,目前只支持存储空间的设置
    storage: 1Gi
  accessModes: # 访问模式
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain # 回收策略

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  nfs: # 存储类型吗,和底层正则的存储对应
    path: /root/data/pv2
    server: 192.168.18.100
  capacity: # 存储能力,目前只支持存储空间的设置
    storage: 2Gi
  accessModes: # 访问模式
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain # 回收策略
  
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv3
spec:
  nfs: # 存储类型吗,和底层正则的存储对应
    path: /root/data/pv3
    server: 192.168.18.100
  capacity: # 存储能力,目前只支持存储空间的设置
    storage: 3Gi
  accessModes: # 访问模式
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain # 回收策略

创建PV

kubectl create -f pv.yaml

查看PV

kubectl get pv -o wide
9.3.3 PVC

PVC是资源的申请,用来声明对存储空间、访问模式、存储类别需求信息,下面是PVC的资源清单文件:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc
  namespace: dev
spec:
  accessModes: # 访客模式
    - 
  selector: # 采用标签对PV选择
  storageClassName: # 存储类别
  resources: # 请求空间
    requests:
      storage: 5Gi

PVC的关键配置参数说明:

  • 访客模式(accessModes):用于描述用户应用对存储资源的访问权限。

  • 用于描述用户应用对存储资源的访问权限:

    • 选择条件(selector):通过Label Selector的设置,可使PVC对于系统中已存在的PV进行筛选。
    • 存储类别(storageClassName):PVC在定义时可以设定需要的后端存储的类别,只有设置了该class的pv才能被系统选出。
    • 资源请求(resources):描述对存储资源的请求。

创建PVC

创建pvc.yaml文件,内容如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
  namespace: dev
spec:
  accessModes: # 访客模式
    - ReadWriteMany
  resources: # 请求空间
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc2
  namespace: dev
spec:
  accessModes: # 访客模式
    - ReadWriteMany
  resources: # 请求空间
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc3
  namespace: dev
spec:
  accessModes: # 访客模式
    - ReadWriteMany
  resources: # 请求空间
    requests:
      storage: 5Gi

创建PVC

kubectl create -f pvc.yaml

查看PVC

kubectl get pvc -n dev -o wide

查看PV

kubectl get pv -o wide

创建Pod使用PVC

创建pvc-pod.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod1
  namespace: dev
spec:
  containers:
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]
    volumeMounts:
    - name: volume
      mountPath: /root/
  volumes:
    - name: volume
      persistentVolumeClaim:
        claimName: pvc1
        readOnly: false

---
apiVersion: v1
kind: Pod
metadata:
  name: pod2
  namespace: dev
spec:
  containers:
    - name: busybox
      image: busybox:1.30
      command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]
      volumeMounts:
        - name: volume
          mountPath: /root/
  volumes:
    - name: volume
      persistentVolumeClaim:
        claimName: pvc2
        readOnly: false

创建Pod

kubectl create -f pvc-pod.yaml

查看Pod

kubectl get pod -n dev -o wide

查看PVC

kubectl get pvc -n dev -o wide

查看PV

kubectl get pv -n dev -o wide

查看NFS中的文件存储

ls /root/data/pv1/out.txt
ls /root/data/pv2/out.txt
9.3.4 生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bJ8HSy1M-1678171945083)(F:\desktop\笔记\k8s学习笔记3.assets\image-20230307144953821.png)]

  • PVC和PV是一一对应的,PV和PVC之间的相互作用遵循如下的生命周期。

  • 资源供应:管理员手动创建底层存储和PV。

  • 资源绑定:

    • 用户创建PVC,kubernetes负责根据PVC声明去寻找PV,并绑定在用户定义好PVC之后,系统将根据PVC对存储资源的请求在以存在的PV中选择一个满足条件的。
      • 一旦找到,就将该PV和用户定义的PVC进行绑定,用户的应用就可以使用这个PVC了。
      • 如果找不到,PVC就会无限期的处于Pending状态,直到系统管理员创建一个符合其要求的PV。
    • PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再和其他的PVC进行绑定了。
  • 资源使用:用户可以在Pod中像volume一样使用PVC,Pod使用Volume的定义,将PVC挂载到容器内的某个路径进行使用。

  • 资源释放:

    • 用户删除PVC来释放PV。
    • 当存储资源使用完毕后,用户可以删除PVC,和该PVC绑定的PV将会标记为“已释放”,但是还不能立刻和其他的PVC进行绑定。通过之前PVC写入的数据可能还留在存储设备上,只有在清除之后该PV才能再次使用。
  • 资源回收:

    • kubernetes根据PV设置的回收策略进行资源的回收。
    • 对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收,才能供新的PVC绑定和使用。
9.3.5 创建PVC后一直绑定不了PV的原因
  • PVC的空间申请大小比PV的空间要大。
  • PVC的storageClassName和PV的storageClassName不一致。
  • PVC的accessModes和PV的accessModes不一致。

        
        

问题记录:

1、ns命名空间删除时卡住,一直显示Terminating的解决方案:

# 执行下面命令
kubectl get namespace <terminating-namespace> -o json > tmp.json

# 然后修改tmp.json文件,将finalizers中的kubernetes删掉

# 新增一个代理端口,代理的其实就是apiserver的非加密端口8080,运行此命令后会占用终端,需要再开一个窗口执行curl命令
kubectl proxy --port=8081

# 新开一个窗口执行下面命令
curl -k -H "Content-Type: application/json" -X PUT --data-binary @tmp.json http://127.0.0.1:8081/api/v1/namespaces/<terminating-namespace>/finalize

# 之后再查看ns已经没有了

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐