当前位置:   article > 正文

容器编排之战——kubernetes

容器编排之战

目录

一、kubernetes简介

1、基本概念

1、传统的应用部署方式

2、新的应用部署方式(部署容器)

3、容器编排工具中的战斗机--------Kubernetes

2、Kubernetes核心概念

3、Kubernetes架构和组件

1、Master节点

2、Node节点

3、组件之间的通信

三、常用镜像仓库

四、Kubernetes集群部署方式

二进制方式部署k8s集群 

1、部署Etcd集群

2、部署Flannel网络

3、在Master节点部署组件

4、在Node节点部署组件

kubeadm方式部署k8s集群

1、获取镜像

 2、所有节点安装docker

3、禁用swap分区

4、所有节点安装kubeadm和kubelet

5、配置启动kubelet(所有节点)

6、配置master节点

 7、所有node节点操作

8、配置使用网络插件

错误整理

五、部署Harbor仓库

六、集群基本操作--查看集群信息

查看node的详细信息

创建命名空间

七、发布第一个容器化应用

八、YAML文件语法解析

九、Pod API属性详解

1、Pod调度

1、指定node节点的名称(nodeName)

2、指定node标签(nodeSelector)

2、域名解析

3、进程共享

十、容器属性

1、Pod 里最重要的字段"Containers":

2、k8s 对 Container 的定义,和 Docker 相比并没有什么太大区别。

3、ImagePullPolicy属性

4、Pod的生命周期

1、简介

2、生命周期

3、案例

 4、扩展

十一、Projected Volume

1、什么是Projected Volume?

2、Secret详解

1、命令方式创建secret

2、yaml方式创建Secret:

3、使用secret

4、映射secret key到指定的路径

5、被挂载的secret内容自动更新

6、以环境变量的形式使用Secret

7、实验:

3、ConfigMap详解

1、创建ConfigMap

2、通过命令行参数--from-literal创建

 3、通过指定文件创建

4、指定目录创建

5、通过事先写好configmap的标准yaml文件创建

6、使用ConfigMap的方式

7、通过环境变量使用

8、通过envFrom、configMapRef、name使得configmap中的所有key/value对儿  都自动变成环境变量:

9、作为volume挂载使用 

4、Downward API

1、环境变量的方式

2、volume挂载方式

5、ServiceAccount

1、什么是ServiceAccount

2、ServiceAccount与UserAccount区别

3、ServiceAccount应用示例

十二、RBAC详解(基于角色的访问控制)

1、什么是RBAC

2、 Role与ClusterRole

3、实验一:role

4、实验二:clusterrole

5、设置上下文和账户切换

十三、容器监控检查及恢复机制

1、命令模式探针

2、http get方式探针

十四、POD的恢复策略

十五、Deployment详解

 1、为什么使用Deployment

2、创建Deployment

十六、Service详解

1、什么是Service

2、Service的几种类型

1、ClusterIP

2、NodePort

3、LoadBalance

4、ExternalName

5、ingress是干嘛的?

6、kube-proxy与iptables的关系

十七、RC资源(了解)

1、什么是RC

2、创建RC

3、完整TOMCAT实例

十八、K8S之暴露IP给外网

1、ClusterIP

2、NodePort(常用)

3、loadbalance

4、Ingress

十九、控制器模式解析

1、什么是控制集群模式

2、控制器种类

二十、滚动更新

二十一、版本回滚

1、查看版本历史

2、回滚到以前的旧版本

3、回滚到更早之前的版本

 二十二、部署DASHBOARD应用

二十三、k8s持久化存储PV和PVC

1、PV和PVC的引入

2、通过NFS实现持久化存储

 3、PV的回收

4、PV&PVC在应用在Mysql的持久化存储实战项目

 5、PV的动态供给

二十四、k8s控制器

1、什么是控制器

2、DaemonSet控制器

 3、StatefulSet控制器

限制:

部署和扩缩保证

部署顺序

收缩顺序

二十五、基于k8s集群的redis-cluster集群


一、kubernetes简介

1、基本概念

kubernetes,简称k8s,是一个开源的,用于管理云平台中多个主机上的容器化的应用,kubernetes的目标是让部署容器化的应用简单且高效,kubernetes提供了应用部署、规划、更新、维护的一种机制。

1、传统的应用部署方式

通过插件或脚本来安装应用。这样做的缺点是应用的运行、配 置、管理、所有生存周期将与当前操作系统绑定,这样做并不利于应用的升级更新/回滚等操作,当然也可以通过创建虚拟机的方式来实现某些功能,但是虚拟机非常重,并不利于 可移植性

2、新的应用部署方式(部署容器)

通过部署容器方式实现,每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。相对于虚拟机,容器能快速部署, 由于容器与底层设施、机器文件系统解耦的,所以它能在不同云、不同版本操作系统间进行迁移。

容器占用资源少、部署快,每个应用可以被打包成一个容器镜像,每个应用与容器间成一对一关系也使容器有更大优势,使用容器可以在 build 或 release 的阶段,为应用创建容器镜像,因为每个应用不需要与其余的应用堆栈组合,也不依赖于生产环境基础结构, 这使得从研发到测试、生产能提供一致环境。类似地,容器比虚拟机轻量、更“透明”, 这更便于监控和管理。

3、容器编排工具中的战斗机--------Kubernetes

Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、 应用容器化管理。在生产环境中部署一个应用程序时,通常要部署该应用的多个实例以便 对应用请求进行负载均衡。
在 Kubernetes 中,我们可以创建多个容器,每个容器里面运行一个应用实例,然后通过内置的负载均衡策略,实现对这一组应用实例的管理、发现、访问,而这些细节都不需要运维人员去进行复杂的手工配置和处理。

2、Kubernetes核心概念

Master

master主要负责资源调度,控制副本,和提供统一访问集群的入口。--核心节点,也是管理节点

  1. master nginx Pod
  2. node1 容器
  3. node2 容器

Node

node是kubernetes集群架构中运行pod的服务节点。node是kubernetes集群操作的单元,用来承载被分配pod的运行,是pod运行的宿主机,有master管理,并汇报容器状态个master,同时根据master要求管理容器生命周期。

Node IP

node节点的ip地址,是kubernetes集群中每个节点的物理网卡的ip地址,是真实存在的物理网络,所有属于这个网络的服务器之间都能够通过这个网络直接通信。

Pod

pod直译是豆荚,可以把容器想象成豆荚里的豆子,把一个或多个关系紧密的豆子包在一起就是豆荚(一个pod)。在k8s中我们不会直接操作容器,而是把容器包装成pod进行管理

运行在node节点上,若干相关容器的组合。pod内包含的容器运行在同一台宿主机上,使用相同的网络命名空间、ip地址和端口,能够通过localhost进行通信。pod是k8s进行创建、调度和管理的最小单位,他提供了比容器更高层次的抽象,使得部署和管理更加灵活。一个pod可以包含一个或多个容器。在公司一般一个pod只运行一个容器,加上自带的pause容器一共两个。

pod就是k8s集群里的“应用”;而一个平台应用,可以由多个容器组成。

 pause容器

每个pod中都有一个pause容器,pause容器作为pod的网络接入点,pod中其他的容器会使用容器映射模式启动并接入到这个pause容器。

  • 属于同一个pod的所有容器共享网络的namespace。
  • 如果pod所在的node宕机,会将这个node上的pod重新调度到其他节点上。

Pod Volume

  • docker volume对应kubernetes中的pod volume;
  • 数据卷,挂载宿主机文件、目录或者外部存储到pod中,为应用服务提供存储,也可以解决pod中容器之间共享数据。

资源限制:每个pod可以设置限额的计算机资源,有cpu和memory;

 Pod IP

pod的ip地址,是docker engine根据docker0网桥的ip地址段进行分配的,通常是一个虚拟的二层网络,位于不同的node上的pod能够彼此通信,需要通过pod ip所在的虚拟二层网络进行通信,而真实的tcp流量则是通过node ip所在的物理网卡流出的

Event

是一个事件记录,记录了时间最早产生的原因、最后重复时间、重复次数、发起者、类型,以及导致此事件的原因等信息。event通常关联到具体资源对象上,是排查故障的重要参考信息

Namespace

命名空间将资源对象逻辑上分配到不同的namespace,可以是不同的项目、用户等区分管理,并设定控制策略,从而实现多租户。命名空间也称为虚拟集群

Replica Set

确保在任何给定时间制定的pod副本数量,并提供声明式更新等功能

Deployment

deployment是一个更高层次的API/资源对象,他管理replicasets和pod,并提供声明式更新等功能

官方建议使用deployment管理replicasets,而不是直接使用replicasets,这就意味着可能永远不需要直接操作replicasets对象,因此deployment将会是使用最频繁的资源对象。

RC-Replication Controller

replication Controller用来管理pod的副本,保证集群中存在指定数量的pod副本。集群副本中的数量大于指定数量,则会停止指定数量之外的多余pod数量,反之,则会启动少于指定数量个数的容器,保证数量不变,replication Controller是实现弹性伸缩、动态扩容和        滚动升级的核心

部署和升级pod,声明某种pod的副本数量在任意时刻都符合某个预期值;

  • pod期待的副本数
  • 用于筛选目标pod的label selector
  • 当pod副本数量小于预期数量的时候,用于创建pod的pod模板

Service

service定义了pod的逻辑集合和访问集合的策略,是真实服务的抽象

service提供了一个统一的服务访问入口以及服务代理和发现机制,用户不需要了解后台pod是如何运行。

一个service定义了访问pod的方式,就像单个固定的ip地址和与其对应的dns名之间的关系

Service其实就是我们经常提起的微服务架构中的一个"微服务",通过分析、识别并建模系统中的所有服务为微服务——Kubernetes Service,最终我们的系统由多个提供不同业务能力而又彼此独立的微服务单元所组成,服务之间通过TCP/IP进行通信,从而形成了我们强大而又灵活的弹性网络,拥有了强大的分布式能力、弹性扩展能力、容错能力;

 如图示,每个Pod都提供了一个独立的Endpoint(Pod IP+ContainerPort)以被客户端访问,多个Pod副本组成了一个集群来提供服务,一般的做法是部署一个负载均衡器来访问它们,为这组Pod开启一个对外的服务端口如8000,并且将这些Pod的Endpoint列表加入8000端口的转发列表中,客户端可以通过负载均衡器的对外IP地址+服务端口来访问此服务。运行在Node上的kube-proxy其实就是一个智能的软件负载均衡器,它负责把对Service的请求转发到后端的某个Pod实例上,并且在内部实现服务的负载均衡与会话保持机制。Service不是共用一个负载均衡器的IP地址,而是每个Servcie分配一个全局唯一的虚拟IP地址,这个虚拟IP被称为Cluster IP。

Cluster IP

Service的IP地址,特性: 

  • ​仅仅作用于Kubernetes Servcie这个对象,并由Kubernetes管理和分配IP地址;
  • ​无法被Ping,因为没有一个"实体网络对象"来响应;
  • ​只能结合Service Port组成一个具体的通信端口;
  • ​Node IP网、Pod IP网与Cluster IP网之间的通信,采用的是Kubernetes自己设计的一种编程方式的特殊的路由规则,与IP路由有很大的不同

Lable

Kubernetes中的任意API对象都是通过Label进行标识,Label的实质是一系列的K/V键值对。Label是Replication Controller和Service运行的基础,二者通过Label来进行关联Node上运行的Pod。

一个label是一个被附加到资源上的键/值对,譬如附加到一个Pod上,为它传递一个用户自定的并且可识别的属性.Label还可以被应用来组织和选择子网中的资源

Endpoint(IP+Port)

标识服务进程的访问点;

注:Node、Pod、Replication Controller和Service等都可以看作是一种"资源对象",几乎所有的资源对象都可以通过Kubernetes提供的kubectl工具执行增、删、改、查等操作并将其保存在etcd中持久化存储。

3、Kubernetes架构和组件

主从分布式架构,Master/Node

 组件:

  1. Kubernetes Master:
  2. 集群控制节点,负责整个集群的管理和控制,基本上Kubernetes所有的控制命令都是发给它,它来负责具体的执行过程,我们后面所有执行的命令基本都是在Master节点上运行的;
  3. 包含如下组件:
  4. 1.Kubernetes API Server
  5. 作为Kubernetes系统的入口,其封装了核心对象的增删改查操作,以RESTful API接口方式提供给外部客户和内部组件调用。维护的REST对象持久化到Etcd中存储。
  6. 2.Kubernetes Scheduler
  7. 为新建立的Pod进行节点(node)选择(即分配机器),负责集群的资源调度。组件抽离,可以方便替换成其他调度器。
  8. 3.Kubernetes Controller
  9. 负责执行各种控制器,目前已经提供了很多控制器来保证Kubernetes的正常运行。
  10. - Replication Controller
  11. 管理维护Replication Controller,关联Replication Controller和Pod,保证Replication Controller定义的副本数量与实际运行Pod数量一致。
  12. ​ - Deployment Controller
  13. 管理维护Deployment,关联Deployment和Replication Controller,保证运行指定数量的Pod。当Deployment更新时,控制实现Replication Controller和 Pod的更新。
  14. - Node Controller
  15. 管理维护Node,定期检查Node的健康状态,标识出(失效|未失效)的Node节点。
  16. - Namespace Controller
  17. 管理维护Namespace,定期清理无效的Namespace,包括Namesapce下的API对象,比如Pod、Service等。
  18. - Service Controller
  19. 管理维护Service,提供负载以及服务代理。
  20. - EndPoints Controller
  21. 管理维护Endpoints,关联Service和Pod,创建Endpoints为Service的后端,当Pod发生变化时,实时更新Endpoints。
  22. - Service Account Controller
  23. 管理维护Service Account,为每个Namespace创建默认的Service Account,同时为Service Account创建Service Account Secret。
  24. - Persistent Volume Controller
  25. 管理维护Persistent Volume和Persistent Volume Claim,为新的Persistent Volume Claim分配Persistent Volume进行绑定,为释放的Persistent Volume执行清理回收。
  26. - Daemon Set Controller
  27. 管理维护Daemon Set,负责创建Daemon Pod,保证指定的Node上正常的运行Daemon Pod。
  28.   - Job Controller
  29. 管理维护Job,为Jod创建一次性任务Pod,保证完成Job指定完成的任务数目
  30. - Pod Autoscaler Controller
  31. 实现Pod的自动伸缩,定时获取监控数据,进行策略匹配,当满足条件时执行Pod的伸缩动作。
  32. Kubernetes Node:
  33. 除了Master,Kubernetes集群中的其他机器被称为Node节点,Node节点才是Kubernetes集群中的工作负载节点,每个Node都会被Master分配一些工作负载(Docker容器),当某个Node宕机,其上的工作负载会被Master自动转移到其他节点上去;
  34. 包含如下组件:
  35.   1.Kubelet
  36.     负责管控容器,Kubelet会从Kubernetes API Server接收Pod的创建请求,启动和停止容器,监控容器运行状态并汇报给Kubernetes API Server。
  37.   2.Kubernetes Proxy
  38.     负责为Pod创建代理服务,Kubernetes Proxy会从Kubernetes API Server获取所有的Service信息,并根据Service的信息创建代理服务,实现Service到Pod的请求路由和转发,从而实现Kubernetes层级的虚拟转发网络。
  39.   3.Docker Engine(docker),Docker引擎,负责本机的容器创建和管理工作;
  40.   
  41.   4.Flannel网络插件
  42. 数据库
  43. etcd数据库,可以部署到master上,也可以独立部署
  44. 分布式键值存储系统。用于保存集群状态数据,比如Pod、Service等对象信息

1、Master节点

etcd:etcd存放的就是集群的状态,一般把所有的集群信息都存放到etcd当中,etcd不属于Kubernetes的某一个部分,而是单独集群部署的,API Server是它唯一的入口,可以直接访问etcd。

API Server:API Server提供了操作资源的唯一入口,例如认证、授权、访问控制、注册或者发现,都是通过API Server来完成的。

Controller Manager:负责管理集群各种资源,保证资源处于预期的状态。Controller Manager由多种controller组成,包括replication controller、endpoints controller、namespace controller、serviceaccounts controller等 。由控制器完成的主要功能主要包括生命周期功能和API业务逻辑,具体如下:

生命周期功能:包括Namespace创建和生命周期、Event垃圾回收、Pod终止相关的垃圾回收、级联垃圾回收及Node垃圾回收等。
API业务逻辑:例如,由ReplicaSet执行的Pod扩展等。
Scheduler:Scheduler调度控制器负责整个集群的资源调度,按照默认或者指定的调度策略将Pod调度到符合要求的Node节点上运行

2、Node节点

Kubelet:Kubelet维护整个容器的生命周期,API Server创建Pod,Scheduler将Pod调度到符合要求的Node节点上,该节点上的Kubelet就会去运行Pod以及Docker,Pod的存储以及网络都是Kubelet进行管理的

Docker:Docker负责镜像的管理,例如镜像的拉取、启动容器等

Kube-proxy:Kube-proxy主要是提供整个集群内部Service的负载均衡和服务发现

3、组件之间的通信

API Server是etcd访问的唯一入口,只有API Server才能访问和操作etcd集群;API Server对内和对外都提供了统一的REST API,其他组件都是通过API Server进行通信的

用户使用kubectl命令来请求API Server接口完成相应操作

Kubernetes内部组件都是通过一种watch机制去监控API Server中的资源变化,然后对其做一些相应的操作

三、常用镜像仓库

  1. daocloud的docker镜像库:daocloud.io/library
  2. ​docker-hub的k8s镜像库:mirrorgooglecontainers
  3. aliyun的k8s镜像库:registry.cn-hangzhou.aliyuncs.com/google-containers
  4. aliyun的docker镜像库web页面:https://cr.console.aliyun.com/cn-hangzhou/images
  5. google的镜像库web页面:https://console.cloud.google.com/gcr/images/google-containers?project=google-containers

四、Kubernetes集群部署方式

方式1、minikube

  1. Minikube是一个工具,可以在本地快速运行一个单点的Kubernetes,尝试Kubernetes或日常开发的用户使用。不能用于生产环境。
  2. ​官方地址:https://kubernetes.io/docs/setup/minikube/

方式2、 kubeadm

  1. Kubeadm也是一个工具,提供kubeadm init和kubeadm join,用于快速部署Kubernetes集群。
  2. ​官方地址:https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/

方式3、 直接使用epel-release yum源,缺点就是版本较低 1.5

方式4、 二进制包

  1. 从官方下载发行版的二进制包,手动部署每个组件,组成Kubernetes集群。
  2. 官方也提供了一个互动测试环境供大家测试:https://kubernetes.io/cn/docs/tutorials/kubernetes-basics/cluster-interactive/

二进制方式部署k8s集群 

目标任务:


  1. 1、Kubernetes集群部署架构规划

  2. 2、部署Etcd集群

  3. 3、在Node节点安装Docker

  4. 4、部署Flannel网络插件

  5. 5、在Master节点部署组件

  6. 6、在Node节点部署组件

  7. 7、查看集群状态

  8. 8、运行一个测试示例

  9. 9、部署Dashboard(Web UI)

准备环境

  1. 三台机器,所有机器相互做解析 centos7.4
  2. 关闭防火墙和selinux
  3. [root@k8s-master ~]# vim /etc/hosts
  4. 10.0.0.130 k8s-master
  5. 10.0.0.131 k8s-node1
  6. 10.0.0.132 k8s-node2

1、部署Etcd集群

使用cfssl来生成自签证书,任何机器都行,证书这块儿知道怎么生成、怎么用即可,暂且不用过多研究(这个证书随便在那台机器生成都可以。哪里用将证书拷贝到哪里就可以了。)

  1. 下载cfssl工具:下载的这些是可执行的二进制命令直接用就可以了
  2. [root@k8s-master ~]# wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
  3. [root@k8s-master ~]# wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
  4. [root@k8s-master ~]# wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
  5. [root@k8s-master ~]# chmod +x cfssl_linux-amd64 cfssljson_linux-amd64 cfssl-certinfo_linux-amd64
  6. [root@k8s-master ~]# mv cfssl_linux-amd64 /usr/local/bin/cfssl
  7. [root@k8s-master ~]# mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
  8. [root@k8s-master ~]# mv cfssl-certinfo_linux-amd64 /usr/bin/cfssl-certinfo
  9. 生成Etcd证书:
  10. 创建以下三个文件:
  11. [root@k8s-master ~]# mkdir cert
  12. [root@k8s-master ~]# cd cert/
  13. [root@k8s-master cert]# vim ca-config.json #生成ca中心的
  14. [root@k8s-master cert]# cat ca-config.json
  15. {
  16. "signing": {
  17. "default": {
  18. "expiry": "87600h"
  19. },
  20. "profiles": {
  21. "www": {
  22. "expiry": "87600h",
  23. "usages": [
  24. "signing",
  25. "key encipherment",
  26. "server auth",
  27. "client auth"
  28. ]
  29. }
  30. }
  31. }
  32. }
  33. [root@k8s-master cert]# vim ca-csr.json #生成ca中心的证书请求文件
  34. [root@k8s-master cert]# cat ca-csr.json
  35. {
  36. "CN": "etcd CA",
  37. "key": {
  38. "algo": "rsa",
  39. "size": 2048
  40. },
  41. "names": [
  42. {
  43. "C": "CN",
  44. "L": "Beijing",
  45. "ST": "Beijing"
  46. }
  47. ]
  48. }
  49. [root@k8s-master cert]# vim server-csr.json #生成服务器的证书请求文件
  50. [root@k8s-master cert]# cat server-csr.json
  51. {
  52. "CN": "etcd",
  53. "hosts": [
  54. "10.0.0.130",
  55. "10.0.0.131",
  56. "10.0.0.132"
  57. ],
  58. "key": {
  59. "algo": "rsa",
  60. "size": 2048
  61. },
  62. "names": [
  63. {
  64. "C": "CN",
  65. "L": "BeiJing",
  66. "ST": "BeiJing"
  67. }
  68. ]
  69. }
  70. 生成证书:
  71. [root@k8s-master cert]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
  72. [root@k8s-master cert]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=www server-csr.json | cfssljson -bare server
  73. [root@k8s-master cert]# ls *pem
  74. ca-key.pem ca.pem server-key.pem server.pem

安装Etcd

二进制包下载地址:https://github.com/coreos/etcd/releases/tag/v3.2.12

  1. 一下步骤三台机器都操作
  2. [root@k8s-master ~]# wget https://github.com/etcd-io/etcd/releases/download/v3.2.12/etcd-v3.2.12-linux-amd64.tar.gz
  3. [root@k8s-master ~]# mkdir /opt/etcd/{bin,cfg,ssl} -p
  4. [root@k8s-master ~]# tar zxvf etcd-v3.2.12-linux-amd64.tar.gz
  5. [root@k8s-master ~]# mv etcd-v3.2.12-linux-amd64/{etcd,etcdctl} /opt/etcd/bin/
  6. 创建etcd配置文件
  7. [root@k8s-master ~]# cd /opt/etcd/cfg/
  8. [root@k8s-master cfg]# vim etcd
  9. #[Member]
  10. ETCD_NAME="etcd01" #节点名称,各个节点不能相同
  11. ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
  12. ETCD_LISTEN_PEER_URLS="https://10.0.0.130:2380" #写当前节点的ip
  13. ETCD_LISTEN_CLIENT_URLS="https://10.0.0.130:2379" #写当前节点的ip
  14. #[Clustering]
  15. ETCD_INITIAL_ADVERTISE_PEER_URLS="https://10.0.0.130:2380" #写当前节点的ip
  16. ETCD_ADVERTISE_CLIENT_URLS="https://10.0.0.130:2379" #写当前节点的ip
  17. ETCD_INITIAL_CLUSTER="etcd01=https://10.0.0.130:2380,etcd02=https://10.0.0.131:2380,etcd03=https://10.0.0.132:2380" #每个节点的ip
  18. ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
  19. ETCD_INITIAL_CLUSTER_STATE="new"
  20. 参数解释:
  21. * ETCD_NAME 节点名称,每个节点名称不一样
  22. * ETCD_DATA_DIR 存储数据目录(他是一个数据库,不是存在内存的,存在硬盘中的,所有和k8s有关的信息都会存到etcd里面的)
  23. * ETCD_LISTEN_PEER_URLS 集群通信监听地址
  24. * ETCD_LISTEN_CLIENT_URLS 客户端访问监听地址
  25. * ETCD_INITIAL_ADVERTISE_PEER_URLS 集群通告地址
  26. * ETCD_ADVERTISE_CLIENT_URLS 客户端通告地址
  27. * ETCD_INITIAL_CLUSTER 集群节点地址
  28. * ETCD_INITIAL_CLUSTER_TOKEN 集群Token
  29. * ETCD_INITIAL_CLUSTER_STATE 加入集群的当前状态,new是新集群,existing表示加入已有集群
  1. systemd管理etcd:
  2. [root@k8s-master cfg]# vim /usr/lib/systemd/system/etcd.service
  3. [Unit]
  4. Description=Etcd Server
  5. After=network.target
  6. After=network-online.target
  7. Wants=network-online.target
  8. [Service]
  9. Type=notify
  10. EnvironmentFile=/opt/etcd/cfg/etcd
  11. ExecStart=/opt/etcd/bin/etcd \
  12. --name=${ETCD_NAME} \
  13. --data-dir=${ETCD_DATA_DIR} \
  14. --listen-peer-urls=${ETCD_LISTEN_PEER_URLS} \
  15. --listen-client-urls=${ETCD_LISTEN_CLIENT_URLS},http://127.0.0.1:2379 \
  16. --advertise-client-urls=${ETCD_ADVERTISE_CLIENT_URLS} \
  17. --initial-advertise-peer-urls=${ETCD_INITIAL_ADVERTISE_PEER_URLS} \
  18. --initial-cluster=${ETCD_INITIAL_CLUSTER} \
  19. --initial-cluster-token=${ETCD_INITIAL_CLUSTER_TOKEN} \
  20. --initial-cluster-state=new \
  21. --cert-file=/opt/etcd/ssl/server.pem \
  22. --key-file=/opt/etcd/ssl/server-key.pem \
  23. --peer-cert-file=/opt/etcd/ssl/server.pem \
  24. --peer-key-file=/opt/etcd/ssl/server-key.pem \
  25. --trusted-ca-file=/opt/etcd/ssl/ca.pem \
  26. --peer-trusted-ca-file=/opt/etcd/ssl/ca.pem
  27. Restart=on-failure
  28. LimitNOFILE=65536
  29. [Install]
  30. WantedBy=multi-user.target
  1. 把刚才生成的证书拷贝到配置文件中的位置:(将master上面生成的证书scp到剩余两台机器上面)
  2. [root@k8s-master cfg]# cd /root/cert/
  3. [root@k8s-master cert]# cp ca*pem server*pem /opt/etcd/ssl
  4. 直接拷贝到剩余两台etcd机器:
  5. [root@k8s-master cert]# scp ca*pem server*pem k8s-node1:/opt/etcd/ssl
  6. [root@k8s-master cert]# scp ca*pem server*pem k8s-node2:/opt/etcd/ssl
  7. 全部启动并设置开启启动:
  8. [root@k8s-master cert]# systemctl daemon-reload
  9. [root@k8s-master cert]# systemctl start etcd
  10. [root@k8s-master cert]# systemctl enable etcd
  11. 都部署完成后,三台机器都检查etcd集群状态:
  12. [root@k8s-master cert]# /opt/etcd/bin/etcdctl --ca-file=/opt/etcd/ssl/ca.pem --cert-file=/opt/etcd/ssl/server.pem --key-file=/opt/etcd/ssl/server-key.pem --endpoints="https://10.0.0.130:2379,https://10.0.0.130:2379,https://10.0.0.130:2379" cluster-health
  13. member 3db8bdf3a21a92c4 is healthy: got healthy result from https://10.0.0.132:2379
  14. member 455016c4da9c6bbd is healthy: got healthy result from https://10.0.0.131:2379
  15. member 8f696e193713398b is healthy: got healthy result from https://10.0.0.130:2379
  16. cluster is healthy
  17. 如果输出上面信息,就说明集群部署成功。
  18. 如果有问题第一步先看日志:/var/log/messages 或 journalctl -u etcd
  19. 报错:
  20. Jan 15 12:06:55 k8s-master1 etcd: request cluster ID mismatch (got 99f4702593c94f98 want cdf818194e3a8c32)
  21. 解决:因为集群搭建过程,单独启动过单一etcd,做为测试验证,集群内第一次启动其他etcd服务时候,是通过发现服务引导的,所以需要删除旧的成员信息,所有节点作以下操作
  22. [root@k8s-master default.etcd]# pwd
  23. /var/lib/etcd/default.etcd
  24. [root@k8s-master1 default.etcd]# rm -rf member/
  25. ========================================================

node节点安装docker(过程略)

2、部署Flannel网络

Flannel要用etcd存储自身一个子网信息,所以要保证能成功连接Etcd,写入预定义子网段:
在node节点部署,如果没有在master部署应用,那就不要在master部署flannel,他是用来给所有的容器用来通信的。

  1. [root@k8s-master ~]# cd cert/
  2. [root@k8s-master cert]# /opt/etcd/bin/etcdctl \
  3. --ca-file=ca.pem --cert-file=server.pem --key-file=server-key.pem \
  4. --endpoints="https://10.0.0.130:2379,https://10.0.0.130:2379,https://10.0.0.130:2379" \
  5. set /coreos.com/network/config '{ "Network": "172.17.0.0/16", "Backend": {"Type": "vxlan"}}'
  6. { "Network": "172.17.0.0/16", "Backend": {"Type": "vxlan"}}
  7. 以下部署步骤在规划的每个node节点都操作。
  8. 下载二进制包:
  9. [root@k8s-node1 ~]# wget https://github.com/coreos/flannel/releases/download/v0.10.0/flannel-v0.10.0-linux-amd64.tar.gz
  10. [root@k8s-node1 ~]# tar zxvf flannel-v0.10.0-linux-amd64.tar.gz
  11. [root@k8s-node1 ~]# mkdir -pv /opt/kubernetes/bin
  12. [root@k8s-node1 ~]# mv flanneld mk-docker-opts.sh /opt/kubernetes/bin
  13. 配置Flannel:
  14. [root@k8s-node1 ~]# mkdir -pv /opt/kubernetes/cfg/
  15. [root@k8s-node1 ~]# vim /opt/kubernetes/cfg/flanneld
  16. FLANNEL_OPTIONS="--etcd-endpoints=https://10.0.0.130:2379,https://10.0.0.130:2379,https://10.0.0.130:2379 -etcd-cafile=/opt/etcd/ssl/ca.pem -etcd-certfile=/opt/etcd/ssl/server.pem -etcd-keyfile=/opt/etcd/ssl/server-key.pem"
  17. systemd管理Flannel:
  18. [root@k8s-node1 ~]# vim /usr/lib/systemd/system/flanneld.service
  19. [Unit]
  20. Description=Flanneld overlay address etcd agent
  21. After=network-online.target network.target
  22. Before=docker.service
  23. [Service]
  24. Type=notify
  25. EnvironmentFile=/opt/kubernetes/cfg/flanneld
  26. ExecStart=/opt/kubernetes/bin/flanneld --ip-masq $FLANNEL_OPTIONS
  27. ExecStartPost=/opt/kubernetes/bin/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/subnet.env
  28. Restart=on-failure
  29. [Install]
  30. WantedBy=multi-user.target
  31. 配置Docker启动指定子网段:可以将源文件直接覆盖掉
  32. [root@k8s-node1 ~]# vim /usr/lib/systemd/system/docker.service
  33. [Unit]
  34. Description=Docker Application Container Engine
  35. Documentation=https://docs.docker.com
  36. After=network-online.target firewalld.service
  37. Wants=network-online.target
  38. [Service]
  39. Type=notify
  40. EnvironmentFile=/run/flannel/subnet.env
  41. ExecStart=/usr/bin/dockerd $DOCKER_NETWORK_OPTIONS
  42. ExecReload=/bin/kill -s HUP $MAINPID
  43. LimitNOFILE=infinity
  44. LimitNPROC=infinity
  45. LimitCORE=infinity
  46. TimeoutStartSec=0
  47. Delegate=yes
  48. KillMode=process
  49. Restart=on-failure
  50. StartLimitBurst=3
  51. StartLimitInterval=60s
  52. [Install]
  53. WantedBy=multi-user.target
  54. 重启flannel和docker:
  55. # systemctl daemon-reload
  56. # systemctl start flanneld
  57. # systemctl enable flanneld
  58. # systemctl restart docker
  59. 注意:如果flannel启动不了请检查设置ip网段是否正确
  60. 检查是否生效:
  61. [root@k8s-node1 ~]# ps -ef | grep docker
  62. root 3632 1 1 22:19 ? 00:00:00 /usr/bin/dockerd --bip=172.17.77.1/24 --ip-masq=false --mtu=1450
  63. 注:
  64. 1. 确保docker0与flannel.1在同一网段。
  65. 2. 测试不同节点互通,在当前节点访问另一个Node节点docker0 IP:案例:node1机器pingnode2机器的docker0上面的ip地址
  66. [root@k8s-node1 ~]# ping 172.17.33.1
  67. PING 172.17.33.1 (172.17.33.1) 56(84) bytes of data.
  68. 64 bytes from 172.17.33.1: icmp_seq=1 ttl=64 time=0.520 ms
  69. 64 bytes from 172.17.33.1: icmp_seq=2 ttl=64 time=0.972 ms
  70. 64 bytes from 172.17.33.1: icmp_seq=3 ttl=64 time=0.642 ms
  71. 如果能通说明Flannel部署成功。如果不通检查下日志:journalctl -u flannel(快照吧!!!)

3、在Master节点部署组件

在部署Kubernetes之前一定要确保etcd、flannel、docker是正常工作的,否则先解决问题再继续。

生成证书

  1. master节点操作--给api-server创建的证书。别的服务访问api-server的时候需要通过证书认证
  2. 创建CA证书:
  3. [root@k8s-master ~]# mkdir -p /opt/crt/
  4. [root@k8s-master ~]# cd /opt/crt/
  5. # vim ca-config.json
  6. {
  7. "signing": {
  8. "default": {
  9. "expiry": "87600h"
  10. },
  11. "profiles": {
  12. "kubernetes": {
  13. "expiry": "87600h",
  14. "usages": [
  15. "signing",
  16. "key encipherment",
  17. "server auth",
  18. "client auth"
  19. ]
  20. }
  21. }
  22. }
  23. }
  24. # vim ca-csr.json
  25. {
  26. "CN": "kubernetes",
  27. "key": {
  28. "algo": "rsa",
  29. "size": 2048
  30. },
  31. "names": [
  32. {
  33. "C": "CN",
  34. "L": "Beijing",
  35. "ST": "Beijing",
  36. "O": "k8s",
  37. "OU": "System"
  38. }
  39. ]
  40. }
  41. [root@k8s-master crt]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
  42. 生成apiserver证书:
  43. [root@k8s-master1 crt]# vim server-csr.json
  44. # cat server-csr.json
  45. {
  46. "CN": "kubernetes",
  47. "hosts": [
  48. "10.0.0.1", //这是后面dns要使用的虚拟网络的网关,不用改,就用这个切忌
  49. "127.0.0.1",
  50. "10.0.0.130", // master的IP地址。
  51. "10.0.0.131",
  52. "10.0.0.132",
  53. "kubernetes",
  54. "kubernetes.default",
  55. "kubernetes.default.svc",
  56. "kubernetes.default.svc.cluster",
  57. "kubernetes.default.svc.cluster.local"
  58. ],
  59. "key": {
  60. "algo": "rsa",
  61. "size": 2048
  62. },
  63. "names": [
  64. {
  65. "C": "CN",
  66. "L": "BeiJing",
  67. "ST": "BeiJing",
  68. "O": "k8s",
  69. "OU": "System"
  70. }
  71. ]
  72. }
  73. [root@k8s-master crt]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes server-csr.json | cfssljson -bare server
  74. 生成kube-proxy证书:
  75. [root@k8s-master crt]# vim kube-proxy-csr.json
  76. # cat kube-proxy-csr.json
  77. {
  78. "CN": "system:kube-proxy",
  79. "hosts": [],
  80. "key": {
  81. "algo": "rsa",
  82. "size": 2048
  83. },
  84. "names": [
  85. {
  86. "C": "CN",
  87. "L": "BeiJing",
  88. "ST": "BeiJing",
  89. "O": "k8s",
  90. "OU": "System"
  91. }
  92. ]
  93. }
  94. [root@k8s-master crt]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy
  95. 最终生成以下证书文件:
  96. [root@k8s-master crt]# ls *pem
  97. ca-key.pem ca.pem kube-proxy-key.pem kube-proxy.pem server-key.pem server.pem

部署apiserver组件---在master节点进行 下载二进制包:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.11.md
下载这个包(kubernetes-server-linux-amd64.tar.gz)就够了,包含了所需的所有组件。

  1. [root@k8s-master ~]# wget https://dl.k8s.io/v1.11.10/kubernetes-server-linux-amd64.tar.gz
  2. [root@k8s-master ~]# mkdir /opt/kubernetes/{bin,cfg,ssl} -pv
  3. [root@k8s-master ~]# tar zxvf kubernetes-server-linux-amd64.tar.gz
  4. [root@k8s-master ~]# cd kubernetes/server/bin
  5. [root@k8s-master bin]# cp kube-apiserver kube-scheduler kube-controller-manager kubectl /opt/kubernetes/bin
  6. [root@k8s-master bin]# cd /opt/crt/
  7. [root@k8s-master crt]# cp server.pem server-key.pem ca.pem ca-key.pem /opt/kubernetes/ssl/
  8. 创建token文件:
  9. [root@k8s-master1 crt]# cd /opt/kubernetes/cfg/
  10. [root@k8s-master1 cfg]# vim token.csv
  11. 674c457d4dcf2eefe4920d7dbb6b0ddc,kubelet-bootstrap,10001,"system:kubelet-bootstrap"
  12. 第一列:随机字符串,自己可生成
  13. 第二列:用户名
  14. 第三列:UID
  15. 第四列:用户组
  16. 创建apiserver配置文件:
  17. [root@k8s-master cfg]# pwd
  18. /opt/kubernetes/cfg
  19. [root@k8s-master cfg]# vim kube-apiserver
  20. KUBE_APISERVER_OPTS="--logtostderr=true \
  21. --v=4 \
  22. --etcd-servers=https://10.0.0.130:2379,https://10.0.0.131:2379,https://10.0.0.132:2379 \
  23. --bind-address=10.0.0.130 \ #master的ip地址,就是安装api-server的机器地址
  24. --secure-port=6443 \
  25. --advertise-address=10.0.0.130 \
  26. --allow-privileged=true \
  27. --service-cluster-ip-range=10.0.0.0/24 \ #这里就用这个网段切记不要修改
  28. --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \
  29. --authorization-mode=RBAC,Node \
  30. --enable-bootstrap-token-auth \
  31. --token-auth-file=/opt/kubernetes/cfg/token.csv \
  32. --service-node-port-range=30000-50000 \
  33. --tls-cert-file=/opt/kubernetes/ssl/server.pem \
  34. --tls-private-key-file=/opt/kubernetes/ssl/server-key.pem \
  35. --client-ca-file=/opt/kubernetes/ssl/ca.pem \
  36. --service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \
  37. --etcd-cafile=/opt/etcd/ssl/ca.pem \
  38. --etcd-certfile=/opt/etcd/ssl/server.pem \
  39. --etcd-keyfile=/opt/etcd/ssl/server-key.pem"
  40. 配置好前面生成的证书,确保能连接etcd。
  41. 参数说明:
  42. * --logtostderr 启用日志
  43. * --v 日志等级
  44. * --etcd-servers etcd集群地址
  45. * --bind-address 监听地址
  46. * --secure-port https安全端口
  47. * --advertise-address 集群通告地址
  48. * --allow-privileged 启用授权
  49. * --service-cluster-ip-range Service虚拟IP地址段
  50. * --enable-admission-plugins 准入控制模块
  51. * --authorization-mode 认证授权,启用RBAC授权和节点自管理
  52. * --enable-bootstrap-token-auth 启用TLS bootstrap功能,后面会讲到
  53. * --token-auth-file token文件
  54. * --service-node-port-range Service Node类型默认分配端口范围
  55. systemd管理apiserver:
  56. [root@k8s-master cfg]# cd /usr/lib/systemd/system
  57. [root@k8s-master system]# vim kube-apiserver.service
  58. [Unit]
  59. Description=Kubernetes API Server
  60. Documentation=https://github.com/kubernetes/kubernetes
  61. [Service]
  62. EnvironmentFile=-/opt/kubernetes/cfg/kube-apiserver
  63. ExecStart=/opt/kubernetes/bin/kube-apiserver $KUBE_APISERVER_OPTS
  64. Restart=on-failure
  65. [Install]
  66. WantedBy=multi-user.target
  67. 启动:
  68. # systemctl daemon-reload
  69. # systemctl enable kube-apiserver
  70. # systemctl start kube-apiserver
  71. # systemctl status kube-apiserver

如果出现以下错误

 去kube-apiserver配置文件中看一下行尾有没有空格,使用set list查看

部署schduler组件---master节点

  1. 创建schduler配置文件:
  2. [root@k8s-master cfg]# vim /opt/kubernetes/cfg/kube-scheduler
  3. KUBE_SCHEDULER_OPTS="--logtostderr=true \
  4. --v=4 \
  5. --master=127.0.0.1:8080 \
  6. --leader-elect"
  7. 参数说明:
  8. * --master 连接本地apiserver
  9. * --leader-elect 当该组件启动多个时,自动选举(HA)
  10. systemd管理schduler组件:
  11. [root@k8s-master cfg]# cd /usr/lib/systemd/system/
  12. [root@k8s-master system]# vim kube-scheduler.service
  13. [Unit]
  14. Description=Kubernetes Scheduler
  15. Documentation=https://github.com/kubernetes/kubernetes
  16. [Service]
  17. EnvironmentFile=-/opt/kubernetes/cfg/kube-scheduler
  18. ExecStart=/opt/kubernetes/bin/kube-scheduler $KUBE_SCHEDULER_OPTS
  19. Restart=on-failure
  20. [Install]
  21. WantedBy=multi-user.target
  22. 启动:
  23. # systemctl daemon-reload
  24. # systemctl enable kube-scheduler
  25. # systemctl start kube-scheduler
  26. # systemctl status kube-scheduler

部署controller-manager组件--控制管理组件

  1. master节点操作:创建controller-manager配置文件:
  2. [root@k8s-master ~]# cd /opt/kubernetes/cfg/
  3. [root@k8s-master cfg]# vim kube-controller-manager
  4. KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=true \
  5. --v=4 \
  6. --master=127.0.0.1:8080 \
  7. --leader-elect=true \
  8. --address=127.0.0.1 \
  9. --service-cluster-ip-range=10.0.0.0/24 \ //这是后面dns要使用的虚拟网络,不用改,就用这个 切忌
  10. --cluster-name=kubernetes \
  11. --cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \
  12. --cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem \
  13. --root-ca-file=/opt/kubernetes/ssl/ca.pem \
  14. --service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem"
  15. systemd管理controller-manager组件:
  16. [root@k8s-master cfg]# cd /usr/lib/systemd/system/
  17. [root@k8s-master system]# vim kube-controller-manager.service
  18. # cat /usr/lib/systemd/system/kube-controller-manager.service
  19. [Unit]
  20. Description=Kubernetes Controller Manager
  21. Documentation=https://github.com/kubernetes/kubernetes
  22. [Service]
  23. EnvironmentFile=-/opt/kubernetes/cfg/kube-controller-manager
  24. ExecStart=/opt/kubernetes/bin/kube-controller-manager $KUBE_CONTROLLER_MANAGER_OPTS
  25. Restart=on-failure
  26. [Install]
  27. WantedBy=multi-user.target
  28. 启动:
  29. # systemctl daemon-reload
  30. # systemctl enable kube-controller-manager
  31. # systemctl start kube-controller-manager
  32. # systemctl status kube-controller-manager.service
  33. 所有组件都已经启动成功,通过kubectl工具查看当前集群组件状态:
  34. [root@k8s-master ~]# /opt/kubernetes/bin/kubectl get cs
  35. NAME STATUS MESSAGE ERROR
  36. scheduler Healthy ok
  37. controller-manager Healthy ok
  38. etcd-2 Healthy {"health": "true"}
  39. etcd-1 Healthy {"health": "true"}
  40. etcd-0 Healthy {"health": "true"}
  41. 如上输出说明组件都正常。

4、在Node节点部署组件

Master apiserver启用TLS认证后,Node节点kubelet组件想要加入集群,必须使用CA签发的有效证书才能与apiserver通信,当Node节点很多时,签署证书是一件很繁琐的事情,因此有了TLS Bootstrapping机制,kubelet会以一个低权限用户自动向apiserver申请证书,kubelet的证书由apiserver动态签署。

认证大致工作流程如图所示:

  1. ----------------------下面这些操作在master节点完成:---------------------------
  2. 将kubelet-bootstrap用户绑定到系统集群角色
  3. [root@k8s-master ~]# /opt/kubernetes/bin/kubectl create clusterrolebinding kubelet-bootstrap \
  4. --clusterrole=system:node-bootstrapper \
  5. --user=kubelet-bootstrap
  6. clusterrolebinding.rbac.authorization.k8s.io/kubelet-bootstrap created
  7. 创建kubeconfig文件:
  8. 在生成kubernetes证书的目录下执行以下命令生成kubeconfig文件:
  9. [root@k8s-master ~]# cd /opt/crt/
  10. 指定apiserver 内网负载均衡地址
  11. [root@k8s-master crt]# KUBE_APISERVER="https://10.0.0.130:6443" #写你master的ip地址,集群中就写负载均衡的ip地址
  12. [root@k8s-master1 crt]# BOOTSTRAP_TOKEN=674c457d4dcf2eefe4920d7dbb6b0ddc 这个就是前面我们在token.csv文件中写的
  13. # 设置集群参数
  14. [root@k8s-master crt]# /opt/kubernetes/bin/kubectl config set-cluster kubernetes \
  15. --certificate-authority=ca.pem \
  16. --embed-certs=true \
  17. --server=${KUBE_APISERVER} \
  18. --kubeconfig=bootstrap.kubeconfig
  19. # 设置客户端认证参数
  20. [root@k8s-master crt]# /opt/kubernetes/bin/kubectl config set-credentials kubelet-bootstrap \
  21. --token=${BOOTSTRAP_TOKEN} \
  22. --kubeconfig=bootstrap.kubeconfig
  23. # 设置上下文参数
  24. [root@k8s-master crt]# /opt/kubernetes/bin/kubectl config set-context default \
  25. --cluster=kubernetes \
  26. --user=kubelet-bootstrap \
  27. --kubeconfig=bootstrap.kubeconfig
  28. # 设置默认上下文
  29. [root@k8s-master crt]# /opt/kubernetes/bin/kubectl config use-context default --kubeconfig=bootstrap.kubeconfig
  30. #====================================================================================
  31. # 创建kube-proxy kubeconfig文件
  32. [root@k8s-master crt]# /opt/kubernetes/bin/kubectl config set-cluster kubernetes \
  33. --certificate-authority=ca.pem \
  34. --embed-certs=true \
  35. --server=${KUBE_APISERVER} \
  36. --kubeconfig=kube-proxy.kubeconfig
  37. [root@k8s-master crt]# /opt/kubernetes/bin/kubectl config set-credentials kube-proxy \
  38. --client-certificate=kube-proxy.pem \
  39. --client-key=kube-proxy-key.pem \
  40. --embed-certs=true \
  41. --kubeconfig=kube-proxy.kubeconfig
  42. [root@k8s-master crt]# /opt/kubernetes/bin/kubectl config set-context default \
  43. --cluster=kubernetes \
  44. --user=kube-proxy \
  45. --kubeconfig=kube-proxy.kubeconfig
  46. [root@k8s-master crt]# /opt/kubernetes/bin/kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
  47. [root@k8s-master crt]# ls
  48. bootstrap.kubeconfig kube-proxy.kubeconfig
  49. #必看:将这两个文件拷贝到Node节点/opt/kubernetes/cfg目录下。
  50. [root@k8s-master crt]# scp *.kubeconfig k8s-node1:/opt/kubernetes/cfg/
  51. [root@k8s-master crt]# scp *.kubeconfig k8s-node2:/opt/kubernetes/cfg/
  1. ----------------------下面这些操作在node节点完成:---------------------------
  2. 部署kubelet组件
  3. #将前面下载的二进制包中的kubelet和kube-proxy拷贝到/opt/kubernetes/bin目录下。
  4. 将master上面的包拷贝过去
  5. [root@k8s-master ~]# scp kubernetes-server-linux-amd64.tar.gz k8s-node1:/root/
  6. [root@k8s-master ~]# scp kubernetes-server-linux-amd64.tar.gz k8s-node2:/root/
  7. [root@k8s-node1 ~]# tar xzf kubernetes-server-linux-amd64.tar.gz
  8. [root@k8s-node1 ~]# cd kubernetes/server/bin/
  9. [root@k8s-node1 bin]# cp kubelet kube-proxy /opt/kubernetes/bin/
  10. 或者直接在master上将解压后的kubernetes/server/bin/目录中的kubelet kube-proxy这两个文件远程拷贝到node节点的/opt/kubernetes/bin目录中
  11. #=====================================================================================
  12. 在两个node节点创建kubelet配置文件:
  13. [root@k8s-node1 ~]# vim /opt/kubernetes/cfg/kubelet
  14. KUBELET_OPTS="--logtostderr=true \
  15. --v=4 \
  16. --hostname-override=10.0.0.131 \ #每个节点自己的ip地址
  17. --kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \
  18. --bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \
  19. --config=/opt/kubernetes/cfg/kubelet.config \
  20. --cert-dir=/opt/kubernetes/ssl \
  21. --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0" #这个镜像需要提前下载
  22. [root@k8s-node1 ~]# docker pull registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0
  23. [root@k8s-node2 ~]# docker pull registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0
  24. 参数说明:
  25. * --hostname-override 在集群中显示的主机名
  26. * --kubeconfig 指定kubeconfig文件位置,会自动生成
  27. * --bootstrap-kubeconfig 指定刚才生成的bootstrap.kubeconfig文件
  28. * --cert-dir 颁发证书存放位置
  29. * --pod-infra-container-image 管理Pod网络的镜像
  30. 其中/opt/kubernetes/cfg/kubelet.config配置文件如下:
  31. [root@k8s-node1 ~]# vim /opt/kubernetes/cfg/kubelet.config
  32. kind: KubeletConfiguration
  33. apiVersion: kubelet.config.k8s.io/v1beta1
  34. address: 10.0.0.131 #写你机器的ip地址
  35. port: 10250
  36. readOnlyPort: 10255
  37. cgroupDriver: cgroupfs
  38. clusterDNS: ["10.0.0.2"] #不要改,就是这个ip地址
  39. clusterDomain: cluster.local.
  40. failSwapOn: false
  41. authentication:
  42. anonymous:
  43. enabled: true
  44. webhook:
  45. enabled: false
  46. systemd管理kubelet组件:
  47. # vim /usr/lib/systemd/system/kubelet.service
  48. [Unit]
  49. Description=Kubernetes Kubelet
  50. After=docker.service
  51. Requires=docker.service
  52. [Service]
  53. EnvironmentFile=/opt/kubernetes/cfg/kubelet
  54. ExecStart=/opt/kubernetes/bin/kubelet $KUBELET_OPTS
  55. Restart=on-failure
  56. KillMode=process
  57. [Install]
  58. WantedBy=multi-user.target
  59. 启动:
  60. # systemctl daemon-reload
  61. # systemctl enable kubelet
  62. # systemctl start kubelet
  63. [root@k8s-master ~]# /opt/kubernetes/bin/kubectl get csr
  64. NAME AGE REQUESTOR CONDITION
  65. node-csr-F5AQ8SeoyloVrjPuzSbzJnFKQaUsier7EGvNFXLKTqM 17s kubelet-bootstrap Pending
  66. node-csr-bjeHSWXOuUDSHganJPL_hDz_8jjYhM2FQyTkbA9pM0Q 18s kubelet-bootstrap Pending
  67. 在Master审批Node加入集群:
  68. 启动后还没加入到集群中,需要手动允许该节点才可以。在Master节点查看请求签名的Node:
  69. [root@k8s-master ~]# /opt/kubernetes/bin/kubectl certificate approve XXXXID
  70. 注意:xxxid 指的是上面的NAME这一列
  71. [root@k8s-master ~]# /opt/kubernetes/bin/kubectl get csr
  72. NAME AGE REQUESTOR CONDITION
  73. node-csr--1TVDzcozo7NoOD3WS2t9xLQqNunsVXj_i2AQ5x1mbs 1m kubelet-bootstrap Approved,Issued
  74. node-csr-L0wqvr69oy8rzXwFm1u1uNx4aEMOOvd_RWPxaAERn_w 27m kubelet-bootstrap Approved,Issued
  75. 查看集群节点信息:
  76. [root@k8s-master ~]# /opt/kubernetes/bin/kubectl get node
  77. NAME STATUS ROLES AGE VERSION
  78. 10.0.0.131 Ready <none> 1m v1.11.10
  79. 10.0.0.132 Ready <none> 17s v1.11.10

部署kube-proxy组件

  1. 创建kube-proxy配置文件:还是在所有node节点 node1、node2都做
  2. [root@k8s-node1 ~]# vim /opt/kubernetes/cfg/kube-proxy
  3. # cat /opt/kubernetes/cfg/kube-proxy
  4. KUBE_PROXY_OPTS="--logtostderr=true \
  5. --v=4 \
  6. --hostname-override=10.0.0.131 \ #写每个node节点ip
  7. --cluster-cidr=10.0.0.0/24 \ //不要改,就是这个ip
  8. --kubeconfig=/opt/kubernetes/cfg/kube-proxy.kubeconfig"
  9. systemd管理kube-proxy组件:
  10. [root@k8s-node1 ~]# cd /usr/lib/systemd/system
  11. # cat /usr/lib/systemd/system/kube-proxy.service
  12. [Unit]
  13. Description=Kubernetes Proxy
  14. After=network.target
  15. [Service]
  16. EnvironmentFile=-/opt/kubernetes/cfg/kube-proxy
  17. ExecStart=/opt/kubernetes/bin/kube-proxy $KUBE_PROXY_OPTS
  18. Restart=on-failure
  19. [Install]
  20. WantedBy=multi-user.target
  21. 启动:
  22. # systemctl daemon-reload
  23. # systemctl enable kube-proxy
  24. # systemctl start kube-proxy
  25. 在master查看集群状态
  26. [root@k8s-master1 ~]# /opt/kubernetes/bin/kubectl get node
  27. NAME STATUS ROLES AGE VERSION
  28. 10.0.0.131 Ready <none> 19m v1.11.10
  29. 10.0.0.132 Ready <none> 18m v1.11.10
  30. 查看集群状态
  31. [root@k8s-master ~]# /opt/kubernetes/bin/kubectl get cs
  32. NAME STATUS MESSAGE ERROR
  33. scheduler Healthy ok
  34. controller-manager Healthy ok
  35. etcd-0 Healthy {"health": "true"}
  36. etcd-1 Healthy {"health": "true"}
  37. etcd-2 Healthy {"health": "true"}
  38. =====================================================================================

部署dashboard(web ui)

在msater上部署

  1. * dashboard-deployment.yaml #部署Pod,提供Web服务
  2. * dashboard-rbac.yaml #授权访问apiserver获取信息
  3. * dashboard-service.yaml #发布服务,提供对外访问
  4. 创建一个目录
  5. [root@k8s-master ~]# mkdir webui
  6. [root@k8s-master ~]# cd webui/
  7. [root@k8s-master webui]# cat dashboard-deployment.yaml
  8. apiVersion: v1
  9. kind: Deployment
  10. metadata:
  11. name: kubernetes-dashboard
  12. namespace: kube-system
  13. labels:
  14. k8s-app: kubernetes-dashboard
  15. kubernetes.io/cluster-service: "true"
  16. addonmanager.kubernetes.io/mode: Reconcile
  17. spec:
  18. selector:
  19. matchLabels:
  20. k8s-app: kubernetes-dashboard
  21. template:
  22. metadata:
  23. labels:
  24. k8s-app: kubernetes-dashboard
  25. annotations:
  26. scheduler.alpha.kubernetes.io/critical-pod: ''
  27. spec:
  28. serviceAccountName: kubernetes-dashboard
  29. containers:
  30. - name: kubernetes-dashboard
  31. image: registry.cn-hangzhou.aliyuncs.com/kube_containers/kubernetes-dashboard-amd64:v1.8.1
  32. resources:
  33. limits:
  34. cpu: 100m
  35. memory: 300Mi
  36. requests:
  37. cpu: 100m
  38. memory: 100Mi
  39. ports:
  40. - containerPort: 9090
  41. protocol: TCP
  42. livenessProbe:
  43. httpGet:
  44. scheme: HTTP
  45. path: /
  46. port: 9090
  47. initialDelaySeconds: 30
  48. timeoutSeconds: 30
  49. tolerations:
  50. - key: "CriticalAddonsOnly"
  51. operator: "Exists"
  52. [root@k8s-master webui]# cat dashboard-rbac.yaml
  53. apiVersion: v1
  54. kind: ServiceAccount
  55. metadata:
  56. labels:
  57. k8s-app: kubernetes-dashboard
  58. addonmanager.kubernetes.io/mode: Reconcile
  59. name: kubernetes-dashboard
  60. namespace: kube-system
  61. ---
  62. kind: ClusterRoleBinding
  63. apiVersion: rbac.authorization.k8s.io/v1beta1
  64. metadata:
  65. name: kubernetes-dashboard-minimal
  66. namespace: kube-system
  67. labels:
  68. k8s-app: kubernetes-dashboard
  69. addonmanager.kubernetes.io/mode: Reconcile
  70. roleRef:
  71. apiGroup: rbac.authorization.k8s.io
  72. kind: ClusterRole
  73. name: cluster-admin
  74. subjects:
  75. - kind: ServiceAccount
  76. name: kubernetes-dashboard
  77. namespace: kube-system
  78. [root@k8s-master webui]# cat dashboard-service.yaml
  79. apiVersion: v1
  80. kind: Service
  81. metadata:
  82. name: kubernetes-dashboard
  83. namespace: kube-system
  84. labels:
  85. k8s-app: kubernetes-dashboard
  86. kubernetes.io/cluster-service: "true"
  87. addonmanager.kubernetes.io/mode: Reconcile
  88. spec:
  89. type: NodePort
  90. selector:
  91. k8s-app: kubernetes-dashboard
  92. ports:
  93. - port: 80
  94. targetPort: 9090
  95. [root@k8s-master webui]# /opt/kubernetes/bin/kubectl create -f dashboard-rbac.yaml
  96. [root@k8s-master webui]# /opt/kubernetes/bin/kubectl create -f dashboard-deployment.yaml
  97. [root@k8s-master webui]# /opt/kubernetes/bin/kubectl create -f dashboard-service.yaml
  98. 等待数分钟,查看资源状态:
  99. 查看名称空间:
  100. [root@k8s-master webui]# /opt/kubernetes/bin/kubectl get all -n kube-system
  101. NAME READY STATUS RESTARTS AGE
  102. pod/kubernetes-dashboard-d9545b947-442ft 1/1 Running 0 21m
  103. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  104. service/kubernetes-dashboard NodePort 10.0.0.143 <none> 80:47520/TCP 21m
  105. NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
  106. deployment.apps/kubernetes-dashboard 1 1 1 1 21m
  107. NAME DESIRED CURRENT READY AGE
  108. replicaset.apps/kubernetes-dashboard-d9545b947 1 1 1 21m
  109. 查看访问端口:
  110. 查看指定命名空间的服务
  111. [root@k8s-master webui]# /opt/kubernetes/bin/kubectl get svc -n kube-system
  112. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  113. kubernetes-dashboard NodePort 10.0.0.143 <none> 80:47520/TCP 22m

访问node节点的ip

 测试

  1. ==========================================================
  2. 运行一个测试示例--在master节点先安装docker服务
  3. 创建一个Nginx Web,判断集群是否正常工
  4. # /opt/kubernetes/bin/kubectl run nginx --image=daocloud.io/nginx --replicas=3
  5. # /opt/kubernetes/bin/kubectl expose deployment nginx --port=88 --target-port=80 --type=NodePort
  6. # /opt/kub.../bin/kubectl delete -f deployment --all
  7. 在master上面查看:
  8. 查看Pod,Service:
  9. # /opt/kubernetes/bin/kubectl get pods #需要等一会
  10. NAME READY STATUS RESTARTS AGE
  11. nginx-64f497f8fd-fjgt2 1/1 Running 3 28d
  12. nginx-64f497f8fd-gmstq 1/1 Running 3 28d
  13. nginx-64f497f8fd-q6wk9 1/1 Running 3 28d
  14. 查看pod详细信息:
  15. # /opt/kubernetes/bin/kubectl describe pod nginx-64f497f8fd-fjgt2
  16. # /opt/kubernetes/bin/kubectl get svc
  17. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  18. kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 28d
  19. nginx NodePort 10.0.0.175 <none> 88:38696/TCP 28d
  20. 访问nodeip加端口
  21. 打开浏览器输入:http://10.0.0.131:38696
  22. 恭喜你,集群部署成功!
  23. ============================

kubeadm方式部署k8s集群

官方文档:

Installing kubeadm | Kubernetes

切记要关闭防火墙和selinux,cpu核心数至少为2;内存4G

kubeadm部署k8s高可用集群的官方文档:

Creating Highly Available Clusters with kubeadm | Kubernetes

实验环境:

k8s-master:10.0.0.130

k8s-node1:10.0.0.131

k8s-node2:10.0.0.132

全部关闭防火墙selinux

1、获取镜像

在docker hub拉取相应的镜像并重新打标:

注意:所有节点都要进行一下操作

  1. 直接从docker官方拉取镜像可能拉取不成功,所以我们可以从 registry.cn-hangzhou.aliyuncs.com拉取镜像,然后修改镜像tag
  2. 写一个脚本,我部署的是k8s集群1.19.1版本的,其他版本套路是一样的,不过可能会出一些小问题,我也没试过,感兴趣的兄弟可以试一下
  3. [root@k8s-master ~]# vim a.sh
  4. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.19.1
  5. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.19.1
  6. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.19.1
  7. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.19.1
  8. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:1.7.0
  9. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.4.13-0
  10. docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.2
  11. docker pull quay-mirror.qiniu.com/coreos/flannel:v0.12.0-amd64
  12. [root@k8s-master ~]# bash a.sh
  13. ....等待镜像拉取成功,注意:kube-controller-manager、kube-proxy、kube-apiserver、kube-scheduler、的版本必须一致,flannel的版本我用的是0.12.0的,这也是看网上搜的用这个版本,pause的镜像是3.2,也是网上搜的,而coredns:1.7.0、etcd:3.4.13-0是我后来初始化失败的错误回显建议我使用这个版本
  14. [root@k8s-master ~]# docker images
  15. REPOSITORY TAG IMAGE ID CREATED SIZE
  16. registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy v1.19.1 33c60812eab8 21 months ago 118MB
  17. registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver v1.19.1 ce0df89806bb 21 months ago 119MB
  18. registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager v1.19.1 538929063f23 21 months ago 111MB
  19. registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler v1.19.1 49eb8a235d05 21 months ago 45.7MB
  20. registry.cn-hangzhou.aliyuncs.com/google_containers/etcd 3.4.13-0 0369cf4303ff 21 months ago 253MB
  21. registry.cn-hangzhou.aliyuncs.com/google_containers/coredns 1.7.0 bfe3a36ebd25 24 months ago 45.2MB
  22. quay.io/coreos/flannel v0.12.0-amd64 4e9f801d2217 2 years ago 52.8MB
  23. registry.cn-hangzhou.aliyuncs.com/google_containers/pause 3.2 80d28bedfe5d 2 years ago 683kB
  24. 然后一个一个改标签就行
  25. registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.19.1 k8s.gcr.io/kube-proxy:v1.19.1
  26. ....一个一个修改

 改成下面这样的,flannel不用改

 2、所有节点安装docker

  1. # yum install -y yum-utils device-mapper-persistent-data lvm2 git
  2. # yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo #记得关闭证书验证,两个
  3. # yum install docker-ce -y

3、禁用swap分区

所有节点都做

  1. # swapoff -a
  2. 注释掉swap分区:
  3. [root@k8s-master ~]# sed -i 's/.*swap.*/#&/' /etc/fstab
  4. # free -m
  5. total used free shared buff/cache available
  6. Mem: 3935 144 3415 8 375 3518
  7. Swap: 0 0 0

4、所有节点安装kubeadm和kubelet

  1. # cat <<EOF > /etc/yum.repos.d/kubernetes.repo
  2. [kubernetes]
  3. name=Kubernetes
  4. baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
  5. enabled=1
  6. gpgcheck=0
  7. repo_gpgcheck=0
  8. gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
  9. EOF
  10. # yum install -y kubelet-1.19.1-0.x86_64 kubeadm-1.19.1-0.x86_64 kubectl-1.19.1-0.x86_64 ipvsadm
  11. 加载ipvs相关内核模块
  12. # vim c.sh
  13. modprobe ip_vs
  14. modprobe ip_vs_rr
  15. modprobe ip_vs_wrr
  16. modprobe ip_vs_sh
  17. modprobe nf_conntrack_ipv4
  18. modprobe br_netfilter
  19. # bash c.sh
  20. 如果重新开机,需要重新加载(可以写在 /etc/rc.local 中开机自动加载)
  21. # vim /etc/rc.local
  22. modprobe ip_vs
  23. modprobe ip_vs_rr
  24. modprobe ip_vs_wrr
  25. modprobe ip_vs_sh
  26. modprobe nf_conntrack_ipv4
  27. modprobe br_netfilter
  28. # chmod +x /etc/rc.local
  29. 配置转发相关参数,否则可能会出错
  30. # cat <<EOF > /etc/sysctl.d/k8s.conf
  31. net.bridge.bridge-nf-call-ip6tables = 1
  32. net.bridge.bridge-nf-call-iptables = 1
  33. vm.swappiness=0
  34. EOF
  35. 使配置生效
  36. # sysctl --system
  37. 查看是否加载成功
  38. # lsmod | grep ip_vs
  39. ip_vs_sh 12688 0
  40. ip_vs_wrr 12697 0
  41. ip_vs_rr 12600 0
  42. ip_vs 141092 6 ip_vs_rr,ip_vs_sh,ip_vs_wrr
  43. nf_conntrack 133387 2 ip_vs,nf_conntrack_ipv4
  44. libcrc32c 12644 3 xfs,ip_vs,nf_conntrack

5、配置启动kubelet(所有节点)

  1. [root@k8s-master ~]# docker info |grep 'Cgroup Driver' | awk '{print $3}'
  2. WARNING: IPv4 forwarding is disabled
  3. cgroupfs #将这个cgroupfs写到等会的配置文件中
  4. 这个是使用国内的源。-###注意我们使用谷歌的镜像--操作下面的第3标题
  5. 2.配置kubelet的cgroups
  6. # cat >/etc/sysconfig/kubelet<<EOF
  7. KUBELET_EXTRA_ARGS="--cgroup-driver=cgroupfs --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.2"
  8. EOF
  9. 3.配置kubelet的cgroups #我们用的是这个
  10. # cat >/etc/sysconfig/kubelet<<EOF
  11. KUBELET_EXTRA_ARGS="--cgroup-driver=cgroupfs --pod-infra-container-image=k8s.gcr.io/pause:3.2"
  12. EOF
  13. # cat >/etc/sysconfig/kubelet<<EOF
  14. KUBELET_EXTRA_ARGS="--cgroup-driver=cgroupfs --pod-infra-container-image=k8s.gcr.io/pause:3.2"
  15. EOF

启动

  1. # systemctl daemon-reload
  2. # systemctl enable kubelet && systemctl restart kubelet
  3. 在这里使用 # systemctl status kubelet,你会发现报错误信息;
  4. 1011 00:26:43 node1 systemd[1]: kubelet.service: main process exited, code=exited, status=255/n/a
  5. 1011 00:26:43 node1 systemd[1]: Unit kubelet.service entered failed state.
  6. 1011 00:26:43 node1 systemd[1]: kubelet.service failed.
  7. 运行 # journalctl -xefu kubelet 命令查看systemd日志才发现,真正的错误是:
  8. unable to load client CA file /etc/kubernetes/pki/ca.crt: open /etc/kubernetes/pki/ca.crt: no such file or directory
  9. #这个错误在运行kubeadm init 生成CA证书后会被自动解决,此处可先忽略。
  10. #简单地说就是在kubeadm init 之前kubelet会不断重启。说我们暂时忽略就行

6、配置master节点

  1. 运行初始化过程如下:
  2. 初始化之前,切记要关闭防火墙和selinux,cpu核心数至少为2
  3. [root@master ~]# kubeadm init --kubernetes-version=v1.19.1 --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=10.0.0.130 --ignore-preflight-errors=Swap
  4. 注:
  5. –kubernetes-version: 用于指定k8s版本;
  6. –apiserver-advertise-address:用于指定kube-apiserver监听的ip地址,就是 master本机IP地址。
  7. –pod-network-cidr:用于指定Pod的网络范围; 10.244.0.0/16
  8. –service-cidr:用于指定SVC的网络范围;
  9. –image-repository: 指定阿里云镜像仓库地址
  10. 注意在检查一下swap分区是否关闭

看到以下信息说明初始化成功

  1. ......
  2. Your Kubernetes control-plane has initialized successfully!
  3. To start using your cluster, you need to run the following as a regular user:
  4. mkdir -p $HOME/.kube
  5. sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  6. sudo chown $(id -u):$(id -g) $HOME/.kube/config
  7. You should now deploy a pod network to the cluster.
  8. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  9. https://kubernetes.io/docs/concepts/cluster-administration/addons/
  10. Then you can join any number of worker nodes by running the following on each as root:
  11. kubeadm join 10.0.0.130:6443 --token ymvzvj.5d3j0hbeq26wx9ff \
  12. --discovery-token-ca-cert-hash sha256:81086257eed9220e16c7ec407848b51adc1dba67cc4823dedde7396b830dbcc2 #需要记住
  13. 上面记录了完成的初始化输出的内容,根据输出的内容基本上可以看出手动初始化安装一个Kubernetes集群所需要的关键步骤。
  14. 其中有以下关键内容:
  15. [kubelet] 生成kubelet的配置文件”/var/lib/kubelet/config.yaml”
  16. [certificates]生成相关的各种证书
  17. [kubeconfig]生成相关的kubeconfig文件
  18. [bootstraptoken]生成token记录下来,后边使用kubeadm join往集群中添加节点时会用到

配置使用kubectl

以下操作在master节点操作

  1. [root@k8s-master ~]# mkdir -p $HOME/.kube
  2. [root@k8s-master ~]# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  3. [root@k8s-master ~]# chown $(id -u):$(id -g) $HOME/.kube/config
  4. 查看node节点
  5. [root@k8s-master ~]# kubectl get nodes
  6. NAME STATUS ROLES AGE VERSION
  7. k8s-master NotReady master 2m41s v1.19.1

 7、所有node节点操作

  1. 配置node节点加入集群:
  2. 如果报错开启ip转发:
  3. # sysctl -w net.ipv4.ip_forward=1
  4. kubeadm join 10.0.0.130:6443 --token ymvzvj.5d3j0hbeq26wx9ff \
  5. --discovery-token-ca-cert-hash sha256:81086257eed9220e16c7ec407848b51adc1dba67cc4823dedde7396b830dbcc2 #就是刚才让记录的东西

 看到下面内容说明成功了,只要没有error,warnning不用管

8、配置使用网络插件

要让 Kubernetes Cluster 能够工作,必须安装 Pod 网络,否则 Pod 之间无法通信。

Kubernetes 支持多种网络方案,这里我们先使用 flannel,后面还会讨论 Canal。

  1. 在master节点操作
  2. # cd ~ && mkdir flannel && cd flannel
  3. # curl -O https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

这个是国外的网址,所以99%失败,下面是我在网上上找到的文件

  1. vim kube-flannel.yml
  2. ---
  3. apiVersion: policy/v1beta1
  4. kind: PodSecurityPolicy
  5. metadata:
  6. name: psp.flannel.unprivileged
  7. annotations:
  8. seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
  9. seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
  10. apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
  11. apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
  12. spec:
  13. privileged: false
  14. volumes:
  15. - configMap
  16. - secret
  17. - emptyDir
  18. - hostPath
  19. allowedHostPaths:
  20. - pathPrefix: "/etc/cni/net.d"
  21. - pathPrefix: "/etc/kube-flannel"
  22. - pathPrefix: "/run/flannel"
  23. readOnlyRootFilesystem: false
  24. # Users and groups
  25. runAsUser:
  26. rule: RunAsAny
  27. supplementalGroups:
  28. rule: RunAsAny
  29. fsGroup:
  30. rule: RunAsAny
  31. # Privilege Escalation
  32. allowPrivilegeEscalation: false
  33. defaultAllowPrivilegeEscalation: false
  34. # Capabilities
  35. allowedCapabilities: ['NET_ADMIN']
  36. defaultAddCapabilities: []
  37. requiredDropCapabilities: []
  38. # Host namespaces
  39. hostPID: false
  40. hostIPC: false
  41. hostNetwork: true
  42. hostPorts:
  43. - min: 0
  44. max: 65535
  45. # SELinux
  46. seLinux:
  47. # SELinux is unused in CaaSP
  48. rule: 'RunAsAny'
  49. ---
  50. kind: ClusterRole
  51. apiVersion: rbac.authorization.k8s.io/v1beta1
  52. metadata:
  53. name: flannel
  54. rules:
  55. - apiGroups: ['extensions']
  56. resources: ['podsecuritypolicies']
  57. verbs: ['use']
  58. resourceNames: ['psp.flannel.unprivileged']
  59. - apiGroups:
  60. - ""
  61. resources:
  62. - pods
  63. verbs:
  64. - get
  65. - apiGroups:
  66. - ""
  67. resources:
  68. - nodes
  69. verbs:
  70. - list
  71. - watch
  72. - apiGroups:
  73. - ""
  74. resources:
  75. - nodes/status
  76. verbs:
  77. - patch
  78. ---
  79. kind: ClusterRoleBinding
  80. apiVersion: rbac.authorization.k8s.io/v1beta1
  81. metadata:
  82. name: flannel
  83. roleRef:
  84. apiGroup: rbac.authorization.k8s.io
  85. kind: ClusterRole
  86. name: flannel
  87. subjects:
  88. - kind: ServiceAccount
  89. name: flannel
  90. namespace: kube-system
  91. ---
  92. apiVersion: v1
  93. kind: ServiceAccount
  94. metadata:
  95. name: flannel
  96. namespace: kube-system
  97. ---
  98. kind: ConfigMap
  99. apiVersion: v1
  100. metadata:
  101. name: kube-flannel-cfg
  102. namespace: kube-system
  103. labels:
  104. tier: node
  105. app: flannel
  106. data:
  107. cni-conf.json: |
  108. {
  109. "name": "cbr0",
  110. "cniVersion": "0.3.1",
  111. "plugins": [
  112. {
  113. "type": "flannel",
  114. "delegate": {
  115. "hairpinMode": true,
  116. "isDefaultGateway": true
  117. }
  118. },
  119. {
  120. "type": "portmap",
  121. "capabilities": {
  122. "portMappings": true
  123. }
  124. }
  125. ]
  126. }
  127. net-conf.json: |
  128. {
  129. "Network": "10.244.0.0/16",
  130. "Backend": {
  131. "Type": "vxlan"
  132. }
  133. }
  134. ---
  135. apiVersion: apps/v1
  136. kind: DaemonSet
  137. metadata:
  138. name: kube-flannel-ds-amd64
  139. namespace: kube-system
  140. labels:
  141. tier: node
  142. app: flannel
  143. spec:
  144. selector:
  145. matchLabels:
  146. app: flannel
  147. template:
  148. metadata:
  149. labels:
  150. tier: node
  151. app: flannel
  152. spec:
  153. affinity:
  154. nodeAffinity:
  155. requiredDuringSchedulingIgnoredDuringExecution:
  156. nodeSelectorTerms:
  157. - matchExpressions:
  158. - key: beta.kubernetes.io/os
  159. operator: In
  160. values:
  161. - linux
  162. - key: beta.kubernetes.io/arch
  163. operator: In
  164. values:
  165. - amd64
  166. hostNetwork: true
  167. tolerations:
  168. - operator: Exists
  169. effect: NoSchedule
  170. serviceAccountName: flannel
  171. initContainers:
  172. - name: install-cni
  173. image: quay.io/coreos/flannel:v0.12.0-amd64
  174. command:
  175. - cp
  176. args:
  177. - -f
  178. - /etc/kube-flannel/cni-conf.json
  179. - /etc/cni/net.d/10-flannel.conflist
  180. volumeMounts:
  181. - name: cni
  182. mountPath: /etc/cni/net.d
  183. - name: flannel-cfg
  184. mountPath: /etc/kube-flannel/
  185. containers:
  186. - name: kube-flannel
  187. image: quay.io/coreos/flannel:v0.12.0-amd64
  188. command:
  189. - /opt/bin/flanneld
  190. args:
  191. - --ip-masq
  192. - --kube-subnet-mgr
  193. - --iface=ens33
  194. resources:
  195. requests:
  196. cpu: "100m"
  197. memory: "50Mi"
  198. limits:
  199. cpu: "100m"
  200. memory: "50Mi"
  201. securityContext:
  202. privileged: false
  203. capabilities:
  204. add: ["NET_ADMIN"]
  205. env:
  206. - name: POD_NAME
  207. valueFrom:
  208. fieldRef:
  209. fieldPath: metadata.name
  210. - name: POD_NAMESPACE
  211. valueFrom:
  212. fieldRef:
  213. fieldPath: metadata.namespace
  214. volumeMounts:
  215. - name: run
  216. mountPath: /run/flannel
  217. - name: flannel-cfg
  218. mountPath: /etc/kube-flannel/
  219. volumes:
  220. - name: run
  221. hostPath:
  222. path: /run/flannel
  223. - name: cni
  224. hostPath:
  225. path: /etc/cni/net.d
  226. - name: flannel-cfg
  227. configMap:
  228. name: kube-flannel-cfg
  229. ---
  230. apiVersion: apps/v1
  231. kind: DaemonSet
  232. metadata:
  233. name: kube-flannel-ds-arm64
  234. namespace: kube-system
  235. labels:
  236. tier: node
  237. app: flannel
  238. spec:
  239. selector:
  240. matchLabels:
  241. app: flannel
  242. template:
  243. metadata:
  244. labels:
  245. tier: node
  246. app: flannel
  247. spec:
  248. affinity:
  249. nodeAffinity:
  250. requiredDuringSchedulingIgnoredDuringExecution:
  251. nodeSelectorTerms:
  252. - matchExpressions:
  253. - key: beta.kubernetes.io/os
  254. operator: In
  255. values:
  256. - linux
  257. - key: beta.kubernetes.io/arch
  258. operator: In
  259. values:
  260. - arm64
  261. hostNetwork: true
  262. tolerations:
  263. - operator: Exists
  264. effect: NoSchedule
  265. - key: node.kubernetes.io/not-ready
  266. operator: Exists
  267. effect: NoSchedule
  268. serviceAccountName: flannel
  269. initContainers:
  270. - name: install-cni
  271. image: quay.io/coreos/flannel:v0.12.0-arm64
  272. command:
  273. - cp
  274. args:
  275. - -f
  276. - /etc/kube-flannel/cni-conf.json
  277. - /etc/cni/net.d/10-flannel.conflist
  278. volumeMounts:
  279. - name: cni
  280. mountPath: /etc/cni/net.d
  281. - name: flannel-cfg
  282. mountPath: /etc/kube-flannel/
  283. containers:
  284. - name: kube-flannel
  285. image: quay.io/coreos/flannel:v0.12.0-arm64
  286. command:
  287. - /opt/bin/flanneld
  288. args:
  289. - --ip-masq
  290. - --kube-subnet-mgr
  291. resources:
  292. requests:
  293. cpu: "100m"
  294. memory: "50Mi"
  295. limits:
  296. cpu: "100m"
  297. memory: "50Mi"
  298. securityContext:
  299. privileged: false
  300. capabilities:
  301. add: ["NET_ADMIN"]
  302. env:
  303. - name: POD_NAME
  304. valueFrom:
  305. fieldRef:
  306. fieldPath: metadata.name
  307. - name: POD_NAMESPACE
  308. valueFrom:
  309. fieldRef:
  310. fieldPath: metadata.namespace
  311. volumeMounts:
  312. - name: run
  313. mountPath: /run/flannel
  314. - name: flannel-cfg
  315. mountPath: /etc/kube-flannel/
  316. volumes:
  317. - name: run
  318. hostPath:
  319. path: /run/flannel
  320. - name: cni
  321. hostPath:
  322. path: /etc/cni/net.d
  323. - name: flannel-cfg
  324. configMap:
  325. name: kube-flannel-cfg
  326. ---
  327. apiVersion: apps/v1
  328. kind: DaemonSet
  329. metadata:
  330. name: kube-flannel-ds-arm
  331. namespace: kube-system
  332. labels:
  333. tier: node
  334. app: flannel
  335. spec:
  336. selector:
  337. matchLabels:
  338. app: flannel
  339. template:
  340. metadata:
  341. labels:
  342. tier: node
  343. app: flannel
  344. spec:
  345. affinity:
  346. nodeAffinity:
  347. requiredDuringSchedulingIgnoredDuringExecution:
  348. nodeSelectorTerms:
  349. - matchExpressions:
  350. - key: beta.kubernetes.io/os
  351. operator: In
  352. values:
  353. - linux
  354. - key: beta.kubernetes.io/arch
  355. operator: In
  356. values:
  357. - arm
  358. hostNetwork: true
  359. tolerations:
  360. - operator: Exists
  361. effect: NoSchedule
  362. serviceAccountName: flannel
  363. initContainers:
  364. - name: install-cni
  365. image: quay.io/coreos/flannel:v0.12.0-arm
  366. command:
  367. - cp
  368. args:
  369. - -f
  370. - /etc/kube-flannel/cni-conf.json
  371. - /etc/cni/net.d/10-flannel.conflist
  372. volumeMounts:
  373. - name: cni
  374. mountPath: /etc/cni/net.d
  375. - name: flannel-cfg
  376. mountPath: /etc/kube-flannel/
  377. containers:
  378. - name: kube-flannel
  379. image: quay.io/coreos/flannel:v0.12.0-arm
  380. command:
  381. - /opt/bin/flanneld
  382. args:
  383. - --ip-masq
  384. - --kube-subnet-mgr
  385. resources:
  386. requests:
  387. cpu: "100m"
  388. memory: "50Mi"
  389. limits:
  390. cpu: "100m"
  391. memory: "50Mi"
  392. securityContext:
  393. privileged: false
  394. capabilities:
  395. add: ["NET_ADMIN"]
  396. env:
  397. - name: POD_NAME
  398. valueFrom:
  399. fieldRef:
  400. fieldPath: metadata.name
  401. - name: POD_NAMESPACE
  402. valueFrom:
  403. fieldRef:
  404. fieldPath: metadata.namespace
  405. volumeMounts:
  406. - name: run
  407. mountPath: /run/flannel
  408. - name: flannel-cfg
  409. mountPath: /etc/kube-flannel/
  410. volumes:
  411. - name: run
  412. hostPath:
  413. path: /run/flannel
  414. - name: cni
  415. hostPath:
  416. path: /etc/cni/net.d
  417. - name: flannel-cfg
  418. configMap:
  419. name: kube-flannel-cfg
  420. ---
  421. apiVersion: apps/v1
  422. kind: DaemonSet
  423. metadata:
  424. name: kube-flannel-ds-ppc64le
  425. namespace: kube-system
  426. labels:
  427. tier: node
  428. app: flannel
  429. spec:
  430. selector:
  431. matchLabels:
  432. app: flannel
  433. template:
  434. metadata:
  435. labels:
  436. tier: node
  437. app: flannel
  438. spec:
  439. affinity:
  440. nodeAffinity:
  441. requiredDuringSchedulingIgnoredDuringExecution:
  442. nodeSelectorTerms:
  443. - matchExpressions:
  444. - key: beta.kubernetes.io/os
  445. operator: In
  446. values:
  447. - linux
  448. - key: beta.kubernetes.io/arch
  449. operator: In
  450. values:
  451. - ppc64le
  452. hostNetwork: true
  453. tolerations:
  454. - operator: Exists
  455. effect: NoSchedule
  456. serviceAccountName: flannel
  457. initContainers:
  458. - name: install-cni
  459. image: quay.io/coreos/flannel:v0.12.0-ppc64le
  460. command:
  461. - cp
  462. args:
  463. - -f
  464. - /etc/kube-flannel/cni-conf.json
  465. - /etc/cni/net.d/10-flannel.conflist
  466. volumeMounts:
  467. - name: cni
  468. mountPath: /etc/cni/net.d
  469. - name: flannel-cfg
  470. mountPath: /etc/kube-flannel/
  471. containers:
  472. - name: kube-flannel
  473. image: quay.io/coreos/flannel:v0.12.0-ppc64le
  474. command:
  475. - /opt/bin/flanneld
  476. args:
  477. - --ip-masq
  478. - --kube-subnet-mgr
  479. resources:
  480. requests:
  481. cpu: "100m"
  482. memory: "50Mi"
  483. limits:
  484. cpu: "100m"
  485. memory: "50Mi"
  486. securityContext:
  487. privileged: false
  488. capabilities:
  489. add: ["NET_ADMIN"]
  490. env:
  491. - name: POD_NAME
  492. valueFrom:
  493. fieldRef:
  494. fieldPath: metadata.name
  495. - name: POD_NAMESPACE
  496. valueFrom:
  497. fieldRef:
  498. fieldPath: metadata.namespace
  499. volumeMounts:
  500. - name: run
  501. mountPath: /run/flannel
  502. - name: flannel-cfg
  503. mountPath: /etc/kube-flannel/
  504. volumes:
  505. - name: run
  506. hostPath:
  507. path: /run/flannel
  508. - name: cni
  509. hostPath:
  510. path: /etc/cni/net.d
  511. - name: flannel-cfg
  512. configMap:
  513. name: kube-flannel-cfg
  514. ---
  515. apiVersion: apps/v1
  516. kind: DaemonSet
  517. metadata:
  518. name: kube-flannel-ds-s390x
  519. namespace: kube-system
  520. labels:
  521. tier: node
  522. app: flannel
  523. spec:
  524. selector:
  525. matchLabels:
  526. app: flannel
  527. template:
  528. metadata:
  529. labels:
  530. tier: node
  531. app: flannel
  532. spec:
  533. affinity:
  534. nodeAffinity:
  535. requiredDuringSchedulingIgnoredDuringExecution:
  536. nodeSelectorTerms:
  537. - matchExpressions:
  538. - key: beta.kubernetes.io/os
  539. operator: In
  540. values:
  541. - linux
  542. - key: beta.kubernetes.io/arch
  543. operator: In
  544. values:
  545. - s390x
  546. hostNetwork: true
  547. tolerations:
  548. - operator: Exists
  549. effect: NoSchedule
  550. serviceAccountName: flannel
  551. initContainers:
  552. - name: install-cni
  553. image: quay.io/coreos/flannel:v0.12.0-s390x
  554. command:
  555. - cp
  556. args:
  557. - -f
  558. - /etc/kube-flannel/cni-conf.json
  559. - /etc/cni/net.d/10-flannel.conflist
  560. volumeMounts:
  561. - name: cni
  562. mountPath: /etc/cni/net.d
  563. - name: flannel-cfg
  564. mountPath: /etc/kube-flannel/
  565. containers:
  566. - name: kube-flannel
  567. image: quay.io/coreos/flannel:v0.12.0-s390x
  568. command:
  569. - /opt/bin/flanneld
  570. args:
  571. - --ip-masq
  572. - --kube-subnet-mgr
  573. resources:
  574. requests:
  575. cpu: "100m"
  576. memory: "50Mi"
  577. limits:
  578. cpu: "100m"
  579. memory: "50Mi"
  580. securityContext:
  581. privileged: false
  582. capabilities:
  583. add: ["NET_ADMIN"]
  584. env:
  585. - name: POD_NAME
  586. valueFrom:
  587. fieldRef:
  588. fieldPath: metadata.name
  589. - name: POD_NAMESPACE
  590. valueFrom:
  591. fieldRef:
  592. fieldPath: metadata.namespace
  593. volumeMounts:
  594. - name: run
  595. mountPath: /run/flannel
  596. - name: flannel-cfg
  597. mountPath: /etc/kube-flannel/
  598. volumes:
  599. - name: run
  600. hostPath:
  601. path: /run/flannel
  602. - name: cni
  603. hostPath:
  604. path: /etc/cni/net.d
  605. - name: flannel-cfg
  606. configMap:
  607. name: kube-flannel-cfg

 上面的这个镜像,是解决网络问题的flannel镜像,每个节点都要拉取,刚开始的时候我们已经拉取过了

 启动flannel

  1. 启动:
  2. # kubectl apply -f ~/flannel/kube-flannel.yml #启动完成之后需要等待一会
  3. NAME READY STATUS RESTARTS AGE
  4. coredns-5644d7b6d9-sm8hs 1/1 Running 0 9m18s
  5. coredns-5644d7b6d9-vddll 1/1 Running 0 9m18s
  6. etcd-kub-k8s-master 1/1 Running 0 8m14s
  7. kube-apiserver-kub-k8s-master 1/1 Running 0 8m17s
  8. kube-controller-manager-kub-k8s-master 1/1 Running 0 8m20s
  9. kube-flannel-ds-amd64-9wgd8 1/1 Running 0 8m42s
  10. kube-proxy-sgphs 1/1 Running 0 9m18s
  11. kube-scheduler-kub-k8s-master 1/1 Running 0 8m10s
  12. 查看:
  13. # kubectl get pods -n kube-system
  14. # kubectl get service
  15. # kubectl get svc --namespace kube-system
  16. 只有网络插件也安装配置完成之后,才能会显示为ready状态

 在master操作

  1. 各种检测:
  2. 1.查看pods:
  3. [root@k8s-master ~]# kubectl get pods -n kube-system
  4. NAME READY STATUS RESTARTS AGE
  5. coredns-5644d7b6d9-sm8hs 1/1 Running 0 39m
  6. coredns-5644d7b6d9-vddll 1/1 Running 0 39m
  7. etcd-kub-k8s-master 1/1 Running 0 37m
  8. kube-apiserver-kub-k8s-master 1/1 Running 0 38m
  9. kube-controller-manager-kub-k8s-master 1/1 Running 0 38m
  10. kube-flannel-ds-amd64-9wgd8 1/1 Running 0 38m
  11. kube-flannel-ds-amd64-lffc8 1/1 Running 0 2m11s
  12. kube-flannel-ds-amd64-m8kk2 1/1 Running 0 2m2s
  13. kube-proxy-dwq9l 1/1 Running 0 2m2s
  14. kube-proxy-l77lz 1/1 Running 0 2m11s
  15. kube-proxy-sgphs 1/1 Running 0 39m
  16. kube-scheduler-kub-k8s-master 1/1 Running 0 37m
  17. 2.查看异常pod信息:
  18. [root@k8s-master ~]# kubectl describe pods kube-flannel-ds-sr6tq -n kube-system
  19. Name: kube-flannel-ds-sr6tq
  20. Namespace: kube-system
  21. Priority: 0
  22. PriorityClassName: <none>
  23. 。。。。。
  24. Events:
  25. Type Reason Age From Message
  26. ---- ------ ---- ---- -------
  27. Normal Pulling 12m kubelet, node2 pulling image "registry.cn-shanghai.aliyuncs.com/gcr-k8s/flannel:v0.10.0-amd64"
  28. Normal Pulled 11m kubelet, node2 Successfully pulled image "registry.cn-shanghai.aliyuncs.com/gcr-k8s/flannel:v0.10.0-amd64"
  29. Normal Created 11m kubelet, node2 Created container
  30. Normal Started 11m kubelet, node2 Started container
  31. Normal Created 11m (x4 over 11m) kubelet, node2 Created container
  32. Normal Started 11m (x4 over 11m) kubelet, node2 Started container
  33. Normal Pulled 10m (x5 over 11m) kubelet, node2 Container image "registry.cn-shanghai.aliyuncs.com/gcr-k8s/flannel:v0.10.0-amd64" already present on machine
  34. Normal Scheduled 7m15s default-scheduler Successfully assigned kube-system/kube-flannel-ds-sr6tq to node2
  35. Warning BackOff 7m6s (x23 over 11m) kubelet, node2 Back-off restarting failed container
  36. 3.遇到这种情况直接 删除异常pod:
  37. [root@k8s-master ~]# kubectl delete pod kube-flannel-ds-sr6tq -n kube-system
  38. pod "kube-flannel-ds-sr6tq" deleted
  39. 4.查看pods:
  40. [root@k8s-master ~]# kubectl get pods -n kube-system
  41. NAME READY STATUS RESTARTS AGE
  42. coredns-5644d7b6d9-sm8hs 1/1 Running 0 44m
  43. coredns-5644d7b6d9-vddll 1/1 Running 0 44m
  44. etcd-kub-k8s-master 1/1 Running 0 42m
  45. kube-apiserver-kub-k8s-master 1/1 Running 0 43m
  46. kube-controller-manager-kub-k8s-master 1/1 Running 0 43m
  47. kube-flannel-ds-amd64-9wgd8 1/1 Running 0 43m
  48. kube-flannel-ds-amd64-lffc8 1/1 Running 0 7m10s
  49. kube-flannel-ds-amd64-m8kk2 1/1 Running 0 7m1s
  50. kube-proxy-dwq9l 1/1 Running 0 7m1s
  51. kube-proxy-l77lz 1/1 Running 0 7m10s
  52. kube-proxy-sgphs 1/1 Running 0 44m
  53. kube-scheduler-kub-k8s-master 1/1 Running 0 42m
  54. 5.查看节点:
  55. [root@k8s-master ~]# kubectl get nodes
  56. NAME STATUS ROLES AGE VERSION
  57. kub-k8s-master Ready master 43m v1.19.1
  58. kub-k8s-node1 Ready <none> 6m46s v1.19.1
  59. kub-k8s-node2 Ready <none> 6m37s v1.19.1
  60. 到此集群配置完成

错误整理

1、移除node节点的方法

[root@k8s-master ~]# kubectl drain k8s-node2 --delete-local-data --force --ignore-daemonsets
  1. [root@k8s-master ~]# kubectl delete nodes k8s-node2
  2. node "k8s-node2" deleted
  3. [root@k8s-master ~]# kubectl get nodes
  4. NAME STATUS ROLES AGE VERSION
  5. k8s-master Ready master 11m v1.19.1
  6. k8s-node1 Ready <none> 7m39s v1.19.1 #node2节点已经移除

2、添加已删除节点

前提:token未失效

  1. 因为之前的token还有效,我这里并没有超出token的有效期;直接执行加入集群的命令即可;
  2. [root@k8s-node2 ~]# kubeadm join 10.0.0.132:6443 --token jvjxs2.xu92rq4fetgtpy1o --discovery-token-ca-cert-hash sha256:56159a0de43781fd57f1df829de4fe906cf355f4fec8ff7f6f9078c77c8c292d

如果这个时候再想添加进来这个node,需要执行两步操作

第一步:停掉kubelet(需要添加进来的节点操作)

[root@k8s-node2 ~]# systemctl stop kubelet

第二步:删除相关文件

[root@k8s-node2 ~]# rm -rf /etc/kubernetes/*

第三步:添加节点

  1. 因为之前的token还有效,我这里并没有超出token的有效期;直接执行加入集群的命令即可;
  2. [root@k8s-node2 ~]# kubeadm join 10.0.0.132:6443 --token jvjxs2.xu92rq4fetgtpy1o --discovery-token-ca-cert-hash sha256:56159a0de43781fd57f1df829de4fe906cf355f4fec8ff7f6f9078c77c8c292d

第四步:验证查看,看一下是否有k8s-node2

kubectl get nodes

3、忘掉token再次添加进k8s集群

前提:token未失效

第一步:主节点执行命令

在主控节点,获取token:

 第二步: 获取ca证书sha256编码hash值

  1. [root@k8s-master ~]# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
  2. 56159a0de43781fd57f1df829de4fe906cf355f4fec8ff7f6f9078c77c8c292d

第三步:主节点移除node2

  1. [root@k8s-master ~]# kubectl drain k8s-node2 --delete-local-data --force --ignore-daemonsets
  2. [root@k8s-master ~]# kubectl delete nodes k8s-node2
  3. [root@k8s-master ~]# kubectl get node

第四步:从节点执行如下的命令

  1. [root@k8s-node2 ~]# systemctl stop kubelet
  2. [root@k8s-node2 ~]# rm -rf /etc/kubernetes/*

第五步:加入集群

指定主节点IP,端口是6443

在生成的证书前有sha256:

[root@k8s-node2 ~]# kubeadm join 10.0.0.132:6443 --token jvjxs2.xu92rq4fetgtpy1o     --discovery-token-ca-cert-hash sha256:56159a0de43781fd57f1df829de4fe906cf355f4fec8ff7f6f9078c77c8c292d

第六步:主节点查看验证4、加载flannel失败

[root@k8s-master flannel]# kubectl apply -f kube-flannel.yml  

4、加载flannel失败

[root@k8s-master flannel]# kubectl apply -f kube-flannel.yml  

 报错:

解决方法:

  1. [root@k8s-master flannel]# kubectl delete -f kube-flannel.yml
  2. [root@k8s-master flannel]# vim kube-flannel.yml #进行版本的修改

  1. 重新创建即可
  2. [root@k8s-master flannel]# kubectl apply -f kube-flannel.yml
  3. 查看文件中,指定的api对象,是否都创建成功
  4. [root@k8s-master flannel]# kubectl get pod -n kube-system

 5、部署flannel网络后pod及容器无法跨主机互通问题

  1. 方法一:重启docker,
  2. 方法二:卸载flannel网络

 方法二:

在master卸载flannel

kubectl delete -f kube-flannel.yml

在node节点清理flannel网络残留文件

  1. ifconfig cni0 down
  2. ip link delete cni0
  3. ifconfig flannel.1 down
  4. ip link delete flannel.1
  5. rm -rf /var/lib/cni/
  6. rm -f /etc/cni/net.d/*

 重新部署flannel网络

  1. [root@k8s-master01 flannel]# kubectl create -f kube-flannel.yml
  2. [root@k8s-master01 flannel]# kubectl get pod -n kube-system
  3. NAME READY STATUS RESTARTS AGE
  4. coredns-5c98db65d4-8bpdd 1/1 Running 0 17s
  5. coredns-5c98db65d4-knfcj 1/1 Running 0 43s
  6. etcd-k8s-master01 1/1 Running 2 10d
  7. kube-apiserver-k8s-master01 1/1 Running 2 10d
  8. kube-controller-manager-k8s-master01 1/1 Running 3 10d
  9. kube-flannel-ds-amd64-56hsf 1/1 Running 0 25m
  10. kube-flannel-ds-amd64-56t49 1/1 Running 0 25m
  11. kube-flannel-ds-amd64-qz42z 1/1 Running 0 25m
  12. kube-proxy-5fn9m 1/1 Running 1 10d
  13. kube-proxy-6hjvp 1/1 Running 2 10d
  14. kube-proxy-t47n9 1/1 Running 2 10d
  15. kube-scheduler-k8s-master01 1/1 Running 4 10d
  16. kubernetes-dashboard-7d75c474bb-4r7hc 1/1 Running 0 23m
  17. [root@k8s-master01 flannel]#

 flannel网络显示正常, 容器之间可以跨主机互通!

其他错误

  1. 错误
  2. 问题1:服务器时间不一致会报错
  3. 查看服务器时间
  4. =====================================
  5. 问题2:kubeadm init不成功,发现如下提示,然后超时报错
  6. [wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
  7. 查看kubelet状态发现如下错误,主机master找不到和镜像下载失败,发现pause镜像是从aliyuncs下载的,其实我已经下载好了官方的pause镜像,按着提示的镜像名称重新给pause镜像打个ali的tag,最后重置kubeadm的环境重新初始化,错误解决
  8. [root@master manifests]# systemctl status kubelet -l
  9. ● kubelet.service - kubelet: The Kubernetes Node Agent
  10. Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: disabled)
  11. Drop-In: /etc/systemd/system/kubelet.service.d
  12. └─10-kubeadm.conf
  13. Active: active (running) since 四 2019-01-31 15:20:32 CST; 5min ago
  14. Docs: https://kubernetes.io/docs/
  15. Main PID: 23908 (kubelet)
  16. Tasks: 19
  17. Memory: 30.8M
  18. CGroup: /system.slice/kubelet.service
  19. └─23908 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --cgroup-driver=cgroupfs --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.1 --cgroup-driver=cgroupfs --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.1
  20. 131 15:25:41 master kubelet[23908]: E0131 15:25:41.432357 23908 kubelet.go:2266] node "master" not found
  21. 131 15:25:41 master kubelet[23908]: E0131 15:25:41.532928 23908 kubelet.go:2266] node "master" not found
  22. 131 15:25:41 master kubelet[23908]: E0131 15:25:41.633192 23908 kubelet.go:2266] node "master" not found
  23. 131 15:25:41 master kubelet[23908]: I0131 15:25:41.729296 23908 kubelet_node_status.go:278] Setting node annotation to enable volume controller attach/detach
  24. 131 15:25:41 master kubelet[23908]: E0131 15:25:41.733396 23908 kubelet.go:2266] node "master" not found
  25. 131 15:25:41 master kubelet[23908]: E0131 15:25:41.740110 23908 remote_runtime.go:96] RunPodSandbox from runtime service failed: rpc error: code = Unknown desc = failed pulling image "registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.1": Error response from daemon: Get https://registry.cn-hangzhou.aliyuncs.com/v2/: dial tcp 0.0.0.80:443: connect: invalid argument
  26. 131 15:25:41 master kubelet[23908]: E0131 15:25:41.740153 23908 kuberuntime_sandbox.go:68] CreatePodSandbox for pod "kube-controller-manager-master_kube-system(e8f43404e60ae844e375d50b1e39d91e)" failed: rpc error: code = Unknown desc = failed pulling image "registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.1": Error response from daemon: Get https://registry.cn-hangzhou.aliyuncs.com/v2/: dial tcp 0.0.0.80:443: connect: invalid argument
  27. 131 15:25:41 master kubelet[23908]: E0131 15:25:41.740166 23908 kuberuntime_manager.go:662] createPodSandbox for pod "kube-controller-manager-master_kube-system(e8f43404e60ae844e375d50b1e39d91e)" failed: rpc error: code = Unknown desc = failed pulling image "registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.1": Error response from daemon: Get https://registry.cn-hangzhou.aliyuncs.com/v2/: dial tcp 0.0.0.80:443: connect: invalid argument
  28. 131 15:25:41 master kubelet[23908]: E0131 15:25:41.740207 23908 pod_workers.go:190] Error syncing pod e8f43404e60ae844e375d50b1e39d91e ("kube-controller-manager-master_kube-system(e8f43404e60ae844e375d50b1e39d91e)"), skipping: failed to "CreatePodSandbox" for "kube-controller-manager-master_kube-system(e8f43404e60ae844e375d50b1e39d91e)" with CreatePodSandboxError: "CreatePodSandbox for pod \"kube-controller-manager-master_kube-system(e8f43404e60ae844e375d50b1e39d91e)\" failed: rpc error: code = Unknown desc = failed pulling image \"registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.1\": Error response from daemon: Get https://registry.cn-hangzhou.aliyuncs.com/v2/: dial tcp 0.0.0.80:443: connect: invalid argument"
  29. 131 15:25:41 master kubelet[23908]: E0131 15:25:41.833981 23908 kubelet.go:2266] node "master" not found

解决方法

  1. 重置kubeadm环境
  2. 整个集群所有节点(包括master)重置/移除节点
  3. 1.驱离k8s-node-1节点上的pod(master上)
  4. [root@k8s-master ~]# kubectl drain kub-k8s-node1 --delete-local-data --force --ignore-daemonsets
  5. 2.删除节点(master上)
  6. [root@k8s-master ~]# kubectl delete node kub-k8s-node1
  7. 3.重置节点(node上-也就是在被删除的节点上)
  8. [root@k8s-node1 ~]# kubeadm reset
  9. 1:需要把master也驱离、删除、重置,这里给我坑死了,第一次没有驱离和删除master,最后的结果是查看结果一切正常,但coredns死活不能用,搞了整整1天,切勿尝试
  10. 2:master上在reset之后需要删除如下文件
  11. # rm -rf /var/lib/cni/ $HOME/.kube/config
  12. ###注意:如果整个k8s集群都做完了,需要重置按照上面步骤操作。如果是在初始化出错只需要操作第三步

五、部署Harbor仓库

实验需要安装docker-compose,需要下载1.10.10版本的Harbor包(这个需要自己去网上找,还可能需要翻..你懂得),因为实验环境考虑到配置问题,我们就把Harbor仓库安装在我们上个实验的master节点上。

  1. 上传两个软件包
  2. docker-compose-linux-2.2.3-x86_64
  3. harbor-offline-installer-v1.10.10.tgz
  4. [root@k8s-master ~]# mv docker-compose-linux-2.2.3-x86_64 /usr/local/bin/docker-compose
  5. [root@k8s-master ~]# chmod +x /usr/local/bin/docker-compose
  6. [root@k8s-master ~]# docker-compose -v
  7. [root@k8s-master ~]# tar zxf harbor-offline-installer-v1.10.10.tgz
  8. [root@k8s-master ~]# mkdir /data/cert -p #创建目录是为了制作假证书,配置https访问Harbor仓库
  9. [root@k8s-master ~]# cd /data/cert/
  10. [root@k8s-master cert]# openssl genrsa -out /data/cert/server.key 2048
  11. [root@k8s-master cert]# openssl req -x509 -new -nodes -key /data/cert/server.key -subj "/CN=10.0.0.130" -days 3650 -out /data/cert/server.crt
  12. [root@k8s-master cert]# ls
  13. server.crt server.key
  14. [root@k8s-master cert]# cd /root/harbor/
  15. [root@k8s-master harbor]# vim harbor.yml
  16. hostname: 10.0.0.130 #修改为本机的ip地址
  17. # http related config
  18. http:
  19. # port for http, default is 80. If https enabled, this port will redirect to https port
  20. port: 80
  21. # https related config
  22. https:
  23. # https port for harbor, default is 443
  24. port: 443
  25. # The path of cert and key files for nginx
  26. certificate: /data/cert/server.crt #这是刚才生成的两个证书的绝对路径
  27. private_key: /data/cert/server.key
  28. .......其他的我们不用动

访问https://10.0.0.130

 创建用户

创建私有仓库

 项目授权

 在两个node节点做一下操作

  1. [root@k8s-node1 ~]# vim /etc/docker/daemon.json
  2. {
  3. "insecure-registries": ["10.0.0.130"]
  4. }
  5. [root@k8s-node1 ~]# systemctl restart docker

拉取测试(刚才创建的是私有仓库,所以拉取的时候需要登录有权限的用户,没有权限的话会失败)

  1. [root@k8s-node1 ~]# docker login 10.0.0.130 --username='soso' --password='***'
  2. [root@k8s-node1 ~]# docker pull daocloud.io/library/nginx
  3. 3.查看
  4. [root@k8s-node1 ~]# docker images
  5. REPOSITORY TAG IMAGE ID CREATED SIZE
  6. daocloud.io/library/nginx latest 98ebf73aba75 3 months ago 109MB
  7. 4.打个tag
  8. [root@k8s-node1 ~]# docker tag daocloud.io/library/nginx:latest 192.168.246.166/jenkins/nginx
  9. 5.上传到仓库
  10. [root@k8s-node1 ~]# docker push 192.168.246.166/jenkins/nginx
  11. The push refers to repository [192.168.246.166/jenkins/nginx]
  12. 589561a3ffb4: Pushed
  13. ef7dbb0cfc81: Pushed
  14. d56055da3352: Pushed
  15. latest: digest: sha256:f83b2ffd963ac911f9e638184c8d580cc1f3139d5c8c33c87c3fb90aebdebf76 size: 948

在web界面中查看镜像是否上传

 拉取测试

来到node2

[root@k8s-node2 ~]# docker login 10.0.0.130 --username='soso' --password='***'

[root@k8s-node2 ~]# docker pull 10.0.0.130/jenkins/nginx

六、集群基本操作--查看集群信息

  1. 1、查看集群信息
  2. [root@k8s-master ~]# kubectl get nodes
  3. NAME STATUS ROLES AGE VERSION
  4. k8s-master Ready master 29h v1.19.1
  5. k8s-node1 Ready <none> 28h v1.19.1
  6. k8s-node2 Ready <none> 28h v1.19.1
  7. 2、删除节点(无效显示的也可以删除)
  8. [root@k8s-master ~]# kubectl delete node kub-k8s-node1
  9. 3、查看某一个节点(节点名称可以用空格隔开写多个)
  10. [root@k8s-master ~]# kubectl get node kub-k8s-node1
  11. NAME STATUS ROLES AGE VERSION
  12. k8s-node1 Ready <none> 15h v1.19.1

使用 kubectl describe 命令,查看一个 API 对象的细节:

注意:Events(事件) 值得你特别关注

在 Kubernetes 执行的过程中,对 API 对象的所有重要操作,都会被记录在这个对象的 Events 里,并且显示在 kubectl describe 指令返回的结果中。

比如,对于这个 Pod,我们可以看到它被创建之后,被调度器调度(Successfully assigned)到了 node-1,拉取了指定的镜像(pulling image),然后启动了 Pod 里定义的容器(Started container)。

这个部分正是我们将来进行 Debug 的重要依据。如果有异常发生,一定要第一时间查看这些 Events,往往可以看到非常详细的错误信息。

查看node的详细信息

  1. [root@k8s-master ~]# kubectl describe node k8s-node1
  2. Name: k8s-node1
  3. Roles: <none>
  4. Labels: beta.kubernetes.io/arch=amd64
  5. beta.kubernetes.io/os=linux
  6. kubernetes.io/arch=amd64
  7. kubernetes.io/hostname=k8s-node1
  8. kubernetes.io/os=linux
  9. ....
  10. Events: <none>
  11. #注意:最后被查看的节点名称只能用get nodes里面查到的name!

查看各组件信息

  1. 查看service的信息
  2. [root@k8s-master ~]# kubectl get service
  3. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  4. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 29h
  5. 在不同的namespace里面查看service
  6. [root@k8s-master ~]# kubectl get service -n kube-system
  7. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  8. kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 29h
  9. -n:namespace名称空间 默认是dafault
  10. 查看所有名称空间内的资源:
  11. [root@k8s-master ~]# kubectl get pods --all-namespaces
  12. 同时查看所有命名空间内的资源:
  13. [root@k8s-master ~]# kubectl get pod,svc -n kube-system
  14. 查看主节点:
  15. [root@k8s-master ~]# kubectl cluster-info
  16. api查询:
  17. [root@k8s-master ~]# kubectl api-versions

创建命名空间

  1. 编写yaml文件
  2. [root@k8s-master ~]# mkdir prome
  3. [root@k8s-master ~]# cd prome/
  4. [root@k8s-master prome]# vim namespace.yaml
  5. ---
  6. apiVersion: v1 #api版本
  7. kind: Namespace #类型---固定的
  8. metadata: #元数据
  9. name: zhaosi #起个名字
  10. labels:
  11. name: zhaosi
  12. 2. 创建资源
  13. [root@k8s-master prome]# kubectl apply -f namespace.yml
  14. namespace/zhaosi created
  15. 3. 查看资源
  16. [root@k8s-master prome]# kubectl get namespace
  17. NAME STATUS AGE
  18. default Active 22h
  19. kube-node-lease Active 22h
  20. kube-public Active 22h
  21. kube-system Active 22h
  22. zhaosi Active 34s
  23. 4.查看某一个namespace
  24. [root@k8s-master prome]# kubectl get namespace zhaosi
  25. 5.查看某个namespace的详细信息
  26. [root@k8s-master prome]# kubectl describe namespace zhaosi
  27. 6.删除名称空间
  28. [root@k8s-master prome]# kubectl delete -f namespace.yml
  29. namespace "ns-monitor" deleted
  30. 或者
  31. [root@k8s-master prome]# kubectl delete namespace zhaosi
  32. namespace "ns-monitor" deleted

七、发布第一个容器化应用

1、有镜像

2、部署应用。--考虑做不做副本不做副本就是pod,做副本以deployment方式去创建。做了副本访问还需要做一个service,使用访问。

发布第一个容器化应用

扮演一个应用开发者的角色,使用这个 Kubernetes 集群发布第一个容器化应用。

1、 作为一个应用开发者,你首先要做的,是制作容器的镜像。
2、 有了容器镜像之后,需要按照 Kubernetes 项目的规范和要求,将你的镜像组织为它能够"认识"的方式,然后提交上去。

什么才是 Kubernetes 项目能"认识"的方式?

就是使用 Kubernetes 的必备技能:编写配置文件。
这些配置文件可以是 YAML 或者 JSON 格式的。

Kubernetes 跟 Docker 等很多项目最大的不同,就在于它不推荐你使用命令行的方式直接运行容器(虽然 Kubernetes 项目也支持这种方式,比如:kubectl run),而是希望你用 YAML 文件的方式,即:把容器的定义、参数、配置,统统记录在一个 YAML 文件中,然后用这样一句指令把它运行起来:

# kubectl create/apply -f 我的配置文件

好处:你会有一个文件能记录下 Kubernetes 到底"run"了什么,方便自身以后查看记录

使用yaml创建pod

YAML文件,对应到k8s中,就是一个API Object(API 对象)。当你为这个对象的各个字段填好值并提交给k8s之后,k8s就会负责创建出这些对象所定义的容器或者其他类型的API资源。

编写yaml文件内容如下:

  1. [root@k8s-master prome]# vim pod.yml
  2. ---
  3. apiVersion: v1 #api版本,支持pod的版本
  4. kind: Pod #Pod,定义类型注意语法开头大写
  5. metadata: #元数据
  6. name: nginx #这是Pod的名字
  7. labels:
  8. app: nginx #自定义,不能数字开头,不能特殊符号开头
  9. spec: #指定的意思
  10. containers: #定义容器
  11. - name: nginx #容器的名字,自定义
  12. image: daocloud.io/library/nginx:latest #镜像,本地用的话,用本地,没有的话会拉取
  13. ports:
  14. - containerPort: 80 #容器暴露的端口

创建Pod

  1. [root@k8s-master prome]# kubectl apply -f pod.yml
  2. pod/nginx created

查看Pod

  1. [root@k8s-master prome]# kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. nginx 1/1 Running 0 74s
  4. =============================================================================
  5. 各字段含义:
  6. NAME: Pod的名称
  7. READY: Pod的准备状况,右边的数字表示Pod包含的容器总数目,左边的数字表示准备就绪的容器数目
  8. STATUS: Pod的状态
  9. RESTARTS: Pod的重启次数
  10. AGE: Pod的运行时间

pod的准备状况指的是Pod是否准备就绪以接收请求,Pod的准备状况取决于容器,即所有容器都准备就绪了,Pod才准备就绪。这时候kubernetes的代理服务才会添加Pod作为后端,而一旦Pod的准备状况变为false(至少一个容器的准备状况为false),kubernetes会将Pod从代理服务的分发后端移除,即不会分发请求给该Pod。

一个pod刚被创建的时候是不会被调度的,因为没有任何节点被选择用来运行这个pod。调度的过程发生在创建完成之后,但是这个过程一般很快,所以你通常看不到pod是处于unscheduler状态的除非创建的过程遇到了问题。

pod被调度之后,分配到指定的节点上运行,这时候,如果该节点没有所需要的image,那么将会自动从默认的Docker Hub上pull指定的image,一切就绪之后,看到pod是处于running状态了

查看pod运行中哪台机器上

  1. [root@k8s-master prome]# kubectl get pods -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  3. nginx 1/1 Running 0 3s 10.244.1.4 k8s-node1 <none> <none>
  4. 可以测试访问:
  5. [root@k8s-master prome]# curl 10.244.1.4 #访问pod的ip

 查看Pod定义的详细信息

  1. 查看pod的详细信息----指定pod名字
  2. [root@k8s-master prome]# kubectl get pod nginx -o yaml
  3. -o:output
  4. yaml:yaml格式也可以是json格式

查看kubectl describe支持查询Pod的状态和生命周期事件

  1. [root@k8s-master prome]# kubectl describe pod nginx
  2. Name: nginx
  3. Namespace: default
  4. Priority: 0
  5. Node: k8s-node1/10.0.0.131
  6. Start Time: Thu, 09 Jun 2022 21:10:08 +0800
  7. Labels: app=nginx
  8. ...
  1. 1.各字段含义:
  2. Name: Pod的名称
  3. Namespace: Pod的Namespace。
  4. Image(s): Pod使用的镜像
  5. Node: Pod所在的Node。
  6. Start Time: Pod的起始时间
  7. Labels: Pod的Label。
  8. Status: Pod的状态。
  9. Reason: Pod处于当前状态的原因。
  10. Message: Pod处于当前状态的信息。
  11. IP: Pod的PodIP
  12. Replication Controllers: Pod对应的Replication Controller。
  13. ===============================
  14. 2.Containers:Pod中容器的信息
  15. Container ID: 容器的ID
  16. Image: 容器的镜像
  17. Image ID:镜像的ID
  18. State: 容器的状态
  19. Ready: 容器的准备状况(true表示准备就绪)。
  20. Restart Count: 容器的重启次数统计
  21. Environment Variables: 容器的环境变量
  22. Conditions: Pod的条件,包含Pod准备状况(true表示准备就绪)
  23. Volumes: Pod的数据卷
  24. Events: 与Pod相关的事件列表
  25. =====
  26. 生命周期:指的是status通过# kubectl get pod
  27. 生命周期包括:running、Pending、completed、

生命周期介绍

  1. Pending:此状态表示Pod 的 YAML 文件已经提交给了 Kubernetes,API 对象已经被创建并保存在 Etcd 当中(准备状态)。但这个 Pod 里有些容器因为某种原因而不能被顺利创建。比如,调度不成功。
  2. Running:此状态表示Pod 已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个正在运行中。
  3. Succeeded:此状态表示 Pod 里的所有容器都正常运行完毕,并且已经退出了。这种情况在运行一次性任务时最为常见。
  4. Failed:此状态表示 Pod 里至少有一个容器以不正常的状态(非 0 的返回码)退出。这个状态的出现,意味着你得想办法 Debug 这个容器的应用,比如查看 Pod 的 Events 和日志。
  5. Unknown:这是一个异常状态(未知状态),表示 Pod 的状态不能持续地被 kubelet 汇报给 kube-apiserver这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题
  6. 其他状态
  7. CrashLoopBackOff: 容器退出,kubelet正在将它重启
  8. InvalidImageName: 无法解析镜像名称
  9. ImageInspectError: 无法校验镜像
  10. ErrImageNeverPull: 策略禁止拉取镜像
  11. ImagePullBackOff: 正在重试拉取
  12. RegistryUnavailable: 连接不到镜像中心
  13. ErrImagePull: 通用的拉取镜像出错
  14. CreateContainerConfigError: 不能创建kubelet使用的容器配置
  15. CreateContainerError: 创建容器失败
  16. m.internalLifecycle.PreStartContainer 执行hook报错
  17. RunContainerError: 启动容器失败
  18. PostStartHookError: 执行hook报错
  19. ContainersNotInitialized: 容器没有初始化完毕
  20. ContainersNotReady: 容器没有准备完毕
  21. ContainerCreating:容器创建中
  22. PodInitializing:pod 初始化中
  23. DockerDaemonNotReady:docker还没有完全启动
  24. NetworkPluginNotReady: 网络插件还没有完全启动

进入Pod容器内部

  1. 通过pod名称
  2. [root@k8s-master prome]# kubectl exec -it nginx /bin/bash
  3. root@nginx:/#

删除Pod

  1. [root@k8s-master prome]# kubectl delete pod pod名1 pod名2 //单个或多个删除
  2. [root@k8s-master prome]# kubectl delete pod --all //批量删除
  3. 举例:
  4. [root@k8s-master prome]# kubectl delete pod nginx
  5. pod "nginx" deleted
  6. [root@k8s-master prome]# kubectl delete -f pod.yaml
  7. pod "nginx" deleted

创建Pod

  1. [root@k8s-master prome]# kubectl apply -f pod.yaml #指定创建pod的yml文件名
  2. [root@k8s-master prome]# kubectl apply -f pod.yaml --validate 想看报错信息,加上--validate参数

重新启动基于yaml文件的应用(这里并不是重新启动服务)

  1. # kubectl delete -f XXX.yaml #删除
  2. # kubectl apply -f XXX.yaml #创建
  1. create与apply的区别:
  2. create创建的应用如果需要修改yml文件,必须先指定yml文件删除,在创建新的pod。
  3. 如果是apply创建的应用可以直接修改yml文件,继续apply创建,不用先删掉。

八、YAML文件语法解析

除了某些强制性的命令,如:kubectl run或者expose等,k8s还允许通过配置文件的方式来创建这些操作对象。

通常,使用配置文件的方式会比直接使用命令行更可取,因为这些文件可以进行版本控制,而且文件的变化和内容也可以进行审核,当使用及其复杂的配置来提供一个稳健、可靠和易维护的系统时,这些点就显得非常重要。

在声明定义配置文件的时候,所有的配置文件都存储在YAML或者JSON格式的文件中并且遵循k8s的资源配置方式。

YAML是专门用来写配置文件的语言,非常简洁和强大,使用比json更方便。它实质上是一种通用的数据串行化格式。

kubernetes中用来定义YAML文件创建Pod和创建Deployment等资源。

  1. 使用YAML用于K8s的定义的好处:
  2. 便捷性:不必添加大量的参数到命令行中执行命令
  3. 可维护性:YAML文件可以通过源头控制,跟踪每次操作
  4. 灵活性:YAML可以创建比命令行更加复杂的结构
  1. YAML语法规则:
  2. 1. 大小写敏感/区分大小写
  3. 2. 使用缩进表示层级关系
  4. 3. 缩进时不允许使用Tab键,只允许使用空格
  5. 4. 缩进的空格数不重要,只要相同层级的元素左侧对齐即可
  6. 5. " 表示注释,从这个字符一直到行尾,都会被解析器忽略
  1. 在 k8s 中,只需要知道两种结构类型:
  2. 1.Lists
  3. 2.Maps
  1. 字典
  2. a={key:value, key1:{key2:{value2}}, key3:{key4:[1,{key5:value5},3,4,5]}}
  1. key: value
  2. key1:
  3. key2: value2
  4. key3:
  5. key4:
  6. - 1
  7. - key5: value5
  8. - 3
  9. - 4
  10. - 5
  11. YAML Maps
  12. Map指的是字典,即一个Key:Value 的键值对信息。
  13. 例如:
  14. ---
  15. apiVersion: v1
  16. kind: Pod
  17. 注:--- 为可选的分隔符 ,当需要在一个文件中定义多个结构的时候需要使用。上述内容表示有两个键apiVersion和kind,分别对应的值为v1和Pod。
  18. Maps的value既能够对应字符串也能够对应一个Maps。
  19. 例如:
  20. ---
  21. apiVersion: v1
  22. kind: Pod
  23. metadata:
  24. name: kube100-site
  25. labels:
  26. app: web
  27. {apiVersion:v1,kind:Pod,Metadata:{name:kube100-site,labels:{app:web}}}
  28. 注:上述的YAML文件中,metadata这个KEY对应的值为一个Maps,而嵌套的labels这个KEY的值又是一个Map。实际使用中可视情况进行多层嵌套。
  29. YAML处理器根据行缩进来知道内容之间的关联。上述例子中,使用两个空格作为缩进,但空格的数据量并不重要,只是至少要求一个空格并且所有缩进保持一致的空格数 。例如,name和labels是相同缩进级别,因此YAML处理器知道他们属于同一map;它知道app是lables的值因为app的缩进更大。
  30. 注意:在YAML文件中绝对不要使用tab键
  1. YAML Lists
  2. List即列表,就是数组
  3. 例如:
  4. args:
  5. - beijing
  6. - shanghai
  7. - shenzhen
  8. - guangzhou
  9. 可以指定任何数量的项在列表中,每个项的定义以连字符(-)开头,并且与父元素之间存在缩进。
  10. 在JSON格式中,表示如下:
  11. {
  12. "args": ["beijing", "shanghai", "shenzhen", "guangzhou"]
  13. }

九、Pod API属性详解

Pod API 对象

Pod是 k8s 项目中的最小编排单位。将这个设计落实到 API 对象上,容器(Container)就成了 Pod 属性里一个普通的字段。

问题:通过yaml文件创建pod的时候里面有容器,这个文件里面到底哪些属性属于 Pod 对象,哪些属性属于 Container?

解决:

Pod 扮演的是传统环境里"虚拟机"的角色。是为了使用户从传统环境(虚拟机环境)向 k8s(容器环境)的迁移,更加平滑。

把 Pod 看成传统环境里的"机器"、把容器看作是运行在这个"机器"里的"用户程序",那么很多关于 Pod 对象的设计就非常容易理解了。

凡是调度、网络、存储,以及安全相关的属性,基本上是 Pod 级别的

共同特征是,它们描述的是"机器"这个整体,而不是里面运行的"程序"。

  1. 比如:
  2. 配置这个"机器"的网卡(即:Pod 的网络定义)
  3. 配置这个"机器"的磁盘(即:Pod 的存储定义)
  4. 配置这个"机器"的防火墙(即:Pod 的安全定义)
  5. 这台"机器"运行在哪个服务器之上(即:Pod 的调度)
  1. kind:指定了这个 API 对象的类型(Type),是一个 Pod,根据实际情况,此处资源类型可以是Deployment、Job、Ingress、Service等。
  2. metadata:包含Pod的一些meta信息,比如名称、namespace、标签等信息.
  3. spec:specification of the resource content 指定该资源的内容,包括一些container,storage,volume以及其他Kubernetes需要的参数,以及诸如是否在容器失败时重新启动容器的属性。可在特定Kubernetes API找到完整的Kubernetes Pod的属性。
  4. specification----->[spesɪfɪˈkeɪʃn]

容器可选的设置属性:

除了上述的基本属性外,还能够指定复杂的属性,包括容器启动运行的命令、使用的参数、工作目录以及每次实例化是否拉取新的副本。 还可以指定更深入的信息,例如容器的退出日志的位置。

容器可选的设置属性包括:

"name""image""command""args""workingDir""ports""env"、resource、"volumeMounts"、livenessProbe、readinessProbe、livecycle、terminationMessagePath、"imagePullPolicy"、securityContext、stdin、stdinOnce、tty

跟"机器"相关的配置

  1. [root@k8s-master ~]# cd prome/
  2. [root@k8s-master prome]# kubectl get pods
  3. NAME READY STATUS RESTARTS AGE
  4. nginx 1/1 Running 0 2d23h
  5. [root@k8s-master prome]# kubectl get pod -o wide #查看pod运行在哪台机器上面

1、Pod调度

  1. nodeSelector
  2. nodeName
  3. 这两个属性的功能是一样的都是用于人工干预调度器

1、指定node节点的名称(nodeName)

  1. 将node1上面的pod删除掉
  2. [root@k8s-master prome]# kubectl delete -f pod.yml
  3. pod "website" deleted
  4. ===========================================
  5. nodeName:是一个供用户将 Pod 与 Node 进行绑定的字段,用法:
  6. 现在指定将pod创在node2上面:
  7. [root@k8s-master prome]# vim pod.yml
  8. ---
  9. apiVersion: v1
  10. kind: Pod
  11. metadata:
  12. name: nginx
  13. labels:
  14. app: nginx
  15. spec:
  16. containers:
  17. - name: nginx
  18. image: daocloud.io/library/nginx:latest
  19. ports:
  20. - containerPort: 80
  21. nodeName: kub-k8s-node2 #指定node节点的名称
  22. 创建
  23. [root@k8s-master prome]# kubectl apply -f pod.yml
  24. pod/nginx created
  25. [root@k8s-master prome]# kubectl get pod -o wide #验证是不是再node2节点上

NodeName:一旦 Pod 的这个字段被赋值,k8s就会被认为这个 Pod 已经经过了调度,调度的结果就是赋值的节点名字。这个字段一般由调度器负责设置,用户也可以设置它来"骗过"调度器,这个做法一般是在测试或者调试的时候才会用到。

2、指定node标签(nodeSelector)

  1. 1.查看node2上面的标签
  2. [root@k8s-master prome]# kubectl describe node kub-k8s-node2

  1. 1.重新创建一个新的pod
  2. "nodeSelector:是一个供用户将 Pod 与 Node 进行绑定的字段",,通过指定标签来指定
  3. [root@k8s-master prome]# vim tomcat.yml
  4. ---
  5. apiVersion: v1
  6. kind: Pod
  7. metadata:
  8. name: tomcat
  9. labels:
  10. app: tomcat
  11. spec:
  12. containers:
  13. - name: tomcat
  14. image: daocloud.io/library/tomcat:8
  15. ports:
  16. - containerPort: 8080
  17. nodeSelector: #指定标签
  18. kubernetes.io/hostname: k8s-node2
  19. 2.创建pod
  20. [root@k8s-master prome]# kubectl apply -f tomcat.yml
  21. pod/tomcat created

 注:表示这个 Pod 永远只能运行在携带了"kubernetes.io/hostname: kub-k8s-node2"标签(Label)的节点上;否则,它将调度失败。

2、域名解析

设置pod容器里面的hosts文件内容,也是做本地解析

HostAliases:定义 Pod 的 hosts 文件(比如 /etc/hosts)里的内容,用法:

  1. 1.首先先将刚创建的pod删除掉
  2. [root@k8s-master prome]# kubectl delete -f tomcat.yml
  3. pod "tomcat" deleted
  4. [root@k8s-master prome]# vim tomcat.yml
  5. ---
  6. apiVersion: v1
  7. kind: Pod
  8. metadata:
  9. name: tomcat
  10. labels:
  11. app: tomcat
  12. spec:
  13. hostAliases:
  14. - ip: "10.0.0.131" #给哪个ip做解析。实验环境下这个ip自定义的
  15. hostnames:
  16. - "foo.remote" #解析的名字。用引号引起来可以写多个
  17. - "bar.remote"
  18. containers:
  19. - name: tomcat
  20. image: daocloud.io/library/tomcat:8
  21. ports:
  22. - containerPort: 8080
  23. 2.创建pod
  24. [root@k8s-master prome]# kubectl apply -f tomcat.yml
  25. pod/tomcat created
  26. 3.连接pod
  27. [root@k8s-master prome]# kubectl exec -it tomcat /bin/bash
  28. root@tomcat:/usr/local/tomcat# cat /etc/hosts #查看hosts文件

注意:

  • 在 k8s 中,如果要设置 hosts 文件里的内容,一定要通过这种方法。否则,如果直接修改了 hosts 文件,在 Pod 被删除重建之后,kubelet 会自动覆盖掉被修改的内容。

3、进程共享

凡是跟容器的 Linux Namespace 相关的属性,也一定是 Pod 级别的

原因:Pod 的设计,就是要让它里面的容器尽可能多地共享 Linux Namespace,仅保留必要的隔离和限制能力。这样,Pod 模拟出的效果,就跟虚拟机里程序间的关系非常类似了。

举例,一个 Pod 定义 yaml 文件如下:

  1. [root@k8s-master prome]# kubectl delete -f pod.yml
  2. pod "nginx" deleted
  3. [root@k8s-master prome]# vim pod.yml #修改如下。最好是提前将镜像pull下来。
  4. ---
  5. apiVersion: v1
  6. kind: Pod
  7. metadata:
  8. name: nginx
  9. labels:
  10. app: nginx
  11. spec:
  12. shareProcessNamespace: true #共享进程名称空间
  13. containers:
  14. - name: nginx
  15. image: daocloud.io/library/nginx:latest
  16. ports:
  17. - containerPort: 80
  18. - name: busybos
  19. image: daocloud.io/library/busybox
  20. stdin: true
  21. tty: true
  22. 2.创建
  23. [root@k8s-master prome]# kubectl apply -f pod.yml
  24. pod/nginx created
  1. 1. 定义了 shareProcessNamespace=true
  2. 表示这个 Pod 里的容器要共享进程(PID Namespace)如果是false则为不共享。
  3. 2. 定义了两个容器:
  4. 一个 nginx 容器
  5. 一个开启了 tty 和 stdin 的 busybos 容器
  6. 在 Pod 的 YAML 文件里声明开启它们俩,等同于设置了 docker run 里的 -it(-i 即 stdin,-t 即 tty)参数。
  7. 可以直接认为 tty 就是 Linux 给用户提供的一个常驻小程序,用于接收用户的标准输入,返回操作系统的标准输出。为了能够在 tty 中输入信息,需要同时开启 stdin(标准输入流)。
  8. 此 Pod 被创建后,就可以使用 shell 容器的 tty 跟这个容器进行交互了。

我们登录node1的机器连接busybox的容器

1571583535653

 在容器里不仅可以看到它本身的 ps 指令,还可以看到 nginx 容器的进程,以及 Infra 容器的 /pause 进程。也就是说整个 Pod 里的每个容器的进程,对于所有容器来说都是可见的:它们共享了同一个 PID Namespace。

  1. [root@k8s-master prome]# kubectl delete -f pod.yml
  2. [root@k8s-master prome]# vim pod.yml
  3. 将shareProcessNamespace=true修改为false
  4. [root@k8s-master prome]# kubectl apply -f pod.yml
  5. pod/nginx created

 凡是 Pod 中的容器要共享宿主机的 Namespace,也一定是 Pod 级别的定义。

  1. 刚才的都是pod里面容器的Namespace,并没有和本机的Namespace做共享,接下来我们可以做与本机的Namespace共享,可以在容器里面看到本机的进程。
  2. [root@k8s-master prome]# kubectl delete -f pod.yml
  3. pod "nginx" deleted
  4. [root@k8s-master prome]# vim pod.yml #修改如下
  5. ---
  6. apiVersion: v1
  7. kind: Pod
  8. metadata:
  9. name: nginx
  10. labels:
  11. app: nginx
  12. spec:
  13. hostNetwork: true #共享宿主机网络
  14. hostIPC: true #共享ipc通信
  15. hostPID: true #共享宿主机的pid
  16. containers:
  17. - name: nginx
  18. image: daocloud.io/library/nginx:latest
  19. ports:
  20. - containerPort: 80
  21. - name: busybos
  22. image: daocloud.io/library/busybox
  23. stdin: true
  24. tty: true
  25. 创建pod
  26. [root@k8s-master prome]# kubectl apply -f pod.yml
  27. pod/nginx created

 定义了共享宿主机的 Network、IPC 和 PID Namespace。这样,此 Pod 里的所有容器,会直接使用宿主机的网络、直接与宿主机进行 IPC 通信、看到宿主机里正在运行的所有进程。

注意:hostPID和shareProcessNamespace不能同时存在

十、容器属性

1、Pod 里最重要的字段"Containers":

"Containers""Init Containers"这两个字段都属于 Pod 对容器的定义,内容也完全相同,只是 Init Containers 的生命周期,会先于所有的 Containers,并且严格按照定义的顺序执行.

2、k8s 对 Container 的定义,和 Docker 相比并没有什么太大区别。

Docker中Image(镜像)、Command(启动命令)、workingDir(容器的工作目录)、Ports(容器要开发的端口),以及 volumeMounts(容器要挂载的 Volume)都是构成 k8s 中 Container 的主要字段。

3、ImagePullPolicy属性

ImagePullPolicy :定义镜像的拉取策略。之所以是一个 Container 级别的属性,是因为容器镜像本来就是 Container 定义中的一部分。

策略作用
Never只使用本地image
Always每次都下载镜像
IfNotPresent优先使用本地image,本地没有再去下载

 默认值:Always:表示每次创建pod都重新拉取一次镜像

  • 镜像存在而且已经是最新版本就不在拉取镜像
  • 如果不存在就下载镜像
  • 如果镜像存在但不是最新版本也会下载镜像

避免:不用latest,每次下载镜像直接指定版本。注:但是有bug,当镜像类似于nginx或者nginx:latest这样的名字时,ImagePullPolicy也会被认为Always。

4、Pod的生命周期

1、简介

Lifecycle:定义Container Lifecycle Hooks。作用是在容器状态发生变化时触发一系列“钩子”。

Lifecycle有两种回调函数:

  • PostStart:容器创建成功后,运行前的任务,用于资源部署,环境准备等。
  • PreStop:在容器被停止前的任务,用于优雅关闭应用程序,通知其他系统等等。

PostStart:在容器启动前,立刻执行一个指定的操作。

注意:PostStart定义的操作,虽然是在Docker容器ENTRYPOINT执行结束后,但它并不严格保证顺序,也就是说,在PostStart启动时,ENTRYPOINT有可能还没有结束。如果PostStart执行超时或者错误,k8s会在该Pod的Events中报出该容器启动失败的错误信息,导致Pod也处于失败的状态。

PreStop:是在容器被杀死之前(比如,收到了SIGKILL信号)。

注意:PreStop操作的执行,是同步的。所以它会阻塞当前的容器杀死流程,直到这个Hook定义操作完成之后,才允许容器被杀死,这跟PostStart不一样。

2、生命周期

Pod 生命周期的变化,主要体现在 Pod API 对象的Status 部分,这是除了 Metadata 和 Spec 之外的第三个重要字段。其中,pod.status.phase,就是 Pod 的当前状态,有如下几种可能的情况:

  1. Pod 生命周期的变化,主要体现在 Pod API 对象的Status 部分,这是除了 Metadata 和 Spec 之外的第三个重要字段。其中,pod.status.phase,就是 Pod 的当前状态,有如下几种可能的情况:
  2. Pending:此状态表示Pod 的 YAML 文件已经提交给了 Kubernetes,API 对象已经被创建并保存在 Etcd 当中(准备状态)。但这个 Pod 里有些容器因为某种原因而不能被顺利创建。比如,调度不成功。
  3. Running:此状态表示Pod 已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个正在运行中。
  4. Succeeded:此状态表示 Pod 里的所有容器都正常运行完毕,并且已经退出了。这种情况在运行一次性任务时最为常见。
  5. Failed:此状态表示 Pod 里至少有一个容器以不正常的状态(非 0 的返回码)退出。这个状态的出现,意味着你得想办法 Debug 这个容器的应用,比如查看 Pod 的 Events 和日志。
  6. Unknown:这是一个异常状态(未知状态),表示 Pod 的状态不能持续地被 kubelet 汇报给 kube-apiserver
  7. 这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题

3、案例

这是k8s官方文档的一个Pod YAML文件

在这个例子中,容器成功启动之后,在/usr/share/message里写了一句“欢迎信息”(即PostStart定义的操作)。而在这个容器被删除之前,我们则先调用了nginx的退出指令(即PreStop定义的操作),从而实现了容器的“优雅退出”。

  1. [root@k8s-master prome]# kubectl delete -f pod.yml
  2. pod "website" deleted
  3. [root@k8s-master prome]# cp pod.yml pod.yml.bak
  4. [root@k8s-master prome]# vim pod.yml
  5. ---
  6. apiVersion: v1
  7. kind: Pod
  8. metadata:
  9. name: lifecycle-demo
  10. spec:
  11. containers:
  12. - name: lifecycle-demo-container
  13. image: daocloud.io/library/nginx
  14. lifecycle:
  15. postStart: #容器启动之后
  16. exec:
  17. command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
  18. preStop: #容器关闭之前
  19. exec:
  20. command: ["/usr/sbin/nginx","-s","quit"]
  21. [root@k8s-master prome]# kubectl get pod -o wide

  1. [root@k8s-node1 ~]# docker exec -it 3d404e658 /bin/bash
  2. root@lifecycle-demo:~# cat /usr/share/message
  3. Hello from the postStart handler

 4、扩展

Pod 对象的 Status 字段,还可以再细分出一组 Conditions:

这些细分状态的值包括:

  • PodScheduled
  • Ready
  • Initialized
  • Unschedulable

它们主要用于描述造成当前 Status 的具体原因是什么。

  • 比如, Pod 当前的 Status 是 Pending,对应的 Condition 是 Unschedulable,这表示它的调度出现了问题。

  • 比如, Ready 这个细分状态表示 Pod 不仅已经正常启动(Running 状态),而且已经可以对外提供服务了。这两者之间(Running 和 Ready)是有区别的,仔细思考一下。

Pod 的这些状态信息,是判断应用运行情况的重要标准,尤其是 Pod 进入了非"Running"状态后,一定要能迅速做出反应,根据它所代表的异常情况开始跟踪和定位,而不是去手忙脚乱地查阅文档。

十一、Projected Volume

注:Projected Volume 是 Kubernetes v1.11 之后的新特性

1、什么是Projected Volume?

在 k8s 中,有几种特殊的 Volume,它们的意义不是为了存放容器里的数据,也不是用来进行容器和宿主机之间的数据交换。

"而是为容器提供预先定义好的数据。"

从容器的角度来看,这些 Volume 里的信息仿佛是被 k8s "投射"(Project)进入容器当中的。

k8s 支持的 Projected Volume 一共有四种:

  • Secret
  • ConfigMap
  • Downward API
  • ServiceAccount Token

2、Secret详解

secret用来保存小片敏感数据的k8s资源,例如密码,token,或者秘钥。这类数据当然也可以存放在Pod或者镜像中,但是放在Secret中是为了更方便的控制如何使用数据,并减少暴露的风险。
 
 用户可以创建自己的secret,系统也会有自己的secret。

 Pod需要先引用才能使用某个secret

Pod有2种方式来使用secret:

1. 作为volume的一个域被一个或多个容器挂载

2. 在拉取镜像的时候被kubelet引用。

内建的secret:由ServiceAccount创建的API证书附加的秘钥k8s自动生成的用来访问apiserver的Secret,所有Pod会默认使用这个Secret与apiserver通信

创建自己的Secret:

  • 方式1:使用kubectl create secret命令
  • 方式2:yaml文件创建Secret

1、命令方式创建secret

假如某个Pod要访问数据库,需要用户名密码,分别存放在2个文件中:username.txt,password.txt 

  1. [root@k8s-master ~]# echo -n 'admin' > ./username.txt
  2. [root@k8s-master ~]# echo -n '1f2d1e2e67df' > ./password.txt

kubectl create secret指令将用户名密码写到secret中,并在apiserver创建Secret

  1. [root@k8s-master ~]# kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
  2. secret/db-user-pass created

查看创建结果:

  1. [root@k8s-master ~]# kubectl get secrets
  2. NAME TYPE DATA AGE
  3. db-user-pass Opaque 2 54s
  4. default-token-6svwp kubernetes.io/service-account-token 3 4d11h
  5. 注: opaque:英[əʊˈpeɪk] 美[oʊˈpeɪk] 模糊

查看详细信息:

  1. [root@k8s-master ~]# kubectl describe secret db-user-pass
  2. Name: db-user-pass
  3. Namespace: default
  4. Labels: <none>
  5. Annotations: <none>
  6. Type: Opaque
  7. Data
  8. ====
  9. password.txt: 12 bytes
  10. username.txt: 5 bytes

get或describe指令都不会展示secret的实际内容,这是出于对数据的保护的考虑,如果想查看实际内容使用命令:

[root@k8s-master ~]# kubectl get secret db-user-pass -o json

base64解码:

  1. [root@k8s-master ~]# echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
  2. 1f2d1e2e67df

2、yaml方式创建Secret:

  1. 创建一个secret.yaml文件,内容用base64编码:明文显示容易被别人发现,这里先转码。
  2. [root@k8s-master ~]# echo -n 'admin' | base64
  3. YWRtaW4=
  4. [root@k8s-master ~]# echo -n '1f2d1e2e67df' | base64
  5. MWYyZDFlMmU2N2Rm

创建一个secret.yaml文件,内容用base64编码

  1. [root@k8s-master prome]# vim secret.yml
  2. ---
  3. apiVersion: v1
  4. kind: Secret
  5. metadata:
  6. name: mysecret
  7. type: Opaque #模糊
  8. data:
  9. username: YWRtaW4=
  10. password: MWYyZDFlMmU2N2Rm

创建

  1. [root@k8s-master prome]# kubectl apply -f secret.yml
  2. secret/mysecret created

解析Secret中内容,还是经过编码的---需要解码

  1. [root@k8s-master ~]# kubectl get secrets
  2. NAME TYPE DATA AGE
  3. default-token-7vc82 kubernetes.io/service-account-token 3 30h
  4. mysecret Opaque 2 6s
  5. [root@k8s-master prome]# kubectl get secret mysecret -o yaml
  6. apiVersion: v1
  7. data:
  8. password: MWYyZDFlMmU2N2Rm
  9. username: YWRtaW4=
  10. kind: Secret
  11. metadata:
  12. creationTimestamp: "2019-10-21T03:07:56Z"
  13. name: mysecret
  14. namespace: default
  15. resourceVersion: "162855"
  16. selfLink: /api/v1/namespaces/default/secrets/mysecret
  17. uid: 36bcd07d-92eb-4755-ac0a-a5843ed986dd
  18. type: Opaque

3、使用secret

一个Pod中引用Secret的例子:

  1. [root@k8s-master prome]# vim pod_use_secret.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: mypod
  6. spec:
  7. containers:
  8. - name: testredis
  9. image: daocloud.io/library/redis
  10. volumeMounts: #挂载一个卷
  11. - name: foo #这个名字需要与定义的卷的名字一致
  12. mountPath: "/etc/foo" #挂载到容器里哪个目录下,随便写
  13. readOnly: true
  14. volumes: #数据卷的定义
  15. - name: foo #卷的名字这个名字自定义
  16. secret: #卷是直接使用的secret。
  17. secretName: mysecret #调用刚才定义的secret
  18. 创建:
  19. [root@k8s-master prome]# kubectl apply -f pod_use_secret.yaml
  20. pod/mypod created
  21. [root@k8s-master prome]# kubectl exec -it mypod /bin/bash
  22. root@mypod:/data# cd /etc/foo/
  23. root@mypod:/etc/foo# ls
  24. password username
  25. root@mypod:/etc/foo# cat password
  26. 1f2d1e2e67df
  • 每一个被引用的Secret都要在spec.volumes中定义。
  • 如果Pod中的多个容器都要引用这个Secret那么每一个容器定义中都要指定自己的volumeMounts,但是Pod定义中声明一次spec.volumes就好了。

4、映射secret key到指定的路径

  1. [root@k8s-master prome]# kubectl delete -f pod_use_secret.yaml
  2. pod "mypod" deleted
  3. [root@k8s-master prome]# vim pod_use_secret.yaml
  4. ---
  5. apiVersion: v1
  6. kind: Pod
  7. metadata:
  8. name: mypod
  9. spec:
  10. containers:
  11. - name: testredis
  12. image: daocloud.io/library/redis
  13. volumeMounts:
  14. - name: foo
  15. mountPath: "/etc/foo"
  16. readOnly: true
  17. volumes:
  18. - name: foo
  19. secret:
  20. secretName: mysecret
  21. items: #定义一个items
  22. - key: username #将那个key重新定义到那个目录下
  23. path: username #相对路径,相对于/etc/foo的路径
  24. 2.创建
  25. [root@k8s-master prome]# kubectl apply -f pod_use_secret.yaml
  26. pod/mypod created
  27. 3.从volume中读取secret的值
  28. [root@k8s-master prome]# kubectl exec -it mypod /bin/bash
  29. root@mypod:/data# cd /etc/foo/
  30. root@mypod:/etc/foo# ls
  31. username
  32. root@mypod:/etc/foo# cat username
  33. admin

username被映射到了/etc/foo目录下。

5、被挂载的secret内容自动更新

也就是如果修改一个Secret的内容,那么挂载了该Secret的容器中也将会取到更新后的值,但是这个时间间隔是由kubelet的同步时间决定的。

  1. 1.设置base64加密
  2. [root@k8s-master prome]# echo qianfeng | base64
  3. cWlhbmZlbmcK
  4. 2.将admin替换成qianfeng
  5. [root@k8s-master prome]# vim secret.yml
  6. ---
  7. apiVersion: v1
  8. kind: Secret
  9. metadata:
  10. name: mysecret
  11. type: Opaque
  12. data:
  13. username: cWlhbmZlbmcK #修改为qianfeng的base64加密后的
  14. password: MWYyZDFlMmU2N2Rm
  15. 1.创建
  16. [root@k8s-master prome]# kubectl apply -f secret.yml
  17. Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
  18. secret/mysecret configured
  19. 2.连接pod容器
  20. [root@k8s-master prome]# kubectl exec -it mypod /bin/bash
  21. root@mypod:/data# cd /etc/foo/
  22. root@mypod:/etc/foo# ls
  23. username
  24. root@mypod:/etc/foo# cat username
  25. qianfeng

6、以环境变量的形式使用Secret

  1. [root@k8s-master prome]# kubectl delete -f pod_use_secret.yaml
  2. pod "mypod" deleted
  3. [root@k8s-master prome]# vim pod_use_secret.yaml
  4. ---
  5. apiVersion: v1
  6. kind: Pod
  7. metadata:
  8. name: mypod
  9. spec:
  10. containers:
  11. - name: testredis
  12. image: daocloud.io/library/redis
  13. env: #定义环境变量
  14. - name: SECRET_USERNAME #创建新的环境变量名称
  15. valueFrom:
  16. secretKeyRef: #调用的key是什么
  17. name: mysecret #变量的值来自于mysecret
  18. key: username #username里面的值
  19. 2.创建使用secret的pod容器
  20. [root@k8s-master prome]# kubectl apply -f pod_use_secret.yaml
  21. pod/mypod created
  22. 3.连接
  23. [root@k8s-master prome]# kubectl exec -it mypod /bin/bash
  24. root@mypod:/data# echo $SECRET_USERNAME #打印一下定义的变量
  25. qianfeng

注:以环境变量的形式使用secret,secret中的内容更新后,挂载该secret的容器中不会取到更新后的值

7、实验:

1. 通过编写 YAML 文件的方式来创建这个 Secret 对象

Secret 对象要求这些数据必须是经过 Base64 转码的,以免出现明文密码的安全隐患。

转码操作:

  1. [root@k8s-master ~]# echo -n 'admin' | base64
  2. YWRtaW4=
  3. [root@k8s-master ~]# echo -n '1f2d1e2e67df' | base64
  4. MWYyZDFlMmU2N2Rm

 注意:像这样创建的 Secret 对象,它里面的内容仅仅是经过了转码,并没有被加密。生产环境中,需要在 Kubernetes 中开启 Secret 的加密插件,增强数据的安全性。

  1. [root@k8s-master prome]# vim create_secret.yml
  2. ---
  3. apiVersion: v1
  4. kind: Secret
  5. metadata:
  6. name: mysecret-01
  7. type: Opaque
  8. data:
  9. user: YWRtaW4=
  10. pass: MWYyZDFlMmU2N2Rm
  11. [root@k8s-master prome]# kubectl apply -f create_secret.yml
  12. secret/mysecret-01 created
  13. [root@k8s-master prome]# kubectl get secret

 用yaml方式创建的secret调用方法如下:

  1. [root@k8s-master prome]# vim test-projected-volume.yaml
  2. ---
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: test-projected-volume
  7. spec:
  8. containers:
  9. - name: test-secret-volume
  10. image: daocloud.io/library/nginx
  11. volumeMounts:
  12. - name: mysql-cred
  13. mountPath: "/projected-volume"
  14. readOnly: true
  15. volumes:
  16. - name: mysql-cred
  17. secret:
  18. secretName: mysecret-01
  19. [root@k8s-master prome]# kubectl apply -f test-projected-volume.yaml
  20. pod/test-projected-volume1 created

 验证这些 Secret 对象是不是已经在容器里了:

  1. [root@k8s-master prome]# kubectl exec -it test-projected-volume /bin/bash
  2. root@test-projected-volume:/# ls
  3. bin dev home lib64 mnt proc root sbin sys usr
  4. boot etc lib media opt projected-volume run srv tmp var
  5. root@test-projected-volume:/# ls projected-volume/
  6. pass user
  7. root@test-projected-volume:/# cat projected-volume/pass
  8. 1f2d1e2e67df
  9. root@test-projected-volume:/# cat projected-volume/user
  10. admin
  11. root@test-projected-volume:/#

 注意:

  1. 如果报错:上面这条命令会报错如下
  2. # kubectl exec -it test-projected-volume /bin/sh
  3. error: unable to upgrade connection: Forbidden (user=system:anonymous, verb=create, resource=nodes, subresource=proxy)
  4. 解决:绑定一个cluster-admin的权限
  5. # kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous
  6. clusterrolebinding.rbac.authorization.k8s.io/system:anonymous created

 结果中看到,保存在 Etcd 里的用户名和密码信息,已经以文件的形式出现在了容器的 Volume 目录里。而这个文件的名字,就是 kubectl create secret 指定的 Key,或者说是 Secret 对象的 data 字段指定的 Key。

3、ConfigMap详解

ConfigMap 与 Secret 类似,用来存储配置文件的kubernetes资源对象,所有的配置内容都存储在etcd中。

与 Secret 的区别:

  • ConfigMap 保存的是不需要加密的、应用所需的配置信息。
  • ConfigMap 的用法几乎与 Secret 完全相同:可以使用 kubectl create configmap 从文件或者目录创建 ConfigMap,也可以直接编写 ConfigMap 对象的 YAML 文件。

1、创建ConfigMap

创建ConfigMap的方式有4种:

方式1:通过直接在命令行中指定configmap参数创建,即--from-literal

方式2:通过指定文件创建,即将一个配置文件创建为一个ConfigMap,--from-file=<文件>

方式3:通过指定目录创建,即将一个目录下的所有配置文件创建为一个ConfigMap,--from-file=<目录>

方式4:事先写好标准的configmap的yaml文件,然后kubectl create -f 创建

(其实方式二和方式三等同于一种方法)

2、通过命令行参数--from-literal创建

创建命令

  1. [root@k8s-master prome]# kubectl create configmap test-configmap --from-literal=user=admin --from-literal=pass=1122334
  2. configmap/test-configmap created
  3. 验证
  4. [root@k8s-master prome]# kubectl get configmap test-configmap -o yaml
  5. apiVersion: v1
  6. data:
  7. pass: "1122334"
  8. user: admin
  9. kind: ConfigMap
  10. metadata:
  11. creationTimestamp: "2019-10-21T07:48:15Z"
  12. name: test-configmap
  13. namespace: default
  14. resourceVersion: "187590"
  15. selfLink: /api/v1/namespaces/default/configmaps/test-configmap
  16. uid: 62a8a0d0-fab9-4159-86f4-a06aa213f4b1

 3、通过指定文件创建

  1. [root@k8s-master prome]# yum -y install nginx
  2. [root@k8s-master prome]# nginx -v
  3. nginx version: nginx/1.22.0
  4. [root@k8s-master prome]# kubectl create configmap test-config2 --from-file=/etc/nginx/nginx.conf
  5. configmap/test-config2 created

结果如下面data内容所示:

 通过指定文件创建时,configmap会创建一个key/value对,key是文件名,value是文件内容。

4、指定目录创建

  1. configmap 目录下的config-1和config-2内容如下所示:
  2. [root@k8s-master ~]# mkdir configmap &&cd configmap
  3. [root@k8s-master config]# vim config
  4. aaa
  5. bbb
  6. c=d
  7. [root@k8s-master config]# vim config2
  8. eee
  9. fff
  10. h=k

 创建:

  1. [root@k8s-master prome]# kubectl create configmap test-config3 --from-file=/root/configmap
  2. configmap/test-config3 created

 指定目录创建时,configmap内容中的各个文件会创建一个key/value对,key是文件名,value是文件内容。

5、通过事先写好configmap的标准yaml文件创建

yaml文件内容如下: 注意其中一个key的value有多行内容时的写法

  1. [root@k8s-master prome]# vim configmap.yaml
  2. ---
  3. apiVersion: v1
  4. kind: ConfigMap
  5. metadata:
  6. name: test-config4
  7. namespace: default
  8. data:
  9. cache_host: memcached-gcxt
  10. cache_port: "11211"
  11. cache_prefix: gcxt
  12. my.cnf: |
  13. [mysqld]
  14. log-bin = mysql-bin
  15. haha = hehe
  16. [root@k8s-master prome]# kubectl apply -f configmap.yaml
  17. configmap/test-config4 created

 查看configmap的详细信息:

[root@k8s-master prome]# kubectl describe configmap

6、使用ConfigMap的方式

使用ConfigMap有三种方式:

  • 一种是通过环境变量的方式,直接传递pod
  • 另一种是通过在pod的命令行下运行的方式
  • 第三种是使用volume的方式挂载入到pod内

示例ConfigMap文件:

  1. [root@k8s-master prome]# vim config-map.yaml
  2. ---
  3. apiVersion: v1
  4. kind: ConfigMap
  5. metadata:
  6. name: config-map
  7. namespace: default
  8. data:
  9. home: jiaxian
  10. hobby: Black silk
  11. [root@k8s-master prome]# kubectl apply -f config-map.yml
  12. configmap/config-map created

7、通过环境变量使用

  1. [root@k8s-master prome]# vim testpod.yml
  2. ---
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: nginx
  7. spec:
  8. containers:
  9. - name: nginx
  10. image: daocloud.io/library/nginx:latest
  11. env:
  12. - name: FROM
  13. valueFrom:
  14. configMapKeyRef:
  15. name: config-map
  16. key: hobby
  17. - name: from
  18. valueFrom:
  19. configMapKeyRef:
  20. name: config-map
  21. key: home
  22. restartPolicy: Never
  23. 创建pod
  24. [root@k8s-master prome]# kubectl apply -f testpod.yml
  25. pod/nginx created

测试

  1. [root@k8s-master prome]# kubectl exec -it nginx /bin/bash
  2. root@nginx:/# echo $FROM
  3. Black silk

8、通过envFrom、configMapRef、name使得configmap中的所有key/value对儿  都自动变成环境变量:

  1. [root@k8s-master prome]# kubectl delete -f testpod.yml
  2. pod "dapi-test-pod" deleted
  3. [root@k8s-master prome]# cp testpod.yml testpod.yml.bak
  4. [root@k8s-master prome]# vim testpod.yml
  5. ---
  6. apiVersion: v1
  7. kind: Pod
  8. metadata:
  9. name: nginx
  10. spec:
  11. containers:
  12. - name: nginx
  13. image: daocloud.io/library/nginx:latest
  14. envFrom:
  15. - configMapRef:
  16. name: config-map
  17. restartPolicy: Never

这样容器里的变量名称直接使用configMap里的key名:

  1. [root@k8s-master prome]# kubectl apply -f testpod.yml
  2. pod/nginx created.
  3. [root@k8s-master prome]# kubectl exec -it nginx /bin/bash
  4. root@nginx:/# env
  5. HOSTNAME=dapi-test-pod
  6. NJS_VERSION=0.3.3
  7. NGINX_VERSION=1.17.1
  8. KUBERNETES_PORT_443_TCP_PROTO=tcp
  9. KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
  10. PKG_RELEASE=1~stretch
  11. KUBERNETES_PORT=tcp://10.96.0.1:443
  12. PWD=/
  13. home=jiaxian
  14. HOME=/root
  15. KUBERNETES_SERVICE_PORT_HTTPS=443
  16. KUBERNETES_PORT_443_TCP_PORT=443
  17. KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
  18. TERM=xterm
  19. SHLVL=1
  20. KUBERNETES_SERVICE_PORT=443
  21. PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
  22. hobby=Black silk
  23. KUBERNETES_SERVICE_HOST=10.96.0.1
  24. _=/usr/bin/env

9、作为volume挂载使用 

把test-config2中的所有key/value挂载进来(就是刚才那个--from-file=/etc/nginx/nginx.conf)

  1. [root@k8s-master prome]# kubectl delete -f testpod.yml
  2. pod "nginx" deleted
  3. [root@k8s-master prome]# vim testpod.yml
  4. ---
  5. apiVersion: v1
  6. kind: Pod
  7. metadata:
  8. name: nginx
  9. spec:
  10. containers:
  11. - name: nginx
  12. image: daocloud.io/library/nginx
  13. volumeMounts:
  14. - name: nginx
  15. mountPath: /etc/nginx/nginx.conf
  16. subPath: nginx.conf
  17. volumes:
  18. - name: nginx
  19. configMap:
  20. name: test-config2
  21. [root@k8s-master prome]# kubectl apply -f testpod.yml
  22. pod/nginx created

我们修改一下test-config2中的nginx.conf的内容做验证

[root@k8s-master prome]# cat /etc/nginx/nginx.conf

  1. [root@k8s-master prome]# kubectl delete configmap test-config2
  2. configmap "test-config2" deleted
  3. [root@k8s-master prome]# kubectl create configmap test-config2 --from-file=/etc/nginx/nginx.conf
  4. configmap/test-config2 created
  5. [root@k8s-master prome]# kubectl delete pod nginx
  6. pod "nginx" deleted
  7. [root@k8s-master prome]# kubectl apply -f testpod.yml
  8. pod/nginx created
  9. [root@k8s-master prome]# kubectl exec -it nginx /bin/bash
  10. kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
  11. root@nginx:/# cat /etc/nginx/nginx.conf

4、Downward API

Downward API用于在容器中获取POD的基本信息,kubernetes原生支持

Downward API提供了两种方式用于将POD的信息注入到容器内部:

  • 环境变量:用于单个变量,可以将POD信息和容器信息直接注入容器内部
  • Volume:将POD信息生成文件,直接挂载到容器内部中去。

1、环境变量的方式

通过Downward API将POD的IP、名称以及对应的namespace注入到容器的环境变量中去,然后在容器中打印全部的环境变量来进行验证

使用fieldRef获取POD的基本信息:

  1. [root@k8s-master biji]# vim test-env-pod.yaml
  2. ---
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: nginx1
  7. namespace: kube-system
  8. spec:
  9. containers:
  10. - name: nginx1
  11. image: daocloud.io/library/nginx:latest
  12. env:
  13. - name: POD_NAME #第一个环境变量的名字
  14. valueFrom: #使用valumeFrom方式设置
  15. fieldRef: #关联一个字段metadata.name
  16. fieldPath: metadata.name #这个字段从当前运行的pod详细信息查看
  17. - name: POD_NAMESPACE
  18. valueFrom:
  19. fieldRef:
  20. fieldPath: metadata.namespace
  21. - name: POD_IP
  22. valueFrom:
  23. fieldRef:
  24. fieldPath: status.podIP
  1. 注意:POD的name和namespace属于元数据,是在POD创建之前就已经定下来了的,所以使用metadata获取就可以了,但是对于POD的IP则不一样,因为POD IP是不固定的,POD重建之后就会改变,它属于状态数据,所以使用status去获取:
  2. 所有的基本信息可以使用下面的方式去查看(describe方式看不出来)
  3. # kubectl get pod nginx2 -o yaml

创建上面的POD:

  1. [root@k8s-master biji]# kubectl apply -f test-env-pod.yaml
  2. pod/nginx1 created

POD创建成功后,查看:

  1. [root@k8s-master biji]# kubectl exec -it nginx1 /bin/bash -n kube-system
  2. kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
  3. root@nginx1:/# env |grep POD
  4. POD_NAME=nginx1
  5. POD_NAMESPACE=kube-system
  6. POD_IP=10.244.2.11

2、volume挂载方式

通过Downward API将POD的Label、Annotation等信息通过volume挂载到某个文件中去,然后在容器中打印该文件的值来验证。

  1. [root@k8s-master biji]# vim test-volume-pod.yaml
  2. ---
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: nginx3
  7. namespace: kube-system
  8. labels:
  9. app: home
  10. web: tomcat
  11. spec:
  12. containers:
  13. - name: nginx3
  14. image: daocloud.io/library/nginx:latest
  15. volumeMounts:
  16. - name: volume
  17. mountPath: /etc/volume
  18. volumes:
  19. - name: volume
  20. downwardAPI:
  21. items:
  22. - path: "labels"
  23. fieldRef:
  24. fieldPath: metadata.labels

将原数据labels和annotaions以及文件的形式挂载到了/etc/volume目录下,创建上面的POD:

  1. [root@k8s-master biji]# kubectl apply -f test-volume-pod.yaml
  2. pod/nginx3 created
  3. [root@k8s-master biji]# kubectl get pod -n kube-system
  4. [root@k8s-master biji]# kubectl exec -it nginx3 /bin/bash -n kube-system
  5. kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
  6. root@nginx3:/# cd /etc/volume/
  7. root@nginx3:/etc/volume# ls
  8. labels
  9. root@nginx3:/etc/volume# cat labels
  10. app="home"
  11. web="tomcat"

在实际应用中,如果你的应用有获取POD的基本信息的需求,就利用Downward API来获取基本信息,然后编写一个启动脚本或者利用initContainer将POD的信息注入到容器中去,然后在自己的应用中就可以正常的处理相关逻辑了。

  1. 目前 Downward API 支持的字段:
  2. 1. 使用 fieldRef 可以声明使用:
  3. spec.nodeName - 宿主机名字
  4. status.hostIP - 宿主机 IP
  5. metadata.name - Pod 的名字
  6. metadata.namespace - Pod 的 Namespace
  7. status.podIP - Pod 的 IP
  8. spec.serviceAccountName - Pod 的 Service Account 的名字
  9. metadata.uid - Pod 的 UID
  10. metadata.labels['<KEY>'] - 指定 <KEY> 的 Label 值
  11. metadata.annotations['<KEY>'] - 指定 <KEY> 的 Annotation 值
  12. metadata.labels - Pod 的所有 Label
  13. metadata.annotations - Pod 的所有 Annotation
  14. 上面这个列表的内容,随着 Kubernetes 项目的发展肯定还会不断增加。所以这里列出来的信息仅供参考,在使用 Downward API 时,还是要记得去查阅一下官方文档。
Secret、ConfigMap,以及 Downward API 这三种 Projected Volume 定义的信息,大多还可以通过环境变量的方式出现在容器里。但是,通过环境变量获取这些信息的方式,不具备自动更新的能力。一般情况下,建议使用 Volume 文件的方式获取这些信息。

5、ServiceAccount

官方文档地址:https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/

k8s中提供了良好的多租户认证管理机制,如RBAC、ServiceAccount还有各种Policy等。

1、什么是ServiceAccount

当用户访问集群(例如使用kubectl命令)时,apiserver会将用户认证为一个特定的User Account(目前通常是ServiceAccount(例如defalut))

Pod 容器中的进程也可以与 apiserver 联系。 当它们在联系 apiserver 的时候,它们会被认证为一个特定的Service Account(例如default)。

使用场景:

  • Service Account它并不是给kubernetes集群的用户使用的,而是给pod里面的进程使用的,它为pod提供必要的身份认证。----专门为pod里面的进程和apiserver通信提供认证的。

2、ServiceAccount与UserAccount区别

  1. 1. User account是为用户设计的,而service account则是为Pod中的进程调用Kubernetes API或其他外部服务而设计的
  2. 2. User account是跨namespace的,而service account则是仅局限它所在的namespace;
  3. 3. 每个namespace都会自动创建一个default service account
  4. 4. Token controller检测service account的创建,并为它们创建secret
  5. 5. 开启ServiceAccount Admission Controller后:
  6. 5.1 每个Pod在创建后都会自动设置spec.serviceAccount为default(除非指定了其他ServiceAccout)
  7. 5.2 验证Pod引用的service account已经存在,否则拒绝创建
  8. 5.3 如果Pod没有指定ImagePullSecrets,则把service account的ImagePullSecrets加到Pod中
  9. 5.4 每个container启动后都会挂载该service account的token和ca.crt/run/secrets/kubernetes.io/serviceaccount/
  10. 每一个pod启动之后都会有一个和认证相关的东西存在pod里面,,存在到哪里呢?

查看系统config配置:

  1. 这里用到的token就是被授权过的SeviceAccount账户的token,集群利用token来使用ServiceAccount账户
  2. [root@k8s-master prome]# cat /root/.kube/config

3、ServiceAccount应用示例

ServiceAccount(服务账号)测试示例

因为平时系统会使用默认service account,我们不需要自己创建,感觉不到service account的存在,本实验是使用自己手动创建的service account

  1. 1、创建serviceaccount
  2. [root@k8s-master ~]# kubectl create serviceaccount mysa
  3. serviceaccount/mysa created
  4. 2、查看mysa
  5. [root@k8s-master ~]# kubectl describe sa mysa
  6. Name: mysa
  7. Namespace: default
  8. Labels: <none>
  9. Annotations: <none>
  10. Image pull secrets: <none>
  11. Mountable secrets: mysa-token-cknwf
  12. Tokens: mysa-token-cknwf
  13. Events: <none>
  14. 3、查看mysa自动创建的secret
  15. [root@k8s-master ~]# kubectl get secret
  16. NAME TYPE DATA AGE
  17. db-user-pass Opaque 2 11h
  18. default-token-6svwp kubernetes.io/service-account-token 3 4d23h
  19. mysa-token-cknwf kubernetes.io/service-account-token 3 76s
  20. mysecret Opaque 2 11h
  21. mysecret-01 Opaque 2 6h58m
  22. pass Opaque 1 7h6m
  23. user Opaque 1 7h7m
  24. 4、使用mysa的sa资源配置pod
  25. [root@k8s-master ~]# cd biji/
  26. [root@k8s-master biji]# vim mysa-pod.yaml
  27. ---
  28. apiVersion: v1
  29. kind: Pod
  30. metadata:
  31. name: nginx-pod
  32. labels:
  33. app: my-pod
  34. spec:
  35. containers:
  36. - name: my-pod
  37. image: daocloud.io/library/nginx
  38. ports:
  39. - name: http
  40. containerPort: 80
  41. serviceAccountName: mysa #指定serviceaccount的名称
  42. 5、导入
  43. [root@k8s-master prome]# kubectl apply -f mysa-pod.yaml
  44. pod/nginx-pod created
  45. 6、查看
  46. [root@k8s-master biji]# kubectl describe pod nginx-pod
  47. 7、查看使用的token和secret(使用的是mysa的token)
  48. [root@k8s-master biji]# kubectl get pod nginx-pod -o jsonpath={".spec.volumes"}
  49. [map[name:mysa-token-cknwf secret:map[defaultMode:420 secretName:mysa-token-cknwf]]]

案例1

  1. [root@k8s-master sa]# kubectl create namespace qiangungun #创建一个名称空间
  2. [root@k8s-master sa]# kubectl get sa -n qiangungun #名称空间下,会自动生成serivceaccount
  3. NAME SECRETS AGE
  4. default 1 74s
  5. [root@k8s-master sa]# kubectl get secrets -n qiangungun #同时会成成一个secrets
  6. NAME TYPE DATA AGE
  7. default-token-9bwxj kubernetes.io/service-account-token 3 2m21s

在创建的名称空间新建一个pod

  1. [root@k8s-master sa]# cat pod_demo.yml
  2. kind: Pod
  3. apiVersion: v1
  4. metadata:
  5. name: task-pv-pod
  6. namespace: qiangungun
  7. spec:
  8. containers:
  9. - name: nginx
  10. image: ikubernetes/myapp:v1
  11. ports:
  12. - containerPort: 80
  13. name: www
  14. [root@k8s-master sa]# kubectl apply -f pod_demo.yml
  15. [root@k8s-master sa]# kubectl get pod -n qiangungun
  16. NAME READY STATUS RESTARTS AGE
  17. task-pv-pod 1/1 Running 0 59s
  18. [root@k8s-master sa]# kubectl get pod -n qiangungun -o yaml

 可以看到,pod将serviceaccount中的secrets挂载到了pod内部的/var/run/secrets/kubernetes.io/serviceaccount

 名称空间新建的pod如果不指定sa,会自动挂载当前名称空间中默认的sa(default)

  1. 进入到pod中,查看验证一下,果然是有的
  2. [root@k8s-master sa]# kubectl exec -it task-pv-pod /bin/sh -n qiangungun
  3. / # cd /var/run/secrets/kubernetes.io/
  4. /var/run/secrets/kubernetes.io # ls
  5. serviceaccount
  6. /var/run/secrets/kubernetes.io # cd serviceaccount/
  7. /var/run/secrets/kubernetes.io/serviceaccount # ls
  8. ca.crt namespace token

案例2:

创建ServiceAccount

  1. [root@k8s-master sa]# cat test-sa.yml
  2. apiVersion: v1
  3. kind: ServiceAccount
  4. metadata:
  5. labels:
  6. k8s-app: kubernetes-dashboard
  7. name: kubernetes-dashboard-admin
  8. namespace: kube-system

将这个ServiceAccount跟ClusterRole进行绑定:

  1. [root@k8s-master sa]# cat test-clusterrolebinding.yml
  2. apiVersion: rbac.authorization.k8s.io/v1beta1
  3. kind: ClusterRoleBinding
  4. metadata:
  5. name: kubernetes-dashboard-admin
  6. labels:
  7. k8s-app: kubernetes-dashboard
  8. roleRef:
  9. apiGroup: rbac.authorization.k8s.io
  10. kind: ClusterRole
  11. name: cluster-admin
  12. subjects:
  13. - kind: ServiceAccount
  14. name: kubernetes-dashboard-admin
  15. namespace: kube-system

这样ServiceAccount就拥有了ClusterRole的权限

  1. [root@k8s-master sa]# cat test-pod.yml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: pod
  6. namespace: kube-system
  7. spec:
  8. containers:
  9. - name: podtest
  10. image: nginx
  11. serviceAccountName: kubernetes-dashboard-admin
  1. 总结:serviceaccount是pod访问apiserver的权限认证,每个pod中都默认会绑定当前名称空间默认的sa中的secrets,来保证pod能正常的访问apiserver;
  2. serviceaccount能和clusterrole进行绑定。绑定成功之后,serviceaccount的权限就和clusterrole一致了。后续pod再使用这个serviceaccount,pod的权限也就相同了;
  3. 不过这个,是pod进程的权限;如果真正关乎与用户的权限的话,还是使用RBAC
  4. 说白了,serviceaccount就是pod使用的账号而已;

十二、RBAC详解(基于角色的访问控制)

1、什么是RBAC

Service Account为服务提供了一种方便的认证机制,但它不关心授权的问题。可以配合RBAC来为Service Account鉴权

在Kubernetes中,授权有ABAC(基于属性的访问控制)、RBAC(基于角色的访问控制)、Webhook、Node、AlwaysDeny(一直拒绝)和AlwaysAllow(一直允许)这6种模式。

在RBAC API中,通过如下的步骤进行授权:

  • 定义角色:在定义角色时会指定此角色对于资源的访问控制的规则;
  • 绑定角色:将主体与角色进行绑定,对用户进行访问授权。

2、 Role与ClusterRole

  • Role:角色可以由命名空间内的Role对象定义,一个Role对象只能用于授予对某一单一命名空间中资源的访问权限。
  • ClusterRole:整个Kubernetes集群范围内有效的角色则通过ClusterRole对象实现。
  1. 简介
  2. role:
  3. 1、允许的操作,如get,list等
  4. 2、允许操作的对象,如pod,service等
  5. rolebinding:
  6. 将哪个用户绑定到哪个role或clusterrole上
  7. clusterrole:(集群角色)
  8. clusterrolebinding:(绑定到s集群)
  9. 如果使用rolebinding绑定到clusterrole上,表示绑定的用户只能用于当前namespace的权限

3、实验一:role

创建k8s账号与RBAC授权使用

  1. 创建账号/用户
  2. 1、创建私钥
  3. [root@k8s-master ~]# (umask 077; openssl genrsa -out soso.key 2048)
  4. Generating RSA private key, 2048 bit long modulus
  5. ...............................+++
  6. ..........................+++
  7. e is 65537 (0x10001)
  8. 用此私钥创建一个csr(证书签名请求)文件
  9. [root@k8s-master ~]# openssl req -new -key soso.key -out soso.csr -subj "/CN=soso"
  10. 拿着私钥和请求文件生成证书
  11. [root@k8s-master ~]# openssl x509 -req -in soso.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out soso.crt -days 365
  12. Signature ok
  13. subject=/CN=soso
  14. Getting CA Private Key
  15. 2、查看证书内容
  16. [root@k8s-master ~]# openssl x509 -in soso.crt -text -noout
  17. 生成账号
  18. [root@k8s-master ~]# kubectl config set-credentials soso --client-certificate=soso.crt --client-key=soso.key --embed-certs=true
  19. User "soso" set.
  20. 3、设置上下文环境--指的是这个账号只能在这个环境中才能用
  21. [root@k8s-master ~]# kubectl config set-context soso@kubernetes --cluster=kubernetes --user=soso
  22. Context "soso@kubernetes" created.
  23. 查看当前的工作上下文
  24. [root@k8s-master ~]# kubectl config view
  25. apiVersion: v1
  26. clusters:
  27. - cluster:
  28. certificate-authority-data: DATA+OMITTED
  29. server: https://192.168.246.166:6443
  30. ....
  31. 4、切换用户(切换上下文)
  32. [root@k8s-master ~]# kubectl config use-context soso@kubernetes
  33. Switched to context "soso@kubernetes".
  34. 验证是否已经切换到了新的上下文
  35. [root@k8s-master ~]# kubectl config current-context
  36. soso@kubernetes
  37. 5.测试(还未赋予权限)
  38. [root@k8s-master ~]# kubectl get pod
  39. Error from server (Forbidden): pods is forbidden: User "soso" cannot list resource "pods" in API group "" in the namespace "default"
  1. 创建一个角色(role)---权限
  2. 1.切回管理帐号先
  3. [root@k8s-master ~]# kubectl config use-context kubernetes-admin@kubernetes
  4. Switched to context "kubernetes-admin@kubernetes".
  5. 创建角色:
  6. [root@k8s-master ~]# kubectl create role myrole --verb=get,list,watch --resource=pod,svc
  7. role.rbac.authorization.k8s.io/myrole created
  8. --verb: 相当于是权限
  9. --resource:给什么资源使用
  10. 2.绑定用户soso(上面创建的用户),绑定role为myrole
  11. [root@k8s-master ~]# kubectl create rolebinding myrole-binding --role=myrole --user=soso
  12. rolebinding.rbac.authorization.k8s.io/myrole-binding created
  13. 3.切换用户
  14. [root@k8s-master ~]# kubectl config use-context soso@kubernetes
  15. Switched to context "soso@kubernetes".
  16. 4.查看权限(只授权了default名称空间pod和svc的get,list,watch权限)
  17. [root@k8s-master ~]# kubectl get pod
  18. NAME READY STATUS RESTARTS AGE
  19. lifecycle-demo 1/1 Running 1 22h
  20. mypod 1/1 Running 0 8h
  21. nginx-configmap 1/1 Running 0 4h29m
  22. nginx-pod 1/1 Running 0 39m
  23. [root@k8s-master ~]# kubectl get pod -n kube-system #无权访问kube-system
  24. Error from server (Forbidden): pods is forbidden: User "soso" cannot list resource "pods" in API group "" in the namespace "kube-system"
  25. [root@k8s-master ~]# kubectl delete pod nginx-pod #无权限删除
  26. Error from server (Forbidden): pods "nginx-pod" is forbidden: User "soso" cannot delete resource "pods" in API group "" in the namespace "default"
  27. 5.切换用户
  28. [root@k8s-master ~]# kubectl config use-context kubernetes-admin@kubernetes
  29. Switched to context "kubernetes-admin@kubernetes".
  30. ...

4、实验二:clusterrole

  1. 6.删除soso账号之前绑定的rolebinding
  2. [root@k8s-master ~]# kubectl delete rolebinding myrole-binding
  3. rolebinding.rbac.authorization.k8s.io "myrole-binding" deleted
  4. 7.创建clusterrole #可以访问全部的namespace
  5. [root@k8s-master ~]# kubectl create clusterrole myclusterrole --verb=get,list,watch --resource=pod,svc
  6. clusterrole.rbac.authorization.k8s.io/myclusterrole created
  7. 8.绑定集群角色到用户soso
  8. [root@k8s-master ~]# kubectl create clusterrolebinding my-cluster-rolebinding --clusterrole=myclusterrole --user=soso
  9. clusterrolebinding.rbac.authorization.k8s.io/my-cluster-rolebinding created
  10. 8.切换账号
  11. [root@k8s-master ~]# kubectl config use-context soso@kubernetes
  12. Switched to context "soso@kubernetes".
  13. 9.查看权限 查看kube-system空间的pod
  14. [root@k8s-master ~]# kubectl get pod -n kube-system
  15. NAME READY STATUS RESTARTS AGE
  16. coredns-5644d7b6d9-sm8hs 1/1 Running 0 5d
  17. coredns-5644d7b6d9-vddll 1/1 Running 0 5d
  18. etcd-kub-k8s-master 1/1 Running 0 5d
  19. 注意:10.切换为管理员用户
  20. [root@k8s-master ~]# kubectl config use-context kubernetes-admin@kubernetes

5、设置上下文和账户切换

设置工作上下文(前提得有用户)

  1. [root@k8s-master ~]# kubectl config set-context soso@kubernetes --cluster=kubernetes --user=soso
  2. Context "soso@kubernetes" created.

查看当前的工作上下文

  1. [root@k8s-master ~]# kubectl config view
  2. apiVersion: v1
  3. clusters:
  4. - cluster:
  5. ....

切换上下文(切换用户)

  1. [root@k8s-master ~]# kubectl config use-context soso@kubernetes
  2. Switched to context "soso@kubernetes".

切换为管理员用户

  1. [root@k8s-master prome]# kubectl config use-context kubernetes-admin@kubernetes
  2. Switched to context "kubernetes-admin@kubernetes".

十三、容器监控检查及恢复机制

​ 在 k8s 中,可以为 Pod 里的容器定义一个健康检查"探针"(Probe)。kubelet 就会根据这个 Probe 的返回值决定这个容器的状态,而不是直接以容器是否运行(来自 Docker 返回的信息)作为依据。这种机制,是生产环境中保证应用健康存活的重要手段。

注:

  • k8s 中并没有 Docker 的 Stop 语义。所以如果容器被探针检测到有问题,查看状态虽然看到的是 Restart,但实际却是重新创建了容器。

1、命令模式探针

  1. [root@k8s-master ~]# cd prome/
  2. [root@k8s-master prome]# vim test-liveness-exec.yaml
  3. ---
  4. apiVersion: v1
  5. kind: Pod
  6. metadata:
  7. labels:
  8. test: liveness
  9. name: test-liveness-exec
  10. spec:
  11. containers:
  12. - name: liveness
  13. image: daocloud.io/library/nginx
  14. args:
  15. - /bin/sh
  16. - -c
  17. - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 50
  18. livenessProbe: #探针,健康检查
  19. exec: #类型
  20. command: #命令
  21. - cat
  22. - /tmp/healthy
  23. initialDelaySeconds: 5 #健康检查,在容器启动 5 s 后开始执行
  24. periodSeconds: 5 #每 5 s 执行一次

文件内容解析:

  • 它在启动之后做的第一件事是在 /tmp 目录下创建了一个 healthy 文件,以此作为自己已经正常运行的标志。而 30 s 过后,它会把这个文件删除掉。

  • 与此同时,定义了一个这样的 livenessProbe(健康检查)。它的类型是 exec,它会在容器启动后,在容器里面执行一句我们指定的命令,比如:“cat /tmp/healthy”。这时,如果这个文件存在,这条命令的返回值就是 0,Pod 就会认为这个容器不仅已经启动,而且是健康的。

  • 这个健康检查,在容器启动 5 s 后开始执行(initialDelaySeconds: 5),每 5 s 执行一次(periodSeconds: 5)。

创建Pod:

  1. [root@k8s-master prome]# kubectl apply -f test-liveness-exec.yaml
  2. pod/test-liveness-exec created

查看 Pod 的状态:

  1. [root@k8s-master prome]# kubectl get pod
  2. NAME READY STATUS RESTARTS AGE
  3. nginx-configmap 1/1 Running 0 16h
  4. nginx-pod 1/1 Running 0 12h
  5. test-liveness-exec 1/1 Running 0 75s

由于已经通过了健康检查,这个 Pod 就进入了 Running 状态。

然后30 s 之后,再查看一下 Pod 的 Events:

[root@k8s-master prome]# kubectl describe pod test-liveness-exec 

发现,这个 Pod 在 Events 报告了一个异常:

  1. Events:
  2. Type Reason Age From Message
  3. ---- ------ ---- ---- -------
  4. Warning Unhealthy 54s (x9 over 3m34s) kubelet, kub-k8s-node1 Liveness probe failed: cat: /tmp/healthy: No such file or directory

这个健康检查探查到 /tmp/healthy 已经不存在了,所以它报告容器是不健康的。那么接下来会发生什么呢?

再次查看一下这个 Pod 的状态:

  1. [root@k8s-master prome]# kubectl get pod test-liveness-exec
  2. NAME READY STATUS RESTARTS AGE
  3. test-liveness-exec 1/1 Running 4 5m19s

这时发现,Pod 并没有进入 Failed 状态,而是保持了 Running 状态。这是为什么呢?

  • RESTARTS 字段从 0 到 1 的变化,就明白原因了:这个异常的容器已经被 Kubernetes 重启了。在这个过程中,Pod 保持 Running 状态不变。

  • 注意:Kubernetes 中并没有 Docker 的 Stop 语义。所以虽然是 Restart(重启),但实际却是重新创建了容器。

  • 这个功能就是 Kubernetes 里的Pod 恢复机制,也叫 restartPolicy。它是 Pod 的 Spec 部分的一个标准字段(pod.spec.restartPolicy),默认值是 Always,即:任何时候这个容器发生了异常,它一定会被重新创建。

小提示:

Pod 的恢复过程,永远都是发生在当前节点上,而不会跑到别的节点上去。事实上,一旦一个 Pod 与一个节点(Node)绑定,除非这个绑定发生了变化(pod.spec.node 字段被修改),否则它永远都不会离开这个节点。这也就意味着,如果这个宿主机宕机了,这个 Pod 也不会主动迁移到其他节点上去。

2、http get方式探针

  1. [root@k8s-master prome]# vim liveness-httpget.yaml
  2. ---
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: liveness-httpget-pod
  7. namespace: default
  8. spec:
  9. containers:
  10. - name: liveness-exec-container
  11. image: daocloud.io/library/nginx
  12. imagePullPolicy: IfNotPresent
  13. ports:
  14. - name: http
  15. containerPort: 80
  16. livenessProbe: #探针,健康检查
  17. httpGet:
  18. port: http
  19. path: /index.html
  20. initialDelaySeconds: 1
  21. periodSeconds: 3

创建该pod

  1. [root@k8s-master prome]# kubectl create -f liveness-httpget.yaml
  2. pod/liveness-httpget-pod created

查看当前pod的状态

  1. [root@k8s-master prome]# kubectl describe pod liveness-httpget-pod
  2. ...
  3. Liveness: http-get http://:http/index.html delay=1s timeout=1s period=3s #success=1 #failure=3
  4. ...

测试将容器内的index.html删除掉

登陆容器

  1. [root@k8s-master prome]# kubectl exec -it liveness-httpget-pod /bin/bash
  2. root@liveness-httpget-pod:/# mv /usr/share/nginx/html/index.html index.html
  3. root@liveness-httpget-pod:/# command terminated with exit code 137

可以看到,当把index.html移走后,这个容器立马就退出了。

此时,查看pod的信息

  1. [root@k8s-master prome]# kubectl describe pod liveness-httpget-pod
  2. ...
  3. Normal Killing 49s kubelet, kub-k8s-node2 Container liveness-exec-container failed liveness probe, will be restarted
  4. Normal Pulled 49s kubelet, kub-k8s-node2 Container image "daocloud.io/library/nginx" already present on machine
  5. ...

看输出,容器由于健康检查未通过,pod会被杀掉,并重新创建

  1. [root@k8s-master prome]# kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. lifecycle-demo 1/1 Running 1 34h
  4. liveness-httpget-pod 1/1 Running 1 5m42s

restarts 为 1

重新登陆容器查看

重新登陆容器,发现index.html又出现了,证明容器是被重拉了。

  1. [root@k8s-master prome]# kubectl exec -it liveness-httpget-pod /bin/bash
  2. root@liveness-httpget-pod:/# cat /usr/share/nginx/html/index.html

十四、POD的恢复策略

  1. Pod 的恢复策略:
  2. 可以通过设置 restartPolicy,改变 Pod 的恢复策略。一共有3种:
  3. 1. Always: 在任何情况下,只要容器不在运行状态,就自动重启容器;
  4. 2. OnFailure: 只在容器异常时才自动重启容器;
  5. 3. Never: 从来不重启容器。
  6. 实际使用时,需要根据应用运行的特性,合理设置这三种恢复策略。

十五、Deployment详解

  1. 使用yaml创建Deployment
  2. k8s deployment资源创建流程:
  3. 1. 用户通过 kubectl 创建 Deployment。
  4. 2. Deployment 创建 ReplicaSet。
  5. 3. ReplicaSet 创建 Pod。

Deployment是一个定义及管理多副本应用(即多个副本 Pod)的新一代对象,与Replication Controller相比,它提供了更加完善的功能,使用起来更加简单方便。

对象的命名方式是:子对象的名字 = 父对象名字 + 随机字符串或数字

 1、为什么使用Deployment

如果Pod出现故障,对应的服务也会挂掉,所以Kubernetes提供了一个Deployment的概念 ,目的是让Kubernetes去管理一组Pod的副本,也就是副本集 ,这样就能够保证一定数量的副本一直可用,不会因为某一个Pod挂掉导致整个服务挂掉。

Deployment 还负责在 Pod 定义发生变化时,对每个副本进行滚动更新(Rolling Update)。

这样使用一种 API 对象(Deployment)管理另一种 API 对象(Pod)的方法,在 k8s 中,叫作"控制器"模式(controller pattern)。Deployment 扮演的正是 Pod 的控制器的角色。

2、创建Deployment

创建deployment并添加volume

  1. [root@k8s-master ~]# mkdir deploy && cd deploy
  2. [root@k8s-master deploy]# vim a.yaml
  3. ---
  4. apiVersion: apps/v1
  5. kind: Deployment
  6. metadata:
  7. name: nginx
  8. spec:
  9. selector: #属性,选择器
  10. matchLabels:
  11. app: nginx
  12. replicas: 2 #管理的副本个数
  13. template: #创建的Pod模板属性
  14. metadata:
  15. labels:
  16. app: nginx
  17. spec:
  18. containers:
  19. - name: nginx
  20. image: daocloud.io/library/nginx:latest
  21. ports:
  22. - containerPort: 80
  23. volumeMounts: #定义挂载卷
  24. - mountPath: "/usr/share/nginx/html"
  25. name: nginx-vol
  26. volumes: #定义共享卷
  27. - name: nginx-vol
  28. emptyDir: {}
  1. [root@k8s-master deploy]# kubectl apply -f deploy.yaml
  2. deployment.apps/nginx created
  3. 检查deployment的列表
  4. [root@k8s-master deploy]# kubectl get deployments
  5. NAME READY UP-TO-DATE AVAILABLE AGE
  6. nginx 2/2 2 2 2m19s
  7. [root@k8s-master deploy]# kubectl get pods -l app=nginx
  8. NAME READY STATUS RESTARTS AGE
  9. nginx-5f857db9f9-xq6vt 1/1 Running 0 2m55s
  10. nginx-5f857db9f9-zgvfj 1/1 Running 0 2m55s
  11. 在这里加了一个-l的参数,即获取所有匹配app:nginx标签的Pod,注意,在命令行都是用“=”,而不是用“:”
  12. 删除Deployment
  13. [root@k8s-master deploy]# kubectl delete deployment nginx
  14. deployment.apps "nginx" deleted
  15. 或者
  16. [root@k8s-master deploy]# kubectl delete -f deploy.yaml
  1. apiVersion:注意这里apiVersion对应的值是extensions/v1beta1或者apps/v1.这个版本号需要根据安装的Kubernetes版本和资源类型进行变化,记住不是写死的。此值必须在kubectl apiversion中
  2. [root@k8s-master prome]# kubectl api-versions
  3. apps/v1beta1
  4. authentication.k8s.io/v1beta1
  5. authorization.k8s.io/v1beta1
  6. autoscaling/v1
  7. batch/v1
  8. certificates.k8s.io/v1alpha1
  9. extensions/v1beta1
  10. policy/v1beta1
  11. rbac.authorization.k8s.io/v1alpha1
  12. storage.k8s.io/v1beta1
  13. v1
  14. kind:资源类型:这里指定为Deployment。
  15. metadata:指定一些meta信息,包括名字或标签之类的。每一个 API 对象都有一个叫作 Metadata 的字段,这个字段是 API 对象的"标识",即元数据,也是我们从 Kubernetes 里找到这个对象的主要依据。
  16. labels:Labels是最主要的字段,是一组 key-value 格式的标签,k8s中的所有资源都支持携带label,默认情况下,pod的label会复制rc的label
  17. k8s使用用户自定义的key-value键值对来区分和标识资源集合(就像rc、pod等资源),这种键值对称为label。
  18. 像 Deployment 这样的控制器对象,就可以通过这个 Labels 字段从 Kubernetes 中过滤出它所关心的被控制对象。
  19. 关于Annotations:在 Metadata 中,还有一个与 Labels 格式、层级完全相同的字段叫 Annotations,它专门用来携带 key-value 格式的内部信息。所谓内部信息,指的是对这些信息感兴趣的,是 Kubernetes 组件本身,而不是用户。所以大多数 Annotations,都是在 Kubernetes 运行过程中,被自动加在这个 API 对象上。
  20. selector:过滤规则的定义,是在 Deployment 的"spec.selector.matchLabels"字段。一般称之为:Label Selector。
  21. pod的label会被用来创建一个selector,用来匹配过滤携带这些label的pods。
  22. 使用labels定位pods
  23. [root@k8s-master deploy]# kubectl get pods -l app=nginx -o wide
  24. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  25. nginx-5f857db9f9-lnbsh 1/1 Running 0 25s 10.244.1.25 k8s-node1 <none> <none>
  26. nginx-5f857db9f9-zh5z9 1/1 Running 0 25s 10.244.2.19 k8s-node2 <none>
  27. 检查你的Pod的IPs:
  28. [root@k8s-master deploy]# kubectl get pods -l app=nginx -o json |grep podIP
  29. "f:podIP": {},
  30. "f:podIPs": {
  31. "podIP": "10.244.1.25",
  32. "podIPs": [
  33. "f:podIP": {},
  34. "f:podIPs": {
  35. "podIP": "10.244.2.19",
  36. "podIPs": [
  37. spec : 一个 k8s 的 API 对象的定义,大多可以分为 Metadata 和 Spec 两个部分。前者存放的是这个对象的元数据,对所有 API 对象来说,这一部分的字段和格式基本上是一样的;而后者存放的,则是属于这个对象独有的定义,用来描述它所要表达的功能。
  38. 这里定义需要两个副本,此处可以设置很多属性,主要是受此Deployment影响的Pod的选择器
  39. replicas:定义的 Pod 副本个数 (spec.replicas) 是:2
  40. template:定义了一个 Pod 模版(spec.template),这个模版描述了想要创建的 Pod 的细节。例子里,这个 Pod 里只有一个容器,这个容器的镜像(spec.containers.image)是 nginx:latest,这个容器监听端口(containerPort)是 80
  41. volumes:是属于 Pod 对象的一部分。需要修改 template.spec 字段
  42. 案例中,在 Deployment 的 Pod 模板部分添加了一个 volumes 字段,定义了这个 Pod 声明的所有 Volume。它的名字叫作 nginx-vol,类型是 emptyDir。
  43. 关于emptyDir 类型:等同于 Docker 的隐式 Volume 参数,即:不显式声明宿主机目录的 Volume。所以,Kubernetes 也会在宿主机上创建一个临时目录,这个目录将来就会被绑定挂载到容器所声明的 Volume 目录上。
  44. k8s 的 emptyDir 类型,只是把 k8s 创建的临时目录作为 Volume 的宿主机目录,交给了 Docker。这么做的原因,是 k8s 不想依赖 Docker 自己创建的那个 _data 目录。
  45. volumeMounts:Pod 中的容器,使用的是 volumeMounts 字段来声明自己要挂载哪个 Volume,并通过 mountPath 字段来定义容器内的 Volume 目录,比如:/usr/share/nginx/html。
  46. hostPath:k8s 也提供了显式的 Volume 定义,它叫做 hostPath。比如下面的这个 YAML 文件:
  47. ...
  48. volumes:
  49. - name: nginx-vol
  50. hostPath:
  51. path: /var/data
  52. 这样,容器 Volume 挂载的宿主机目录,就变成了 /var/data

十六、Service详解

1、什么是Service

service是pod的一个逻辑分组,是pod服务的对外入口抽象。service同样也通过pod的标签来选择pod,与控制器一致。

 service提供pod的负载均衡的能力,但是只提供4层负载均衡的能力,而没有7层功能,只能到ip层面。

2、Service的几种类型

  • ClusterIP:默认类型,自动分配一个仅可在内部访问的虚拟IP。应用方式:内部服务访问
  • NodePort:在ClusterIP的基础之上,为集群内的每台物理机绑定一个端口,外网通过任意节点的物理机IP:端口来访问服务。应用方式:外服访问服务
  • LoadBalance:在NodePort基础之上,提供外部负载均衡器与外网统一IP,此IP可以将请求转发到对应服务上。这个是各个云厂商提供的服务。应用方式:外服访问服务
  • ExternalName:引入集群外部的服务,可以在集群内部通过别名方式访问(通过 serviceName.namespaceName.svc.cluster.local访问)

1、ClusterIP

  1. 创建deployment,nginx2个副本,然后再创建一个ClusterIP类型的service
  2. vim a.yaml
  3. ---
  4. apiVersion: apps/v1
  5. kind: Deployment
  6. metadata:
  7. name: nginx-dep
  8. spec:
  9. selector: #选择app:nginx2标签的Pod
  10. matchLabels:
  11. app: nginx2
  12. replicas: 2
  13. template: #pod的模板
  14. metadata:
  15. labels:
  16. app: nginx2
  17. spec:
  18. containers:
  19. - name: nginx
  20. image: daocloud.io/library/nginx:latest
  21. ports:
  22. - containerPort: 80
  23. ---
  24. apiVersion: v1
  25. kind: Service
  26. metadata:
  27. name: nginx-svc-clusterip
  28. spec:
  29. type: ClusterIP #类型
  30. selector: #选择app:nginx2标签的Pod
  31. app: nginx2
  32. ports:
  33. - protocol: TCP #使用的协议TCP,默认也是TCP协议
  34. port: 80 #service对外提供的端口
  35. targetPort: 80 #代理的容器的端口
  36. [root@k8s-master service]# kubectl apply -f a.yaml
  37. deployment.apps/nginx-dep created
  38. service/nginx-svc-clusterip created
  39. [root@k8s-master service]# kubectl get svc
  40. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  41. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d4h
  42. nginx-svc-clusterip ClusterIP 10.107.84.41 <none> 80/TCP 33s

 测试ClusterIP

  1. 可以新建一个带有curl命令的Pod验证,因为我用的镜像中有curl命令,我直接进入deployment管理的2个nginx副本中修改默认页面进行验证,只需要修改一个就行,与另一个不同,方便验证
  2. 进入第1个nginx的pod中:
  3. [root@k8s-master service]# kubectl get pod -o wide
  4. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  5. nginx-dep-64b7464986-k2n8r 1/1 Running 0 2m45s 10.244.2.24 k8s-node2 <none> <none>
  6. nginx-dep-64b7464986-rwx2b 1/1 Running 0 2m45s 10.244.1.29 k8s-node1 <none> <none>
  7. [root@k8s-master service]# kubectl exec -it nginx-dep-64b7464986-rwx2b /bin/bash
  8. kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
  9. root@nginx-dep-64b7464986-rwx2b:/# echo "black silk" > /usr/share/nginx/html/index.html

直接在这个pod中,我们访问clusterIP进行验证

2、NodePort

三台服务器,master和两个node节点都安装iptables

yum install -y iptables iptables-services

 在上面clusterip的基础上添加nodeport的类型

  1. vim a.yaml
  2. ---
  3. apiVersion: apps/v1
  4. kind: Deployment
  5. metadata:
  6. name: nginx-dep
  7. spec:
  8. selector:
  9. matchLabels:
  10. app: nginx2
  11. replicas: 2
  12. template:
  13. metadata:
  14. labels:
  15. app: nginx2
  16. spec:
  17. containers:
  18. - name: nginx
  19. image: daocloud.io/library/nginx:latest
  20. ports:
  21. - containerPort: 80
  22. ---
  23. apiVersion: v1
  24. kind: Service
  25. metadata:
  26. name: nginx-svc-clusterip
  27. spec:
  28. type: ClusterIP
  29. selector:
  30. app: nginx2
  31. ports:
  32. - protocol: TCP
  33. port: 80
  34. targetPort: 80
  35. ---
  36. apiVersion: v1
  37. kind: Service
  38. metadata:
  39. name: service-nodeport
  40. spec:
  41. type: NodePort
  42. selector:
  43. app: nginx2
  44. ports:
  45. - protocol: TCP
  46. port: 80 #service对外提供的端口
  47. targetPort: 80 #代理的容器的端口
  48. nodePort: 30007 #在物理机上开辟的端口,从30000-32767
  49. [root@k8s-master service]# kubectl apply -f a.yaml
  50. deployment.apps/nginx-dep unchanged
  51. service/nginx-svc-clusterip unchanged
  52. service/service-nodeport created
  53. [root@k8s-master service]# kubectl get svc
  54. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  55. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d4h
  56. nginx-svc-clusterip ClusterIP 10.107.84.41 <none> 80/TCP 15m
  57. service-nodeport NodePort 10.102.75.15 <none> 80:30007/TCP 110s

画一张图结合上面的实验帮助理解一下nodePort与clusterIP

 测试NodePort:

清理一下缓存,访问另一个node

端口详解

  1. 服务中的3个端口设置
  2. 这几个port的概念很容易混淆,比如创建如下service:
  3. apiVersion: v1
  4. kind: Service
  5. metadata:
  6. name: nginx2
  7. spec:
  8. type: NodePort
  9. ports:
  10. - port: 80
  11. nodePort: 30007
  12. targetPort: 80
  13. selector:
  14. app: nginx2

port 

这里的port表示:service暴露在cluster ip上的端口,cluster ip:port 是提供给集群内部客户访问service的入口。

nodePort 

首先,nodePort是kubernetes提供给集群外部客户访问service入口的一种方式(另一种方式是LoadBalancer),所以,<nodeIP>:nodePort 是提供给集群外部客户访问service的入口。

 targetPort

targetPort很好理解,targetPort是pod上的端口,从port和nodePort上到来的数据最终经过kube-proxy流入到后端pod的targetPort上进入容器。

 port、nodePort总结

总的来说,port和nodePort都是service的端口,前者暴露给集群内客户访问服务,后者暴露给集群外客户访问服务。从这两个端口到来的数据都需要经过反向代理kube-proxy流入后端pod的targetPod,从而到达pod上的容器内。

3、LoadBalance

这个LoadBalance实验需要有公网ip以及对应解析的域名,我做的话还得重新在云服务器上部署k8s集群,所以就简单说一下过程,要是有想看完整实验过程的兄弟可以私信我,我做好单发一篇

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: loadbalance-test
  5. spec:
  6. ports:
  7. - name: loadbalance-port
  8. #service对外提供的端口
  9. port: 80
  10. # 代理的容器的端口
  11. targetPort: 80
  12. # 在物理机上开辟的端口,从30000开始
  13. nodePort: 32138
  14. selector:
  15. app: nginx
  16. type: LoadBalancer
  17. status:
  18. loadBalancer:
  19. ingress:
  20. - ip: 云厂商LoadbalanceIP
  1. [root@ master ~]# kubectl get svc -n test
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. loadbalance-test LoadBalancer 172.21.10.152 LoadbalanceIP 80:32138/TCP 4m

4、ExternalName

  1. [root@k8s-master service]# cat b.yaml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: service-ext
  6. spec:
  7. type: ExternalName
  8. # 引入外部服务
  9. externalName: www.baidu.com
  10. [root@k8s-master service]# kubectl apply -f b.yaml
  11. service/service-ext created

 任意找个pod来访问服务,通过kubectl exec -it podname sh 来对pod执行sh命令,这样可以进入容器内部,进入一个有ping命令的pod内部

[root@k8s-master service]# kubectl exec -it nginx-dep-64b7464986-k2n8r /bin/bash

 所以ExternalName也就是给另外一个网站的域名,起了一个内部使用的别名而已;

5、ingress是干嘛的?

service只能提供4层负载均衡的能力,虽然service可以通过NodePort的方式来服务,但是随着服务的增多,会在物理机上开辟太多端口,管理起来混乱。

那么我们换一种思路来暴露服务,创建一个具有N个副本的nginx服务,在nginx服务内配置各个服务的域名与集群内部的服务的IP,这些nginx服务再通过NodePort的方式来暴露。外部服务通过域名:Nginx NodePort端口来访问nginx,nginx再通过域名反向代理到真实服务。

上面的这个流程就是ingress做的事,ingress分为ingress controller与ingress配置。ingress controller是反向代理服务器,对外通过NodePort(或者其他方式)来暴露,ingress配置是抽象出来的域名代理配置。

上面我们提到有一个叫作 LoadBalancer 类型的 Service,它会为你在 Cloud Provider(比如:Google Cloud 或者 OpenStack)里创建一个与该 Service 对应的负载均衡服务。但是,相信你也应该能感受到,由于每个 Service 都要有一个负载均衡服务,所以这个做法实际上既浪费成本又高。作为用户,我其实更希望看到 Kubernetes 为我内置一个全局的负载均衡器。然后,通过我访问的 URL,把请求转发给不同的后端 Service。这种全局的、为了代理不同后端 Service 而设置的负载均衡服务,就是 Kubernetes 里的 Ingress 服务。

Ingress 的功能其实很容易理解:所谓 Ingress 就是 Service 的“Service”,这就是它们两者的关系。

internet | [ Ingress ] --|-----|-- [ Services ]

通过使用 Kubernetes 的 Ingress 来创建一个统一的负载均衡器,从而实现当用户访问不同的域名时,访问后端不同的服务

Ingress 负载均衡

可以将 Ingress 配置为服务提供外部可访问的 URL、负载均衡流量、终止 SSL/TLS,以及提供基于名称的虚拟主机等能力。 Ingress 控制器 通常负责通过负载均衡器来实现 Ingress,尽管它也可以配置边缘路由器或其他前端来帮助处理流量。

你必须具有 Ingress 控制器 才能满足 Ingress 的要求。 仅创建 Ingress 资源本身没有任何效果。你可能需要部署 Ingress 控制器,例如 ingress-nginx。 你可以从许多 Ingress 控制器 中进行选择。

假如我现在有这样一个站点:https://cafe.example.com。其中 https://cafe.example.com/coffee,对应的是“咖啡点餐系统”。而 https://cafe.example.com/tea,对应的则是“茶水点餐系统”。这两个系统,分别由名叫 coffee 和 tea 这样两个 Deployment 来提供服务,可以看到这是一种经典的扇出(fanout)行为。

6、kube-proxy与iptables的关系

当service有了port和nodePort之后,就可以对内/外提供服务。那么其具体是通过什么原理来实现的呢?原因就在kube-proxy在本地node上创建的iptables规则。

Kube-Proxy 通过配置 DNAT 规则(从容器出来的访问,从本地主机出来的访问两方面),将到这个服务地址的访问映射到本地的kube-proxy端口(随机端口)。然后 Kube-Proxy 会监听在本地的对应端口,将到这个端口的访问给代理到远端真实的 pod 地址上去。

不管是通过集群内部服务入口:port还是通过集群外部服务入口:nodePort的请求都将重定向到本地kube-proxy端口(随机端口)的映射,然后将到这个kube-proxy端口的访问给代理到远端真实的 pod 地址上去。

十七、RC资源(了解)

1、什么是RC

Replication Controller(简称rc)用来管理Pod的副本,保证集群中存在指定数量的Pod副本。集群中副本的数量大于指定数量,则会停止指定数量之外的多余容器数量,反之,则会启动少于指定数量个数的容器,保证数量不变。Replication Controller是实现弹性伸缩、动态扩容和滚动升级的核心。

RC 的主要功能点:

  • 确保pod数量:指定某个服务在Kubernetes中有相应数量的Pod在运行;
  • 确保pod健康:当pod不健康,运行出错或者无法提供服务时,会杀死不健康pod并重新创建,保持pod数量一致;
  • 弹性伸缩:当业务高峰期的时候可以设置扩增pod数量,配合监控就可以做自动伸缩了;
  • 滚动升级:也就是蓝绿发布,当一个pod使用的镜像更新,采用滚动升级模式,RC会自动一个个pod的进行升级,关闭一个pod的同时进行升级,且在原镜像基础上创建一个新pod,当一个pod更新完成再关闭一个旧镜像pod。

2、创建RC

1.使用yaml创建并启动replicas集合

  1. k8s通过Replication Controller来创建和管理各个不同的重复容器集合(实际上是重复的pods)。
  2. Replication Controller会确保pod的数量在运行的时候会一直保持在一个特殊的数字,即replicas的设置。
  1. [root@k8s-master ~]# cd prome/
  2. [root@k8s-master prome]# vim nginx-rc.yml
  3. ---
  4. apiVersion: v1
  5. kind: ReplicationController
  6. metadata:
  7. name: my-nginx
  8. spec:
  9. replicas: 2
  10. template:
  11. metadata:
  12. labels:
  13. app: nginx
  14. spec:
  15. containers:
  16. - name: nginx
  17. image: daocloud.io/library/nginx
  18. ports:
  19. - containerPort: 80
和定义一个pod的YAML文件相比,不同的只是kind的值为ReplicationController,replicas的值需要指定,pod的相关定义在template中,pod的名字不需要显式地指定,因为它们会在rc中创建并赋予名字

创建rc:

  1. [root@k8s-master prome]# kubectl apply -f nginx-rc.yml
  2. replicationcontroller/my-nginx created

和直接创建pod不一样,rc将会替换因为任何原因而被删除或者停止运行的Pod,比如说pod依赖的节点挂了。所以我们推荐使用rc来创建和管理复杂应用,即使你的应用只要使用到一个pod,在配置文件中忽略replicas字段的设置即可

2、查看Replication Controller的状态

  1. [root@k8s-master prome]# kubectl get rc
  2. NAME DESIRED CURRENT READY AGE
  3. my-nginx 2 2 2 11

这个状态表示,你创建的rc将会确保你一直有两个nginx的副本。

也可以和直接创建Pod一样查看创建的Pod状态信息:

  1. [root@k8s-master prome]# kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. dep01-58f6d4d4cb-g6vtg 1/1 Running 0 3h8m
  4. dep01-58f6d4d4cb-k6z47 1/1 Running 0 3h8m
  5. my-nginx-7kbwz 1/1 Running 0 2m49s
  6. my-nginx-jkn8l 1/1 Running 0 2m49s

3、删除Replication Controller

  1. 当你想停止你的应用,删除你的rc,可以使用:
  2. [root@k8s-master prome]# kubectl delete rc my-nginx
  3. replicationcontroller "my-nginx" deleted

默认的,这将会删除所有被这个rc管理的pod,如果pod的数量很大,将会花一些时间来完成整个删除动作,如果你想使这些pod停止运行,请指定–cascade=false。

如果你在删除rc之前尝试删除pod,rc将会立即启动新的pod来替换被删除的pod

3、完整TOMCAT实例

注意:本文中和上文中的NodePort没有完全解决外部访问Service的所有问题,比如负载均衡,假如我们又10个Node,则此时最好有一个负载均衡器,外部的请求只需访问此负载均衡器的IP地址,由负载局衡器负责转发流量到后面某个Node的NodePort上。这个负载均衡器可以是硬件,也可以是软件方式,例如HAProxy或者Nginx;

Java Web应用

注:Tomcat有可能无法正常启动,原因是虚机的内存和CPU设置过小,请酌情调大!

下载镜像

[root@k8s-node1 ~]#  docker pull daocloud.io/library/tomcat:8.5.43

构建Tomcat RC定义文件

  1. [root@k8s-master prome]# vim myweb.rc.yml
  2. ---
  3. apiVersion: v1
  4. kind: ReplicationController
  5. metadata:
  6. name: myweb
  7. spec:
  8. replicas: 2
  9. selector:
  10. app: myweb
  11. template:
  12. metadata:
  13. labels:
  14. app: myweb
  15. spec:
  16. containers:
  17. - name: myweb
  18. image: daocloud.io/library/tomcat:8.5.43
  19. ports:
  20. - containerPort: 8080 #在8080端口上启动容器进程,PodIP与容器端口组成Endpoint,代表着一个服务进程对外通信的地址

发布到Kubernetes集群

创建RC

  1. [root@k8s-master prome]# kubectl apply -f myweb.rc.yml
  2. replicationcontroller/myweb created

查看RC

  1. [root@-k8s-master prome]# kubectl get rc
  2. NAME DESIRED CURRENT READY AGE
  3. myweb 1 1 1 20s

查看Pod

  1. [root@-k8s-master prome]# kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. myweb-shjfn 1/1 Running 0 52s

构建Tomcat Kubernetes Service定义文件

  1. [root@k8s-master prome]# vim myweb-svc.yaml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: myweb
  6. spec:
  7. type: NodePort
  8. ports:
  9. - port: 8081
  10. nodePort: 30009
  11. targetPort: 8080
  12. selector:
  13. app: myweb

创建

  1. [root@k8s-master prome]# kubectl apply -f myweb-svc.yaml
  2. service/myweb created

查看SVC

  1. [root@k8s-master prome]# kubectl get svc
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d22h
  4. mysvc NodePort 10.110.160.108 <none> 8080:30001/TCP 3h37m
  5. myweb NodePort 10.96.19.61 <none> 8081:30009/TCP 33s

运行

浏览器中输入http://虚拟机IP:30009即可呈现如下内容:


注意在节点(node)中访问,不是master

[root@k8s-node1 ~]# curl 10.0.0.132:30009

如果你下载的tomcat镜像没有curl命令的话可以换成nginx镜像,创建rc:

  1. 创建rc
  2. [root@k8s-master prome]# cat nginx-rc.yml
  3. ---
  4. apiVersion: v1
  5. kind: ReplicationController
  6. metadata:
  7. name: my-nginx
  8. spec:
  9. replicas: 2
  10. template:
  11. metadata:
  12. labels:
  13. app: nginx
  14. spec:
  15. containers:
  16. - name: nginx
  17. image: daocloud.io/library/nginx
  18. ports:
  19. - containerPort: 80
  20. [root@k8s-master prome]# kubectl apply -f nginx-rc.yml
  21. 通过创建service暴露端口
  22. [root@k8s-master prome]# cat nginx-svc.yml
  23. apiVersion: v1
  24. kind: Service
  25. metadata:
  26. name: mynginx
  27. spec:
  28. type: NodePort
  29. ports:
  30. - port: 8082
  31. nodePort: 30010
  32. targetPort: 80
  33. selector:
  34. app: nginx
  35. [root@k8s-master prome]# kubectl apply -f nginx-svc.yml

访问测试:

换一个Tomcat镜像测试

  1. 创建关于tomcat的rc
  2. [root@k8s-master prome]# cat myweb.rc.yml
  3. ---
  4. apiVersion: v1
  5. kind: ReplicationController
  6. metadata:
  7. name: myweb
  8. spec:
  9. replicas: 2
  10. selector:
  11. app: myweb
  12. template:
  13. metadata:
  14. labels:
  15. app: myweb
  16. spec:
  17. containers:
  18. - name: myweb
  19. image: hub.c.163.com/public/tomcat:7.0.28
  20. ports:
  21. - containerPort: 8080
  22. [root@k8s-master prome]# kubectl apply -f myweb.rc.yml
  23. 创建service通过NodePort的方式暴露tomcat容器的端口到外网
  24. [root@k8s-master prome]# cat myweb-svc.yaml
  25. apiVersion: v1
  26. kind: Service
  27. metadata:
  28. name: myweb
  29. spec:
  30. type: NodePort
  31. ports:
  32. - port: 8081
  33. nodePort: 30009
  34. targetPort: 8080
  35. selector:
  36. app: myweb
  37. [root@k8s-master prome]# kubectl apply -f myweb-svc.yml

访问测试,成功

十八、K8S之暴露IP给外网

转发K8S后端服务的四种方式

1、ClusterIP

此类型会提供一个集群内部的虚拟IP(与Pod不在同一网段),以供集群内部的pod之间通信使用。ClusterIP也是Kubernetes service的默认类型。

2、NodePort(常用)

外网client—>nodeIP+nodePort—>podIP+PodPort

为每个节点暴露一个端口,通过nodeip + nodeport可以访问这个服务,同时服务依然会有cluster类型的ip+port。内部通过clusterip方式访问,外部通过nodeport方式访问。

3、loadbalance

LoadBalancer在NodePort基础上,K8S可以请求底层云平台创建一个负载均衡器,将每个Node作为后端,进行服务分发。

4、Ingress

Ingress是一种HTTP方式的路由转发机制,为K8S服务配置HTTP负载均衡器,通常会将服务暴露给K8S群集外的客户端。

十九、控制器模式解析

1、什么是控制集群模式

k8s 项目通过一个称作"控制器模式"(controller pattern)的设计方法,来统一地实现对各种不同的对象或者资源进行的编排操作。

k8s核心就是用一个东西去控制另一个东西,所有的内容都是被控制的,容器镜像虽然好用,但是容器这样一个"沙盒"的概念,对于描述应用来说太过简单。好比,集装箱固然好用,如果它四面都光秃秃的,吊车还怎么把这个集装箱吊起来并摆放好呢?

所以,Pod 对象,其实就是容器的升级版。它对容器进行了组合,添加了更多的属性和字段。这就好比给集装箱四面安装了吊环,使得 Kubernetes 这架"吊车",可以更轻松地操作它。

而 k8s 操作这些"集装箱"的逻辑,都由控制器(Controller)完成

回顾 Deployment 这个最基本的控制器对象。之前讲过一个 nginx-deployment 的例子:

  1. 1
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5. name: nginx-deployment
  6. spec:
  7. selector:
  8. matchLabels:
  9. app: nginx
  10. replicas: 2
  11. template:
  12. metadata:
  13. labels:
  14. app: nginx
  15. spec:
  16. containers:
  17. - name: nginx
  18. image: nginx:1.7.9
  19. ports:
  20. - containerPort: 80

这个 Deployment 定义的编排动作为:

  • 确保携带了 app=nginx 标签的 Pod 的个数,永远等于 spec.replicas 指定的个数,即 2 个。
  • 如果在这个集群中,携带 app=nginx 标签的 Pod 的个数大于 2 的时候,就会有旧的 Pod 被删除;反之,就会有新的 Pod 被创建。

究竟是 Kubernetes 项目中的哪个组件,在执行这些操作呢?

kube-controller-manager 组件:这个组件,就是一系列控制器的集合

2、控制器种类

所有控制器:

  1. deployment job podautoscaler
  2. cloud disruption namespace
  3. replicaset serviceaccount volume
  4. cronjob garbagecollector nodelifecycle replication statefulset daemon

上面的每一个控制器,都以独有的方式负责某种编排功能。而Deployment,正是这些控制器中的一种。

  1. 而被控制对象的定义,则来自于一个"模板"。比如,Deployment 里的 template 字段。
  2. Deployment 这个 template 字段里的内容,跟一个标准的 Pod 对象的 API 定义,丝毫不差。而所有被这个 Deployment 管理的 Pod 实例,都是根据这个 template 字段的内容创建出来的。

对 Deployment 以及其他类似的控制器,做一个总结:

img

如图,类似 Deployment 的一个控制器,都是由两部分组成:

  • 上半部分的控制器定义(包括期望状态)
  • 下半部分的被控制对象的模板组成的。

也正是在这个统一的编排框架下,不同的控制器可以在具体执行过程中,设计不同的业务逻辑,从而达到不同的编排效果。

这个实现思路,正是 k8s 进行容器编排的核心原理。

二十、滚动更新

概念:

将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是"滚动更新"

实验:

  1. [root@k8s-master prome]# kubectl get deploy
  2. NAME READY UP-TO-DATE AVAILABLE AGE
  3. dep01 2/2 2 2 4h41m
  4. nginx-deployment 2/2 2 2 5h13m
  5. 我们将nginx-deploument的副本数量变成4个,现在2
  6. [root@k8s-master prome]# vim deployment.yaml #修改如下内容
  7. 将replicas: 2
  8. 修改为:
  9. replicas: 4

1571754655365
创建上节儿的:nginx-deployment

  1. [root@k8s-master prome]# kubectl apply -f deployment.yaml --record
  2. deployment.apps/nginx-deployment configured
  3. --record 记录下每次操作所执行的命令,以方便后面查看

检查nginx-deployment 创建后的状态信息:

  1. [root@k8s-master prome]# kubectl get deploy
  2. NAME READY UP-TO-DATE AVAILABLE AGE
  3. dep01 2/2 2 2 4h53m
  4. nginx-deployment 4/4 4 4 5h25m

返回结果中四个状态字段含义:

  1. DESIRED:
  2. 如果有就表示用户期望的 Pod 副本个数(spec.replicas 的值);
  3. CURRENT
  4. 当前处于 Running 状态的 Pod 的个数;
  5. UP-TO-DATE
  6. 当前处于最新版本的 Pod 的个数,所谓最新版本指的是 Pod 的 Spec 部分与 Deployment 里 Pod 模板里定义的完全一致;
  7. AVAILABLE:
  8. 当前已经可用的 Pod 的个数,即:既是 Running 状态,又是最新版本,并且已经处于 Ready(健康检查正确)状态的 Pod 的个数。只有这个字段,描述的才是用户所期望的最终状态。

修改 Deployment:

  1. [root@k8s-master prome]# kubectl get deploy
  2. NAME READY UP-TO-DATE AVAILABLE AGE
  3. dep01 2/2 2 2 4h59m
  4. nginx-deployment 4/4 4 4 5h32m
  5. 将dep01的副本将2变为3
  6. [root@k8s-master prome]# kubectl edit deployment/dep01
  7. # reopened with the relevant failures.
  8. #
  9. apiVersion: apps/v1
  10. ...
  11. spec:
  12. progressDeadlineSeconds: 600
  13. replicas: 3 #将这里原来的2改为3
  14. revisionHistoryLimit: 10
  15. selector:
  16. matchLabels:
  17. ...
  18. 保存退出,vim的方式
  19. [root@k8s-master prome]# kubectl edit deployment/dep01
  20. deployment.apps/dep01 edited
  1. [root@k8s-master prome]# kubectl get deploy
  2. NAME READY UP-TO-DATE AVAILABLE AGE
  3. dep01 3/3 3 3 5h16m
  4. nginx-deployment 4/4 4 4 5h48m

进行版本的升级

  1. 创建一个新的deploy
  2. [root@k8s-master prome]# cp nginx-depl.yml nginx-depl02.yml
  3. [root@k8s-master prome]# vim nginx-depl02.yml
  4. apiVersion: apps/v1
  5. kind: Deployment
  6. metadata:
  7. name: dep02 #注意修改
  8. spec:
  9. selector:
  10. matchLabels:
  11. app: web1
  12. replicas: 2
  13. template:
  14. metadata:
  15. name: testnginx9
  16. labels:
  17. app: web1
  18. spec:
  19. containers:
  20. - name: testnginx9
  21. image: daocloud.io/library/nginx:1.14 #注意修改
  22. ports:
  23. - containerPort: 80
  24. [root@k8s-master prome]# kubectl apply -f nginx-depl02.yml
  25. deployment.apps/dep02 created
  26. [root@k8s-master prome]# kubectl get pods
  27. NAME READY STATUS RESTARTS AGE
  28. dep01-58f6d4d4cb-997jw 1/1 Running 0 16m
  29. dep01-58f6d4d4cb-g6vtg 1/1 Running 0 5h32m
  30. dep02-78dbd944fc-47czr 1/1 Running 0 44s
  31. dep02-78dbd944fc-4snsj 1/1 Running 0 25s
  32. [root@k8s-node1 ~]# docker exec -it 7e491bb33dcd /bin/bash
  33. root@dep02-8594cd6447-z5mzs:/# nginx -v
  34. nginx version: nginx/1.14.2
  35. 将nginx的版本从1.14升级到1.16
  36. [root@k8s-master prome]# kubectl edit deployment/dep02
  37. # Please edit the object below. Lines beginning with a '#' will be ignored,
  38. # and an empty file will abort the edit. If an error occurs while saving this file will be
  39. ...
  40. spec:
  41. containers:
  42. - image: daocloud.io/library/nginx:1.16 #将这里原来的nginx:1.14修改为nginx:1.16
  43. imagePullPolicy: Always
  44. name: testnginx9
  45. ports:
  46. - containerPort: 80
  47. ...
  48. 保存退出,vim的方式
  49. [root@k8s-master prome]# kubectl edit deployment/dep01
  50. deployment.apps/dep01 edited

这时可以通过查看 Deployment 的 Events,看到这个"滚动更新"的流程:

  1. [root@k8s-master prome]# kubectl describe deployment dep02
  2. ...
  3. Events:
  4. Type Reason Age From Message
  5. ---- ------ ---- ---- -------
  6. Normal ScalingReplicaSet 50s deployment-controller Scaled up replica set dep02-846bf8775b to 2
  7. Normal ScalingReplicaSet 9s deployment-controller Scaled up replica set dep02-58f8d5678 to 1
  8. Normal ScalingReplicaSet 8s deployment-controller Scaled down replica set dep02-846bf8775b to 1
  9. Normal ScalingReplicaSet 8s deployment-controller Scaled up replica set dep02-58f8d5678 to 2
  10. Normal ScalingReplicaSet 5s deployment-controller Scaled down replica set dep02-846bf8775b to 0
如此交替进行,新 ReplicaSet 管理的 Pod 副本数,从 0 个变成 1 个,再变成 2 个,最后变成 3 个。而旧的 ReplicaSet 管理的 Pod 副本数则从 3 个变成 2 个,再变成 1 个,最后变成 0 个。这样,就完成了这一组 Pod 的版本升级过程。

验证

  1. [root@k8s-master prome]# kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. dep02-78dbd944fc-69t8x 1/1 Running 0 11h
  4. dep02-78dbd944fc-7cn86 1/1 Running 0 11h
  5. [root@k8s-master prome]# kubectl exec -it dep02-78dbd944fc-69t8x /bin/bash
  6. root@dep02-78dbd944fc-69t8x:/# nginx -v
  7. nginx version: nginx/1.16.1
  8. root@dep02-78dbd944fc-69t8x:/# exit

滚动更的好处:

  • 在升级刚开始的时候,集群里只有 1 个新版本的 Pod。如果这时,新版本 Pod 有问题启动不起来,那么"滚动更新"就会停止,从而允许开发和运维人员介入。
  • 而在这个过程中,由于应用本身还有两个旧版本的 Pod 在线,所以服务并不会受到太大的影响。

二十一、版本回滚

1、查看版本历史

  1. [root@k8s-master prome]# kubectl rollout history deployment/dep02
  2. deployment.apps/dep02
  3. REVISION CHANGE-CAUSE
  4. 1 <none>
  5. 2 <none>

2、回滚到以前的旧版本

​ 把整个 Deployment 回滚到上一个版本:

  1. [root@k8s-master prome]# kubectl rollout undo deployment/dep02
  2. deployment.apps/dep02 rolled back

查看回滚状态

  1. [root@k8s-master prome]# kubectl rollout status deployment/dep02
  2. deployment "dep02" successfully rolled out

验证:

  1. [root@k8s-master prome]# kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. dep02-8594cd6447-pqtxk 1/1 Running 0 55s
  4. dep02-8594cd6447-tt4h4 1/1 Running 0 51s
  5. [root@k8s-master prome]# kubectl exec -it dep02-8594cd6447-tt4h4 /bin/bash
  6. root@dep02-8594cd6447-tt4h4:/# nginx -v
  7. nginx version: nginx/1.14.2

3、回滚到更早之前的版本

(1)使用 kubectl rollout history 命令查看每次 Deployment 变更对应的版本。

  1. [root@k8s-master prome]# kubectl rollout history deployment/dep02
  2. deployment.apps/dep02
  3. REVISION CHANGE-CAUSE
  4. 2 <none>
  5. 3 <none>

由于在创建这个 Deployment 的时候,指定了–record 参数,所以创建这些版本时执行的 kubectl 命令,都会被记录下来。

​ 查看每个版本对应的 Deployment 的 API 对象的细节:

  1. [root@k8s-master prome]# kubectl rollout history deployment/dep02 --revision=3
  2. deployment.apps/dep02 with revision #3
  3. Pod Template:
  4. Labels: app=web1
  5. pod-template-hash=8594cd6447
  6. Containers:
  7. testnginx9:
  8. Image: daocloud.io/library/nginx:1.14
  9. Port: 80/TCP
  10. Host Port: 0/TCP
  11. Environment: <none>
  12. Mounts: <none>
  13. Volumes: <none>

(2)在 kubectl rollout undo 命令行最后,加上要回滚到的指定版本的版本号,就可以回滚到指定版本了。

  1. [root@k8s-master prome]# kubectl rollout undo deployment/dep02 --to-revision=2
  2. deployment.apps/dep02 rolled back

验证:

  1. [root@k8s-master prome]# kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. dep02-78dbd944fc-8nvxl 1/1 Running 0 86s
  4. dep02-78dbd944fc-sb9sj 1/1 Running 0 88s
  5. [root@k8s-master prome]# kubectl exec -it dep02-78dbd944fc-8nvxl /bin/bash
  6. root@dep02-78dbd944fc-8nvxl:/# nginx -v
  7. nginx version: nginx/1.16.1

 二十二、部署DASHBOARD应用

  1. 注意:最后部署成功之后,因为有5种方式访问dashboard:我们这里只使用Nodport方式访问
  2. 1. Nodport方式访问dashboard,service类型改为NodePort
  3. 2. loadbalacer方式,service类型改为loadbalacer
  4. 3. Ingress方式访问dashboard
  5. 4. API server方式访问 dashboard
  6. 5. kubectl proxy方式访问dashboard

1、准备yaml文件(可以自己去网上找,也可以自己编写,下面这个文件是我从网上找的,里边的内容已经修改好了,各位老铁可以直接使用)

  1. [root@k8s-master dashboard]# cat recommended.yaml
  2. # Copyright 2017 The Kubernetes Authors.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. apiVersion: v1
  16. kind: Namespace
  17. metadata:
  18. name: kubernetes-dashboard
  19. ---
  20. apiVersion: v1
  21. kind: ServiceAccount
  22. metadata:
  23. labels:
  24. k8s-app: kubernetes-dashboard
  25. name: kubernetes-dashboard
  26. namespace: kubernetes-dashboard
  27. ---
  28. kind: Service
  29. apiVersion: v1
  30. metadata:
  31. labels:
  32. k8s-app: kubernetes-dashboard
  33. name: kubernetes-dashboard
  34. namespace: kubernetes-dashboard
  35. spec:
  36. type: NodePort
  37. ports:
  38. - port: 443
  39. targetPort: 8443
  40. nodePort: 31000
  41. selector:
  42. k8s-app: kubernetes-dashboard
  43. ---
  44. apiVersion: v1
  45. kind: Secret
  46. metadata:
  47. labels:
  48. k8s-app: kubernetes-dashboard
  49. name: kubernetes-dashboard-certs
  50. namespace: kubernetes-dashboard
  51. type: Opaque
  52. ---
  53. apiVersion: v1
  54. kind: Secret
  55. metadata:
  56. labels:
  57. k8s-app: kubernetes-dashboard
  58. name: kubernetes-dashboard-csrf
  59. namespace: kubernetes-dashboard
  60. type: Opaque
  61. data:
  62. csrf: ""
  63. ---
  64. apiVersion: v1
  65. kind: Secret
  66. metadata:
  67. labels:
  68. k8s-app: kubernetes-dashboard
  69. name: kubernetes-dashboard-key-holder
  70. namespace: kubernetes-dashboard
  71. type: Opaque
  72. ---
  73. kind: ConfigMap
  74. apiVersion: v1
  75. metadata:
  76. labels:
  77. k8s-app: kubernetes-dashboard
  78. name: kubernetes-dashboard-settings
  79. namespace: kubernetes-dashboard
  80. ---
  81. kind: Role
  82. apiVersion: rbac.authorization.k8s.io/v1
  83. metadata:
  84. labels:
  85. k8s-app: kubernetes-dashboard
  86. name: kubernetes-dashboard
  87. namespace: kubernetes-dashboard
  88. rules:
  89. # Allow Dashboard to get, update and delete Dashboard exclusive secrets.
  90. - apiGroups: [""]
  91. resources: ["secrets"]
  92. resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs", "kubernetes-dashboard-csrf"]
  93. verbs: ["get", "update", "delete"]
  94. # Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map.
  95. - apiGroups: [""]
  96. resources: ["configmaps"]
  97. resourceNames: ["kubernetes-dashboard-settings"]
  98. verbs: ["get", "update"]
  99. # Allow Dashboard to get metrics.
  100. - apiGroups: [""]
  101. resources: ["services"]
  102. resourceNames: ["heapster", "dashboard-metrics-scraper"]
  103. verbs: ["proxy"]
  104. - apiGroups: [""]
  105. resources: ["services/proxy"]
  106. resourceNames: ["heapster", "http:heapster:", "https:heapster:", "dashboard-metrics-scraper", "http:dashboard-metrics-scraper"]
  107. verbs: ["get"]
  108. ---
  109. kind: ClusterRole
  110. apiVersion: rbac.authorization.k8s.io/v1
  111. metadata:
  112. labels:
  113. k8s-app: kubernetes-dashboard
  114. name: kubernetes-dashboard
  115. rules:
  116. # Allow Metrics Scraper to get metrics from the Metrics server
  117. - apiGroups: ["metrics.k8s.io"]
  118. resources: ["pods", "nodes"]
  119. verbs: ["get", "list", "watch"]
  120. ---
  121. apiVersion: rbac.authorization.k8s.io/v1
  122. kind: RoleBinding
  123. metadata:
  124. labels:
  125. k8s-app: kubernetes-dashboard
  126. name: kubernetes-dashboard
  127. namespace: kubernetes-dashboard
  128. roleRef:
  129. apiGroup: rbac.authorization.k8s.io
  130. kind: Role
  131. name: kubernetes-dashboard
  132. subjects:
  133. - kind: ServiceAccount
  134. name: kubernetes-dashboard
  135. namespace: kubernetes-dashboard
  136. ---
  137. apiVersion: rbac.authorization.k8s.io/v1
  138. kind: ClusterRoleBinding
  139. metadata:
  140. name: kubernetes-dashboard
  141. roleRef:
  142. apiGroup: rbac.authorization.k8s.io
  143. kind: ClusterRole
  144. name: kubernetes-dashboard
  145. subjects:
  146. - kind: ServiceAccount
  147. name: kubernetes-dashboard
  148. namespace: kubernetes-dashboard
  149. ---
  150. kind: Deployment
  151. apiVersion: apps/v1
  152. metadata:
  153. labels:
  154. k8s-app: kubernetes-dashboard
  155. name: kubernetes-dashboard
  156. namespace: kubernetes-dashboard
  157. spec:
  158. replicas: 1
  159. revisionHistoryLimit: 10
  160. selector:
  161. matchLabels:
  162. k8s-app: kubernetes-dashboard
  163. template:
  164. metadata:
  165. labels:
  166. k8s-app: kubernetes-dashboard
  167. spec:
  168. containers:
  169. - name: kubernetes-dashboard
  170. image: kubernetesui/dashboard:v2.4.0
  171. imagePullPolicy: Always
  172. ports:
  173. - containerPort: 8443
  174. protocol: TCP
  175. args:
  176. - --auto-generate-certificates
  177. - --namespace=kubernetes-dashboard
  178. # Uncomment the following line to manually specify Kubernetes API server Host
  179. # If not specified, Dashboard will attempt to auto discover the API server and connect
  180. # to it. Uncomment only if the default does not work.
  181. # - --apiserver-host=http://my-address:port
  182. volumeMounts:
  183. - name: kubernetes-dashboard-certs
  184. mountPath: /certs
  185. # Create on-disk volume to store exec logs
  186. - mountPath: /tmp
  187. name: tmp-volume
  188. livenessProbe:
  189. httpGet:
  190. scheme: HTTPS
  191. path: /
  192. port: 8443
  193. initialDelaySeconds: 30
  194. timeoutSeconds: 30
  195. securityContext:
  196. allowPrivilegeEscalation: false
  197. readOnlyRootFilesystem: true
  198. runAsUser: 1001
  199. runAsGroup: 2001
  200. volumes:
  201. - name: kubernetes-dashboard-certs
  202. secret:
  203. secretName: kubernetes-dashboard-certs
  204. - name: tmp-volume
  205. emptyDir: {}
  206. serviceAccountName: kubernetes-dashboard
  207. nodeSelector:
  208. "kubernetes.io/os": linux
  209. # Comment the following tolerations if Dashboard must not be deployed on master
  210. tolerations:
  211. - key: node-role.kubernetes.io/master
  212. effect: NoSchedule
  213. ---
  214. kind: Service
  215. apiVersion: v1
  216. metadata:
  217. labels:
  218. k8s-app: dashboard-metrics-scraper
  219. name: dashboard-metrics-scraper
  220. namespace: kubernetes-dashboard
  221. spec:
  222. ports:
  223. - port: 8000
  224. targetPort: 8000
  225. selector:
  226. k8s-app: dashboard-metrics-scraper
  227. ---
  228. kind: Deployment
  229. apiVersion: apps/v1
  230. metadata:
  231. labels:
  232. k8s-app: dashboard-metrics-scraper
  233. name: dashboard-metrics-scraper
  234. namespace: kubernetes-dashboard
  235. spec:
  236. replicas: 1
  237. revisionHistoryLimit: 10
  238. selector:
  239. matchLabels:
  240. k8s-app: dashboard-metrics-scraper
  241. template:
  242. metadata:
  243. labels:
  244. k8s-app: dashboard-metrics-scraper
  245. spec:
  246. securityContext:
  247. seccompProfile:
  248. type: RuntimeDefault
  249. containers:
  250. - name: dashboard-metrics-scraper
  251. image: kubernetesui/metrics-scraper:v1.0.7
  252. ports:
  253. - containerPort: 8000
  254. protocol: TCP
  255. livenessProbe:
  256. httpGet:
  257. scheme: HTTP
  258. path: /
  259. port: 8000
  260. initialDelaySeconds: 30
  261. timeoutSeconds: 30
  262. volumeMounts:
  263. - mountPath: /tmp
  264. name: tmp-volume
  265. securityContext:
  266. allowPrivilegeEscalation: false
  267. readOnlyRootFilesystem: true
  268. runAsUser: 1001
  269. runAsGroup: 2001
  270. serviceAccountName: kubernetes-dashboard
  271. nodeSelector:
  272. "kubernetes.io/os": linux
  273. # Comment the following tolerations if Dashboard must not be deployed on master
  274. tolerations:
  275. - key: node-role.kubernetes.io/master
  276. effect: NoSchedule
  277. volumes:
  278. - name: tmp-volume
  279. emptyDir: {}

2、下载镜像(这两个镜像需要从k8s官网上下载,所以直接下载肯定是下不下来的,需要利用一些fq软件,你懂得)

  1. 由于yaml配置文件中指定的镜像
  2. 三台机器都下载
  3. [root@k8s-master ~]# docker pull kubernetesui/dashboard:v2.4.0
  4. [root@k8s-master ~]# docker pull kubernetesui/metrics-scraper:v1.0.7

3、创建应用

  1. [root@k8s-master ~]# kubectl apply -f recommended.yaml
  2. namespace/kubernetes-dashboard unchanged
  3. serviceaccount/kubernetes-dashboard unchanged
  4. service/kubernetes-dashboard unchanged
  5. Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
  6. secret/kubernetes-dashboard-certs configured
  7. secret/kubernetes-dashboard-csrf configured
  8. secret/kubernetes-dashboard-key-holder unchanged
  9. configmap/kubernetes-dashboard-settings unchanged
  10. role.rbac.authorization.k8s.io/kubernetes-dashboard unchanged
  11. clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard unchanged
  12. rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard unchanged
  13. clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard unchanged
  14. deployment.apps/kubernetes-dashboard unchanged
  15. service/dashboard-metrics-scraper unchanged
  16. deployment.apps/dashboard-metrics-scraper unchanged

查看Pod 的状态为running说明dashboard已经部署成功:

  1. [root@k8s-master dashboard]# kubectl get pod -n kubernetes-dashboard -o wide | grep dashboard
  2. dashboard-metrics-scraper-57cbb8b86c-sjz9g 1/1 Running 0 8h 10.244.2.47 k8s-node2 <none> <none>
  3. kubernetes-dashboard-6b9b6c9d46-h5gwr 1/1 Running 0 8h 10.244.2.48 k8s-node2 <none>

Dashboard 会在 kube-system namespace 中创建自己的 Deployment 和 Service:

  1. [root@k8s-master dashboard]# kubectl get deployment kubernetes-dashboard -n kubernetes-dashboard
  2. NAME READY UP-TO-DATE AVAILABLE AGE
  3. kubernetes-dashboard 1/1 1 1 8h
  1. [root@k8s-master dashboard]# kubectl get service kubernetes-dashboard -n kubernetes-dashboard
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. kubernetes-dashboard NodePort 10.109.96.224 <none> 443:31000/TCP 8h

4、访问dashboard

官方文档:https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#accessing-the-dashboard-ui

查看service,TYPE类型已经变为NodePort,端口为31000

  1. [root@k8s-master dashboard]# kubectl get service -n kube-system | grep dashboard
  2. dashboard-metrics-scraper ClusterIP 10.107.226.18 <none> 8000/TCP 8h
  3. kubernetes-dashboard NodePort 10.109.96.224 <none> 443:31000/TCP 8h

查看dashboard运行在那台机器上面

  1. [root@k8s-master dashboard]# kubectl get pods -n kube-system -o wide|grep dashboard
  2. dashboard-metrics-scraper-57cbb8b86c-sjz9g 1/1 Running 0 8h 10.244.2.47 k8s-node2 <none> <none>
  3. kubernetes-dashboard-6b9b6c9d46-h5gwr 1/1 Running 0 8h 10.244.2.48 k8s-node2 <none> <none>
  4. 在node2

浏览器访问:https://10.0.0.132:31000

如果访问失败,显示

这种情况是浏览器的安全策略导致的,可以更换双核浏览器,或者通过一下方式解决

生成证书密钥

[root@k8s-master dashboard]# openssl genrsa -out dashboard.key 2048 

使用刚才生成的密钥,创建证书申请文件

[root@k8s-master dashboard]# openssl req -new -out dashboard.csr -key dashboard.key -subj '/CN=10.0.0.130'

签发证书

  1. [root@k8s-master dashboard]# openssl x509 -req -in dashboard.csr -signkey dashboard.key -out dashboard.cr
  2. [root@k8s-master dashboard]# ls
  3. dashboard.crt dashboard.csr dashboard.key recommended.yaml

然后删除原有的secret,根据生成的证书创建新的secret

  1. [root@k8s-master dashboard]# kubectl delete secret kubernetes-dashboard-certs -n kube-system
  2. [root@k8s-master dashboard]# kubectl create secret generic kubernetes-dashboard-certs --from-file=dashboard.key --from-file=dashboard.crt -n kube-system

然后删除kubernetes-dashboard的pod,因为我们是用deployment创建的pod,删除这个pod后,deployment会立马生成一个pod,不能重新kubectl apply -f recommended.yaml,这样的话,之前创建的证书等于白做了。

  1. [root@k8s-master dashboard]# kubectl get pod -n kube-system|grep dashboard
  2. 查看kubernetes-dashboard的名字
  3. 删除之前的kubernetes-dashboard********
  4. [root@k8s-master dashboard]# kubectl delete pod kubernetes-dashboard-6b9b6c9d46-66lsx -n kube-system

然后在浏览去访问就可以看到正常的页面了

 选择使用token登录

 5、制作token

  1. [root@k8s-master dashboard]# cat a.yaml
  2. ---
  3. apiVersion: v1
  4. kind: ServiceAccount
  5. metadata:
  6. name: admin-user
  7. namespace: kube-system
  8. ---
  9. apiVersion: rbac.authorization.k8s.io/v1
  10. kind: ClusterRoleBinding
  11. metadata:
  12. name: admin-user
  13. roleRef:
  14. apiGroup: rbac.authorization.k8s.io
  15. kind: ClusterRole
  16. name: cluster-admin
  17. subjects:
  18. - kind: ServiceAccount
  19. name: admin-user
  20. namespace: kube-system

执行yaml文件

  1. [root@k8s-master ~]# kubectl create -f dashboard-adminuser.yaml
  2. serviceaccount/admin-user created
  3. clusterrolebinding.rbac.authorization.k8s.io/admin-user created

说明:上面创建了一个叫admin-user的服务账号,并放在kube-system命名空间下,并将cluster-admin角色绑定到admin-user账户,这样admin-user账户就有了管理员的权限。默认情况下,kubeadm创建集群时已经创建了cluster-admin角色,直接绑定即可。

6、查看admin-user的token

  1. [root@k8s-master dashboard]# kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')
  2. Name: admin-user-token-8kzjr
  3. Namespace: kube-system
  4. Labels: <none>
  5. Annotations: kubernetes.io/service-account.name: admin-user
  6. kubernetes.io/service-account.uid: d00f22a7-c6eb-4888-b591-6fbe3832b85c
  7. Type: kubernetes.io/service-account-token
  8. Data
  9. ====
  10. token: eyJhbGciOiJSUzI1NiIsImtpZCI6IjFxd3B3SF9JRkFsaHl4S0paSlh4Yy05NWw4bnhWQ1ZCMFB3TEFOTFRVZ3MifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLThrempyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJkMDBmMjJhNy1jNmViLTQ4ODgtYjU5MS02ZmJlMzgzMmI4NWMiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.FKl475PN8hMII50S8-OHDlJvNf_09s1fZZBQ-2-trfMPuktTRZrXP6HhE-8uC0eLkVXwlU8l4bwzBLz4m00U43tF1EemFzQ8NJnGfH_vbguTGjESjZBWVEaX-7X7jvS_7I9pELFheAaLotHl_BmFHhFPraGQb49e7Jcw0wHklntSErNb2Tf2INejO21RCOyASEUsF_5IzSC05gnSI-P0Sdt9ppn8MlfCSu-E9ELDTEXXiyqW7gYuOoWywNk1qvA0H0s0cz5Nw-QO9dtgbHhnUowob5ohZz0VeyJHJZSXshb65woq3M-yow9njxm0WPsCuYlkuuVVqV8tprfIyIrNPA
  11. ca.crt: 1066 bytes
  12. namespace: 11 bytes

把获取的token复制到登录界面的token输入框中

 7、使用dashboard

Dashboard 界面结构分为三个大的区域:

  1. 顶部操作区,在这里用户可以搜索集群中的资源、创建资源或退出。

  2. 左边导航菜单,通过导航菜单可以查看和管理集群中的各种资源。菜单项按照资源的层级分为两类:Cluster 级别的资源 ,Namespace 级别的资源 ,默认显示的是 default Namespace,可以进行切换

  3. 中间主体区,在导航菜单中点击了某类资源,中间主体区就会显示该资源所有实例,比如点击 Pods。

二十三、k8s持久化存储PV和PVC

1、PV和PVC的引入

  1. Volume 提供了非常好的数据持久化方案,不过在可管理性上还有不足。
  2. 拿前面 AWS EBS 的例子来说,要使用 Volume,Pod 必须事先知道如下信息:
  3. 当前 Volume 来自 AWS EBS。
  4. EBS Volume 已经提前创建,并且知道确切的 volume-id。
  5. Pod 通常是由应用的开发人员维护,而 Volume 则通常是由存储系统的管理员维护。开发人员要获得上面的信息:
  6. 要么询问管理员。
  7. 要么自己就是管理员。
  8. 这样就带来一个管理上的问题:应用开发人员和系统管理员的职责耦合在一起了。如果系统规模较小或者对于开发环境这样的情况还可以接受。但当集群规模变大,特别是对于生成环境,考虑到效率和安全性,这就成了必须要解决的问题。
  9. Kubernetes 给出的解决方案是 PersistentVolume 和 PersistentVolumeClaim。
  10. PersistentVolume (PV) 是外部存储系统中的一块存储空间,由管理员创建和维护。与 Volume 一样,PV 具有持久性,生命周期独立于 Pod。
  11. PersistentVolumeClaim (PVC) 是对 PV 的申请 (Claim)。PVC 通常由普通用户创建和维护。需要为 Pod 分配存储资源时,用户可以创建一个 PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,Kubernetes 会查找并提供满足条件的 PV。
  12. 有了 PersistentVolumeClaim,用户只需要告诉 Kubernetes 需要什么样的存储资源,而不必关心真正的空间从哪里分配,如何访问等底层细节信息。这些 Storage Provider 的底层信息交给管理员来处理,只有管理员才应该关心创建 PersistentVolume 的细节信息。

2、通过NFS实现持久化存储

下面这个事项相当于PV/PVC的静态供给,后面我们还会说一下PV/PVC的动态供给

1、所有节点安装nfs

yum install -y nfs-common nfs-utils

在master节点创建共享目录,并授权

  1. mkdir /nfsdata
  2. chmod 777 /nfsdata -R

编辑exports文件

  1. [root@k8s-master ~]# vim /etc/exports
  2. /nfsdata *(rw,no_root_squash,no_all_squash,sync)
  3. [root@k8s-master ~]# systemctl start rpcbind nfs

2、创建PV

  1. [root@k8s-master pv]# cat a.yaml
  2. apiVersion: v1
  3. kind: PersistentVolume
  4. metadata:
  5. name: mypv1
  6. spec:
  7. capacity:
  8. storage: 1Gi
  9. accessModes:
  10. - ReadWriteOnce
  11. persistentVolumeReclaimPolicy: Recycle
  12. storageClassName: nfs
  13. nfs:
  14. path: /nfsdata
  15. server: 10.0.0.130 #这个是nfs服务端的ip,这里我们的服务端放在了master节点上
  1. ① capacity 指定 PV 的容量为 1G。
  2. ② accessModes 指定访问模式为 ReadWriteOnce,支持的访问模式有:
  3. ReadWriteOnce – PV 能以 read-write 模式 mount 到单个节点。
  4. ReadOnlyMany – PV 能以 read-only 模式 mount 到多个节点。
  5. ReadWriteMany – PV 能以 read-write 模式 mount 到多个节点。
  6. ③ persistentVolumeReclaimPolicy 指定当 PV 的回收策略为 Recycle,支持的策略有:
  7. Retain – 需要管理员手工回收。
  8. Recycle – 清除 PV 中的数据,效果相当于执行 rm -rf /nfsdata/*
  9. Delete – 删除 Storage Provider 上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。
  10. ④ storageClassName 指定 PV 的 class 为 nfs。相当于为 PV 设置了一个分类,PVC 可以指定 class 申请相应 class 的 PV。 #如果没有指定分类的话,PVC会根据申请PV的大小来选择PV,如果有两个PV的大小是一样的,则会随机选取一个
  11. ⑤ 指定 PV 在 NFS 服务器上对应的目录。
  1. [root@k8s-master pv]# kubectl apply -f a.yaml
  2. persistentvolume/mypv1 created

 STATUSAvailable,表示 mypv1 就绪,可以被 PVC 申请。

3、创建pvc,PVC 就很简单了,只需要指定 PV 的容量,访问模式和 class。

  1. [root@k8s-master pv]# cat b.yaml
  2. apiVersion: v1
  3. kind: PersistentVolumeClaim
  4. metadata:
  5. name: mypvc1
  6. spec:
  7. accessModes:
  8. - ReadWriteOnce
  9. resources:
  10. requests:
  11. storage: 1Gi
  12. storageClassName: nfs
  1. [root@k8s-master pv]# kubectl apply -f b.yaml
  2. persistentvolumeclaim/mypvc1 created

4、创建POD

  1. [root@k8s-master pv]# cat c.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: mypod1
  6. labels:
  7. app: nginx5
  8. spec:
  9. containers:
  10. - name: mypod1
  11. image: daocloud.io/library/nginx:latest
  12. volumeMounts:
  13. - mountPath: "/usr/share/nginx/html"
  14. name: mydata
  15. volumes:
  16. - name: mydata
  17. persistentVolumeClaim:
  18. claimName: mypvc1
  19. ---
  20. apiVersion: v1
  21. kind: Service
  22. metadata:
  23. name: mysvc
  24. spec:
  25. type: NodePort
  26. selector:
  27. app: nginx5
  28. ports:
  29. - protocol: TCP
  30. port: 8082
  31. targetPort: 80
  32. nodePort: 30008
  33. 与使用普通 Volume 的格式类似,在 volumes 中通过 persistentVolumeClaim 指定使用 mypvc1 申请的 Volume。
  1. [root@k8s-master pv]# kubectl apply -f c.yaml
  2. pod/mypod1 created
  3. service/mysvc created

验证:

  1. [root@k8s-master pv]# kubectl exec -it mypod1 /bin/bash
  2. kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
  3. root@mypod1:/# echo "black silk" > /usr/share/nginx/html/index.html
  4. root@mypod1:/# touch /usr/share/nginx/html/a.txt
  5. root@mypod1:/# exec attach failed: error on attach stdin: read escape sequence
  6. command terminated with exit code 126
  7. [root@k8s-master pv]# ls /nfsdata/ #也可在nfs的共享目录中查看到,说明卷共享成功
  8. a.txt index.html
  9. 可见,在 Pod 中创建的文件 /usr/share/nginx/html 、a.txt 确实已经保存到了 NFS 服务器目录 /nfsdata中。
  10. 如果不再需要使用 PV,可用删除 PVC 回收 PV。
  11. 在这里,可以尝试在任何一方删除文件,文件在两端都会消失;
  12. [root@k8s-master pv]# kubectl get pod -o wide
  13. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  14. mypod1 1/1 Running 0 2m6s 10.244.1.58 k8s-node1 <none> <none>

 3、PV的回收

当 PV 不再需要时,可通过删除 PVC 回收。未删除pvc之前 pv的状态是Bound

步骤是先删除pod,在删除PVC,因为我们设置的策略是Recycle,删除完PVC后,共享目录中的数据就被回收了。

删除pod

  1. [root@k8s-master pv]# kubectl delete -f c.yaml
  2. pod "mypod1" deleted
  3. service "mysvc" deleted

 删除PVC

  1. [root@k8s-master pv]# kubectl delete -f b.yaml
  2. persistentvolumeclaim "mypvc1" deleted

验证数据是否被回收(可以看到数据已经被回收了),PV的状态也变为Available状态,此时解除绑定后则可以被新的PVC申请。

[root@k8s-master pv]# vim a.yaml

 但这可能不是我们想要的结果,如果我希望数据保留,可以将策略设置为Retain

[root@k8s-master pv]# vim a.yaml

  1. [root@k8s-master pv]# kubectl apply -f a.yaml
  2. persistentvolume/mypv1 configured
  3. 重新创建PVC和POD
  4. [root@k8s-master pv]# kubectl apply -f b.yaml
  5. persistentvolumeclaim/mypvc1 created
  6. [root@k8s-master pv]# kubectl apply -f c.yaml
  7. pod/mypod1 created
  8. service/mysvc created

 验证Retain策略

  1. [root@k8s-master pv]# kubectl exec -it mypod1 /bin/bash
  2. kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
  3. root@mypod1:/# ls /usr/share/nginx/html/
  4. root@mypod1:/# echo "Retain" > //usr/share/nginx/html/index.html
  5. [root@k8s-master pv]# cat /nfsdata/index.html
  6. Retain
  7. 删除pod和pvc
  8. [root@k8s-master pv]# kubectl delete -f c.yaml
  9. pod "mypod1" deleted
  10. service "mysvc" deleted
  11. [root@k8s-master pv]# kubectl delete -f b.yaml
  12. persistentvolumeclaim "mypvc1" deleted
  13. [root@k8s-master pv]# cat /nfsdata/index.html
  14. Retain #发现数据还保留着

虽然 mypv1 中的数据得到了保留,但其 PV 状态会一直处于 Released,不能被其他 PVC 申请。为了重新使用存储资源,可以删除并重新创建 mypv1。删除操作只是删除了 PV 对象,存储空间中的数据并不会被删除

  1. [root@k8s-master pv]# kubectl delete -f a.yaml
  2. persistentvolume "mypv1" deleted
  3. [root@k8s-master pv]# cat /nfsdata/index.html
  4. Retain #删除PV后数据还在
  5. 重新创建一下PV,查看其状态
  6. [root@k8s-master pv]# kubectl apply -f a.yaml
  7. persistentvolume/mypv1 created

 新建的 mypv1 状态为 Available,已经可以被 PVC 申请。

PV 还支持 Delete 的回收策略,会删除 PV 在 Storage Provider 上对应存储空间。NFS 的 PV 不支持 Delete,支持 Delete 的 Provider 有 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。

4、PV&PVC在应用在Mysql的持久化存储实战项目

创建PV和PVC

  1. [root@k8s-master mysqlpv]# vim a.yaml
  2. apiVersion: v1
  3. kind: PersistentVolume
  4. metadata:
  5. name: mysql-pv
  6. spec:
  7. capacity:
  8. storage: 1Gi
  9. accessModes:
  10. - ReadWriteOnce
  11. persistentVolumeReclaimPolicy: Retain
  12. storageClassName: nfs
  13. nfs:
  14. path: /nfsdata/mysql-pv
  15. server: 10.0.0.130
  16. [root@k8s-master mysqlpv]# vim b.yaml
  17. apiVersion: v1
  18. kind: PersistentVolumeClaim
  19. metadata:
  20. name: mysql-pvc
  21. spec:
  22. accessModes:
  23. - ReadWriteOnce
  24. resources:
  25. requests:
  26. storage: 1Gi
  27. storageClassName: nfs
  28. [root@k8s-master mysqlpv]# vim c.yaml
  29. apiVersion: v1
  30. kind: Service
  31. metadata:
  32. name: mysql
  33. spec:
  34. ports:
  35. - port: 3306
  36. selector:
  37. app: mysql
  38. ---
  39. apiVersion: apps/v1
  40. kind: Deployment
  41. metadata:
  42. name: mysql
  43. spec:
  44. selector:
  45. matchLabels:
  46. app: mysql
  47. template:
  48. metadata:
  49. labels:
  50. app: mysql
  51. spec:
  52. containers:
  53. - image: daocloud.io/library/mysql:5.7.5-m15
  54. name: mysql
  55. env:
  56. - name: MYSQL_ROOT_PASSWORD
  57. value: password
  58. ports:
  59. - containerPort: 3306
  60. name: mysql
  61. volumeMounts:
  62. - name: mysql-persistent-storage
  63. mountPath: /var/lib/mysql
  64. volumes:
  65. - name: mysql-persistent-storage
  66. persistentVolumeClaim:
  67. claimName: mysql-pvc
  68. [root@k8s-master mysqlpv]# kubectl apply -f a.yaml
  69. [root@k8s-master mysqlpv]# kubectl apply -f b.yaml
  70. [root@k8s-master mysqlpv]# kubectl apply -f c.yaml
  71. [root@k8s-master mysqlpv]# kubectl get pod
  72. NAME READY STATUS RESTARTS AGE
  73. mysql-6b64cbd988-gs62v 1/1 Running 1 3m55s
  74. [root@k8s-master mysqlpv]# kubectl exec -it mysql-6b64cbd988-gs62v /bin/sh
  75. # mysql -uroot -p'password'
  76. mysql> create database aaa;

  1. [root@k8s-master mysqlpv]# kubectl get pod -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  3. mysql-6b64cbd988-gs62v 1/1 Running 1 6m11s 10.244.1.80 k8s-node1 <none>

关闭 k8s-node1,模拟节点宕机故障。

由于node1节点已经宕机,node2节点接管了这个任务,pod转移,需要等待一段时间,我这里等待了8分钟左右。

 从新进入pod,数据依然存在,持久化成功。很安全

 5、PV的动态供给

前面的例子中,我们提前创建了 PV,然后通过 PVC 申请 PV 并在 Pod 中使用,这种方式叫做静态供给(Static Provision)。

与之对应的是动态供给(Dynamical Provision),即如果没有满足 PVC 条件的 PV,会动态创建 PV。相比静态供给,动态供给有明显的优势:不需要提前创建 PV,减少了管理员的工作量,效率高。

动态供给是通过 StorageClass 实现的,StorageClass 定义了如何创建 PV

Dynamic Provisioning机制工作的核心在于StorageClass的API对象。

StorageClass声明存储插件,用于自动创建PV

当我们k8s业务上来的时候,大量的pvc,此时我们人工创建匹配的话,工作量就会非常大了,需要动态的自动挂载相应的存储

我们需要使用到StorageClass,来对接存储,靠他来自动关联pvc,并创建pv。

Kubernetes支持动态供给的存储插件:https://kubernetes.io/docs/concepts/storage/storage-classes/
因为NFS不支持动态存储,所以我们需要借用这个存储插件。
nfs动态相关部署可以参考:
https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client/deploy
部署步骤:

  1. 主节点配置nfs服务端
  2. [root@k8s-master pvc-test]# mkdir /opt/container_data
  3. [root@k8s-master pvc-test]# chmod 777 -R /opt/container_data
  4. [root@k8s-master pvc-test]# cat /etc/exports
  5. /opt/container_data *(rw,no_root_squash,no_all_squash,sync)
  6. [root@k8s-master pvc-test]# systemctl start rpcbind
  7. [root@k8s-master pvc-test]# systemctl start nfs

 1、定义一个storage

  1. [root@k8s-master pvc-test]# cat storageclass-nfs.yaml
  2. apiVersion: storage.k8s.io/v1
  3. kind: StorageClass
  4. metadata:
  5. name: managed-nfs-storage
  6. provisioner: fuseim.pri/ifs

2、部署授权

因为storage自动创建pv需要经过kube-apiserver,所以要进行授权

创建1个sa(serviceaccount)

创建1个clusterrole,并赋予应该具有的权限,比如对于一些基本api资源的增删改查;

创建1个clusterrolebinding,将sa和clusterrole绑定到一起;这样sa就有权限了;

然后pod中再使用这个sa,那么pod再创建的时候,会用到sa,sa具有创建pv的权限,便可以自动创建pv;

  1. [root@k8s-master pvc-test]# cat rbac.yaml
  2. apiVersion: v1
  3. kind: ServiceAccount
  4. metadata:
  5. name: nfs-client-provisioner
  6. ---
  7. kind: ClusterRole
  8. apiVersion: rbac.authorization.k8s.io/v1
  9. metadata:
  10. name: nfs-client-provisioner-runner
  11. rules:
  12. - apiGroups: [""]
  13. resources: ["persistentvolumes"]
  14. verbs: ["get", "list", "watch", "create", "delete"]
  15. - apiGroups: [""]
  16. resources: ["persistentvolumeclaims"]
  17. verbs: ["get", "list", "watch", "update"]
  18. - apiGroups: ["storage.k8s.io"]
  19. resources: ["storageclasses"]
  20. verbs: ["get", "list", "watch"]
  21. - apiGroups: [""]
  22. resources: ["events"]
  23. verbs: ["list", "watch", "create", "update", "patch"]
  24. ---
  25. kind: ClusterRoleBinding
  26. apiVersion: rbac.authorization.k8s.io/v1
  27. metadata:
  28. name: run-nfs-client-provisioner
  29. subjects:
  30. - kind: ServiceAccount
  31. name: nfs-client-provisioner
  32. namespace: default
  33. roleRef:
  34. kind: ClusterRole
  35. name: nfs-client-provisioner-runner
  36. apiGroup: rbac.authorization.k8s.io

3、部署一个自动创建PV的服务

 这里自动创建pv的服务由nfs-client-provisioner 完成

  1. [root@k8s-master pvc-test]# cat deployment-nfs.yaml
  2. kind: Deployment
  3. apiVersion: apps/v1
  4. metadata:
  5. name: nfs-client-provisioner
  6. spec:
  7. selector:
  8. matchLabels:
  9. app: nfs-client-provisioner
  10. replicas: 1
  11. strategy:
  12. type: Recreate
  13. template:
  14. metadata:
  15. labels:
  16. app: nfs-client-provisioner
  17. spec:
  18. serviceAccount: nfs-client-provisioner
  19. containers:
  20. - name: nfs-client-provisioner
  21. image: lizhenliang/nfs-client-provisioner:v2.0.0
  22. volumeMounts:
  23. - name: nfs-client-root
  24. mountPath: /persistentvolumes
  25. env:
  26. - name: PROVISIONER_NAME
  27. #这个值是定义storage里面的那个值
  28. value: fuseim.pri/ifs
  29. - name: NFS_SERVER
  30. value: 172.17.0.21
  31. - name: NFS_PATH
  32. value: /opt/container_data
  33. volumes:
  34. - name: nfs-client-root
  35. nfs:
  36. server: 172.17.0.21
  37. path: /opt/container_data
  38. 参数解释:
  39. strategy:
  40. type: Recreate
  41. ==========================================================================================
  42. Recreate:设置spec.strategy.type=Recreate,该策略下将杀掉正在运行的Pod,然后创建新的。
  43. RollingUpdate:设置spec.strategy.type=RollingUpdate,滚动更新,即逐渐减少旧Pod的同时逐渐增加新Pod。
  44. 其中默认的RollingUpdate滚动更新策略的“边删除边更新”保证了在更新期间的服务可用性,在使用这个策略时,有两个可定义参数:
  45. spec.strategy.RollingUpdate.maxUnavailable:更新过程中Pod数量可以低于Pod期望副本的数量或百分比(默认25%)
  46. spec.strategy.RollingUpdate.maxSurge:更新过程中Pod数量可以超过Pod期望副本的数量或百分比(默认25%)

 创建:

  1. [root@k8s-master pvc-test]# kubectl apply -f storageclass-nfs.yaml
  2. [root@k8s-master pvc-test]# kubectl apply -f rbac.yaml
  3. [root@k8s-master pvc-test]# kubectl apply -f deployment-nfs.yaml

 查看创建好的storage

[root@k8s-master storage]# kubectl get sc

 nfs-client-provisioner 会以pod运行在k8s中,

  1. [root@k8s-master pvc-test]# kubectl get pod
  2. NAME READY STATUS RESTARTS AGE
  3. nfs-client-provisioner-c977976db-87ddb 1/1 Running 0 41s

 4、部署有状态服务,测试自动创建pv
部署yaml文件参考:https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/
我们部署一个nginx服务,让其html下面自动挂载数据卷

  1. [root@k8s-master pvc-test]# cat nginx.yaml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: nginx
  6. labels:
  7. app: nginx
  8. spec:
  9. ports:
  10. - port: 80
  11. name: web
  12. clusterIP: None
  13. selector:
  14. app: nginx
  15. ---
  16. apiVersion: apps/v1
  17. kind: StatefulSet
  18. metadata:
  19. name: web
  20. spec:
  21. serviceName: "nginx"
  22. replicas: 2
  23. selector:
  24. matchLabels:
  25. app: nginx
  26. template:
  27. metadata:
  28. labels:
  29. app: nginx
  30. spec:
  31. containers:
  32. - name: nginx
  33. image: nginx
  34. ports:
  35. - containerPort: 80
  36. name: web
  37. volumeMounts:
  38. - name: www
  39. mountPath: /usr/share/nginx/html
  40. volumeClaimTemplates:
  41. - metadata:
  42. name: www
  43. spec:
  44. accessModes: [ "ReadWriteOnce" ]
  45. storageClassName: "managed-nfs-storage"
  46. resources:
  47. requests:
  48. storage: 1Gi
  49. [root@k8s-master pvc-test]# kubectl apply -f nginx.yaml

  进入其中一个容器,创建一个文件:

  1. [root@k8s-master pvc-test]#
  2. [root@k8s-master pvc-test]# kubectl exec -it web-0 /bin/sh
  3. kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
  4. # cd /usr/share/nginx/html
  5. # touch 1.txt

 直接在web-1的目录下,创建一个文件:

[root@k8s-master pvc-test]# touch /opt/container_data/default-www-web-1-pvc-20f70001-2ca3-43c6-a12e-4d9195e54880/2.txt

 而且,删除一个pod   web-0,数据仍然存在,不会丢失。保证了数据持久化;

二十四、k8s控制器

1、什么是控制器

kubernetes中内建了很多controller(控制器),这些相当于一个状态机,用来控制pod的具体状态和行为。

上文中我们使用过Deployment、RC(ReplicationController)和StatefulSet,这些都是k8s的控制器

  1. 部分控制器类型如下:
  2. ReplicationController 和 ReplicaSet
  3. Deployment
  4. DaemonSet
  5. StatefulSet
  6. Job/CronJob
  7. HorizontalPodAutoscaler

2、DaemonSet控制器

  1. DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。当有节点加入集群时,会为他们新增一个 Pod。当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。
  2. DaemonSet 的一些典型用法:
  3. 在每个节点上运行集群存储 DaemonSet,例如 glusterd、ceph。
  4. 在每个节点上运行日志收集 DaemonSet,例如 fluentd、logstash。
  5. 在每个节点上运行监控 DaemonSet,例如 Prometheus Node Exporter、Flowmill、Sysdig 代理、collectd、Dynatrace OneAgent、AppDynamics 代理、Datadog 代理、New Relic 代理、Ganglia gmond 或者 Instana 代理。
  6. 一个简单的用法是在所有的节点上都启动一个 DaemonSet,并作为每种类型的 daemon 使用。
  7. 一个稍微复杂的用法是单独对每种 daemon 类型使用一种DaemonSet。这样有多个 DaemonSet,但具有不同的标识,并且对不同硬件类型具有不同的内存、CPU 要求。

备注:DaemonSet 中的 Pod 可以使用 hostPort,从而可以通过节点 IP 访问到 Pod;因为DaemonSet模式下Pod不会被调度到其他节点。使用示例如下:

  1. ports:
  2. - name: httpd
  3. containerPort: 80
  4. #除非绝对必要,否则不要为 Pod 指定 hostPort。 将 Pod 绑定到hostPort时,它会限制 Pod 可以调度的位置数;DaemonSet除外
  5. #一般情况下 containerPort与hostPort值相同
  6. hostPort: 8090 #可以通过宿主机+hostPort的方式访问该Pod。例如:pod在/调度到了k8s-node0210.0.0.132】,那么该Pod可以通过10.0.0.132:8090方式进行访问。
  7. protocol: TCP

举个栗子:

1、创建DaemonSet

DaemonSet的描述文件和Deployment非常相似,只需要修改Kind,并去掉副本数量的配置即可,当然,我们这里的pod运行的是nginx,作为案例;

  1. [root@k8s-master daemonset]# cat nginx-daemonset.yml
  2. apiVersion: apps/v1
  3. kind: DaemonSet
  4. metadata:
  5. name: nginx-daemonset
  6. labels:
  7. app: nginx
  8. spec:
  9. selector:
  10. matchLabels:
  11. app: nginx
  12. template:
  13. metadata:
  14. labels:
  15. app: nginx
  16. spec:
  17. containers:
  18. - name: nginx
  19. image: daocloud.io/library/nginx:latest
  20. ports:
  21. - name: nginx
  22. containerPort: 80
  23. hostPort: 8090 #使用的hostPort 相当于docker的-p 端口映射
  24. protocol: TCP

2、测试效果

用宿主机的ip+8090端口,即可访问到:

 每个node节点上都有一个

 尝试删除,也会重建

 3、StatefulSet控制器

StatefulSet 是用来管理有状态应用的工作负载 API 对象。

StatefulSet 中的 Pod 拥有一个具有黏性的、独一无二的身份标识。这个标识基于 StatefulSet 控制器分配给每个 Pod 的唯一顺序索引。Pod 的名称的形式为- 。例如:web的StatefulSet 拥有两个副本,所以它创建了两个 Pod:web-0和web-1。

和 Deployment 相同的是,StatefulSet 管理了基于相同容器定义的一组 Pod。但和 Deployment 不同的是,StatefulSet 为它们的每个 Pod 维护了一个固定的 ID。这些 Pod 是基于相同的声明来创建的,但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。

【使用场景】StatefulSets 对于需要满足以下一个或多个需求的应用程序很有价值:

  1. 稳定的、唯一的网络标识符,即Pod重新调度后其PodName和HostName不变【当然IP是会变的】
  2. 稳定的、持久的存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC实现
  3. 有序的、优雅的部署和缩放
  4. 有序的、自动的滚动更新
    如上面,稳定意味着 Pod 调度或重调度的整个过程是有持久性的。

如果应用程序不需要任何稳定的标识符或有序的部署、删除或伸缩,则应该使用由一组无状态的副本控制器提供的工作负载来部署应用程序,比如使用 Deployment 或者 ReplicaSet 可能更适用于无状态应用部署需要。

限制:

给定 Pod 的存储必须由 PersistentVolume 驱动 基于所请求的 storage class 来提供,或者由管理员预先提供。

删除或者收缩 StatefulSet 并不会删除它关联的存储卷。这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。

StatefulSet 当前需要 headless 服务 来负责 Pod 的网络标识。你需要负责创建此服务。
当删除 StatefulSets 时,StatefulSet 不提供任何终止 Pod 的保证。为了实现 StatefulSet 中的 Pod 可以有序和优雅的终止,可以在删除之前将 StatefulSet 缩放为 0。

在默认 Pod 管理策略(OrderedReady) 时使用滚动更新,可能进入需要人工干预才能修复的损坏状态。

有序索引

对于具有 N 个副本的 StatefulSet,StatefulSet 中的每个 Pod 将被分配一个整数序号,从 0 到 N-1,该序号在 StatefulSet 上是唯一的。

StatefulSet 中的每个 Pod 根据 StatefulSet 中的名称和 Pod 的序号来派生出它的主机名。组合主机名的格式为(StatefulSet名称)—(序号)。

部署和扩缩保证

对于包含 N 个 副本的 StatefulSet,当部署 Pod 时,它们是依次创建的,顺序为 0~(N-1)。
当删除 Pod 时,它们是逆序终止的,顺序为 (N-1)~0。

在将缩放操作应用到 Pod 之前,它前面的所有 Pod 必须是 Running 和 Ready 状态。

在 Pod 终止之前,所有的继任者必须完全关闭。

StatefulSet 不应将 pod.Spec.TerminationGracePeriodSeconds 设置为 0。这种做法是不安全的,要强烈阻止。

部署顺序

在下面的 nginx 示例被创建后,会按照 web-0、web-1、web-2 的顺序部署三个 Pod。在 web-0 进入 Running 和 Ready 状态前不会部署 web-1。在 web-1 进入 Running 和 Ready 状态前不会部署 web-2。

如果 web-1 已经处于 Running 和 Ready 状态,而 web-2 尚未部署,在此期间发生了 web-0 运行失败,那么 web-2 将不会被部署,要等到 web-0 部署完成并进入 Running 和 Ready 状态后,才会部署 web-2。

收缩顺序

如果想将示例中的 StatefulSet 收缩为 replicas=1,首先被终止的是 web-2。在 web-2 没有被完全停止和删除前,web-1 不会被终止。当 web-2 已被终止和删除;但web-1 尚未被终止,如果在此期间发生 web-0 运行失败,那么就不会终止 web-1,必须等到 web-0 进入 Running 和 Ready 状态后才会终止 web-1。

下面这个案例跟上文中的动态PV供给是一样的,只是后来我们验证的东西不一样。

  1. 我把所有的内容写到一个文件中,各位兄弟可以不看这个文件,因为跟上文中的一样,可以直接看后边的验证思路
  2. [root@k8s-master sts]# cat a.yaml
  3. apiVersion: storage.k8s.io/v1
  4. kind: StorageClass
  5. metadata:
  6. name: managed-nfs-storage
  7. provisioner: fuseim.pri/ifs
  8. ---
  9. apiVersion: v1
  10. kind: ServiceAccount
  11. metadata:
  12. name: nfs-client-provisioner
  13. ---
  14. apiVersion: rbac.authorization.k8s.io/v1
  15. kind: ClusterRole
  16. metadata:
  17. name: nfs-client-provisioner-runner
  18. rules:
  19. - apiGroups: [""]
  20. resources: ["persistentvolumes"]
  21. verbs: ["get","list","watch","create","delete"]
  22. - apiGroups: [""]
  23. resources: ["persistentvolumeclaims"]
  24. verbs: ["get", "list", "watch", "update"]
  25. - apiGroups: ["storage.k8s.io"]
  26. resources: ["storageclasses"]
  27. verbs: ["get", "list", "watch"]
  28. - apiGroups: [""]
  29. resources: ["events"]
  30. verbs: ["list", "watch", "create", "update", "patch"]
  31. ---
  32. apiVersion: rbac.authorization.k8s.io/v1
  33. kind: ClusterRoleBinding
  34. metadata:
  35. name: run-nfs-client-provisioner
  36. subjects:
  37. - kind: ServiceAccount
  38. name: nfs-client-provisioner
  39. namespace: default
  40. roleRef:
  41. kind: ClusterRole
  42. name: nfs-client-provisioner-runner
  43. apiGroup: rbac.authorization.k8s.io
  44. ---
  45. apiVersion: apps/v1
  46. kind: Deployment
  47. metadata:
  48. name: nfs-client-provisioner
  49. spec:
  50. selector:
  51. matchLabels:
  52. app: nfs-client-provisioner
  53. replicas: 1
  54. strategy:
  55. type: Recreate
  56. template:
  57. metadata:
  58. labels:
  59. app: nfs-client-provisioner
  60. spec:
  61. serviceAccount: nfs-client-provisioner
  62. containers:
  63. - name: nfs-client-provisioner
  64. image: lizhenliang/nfs-client-provisioner:v2.0.0
  65. volumeMounts:
  66. - name: nfs-client-root
  67. mountPath: /persistentvolumes
  68. env:
  69. - name: PROVISIONER_NAME
  70. value: fuseim.pri/ifs
  71. - name: NFS_SERVER
  72. value: 10.0.0.130
  73. - name: NFS_PATH
  74. value: /opt/container_data
  75. volumes:
  76. - name: nfs-client-root
  77. nfs:
  78. server: 10.0.0.130
  79. path: /opt/container_data
  80. ---
  81. apiVersion: v1
  82. kind: Service
  83. metadata:
  84. name: nginx
  85. labels:
  86. app: nginx
  87. spec:
  88. ports:
  89. - port: 80
  90. name: web
  91. clusterIP: None
  92. selector:
  93. app: nginx
  94. ---
  95. apiVersion: apps/v1
  96. kind: StatefulSet
  97. metadata:
  98. name: web
  99. spec:
  100. serviceName: "nginx"
  101. replicas: 2
  102. selector:
  103. matchLabels:
  104. app: nginx
  105. template:
  106. metadata:
  107. labels:
  108. app: nginx
  109. spec:
  110. containers:
  111. - name: nginx
  112. image: daocloud.io/library/nginx:latest
  113. ports:
  114. - containerPort: 80
  115. name: web
  116. volumeMounts:
  117. - name: www
  118. mountPath: /usr/share/nginx/html
  119. volumeClaimTemplates:
  120. - metadata:
  121. name: www
  122. spec:
  123. accessModes: [ "ReadWriteOnce" ]
  124. storageClassName: "managed-nfs-storage"
  125. resources:
  126. requests:
  127. storage: 1Gi
  128. [root@k8s-master sts]# kubectl apply -f a.yaml
  129. storageclass.storage.k8s.io/managed-nfs-storage created
  130. serviceaccount/nfs-client-provisioner created
  131. clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
  132. clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
  133. deployment.apps/nfs-client-provisioner created
  134. service/nginx created
  135. statefulset.apps/web created

验证解析

每个 Pod 都拥有一个基于其顺序索引的稳定的主机名

使用 kubectl run 运行一个提供 nslookup 命令的容器,该命令来自于 dnsutils 包。通过对 Pod 的主机名执行 nslookup,你可以检查他们在集群内部的 DNS 地址

  1. [root@k8s-master sts]# cat pod.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: testnginx
  6. spec:
  7. containers:
  8. - name: testnginx
  9. image: daocloud.io/library/nginx:1.12.0-alpine
  10. [root@k8s-master sts]# kubectl apply -f pod.yaml
  11. [root@k8s-master sts]# kubectl exec -it testnginx /bin/sh

重启pod会发现,pod中的ip已经发生变化,但是pod的名称并没有发生变化;这就是为什么不要在其他应用中使用 StatefulSet 中的 Pod 的 IP 地址进行连接,这点很重要

  1. [root@master pvc-test]# kubectl delete pod -l app=nginx
  2. pod "web-0" deleted
  3. pod "web-1" deleted
  4. [root@master pvc-test]# kubectl get pod -o wide

写入稳定的存储

将 Pod 的主机名写入它们的index.html文件并验证 NGINX web 服务器使用该主机名提供服务

  1. [root@k8s-master sts]# kubectl exec -it web-0 /bin/sh
  2. # cd /usr/share/nginx/html
  3. # echo youngfit-1 > index.html
  4. [root@k8s-master sts]# kubectl exec -it web-1 /bin/sh
  5. # cd /usr/share/nginx/html
  6. # echo youngfit-2 > index.html
  7. [root@k8s-master sts]# ls /opt/container_data/default-www-web-0-pvc-ae99bd8d-a337-458d-a178-928cf4602713/
  8. index.html
  9. [root@k8s-master sts]# ls /opt/container_data/default-www-web-1-pvc-afac76ea-9faf-41ac-b03d-7ffc9e277029/
  10. index.html

  1. [root@k8s-master sts]# curl 10.244.4.5
  2. youngfit-1
  3. [root@k8s-master sts]# curl 10.244.1.4
  4. youngfit-2
  5. 再次删除
  6. [root@k8s-master sts]# kubectl delete pod -l app=nginx
  7. pod "web-0" deleted
  8. pod "web-1" deleted
  9. [root@k8s-master sts]# kubectl apply -f nginx.yaml
  10. [root@k8s-master sts]# kubectl get pod
  11. NAME READY STATUS RESTARTS AGE
  12. nfs-client-provisioner-56cc44bd5-2hgxc 1/1 Running 0 27m
  13. testnginx 1/1 Running 0 6m20s
  14. web-0 1/1 Running 0 13s
  15. web-1 1/1 Running 0 6s

再次查看

扩容/缩容 StatefulSet

扩容/缩容StatefulSet 指增加或减少它的副本数。这通过更新replicas字段完成。你可以使用kubectl scale 或者kubectl patch来扩容/缩容一个 StatefulSet。

二十五、基于k8s集群的redis-cluster集群

实验思路:需要提前准备好nfs存储,然后制作动态存储storageclass与nfs关联,制作动态存储就需要权限,所以需要serviceaccount和clusterrole以及clusterrolebinding

,需要将redis的配置文件导入到pod中,所以需要configmap,然后就需要statefulset来运行redis实例了,之后我们需要一个redis-tribe工具初始换redis-cluster集群,之后就可以进行各种验证了。

1、提前准备好nfs存储

  1. [root@k8s-master ~]# yum -y install nfs-utils nfs-common rpcbind
  2. [root@k8s-master ~]# mkdir /data/nfs
  3. [root@k8s-master ~]# chmod -R 777 /data/nfs
  4. [root@k8s-master ~]# vim /etc/exports
  5. /data/nfs *(rw,no_root_squash,sync,no_all_squash)
  6. [root@k8s-master ~]# systemctl start rpcbind nfs
  7. [root@k8s-master ~]# systemctl status nfs
  8. 其余节点下载nfs客户端,确保可以挂载
  9. # yum -y install nfs-utils nfs-common
  10. 下载好之后,可以尝试用挂载命令试试,能否正常挂载

2、制作动态存储

  1. 可以使用helm工具,也可以写yaml文件创建
  2. helm方式:
  3. [root@k8s-master ~]# helm install stable/nfs-client-provisioner --set nfs.server=192.168.153.148 --set nfs.path=/data/nfs
  4. [root@k8s-master ~]# helm list
  5. 配置文件方式
  6. 需要先定义一个storageclass
  7. [root@k8s-master redis]# cat sc.yaml
  8. apiVersion: storage.k8s.io/v1
  9. kind: StorageClass
  10. metadata:
  11. labels:
  12. app: redis
  13. appCluster: redis-cluster
  14. name: nfs-client
  15. provisioner: fuseim.pri/ifs
  16. [root@k8s-master redis]# kubectl apply -f sc.yaml
  17. 然后我们创建一个自动创建pv的服务,这个服务需要关联一个serviceaccount
  18. [root@k8s-master redis]# cat sa.yaml
  19. apiVersion: v1
  20. kind: ServiceAccount
  21. metadata:
  22. name: nfs-client-provisioner
  23. [root@k8s-master redis]# kubectl apply -f sa.yaml
  24. [root@k8s-master redis]# cat deploy.yaml
  25. ---
  26. apiVersion: apps/v1
  27. kind: Deployment
  28. metadata:
  29. name: nfs-client-provisioner
  30. spec:
  31. selector:
  32. matchLabels:
  33. app: nfs-client-provisioner
  34. replicas: 1
  35. strategy:
  36. type: Recreate
  37. template:
  38. metadata:
  39. labels:
  40. app: nfs-client-provisioner
  41. spec:
  42. serviceAccount: nfs-client-provisioner
  43. containers:
  44. - name: nfs-client-provisioner
  45. image: lizhenliang/nfs-client-provisioner:v2.0.0
  46. volumeMounts:
  47. - name: nfs-client-root
  48. mountPath: /persistentvolumes
  49. env:
  50. - name: PROVISIONER_NAME
  51. value: fuseim.pri/ifs
  52. - name: NFS_SERVER
  53. value: 10.0.0.130
  54. - name: NFS_PATH
  55. value: /data/nfs
  56. volumes:
  57. - name: nfs-client-root
  58. nfs:
  59. server: 10.0.0.130
  60. path: /data/nfs #这个卷的目录要跟我们创建的共享目录对应
  61. [root@k8s-master redis]# kubectl apply -f deploy.yaml

 3、redis配置文件configmap

  1. #将redis配置文件内容
  2. [root@k8s-master redis]# cat redis.conf
  3. appendonly yes
  4. cluster-enabled yes
  5. cluster-config-file /var/lib/redis/nodes.conf
  6. cluster-node-timeout 5000
  7. dir /var/lib/redis
  8. port 6379
  9. [root@k8s-master redis]# kubectl create configmap redis-conf --from-file=redis.conf

 

 4、创建一个无头service服务

  1. [root@k8s-master redis]# cat headless-service.yaml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: redis-service
  6. labels:
  7. app: redis
  8. appCluster: redis-cluster
  9. spec:
  10. ports:
  11. - name: redis-port
  12. port: 6379
  13. clusterIP: None
  14. selector:
  15. app: redis
  16. appCluster: redis-cluster
  17. [root@k8s-master redis]# kubectl apply -f headless-service.yaml

 5、statefulSet运行redis实例

创建好Headless service后,就可以利用StatefulSet创建Redis 集群节点,这也是本文的核心内容。我们先创建redis.yml文件:

  1. [root@k8s-master redis]# cat redis.yaml
  2. apiVersion: apps/v1
  3. kind: StatefulSet
  4. metadata:
  5. name: redis-app
  6. spec:
  7. serviceName: "redis-service"
  8. replicas: 6
  9. selector:
  10. matchLabels:
  11. app: redis
  12. appCluster: redis-cluster
  13. template:
  14. metadata:
  15. labels:
  16. app: redis
  17. appCluster: redis-cluster
  18. spec:
  19. terminationGracePeriodSeconds: 20
  20. affinity:
  21. podAntiAffinity:
  22. preferredDuringSchedulingIgnoredDuringExecution:
  23. - weight: 100
  24. podAffinityTerm:
  25. labelSelector:
  26. matchExpressions:
  27. - key: app
  28. operator: In
  29. values:
  30. - redis
  31. topologyKey: kubernetes.io/hostname
  32. containers:
  33. - name: redis
  34. image: daocloud.io/library/redis:6-alpine3.12
  35. command:
  36. - "redis-server" #pod运行后执行的命令
  37. args:
  38. - "/etc/redis/redis.conf" #运行命令加的参数
  39. - "--protected-mode" #加密模式为no
  40. - "no"
  41. resources:
  42. requests:
  43. cpu: "100m" #分配的cpu使用率
  44. memory: "100Mi" #分配的内存
  45. ports:
  46. - name: redis
  47. containerPort: 6379
  48. protocol: "TCP"
  49. - name: cluster
  50. containerPort: 16379
  51. protocol: "TCP"
  52. volumeMounts:
  53. - name: "redis-conf"
  54. mountPath: "/etc/redis"
  55. - name: "redis-data"
  56. mountPath: "/var/lib/redis"
  57. volumes:
  58. - name: "redis-conf"
  59. configMap:
  60. name: "redis-conf" #引用我们前面创建的configmap
  61. items:
  62. - key: "redis.conf"
  63. path: "redis.conf"
  64. volumeClaimTemplates: #pvc的模板
  65. - metadata:
  66. name: redis-data
  67. spec:
  68. accessModes: [ "ReadWriteMany" ]
  69. storageClassName: "nfs-client"
  70. resources:
  71. requests:
  72. storage: 200M #这个值可以适当调大
  73. 这个时候直接创建会失败,因为权限不够,需要给这个statefulset管理的pod提权,我们直接创建试一下
  74. [root@k8s-master redis]# kubectl apply -f redis.yaml
  75. statefulset.apps/redis-app created

  1. 我们删除这个statefulset,给它提权重新创建
  2. [root@k8s-master redis]# kubectl delete -f redis.yaml
  3. statefulset.apps "redis-app" deleted
  4. [root@k8s-master redis]# cat rbac.yaml
  5. ---
  6. apiVersion: rbac.authorization.k8s.io/v1
  7. kind: ClusterRole
  8. metadata:
  9. name: nfs-client-provisioner-runner
  10. rules:
  11. - apiGroups: [""]
  12. resources: ["persistentvolumes"]
  13. verbs: ["get","list","watch","create","delete"]
  14. - apiGroups: [""]
  15. resources: ["persistentvolumeclaims"]
  16. verbs: ["get", "list", "watch", "update"]
  17. - apiGroups: ["storage.k8s.io"]
  18. resources: ["storageclasses"]
  19. verbs: ["get", "list", "watch"]
  20. - apiGroups: [""]
  21. resources: ["events"]
  22. verbs: ["list", "watch", "create", "update", "patch"]
  23. ---
  24. apiVersion: rbac.authorization.k8s.io/v1
  25. kind: ClusterRoleBinding
  26. metadata:
  27. name: run-nfs-client-provisioner
  28. subjects:
  29. - kind: ServiceAccount
  30. name: nfs-client-provisioner
  31. namespace: default
  32. roleRef:
  33. kind: ClusterRole
  34. name: nfs-client-provisioner-runner
  35. apiGroup: rbac.authorization.k8s.io
  36. [root@k8s-master redis]# kubectl apply -f rbac.yaml
  37. [root@k8s-master redis]# kubectl apply -f redis.yaml

 6.验证唯一访问标识可用性

如上,总共创建了6个Redis节点(Pod),其中3个将用于master,另外3个分别作为master的slave;Redis的配置通过volume将之前生成的redis-conf这个Configmap,挂载到了容器的/etc/redis/redis.conf;Redis的数据存储路径使用volumeClaimTemplates声明(也就是PVC),其会绑定到我们先前创建的PV上。

这里有一个关键概念——Affinity,请参考官方文档详细了解。其中,podAntiAffinity表示反亲和性,其决定了某个pod不可以和哪些Pod部署在同一拓扑域,可以用于将一个服务的POD分散在不同的主机或者拓扑域中,提高服务本身的稳定性。

而PreferredDuringSchedulingIgnoredDuringExecution 则表示,在调度期间尽量满足亲和性或者反亲和性规则,如果不能满足规则,POD也有可能被调度到对应的主机上。在之后的运行过程中,系统不会再检查这些规则是否满足。

在这里,matchExpressions规定了Redis Pod要尽量不要调度到包含app为redis的Node上,也即是说已经存在Redis的Node上尽量不要再分配Redis Pod了。但是,由于我们只有三个Node,而副本有6个,因此根据PreferredDuringSchedulingIgnoredDuringExecution,这些豌豆不得不得挤一挤,挤挤更健康~

另外,根据StatefulSet的规则,我们生成的Redis的6个Pod的hostname会被依次命名为:statefulset名称—序号:如图:

如上,可以看到这些Pods在部署时是以{0…N-1}的顺序依次创建的。注意,直到redis-app-0状态启动后达到Running状态之后,redis-app-1 才开始启动。
同时,每个Pod都会得到集群内的一个DNS域名,格式为podname.servicename.namespace名称.svc.cluster.local,比如:redis-app-0.redis-service.default.svc.cluster.local

创建一个测试pod唯一标识的pod服务(其实只要有ping命令就行),测试完删除即可

  1. [root@k8s-master redis]# cat busybox.yaml
  2. ---
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: busybox
  7. spec:
  8. containers:
  9. - name: busybox
  10. image: daocloud.io/library/busybox
  11. stdin: true
  12. tty: true
  13. [root@k8s-master redis]# kubectl apply -f busybox.yaml

可以看到, redis-app-0的IP为10.244.1.33;redis-app-1的IP为10.244.2.20。当然,若Redis Pod迁移或是重启(我们可以手动删除掉一个Redis Pod来测试),IP是会改变的,但是Pod的域名、SRV records、A record都不会改变。

另外可以发现,我们之前创建的pv都被成功绑定了:

7、集群初始化

 创建好6个Redis Pod后,我们还需要利用常用的Redis-tribe工具进行集群的初始化

创建Ubuntu容器:由于Redis集群必须在所有节点启动后才能进行初始化,而如果将初始化逻辑写入Statefulset中,则是一件非常复杂而且低效的行为。这里,哥不得不赞许一下原项目作者的思路,值得学习。也就是说,我们可以在K8S上创建一个额外的容器,专门用于进行K8S集群内部某些服务的管理控制。

这里,我们专门启动一个Ubuntu的容器,可以在该容器中安装Redis-tribe,进而初始化Redis集群,执行:

  1. [root@k8s-master redis-ha]# kubectl run -it ubuntu --image=ubuntu --restart=Never /bin/bash
  2. 如果上面镜像下载失败:
  3. [root@k8s-master redis-ha]# kubectl run -it ubuntu --image=daocloud.io/library/ubuntu:artful-20170619 --restart=Never /bin/bash

然后下载我们等会需要用到的工具

  1. root@ubuntu:/# cat > /etc/apt/sources.list << EOF
  2. > deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
  3. > deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
  4. >
  5. > deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
  6. > deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
  7. >
  8. > deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
  9. > deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
  10. >
  11. > deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
  12. > deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
  13. >
  14. > deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
  15. > deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
  16. > EOF
  17. root@ubuntu:/# apt-get update

root@ubuntu:/# apt-get install -y vim wget python2.7 python-pip redis-tools dnsutils

 然后我们测试一下ubuntu这个pod能不能跟redis进行通信,结果可以

  1. root@ubuntu:/# nslookup redis-app-1.redis-service.default.svc.cluster.local
  2. Server: 10.244.1.139
  3. Address: 10.244.1.139#53
  4. Name: redis-app-1.redis-service.default.svc.cluster.local
  5. Address: 10.244.1.139

然后我们安装redis-tribe工具

root@ubuntu:/# pip install redis-trib==0.5.1

 然后,创建只有Master节点的集群:

  1. root@ubuntu:/# redis-trib.py create \
  2. `dig +short redis-app-0.redis-service.default.svc.cluster.local`:6379 \
  3. `dig +short redis-app-1.redis-service.default.svc.cluster.local`:6379 \
  4. `dig +short redis-app-2.redis-service.default.svc.cluster.local`:6379

其次,为每个Master添加Slave

  1. root@ubuntu:/# redis-trib.py replicate \
  2. --master-addr `dig +short redis-app-0.redis-service.default.svc.cluster.local`:6379 \
  3. --slave-addr `dig +short redis-app-3.redis-service.default.svc.cluster.local`:6379
  4. root@ubuntu:/# redis-trib.py replicate \
  5. --master-addr `dig +short redis-app-1.redis-service.default.svc.cluster.local`:6379 \
  6. --slave-addr `dig +short redis-app-4.redis-service.default.svc.cluster.local`:6379
  7. root@ubuntu:/# redis-trib.py replicate \
  8. --master-addr `dig +short redis-app-2.redis-service.default.svc.cluster.local`:6379 \
  9. --slave-addr `dig +short redis-app-5.redis-service.default.svc.cluster.local`:6379

至此,我们的Redis集群就真正创建完毕了,连到任意一个Redis Pod中检验一下:

  1. 退出ubuntu pod
  2. [root@k8s-master redis]# kubectl exec -it redis-app-5 /bin/bash
  3. root@redis-app-5:/data# /usr/local/bin/redis-cli -c
  4. 127.0.0.1:6379> CLUSTER NODES

8、 创建用于访问Service(可做可不做)

前面我们创建了用于实现StatefulSet的Headless Service,但该Service没有Cluster Ip,因此不能用于外界访问。所以,我们还需要创建一个Service,专用于为Redis集群提供访问和负载均衡:

  1. [root@k8s-master redis]# vim redis-access-service.yaml
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: redis-access-service
  6. labels:
  7. app: redis
  8. spec:
  9. ports:
  10. - name: redis-port
  11. protocol: "TCP"
  12. port: 6379
  13. targetPort: 6379
  14. selector:
  15. app: redis
  16. appCluster: redis-cluster
  17. [root@k8s-master redis]# kubectl apply -f redis-access-service.yaml

如上,该Service名称为 redis-access-service,在K8S集群中暴露6379端口,并且会对labels nameapp: redisappCluster: redis-cluster的pod进行负载均衡。

9、测试主从切换

在K8S上搭建完好Redis集群后,我们最关心的就是其原有的高可用机制是否正常。这里,我们可以任意挑选一个Master的Pod来测试集群的主从切换机制,如redis-app-0

说明:一般前3个为主节点,后三个为从节点。

 如上可以看到,redis-app-0、redis-app-1、redis-app-2为master

redis-app-3、redis-app-4、redis-app-5为slave。

验证:删除一个master节点的pod,等几秒钟看一下这个master节点对应的slave会不会变成master

 疑问扩展

  1. 六、疑问
  2. 至此,大家可能会疑惑,那为什么没有使用稳定的标志,Redis Pod也能正常进行故障转移呢?这涉及了Redis本身的机制。因为,Redis集群中每个节点都有自己的NodeId(保存在自动生成的nodes.conf中),并且该NodeId不会随着IP的变化和变化,这其实也是一种固定的网络标志。也就是说,就算某个Redis Pod重启了,该Pod依然会加载保存的NodeId来维持自己的身份。我们可以在NFS上查看redis-app-1的nodes.conf文件:
  3. [root@k8s-node2 ~]# cat /usr/local/k8s/redis/pv1/nodes.conf 96689f2018089173e528d3a71c4ef10af68ee462 192.168.169.209:6379@16379 slave d884c4971de9748f99b10d14678d864187a9e5d3 0 1526460952651 4 connected237d46046d9b75a6822f02523ab894928e2300e6 192.168.169.200:6379@16379 slave c15f378a604ee5b200f06cc23e9371cbc04f4559 0 1526460952651 1 connected
  4. c15f378a604ee5b200f06cc23e9371cbc04f4559 192.168.169.197:6379@16379 master - 0 1526460952651 1 connected 10923-16383d884c4971de9748f99b10d14678d864187a9e5d3 192.168.169.205:6379@16379 master - 0 1526460952651 4 connected 5462-10922c3b4ae23c80ffe31b7b34ef29dd6f8d73beaf85f 192.168.169.198:6379@16379 myself,slave c8a8f70b4c29333de6039c47b2f3453ed11fb5c2 0 1526460952565 3 connected
  5. c8a8f70b4c29333de6039c47b2f3453ed11fb5c2 192.168.169.201:6379@16379 master - 0 1526460952651 6 connected 0-5461vars currentEpoch 6 lastVoteEpoch 4
  6. 如上,第一列为NodeId,稳定不变;第二列为IP和端口信息,可能会改变。
  7. 这里,我们介绍NodeId的两种使用场景:
  8. 当某个Slave Pod断线重连后IP改变,但是Master发现其NodeId依旧, 就认为该Slave还是之前的Slave。
  9. 当某个Master Pod下线后,集群在其Slave中选举重新的Master。待旧Master上线后,集群发现其NodeId依旧,会让旧Master变成新Master的slave。
  10. 对于这两种场景,大家有兴趣的话还可以自行测试,注意要观察Redis的日志

都看到这里了,总结不易,给个三连吧~~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/661234
推荐阅读
相关标签
  

闽ICP备14008679号