Kubernetes容器编排解决方案【基础篇】

-author: liuchao

-email: mirs_chao@163.com

-gitee: https://gitee.com/mirschao


1. kubernetes简要概述

  Kubernetes 是用于自动部署, 扩展和管理容器化应用程序的开源系统. 它将组成应用程序的容器组合成逻辑单元, 以便于管理和服务发现. Kubernetes 源自Google 15 年生产环境的运维经验, 同时凝聚了社区的最佳创意和实践

  在容器化应用的大趋势下, 越来越多的单体应用被划分成了无数个微小的服务, 从之前的裸机部署单体服务到现今使用容器承载服务运行; 容器越来越贴近研发、运维、测试等IT职能部门, 越来越多的人也渐渐转向了云进行开发, 而不再单独强调单体一致性问题; 这一切的功臣就是 — 容器;

  其实容器这个产物, 早在Linux内核诞生后就已经有了它, 那时候程序员创建一个容器需要编写代码, 调用Linux内核中的Cgroup 和 NameSpace用作资源限制, 而创建出来的容器所观看到的底层文件系统却是和宿主机是同一套文件系统, 导致在容器中操作文件就相当于更改了宿主机的文件系统, 这就大大暴露了容器的应用性差的特性; 后来横空出世的docker解决了这个问题, docker创造了一个叫做镜像的东西, 并将其进行分层, 使得用户在启动容器的时候将镜像拷贝一份读写层挂载到容器中, 让容器看到的底层文件系统只是镜像中的内容从而避免了上述的问题, 镜像带来的好处远不止这一个, 比如在生产、测试、研发三个环境中总是因为环境不统一造成上线后的应用很快崩溃的局面, 而有了镜像就不需要考虑环境的问题, 因为所有的服务运行在容器中, 而提供给容器运行的底层文件系统是镜像, 只要使用的是同一个镜像那么不管在什么环境中, 都是一样的效果, 这对于运维、测试、研发三类人而言就能彻底的和应用运行环境要考虑的事情甩手拜拜了; 而docker这个引擎又提供了很多易于使用的控制容器的指令, 大大降低了程序员操作的难度, 又提供相应的镜像和存储镜像的公共仓库, 使得容器技术飞速发展起来;

  就在容器飞速发展的第五年里, Google发布了容器编排系统 kubernetes, 它解决了容器在多个宿主机间调度的问题, 同时提供可靠的API接口来控制容器的运行, 并时刻保持容器启动的状态, 融合了故障自愈、服务发现等新功能; 从而占领了容器市场的高地; 从此容器圈内又向容器编排发起了猛烈的攻势;

1.1 kubernetes 功能简介
  • 服务发现和负载均衡

      Kubernetes 可以使用 DNS 名称或自己的 IP 地址公开容器, 如果进入容器的流量很大, Kubernetes 可以负载均衡并分配网络流量, 从而使部署稳定. 常用的DNS插件为coreDNS, 用作服务发现和集群中容器通讯; 负载均衡器常使用集群内的service资源进行对外暴露服务, 同时也提供ingress插件接口对外暴露服务

  • 存储编排

      Kubernetes 允许你自动挂载你选择的存储系统, 例如本地存储、公共云提供商等. 对于集群内部的应用有时会需要持久化数据, 这时容器如果被删除数据也会随之消失, 所以一个稳定的外部存储集群就显得很重要了; 常见的对k8s提供存储能力的集群有: heketi + gluesterfs 、Rook + Ceph、阿里云OSS等, 配合集群内的storageclass可直接向存储集群申请存储空间

  • 自动部署和回滚

      你可以使用 Kubernetes 描述已部署容器的所需状态, 它可以以受控的速率将实际状态 更改为期望状态. 例如, 你可以自动化 Kubernetes 来为你的部署创建新容器, 删除现有容器并将它们的所有资源用于新容器. 控制k8s集群中容器的状态大多数时候都会采用 yaml 资源配置清单的形式, 方便记忆也易于识别

  • 自动完成装箱计算

      Kubernetes 允许你指定每个容器所需 CPU 和内存(RAM). 当容器指定了资源请求时, Kubernetes 可以做出更好的决策来管理容器的资源. 这就与Linux操作系统中的资源限额有关系了, 有些容器在接收高并发访问的时候, 往往会无限制的占用宿主机资源, 从而导致其他服务容器争抢不到应有的资源进行运行和服务, 从而导致大面积瘫痪

  • 自我修复

      Kubernetes 重新启动失败的容器、替换容器、杀死不响应用户定义的 运行状况检查的容器, 并且在准备好服务之前不将其通告给客户端. 在创建pod的时候, 只有当设置在pod中的探针全部探测正常后才会将pod连接到集群内网络中对外进行服务, 否则将通过监控告警给运维人员; 同样的对于k8s中的容器会受到kubelet和kube-apiserver的双重管理, kubelet上报容器运行状态, api-server向etcd中请求期望状态, 比较后让其达到期望的状态, 从而保证其功能/服务容器永久保持在线

  • 密钥与配置管理

      Kubernetes 允许你存储和管理敏感信息, 例如密码、OAuth 令牌和 ssh 密钥. 你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置, 也无需在堆栈配置中暴露密钥. 对于无状态应用(exam: nginx)的配置文件, 可以使用k8s中的configmap资源进行存储, 但对于密码和某些私密信息而言就可以使用secret资源进行存储, 从而避免频繁更改和私密信息泄漏的风险

1.2 Kubernetes架构及组件

  一个 Kubernetes 集群由一组被称作节点的机器组成。这些节点上运行 Kubernetes 所管理的容器化应用。集群具有至少一个工作节点。工作节点托管作为应用负载的组件的 Pod 。控制平面管理集群中的工作节点和 Pod 。 为集群提供故障转移和高可用性,这些控制平面一般跨多主机运行,集群跨多个节点运行。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • kube-apiserver

    API 服务器是 Kubernetes 控制面的组件, 该组件公开了 Kubernetes API。 API 服务器是 Kubernetes 控制面的前端。Kubernetes API 服务器的主要实现是 kube-apiserver。 kube-apiserver 设计上考虑了水平伸缩,也就是说,它可通过部署多个实例进行伸缩。 你可以运行 kube-apiserver 的多个实例,并在这些实例之间平衡流量。

  • etcd

    etcd 是兼具一致性和高可用性的键值数据库,可以作为保存 Kubernetes 所有集群数据的后台数据库。你的 Kubernetes 集群的 etcd 数据库通常需要有个备份计划。要了解 etcd 更深层次的信息,请参考 etcd 文档

  • kube-scheduler

    控制平面组件,负责监视新创建的、未指定运行节点(node)Pods,选择节点让 Pod 在上面运行。调度决策考虑的因素包括单个 Pod 和 Pod 集合的资源需求、硬件/软件/策略约束、亲和性和反亲和性规范、数据位置、工作负载间的干扰和最后时限。

  • kube-controller-manager

    运行控制器进程的控制平面组件。从逻辑上讲,每个控制器都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在一个进程中运行。这些控制器包括:

    • 节点控制器(Node Controller):负责在节点出现故障时进行通知和响应

    • 任务控制器(Job controller):监测代表一次性任务的 Job 对象,然后创建 Pods 来运行这些任务直至完成

    • 端点控制器(Endpoints Controller):填充端点(Endpoints)对象(即加入 Service 与 Pod)

    • 服务帐户和令牌控制器(Service Account & Token Controllers):为新的命名空间创建默认帐户和 API 访问令牌

  • cloud-controller-manager

    云控制器管理器是指嵌入特定云的控制逻辑的 控制平面组件。 云控制器管理器使得你可以将你的集群连接到云提供商的 API 之上, 并将与该云平台交互的组件同与你的集群交互的组件分离开来。cloud-controller-manager 仅运行特定于云平台的控制回路。 如果你在自己的环境中运行 Kubernetes,或者在本地计算机中运行学习环境, 所部署的环境中不需要云控制器管理器。与 kube-controller-manager 类似,cloud-controller-manager 将若干逻辑上独立的 控制回路组合到同一个可执行文件中,供你以同一进程的方式运行。 你可以对其执行水平扩容(运行不止一个副本)以提升性能或者增强容错能力。下面的控制器都包含对云平台驱动的依赖:

    • 节点控制器(Node Controller):用于在节点终止响应后检查云提供商以确定节点是否已被删除

    • 路由控制器(Route Controller):用于在底层云基础架构中设置路由

    • 服务控制器(Service Controller):用于创建、更新和删除云提供商负载均衡器

  • Node 组件: 节点组件在每个节点上运行,维护运行的 Pod 并提供 Kubernetes 运行环境。

  • kubelet

    一个在集群中每个节点(node)上运行的代理。 它保证容器(containers)都 运行在 Pod 中。kubelet 接收一组通过各类机制提供给它的 PodSpecs,确保这些 PodSpecs 中描述的容器处于运行状态且健康。 kubelet 不会管理不是由 Kubernetes 创建的容器。

  • kube-proxy

    kube-proxy 是集群中每个节点上运行的网络代理, 实现 Kubernetes 服务(Service) 概念的一部分。kube-proxy 维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话与 Pod 进行网络通信。如果操作系统提供了数据包过滤层并可用的话,kube-proxy 会通过它来实现网络规则。否则, kube-proxy 仅转发流量本身。

  • 容器运行时(Container Runtime)

    容器运行环境是负责运行容器的软件。Kubernetes 支持容器运行时,例如 DockercontainerdCRI-O 以及 Kubernetes CRI (容器运行环境接口) 的其他任何实现。

  • 插件(Addons)

    插件使用 Kubernetes 资源实现集群功能。 因为这些插件提供集群级别的功能,插件中命名空间域的资源属于 kube-system 命名空间。下面描述众多插件中的几种。有关可用插件的完整列表,请参见 插件(Addons)

    • DNS

      尽管其他插件都并非严格意义上的必需组件,但几乎所有 Kubernetes 集群都应该 有集群 DNS, 因为很多示例都需要 DNS 服务。集群 DNS 是一个 DNS 服务器,和环境中的其他 DNS 服务器一起工作,它为 Kubernetes 服务提供 DNS 记录。Kubernetes 启动的容器自动将此 DNS 服务器包含在其 DNS 搜索列表中。

    • Web 界面(仪表盘)

      Dashboard 是 Kubernetes 集群的通用的、基于 Web 的用户界面。 它使用户可以管理集群中运行的应用程序以及集群本身并进行故障排除。

    • 容器资源监控

      容器资源监控 将关于容器的一些常见的时间序列度量值保存到一个集中的数据库中,并提供用于浏览这些数据的界面。

    • 集群层面日志

      集群层面日志 机制负责将容器的日志数据 保存到一个集中的日志存储中,该存储能够提供搜索和浏览接口 ELK/Loki+grafana

2. Kubernetes 集群部署

2.1 测试环境集群部署
role ipaddress configure
k8s-master 10.9.68.98 4 core, 4Gb; 50GBS, CentOS 7.9
k8s-worker-01 10.9.68.93 4 core, 4Gb; 100GBS, CentOS 7.9
k8s-worker-02 10.9.68.91 4 core, 4Gb; 100GBS, CentOS 7.9

⚠️**不要用克隆机器作这次的实验**

  测试集群部署采用单master节点部署, 可以使IT人员得到一个完整功能的测试Kubernetes集群; 由于其构建简单, 所以其内部很多相关设计没有凸显出来, 想了解Kubernetes的组件配置工作及细节请移至 生产环境集群部署 查看;

#>>> 下载部署k8s集群的安装仓库
$ git clone https://gitee.com/mirschao/k8sconfig.git
$ cd k8sconfig/kubeadm-deploys

$ sed -i 's/MASTER/10.9.68.98/' initialenv.sh
$ sed -i 's/WORKER1/10.9.68.93/' initialenv.sh
$ sed -i 's/WORKER2/10.9.68.91/' initialenv.sh
  • 初始化每个节点(all master & all node)

    $ bash initialenv.sh
    
  • 安装 kubeadm 程序(all master & all node)

    $ yum list kubeadm.x86_64 --showduplicates | sort -r
    kubeadm.x86_64                       1.21.2-0                         kubernetes
    kubeadm.x86_64                       1.21.14-0                        kubernetes
    kubeadm.x86_64                       1.21.13-0                        kubernetes
    
    #> master中执行
    $ yum -y install kubeadm-1.21.14-0 kubelet-1.21.14-0 kubectl-1.21.14-0
    #> worker中执行
    $ yum -y install kubeadm-1.21.14-0 kubelet-1.21.14-0
    
    #> master及worker节点均要执行
    $ cat <<-EOF >/etc/sysconfig/kubelet
    KUBELET_EXTRA_ARGS="--cgroup-driver=systemd"
    EOF
    
    $ systemctl enable --now kubelet
    
  • 生成集群初始化配置文件(only k8s-master)

    $ kubeadm config print init-defaults >initial.yaml
    
    #> 配置初始化文件
    $ IPADDRESS=$(ifconfig | grep ens33 -A 2 | awk 'NR==2{ print $2 }')
    $ sed -i "s/1.2.3.4/${IPADDRESS}/" initial.yaml
    
    $ REGISTRYADDR="registry.cn-hangzhou.aliyuncs.com/google_containers"
    $ sed -i "s#k8s.gcr.io#${REGISTRYADDR}#" initial.yaml
    
    $ sed -i "s# node# ${HOSTNAME}#" initial.yaml
    
    #> 拉取初始化所需要的镜像文件
    $ kubeadm config images pull --config initial.yaml
    
    #> 初始化集群
    $ kubeadm init --config initial.yaml --upload-certs
    
    our Kubernetes control-plane has initialized successfully!
    
    To start using your cluster, you need to run the following as a regular user:
    #######>>>以下三步需要在命令行中执行, 最好创建一个用户进行设置<<<#######
      mkdir -p $HOME/.kube
      sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
      sudo chown $(id -u):$(id -g) $HOME/.kube/config
    
    Alternatively, if you are the root user, you can run:
    #######>>>以下设置需要在命令行中执行, 最好加入~/.bashrc中进行设置<<<#######
      export KUBECONFIG=/etc/kubernetes/admin.conf
    
    You should now deploy a pod network to the cluster.
    Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
      https://kubernetes.io/docs/concepts/cluster-administration/addons/
    
    Then you can join any number of worker nodes by running the following on each as root:
    
    kubeadm join 10.9.68.98:6443 --token abcdef.0123456789abcdef \
    	--discovery-token-ca-cert-hash sha256:834e4ada6f3abd2525e0d43a7a1b53b0abf713d5f64dd555fc927d90dc8dac0b
    
    #> 部署calico网络插件
    $ sed -i 's#etcd_endpoints: "http://<ETCD_IP>:<ETCD_PORT>"#etcd_endpoints: "https://10.9.68.98:2379"#g' calico-etcd.yaml
    
    $ ETCD_CA=`cat /etc/kubernetes/pki/etcd/ca.crt | base64 | tr -d '\n'`
    $ ETCD_CERT=`cat /etc/kubernetes/pki/etcd/server.crt | base64 | tr -d '\n'`
    $ ETCD_KEY=`cat /etc/kubernetes/pki/etcd/server.key | base64 | tr -d '\n'`
    
    $ sed -i "s@# etcd-key: null@etcd-key: ${ETCD_KEY}@g; s@# etcd-cert: null@etcd-cert: ${ETCD_CERT}@g; s@# etcd-ca: null@etcd-ca: ${ETCD_CA}@g" calico-etcd.yaml
    
    $ sed -i 's#etcd_ca: ""#etcd_ca: "/calico-secrets/etcd-ca"#g; s#etcd_cert: ""#etcd_cert: "/calico-secrets/etcd-cert"#g; s#etcd_key: ""#etcd_key: "/calico-secrets/etcd-key"#g' calico-etcd.yaml
    
    $ sed -i 's@# - name: CALICO_IPV4POOL_CIDR@- name: CALICO_IPV4POOL_CIDR@g; s@#   value: "192.168.0.0/16"@  value: '"192.168.0.0/16"'@g;' calico-etcd.yaml
    
    $ kubectl apply -f calico-etcd.yaml
    
    #> 配置calicoctl设置
    $ wget https://github.com/projectcalico/calico/releases/download/v3.23.1/calicoctl-linux-amd64 -O /usr/local/bin/calicoctl
    
    $ chmod a+x /usr/local/bin/calicoctl
    
    $ calicoctl node status
    Calico process is running.
    
    IPv4 BGP status
    +----------------+-------------------+-------+----------+-------------+
    |  PEER ADDRESS  |     PEER TYPE     | STATE |  SINCE   |    INFO     |
    +----------------+-------------------+-------+----------+-------------+
    | 10.9.68.93 | node-to-node mesh | up    | 13:53:54 | Established |
    | 10.9.68.91 | node-to-node mesh | up    | 13:59:43 | Established |
    +----------------+-------------------+-------+----------+-------------+
    
    IPv6 BGP status
    No IPv6 peers found.
    
  • 对于集群后续的配置和设置

    #> 集群网络规则采用 ipvs 模式
    $ kubectl edit configmap kube-proxy -n kube-system
       ...
    43   mode: "ipvs"
       ...
    
    $ kubectl get pod -n kube-system
    NAME                                       READY   STATUS            RESTARTS   AGE
    calico-kube-controllers-7f494f5676-79nlm   1/1     Running           0          4m8s
    calico-node-g5xnl                          1/1     Running           0          4m8s
    calico-node-gtdv7                          1/1     Running           0          4m8s
    calico-node-wtf9x                          0/1     PodInitializing   0          4m8s
    coredns-6f6b8cc4f6-5ghl9                   1/1     Running           0          21m
    coredns-6f6b8cc4f6-xrr9t                   1/1     Running           0          21m
    etcd-k8s-master                            1/1     Running           0          21m
    kube-apiserver-k8s-master                  1/1     Running           0          21m
    kube-controller-manager-k8s-master         1/1     Running           0          21m
    kube-proxy-28hd5                           1/1     Running           0          16m
    kube-proxy-b76xv                           1/1     Running           0          17m
    kube-proxy-bz7pd                           1/1     Running           0          21m
    kube-scheduler-k8s-master                  1/1     Running           0          21m
    
    $ kubectl delete pod kube-proxy-28hd5 -n kube-system
    pod "kube-proxy-28hd5" deleted
    $ kubectl delete pod kube-proxy-b76xv -n kube-system
    pod "kube-proxy-b76xv" deleted
    $ kubectl delete pod kube-proxy-bz7pd -n kube-system
    pod "kube-proxy-bz7pd" deleted
    
    $ kubectl get pod -n kube-system
    NAME                                       READY   STATUS            RESTARTS   AGE
    calico-kube-controllers-7f494f5676-79nlm   1/1     Running           0          5m21s
    calico-node-g5xnl                          1/1     Running           0          5m21s
    calico-node-gtdv7                          1/1     Running           0          5m21s
    calico-node-wtf9x                          0/1     PodInitializing   0          5m21s
    coredns-6f6b8cc4f6-5ghl9                   1/1     Running           0          22m
    coredns-6f6b8cc4f6-xrr9t                   1/1     Running           0          22m
    etcd-k8s-master                            1/1     Running           0          22m
    kube-apiserver-k8s-master                  1/1     Running           0          22m
    kube-controller-manager-k8s-master         1/1     Running           0          22m
    kube-proxy-htks4                           1/1     Running           0          11s
    kube-proxy-rdtvw                           1/1     Running           0          26s
    kube-proxy-zv84c                           1/1     Running           0          38s
    kube-scheduler-k8s-master                  1/1     Running           0          22m
    
    #> 如果集群初始化失败:(每个节点都要执行)
    $ kubeadm reset -f; ipvsadm --clear; rm -rf ~/.kube
    $ systemctl restart kubelet
    
    #> 如果忘记token值
    $ kubeadm token create --print-join-command
    $ kubeadm init phase upload-certs --upload-certs
    
  • 测试集群是否成功安装

    $ cat <<-EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: webserver
    spec:
      containers:
      - name: nginx-contianer
        image: nginx:1.21
        ports:
        - containerPort: 80
    EOF
    
2.2 生产环境集群部署

Pod-net-segment: 192.168.0.0/16

Service-net-segment: 10.96.0.0/12

role ipaddress Configure
loadbalancer 10.9.12.99 None
prod-master-01 & etcd 10.9.12.101 4core, 4Gb, 50G; CentOS 7.9
prod-master-02 & etcd 10.9.12.102 4core, 4Gb, 50G; CentOS 7.9
prod-master-03 & etcd 10.9.12.103 4core, 4Gb, 50G; CentOS 7.9
prod-worker-01 10.9.12.111 4core, 16Gb, 100G; CentOS 7.9
prod-worker-02 10.9.12.112 4core, 16Gb, 100G; CentOS 7.9

⚠️**不要用克隆机器作这次的实验**

  生产环境部署k8s在现今的IT圈中, 比较流行在阿里云或者腾讯云上直接购买Kubernetes的SaaS服务, 而不是自己费劲儿部署一套出来; 但是对于IDC机房中的裸金属服务器而言, 并没有云那么方便, 所以还是需要自己手动的部署一套可高度定制化的Kubernetes集群; 这时就需要使用二进制方式部署Kubernetes集群了; 而且还要考虑高可用方面的设置;

$ git clone https://gitee.com/mirschao/k8sconfig.git
$ cd k8sconfig/binary-deploys

$ sed -i 's/LOADBALANCER/10.9.12.99/' initialenv.sh
$ sed -i 's/MASTER1/10.9.12.101/' initialenv.sh
$ sed -i 's/MASTER2/10.9.12.102/' initialenv.sh
$ sed -i 's/MASTER3/10.9.12.103/' initialenv.sh
$ sed -i 's/WORKER1/10.9.12.111/' initialenv.sh
$ sed -i 's/WORKER2/10.9.12.112/' initialenv.sh
  • 所有节点初始化

    $ bash initialenv.sh
    
  • 所有节点升级内核 【https://mirrors.tuna.tsinghua.edu.cn/elrepo/kernel/el7/x86_64/RPMS/】

    $ yum -y localinstall kernel-lt-*
    $ grub2-set-default 0 && grub2-mkconfig -o /etc/grub2.cfg
    $ grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"
    $ reboot
    
  • prod-master-01中操作: 解压并分发二进制指令

    $ tar xf kubernetes-server-linux-amd64.tar.gz --strip-components=3 -C /usr/local/bin/ kubernetes/server/bin/kube{let,ctl,-apiserver,-controller-manager,-scheduler,-proxy}
    
    $ for HOST in prod-master-02 prod-master-03;do scp /usr/local/bin/kube* ${HOST}:/usr/local/bin/;done
    
    $ for HOST in prod-worker-01 prod-worker-02;do scp /usr/local/bin/kube{let,-proxy} ${HOST}:/usr/local/bin/;done
    
    $ tar -zxvf etcd-v3.5.4-linux-amd64.tar.gz --strip-components=1 -C /usr/local/bin/ etcd-v3.5.4-linux-amd64/etcd{,ctl}
    
    $ for HOST in prod-master-02 prod-master-03;do scp /usr/local/bin/etcd* ${HOST}:/usr/local/bin/;done
    
  • prod-master-01中操作: 生成集群内部通信证书

    $ wget https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssl_1.6.1_linux_amd64 -O /usr/local/bin/cfssl
    $ wget https://github.com/cloudflare/cfssl/releases/download/v1.6.1/cfssljson_1.6.1_linux_amd64 -O /usr/local/bin/cfssljson
    
    $ chmod a+x /usr/local/bin/cfssl*
    
    $ cd certs
    $ sed -i 's/LOADBALANCER/10.9.12.99/' generate-certs.sh
    $ sed -i 's/MASTER1/10.9.12.101/' generate-certs.sh
    $ sed -i 's/MASTER2/10.9.12.102/' generate-certs.sh
    $ sed -i 's/MASTER3/10.9.12.103/' generate-certs.sh
    
    $ bash generate-certs.sh
    $ cp /etc/etcd/ssl/* /etc/kubernetes/pki/etcd/
    
    #>>> 将k8s以及etcd证书传送到两外两台master机器中
    $ for HOST in prod-master-02 prod-master-03;do scp -r /etc/kubernetes/* ${HOST}:/etc/kubernetes/;done
    
    $ for HOST in prod-master-02 prod-master-03;do scp /etc/etcd/ssl/* ${HOST}:/etc/etcd/ssl/;done
    
  • prod-master-01中操作: 配置etcd服务

    $ cd services/etcd
    $ sed 's/HOSTNAME/prod-master-01/; s/MASTER1/10.9.12.101/; s/MASTER2/10.9.12.102/; s/MASTER3/10.9.12.103/; s/MASTER/10.9.12.101/' etcd-config.yaml >etcd-config-01.yaml
    
    $ sed 's/HOSTNAME/prod-master-02/; s/MASTER1/10.9.12.101/; s/MASTER2/10.9.12.102/; s/MASTER3/10.9.12.103/; s/MASTER/10.9.12.102/' etcd-config.yaml >etcd-config-02.yaml
    
    $ sed 's/HOSTNAME/prod-master-03/; s/MASTER1/10.9.12.101/; s/MASTER2/10.9.12.102/; s/MASTER3/10.9.12.103/; s/MASTER/10.9.12.103/' etcd-config.yaml >etcd-config-03.yaml
    
    $ cp etcd-config-01.yaml /etc/etcd/etcd.config.yml
    $ scp etcd-config-02.yaml prod-master-02:/etc/etcd/etcd.config.yml
    $ scp etcd-config-03.yaml prod-master-03:/etc/etcd/etcd.config.yml
    
    $ cp etcd-service-config.service /usr/lib/systemd/system/etcd.service
    $ scp etcd-service-config.service prod-master-02:/usr/lib/systemd/system/etcd.service
    $ scp etcd-service-config.service prod-master-03:/usr/lib/systemd/system/etcd.service
    
    ######>每台master均执行<######
    $ systemctl daemon-reload && systemctl enable --now etcd
    
    ######>任意master执行检查<######
    $ export ETCDCTL_API=3
    $ etcdctl --endpoints="10.9.12.101:2379,10.9.12.102:2379,10.9.12.103:2379" \
      --cacert=/etc/kubernetes/pki/etcd/etcd-ca.pem --cert=/etc/kubernetes/pki/etcd/etcd.pem \
      --key=/etc/kubernetes/pki/etcd/etcd-key.pem endpoint status \
      --write-out=table
    
  • prod-master-01中操作: 配置负载均衡及高可用

    #> 三台master中均执行
    $ yum -y install haproxy keepalived
    
    $ cd services/loadbalancer
    
    $ sed -i 's/MASTER1/10.9.12.101/' haproxy.conf
    $ sed -i 's/MASTER2/10.9.12.102/' haproxy.conf
    $ sed -i 's/MASTER3/10.9.12.103/' haproxy.conf
    
    $ cp -rf haproxy.conf /etc/haproxy/haproxy.cfg
    $ for HOST in prod-master-02 prod-master-03;do scp haproxy.conf ${HOST}:/etc/haproxy/haproxy.cfg;done
    
    $ NETINTERFACE="所在机器的网卡名字"
    $ sed "s#MSID#${NETINTERFACE}#; s#MSI#10.9.12.101#; s#LOADBALANCER#10.9.12.99#" keepalived.conf >k1.conf
    $ cp -rf k1.conf /etc/keepalived/keepalived.conf
    
    $ sed "s#MSID#${NETINTERFACE}#; s#MSI#10.9.12.102#; s#LOADBALANCER#10.9.12.99#" keepalived.conf >k2.conf
    $ scp k2.conf prod-master-02:/etc/keepalived/keepalived.conf
    
    $ sed "s#MSID#${NETINTERFACE}#; s#MSI#10.9.12.103#; s#LOADBALANCER#10.9.12.99#" keepalived.conf >k3.conf
    $ scp k3.conf prod-master-03:/etc/keepalived/keepalived.conf
    
    $ cp -rf check.sh /etc/keepalived/check_apiserver.sh
    $ for HOST in prod-master-02 prod-master-03;do scp check.sh ${HOST}:/etc/keepalived/check_apiserver.sh;done
    
    #> 三台master中均执行
    $ systemctl enable --now haproxy keepalived
    
  • prod-master-01中操作: 配置apiserver、controller、scheduler服务配置

    $ cd cplane
    
    #> apiserver configure.
    $ sed 's/LBMASTER/10.9.12.101/; s/MASTER1/10.9.12.101/; s/MASTER2/10.9.12.102/; s/MASTER3/10.9.12.103/' apiserver.service >api1.service
    $ cp api1.service /usr/lib/systemd/system/kube-apiserver.service
    
    $ sed 's/LBMASTER/10.9.12.102/; s/MASTER1/10.9.12.101/; s/MASTER2/10.9.12.102/; s/MASTER3/10.9.12.103/' apiserver.service >api2.service
    $ scp api2.service prod-master-02:/usr/lib/systemd/system/kube-apiserver.service
    
    $ sed 's/LBMASTER/10.9.12.103/; s/MASTER1/10.9.12.101/; s/MASTER2/10.9.12.102/; s/MASTER3/10.9.12.103/' apiserver.service >api3.service
    $ scp api3.service prod-master-03:/usr/lib/systemd/system/kube-apiserver.service
    
    #> contoller configure.
    POD_NET_SEGMENT='192.168.0.0/16'
    $ sed -i "s#POD_NET_SEGMENT#${POD_NET_SEGMENT}#" controller.service
    
    $ cp controller.service /usr/lib/systemd/system/kube-controller-manager.service
    $ for HOST in prod-master-02 prod-master-03;do scp controller.service ${HOST}:/usr/lib/systemd/system/kube-controller-manager.service;done
    
    #> scheduler configure.
    $ cp scheduler.service /usr/lib/systemd/system/kube-scheduler.service
    $ for HOST in prod-master-02 prod-master-03;do scp scheduler.service ${HOST}:/usr/lib/systemd/system/kube-scheduler.service;done
    
    #> 三台master中均执行
    $ systemctl daemon-reload
    $ systemctl enable --now kube-scheduler kube-controller-manager kube-apiserver
    
  • prod-master-01中执行: 配置集群认证证书自动颁发

    $ cd bootstrap
    
    $ sed -i 's#LOADBALANCER#10.9.12.99#' create-certs.sh
    $ bash create-certs.sh
    
    $ for HOST in prod-master-02 prod-master-03;do scp /etc/kubernetes/bootstrap-kubelet.kubeconfig ${HOST}:/etc/kubernetes/bootstrap-kubelet.kubeconfig;done
    
  • prod-master-01中执行: 配置所有节点的kubelet、kube-proxy

    $ cd allmachines
    
    #> kubelet配置
    $ cp kubelet.service /usr/lib/systemd/system/kubelet.service
    $ for HOST in prod-master-02 prod-master-03 prod-worker-01 prod-worker-02;do scp kubelet.service ${HOST}:/usr/lib/systemd/system/kubelet.service;done
    
    $ cp kubelet-10.conf /etc/systemd/system/kubelet.service.d/10-kubelet.conf
    $ for HOST in prod-master-02 prod-master-03 prod-worker-01 prod-worker-02;do scp kubelet-10.conf ${HOST}:/etc/systemd/system/kubelet.service.d/10-kubelet.conf;done
    
    $ cp kubelet.yaml /etc/kubernetes/kubelet-conf.yml
    $ for HOST in prod-master-02 prod-master-03 prod-worker-01 prod-worker-02;do scp kubelet.yaml ${HOST}:/etc/kubernetes/kubelet-conf.yml;done
    
    #[ADD NODE]> 想增加worker时可执行以下两条指令
    $ for NODE in prod-worker-01 prod-worker-02;do for FILE in etcd-ca.pem etcd.pem etcd-key.pem;do scp /etc/etcd/ssl/${FILE} ${NODE}:/etc/etcd/ssl;done;done
    
    $ for NODE in prod-worker-01 prod-worker-02;do for FILE in pki/ca.pem pki/ca-key.pem pki/front-proxy-ca.pem bootstrap-kubelet.kubeconfig;do scp /etc/kubernetes/${FILE} ${NODE}:/etc/kubernetes/${FILE};done;done
    
    #> kube-proxy配置
    $ sed -i 's/LOADBALANCER/10.9.12.99/' kube-proxy-create.sh
    $ bash kube-proxy-create.sh
    
    $ cp kube-proxy.service /usr/lib/systemd/system/kube-proxy.service
    $ for HOST in prod-master-02 prod-master-03 prod-worker-01 prod-worker-02;do scp kube-proxy.service ${HOST}:/usr/lib/systemd/system/kube-proxy.service;done
    
    $ POD_NET_SEGMENT='192.168.0.0/16'
    $ sed -i "s#POD_NET_SEGMENT#${POD_NET_SEGMENT}#" kube-proxy.conf
    $ cp kube-proxy.conf /etc/kubernetes/kube-proxy.conf
    $ for HOST in prod-master-02 prod-master-03 prod-worker-01 prod-worker-02;do scp kube-proxy.conf ${HOST}:/etc/kubernetes/kube-proxy.conf;done
    
    #[ADD NODE]> 想增加worker时可执行以下两条指令
    $ for HOST in prod-master-02 prod-master-03 prod-worker-01 prod-worker-02;do scp /etc/kubernetes/kube-proxy.kubeconfig ${HOST}:/etc/kubernetes/kube-proxy.kubeconfig;done
    
    #> 所有节点操作
    $ systemctl daemon-reload
    $ systemctl enable --now kubelet kube-proxy
    
  • prod-master-01中操作: 安装calico网络插件

    $ cd binary-deploys
    
    $ sed -i 's#etcd_endpoints: "http://<ETCD_IP>:<ETCD_PORT>"#etcd_endpoints: "https://10.9.12.101:2379,https://10.9.12.102:2379,https://10.9.12.103:2379"#g' calico-etcd.yaml
    
    $ ETCD_CA=$(cat /etc/kubernetes/pki/etcd/etcd-ca.pem | base64 | tr -d '\n')
    $ ETCD_CERT=$(cat /etc/kubernetes/pki/etcd/etcd.pem | base64 | tr -d '\n')
    $ ETCD_KEY=$(cat /etc/kubernetes/pki/etcd/etcd-key.pem | base64 | tr -d '\n')
    
    $ sed -i "s@# etcd-key: null@etcd-key: ${ETCD_KEY}@g; s@# etcd-cert: null@etcd-cert: ${ETCD_CERT}@g; s@# etcd-ca: null@etcd-ca: ${ETCD_CA}@g" calico-etcd.yaml
    
    $ sed -i 's#etcd_ca: ""#etcd_ca: "/calico-secrets/etcd-ca"#g; s#etcd_cert: ""#etcd_cert: "/calico-secrets/etcd-cert"#g; s#etcd_key: ""#etcd_key: "/calico-secrets/etcd-key"#g' calico-etcd.yaml
    
    $ sed -i 's@# - name: CALICO_IPV4POOL_CIDR@- name: CALICO_IPV4POOL_CIDR@g; s@#   value: "192.168.0.0/16"@  value: '"192.168.0.0/16"'@g;' calico-etcd.yaml
    
    $ kubectl apply -f calico-etcd.yaml
    
  • prod-master-01中操作: 安装coreDNS服务发现

    $ cd coredns
    
    $ ./deploy.sh -s -i 10.96.0.10 >coredns.yaml
    $ COREDNS_VERSION=$(grep "image:" coredns.yaml | awk '{ print $2 }')
    $ sed "s#${COREDNS_VERSION}#coredns/coredns:1.8.3#" coredns.yaml | kubectl apply -f -
    
  • prod-master-01中操作: 验证集群的完整性

    集群安装验证
    # 原则:
    	- pod能解析service
    	- pod必须能解析跨namespace的service
    	- 每个节点都能连通kubernetes service的 443及53端口
    	- pod和pod之间能正常通信(涵盖同namespace和不同namespace)
    
    # 创建pod用于测试
    $ cat <<-EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: busybox
      namespace: default
    spec:
      containers:
      - name: busybox
        image: busybox:1.28
        command:
          - sleep
          - "36000"
        imagePullPolicy: IfNotPresent
      restartPolicy: Always
    EOF
    
    
    $ kubectl exec busybox -n default -- nslookup kubernetes
    Server:    10.96.0.10
    Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
    
    Name:      kubernetes
    Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local
    
    
    $ kubectl exec busybox -n default -- nslookup kube-dns.kube-system
    Server:    10.96.0.10
    Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
    
    Name:      kube-dns.kube-system
    Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
    
    #> 各个节点执行测试端口连通性
    $ telnet 10.96.0.1 443
    Trying 10.96.0.1...
    Connected to 10.96.0.1.
    Escape character is '^]'.
    
    #> 清除测试pod
    $ kubectl delete pod busybox
    
2.3 增加worker节点
#> 以下步骤需要在prod-master-01中执行
$ cd addworker
$ chmod a+x add.sh
$ ./add.sh <dest_ipaddress>
2.4 部署dashboard
#> 以下步骤需要在prod-master-01中执行
$ cd dashboard

##>> 生成私有证书
$ openssl genrsa -out dashboard.key 2048
$ openssl req -days 3650 -new -out dashboard.csr -key dashboard.key -subj '/CN=10.9.12.99'
$ openssl x509 -req -in dashboard.csr -signkey dashboard.key -out dashboard.crt

$ kubectl apply -f dashboard.yaml

##>> 创建私有secret资源
$ kubectl create secret generic kubernetes-dashboard-certs --from-file=dashboard.key --from-file=dashboard.crt -n kubernetes-dashboard

##>> 创建dashboard管理serviceaccount
$ kubectl create serviceaccount dashboard-admin -n kubernetes-dashboard
$ kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:dashboard-admin

##>> 获取登录token
$ kubectl describe secrets -n kubernetes-dashboard $(kubectl -n kubernetes-dashboard get secret | awk '/dashboard-admin/{print $1}') | tail -1

⚠️: dashboard在访问时不接受 Google Chrome浏览器进行访问

3. Kubernetes基础资源

  Kubernetes中的基础资源包含最小的执行单元Pod、用于暴露业务服务的四层负载Service、用于暴露业务服务的七层负载Ingress、用来控制Pod运行的控制器(Deployment、DaemonSet、Job、CronJob、StatefulSet、ReplicaSet)、用来提供业务容器持久化存储的StorageClass动态存储(PV, PVC)、用来解耦业务程序与配置的configmap、用来存储集群中的加密信息的Secret; 这些基础资源共同作用组成满足不同应用场景的业务服务体系, 再配合上开源社区提供的产品就构成了整个k8s的周边生态了;

3.1 k8s中的资源层级关系及管理

namespace

Ingress

Deployment

DaemonSet

CronJob

Job

StatefulSet

ReplicaSet

Pod

Service

ConfigMap

Secret

StorageClass[不属于namespace]

pv,pvc[会创建到指定的namespace下]

  • k8s基本控制指令

    # 查看对应资源: 状态
    $ kubectl get <SOURCE_NAME> -n <NAMESPACE> -owide
    
    # 查看对应资源: 事件信息
    $ kubectl describe <SOURCE_NAME> <SOURCE_NAME_RANDOM_ID> -n <NAMESPACE>
    
    # 查看pod资源: 日志
    $ kubectl logs -f <SOURCE_NAME_RANDOM_ID> [CONTINER_NAME] -n <NAMESPACE>
    
    # 创建资源: 根据资源清单
    $ kubectl apply[or create] -f <SOURCE_FILENAME>.yaml
    
    # 删除资源: 根据资源清单
    $ kubectl delete -f <SOURCE_FILENAME>.yaml
    
    # 修改资源: 根据反射出的etcd中的配置内容, 生产中不允许该项操作, 且命令禁止
    $ kubectl edit <SOURCE_NAME> <SOURCE_NAME_RANDOM_ID> -n <NAMESPACE>
    
3.2 k8s中的Pod

  Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元; Pod 中会启动一个或一组紧密相关的业务容器, 各个业务容器相当于Pod 中的各个进程, 此时就可以将Pod 作为虚拟机看待; 在创建 Pod 时会启动一个init容器, 用来初始化存储和网络, 其余的业务容器都将在init容器启动后启动, 业务容器共享init容器的存储和网络; Pod 只是一个逻辑单元, 并不是真实存在的“主机”, 这种类比主机的概念可以更好的符合现有互联网中几乎所有的虚拟化设计; 像之前运行在虚拟机中的 nginx、mysql、php均可以使用对应的镜像运行出对应的容器在Pod中, 来类比虚拟机中运行这三者;

截屏2022-05-27 22.38.29

  对于 Pod 而言, 在运行的过程中, k8s为了控制其生命周期的状态, 增加了容器探测指针资源限额期望状态保持多容器结合安全策略设定控制器受管故障处理策略 等; Pod在平时是不能够被单独创建的, 而是需要使用控制器对其创建, 这样可以时刻保持Pod的期望状态;

  在Kubernetes中所有的资源均可通过命令行参数或者资源清单[yaml/json]的方式进行创建和修改, 但由于Kubernetes属于声明式资源控制集群, 故大多管理Kubernetes集群的方式采用资源配置清单; 资源配置清单可以很好的追溯资源的原型和详细的配置, 且配合版本控制能够完成更好的溯源、回滚、发布等操作; 所以课程中所有的资源创建和修改都会采用资源配置清单的方式, 除部分命令行操作以外;

3.2.1 单容器 Pod
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-single
  namespace: default
  labels:
    app: nginx
spec:
  restartPolicy: OnFailure
  containers:
  - name: webserver
    image: mirschao/webserver:v1
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80
3.2.2 多容器 Pod
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-many
  namespace: default
  labels:
  	app: nginx
spec:
  restartPolicy: OnFailure
  containers:
  - name: webserver
    image: mirschao/webserver:v1
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80
  - name: php-fpm
    image: php:8.0-fpm-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 9000
  nodeSelector:
    name: k8s-worker-02
3.2.3 共享数据卷
---
kind: Pod
apiVersion: v1
metadata:
  name: nginx
  namespace: default
  labels:
    app: nginx
spec:
  restartPolicy: OnFailure
  containers:
  - name: webserver
    image: mirschao/webserver:v1
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80
    volumeMounts:
    - name: webdir
      mountPath: /usr/share/nginx/html
  - name: php-fpm
    image: php:8.0-fpm-alpine
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 9000
    volumeMounts:
    - name: webdir
      mountPath: /usr/share/nginx/html
  volumes:
  - name: webdir
    hostPath:
      path: /tmp/webdir
3.2.4 容器探针

针对运行中的容器,kubelet 可以选择是否执行以下三种探针,以及如何针对探测结果作出反应:

  • livenessProbe:指示容器是否正在运行.如果存活态探测失败,则 kubelet 会杀死容器, 并且容器将根据其重启策略决定未来.如果容器不提供存活探针, 则默认状态为 Success.
  • readinessProbe:指示容器是否准备好为请求提供服务.如果就绪态探测失败, 端点控制器将从与 Pod 匹配的所有服务的端点列表中删除该 Pod 的 IP 地址. 初始延迟之前的就绪态的状态值默认为 Failure. 如果容器不提供就绪态探针,则默认状态为Success
  • startupProbe: 指示容器中的应用是否已经启动.如果提供了启动探针,则所有其他探针都会被 禁用,直到此探针成功为止.如果启动探测失败,kubelet 将杀死容器,而容器依其 重启策略进行重启. 如果容器没有提供启动探测,则默认状态为 Success

Probe 是由 kubelet 对容器执行的定期诊断. 要执行诊断,kubelet 调用由容器实现的 Handler (处理程序).有三种类型的处理程序:

  • ExecAction: 在容器内执行指定命令.如果命令退出时返回码为 0 则认为诊断成功.
  • TCPSocketAction: 对容器的 IP 地址上的指定端口执行 TCP 检查.如果端口打开,则诊断被认为是成功的.
  • HTTPGetAction: 对容器的 IP 地址上指定端口和路径执行 HTTP Get 请求.如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的.

每次探测都将获得以下三种结果之一:

  • Success(成功):容器通过了诊断.
  • Failure(失败):容器未通过诊断.
  • Unknown(未知):诊断失败,因此不会采取任何行动.

liveness之exec探针

periodSeconds 字段指定了 kubelet 应该每 5 秒执行一次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 5 秒。 kubelet 在容器内执行命令 cat /tmp/healthy 来进行探测。 如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。 如果这个命令返回非 0 值,kubelet 会杀死这个容器并重新启动它。

---
apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec
  namespace: default
  labels:
    test: liveness
spec:
  containers:
  - name: liveness-probe
    image: mirschao/probebusybox:v1
    imagePullPolicy: IfNotPresent
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 60
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 3
      periodSeconds: 5

liveness之http探针

periodSeconds 字段指定了 kubelet 每隔 3 秒执行一次存活探测。 initialDelaySeconds 字段告诉 kubelet 在执行第一次探测前应该等待 3 秒。 kubelet 会向容器内运行的服务(服务会监听 8080 端口)发送一个 HTTP GET 请求来执行探测。 如果服务器上 /healthz 路径下的处理程序返回成功代码,则 kubelet 认为容器是健康存活的。 如果处理程序返回失败代码,则 kubelet 会杀死这个容器并且重新启动它

---
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - name: liveness-probe
    image: mirschao/webserver:liveness-httpget-probe
    imagePullPolicy: IfNotPresent
    livenessProbe:
      httpGet:
        path: /healthz
        port: 80
        httpHeaders:
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

liveness之tcp端口探针

kubelet 会在容器启动 5 秒后发送第一个就绪探测。 这会尝试连接 webserver 容器的 80 端口。 如果探测成功,这个 Pod 会被标记为就绪状态,kubelet 将继续每隔 10 秒运行一次检测。除了就绪探测,这个配置包括了一个存活探测。 kubelet 会在容器启动 15 秒后进行第一次存活探测。 与就绪探测类似,会尝试连接 webserver 容器的 80 端口。 如果存活探测失败,这个容器会被重新启动

---
apiVersion: v1
kind: Pod
metadata:
  name: liveness-tcpsocket
  labels:
    app: webserver
spec:
  containers:
  - name: webserver
    image: mirschao/webserver:liveness-httpget-probe
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80
    livenessProbe:
      tcpSocket:
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 10

多探针共存

有时候,会有一些现有的应用在启动时需要较长的初始化时间。 要这种情况下,若要不影响对死锁作出快速响应的探测,设置存活探测参数是要技巧的。 技巧就是使用相同的命令来设置启动探测,针对 HTTP 或 TCP 检测,可以通过将 failureThreshold * periodSeconds 参数设置为足够长的时间来应对糟糕情况下的启动时间; failureThreshold表示检查几次后认为不健康; 有启动探测,应用程序将会有最多 5 分钟(30 * 10 = 300s)的时间来完成其启动过程。 一旦启动探测成功一次,存活探测任务就会接管对容器的探测,对容器死锁作出快速响应。 如果启动探测一直没有成功,容器会在 300 秒后被杀死,并且根据 restartPolicy 来 执行进一步处置

ports:
- name: liveness-port
  containerPort: 8080
  hostPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 1
  periodSeconds: 10

startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10

readiness之exec探针

有时候,应用会暂时性地无法为请求提供服务。 例如,应用在启动时可能需要加载大量的数据或配置文件,或是启动后要依赖等待外部服务。 在这种情况下,既不想杀死应用,也不想给它发送请求。 Kubernetes 提供了就绪探测器来发现并缓解这些情况。 容器所在 Pod 上报还未就绪的信息,并且不接受通过 Kubernetes Service 的流量. 就绪探测器在容器的整个生命周期中保持运行状态; 活跃探测器 不等待 就绪性探测器成功。 如果要在执行活跃探测器之前等待,应该使用 initialDelaySecondsstartupProbe; 就绪探测器的配置和存活探测器的配置相似; 唯一区别就是要使用 readinessProbe 字段, 而不是 livenessProbe 字段;

readinessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5
3.2.5 资源限额

如果你没有为一个容器指定内存限制,则自动遵循以下情况之一

  • 容器可无限制地使用内存.容器可以使用其所在节点所有的可用内存, 进而可能导致该节点调用 OOM Killer. 此外,如果发生 OOM Kill,没有资源限制的容器将被杀掉的可行性更大.
  • 运行的容器所在命名空间有默认的内存限制,那么该容器会被自动分配默认限制. 集群管理员可用使用 LimitRange 来指定默认的内存限制.
---
apiVersion: v1
kind: Pod
metadata:
  name: memory-limit
  namespace: default
spec:
  containers:
  - name: memory-demo
    image: mirschao/stress:memory-limit
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]

如果你没有为容器指定 CPU 限制,则会发生以下情况之一

  • 容器在可以使用的 CPU 资源上没有上限.因而可以使用所在节点上所有的可用 CPU 资源.
  • 容器在具有默认 CPU 限制的名字空间中运行,系统会自动为容器设置默认限制. 集群管理员可以使用 LimitRange 指定 CPU 限制的默认值.
---
apiVersion: v1
kind: Pod
metadata:
  name: cpu-limit
  namespace: pod-webservice
spec:
  containers:
  - name: cpu-demo
    image: mirschao/stress:cpu-limit
    resources:
      limits:
        cpu: "1"
      requests:
        cpu: "0.5"
    args:
    - -cpus
    - "2"

使用右侧命令观察pod的生命周期状态 watch -n 1 kubectl get pods -owide

3.3 k8s中的Deployment

  在Kubernetes中经常使用的核心角色就是pod, 但是单独的创建pod则会导致遇到错误时没有 合适的处理解决方案 而再导致业务宕掉, 那么在Kubernetes中就需要一个专门控制pod运行的控制器, 来保障pod时刻都会保持在期望的状态下运行, 从而让pod不会因为运行错误或负载超量而导致的非正常退出, 而影响业务的正常运行; 而且**控制器并不是控制一个pod而是控制多个相同的pod, 让业务运行不再受到单点故障的困扰; 且时刻保持在与 etcd 中声明的期望状态**;

  Deployment控制器是Kubernetes中最常用的控制器, 大多用来控制 无状态应用 的运行和状态维护, 它会根据**标签来控制携带对应标签的pod; 但究其原理: deployment 去控制 replicaset, replicaset 控制 pod; 那也就是说deployment是控制副本集的控制器, 用来管理新老的replicaset副本集, 从而能够完成对业务pod版本的更新和回退; 而对于 pod的数量 是由replicaset来控制的, 主要控制pod的运行实例个数(准确的说应该是健康实例个数**); 当然不管是deployment还是replicaset都是通过kubelet向apiserver请求etcd中的期望状态值和声明好的配置内容的, 从而来保障pod的稳定健康的运行;

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

尝试使用 kubectl delete pod nginx-<custom_id> -n <namespace> 来删除pod, 并观察是否有新的pod启动起来了; 这时就能体会到Kubernetes控制的强大之处了; 如果虚拟机也配备这样的能力岂不更加具备未来色彩; 当然Google已经实现了, 那就是他们这么多年一直在用的 Borg系统 , 同时它也是Kubernetes的前身, 也就是说其脱产于Borg系统;

并不是只有nginx才是无状态应用, 比如tomcat、前后端的服务、数据分析的服务等, 这些不存储请求端数据的应用就是无状态应用; 下面以前端Vue代码为例演示如何在Kubernetes中发布前端应用的服务;

Dcokerfile:

# 多阶段构建应用镜像
FROM node:14 AS builder
WORKDIR /srv/app/projects
COPY . .
RUN npm install && npm run build

FROM nginx
RUN mkdir /app
COPY --from=builder /srv/app/projects/dist /app
COPY nginx.conf /etc/nginx/nginx.conf

Nginx.conf:

user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
  worker_connections  1024;
}
http {
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;
  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
  access_log  /var/log/nginx/access.log  main;
  sendfile        on;
  keepalive_timeout  65;
  server {
    listen       80;
    server_name  daemon.hiops.icu;
    location / {
      root   /app;
      index  index.html;
      try_files $uri $uri/ /index.html;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
      root   /usr/share/nginx/html;
    }
  }
}

deployment.yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: fronted-daemon-deployment
  labels:
    app: fronted-daemon
spec:
  replicas: 3
  selector:
    matchLabels:
      app: fronted
  template:
    metadata:
      labels:
        app: fronted
    spec:
      containers:
      - name: vue-app-index
        image: mirschao/fronted:vue-app-index-v1.0
        ports:
        - containerPort: 80

Deployment之滚动更新: 假设对于上述的vue应用而言, 并不是一成不变的, 需要在研发的过程中进行更新以适配不同的用户喜好, 那就需要在特定的时间或者空间中去更新它, 那么此时经过分析可知, 更新deployment控制的pod实际上就是改变pod中所应用的镜像tag即可, 这样就可以将新代码封装的镜像替换掉旧镜像进行版本和功能上的更新了; 也称之为版本迭代;

但值得说明的是这并不是作为发布迭代的最好方案, 因为在更新的过程中是新旧版本交替的过程, 也是一个此消彼长的过程; 旧版本在被删除的时候并不会考虑到正在链接用户的请求是否被处理完成, 那么直接滚动更新就会造成不好的用户体验, 从而影响网站的用户增长率问题, 此问题在后续的章节中会详细讲解 ArgoCD 的发布模式;

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: fronted-daemon-deployment
  labels:
    app: fronted-daemon
spec:
  replicas: 3
  selector:
    matchLabels:
      app: fronted
  template:
    metadata:
      labels:
        app: fronted
    spec:
      containers:
      - name: vue-app-index
        image: mirschao/fronted:vue-app-index-v1.1
        ports:
        - containerPort: 80

Deployment回滚: 这时就需要 replicaset 来起作用了, 每次更新deployment的时候都会将原来的 replicaset 记录下来, 而此时想要回滚即将原来的 replicaset 启动起来即可; 但在生产中这是禁止直接操作的, 大多数时候你所编写的yaml资源清单都是与某些仓库关联的, 所以牵一发而动全身, 一定要分析好再修改, 此细节在后续课程会详细讲解; 这里为了实验采用最原始暴力的方式:

#!/usr/bin/env bash
#
# author: liuchao
# email: mirs_chao@163.com
# github: https://github.com/mirschao
# usage: controller rollout deployment before one version.
# ⚠️: 本脚本请勿轻易在所入职公司直接采用, 需进行仔细斟酌和修改后再使用, 执行前请测试!!!


function rolloutVersion() {
  
}

rolloutVersion $1
3.4 k8s中的DaemonSet
3.5 k8s中的CronJob、Job
3.6 k8s中的Service

Metallb 裸金属集群网络负载均衡器(Beta)

$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.2/config/manifests/metallb-native.yaml

$ cat <<-EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: cheap
  namespace: metallb-system
spec:
  addresses:
  - 10.9.12.0/24
EOF
3.7 k8s中的ConfigMap、Secret
3.8 k8s中的StorageClass
3.8.1 nfs共享卷
3.8.2 glusterfs共享卷
3.9 k8s中的StatefulSet

4. Kubernetes错误排查导向图

k8s_error_check
#!/usr/bin/env bash
#
# coding: utf-8
# author: liuchao
# email: mirs_chao@163.com
# github: https://github.com/mirschao
# usage: analyst kubernetes error

function upgrade() {
  if which git; then
    if [ $(git fetch origin | wc -l) -gt 0 ]; then
      
    fi
  fi
}
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐