Kubernetes

  1. 概述
  2. 架构
    1. Control Plane
      1. 主控件
        1. kube-apiserver
        2. kube-scheduler
        3. kube-controller-manager
      2. etc
      3. cloud-controller-manager
    2. 节点
      1. 节点容量
    3. 节点群
      1. Control Plane的配置
  3. 存储
      1. emptyDir
      2. hostPath
      3. Google Cloud Engine
      4. AWS EC2
      5. 自搭服务器
      6. 其他
    1. 持久性卷
      1. 背景
      2. 概览
  4. 网络
    1. Ingress
      1. 自签名证书
    2. DNS
    3. 集群内部流量
  5. 容器
    1. 健康检查
      1. 存活探测器
      2. 就绪探测器
      3. 启动探测器
    2. 开发与部署的配置
      1. 背景
      2. Secret
      3. ConfigMap
    3. 开发应用
    4. 获取上层信息
      1. 通过Downward API传输
      2. 访问Rest API
    5. 推荐容器
  6. 安全
    1. 资源访问
      1. 云服务层次资源
      2. 云=>集群
      3. 集群层次资源
        1. RBAC授权定义
        2. 主体
          1. 服务账号
        3. 角色与角色绑定
          1. 角色
          2. 角色绑定
          3. 集群角色
          4. 集群角色绑定
      4. 容器层次资源
        1. 语境
    2. 网络安全
      1. Calico
  7. 日志
    1. 管理方式
  8. K8s扩展
  9. 术语
    1. Kubernetes API
    2. 对象特征
      1. 标签和选择器
    3. 未分类对象
      1. Endpoint
    4. 基础对象
      1. Pod
      2. 命名空间
      3. 服务
        1. 发布服务
        2. 发现服务
        3. 会话亲和性
        4. 本地服务优先
    5. 高级对象
      1. ReplicationController
      2. ReplicaSet
      3. Deployment
      4. Job
      5. CronJob
      6. DaemonSet
      7. StatefulSet
  10. 常见讨论
    1. 容器 vs Pod
    2. 命令
      1. kubectl describe vs get
      2. kubectl get
      3. kubectl create vs apply
      4. kubectl attach vs exec
      5. kubectl删除
        1. kubectl cordon
        2. kubectl drain
    3. 如何使用SSH?
    4. 代理服务器
    5. 部署很麻烦!

Kubernetes是一个开源系统,用于自动化部署、伸缩scaling、管理容器化应用。

概述

开发者使用 Kubernetes 有两种方式:命令行工具(如:kubectlkubeadm)、客户端库。而这些连接方式均是调用REST API,即调用Kubernetes API[官网] (本文以kubectl作为示例进行介绍)

和Docker一样,开发者进行部署有两种方式:

  • 通过使用YAML文件记录 Kubernetes 相关信息,然后使用kubectl apply -f 文件名命令进行运行。
  • 直接通过命令行部署。

当应用开发完毕后,应用将通过(Docker)容器进行包裹,通过上传最新版本的容器,可达到“发布应用”的效果。

当多个容器需要紧密的联系时,就需要 Pod 提供支持。[官网] Pod管理的信息有应用的版本、应用所需的网络资源和存储资源。需理解的是,容器必须存储在Pod当中,Pod是K8s部署的最小单位,不管Pod中有一个容器还是多个容器。

K8s中拥有各种控制器以及对应对象,有的用于确保集群中Pod的数量;有的用于确保每个节点都拥有一个Pod;有的用于定时操作。[官网]

在集群里,为了防止应用突然间停止而导致无法使用,一个应用所在的Pod往往有多份副本,其中一个Pod突然间停止也无所谓,有其他代替品。而每个Pod都有自身的IP地址,但由于Pod可能会突然间停止,所以我们不能固定地使用某一个Pod的IP地址,因此我们希望有一种机制,“当一个Pod不能使用时,该机制帮我们自动连接到其他Pod上”。这种机制就叫服务[Google Cloud]

k8s的默认服务只能在集群中调用,常见的用法是前端调用后端服务。而当要在集群外进行调用时,就可以使用云服务商提供的负载均衡器。

一个外部入口(如网站)只有一个IP地址,如果想通过一个外部入口访问集群里若干个服务时该怎么办呢?只能通过URL的路径进行映射。如:通过http://ip地址/服务1 访问 服务1http://ip地址/服务2 访问 服务2。这种机制叫做 Ingress(入口)

架构

redhat

图片来源:https://www.redhat.com/zh/topics/containers/kubernetes-architecture 注:亚马逊eks集群默认情况下有若干个组件是不启动的。[eks]

Control Plane

控制平面Control Plane位于主节点Master Node,包含主控件组Master、etcd。控制平面 通常用于与工作节点进行交互。[Redhat]

主控件

由于Master是由三个进程组成的,所以可以翻译为“主控件组”。Master所在的工作节点将会被指定为 主节点。[官网] 主控组件负责管理集群。[官网]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
            |--------------------|                            
command-->  |   kube-apiserver   | ---> change object's 
            |--------------------|      desired state          
                                             ↑
                                             | watch
                                |---------------------------| 
       change object's  <---    |   kube-contoller-manager  |
       current state            |---------------------------|
       
---------------------------------------------------------------

            |--------------------|                            
command-->  |   kube-apiserver   | ---> change object's 
            |--------------------|      desired state          
                                             ↑
                                             | watch
                                |---------------------------| 
      	    create object <---  |        kube-scheduler     |
                                |---------------------------|

kubectl cluster-info可以查看到主控件的IP地址。

创建一个的流程的详细流程可以参考:[Kubernetes in Action的11.2.2 事件链]

kube-apiserver

API服务器组件kube-apiserver(API server)用于与(集群的)外界进行通讯。API server将会判断接请求是否有效,如果有效就会处理。kubectl 等命令行实质就是和该组件通讯。

kube-scheduler

调度器kube-scheduler用于调度资源。观察是否存在新创建的Pod没有指派到节点,如果存在的话,则将其指派到其中一个节点上。

kube-controller-manager

控制器管理器kube-controller-manager通过控制器进行维护集群。从API server接收到的命令,将会修改集群某些对象的期待状态(desired state),控制器观察到这些期待状态的变化,就会将这些对象的当前状态(current state)变为期待状态(desired state)。[官网][官网]

  • 节点控制器Node controller:负责监视节点,当节点宕不可用时,进行通知。
  • 复制控制器Replication controller:负责维护每一个复制控制器对象所关联的Pod的数量正确性。
  • Endpoints controller:负责填充 Endpoints对象
  • 服务账号Service Account & 令牌控制器Token controllers:创建默认的账号和API访问令牌。

etc

etcd一致性和高可用的键值存储软件,用于备份 Kubernetes 的所有集群。[官网] 如Pod、控制器、服务、密钥等信息均需要要给持久化存储的位置,而该位置就是etc。

cloud-controller-manager

云控制器管理器cloud-controller-manager让你可以将集群连接上云服务提供商的API。[官网]

节点

当谈起节点Node,默认说的是对象是工作节点,而不是主节点

  • 每一个节点都会运行一个kubelet作为代理,与 Master 进行通信。kubelet确保容器在Pod中健康地运行[官网] 包括:
    • 创建Node资源进行注册
    • 启动Pod和其容器
    • 监控容器
    • 向API服务器组件kube-apiserver(API server)报告
  • 每一个节点都回运行一个kube-proxy作为网络代理。kube-proxy负责节点的网络规则,通过这些网络规则,你可以在通过集群内外的网络会话访问Pod。[官网] 包含:
    • 监听服务的正常
    • 更行相关规则。规则的运行模式可扩展阅读此处
  • 容器运行时Container runtime软件负责运行中的容器。[官网]

节点容量

亚马逊云对每一种虚拟机类型的Pods限制是不同,可参考

节点群

节点可以通过手动创建,也可以通过受管理的节点群Managed node groups创建,后者则更为自动化。

在亚马逊k8s中,可以通过以下方式创建节点群:

1
2
3
4
5
# 创建节点群,并指定节点数量
eksctl create nodegroup --cluster=${集群名} #--name=${节点群名称}  --nodes=${节点数量}
# 查看
eksctl get nodegroup --cluster=${集群名}

Control Plane的配置

1
2
3
4
5
6
7
8
9
10
11
12
# 查看所有命名空间下的Pod
[vagrant@localhost ~]$ kubectl get pods --all-namespaces 
NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
kube-system   coredns-6955765f44-lrt6z           1/1     Running   0          175d
kube-system   coredns-6955765f44-xbtc2           1/1     Running   1          175d
kube-system   etcd-minikube                      1/1     Running   1          175d
kube-system   kube-addon-manager-minikube        1/1     Running   1          175d
kube-system   kube-apiserver-minikube            1/1     Running   1          175d
kube-system   kube-controller-manager-minikube   1/1     Running   1          175d
kube-system   kube-proxy-69mqp                   1/1     Running   1          175d
kube-system   kube-scheduler-minikube            1/1     Running   1          175d
kube-system   storage-provisioner                1/1     Running   2          175d

组件是以Pod的形式,因此,我们可以使用以下命令行进行访问:

1
2
3
4
[vagrant@localhost ~]$ kubectl exec -it -n kube-system kube-apiserver-minikube -- /bin/sh
# kube-apiserver
W0715 13:56:17.176154      21 services.go:37] No CIDR for service cluster IPs specified. 
...

k8s中各个组件的命令行工具(如:kube-apiserver)都是事先安装好的,所以我们登陆后就能使用,不需要自行安装!

而在AWS中,eks取代了Contro Plane[eks],所以我们将无法通过上述方式访问组件并配置。

存储

存储方案有很多。托管式的有如:网络附加存储(NAS)、数据库、文件服务器;非托管式的则利用Kubernetes 存储抽象,如:[Google Cloud][Google Cloud][官网]

对于应用而言,将数据写入容器中的磁盘文件是最简单的途径,但这种方法存在缺陷。如果容器因其他任何原因崩溃或停止,文件将会丢失。此外,一个容器内的文件不可供同一 Pod 中运行的其他容器访问。 卷Volume可以解决这两个问题。

emptyDir

emptyDir:用于存储临时数据的简单空目录的临时卷

应用场景:

  • 一个Pod中拥有若干个容器,容器之间共享数据(通过磁盘传输数据)。
  • emptyDir.medium字段设置成Memory(存储介质为内存),以提高操作速度,同时又不用更改代码。

hostPath

hostPath:将工作节点的目录挂载到Pod中。

Chapter 6. Volumes: attaching disk storage to containers ...

应用场景:

  • 单节点持久化存储

Google Cloud Engine

gcePersistentDisk(GCE持久化磁盘),教程参考此处

AWS EC2

awsElasticBlockStore。( Amazon Web Service 弹性块存储卷)。

自搭服务器

可用nfs挂载到pod中的NFS共享卷。

其他

cinder、 cephfs、 iscsi、 flocker、 glusterfs、 quobyte、 rbd、 flexVolume、 vsphere- Volume、 photonPersistentDisk、 scaleIO 用于挂载其他类型的网络存储。

configMap、 secret、 downwardAPI:用于 将 Kubernetes部分资源和集群信息 公开给 pod 的特殊类型的 卷。

持久性卷

背景

开发团队中的开发者不应该了解底层硬件内容,而该职责为管理员。上述中的持久化存储卷要求开发者了解硬件,为了解决该问题,产生了持久性卷Persistent Volumes进行解耦

概览

1
2
3
4
5
6
---------	   -----------		----
|持久化卷 | <---|持久化卷声明| <---|Pod|
---------      -----------		-----
【管理员职责】 	【开  发  者 职  责】
	↓
 创建存储

在静态Static模式下,

  1. 集群管理员需要创建持久化卷
  2. 开发者想要为Pod配置持久化存储时,必须创建持久化卷声明PersistentVolumeClaim (通过它进行声明将要使用一个持久化卷)。
  3. 创建声明后,在Pod的配置中添加持久化声明即可。
1
2
3
4
5
6
---------	   -----------		----
|存储类   | <---|持久化卷声明| <---|Pod|
---------      -----------		-----
【管理员职责】 	【开  发  者 职  责】
						  ↓
 						创建存储							

在动态Dynamic模式下,

  • 管理员
    1. 创建存储类Storage Class,存储类用于描述可供使用的持久化方案有那些
  • 开发者
    1. 创建持久化卷声明。(持久化声明将自动根据存储类而 创建持久化卷)
    2. 创建声明后,在Pod的配置中添加持久化声明即可。

网络

服务相关的内容再此不重复。

Ingress

img

图片参考此处;Ingress的英文解释参考此处

Ingress 用于管理外部流量traffic该以什么规则进入集群,如何访问服务。Ingress 解决以下的问题:

只使用一个host[:port] 的情况下,如何若干个服务?

示例:Gitlab下有若干个服务

  • 通过访问 gitlab.IP地址.nip.io 就可以访问到 gitlab-webservice 服务。而该服务就是 Gitlab 的网站。
  • 通过访问 registry.IP地址.nip.io 就可以访问到 gitlab-registry 服务。通过该服务,可以快速将应用作为容器进行部署。

如何使用HTTPS(证书)?

自签名证书

自签名证书Self-Signed CA Certificate签发过程:

  1. 生成证书
  2. 添加到Sercert,进行秘密保管。
  3. 添加相关配置至YAML文件。[官网YAML]

DNS

在后续版本中,官网推荐使用 CoreDNS 代替 kube-dns 作为DNS服务器。[官网] 通过容器中的/etc/resolv.conf可以查看到域名服务器nameserver,如想修改该文件中的配置,应该通过kubectl命令行修改对应的ConfigMap而实现。

示例参考此处

集群内部流量

参考网络安全章节。

容器

容器章节内容可查看Docker - 维基百科

健康检查

K8s的对于容器的健康检查(Health Check)有三种:存活探测器Liveness Probe、就绪就绪探测器Readiness Probe、启动探测器Startup Probe[官网][官网]

执行顺序如下:

1
2
启动容器 -->  Startup Probe --->  Readiness Probe
                            |->  Liveness Probe

存活探测器

存活探测器Liveness Probe,用于探测容器是否运行(存活)。根据存活探测器类型不同,判断存活条件也将不同:

  • 命令行:返回码为0时即存活。[官网]
  • HTTP:返回码为2xx或3xx即存活。[官网]
  • TCP:连接容器指定的端口,连接成功即存活。[官网]

命令行类型示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: k8s.gcr.io/busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5 # 容器启动后5秒进行探测
      periodSeconds: 5  # 第一次探测后,每隔5秒进行探测
      failureThreshold: 10 # 失败10次后才被视为失败(默认值为3次)
      successThreshold: 2 # 在失败后,需成功2次后才被视为成功(默认为1次)
      timeoutSeconds: 1   # 探针检查时间的长度,当超过1时将会失败(默认1)

应用场景:

  • 服务器的正常运行

就绪探测器

就绪探测器Readiness Probe:当符合条件时,容器将被视为已就绪。就绪探测器的一个用途:当一个Pod的所有容器就绪后,将会告诉服务该Pod可被使用

存活探测器一样有三种分类:

  • 命令行:返回码为0时即就绪。
  • HTTP:返回码为2xx或3xx即就绪。
  • TCP:连接容器指定的端口,连接成功即就绪。

应用场景:

  • 应用启动时间很长
  • 依赖外部(不能直接使用)

启动探测器

启动探测器Startup Probe:当符合条件时,容器被视为已启动。当应用需要长时间进行启动时,启动探测 会在一定时间内不断地探测应用是否启动成功,当应用启动成功后,存活探测或就绪探测 可被启动;超过探测时间的话,容器将会被杀死,并且根据 restartPolicy 来做出相应操作。

开发与部署的配置

背景

开发环境与产品环境是存在差异,该差异体现在配置。通过环境变量的形式进行配置应用,可以不修改容器的情况下,同时在开发环境与产品环境下进行部署应用。但通过YAML描述文件配置环境变量存在一定的缺陷,如:

  • 环境变量数量过多
  • 传输对象仅限于字符串,不能传输文件。[k8s in action 代码清单7.13]
  • 如何保持环境变量处于最新状态。

ConfigMap 作为一个对象,对上述问题拥有更好的处理。而对于需保密内容,应该使用Secret,Secret使用内存存储数据。

Secret

外部将数据传入容器,并作为环境变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
...
# Sercet存储数据
data: # data->stringData 数据将不可见
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

apiVersion: v1
...
# Pod访问环境变量
spec:
  containers:
	...
    env:
    - name: SECRET_USERNAME # 环境变量名
      valueFrom:
        secretKeyRef:
          name: test-secret # Sercet名
          key: username # Sercet的键

示例:地址1地址2

传输数据,通过文件方式访问,文件可以通过base64形式存储。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: nginx
      volumeMounts:
        - name: [自定义卷名]
          mountPath: [secret挂载的位置]
  volumes:
    - name: [自定义卷名]
      secret:
        secretName: [secret名]
1
2
3
# secret中只有两个键时候
$ ls [secret挂载的位置]
password username

参考:地址1

ConfigMap

ConfigMap用法和Sercet类似,暂时省略。

开发应用

在开发应用期间,需要将源代码传输运行环境并运行,运行环境可以是普通的虚拟机,也可以是容器,但是考虑到开发环境要尽量与部署环境一致,以及基于K8s的开发与传统开发存在一定的差异,所以我们应该在容器中进行运行测试。

容器运行应用:需要将源代码传输到容器中。可以在容器中安装SSH,通过SFTP进行传输;通过更新Docker容器方式同步文件。而现在有不少工具进行辅助,参考:地址一地址二

同时,部署环境还要考虑到使用本地还是非本地,本地常常使用Minikube。

获取上层信息

容器的上一层是Pod,拥有IP、主机名等信息(元数据)。容器获得Pod信息有以下方式。

通过Downward API传输

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 方式1:将元数据以文件形式存储到容器里
spec:
  containers:
    - name: [容器名]
	  ...
      volumeMounts:
        - name: [自定义卷名]
          mountPath: [容器路径]
volumes:
    - name: [自定义卷名]
      downwardAPI:
        items:
          - path: "存储该元数据的文件名称" 
            fieldRef:
              fieldPath: metadata.labels #元数据的数据名
1
2
3
4
5
6
7
8
9
10
# 方式1:将元数据以环境变量形式存储到容器里
spec:
  containers:
    - name: test-container
      ...
      env:
        - name: [环境变量名]
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName #元数据的数据名

参考:地址1地址2

访问Rest API

通过访问Kubernetes API,直接获得信息。

  • 直接访问,需要手动导入证书。[官网]
  • 通过代理访问,需要启动代理,并从代理处访问。[官网]
  • Ambassador代理容器,从代理处访问。[k8s in action - 8.2.3]

推荐容器

busybox

1
kubectl run -i --tty busybox --image=busybox -- sh

安全

资源访问

资源访问和管理可以分以下层次:

当我们登陆AWS、Google Cloud等云服务时,需要用到账号,而每个账号能进行什么操作什么资源,这由IAMIdentity and Access Management管理。通过IAM,我们可以让某些账号只能访问kubernetes集群而不能写和删除。

而云服务除了kubernetes以外还有其他,在kubernetes集群中的资源管理,是由RBACRole-based access control进行管理。举例,通过RBAC,我们可以控制某些Pod只能访问kubernetes API中的哪些资源。

在更下一层次,就是容器。该层次和操作系统类似,有些资源只有超级用户root或者指定用户才访问,因此该层次的策略是指定容器操作的用户权限,如:指定某个容器内的操作全是普通用户权限运行超级用户权限

云服务层次资源

IAM模型分为三个部分:

  • 成员:Google 帐号、IAM账号等
  • 角色:角色包含授权的具体内容
  • 政策Policy: 权限

一个账号(主账号)可以生成若干个IAM账号,分发给其他人,主账号管理者其他IAM账号。而为了方便管理,产生了群Group概念:

1
2
3
4
5
6
7
		  |-[user-1]
 [group]--|-[user-2]
	↑	  |-[user-3] 		 
  attach
    |
 [policy]
[某个群]下面的[用户]都拥有[某个权限]
1
2
3
4
5
6
  [user]
	↑	  	 
  attach
    |
 [policy]
[某个用户]拥有[某个权限]

当我们有两个主账号时,仅仅通过上述方式的话,账号1的权限无法授予给账号2,我们必须通过角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|-------------------|
|[amazon account-1]	|
|   [policies]		|
|       |			|
|     attach		|
|       ↓			|
|-----[role]--------|
	   ↑
	   |
|----[policy]-------|
|       |			|
|	 attach			|
|	   ↓			|
|  [IAM account]	|
|  					|
|[amazon account-2]	|
|-------------------|

过程如下:

  1. 账号2将权限授予给账号1
  2. 账号1去访问角色,从而获得权限,而通常会把这权限分配给旗下的IAM用户(账号)

云=>集群

云层次和集群层次负责的权限不同,但是使用的主体往往是一样。举例:

  1. aws根用户创建了一个IAM用户鸦鸦
  2. k8s为其创建一个k8s用户,该配置过程可以通过以下配置文件:
1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapUsers: |
    - userarn: arn:aws:iam::[awsID]:user/鸦鸦
      username: 鸦鸦

aws的话可以使用eksctl命令进行创建。

  1. 创建一个角色,并使用角色绑定鸦鸦和该角色绑定在一起。
  2. 安装kubectl并修改凭证和ID,以鸦鸦用户身份登入k8s。

具体参考教程

集群层次资源

RBAC授权定义

RBAC授权Role-based access control  Authorization,可译为基于角色的访问控制授权。

1
2
[subjects]<--[RoleBinding]-->[role]
[subjects]<--[ClusterRoleBinding]-->[role]

举例:集群中有一个用户(subject)叫kyakya酱,而ta的角色(role)是管理员kyakya酱管理员RoleBinding绑定起来,并记录。

主体

主体subject有若干个选择:用户UserGroup服务账号Service Account[官网]

  • User用于分配个人权限。
  • Group常用于分配团队,例如:通过Group分配管理员、开发者、集成团队,示例参考
  • Service Account。
服务账号

通常操作者需要连接上集群,才能进行操作,而操作者的对象是REST API。我们通过以下方式可以在操作者的主机上直接查看REST API的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
# 获得存储在secrets里的默认Service Account的名称
DefaultTokenName=`kubectl get secrets -o jsonpath="{.items[*].metadata.name}" | grep -E 'default\-token'`

# 获得该Service Account的Token
Token=`kubectl get secrets ${DefaultTokenName} -o=jsonpath='{.data.token}' | base64 --decode`
# 获得该Service Account的证书,并保存为ca_cert.pem
kubectl get secrets ${DefaultTokenName} -o=jsonpath='{.data.ca\.crt}' | base64 --decode > ./ca_cert.pem

# 通过curl命令访问REST API的网址
curl \
--header "Authorization: Bearer ${Token}" \
--cacert ./ca_cert.pem \
-X GET https://2C1A77626A2087EBA1D1123EA9398DAF.gr7.ap-northeast-1.eks.amazonaws.com/api

背景1:

默认情况下在容器中,我们可以通过使用/var/run/secrets/kubernetes.io/serviceaccount/tokentoken直接访问API中的所有资源[官网] 允许所有的Pod都有权限访问token将造成安全问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ kubectl describe secrets default-token-6qzsw 
Name:         default-token-6qzsw
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: default
              kubernetes.io/service-account.uid: 2696c83d-c613-4926-8e53-ddd8e8e6bc9b

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  7 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6InY5NmhaZUNOTnJ6Tm1mbmdXU2JuZkhZV0ZUMWg2TlNuamk2TDdoaGYtLTgifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tNnF6c3ciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjI2OTZjODNkLWM2MTMtNDkyNi04ZTUzLWRkZDhlOGU2YmM5YiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.AtYCSzZXSMS2jnLL6G1DqbEEThJDl8PgFxz8FaXjcKVw50aVictWr88Xykgeni7ht63No_9mWQoDSCbUQXvRi1Q9rLdZL1QKj0v6fokxegnVbW1PlR_dJxQO9yq3AV1SbL02x6ERabEirZTETVUna56WVj8vUur_2rx4tg3SKETUI3oJdw8OoissB-jlJAUCjJQZrPvHAkuOD8oxRUFDHRrhI9uzCyq70f7Ayeto59Cxjw8ByG2N9zbLPX5PpLoy4cVDP1SeLIDuMiLJAQo5iz-kWbyggVKe4LT10BT0gCo5hEgJsqO79zuoN-QTerXhgBT5Q7MxFkiSmyKm7ChkXA

背景2:当一个容器想要访问亚马逊里其他的服务时,正常来说需要配置密钥ID和密钥,但是如果OpenID Connect (OIDC) 与服务账号结合,那么在部署Pod时,就不再需要手动配置密钥ID和密钥。示例

为了对访问权限进行限制,我们可以创建自定义服务账号ServiceAccount,再通RBAC授权进行关联,从而达到目的。

服务账号的配置文件如下:

1
2
3
4
apiVersion: v1
kind: ServiceAccount
metadata:
  name: [服务账号名]

并不包含任何权限配置,所以与需要RBAC进行关联。示例可参考此处

角色与角色绑定

RBAC API中有四种对象:

  • 角色Role 与 角色绑定RoleBinding
  • 集群角色ClusterRole 与 集群角色绑定ClusterRoleBinding

操作范围:集群角色>角色

角色

角色:用于设置命名空间的权限。

示例参考

1
2
3
4
5
6
7
8
9
10
# 授权对命名空间"default"中的Pods的读取权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default	# 角色所在的命名空间
  name: pod-reader	
rules:
- apiGroups: [""] # "" 对象API是kubrnetes API
  resources: ["pods"]  # 允许操作Pods
  verbs: ["get", "watch", "list"] # 允许get list watch

rules下的字段分别意味着:

  • apiGroups:API群API groups,kubrnetes API的一组唯一的相对路径。 API groups的存在为了让kubrnetes API更好地扩展。[官网] 在这里,我们可以把API groups视为一种命名空间即可。 当apiGroups的值为""时表示为kubrnetes API。其他可参考kubectl api-resources官网
  • resources:该命名空间下可操作的资源。值参考官网
  • verbs:可操作的动作。值参考此处
  • resourceNames:资源名称(可选)
角色绑定

角色绑定是讲角色用户绑定在一起。[官网]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: rbac.authorization.k8s.io/v1
# 讲角色"pod-reader"与用户"jane"绑定
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User # User/Group/ServiceAccount
  name: jane # "name" is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role # Role/ClusterRole
  name: pod-reader # 角色
  apiGroup: rbac.authorization.k8s.io
集群角色

集群角色:用于设定非命名空间资源的权限。[官网]

1
2
3
4
5
6
7
8
9
10
# 授权对所有/单个命名空间中的secrets的读取权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # 此处的 "namespace" 被省略掉是因为 ClusterRoles 是没有命名空间的。
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]
集群角色绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: rbac.authorization.k8s.io/v1
# 这个角色绑定允许 "dave" 用户在 "development" 命名空间中有读取 secrets 的权限。 
kind: RoleBinding
metadata:
  name: read-secrets
  namespace: [命名空间] # 如development
subjects:
- kind: User
  name: dave # 名称区分大小写
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

容器层次资源

语境

在默认的语境Context下,容器是使用是超级用户root。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000   # 用户ID,超级用户是0
    runAsGroup: 3000
    fsGroup: 2000
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      allowPrivilegeEscalation: false

不为超级用户:[参考手册]

1
2
  securityContext:  
    runAsNonRoot: true

网络安全

控制流量的方向,如:控制流量只能从前端流向后端。

Calico

示例1:由于from为空,所以没有任何流量流向名为stars的命名空间。换言之禁止流量流向stars的命名空间。

1
2
3
4
5
6
7
8
9
10
# default-deny.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: default-deny
spec:
  podSelector:
    matchLabels: {}  #
#  ingress:
#    - from:
1
kubectl apply -n stars -f default-deny.yaml

示例2:服务management-ui的流量可流向stars命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# allow-ui.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  namespace: stars
  name: allow-ui
spec:
  podSelector:
    matchLabels: {}
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              role: management-ui
# kubectl apply -f allow-ui.yaml

示例三:client服务的流量可通往stars命名空间的frontend服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# frontend-policy.yaml
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  namespace: stars
  name: frontend-policy
spec:
  podSelector:
    matchLabels:
      role: frontend
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              role: client
      ports:
        - protocol: TCP
          port: 80
# kubectl apply -f frontend-policy.yaml

日志

容器化应用写入 stdoutstderr 的任何数据,都会被容器引擎捕获并被重定向到节点的 /var/log/containers//var/log/pods/[Logging Architecture]

1
2
kubectl logs [Pod名字]			 # 查看当前日志
kubectl logs [Pod名字] --previous  # 查看崩溃前日志

管理方式

//todohttps://kubernetes.io/zh/docs/concepts/cluster-administration/logging/

K8s扩展

K8s得扩展包含:

  • 自定义对象
  • 自定义控制器
  • 自定义API服务器API Server

在没有使用自定义对象之前,我们使用K8s可以说是基于对象的编程,但是一旦自定义对象后,我们可以视之为面向对象的编程。就像Java等面向对象编程语言那样,通过自定义类/模板文件,可以简化系统。

术语

Kubernetes API

Kubernetes API是REST API 是,Kubernetes 的基础组件。组件间的操作和通信,以及外部用户命令都是通过该API完成的,因此,Kubernetes平台里的任何对象都可以说是该API的执行对象。该API由 API服务器(kube-apiserver)管理。

对象特征

K8s把对象分为两个状态:期望状态Desired State当前状态Current State

通过 kubectl get pods [Pod名字] -o json 命令,开发者可以查看对象对象当前状态期望状态

1
2
3
4
5
6
7
8
# kubectl get pods [Pod名字] -o json
{
  "apiVersion": "v1",   	# API版本
  "kind": "Pod",			# 对象类型
  "metadata": {...},		# 元数据
  "spec": {...},   			# 期待状态
  "status": {...}, 			# 当前状态
}
  • apiVersion : Kubernetes API 的版本。
  • kind:对象的类型。常见的对象有:PodDeployment
  • metadata - 帮助识别对象唯一性的数据。
    • name 名字,每个类型的资源之间名字不可以重复。调用命令时常用的参数就是名字。
    • UID ,每个生命周期的每个对象UID都不同,用于标识对象的历史。[官网]
    • namespace命名空间(可选的) ,用于需要跨团队或跨项目的场景。[官网]

标签和选择器

选择器分为:

  • 标签选择器Label selector,在yaml定义文件名为selector,操作对象为标签 label
  • 字段选择器Field selector,用于命令查找对象时进行筛选,操作对象是对象几乎所有字段。示例:kubectl get pods --field-selector status.phase=Running
  • 节点选择器Node selector,在yaml定义文件名名为nodeSelector,操作对象为拥有特定标签的节点。

标记物:

  • 标签Label,自定义元数据(位置metatdata/labels),标签选择器节点选择器的标记数据。
  • Annotation,自定义元数据(位置metatdata/annotation**),其他用途
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
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
  annotation: 
  	....       
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
        nodeSelector:
		    accelerator: nvidia-tesla-p100

开发者可通过选择器(全称:标签选择器,Label selector)查找到对应拥有标签(label)的对象,并其进行指定。标签是键值对结构体数据。选择器有两种:基于相等性的equality-based基于集合的set-based基于相等性 意味着运算符是以等号、不等号为主。 基于集合 意味着操作符以innotinexists为主。

该如何使用标签进行管理呢?官网推荐使用 前缀/名称 对标签进行命名,前缀是为了区别不同用户的对象。名称的命名可参考:官网[非官网来源]

未分类对象

Endpoint

Endpoint对象<译:终点>是具体的URL,具体可以参考REST的Endpoint。[官方未定义-2020.06.01]

基础对象

Pod

Pod(直译:豆荚)是K8s的最小单元(atomic unit)。Pod封装了若干个容器,各容器可共享同一IP地址端口、存储资源(存储卷)。

通常,一个Pod只会装载一个容器,除非各容器间需要使用文件系统(存储资源)进行通讯。

参考:官网Google Cloud

命名空间

通常,集群会生成一个默认命名空间namespace去装载Pod、服务、Deployment等。命名空间使用场景:当开发团队和产品团队同时使用,希望不互相影响。命名空间可以从默认切换至自定义。[官网]

命名空间只是对操作进行隔离,并不对网络进行隔离。[Kubernetes in Action]

服务

服务service,可以理解为逻辑上的Pod。开发者通过服务的DNS名称名(DNS name)可以找到服务,然后通过服务可以调用某一Pod。[官网] 调用方 通过调用服务的方式,避免了调用方与Pod的耦合,这样当Pod宕机时,也不会影响到调用方,这也可用于负载均衡服务发现等场景。[官网]

1
2
3
4
5
6
7
                         选择一个Endpoint进行调用
            |-----------| -------- Endpoint 1 -----> [Pod 1 暴露端口:80 IP:10.244.0.25]
            |           |        [10.244.0.25:80]
   调用 -->  | service   | -------- Endpoint 2 -----> [Pod 2 暴露端口:80 IP:10.244.0.26] 
            |10.96.92.53|        [10.244.0.26:80]      
            |-----------| -------- Endpoint 3 -----> [Pod 3 暴露端口:80 IP:10.244.0.27] 
                                 [10.244.0.27:80]
发布服务

K8s有以下发布服务Publishing Services方式:[官网][Google Cloud]

  • ClusterIP(默认类型):只有在集群Cluster里的IP才能访问该服务。
  • 节点端口NodePort:通过每个节点Node的某一特定端口Port访问该服务。
  • 负载均衡器LoadBalancer:通过云服务商的负载均衡器访问该类型服务。
  • 外部名字ExternalName:需访问的服务在集群外部, 在集群内部通过访问该服务的DNS名称,从而进行访问外部服务名字。
  • 无头服务Headless Services:这里的无头意味着没有clusterIP。而当我们使用 nslookup 等查看该服务的DNS时,将会发现对应的地址指向了该服务使用的Pod。[参考示例]

通过节点 IP地址 进行暴露服务,可使用;通过云服务提供商的负载均衡器暴露服务,则使用LoadBalancer;而当服务不在集群内,在集群之外,可以使用ExternalName 模式的服务进行重定向。

发布服务可使用kubectl expose[官网]

1
 kubectl expose deployment hello-world --type=LoadBalancer --name=my-service
发现服务

在Pod初始化时,将会把服务的IP记录到该Pod的环境变量里。kubectl exec [Pod名] env 可查看到环境变量的内容。

另一种则是通过DNS完整域名Fully Qualified Domain Name查找服务。查找地址:

1
2
# svc:service
[对象名].[命名空间].svc.cluster.local
会话亲和性

如果浏览器开启 keep-alive 设置,则拥有会话亲和性Session Affinityservice.spec.sessionAffinity 设置成 ClientIP拥有会话亲和性[官网][Kubernetes in Action-5.3.3]

本地服务优先

spec.externalTrafficPolicy 外部流量策略设置为local时,服务调用方将优先使用本地的服务,可是存在缺点:本地缺少服务时将挂起、可能导致服务流量不均匀

高级对象

高级对象是依赖于控制器 ,而控制器是建立于基础对象之上,并为之添加功能性和方便性。[官网]

ReplicationController

ReplicationController 用于确保Pod的数量正常。

ReplicaSet

ReplicaSet 是 ReplicationController 的升级版本,ReplicaSet 的标签选择器更加灵活。

Deployment

Deployment控制器是ReplicaSet的升级版本,并且基于ReplicaSet,对更新Pod该操作进行优化。

Job

Job用于执行一次性one-off任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never # always重启、Never不重启、OnFailure失败重启
  backoffLimit: 4  # 尝试4次会视为失败
  activeDeadlineSeconds: 100 # 100秒内必须执行成功,否则中止

CronJob

Cron任务类型。

DaemonSet

DaemonSet(Daemon:守护进程) 为了每一个节点分配有且只有一个Pod,使用场景有:[官网][Google CLoud]

  • 存储用的守护进程。
  • 日志收集的守护进程。
  • 节点监控的守护进程。

StatefulSet

StatefulSet 用于管理具有特殊性的 Pod ,换言之,每一个 Pod 都是不一样。这种特殊的内容,就可以叫做状态。一般而言,一个 MySQL 的 Pod 将指定一个 Storage volume,而每一个 Storage volume 都是不一样。如果要管理若干个 MySQL 的 Pod,那么就需要使用 StatefulSet

常见讨论

容器 vs Pod

#todo

命令

kubectl describe vs get

kubectl describe pods [pod名字]kubectl get pods [pod名字]究竟有什么区别?通过 kubectl describe --help 命令,可以获取describe相关信息:

Show details of a specific resource or group of resources

Print a detailed description of the selected resources, including related resources such as events or controllers. You may select a single object by name, all objects of that type, provide a name prefix, or label selector. For example:

kubectl get 的区别在于:

  • kubectl get 包含资源信息
  • kubectl describe 包含:资源、事件event 、控制器controller

kubectl get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# wide中文:广泛的
# 输出Pods的更多信息
kubectl get pods --output wide

# Custom columns用法
# 通过下述类似语句查看结构
kubectl get daemonsets aws-node  --namespace=kube-system -o yaml

# 第一栏名为NAME,值为.metadata.name的值
# 第二栏名为RSRC,值为.metadata.resourceVersion的值
kubectl get daemonsets aws-node \
--namespace=kube-system \
 -o custom-columns=\
NAME:.metadata.name,\
RSRC:.metadata.resourceVersion

kubectl create vs apply

kubectl apply - Apply or Update a resource from a file or stdin.[原址]

kubectl apply:创建、更新资源;kubectl create:创建资源

kubectl attach vs exec

前者是附属于主程序,后者是在容器中运行一个进程。

kubectl删除

kubectl cordon

cordon,英文解释:

Prevent access to or from an area or building by surrounding it with police or other guards.

中文译为:封锁。

阻止新的Pods加入该节点。[官网]

1
kubectl cordon $NODENAME
kubectl drain

drain,名词译为下水管,在这里可以理解成“排出”。

云平台下,意味着删除虚拟机。其他情况下,意味着关掉物理机。[官网]

1
kubectl drain <node name>

如何使用SSH?

在传统软件开发,当leader等人部署好系统环境后,团队将通过自动或手动的方式将代码传送到服务器上,并对服务进行测试。这里开发者们常见的操作有:访问服务(在指定服务器外进行访问)访问服务器资源(在指定服务器内进行访问)

如果服务器在局域网内,开发者们则通过SSH则可以访问服务器资源;直接访问服务。

如果服务器不再局域网内,则通过SSH两次跳转的方式(第一次是SSH服务器,第二次是指定服务器),然后访问服务器资源。在SSH两次跳转后,通过SSH端口转发,将本地端口转发指定服务器,通过访问本地的端口,从而间接访问服务器端口。

k8s 访问服务官网

1
2
3
4
5
$ kubectl port-forward [Pod名字] [本地端口]:[远程端口]
Forwarding from 127.0.0.1:[本地端口] -> [远程端口]
Forwarding from [::1]:[本地端口] -> [远程端口]
Handling connection for [本地端口]
Handling connection for [本地端口]

访问服务器资源官网

1
2
3
4
5
6
# 运行一个软件
kubectl exec [Pod名字] [命令]
# 交互式
kubectl exec -it [Pod名字] -- /bin/bash
# 访问多容器Pod中的某一容器
kubectl exec -it [Pod名字] --container [容器名] -- /bin/bash

代理服务器

//TODO

kubectl proxy 该命令将会生成代理,通过该代理,我们能直接访问 REST API 。通过http://[代理IP]:[端口]/api 等网址可以查看集群各种信息。

部署很麻烦!

有时候我们想要一次性创建Deployment、Service、Loaderbalance,我们可以通过脚本一次性执行,也可以通过工具helm(头盔)