控制器Statefulset

Statefulset 控制器:概念、原理解读 StatefulSet 是为了管理有状态服务的问题而设计的

有状态服务?

StatefulSet 是有状态的集合,管理有状态的服务,它所管理的 Pod 的名称不能随意变化。数据 持久化的目录也是不一样,每一个 Pod 都有自己独有的数据持久化存储目录。比如 MySQL 主 从、redis 集群等。

无状态服务?

RC、Deployment、DaemonSet 都是管理无状态的服务,它们所管理的 Pod 的 IP、名字,启 停顺序等都是随机的。个体对整体无影响,所有 pod 都是共用一个数据卷的,部署的 tomcat 就 是无状态的服务,tomcat 被删除,在启动一个新的 tomcat,加入到集群即可,跟 tomcat 的名 字无关。


StatefulSet 由以下几个部分组成:

1. Headless Service:用来定义 pod 网路标识,生成可解析的 DNS 记录

2. volumeClaimTemplates:存储卷申请模板,创建 pvc,指定 pvc 名称大小,自动创建 pvc,且 pvc 由存储类供应。

3. StatefulSet:管理 pod 的


什么是 Headless service?

Headless service 

不分配 clusterIP,headless service 可以通过解析 service 的 DNS,返回所 有 Pod 的 dns 和 ip 地址 (statefulSet 部署的 Pod 才有 DNS),普通的 service,只能通过解析 service 的 DNS 返回 service 的 ClusterIP。

为什么要用 headless service(没有 service ip 的 service)?

在使用 Deployment 时,创建的 Pod 名称是没有顺序的,是随机字符串,在用 statefulset 管理 pod 时要求 pod 名称必须是有序的 ,每一个 pod 不能被随意取代,pod 重建后 pod 名称还是一样的。因为 pod IP 是变化的,所以要用 Pod 名称来识别。pod 名称是 pod 唯一性的标识符,必须持久稳定有效。这时候要用到无头服务,它可以给每个 Pod 一个唯一的名称。


1.headless service 会为 service 分配一个域名

<service name>.$<namespace name>.svc.cluster.local

K8s 中资源的全局 FQDN 格式: 

Service_NAME.NameSpace_NAME.Domain.LTD.

Domain.LTD.=svc.cluster.local.    #这是默认 k8s 集群的域名。

FQDN 全称 Fully Qualified Domain Name 

即全限定域名:同时带有主机名和域名的名称 FQDN = Hostname + DomainName

如 主机名是 god

域名是 baidu.com

FQDN= god.baidu.com


2.StatefulSet 会为关联的 Pod 保持一个不变的 Pod Name statefulset 中 Pod 的名字格式为$(StatefulSet name)-$(pod 序号)

3.StatefulSet 会为关联的 Pod 分配一个 dnsName

$.$.$.svc.cluster.local

为什么要用 volumeClaimTemplate?

对于有状态应用都会用到持久化存储,比如 mysql 主从,由于主从数据库的数据是不能存放在一 个目录下的,每个 mysql 节点都需要有自己独立的存储空间。而在 deployment 中创建的存储卷 是一个共享的存储卷,多个 pod 使用同一个存储卷,它们数据是同步的,而 statefulset 定义中 的每一个 pod 都不能使用同一个存储卷,这就需要使用 volumeClainTemplate,当在使用 statefulset 创建 pod 时,volumeClainTemplate 会自动生成一个 PVC,从而请求绑定一个 PV,每一个 pod 都有自己专用的存储卷。Pod、PVC 和 PV 对应的关系图如下:



Statefulset 资源清单文件编写


kubectl explain statefulset.spec

apiVersion <string>    #定义 statefulset 资源需要使用的 api 版本

kind。                         #定义的资源类型

metadata<Object>     #元数据

spec <Object>             #定义容器相关的信息

podManagementPolicy #pod 管理策略

replicas <integer> #副本数

revisionHistoryLimit <integer> #保留的历史版本

selector <Object> -required- #标签选择器,选择它所关联的 pod

serviceName -required- #headless service 的名字

template <Object> -required- #生成 pod 的模板

updateStrategy <Object> #更新策略

volumeClaimTemplates<[]Object> #存储卷申请模板

#查看 statefulset 的 spec.template 字段如何定义?

#对于 template 而言,其内部定义的就是 pod,pod 模板是一个独立的对象

KIND: StatefulSet

VERSION:  apps/v1

RESOURCE: template <Object>

DESCRIPTION:

    template is the object that describes the pod that will be created if

    insufficient replicas are detected. Each pod stamped out by the StatefulSet

    will fulfill this Template, but have a unique identity from the rest of the

    StatefulSet.

    PodTemplateSpec describes the data a pod should have when created from a

    template


statefulset 资源中有两个 spec 字段。第一个 spec 声明的是 statefulset 定 义多少个 Pod 副本(默认将仅部署 1 个 Pod)、匹配 Pod 标签的选择器、创建 pod 的模板、存 储卷申请模板,第二个 spec 是 spec.template.spec:主要用于 Pod 里的容器属性等配置。 .spec.template 里的内容是声明 Pod 对象时要定义的各种属性,所以这部分也叫做 PodTemplate(Pod 模板)。还有一个值得注意的地方是:在.spec.selector 中定义的标签选择 器必须能够匹配到 spec.template.metadata.labels 里定义的 Pod 标签,否则 Kubernetes 将 不允许创建 statefulset。


使用Statefulset 部署 web


vim class-web.yaml

apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

  name: nfs-web

provisioner: example.com/nfs       #对应供应商的存储类名字。env 下 nfs-deployment.yaml

kubectl apply -f class-web.yaml

kubectl get storageclass

NAME      PROVISIONER      RECLAIMPOLICY  VOLUMEBINDINGMODE  ALLOWVOLUMEEXPANSION  AGE

nfs      example.com/nfs  Delete          Immediate          false                  22h

nfs-web  example.com/nfs  Delete          Immediate          false                  6m47s

#编写一个 Statefulset 资源清单文件

cat statefulset.yaml

apiVersion: v1

kind: Service

metadata:

  name: nginx

  labels:

    app: nginx

spec:

  ports:

  - port: 80

    name: web

  clusterIP: None #svc里面的字段type: ClusterIP/NodePort,这个是指定服务类型的,而clusterIP:None属于无         头服务也是svc的一种

  selector:

    app: nginx

---

apiVersion: apps/v1

kind: StatefulSet

metadata:

  name: web

spec:

  selector:

    matchLabels:

      app: nginx

  serviceName: "nginx"

  replicas: 2

  template:

    metadata:

    labels:

      app: nginx

    spec:

      containers:

      - name: nginx

        image: nginx

        ports:

        - containerPort: 80

          name: web

        volumeMounts:

        - name: www

          mountPath: /usr/share/nginx/html

  volumeClaimTemplates:

  - metadata:

      name: www

    spec:

      accessModes: ["ReadWriteOnce"]

      storageClassName: "nfs-web"

      resources:

        requests:

          storage: 1Gi

kubectl apply -f statefulset.yaml

#查看 statefulset 是否创建成功

# kubectl get statefulset

#查看pod

kubectl get pods -l app=nginx


kubectl get pvc

NAME          STATUS  VOLUME                                    CAPACITY  ACCESS MODES  STORAGECLASS  AGE

www-web-0    Bound    pvc-3dc06339-e3d4-443f-a152-ec8728fe26f2  1Gi        RWO            nfs-web        18m

www-web-1    Bound    pvc-cc3e704f-09d7-4911-8687-d22ba1f28eba  1Gi        RWO            nfs-web        17m


kubectl get pv

NAME                                      CAPACITY  ACCESS MODES  RECLAIM POLICY  STATUS      CLAIM                STORAGECLASS  REASON  AGE

pvc-3dc06339-e3d4-443f-a152-ec8728fe26f2  1Gi        RWO            Delete          Bound      default/www-web-0    nfs-web                19m

pvc-cc3e704f-09d7-4911-8687-d22ba1f28eba  1Gi        RWO            Delete          Bound      default/www-web-1    nfs-web

#如果更新镜像,会自动删除pod,然后更新

kubectl edit sts web   

#  查看镜像更改

kubectl describe pods web-0



#查看 headless service

kubectl get svc -l app=nginx

#查看 pod 主机名

for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done

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

kubectl exec -it web-1 -- /bin/bash

root@web-1:/# apt-get update 

root@web-1:/# apt-get install dnsutils -y

root@web-1:/# nslookup web-0.nginx.default.svc.cluster.local Server: 10.96.0.10

Address: 10.96.0.10#53

Name: web-0.nginx.default.svc.cluster.local


#statefulset 创建的 pod 也是有 dns 记录的

Address: 10.244.209.154 #解析的是 pod 的 ip 地址

root@web-1:/# nslookup nginx.default.svc.cluster.local Server: 10.96.0.10

Address: 10.96.0.10#53

Name: nginx.default.svc.cluster.local #查询 service dns,会把对应的 pod ip 解析出来 Address: 10.244.209.139

Name: nginx.default.svc.cluster.local Address: 10.244.209.140

root@web-1:/# dig -t A nginx.default.svc.cluster.local @10.96.0.10

; <<>> DiG 9.11.5-P4-5.1+deb10u3-Debian <<>> -t A nginx.default.svc.cluster.local @10.96.0.10

;; global options: +cmd

;; Got answer:

;; WARNING: .local is reserved for Multicast DNS

;; You are currently testing what happens when an mDNS query is leaked to DNS

;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27207

;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; WARNING: recursion requested but not available


dig 的使用

dig -t A nginx.default.svc.cluster.local @10.96.0.10 格式如下:

@来指定域名服务器

A 为解析类型 ,A 记录

-t 指定要解析的类型

A 记录:

A 记录是解析域名到 IP


资源清单详细解读:

apiVersion: v1。    #定义 api 版本

kind: Service         #定义要创建的资源:service

metadata:

  name: nginx          #定义 service 的名字

  labels:

    app: nginx         #service 的标签

spec:

  ports:

  - port: 80

    name: web

  clusterIP: None       #创建一个没有 ip 的 service

  selector:

    app: nginx             #选择拥有 app=nginx 标签的 pod

---

apiVersion: apps/v1

kind: StatefulSet

metadata:

  name: web

spec:

  selector:

    matchLabels:

      app: nginx

  serviceName: "nginx"         #headless service 的名字

  replicas: 2                      

  template:                            #定义 pod 的模板

    metadata:

    labels:

      app: nginx

    spec:

      containers:

      - name: nginx

        image: nginx

        ports:

        - containerPort: 80

          name: web

        volumeMounts:

        - name: www

          mountPath: /usr/share/nginx/html

  volumeClaimTemplates:                       #存储卷申请模板

  - metadata:

      name: www

    spec:

      accessModes: ["ReadWriteOnce"]

      storageClassName: "nfs-web"          #指定从哪个存储类申请 pv

      resources:

        requests:

          storage: 1Gi                              #需要 1G 的 pvc,会自动跟符合条件的 pv 绑定


 service 和 headless service 区别:

通过 deployment 创建 pod,pod 前端创建一个 service

cat deploy-service.yaml

apiVersion: v1

kind: Service

metadata:

  name: my-nginx

  labels:

    run: my-nginx

spec:

  type: ClusterIP

  selector:

    run: my-nginx  #选择拥有 run=my-nginx 标签的 pod

  ports:

  - targetPort: 80  #pod 容器中定义的端口

    protocol: TCP

    port: 80        #service 的端口,暴露给 k8s 集群内部服务访问

---

apiVersion: apps/v1

kind: Deployment

metadata:

  name: my-nginx

spec:

  selector:

    matchLabels:

      run: my-nginx

  replicas: 2

  template:

    metadata:

      name: nginx

      labels:

          run: my-nginx

    spec:

      containers:

      - name: my-nginx

        image: busybox

        ports:

        - containerPort: 80

        command:

        - sleep

        - "3600"

kubectl apply -f deploy-service.yaml

kubectl get svc -l run=my-nginx

NAME      TYPE        CLUSTER-IP    EXTERNAL-IP  PORT(S)  AGE

my-nginx  ClusterIP  10.100.89.90  <none>        80/TCP    53s


kubectl exec -it web-1 -- /bin/bash root@web-1:/# nslookup my-nginx.default.svc.cluster.local Server: 10.96.0.10

Address: 10.96.0.10#53

Name: my-nginx.default.svc.cluster.local

Address: 10.100.89.90 #

解析的是 service 的 ip 地址


#Statefulset 实现 pod 的动态扩容


如果我们觉得两个副本太少了,想要增加,只需要修改配置文件 statefulset.yaml 里的 replicas 的值即可,原来 replicas: 2,现在变成 replicaset: 3,修改之后,执行如下命令更新:

kubectl apply -f statefulset.yaml

kubectl get sts

#也可以直接编辑控制器实现扩容

kubectl edit sts web

#这个是我们把请求提交给了 apiserver,实时修改


#查看是不是变成5个了

kubectl get pods -l app=nginx

#Statefulset 实现 pod 的动态缩容

如果我们觉得 4 个 Pod 副本太多了,想要减少,只需要修改配置文件 statefulset.yaml 里的 replicas 的值即可,把 replicaset:4 变成 replicas: 2,修改之后,执行如下命令更新:

]# kubectl apply -f statefulset.yaml


#Statefulset 实现 pod 的更新,更新镜像

docker load -I myapp.tar.gz         上传到工作节点

kubectl edit sts web

#修改镜像 nginx 变成- image: ikubernetes/myapp:v2,修改之后保存退出

kubectl get pods -o wide -l app=nginx

 pod 已经使用刚才更新的镜像 ikubernetes/myapp:v2 了


k8s 配置管理中心 Configmap


什么是 Configmap?

Configmap 是 k8s 中的资源对象,用于保存非机密性的配置的,数据可以用 key/value 键值对的 形式保存,也可通过文件的形式保存。

Configmap 能解决哪些问题? 

我们在部署服务的时候,每个服务都有自己的配置文件,如果一台服务器上部署多个服务:nginx、 tomcat、apache 等,那么这些配置都存在这个节点上,假如一台服务器不能满足线上高并发的要 求,需要对服务器扩容,扩容之后的服务器还是需要部署多个服务:nginx、tomcat、apache,新 增加的服务器上还是要管理这些服务的配置,如果有一个服务出现问题,需要修改配置文件,每台物 理节点上的配置都需要修改,这种方式肯定满足不了线上大批量的配置变更要求。 所以,k8s 中引 入了 Configmap 资源对象,可以当成 volume 挂载到 pod 中,实现统一的配置管理。


1、Configmap 是 k8s 中的资源, 相当于配置管理中心,可以有一个或者多个 Configmap; 2、Configmap 可以做成 Volume,k8s pod 启动之后,通过 volume 形式映射到容器内部指定 目录上


Configmap 应用场景

1、使用 k8s 部署应用,当你将应用配置写进代码中,更新配置时也需要打包镜像,configmap 可 以将配置信息和 docker 镜像解耦,以便实现镜像的可移植性和可复用性,因为一个 configMap 其 实就是一系列配置信息的集合,可直接注入到 Pod 中给容器使用。configmap 注入方式有两种,一 种将 configMap 做为存储卷,一种是将 configMap 通过 env 中 configMapKeyRef 注入到容器 中。 2、使用微服务架构的话,存在多个服务共用配置的情况,如果每个服务中单独一份配置的话,那么 更新配置就很麻烦,使用 configmap 可以友好的进行配置共享。

2. 局限性

ConfigMap 在设计上不是用来保存大量数据的。在 ConfigMap 中保存的数据不可超过 1 MiB。 如果你需要保存超出此尺寸限制的数据,可以考虑挂载存储卷或者使用独立的数据库或者文件服务。


#把 configmap 做成 volume,挂载到 pod

cat mysql-configmap.yaml

apiVersion: v1

kind: ConfigMap

metadata:

  name: mysql

  labels:

    app: mysql

data:

  master.conf: |

    [mysqld]

    log-bin

    log_bin_trust_function_creators=1

    lower_case_table_names=1

  slave.cnf: |

    [mysqld]

    super-read-only

    log_bin_trust_function_creators=1

kubectl apply -f mysql-configmap.yaml


cat mysql-pod-volume.yaml

apiVersion: v1

kind: Pod

metadata:

  name: mysql-pod-volume

spec:

  containers:

  - name: mysql

    image: busybox

    command: ["bin/sh","-c","sleep 3600"]

    volumeMounts:

    - name: mysql-config

      mountPath: /tmp/config

  volumes:

  - name: mysql-config

    configMap:

      name: mysql

  restartPolicy: Never

kubectl apply -f mysql-pod-volume.yaml

kubectl exec -it mysql-pod-volume -- /bin/sh

/ # cd /tmp/config/

/tmp/config # ls

master.conf  slave.cnf

/tmp/config # vi master.conf


serviceaccount 与 rbac


1、ServiceAccount

kubernetes 中账户区分为:User Accounts(用户账户) 和 Service Accounts(服务账户) 两种:

UserAccount 是给 kubernetes 集群外部用户使用的,例如运维或者集群管理人员,K8s 上的用户 账户 useraccount,kubeadm 安装的 k8s,默认用户账号是 kubernetes-admin;


k8s 客户端(一般用:kubectl) ------>API Server

APIServer 需要对客户端做认证,默认使用工具安装的 K8s,会在用户家目录下创建一个认证配置 文件 .kube/config 这里面保存了客户端访问 API Server 的密钥相关信息,这样当用 kubectl 访问 k8s 时,它就会自动读取该配置文件,向 API Server 发起认证,然后完成操作请求。


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容