参考文献

  • Kubernetes权威指南: 从Docker到Kubernetes实践全接触
  • Kubernetes in Action中文版

容器编排系统

  • 容器编排是指自动化容器应用的部署,管理,扩展和联网的一系列管控操作,能后控制和自动化许多任务、包括调度和部署容器、在容器之间分配资源、扩缩容器应用规模、在主机不可用或资源不足时将容器从一台主机迁移到其他主机、负载均衡以及监视容器和主机的运行状况等.
  • 容器编排系统用于完成容器编排相关的任务.
    • Kubernetes,MesosDocker Swarm等为代表的这类工具通常需要用户在YAMLJSON格式的配置清单中描述应用程序的配置,以指示编排系统在何处检索容器镜像(私有仓库或者某外部仓库)、如何在容器之间建立网络、在何处存储日志以及如何挂载存储卷等.
    • 确定调度目标后,编排工具将根据预定规范管理容器的生命周期
  • 容器编排系统能够为用户提供如下关键能力:
    • 集群管理与基础设施抽象: 将多个虚拟机或物理机构建成协同运行的集群,并将这些硬件基础设施抽象为一个统一的资源池.
    • 资源分配和优化: 基于配置清单中指定的资源需求与现实可用的资源量,利用成熟的调度算法合理调度工作负载.
    • 应用部署: 支持跨主机自动部署容器化应用,支持多版本并存、滚动更新和回滚等机制
    • 应用伸缩: 支持应用实例规模的自动或手动伸缩.
    • 应用隔离: 支持租户、项目或应用进行访问隔离
    • 服务可用性: 利用状态检测和应用重构等机制确保服务始终健康运行.

Kubernetes集群架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
                               +-------------+                              
| |
| | +---------------+
| | +-----> | Node 1 |
| Kubernetes | | +---------------+
+-----------------+ | Server | |
| CLI | | | | +---------------+
| (Kubectl) |----------->| ( Master ) |<------+-----> | Node 2 |
| | | | | +---------------+
+-----------------+ | | |
| | | +---------------+
| | +-----> | Node 3 |
| | +---------------+
+-------------+
  • 在硬件级别,一个Kubernetes集群由节点组成,这些节点被分成以下两种类型:
    • 主节点: 它承载着Kubernetes控制和管理整个集群系统的控制面板
    • 工作节点: 它们运行用户实际部署的应用

Master node控制节点

Master

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+----------------------------------------------------------+          
| Master |
| +-------------------------+ |
| +------->| API Server |<--------+ |
| | | | | |
| v +-------------------------+ v |
| +----------------+ ^ +--------------------+ |
| | | | | | |
| | Scheduler | | | Controller Manager | |
| | | | | | |
| +----------------+ v +--------------------+ |
| +------------------------------------------------------+ |
| | | |
| | Cluster state store | |
| | | |
| +------------------------------------------------------+ |
+----------------------------------------------------------+
  • 控制面板用于控制集群并使它工作.它包含多个组件,组件可以运行在单个主节点或者通过副本分别部署在多个节点以确保高可用性.这些组件是:
    • Kubernetes API Server你和其他控制面板组件都要和它通信
    • Scheduler,它调度你的应用(为应用的每个可部署自检分配一个工作节点)
    • Controller Manager,它执行集群级别的功能,如复制组件,持续跟踪工作节点,处理节点失败等
    • etcd,一个可靠的分布式数据存储,它能持久化存储集群配置
  • 控制面板的组件持有并控制集群状态,但是它们不运行你的应用程序,运行是由工作节点完成的.

Work node工作节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+--------------------------------------------------------+       
| +---------------------+ +---------------------+ |
| | kubelet | | kube-proxy | |
| | | | | |
| +---------------------+ +---------------------+ |
| +----------------------------------------------------+ |
| | Container Runtime (Docker) | |
| | +---------------------+ +---------------------+ | |
| | |Pod | |Pod | | |
| | | +-----+ +-----+ | |+-----++-----++-----+| | |
| | | |C1 | |C2 | | ||C1 ||C2 ||C3 || | |
| | | | | | | | || || || || | |
| | | +-----+ +-----+ | |+-----++-----++-----+| | |
| | +---------------------+ +---------------------+ | |
| +----------------------------------------------------+ |
+--------------------------------------------------------+
  • 工作节点是运行容器化应用的机器.运行,监控和管理应用服务的任务是由以下组件完成的:
    • Docker,rtk或其他容器类型
    • Kubelet,它与API服务器通信,并管理它所在节点的容器
    • Kubernetes Service Proxy(kube-proxy) ,它负责组件之间的负载均衡网络流量

流程说明

  • 以部署一个nginx服务来举例,首先要明确,一旦Kubernetes环境启动之后,masternode都会将自身的信息存储到etcd数据库中
  • 一个nginx服务的安装请求会首先被发送到master节点的apiServer组件
  • apiServer组件会调用Scheduler组件来决定到底应该把这个服务安装到哪个node节点上
    • 在此时,它会从etcd中读取各个node节点的信息,然后按照一定的算法进行选择,并将结果告知apiServer
  • apiServer调用controller-manager去调度node节点安装nginx服务
  • node节点的kubelet接收到指令后,会通知docker,然后由docker来启动一个nginxpod
    • podKubernetes的最小操作单元,容器必须跑在pod
  • 一个nginx服务就运行了,如果需要访问nginx,就需要通过kube-proxy来对pod产生访问的代理

集群类型

  • 一主多从: 一台master节点和多台node节点,搭建简单,但是有单机故障风险,适合测试环境
  • 多主多从: 多台master节点和多台node节点,搭建麻烦,安全性高,适合生产环境

安装方式

  • minikube: 一个用于快速搭建单节点的Kubernetes的工具
  • kubeadm: 一种用于快速搭建Kubernetes集群的工具
  • 二进制包: 从官网下载每个组件的二进制包,去依次去安装.

资源说明

img

Kubernetes概念

  • master: 集群控制节点,每个集群需要至少一个master节点负责集群的管控
  • node: 工作负载节点,有master分配容器到这些node工作节点上,然后node节点上的docker负责容器的运行
  • pod: Kubernetes的最小控制单元,容器都是运行在pod中的,一个pod中可以有1个或多个容器
  • controller: 控制器,通过它来实现对pod的管理,比如启动pod,停止pod,伸缩pod的数量等.
  • service: pod对外服务的统一入口,下面可以维护同一类的多个pod
  • label: 标签,用户对pod进行分类,同一类pod会有相同的标签
  • namespace: 命名空间,用来隔离pod的运行环境

minikube

启动一个minikube虚拟机

1
minikube start
  • 启动集群需要花费超过一分钟的时间

  • 安装Kubernetes客户端kubectl

    1
    2
    curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
    sudo install minikube-linux-amd64 /usr/local/bin/minikube
  • 使用kubectl查看集群是否正常工作

    1
    kubectl cluster-info
  • 使用kubectl列出集群节点

    1
    kubectl get nodes
  • 打开使用MinikubeKubernetes 集群的dashboard

    1
    minikube dashboard

kubeadmin创建集群

kubeadm init之后忘记kubeadm join命令的信息

重新生成kubeadm join命令
1
kubeadm token create --print-join-command
查看现有的 kubeadm 令牌
1
kubeadm token list

node

查看节点信息

1
2
3
4
5
6
# kubectl get nodes
# kubectl get node
# kubectl get no

# 查看详细信息
# kubectl get nodes -o wide

查看当前Kubernetes版本支持的所有对象

1
kubectl api-resources

pod

  • 一个pod是一组紧密相关的容器,它们总是一起运行在同一个工作节点上,以及同一个Linux命名空间中.每个pod就像一个独立的逻辑机器,拥有自己的IP,主机名,进程等,运行一个独立的应用程序.应用程序可以是单个进程,运行在单个容器中,也可以是一个应用进程或者其他支持进程,每个进程都在自己的容器中运行.
  • 一个pod的所有容器都运行在同一个逻辑机器上,而其他pod中的容器,即使运行在同一个工作节点上,也会出现在不同节点上.
  • pod是逻辑主机,其行为与非容器世界中的物理主机或虚拟机非常相似.此外在同一个pod中进程与运行在同一个物理机或虚拟机上的进程相似,只是每个进程都封装在一个容器之中.

列出pod

1
2
3
kubectl get pods 
# 显示IP地址
kubectl get pods -o wide

显示命令详细过程

  • 在使用kubectl命令的时候,可以加上一个参数 --v=9,它会显示出详细的命令执行过程,清楚地看到发出的HTTP请求

    1
    kubectl get pods --v=9

列出服务

1
2
3
kubectl get services
# 或者
kubectl get svc

podYAML描述文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# kubectl get po domain-admin -o yaml
# YAML描述文件所使用的Kubernetes
apiVersion: v1

# Kubernetes对象资源类型
kind: Pod

# pod元数据(名称,标签,注解等)
metadata:
creationTimestamp: "2023-09-27T10:03:53Z"
labels:
run: domain-admin
name: domain-admin
namespace: default
resourceVersion: "70836"
uid: ed76db96-3eb0-4bbc-8eb1-15184aea57be

# pod规格/内容(pod容器列表,volume等)
spec:
containers:
- image: mouday/domain-admin:latest
imagePullPolicy: Always
name: domain-admin
ports:
- containerPort: 8080
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-bq2xw
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: minikube
preemptionPolicy: PreemptLowerPriority
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- name: kube-api-access-bq2xw
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace

# pod及其内部容器的详细状态
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2023-09-27T10:03:53Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2023-10-08T08:56:58Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2023-10-08T08:56:58Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2023-09-27T10:03:53Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: docker://70a6e2ce3ee6edccff6167cbb1fb93ad3979762e082ac56b02be8c361e0c8ce7
image: mouday/domain-admin:latest
imageID: docker-pullable://mouday/domain-admin@sha256:beb8957f20f0c4761858245f51758f5942301d5b5163c2ba73bd8c0b9b4eaf16
lastState:
terminated:
containerID: docker://2c0049723163130733f6223843bdb9041c22a03b74a943795fdf271567cc96fa
exitCode: 255
finishedAt: "2023-10-08T08:56:20Z"
reason: Error
startedAt: "2023-09-27T10:04:09Z"
name: domain-admin
ready: true
restartCount: 1
started: true
state:
running:
startedAt: "2023-10-08T08:56:58Z"
hostIP: 192.168.49.2
phase: Running
podIP: 10.244.0.6
podIPs:
- ip: 10.244.0.6
qosClass: BestEffort
startTime: "2023-09-27T10:03:53Z"
pod定义的主要部分
  • pod定义有这么几个部分组成: 首先是YAML中使用的Kubernetes API版本和YAML描述的资源类型;其次是几乎在所有Kubernetes 资源中都可以找到的三大重要部分:
    • metadata 包括名称、命名空间、标签和关于该容器的其他信息
    • spec 包含pod内容的实际说明,例如pod的容器,卷和其他数据
    • status包含运行中的pod的当前信息,例如pod所处的条件,每个容器的描述和状态,以及内部IP和其他基本信息.

Pod中几个重要字段的含义和用法

  • nodeSelector: 是一个供用户将PodNode进行绑定的字段

    1
    2
    3
    4
    5
    6
    7
    apiVersion: v1 
    kind: Pod
    ...
    spec:
    # 这样的一个配置,意味着这个 Pod 永远只能运行在携带了“disktype: ssd”标签(Label)的节点上;否则,它将调度失败
    nodeSelector:
    disktype: ssd
  • nodeName: 一旦 Pod 的这个字段被赋值,Kubernetes项目就会被认为这个 Pod 已经经过了调度,调度的结果就是赋值的节点名字.所以,这个字段一般由调度器负责设置,但用户也可以设置它来“骗过”调度器,当然这个做法一般是在测试或者调试的时候才会用到

  • hostAliases: 定义了 Pod 的 hosts 文件(比如 /etc/hosts)里的内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    apiVersion: v1 
    kind: Pod
    ...
    spec:
    hostAliases:
    - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"

使用kubectl explain解释API对象字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# kubectl explain pods
KIND: Pod
VERSION: v1

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

FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

metadata <Object>
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

spec <Object>
Specification of the desired behavior of the pod. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

status <Object>
Most recently observed status of the pod. This data may not be up to date.
Populated by the system. Read-only. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

# 进一步查看spec属性信息
# kubectl explain pods.spec

kubectl生成一份""文档样板’’

  • kubectl的两个特殊参数 --dry-run=client-o yaml,前者是空运行,后者是生成YAML格式,结合起来使用就会让kubectl不会有实际的创建动作,而只生成YAML文件

    1
    kubectl run ngx --image=nginx:alpine --dry-run=client -o yaml

使用kubectl create来创建pod

1
kubectl create -f kubia-manual.yaml

查看应用程序日志

1
kubectl logs <pod-name>
  • 每天或每次日志文件达到10MB大小时,容器日志都会自动轮替.kubectl logs命令仅显示最后一次轮替后的日志条目
1
kubectl logs <pod-name> -c <container-name>

pod发送请求

  • 将本地网络端口转发到pod中端口

    1
    kubectl port-forward kubia-manual 8888:8080

Pod生命周期

状态 说明
Pending 这个状态意味着,Pod 的 YAML 文件已经提交给了 Kubernetes,API 对象已经被创建并保存在 Etcd 当中.但是,这个 Pod 里有些容器因为某种原因而不能被顺利创建.比如,调度不成功
Running 这个状态下,Pod 已经调度成功,跟一个具体的节点绑定.它包含的容器都已经创建成功,并且至少有一个正在运行中
Succeeded 这个状态意味着,Pod 里的所有容器都正常运行完毕,并且已经退出了.这种情况在运行一次性任务时最为常见
Failed 这个状态下,Pod 里至少有一个容器以不正常的状态(非 0 的返回码)退出.这个状态的出现,意味着你得想办法 Debug 这个容器的应用,比如查看 Pod 的 Events 和日志
Unknown 这是一个异常状态,意味着 Pod 的状态不能持续地被 kubelet 汇报给 kube-apiserver,这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题

删除pod

按名称删除
1
kubectl delete po kubia-gpu
  • 在删除pod的过程中,实际上我们在指示Kubernetes终止该pod中所有容器.
  • Kubernetes向进程发送一个SIGTERM信号并等待一定的秒数(默认为30),使其正常关闭.如果它没有及时关闭,则通过SIGKILL终止该进程.
使用标签选择器删除pod
1
2
3
# kubectl delete po -l creation_method=manual
pod "kubia-manual" deleted
pod "kubia-manual-v2" deleted
通过删除整个命名空间来删除pod
1
2
# kubectl delete ns custom-namespace
namespace "custom-namespace" deleted
删除命名空间中的所有pod,但保留命名空间
1
# kubectl delete po --all
删除命名空间中(几乎)所有资源
1
# kubectl delete all --all
  • 第一个all指定正在删除所有资源类型,而--all 选项指定将删除所有资源实例,而不是按名称指定它们.

label标签

  • 标签是一种简单却功能强大的Kubernetes特性,不仅可以组织pod,也可以组织所有其他的Kubernetes资源.
  • 标签是可以附加到资源(任何Kubernetes对象)的任意键值对,用以选择具有该确切标签的资源(这是通过标签选择器完成的).只要标签的key在资源内是唯一的,一个资源便可以拥有多个标签.
  • 通常在我们创建资源时就会将标签附加到资源上,但之后我们也可以再添加其他标签,或者修改现有标签的值,而无须重新创建资源.

创建pod时指定标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: kubia-manual-v2
# 添加两个标签
labels:
creation_method: manual
env: prod
spec:
containers:
- image: luksa/kubia
name: kubia
ports:
- containerPort: 8080
protocol: TCP

查看pod标签

1
2
3
4
5
# kubectl get po --show-labels
NAME READY STATUS RESTARTS AGE LABELS
domain-admin 1/1 Running 1 (16h ago) 11d run=domain-admin
kubia-manual 1/1 Running 0 15h <none>
kubia-manual-v2 0/1 ImagePullBackOff 0 74s creation_method=manual,env=prod
  • 使用-L选项指定标签分别显示在自己的列中,而不是列出所有标签

    1
    2
    3
    4
    5
    # kubectl get po -L creation_method,env
    NAME READY STATUS RESTARTS AGE CREATION_METHOD ENV
    domain-admin 1/1 Running 1 (16h ago) 11d
    kubia-manual 1/1 Running 0 15h
    kubia-manual-v2 1/1 Running 0 8m49s manual prod

修改现有pod的标签

1
2
# kubectl label po kubia-manual creation_method=manual
pod/kubia-manual labeled
1
2
3
# 更改现有标签时,需要使用--overwrite
# kubectl label po kubia-manual-v2 env=debug --overwrite
pod/kubia-manual-v2 labeled

标签选择器

  • 标签要与标签选择器结合在一起.标签选择器允许我们选择标记特定标签的pod子集,并对这些pod执行操作.可以说标签选择器是一种能够根据是否具有特定值的特定标签来过滤资源的准则.
  • 标签选择器根据资源的以下条件来选择资源:
    • 包含(或不包含)使用特定键的标签
    • 包含具有特定键和值的标签
    • 包含具有特定键的标签,但其值与我们指定的不同

使用标签选择器列出pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# kubectl get po -l creation_method=manual
NAME READY STATUS RESTARTS AGE
kubia-manual 1/1 Running 0 16h
kubia-manual-v2 1/1 Running 0 36m

# 列出包含env标签的所有pod
# kubectl get po -l env
NAME READY STATUS RESTARTS AGE
kubia-manual-v2 1/1 Running 0 42m

# 列出没有nev标签的pod
# kubectl get po -l '!env'
NAME READY STATUS RESTARTS AGE
domain-admin 1/1 Running 1 (17h ago) 11d
kubia-manual 1/1 Running 0 16h
  • windows下会有问题,需要将单引号去掉
1
2
3
4
5
6
7
# kubectl get po -l '!env'
Error from server (BadRequest): Unable to find "/v1, Resource=pods" that match label selector "'!env'", field selector "": unable to parse requirement: <nil>: Invalid value: "'": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')

# kubectl get po -l !env
NAME READY STATUS RESTARTS AGE
domain-admin 1/1 Running 1 (17h ago) 11d
kubia-manual 1/1 Running 0 16h

在标签选择器中使用多条件

  • 在包含多个逗号分隔清空下,可以在标签选择器中同时使用多个条件,此时资源需要全部匹配才算成功匹配了选择器.

使用标签分类工作节点

  • 向节点提假标签来展示这个功能特性,可以通过将标签gpu=true添加到其中一个节点上来实现(只需要从kubectl get nodes返回的列表中选择一个)
1
2
3
4
5
6
# kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready control-plane 11d v1.27.4

# kubectl label node minikube gpu=true
node/minikube labeled

pod调度到特定节点

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: kubia-gpu
spec:
# 节点选择器要求Kubernetes只将pod部署到包含标签gpu=true的节点上
nodeSelector:
gpu: "true"
containers:
- image: luksa/kubia
name: kubia

Volume

Projected Volume

  • 投射数据卷

  • 类别

    • secret

      • 它的作用,是帮你把 Pod 想要访问的加密数据,存放到 Etcd 中。然后,你就可以通过在 Pod 的容器里挂载 Volume 的方式,访问到这些 Secret
        里保存的信息了
    • downwardAPI

      • 它的作用是:让 Pod 里的容器能够直接获取到这个 Pod API 对象本身的信息。
    • configMap

    • serviceAccountToken

    • clusterTrustBundle

ConfigMap/Secret

  • ConfigMap用来保存明文配置,Secret用来保存秘密配置

    1
    2
    3
    # 创建secret YAML样板
    export out="--dry-run=client -o yaml" # 定义Shell变量
    kubectl create cm info $out
    1
    2
    # 创建secret YAML样板
    kubectl create secret generic user --from-literal=name=root $out

注解pod

  • 除了标签外,pod和其他对象还可以包含注解.注解也是键值对,所以它们本质上与标签非常相似.但与标签不同,注解并不是为了保存标识信息而存在的,它们不能像标签一样用于对对象进行分组.当我们选择标签选择器选择对象时,就不存在注解选择器这样的东西.

添加和修改注解

1
2
3
4
5
6
7
8
# kubectl annotate pod kubia-manual holelin.com/someannotation="foo bar"
pod/kubia-manual annotated

# kubectl describe pod kubia-manual
Name: kubia-manual
.....
Annotations: holelin.com/someannotation: foo bar
.....

使用命名空间对资源进行分组

列出集群中所有命名空间

1
2
3
4
5
6
7
# kubectl get ns
NAME STATUS AGE
default Active 11d
kube-node-lease Active 11d
kube-public Active 11d
kube-system Active 11d
kubernetes-dashboard Active 18h
1
2
3
4
5
6
7
8
9
# kubectl get po --namespace kube-system
NAME READY STATUS RESTARTS AGE
coredns-5d78c9869d-wdstl 1/1 Running 1 (18h ago) 11d
etcd-minikube 1/1 Running 1 (18h ago) 11d
kube-apiserver-minikube 1/1 Running 1 (18h ago) 11d
kube-controller-manager-minikube 1/1 Running 1 (18h ago) 11d
kube-proxy-knb4s 1/1 Running 1 (18h ago) 11d
kube-scheduler-minikube 1/1 Running 1 (18h ago) 11d
storage-provisioner 1/1 Running 3 (18h ago) 11d
  • 可以使用-n来代替--namespace

创建一个命名空间

  • YAML文件创建命名空间,创建一个custom-namespace.yaml,然后执行kubectl create -f custom-namespace.yaml

    1
    2
    3
    4
    5
    6
    apiVersion: v1
    # 这表示我们正在定义一个命名空间
    kind: Namespace
    metadata:
    # 这是命名空间的名称
    name: custom-namespace
  • 使用kubectl create namespace命令创建命名空间

    1
    2
    # kubectl create namespace custom-namespace
    namespace/custom-namespace created

管理其他命名空间中的对象

  • 如果想要在刚创建的命名空间中创建资源,可以选择在metadata字段中添加一个namespace: custom-namespace属性,也可以使用kubectl create命令创建资源时指定命名空间

    1
    2
    # kubectl create -f kubia-manual.yaml -n custom-namespace
    pod/kubia-manual created
  • 在列出,描述,修改或删除其他命名空间中的对象时,需要给kubectl命令传递--namespace/-n选项.如果不指定命名空间 ,kubectl将在当前上下文配置的默认命名空间中执行操作.而当前上下文的命名空间和当前上下文本身可以通过kubectl config命令进行更改.

    • 要想快速切换到不同的命名空间,可以设置以下别名:alias kcd='kubectl config set-context $(kubectl config current-context) --namespace,然后可以使用kcd some-namespace在命名空间之间进行切换.

保持pod健康

存活探针

  • Kubernetes可以通过存活探针(liveness probe)检查容器是否还在运行.可以为pod中的每个容器单独指定存活探针.如果探针失败,Kubernetes将定期执行探针并重新启动容器.
  • Kubernetes有以下三种探测同期的机制
    • HTTP GET探针对容器的IP地址(指定的端口和路径)执行HTTP GET请求.
      • 如果探测器收到响应,并且响应状态码不代表错误(换句话,如果HTTP响应状态码是2xx3xx),则认为探测成功.如果服务器返回错误响应状态码或者根本没响应,那么探测就被认为失败,容器将被重新启动.
    • TCP套接字探针尝试与容器指定端口建立TCP连接.如果连接成功建立,则探测成功.否则,容器重新成功.
    • Exec探针在容器内执行任意命令,并检查命令的退出状态码.如果状态码是0,则探测成功.所有其他状态码都被认为失败.

创建基于HTTP的存活探针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: kubia-liveness
spec:
containers:
- image: luksa/kubia-unhealthy
name: kubia
# 一个HTTP GET 存活探针
livenessProbe:
httpGet:
# HTTP请求的路径
path: /
# 探针连接的网络端口
port: 8080

获取崩溃容器的应用日志

1
kubectl logs kubia-liveness --previous

重启容器后的pod描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# kubectl describe po kubia-liveness
Name: kubia-liveness
Namespace: default
Priority: 0
Service Account: default
Node: minikube/192.168.49.2
Start Time: Mon, 09 Oct 2023 13:11:10 +0800
Labels: <none>
Annotations: <none>
Status: Running
IP: 10.244.0.14
IPs:
IP: 10.244.0.14
Containers:
kubia:
Container ID: docker://b678f38acb13d7224696d7551f427aa1c1af3e6b1cd37568ed33f9421e986b0d
Image: luksa/kubia-unhealthy
Image ID: docker-pullable://luksa/kubia-unhealthy@sha256:5c746a42612be61209417d913030d97555cff0b8225092908c57634ad7c235f7
Port: <none>
Host Port: <none>
State: Running
Started: Mon, 09 Oct 2023 13:13:35 +0800
Last State: Terminated
Reason: Error
Exit Code: 137
Started: Mon, 09 Oct 2023 13:11:44 +0800
Finished: Mon, 09 Oct 2023 13:13:30 +0800
Ready: True
Restart Count: 1
Liveness: http-get http://:8080/ delay=0s timeout=1s period=10s #success=1 #failure=3
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-7srr9 (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-7srr9:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 4m13s default-scheduler Successfully assigned default/kubia-liveness to minikube
Normal Pulled 3m40s kubelet Successfully pulled image "luksa/kubia-unhealthy" in 32.5531712s (32.5532057s including waiting)
Normal Created 108s (x2 over 3m39s) kubelet Created container kubia
Normal Started 108s (x2 over 3m39s) kubelet Started container kubia
Normal Pulled 108s kubelet Successfully pulled image "luksa/kubia-unhealthy" in 4.7247765s (4.7247968s including waiting)
Warning Unhealthy 33s (x6 over 2m43s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 500
Normal Killing 33s (x2 over 2m23s) kubelet Container kubia failed liveness probe, will be restarted
Normal Pulling 3s (x3 over 4m12s) kubelet Pulling image "luksa/kubia-unhealthy"
  • 可以看到容器现在正在运行,但之前由于错误而终止.退出代码为137,这有特殊的含义–表示该进程由外部信号终止.
    • 数字137是两个数字的总和:128+x,其中x是终止进程的信号编号.在这个例子,x等于9,这是SIGKILL的信号编号,意味这这个进程被强行终止.
      • 退出代码127表示进程被外部信号终止,退出代码为128+9(SIGKILL)
      • 退出代码143对应128+15(SIGTERM)
    • 当容器被强行终止时,会创建一个全新的容器–而不是重启原来的容器.

配置存活探针的附加属性

  • 定义探针时可以自定义这些附加参数.例如,要设置初始延迟,请将initialDelaySeconds属性添加到存活探针的配置中.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
name: kubia-liveness
spec:
containers:
- image: luksa/kubia-unhealthy
name: kubia
# 一个HTTP GET 存活探针
livenessProbe:
httpGet:
# HTTP请求的路径
path: /
# 探针连接的网络端口
port: 8080
# Kubernetes会在第一次探测前等待15秒
initialDelaySeconds: 15
  • 如果没有设置初始延迟,探针将在启动时立即开始探测容器,这通常会导致探测失败,因为应用程序还没准备好开始接收请求.如果失败次数超过阀值,在应用程序能正确相应请求之前,容器就会重启.
    • 务必记得设置一个初始延迟来说明应用程序的启动时间.

创建有效的存活探针

存活探针一个检查什么
  • 为了更好的进行存活检查,需要将探针配置为请求特定的URL路径(例如/health),并让应用从内部对内部运行的所有重要组件执行状态检查,以确保他们都没有终止或停止响应.
    • 请确保/health HTTP端点不需要认证,否则探测会一直失败,导致你的容器无限重启.
保持探针轻量
  • 存活探针不应消耗太多的计算资源,并且运行不应该花太长时间.默认情况下,探测器执行的频率相对较高,必须在一秒内执行完毕.

ReplicationController

  • ReplicationController是一种Kubernetes资源,可确保它的pod始终保持运行状态.如果pod因任何原因消失(例如节点从集群中消失或由于该pod已从节点中逐出),则ReplicationController会注意到缺少了pod并创建替代pod.

控制器的协调流程

  • ReplicationController的工作是确保pod的数量始终与其标签选择器匹配.如果不匹配,则ReplicationController将根据所需,采取适当的操作来协调pod的数量.

ReplicationController的三部分

  • label selector(标签选择器),用于确定ReplicationController作用域有哪些pod
  • replica count(副本个数),指定运行的pod数量
  • pod template(pod模板),用于创建新的pod副本
  • ReplicationController的副本个数,标签选择器,甚至是pod模板都可以随时修改,但只有副本数量的变更会影响现有的pod.

更改控制器的标签选择器或pod模板的效果

  • 更改标签选择器和pod模板对现有pod没有影响.更改标签选择器会使现有的pod脱离ReplicationController的范围,因此控制器会停止关注他们.
  • 在创建pod后,ReplicationController也不关心其pod的实际"内容"(容器镜像,环境变量及其他).因此,该模版仅影响由此ReplicationController创建的新pod.

使用ReplicationController的好处

  • 确保一个pod(或多个pod)持续运行,方法是在现有pod丢失是启动一个新的pod
  • 集群节点发生故障时,它将为故障节点上运行的所有pod(即受ReplicationController控制的节点上的那些pod)创建替代副本.
  • 它能轻松实现pod的水平伸缩–手动和自动都可以

创建一个ReplicationController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: ReplicationController
metadata:
name: kubia
spec:
replicas: 3
selector:
app: kubia
template:
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: luksa/kubia
ports:
- containerPort: 8080
  • 上传文件到API服务器时,Kubernetes会创建一个名为kubia的新ReplicationController.它确保符合标签选择器app=kubiapod实例始终是三个.当没有足够的pod时,根据提供的pod模板创建新的pod.
  • 模板中的pod标签必须和ReplicationController的标签选择器匹配,否则控制器将无休止地创建新的容器.因为启动新的pod不会使实际的副本数据量接近期望的副本数量.为了防止出现这种情况,API服务会校验ReplicationController的定义不会接收错误配置.
  • 不指定选择器也是一种选择,在这种情况下,它会自动根据pod模版中的标签自动配置.

获取有关ReplicationController的信息

1
kubectl get rc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# kubectl describe rc kubia
Name: kubia
Namespace: default
Selector: app=kubia
Labels: app=kubia
Annotations: <none>
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=kubia
Containers:
kubia:
Image: luksa/kubia
Port: 8080/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 44h replication-controller Created pod: kubia-x84wk
Normal SuccessfulCreate 44h replication-controller Created pod: kubia-7kg8g
Normal SuccessfulCreate 93s replication-controller Created pod: kubia-wswdz

控制器如何创建新的pod

  • 控制器通过创建一个新的替代pod来响应pod的删除操作.从技术上讲,它并没有对删除本身做反应,而是针对由此产生的状态,即pod数量不足.

  • 虽然ReplicationController会立即收到删除的pod的通知(API服务器允许客户端监听资源和资源列表的更改),但这不是它创建替代pod的原因.该通知会触发控制器检查实际的pod数量并采取适当的措施.

修改pod模板

  • ReplicationControllerpod模板可以随时修改.更改pod模板就像用一个曲奇到替换另一个,它只会影像你之后切出的曲奇,并不会影响你已经剪切的曲奇.要修改旧的pod,你需要删除它们,并让ReplicationController根据新模板将其替换为新的pod.

  • 可以试着编辑ReplicationController并向pod模板添加标签

    1
    kubectl edit rc kubia
    • 这将在你的默认文本编辑器中打开ReplicationControllerYAML配置.

水平缩放pod

  • 放大或者缩小pod的数量规模就和在ReplicationController资源中更改Replicas字段的值一样简单.更改之后,ReplicationController将会看到存在太多的pod并删除其中的一部分(缩容时),或者看到它们数目太少并创建pod(扩容时).
使用kubectl scale 命令缩容
1
2
# scale rc kubia --replicas=3
replicationcontroller/kubia scaled

删除一个ReplicationController

  • 当你通过kubectl delete删除ReplicationController时,pod也会被删除.但是由于由ReplicationController创建的pod不是ReplicationController的组成部分,只是由其进行管理,因此可以值删除ReplicationController并保持pod运行.

  • 当使用kubectl delete删除ReplicationController时,可以通过给命令增加--cascade=orphan选项来保持pod运行

    1
    kubectl delete rc kubia --cascade=orphan

使用ReplicaSet而不是ReplicationController

  • ReplicationController是用于复制和在异常时重新调度节点的唯一Kubernetes组件,后来又引入一个名为ReplicaSet的类似资源.它是新一代的ReplicationController,并且将其完全替换掉(ReplicationController最终将被弃用).

定义ReplicaSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: kubia
spec:
replicas: 3
selector:
matchLabels:
app: kubia
template:
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: luksa/kubia
ports:
- containerPort: 8080

查看ReplicaSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# kubectl get rs
NAME DESIRED CURRENT READY AGE
kubia 3 3 3 2m50s

# kubectl describe rs
Name: kubia
Namespace: default
Selector: app=kubia
Labels: <none>
Annotations: <none>
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=kubia
Containers:
kubia:
Image: luksa/kubia
Port: 8080/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Events: <none>

使用ReplicaSet的更富表达力的标签选择器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: kubia
spec:
replicas: 3
selector:
matchExpressions:
- key: app
operator: In
values:
- kubia
template:
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: luksa/kubia
ports:
- containerPort: 8080
  • 可以给选择器添加额外的表达式,每个表达式都必须包含一个key,一个operator(运算符),并且可能还有一个values的列表(取决于运算符)
    • In: Label的值必须与其中一个指定的values匹配.
    • NotIn: Label的值与任何指定的values不匹配
    • Exists: pod必须包含一个指定名称的标签(值不重要).使用此运算符时,不应指定values字段
    • DoesNotExists: pod不得包含有指定名称的标签.values属性不得指定.
  • 如果指定了多个表达式,则所有这些表达式都必须为true才能使选择器与pod匹配(条件是AND关系).如果同时指定matchLabelsmatchExpressions,则所有标签都必须匹配,并且所有表达式必须计算为true以使该pod与选择器匹配.

删除ReplicaSet

1
kubectl delete rs kubia

DaemonSet

创建一个DaemonSet YAML文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ssd-monitor
spec:
selector:
matchLabels:
app: ssd-monitor
template:
metadata:
labels:
app: ssd-monitor
spec:
containers:
- name: main
image: luksa/ssd-monitor

查看DaemonSet

1
2
3
# kubectl get ds
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
ssd-monitor 1 1 0 1 0 <none> 7s

Job

  • Kubernetes通过Job资源提供了对此的支持,它允许你运行一种pod,该pod在内部进程成功结束时,不重启容器.一旦任务完成.pod就被认为处于完成状态.
  • 在发上节点故障时,该节点上由Job管理的pod将按照ReplicaSetpod的方式,重新安排到其他节点.如果进程本身异常退出(进程返回错误退出代码时),可以将Job配置为重新启动容器.

控制离线作业的重要字段

  • 控制离线作业的重要字段

    • activeDeadlineSeconds,设置Pod运行的超时时间。

    • backoffLimit,设置Pod的失败重试次数。

    • completions,Job完成需要运行多少个Pod,默认是1个

创建一个Job

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
spec:
template:
metadata:
labels:
app: batch-job
spec:
# Job不能使用Always 默认的重新启动策略
restartPolicy: OnFailure
containers:
- name: main
image: luksa/batch-job

查看Job

1
2
3
kubectl get job
NAME COMPLETIONS DURATION AGE
batch-job 0/1 17s 17s

顺序运行Job pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: batch/v1
kind: Job
metadata:
name: multi-completion-batch-job
spec:
completions: 5
template:
metadata:
labels:
app: batch-job
spec:
# Job不能使用Always 默认的重新启动策略
restartPolicy: OnFailure
containers:
- name: main
image: luksa/batch-job

并行运行Job pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: batch/v1
kind: Job
metadata:
name: multi-completion-parallel-batch-job
spec:
completions: 5
parallelism: 2
template:
metadata:
labels:
app: batch-job
spec:
# Job不能使用Always 默认的重新启动策略
restartPolicy: OnFailure
containers:
- name: main
image: luksa/batch-job

CronJob

  • Job资源在创建时会立即运行pod,但是许多批处理任务需要再特定的时间运行,或者在指定的时间间隔内重复运行.
  • Kubernetes中的cron任务通过创建CronJob资源进行配置.运行任务的时间表以知名的cron格式指定.
创建一个CronJob
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: batch/v1
kind: CronJob
metadata:
name: batch-job-every-fifteen-minutes
spec:
schedule: "0,15,30,45 * * * *"
jobTemplate:
spec:
template:
metadata:
labels:
app: periodic-batch-job
spec:
restartPolicy: OnFailure
containers:
- name: main
image: luksa/batch-job

Service

创建服务

通过kubectl expose创建服务
通过YAML描述文件来创建服务
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: kubia

检测新的服务

1
2
3
4
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d4h
kubia ClusterIP 10.103.164.179 <none> 80/TCP 5s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# kubectl describe svc kubia
Name: kubia
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=kubia
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.103.164.179
IPs: 10.103.164.179
Port: <unset> 80/TCP
TargetPort: 8080/TCP
Endpoints: 10.244.0.54:8080,10.244.0.55:8080,10.244.0.56:8080
Session Affinity: None
Events: <none>

从内部集群测试服务

  • 方法一: 创建一个pod,它将请求发送到服务的集群IP并记录响应

  • 方法二: 使用ssh远程登录到其中一个Kubernetes节点,然后使用curl命令

  • 方法三: 通过kubectl exec命令在一个已经存在的pod中执行curl命令

    1
    2
    kubectl exec  kubia-7fxh9 -- curl -s  http://10.103.164.179
    You've hit kubia-7fxh9
  • 为什么使用双横杠? 双横杠(--)代表着kubectl命令项的结束.在两个横杠之后的内容是指在pod内部需要执行的命令.如果需要执行的命令并没有以横杠开始的参数,横杠也不是必须的.如下情况,如果这里不使用横杠号,-s选项会被解析成kubectl exec选项,会导致结果异常和歧义错误.

配置服务上的会话亲和性

  • 如果希望特定客户端产生的所有请求每次都指向同一个pod,可以设置服务的sessionAffinity属性为ClientIp(而不是None,None是默认值)

    1
    2
    3
    4
    5
    6
    7
    apiVersion: v1
    kind: Service
    metadata:
    name: kubia
    spec:
    sessionAffinity: ClientIP
    ...
  • Kubernetes仅仅支持两种形式的会话亲和性服务:NoneClinetIP.

同一个服务暴露多个端口

  • 创建的服务可以暴露一个端口,也可以暴露多个端口.

    • 注意: 在创建一个多个端口的服务的时候,必须给每个端口指定名字.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    apiVersion: v1
    kind: Service
    metadata:
    name: kubia
    spec:
    ports:
    - name: http
    port: 80
    targetPort: 8080
    - name: https
    port: 443
    targetPort: 8443
    selector:
    app: kubia
    • 标签选择器应用于整个服务,不能对每个端口做单独的配置.如果不同的pod有不同的端口映射关系,需要创建两个服务.
使用命名的端口
  • pod定义中指定port名称
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Pod
spec:
container:
- name: kubia
ports:
# 端口8080被命名为http
- name: http
containerPort: 8080
# 端口8443被命名为https
- name: https
containerPort: 8443
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
# 将端口80映射到容器中被称为http端口
- name: http
port: 80
targetPort: http
# 将端口443映射到容器中被称为https端口
- name: https
port: 443
targetPort: https
selector:
app: kubia

服务发现

通过环境变量发现服务
通过DNS发现服务
通过FQDN连接服务
pod容器中运行shell

服务的类型

  • Service 目前有 4 种类型:
    • ClusterIP: 是 K8S 当前默认的 Service 类型.将 service 暴露于一个仅集群内可访问的虚拟 IP 上.
    • NodePort: 是通过在集群内所有 Node 上都绑定固定端口的方式将服务暴露出来,这样便可以通过 <NodeIP>:<NodePort> 访问服务了.
    • LoadBalancer: 是通过 Cloud Provider 创建一个外部的负载均衡器,将服务暴露出来,并且会自动创建外部负载均衡器路由请求所需的 NodeportClusterIP .
    • ExternalName: 是通过将服务由 DNS CNAME 的方式转发到指定的域名上将服务暴露出来,这需要 kube-dns 1.7 或更高版本支持.

Endpoint

  • 服务并不是和pod直接相连的,是通过一种资源介于两者之间–Endpoint资源.

  • Endpoint资源就是暴露一个服务的IP地址和端口的列表,Endpoint资源和其他Kubernetes资源一样.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # kubectl describe svc kubia
    Name: kubia
    Namespace: default
    Labels: <none>
    Annotations: <none>
    Selector: app=kubia
    Type: ClusterIP
    IP Family Policy: SingleStack
    IP Families: IPv4
    IP: 10.103.164.179
    IPs: 10.103.164.179
    Port: <unset> 80/TCP
    TargetPort: 8080/TCP
    Endpoints: 10.244.0.69:8080,10.244.0.71:8080,10.244.0.72:8080
    Session Affinity: None
    Events: <none>
    1
    2
    3
    # kubectl get endpoints kubia
    NAME ENDPOINTS AGE
    kubia 10.244.0.54:8080,10.244.0.55:8080,10.244.0.56:8080 106m
  • 如果创建了不包含pod选择器的服务,Kubernetes将不会创建Endpoint资源(毕竟,缺少选择器,将不会知道服务中包含哪些pod),这样就需要创建Endpoint资源来指定该服务的Endpoint列表.

  • 要使用手动配置endpoint的方式创建服务,需要创建服务和Endpoint资源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    apiVersion: v1
    kind: Service
    metadata:
    name: external-service
    spec:
    ports:
    - port: 80
    ---
    apiVersion: v1
    kind: Endpoints
    metadata:
    name: external-service
    subsets:
    - addresses:
    - ip: 11.11.11.11
    - ip: 22.22.22.22
    ports:
    - port: 80
  • Endpoint对象需要与服务具有相同的名称,并包含该服务的目标IP地址和端列表.服务和Endpoint资源都发布到服务器后,这样服务就可以像具有pod选择器那样的服务正常使用.

将服务暴露给外部客户端

  • 有几种方式可以在外部访问服务
    • 将服务的类型设置为NodePort: 每个集群节点都会在节点上打开一个端口,对于NodePort服务,每个集群节点在节点本身上打开一个端口,并将在该端口上接收到的流量重定向到基础服务.该服务仅在内部集群IP和端口才可以访问,但也可以通过所有节点上的专用端口访问.
    • 将服务的类型设置为LoadBalance,NodePort类型的一种扩展,这使得服务可以通过一个专用的负载均衡器来访问,这是Kubernetes中正在运行的云基础设施提供的.负载均衡器将流量重定向到跨所有节点的节点端口.客户端通过负载均衡器的IP连接到服务.
    • 创建一个Ingress资源,通过一个IP地址公开多个服务,它运行在HTTP层(网络协议第7层)上,因此可以提供比工作在4层的服务更多的功能.

使用NodePort类型的服务

  • 通过创建NodePort服务,可以让Kubernetes在其所有节点上保留一个端口(所有节点都使用相同的端口号),并将传入的连接转发给作为服务部分的pod.

  • 这与常规服务类似(它们实际类型是ClusterIP),但是不仅可以通过服务的内部集群IP发那个访问NodePort服务,还可以通过任何节点的IP和预留节点端口发那个访问NodePort服务.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    apiVersion: v1
    kind: Service
    metadata:
    name: kubia-nodeport
    spec:
    type: NodePort
    ports:
    # 服务集群IP的端口号
    - port: 80
    # 背后pod的目标端口号
    targetPort: 8080
    # 通过集群节点的30123端口访问该服务
    nodePort: 30123
    selector:
    app: kubia

通过负载均衡器将服务暴露出来

  • 负载均衡器拥有自己独一无二的可公开的访问IP地址,并将所有连接重定向到服务.可以通过负载均衡器的IP地址访问服务.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    apiVersion: v1
    kind: Service
    metadata:
    name: kubia-loadbalancer
    spec:
    type: LoadBalancer
    ports:
    # 服务集群IP的端口号
    - port: 80
    # 背后pod的目标端口号
    targetPort: 8080
    selector:
    app: kubia

通过Ingress暴露服务

  • 为什么需要Ingress ?
    • 一个重要的原因是每个LoadBalancer服务都需要自己的负载均衡器,以及独有的公有IP地址,而Ingress只需要一个公网IP就能为许多服务提供访问.当客户端向Ingress发送HTTP请求时,Ingress会根据请求的主机名和路径决定请求转发的服务.

认证和授权

  • K8S 中几乎所有的操作都需要经过 kube-apiserver 处理,所以为了安全起见,K8S 为它提供了三类安全访问的措施.分别是:
    • 用于识别用户身份的认证(Authentication)
    • 用于控制用户对资源访问的授权(Authorization)
    • 用于资源管理方面的准入控制(Admission Control)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
+-----------------------------------------------------------------------------------------------------------+
| |
| +---------------------------------------------------------------------------+ +--------+ |
| | | | | |
| +--------+ | +------------------+ +----------------+ +--------------+ +------+ | | | |
| | | | | | | | | Admission | | | | | | |
| | Client +------> | Authentication +-> | Authorization +-> | Control +-> |Logic | +--> | Others | |
| | | | | | | | | | | | | | | |
| +--------+ | +------------------+ +----------------+ +--------------+ +------+ | | | |
| | | | | |
| | | | | |
| | Kube-apiserver | | | |
| +---------------------------------------------------------------------------+ +--------+ |
| |
+-----------------------------------------------------------------------------------------------------------+

Authentication

K8S 中的用户
  • K8S 中有两类用户,一般用户及 Service Account

    • 一般用户:一般用户只能通过外部服务进行管理,由管理员进行私钥分发.这也意味着 K8S 中并没有任何表示一般用户的对象,所以一般用户是无法通过 API 直接添加到集群的.
    • Service Account:由 K8S API 管理的用户,与特定的 NameSpace(命名空间)绑定.由 API Server 自动创建或者通过 API 手动进行创建. 同时,它会自动挂载到 Pod 中容器的 /var/run/secrets/kubernetes.io/serviceaccount/ 目录中,其中会包含 NameSpace token 等信息,并允许集群内进程与 API Server 进行交互.
  • 对集群操作的 API 都是与用户相关联的,或者被视为匿名请求.匿名请求可通过 kube-apiserver--anonymous-auth 参数进行控制,默认是开启的,匿名用户默认的用户名为 system:anonymous,所属组为 system:unauthenticated

K8S 中的认证机制
  • X509 客户端证书:这个认证机制我们并不陌生,我们前面搭建集群时,虽然没有指定配置文件,但 kubeadm 已经添加了默认参数 --client-ca-file=/etc/kubernetes/pki/ca.crt 而在进行认证时,将会使用客户端证书 subject 的 CN 域(Common Name)用作用户名,O 域(Organization)用作组名.
  • 引导 Token:当集群通过 kubeadm init 初始化完成后,将会展示一行提示,其中便携带着引导 Token.如果不使用 kubeadm 时,需要设置 --enable-bootstrap-token-auth=true.
  • 静态 Token 文件:启动 Kube-apiserver 时,设置 --token-auth-file=SOMEFILE 并在请求时,加上 Authorization: Bearer TOKEN 的请求头即可.
  • 静态密码文件:与静态 Token 文件类似,设置 --basic-auth-file=SOMEFILE 并在请求时,加上 Authorization: Basic BASE64ENCODED(USER:PASSWORD) 的头即可.
  • Service Account Token:这是默认启用的机制
  • OpenID:其实是提供了 OAuth2 的认证支持,像 Azure 或 Google 这类云厂商都提供了相关支持.
  • 认证代理:主要是配合身份验证代理进行使用,比如提供一个通用的授权网关供用户使用.
  • Webhook:提供 Webhook 配合一个远端服务器使用.

Authorization

  • 授权,也就是在验证当前发起请求的用户是否有相关的权限
  • 授权是以认证的结果为基础的,授权机制检查用户通过认证后的请求中所包含的属性来进行判断.
  • K8S 支持多种授权机制,用户想要正确操作资源,则必须获得授权,所以 K8S 默认情况下的权限都是拒绝.当某种授权机制通过或者拒绝后,便会立即返回,不再去请求其他的授权机制;当所有授权机制都未通过时便会返回 403 错误了.
K8S 支持的授权机制
  • ABAC(Attribute-Based Access Control):基于属性的访问控制,在使用时需要先配置 --authorization-mode=ABAC--authorization-policy-file=SOME_FILENAME .ABAC 本身设计是非常好的,但是在 K8S 中使用却有点过于繁琐,这里不再赘述.
  • RBAC(Role-based access control):基于角色的访问控制,自 K8S 1.6 开始 beta,1.8 进入稳定版,已被大量使用.而当我们使用 kubeadm 安装集群的时候,默认将会添加 --authorization-mode=Node,RBAC 的参数,表示同时开启 NodeRBAC 授权机制.
  • Node:这是一种特殊用途的授权机制,专门用于对 kubelet 发出的 API 请求做授权验证.
  • Webhook:使用外部的 Server 通过 API 进行授权校验,需要在启动时候增加 --authorization-webhook-config-file=SOME_FILENAME 以及 --authorization-mode=Webhook
  • AlwaysAllow:默认配置,允许全部.
  • AlwaysDeny:通常用于测试,禁止全部.

Role

  • K8S 中的角色从类别上主要有两类,RoleClusterRole.

    • Role:可以当作是一组权限的集合,但被限制在某个 Namespace 内(K8S 的 Namespace).
    • ClusterRole:对于集群级别的资源是不被 Namespace 所限制的,并且还有一些非资源类的请求,所以便产生了它.
  • 当已经了解到角色后,剩下给用户授权也就只是需要做一次绑定即可.在 K8S 中将这一过程称之为 binding,即 rolebindingclusterrolebinding.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    # kubectl get roles --all-namespaces=true
    NAMESPACE NAME CREATED AT
    kube-public kubeadm:bootstrap-signer-clusterinfo 2023-09-27T09:48:45Z
    kube-public system:controller:bootstrap-signer 2023-09-27T09:48:44Z
    kube-system extension-apiserver-authentication-reader 2023-09-27T09:48:44Z
    kube-system kube-proxy 2023-09-27T09:48:46Z
    kube-system kubeadm:kubelet-config 2023-09-27T09:48:44Z
    kube-system kubeadm:nodes-kubeadm-config 2023-09-27T09:48:44Z
    kube-system system::leader-locking-kube-controller-manager 2023-09-27T09:48:44Z
    kube-system system::leader-locking-kube-scheduler 2023-09-27T09:48:44Z
    kube-system system:controller:bootstrap-signer 2023-09-27T09:48:44Z
    kube-system system:controller:cloud-provider 2023-09-27T09:48:44Z
    kube-system system:controller:token-cleaner 2023-09-27T09:48:44Z
    kube-system system:persistent-volume-provisioner 2023-09-27T09:48:47Z
    kubernetes-dashboard kubernetes-dashboard 2023-10-08T08:58:01Z

    # kubectl get rolebindings --all-namespaces=true
    NAMESPACE NAME ROLE AGE
    kube-public kubeadm:bootstrap-signer-clusterinfo Role/kubeadm:bootstrap-signer-clusterinfo 25d
    kube-public system:controller:bootstrap-signer Role/system:controller:bootstrap-signer 25d
    kube-system kube-proxy Role/kube-proxy 25d
    kube-system kubeadm:kubelet-config Role/kubeadm:kubelet-config 25d
    kube-system kubeadm:nodes-kubeadm-config Role/kubeadm:nodes-kubeadm-config 25d
    kube-system system::extension-apiserver-authentication-reader Role/extension-apiserver-authentication-reader 25d
    kube-system system::leader-locking-kube-controller-manager Role/system::leader-locking-kube-controller-manager 25d
    kube-system system::leader-locking-kube-scheduler Role/system::leader-locking-kube-scheduler 25d
    kube-system system:controller:bootstrap-signer Role/system:controller:bootstrap-signer 25d
    kube-system system:controller:cloud-provider Role/system:controller:cloud-provider 25d
    kube-system system:controller:token-cleaner Role/system:controller:token-cleaner 25d
    kube-system system:persistent-volume-provisioner Role/system:persistent-volume-provisioner 25d
    kubernetes-dashboard kubernetes-dashboard Role/kubernetes-dashboard 14d