K8s基本概念

Node

一个物理节点或虚拟机

Pod

k8s的基本组件和最小单元,是container的抽象。用来运行容器,一个pod中可以运行多个容器,但通常只运行一个

Serivce

可以被附加在Pod上的静态(永久)IP,Pod和Service的生命周期是不同的,Pod停止后,Service依然存在。Service可以绑定多个Pod,起到负载均衡的作用。

Ingress

用于实现用域名的方式访问k8s内部应用

ConfigMap

来保存key-value pair配置数据

Secret

存储加密的配置数据

Volume

挂载一个存储到Pod中,可以是Pod运行的服务器(即Node)的存储,或是远程存储(不是K8s的一部分)。K8s不管理存储。

Deployment

Pod的抽象,创建Pod的蓝图,可以指定由多少个Pod副本

StatefulSet

数据库等有状态的Pod,通过StatefulSet来启动,而不是Deployment

K8s架构

K8s集群的Node分为master Node和worker Node,Wokrder Node中可以存在多个Pod。

每个Worker Node要安装三样东西:

  • Contaier Runtime,如Dokcer
  • Kublete,接受配置,并在Node中自动Pod,分配Node的资源(CPU,RAM)给Pod
  • Kube Proxy,在请求转发到Pod的组件,起到负载均衡的作用

Master Node

  • Api Server,与K8s集群交互的唯一通道
  • Scheduler,调度器,寻找一个用来部署Pod的Node,然后由Kublete启动Pod
  • Controller Manager,用来检测Pod的状态,如果Pod crash后,Controller会通知Scheduler重新调度该Pod
  • etdc,用来表示集群的KV对,集群所有的状态改变都存储在其中。Controller的行为依据etdc来运行 Master Node一般部署多个,所以Api Server是负载均衡的,etcd是分布式存储。

Kubectl

K8s集群的命令行工具

Kubectl基本命令

  • kebuectl get node.获取所有Node
  • kubectl get pod.获取所有pod
    $ kubectl get pod
    NAME                          READY   STATUS    RESTARTS   AGE
    nginx-depl-5ddc44dd46-ss96b   1/1     Running   0          3h34m
    

    [-o] output, -o wide 获取更多信息, -o yaml获得详细信息,并以yaml的形式展示

  • kubectl get service
  • kubectl edit [deploy-name],获得deployment的配置文件。修改配置文件后,pod在列表中替换直到消失,但是replicaset中还存在,只是数量都为0
  • kubectl logs [pod-name],获取Pod中应用的日志
  • kubectl exec -it [pod-name] – /bin/bash,在容器中打开交互式命令行
  • kubectl delete [deployment],删除deployment,会连带删除deplicaset和pod
  • kubectl describe pod [pod-name],获取pod的信息,用于调试

创建Deployment

kubectl create depolyment [name] –image=[image-name],创建一个最基本的deployment,只有名称和镜像名

Replicaset

Replicaset用于管理Pod的副本,实践中,不能对这个对象作修改,

$ kubectl get replicaset
NAME                    DESIRED   CURRENT   READY   AGE
nginx-depl-5ddc44dd46   1         1         1       3h34m

K8s配置文件

Deployment Configuration

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec: # for deployment
  replicas: 2 # 部署多少个副本
  selector:
    matchLabels:
      app: nginx
  template: # 在一个配置文件中再套一层
    metadata:
      labels: # label用于识别一组资源,是一组键值对
        app: nginx
    spec: # for a pod,Pod的blueprint,
      containers:
      - name: nginx # 在Pod部署一个container
        image: nginx:1.16
        ports:
        - containerPort: 8080

Service Configuration

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector: # 在deployment和pod之间建立连接
    app: nginx
  ports:
    - protocol: TCP
      port: 80 # 请求被发往80端口
      targetPort: 8080 # pod在监听那个端口,service将请求转发到对应的pod

Secret Configuration

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  password: YWRtaW4xMjM= # base64编码

ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-configmap
data:
  database_url: mysql-service

配置文件可以定义Department、Service等。配置文件由三部分组成:

  • metadata。name
  • specification
  • status。自动生成,K8s用来比较期望的状态和当前状态(存储在etcd中)来做出改变

kubectl apply -f [file-name]。部署一个配置文件,一个配置文件可以被反复部署,多次部署后K8s会识别到是一次更新,如下:

$ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment created
$ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment configured

外网可访问的Service

默认创建的service分配的IP是k8s集群内部IP,所以叫internal service,通过指定type为LoadBalancer和nodePort,将其变为external service。默认的service type是ClusterIP。

apiVersion: v1
kind: Service
metadata:
  name: mysql-express-service
spec:
  selector:
    app: mysql-express # 转发到哪个Pod
  type: LoadBalancer  
  ports:
    - protocol: TCP
      port: 3333
      targetPort: 3333
      nodePort: 30000 # 对外暴露的端口

通过describe命令查看service的部署

$ kubectl describe service nginx-service
...
Port:              <unset>  80/TCP
TargetPort:        8080/TCP
Endpoints:         172.17.0.3:8080,172.17.0.4:8080 // 请求转发的目的
Session Affinity:  None
Events:            <none>

Namespace

可以认为是K8s集群中的虚拟集群。多个团队如果再同一个命名空间部署同一个name的组件,会覆盖掉另一个。不同的namespace有隔离作用。通过kubectl获取的组件都是在默认default namespace。部署component时,通过选项-n(–namespace)指定要部署到哪个namespace。或者在配置文件的metadata中指定。 Servcie可以跨命名空间共享,指定service name时,在后面加上所在的namespace即可。

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-configmap
  namespace: other-namespace ##
data:
  database_url: mysql-service.some-namespace # 通过后缀指定

Ingress

通过暴露同一的外部访问方式,将请求转发给service。每个external service会占用一个端口,ingress可以通过域名的形式访问。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name
spec:
  rules:
    - host: app.com # 访问的host
      http:
        paths: # 通过url定义转发规则
          - backend:
              serviceName: my-service # 转发给对应的internal service
              servicePort: 8080 # service的port

Controller

Ingress只是K8s的一层抽象,如果要让IIngress工作,必须选取一个实现,这个实现被称为Controller。Controller也是运行在K8s商上的Pod。Controller是集群中域名的entry point。

安装controller

$ kubectl addons enable ingress

这个命令会安装默认的ingress实现,即nginx-ingress-controller,并运行一个pod。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: dashboard-ingress
  namespace: kubernetes-dashboard
spec:
  rules:
  - host: dashboard.com
    http:
      paths:
      - backend:
          serviceName: kubernetes-dashboard
          servicePort: 80

Helm

K8s的包管理器,打包yaml文件,并将其分发到仓库上,能被其他用户共享。yaml文件的集合也被称为Helm Charts。

Helm可以用于构建蓝图;或者是模板,动态将文件中的placeholder进行替换。Template file存放这yaml模板,placeholder的值在value.yml中寻找。

结构

Helm的顶层是个文件夹,文件夹的名称是charts的名称。Chart.yaml包含chart的元数据,如名称、依赖、版本。Values.yaml包含template中的 value,可以有默认值并重载它。charts文件夹有chart的依赖,有其他chart文件。template文件夹包含模板文件。

mychart/
	Chart.yaml
	cahrts/
	templates/

当执行 helm install 命令时,template会从Values中填充,生产K8s能够识别的文件。

替换默认值

helm install --values=my-value.yml <cahrtname>

这个命令会将my-value.yml中的值合并到Value.yml中。

或:

helm install --set version=xxx

Release Managerment

Volumes

K8s有三种持久化数据的组件,并相互配合

  • Persistent Volume, pv
  • Persistent Volume Claim, pvc
  • Storage Class, sc

K8s没有开箱即用的持久化方案,需要自己配置在pod重启时保存数据。因为不知道pod何时重启,所以storage要能被所有pod访问到

pv

pv是集群中的资源,像cpu和ram一样。用来存储数据。pv由k8s配置文件创建。

pv是一个抽象概念,它必须由实际的物理存储提供实现,可以选择node的物理存储,或者aws之类的云存储。pv没有命名空间。

storageClass:

  • hostpath 类似docker,将node的路径挂载到pod中。但是在集群中pod可能会被调度到其他node上 ```yml apiVersion: v1 kind: PersistentVolume metadata: name: example-volume labels: type: local spec: #we use local node storage here! #kubectl get storageclass

    通过kubectl get storageclass获取。k8s存储类型接口,k8s需要知道使用的何种类型的存储

    storageClassName: hostpath # docker提供的 capacity: # 容量 storage: 1Gi accessModes: # 权限

    • ReadWriteOnce hostPath: path: “/mnt/data” # 在host上的哪个路径 ```

pvc

pv由管理创建,开发者在应用声明他们想用到的pv,这在pvc中配置。配置会会匹配到一个具体pv,然后应用。向pv申请存储。有namespace

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: example-claim
spec:
  storageClassName: hostpath
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 50Mi # 从pv中申请50M

在pod应用:

apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-config
  labels:
    app: postgres
data:
  POSTGRES_DB: postgresdb
  POSTGRES_USER: admin
  POSTGRES_PASSWORD: admin123
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres
  selector:
    matchLabels:
      app: postgres
  replicas: 1
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:10.4
          imagePullPolicy: "IfNotPresent"
          ports:
          - containerPort: 5432
          envFrom:
          - configMapRef:
              name: postgres-config
          volumeMounts: # 挂载到pod哪个目录下
          - name: data # 指向volume
            mountPath: /var/lib/postgresql/data 
      volumes:
      - name: data
        persistentVolumeClaim: # 指向一个具体的pvc
          claimName: example-claim
---
apiVersion: v1
kind: Service
metadata:
  name: postgres
  labels:
    app: postgres
spec:
  selector:
    app: postgres
  ports:
    - protocol: TCP
      name: http
      port: 5432
      targetPort: 5432

应用发起存储请求时,被发送给pvc,然后pvc将请求发送给匹配的pv。找到匹配的pv后,volume会被挂载到pod上

sc

当pvc想要pv时,sc可以用来动态创建pv。

StatefulSet

StatefulSet和Deployment相似,可以有replica和storage。Deployment创建时会有个随机的名称,创建和删除pod是随机的。StatefulSet创建时要在yml指定名称,因为要有一种机制来决定那个节点是主节点,其他节点同步主节点数据,所以存储(pv)也是分开的。pod会共享volume,statefulset有自己的pv。pod没有DNS。

statefulset在启动是不会同时启动,而是会一个个启动。stateful命名规则为,[podname]-[ordinal],ordinal会从0累加。

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-cluster
data:
  update-node.sh: |
    #!/bin/sh
    REDIS_NODES="/data/nodes.conf"
    sed -i -e "/myself/ s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/${POD_IP}/" ${REDIS_NODES}
    exec "$@"
  redis.conf: |+
    cluster-enabled yes
    cluster-require-full-coverage no
    cluster-node-timeout 15000
    cluster-config-file /data/nodes.conf
    cluster-migration-barrier 1
    appendonly yes
    protected-mode no
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cluster
spec:
  serviceName: redis-cluster # 暴露pod
  replicas: 6
  selector:
    matchLabels:
      app: redis-cluster
  template:
    metadata:
      labels:
        app: redis-cluster
    spec:
      containers:
      - name: redis
        image: redis:5.0.1-alpine
        ports:
        - containerPort: 6379
          name: client
        - containerPort: 16379
          name: gossip
        command: ["/conf/update-node.sh", "redis-server", "/conf/redis.conf"]
        env:
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        volumeMounts:
        - name: conf
          mountPath: /conf
          readOnly: false
        - name: data # redis写入数据的路径
          mountPath: /data
          readOnly: false
      volumes:
      - name: conf
        configMap:
          name: redis-cluster
          defaultMode: 0755
  volumeClaimTemplates: # 通过pvc来持久化数据,为每个pod动态提供volume
  - metadata:
      name: data 
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "hostpath"
      resources:
        requests:
          storage: 50Mi # 在data这个volume里面,声明50M的存储
---
apiVersion: v1
kind: Service
metadata:
  name: redis-cluster
spec:
  clusterIP: None
  ports:
  - port: 6379
    targetPort: 6379
    name: client
  - port: 16379
    targetPort: 16379
    name: gossip
  selector:
    app: redis-cluster