Funky's NoteBook

Kubernetes Information Collection

字数统计: 15,569阅读时长: 62 min
2018/07/17 Share

kubernetes 架构

分层架构

分层架构

  • 核心层:Kubernetes最核心的功能,对外提供API构建高层的应用,对内提供 插件式应用执行环境

  • 应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路 由(服务发现、DNS解析等)

  • 管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩 展、动态Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy 等)

  • 接口层:kubectl命令行工具、客户端SDK以及集群联邦

  • 生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为

    两个范畴 :

    • Kubernetes外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS应用 、ChatOps等
    • Kubernetes内部:CRI、CNI、CVI、镜像仓库、CloudProvider、集群自身的配置 和管理等

架构设计优缺点

  • 优点

    • 容错性:保证Kubernetes系统稳定性和安全性的基础
    • 易扩展性:保证Kubernetes对变更友好,可以快速迭代增 加新功能的基础。
    • API分版本,API可自由扩展(CRD)
    • 插件化,调度器,容器运行时,存储均可扩展
    • 声明式(Declarative)的而不是命令式(Imperative): 声明式操作在分布式系统中的好处是稳定,不怕丢操作 或运行多次,例如设置副本数为3的操作运行多次也还是 一个结果,而给副本数加1的操作就不是声明式的,运行 多次结果就错了。
  • 缺点

    • 配置中心化:所有状态都保存在中心的etcd上,而非分布

      式存储,性能有一定制约

    • 单体调度:调度一致性好而吞吐低

      调度

k8s提供的抽象

架构

EN CN
Container 容器
Pod 容器组
ReplicaSet 副本组合
Service 服务
Label 标签
Node 节点
  • Pod

    pod

    • Pod是在Kubernetes集群中运行部署应用或服务的最小单元,它是可以支持多容器的。Pod的设计理念是支持多个 容器在一个Pod中共享网络地址和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务 。Pod对多容器的支持是K8最基础的设计理念。比如你运行一个操作系统发行版的软件仓库,一个Nginx容器用 来发布软件,另一个容器专门用来从源仓库做同步,这两个容器的镜像不太可能是一个团队开发的,但是他们 一块儿工作才能提供一个微服务;这种情况下,不同的团队各自开发构建自己的容器镜像,在部署的时候组合 成一个微服务对外提供服务。
    • Pod是Kubernetes集群中所有业务类型的基础,可以看作运行在K8集群中的小机器人,不同类型的业务就需要不 同类型的小机器人去执行。目前Kubernetes中的业务主要可以分为长期伺服型(long-running)、批处理型( batch)、节点后台支撑型(node-daemon)和有状态应用型(stateful application);分别对应的控制器为 Deployment、Job、DaemonSet和PetSet。
  • 副本集 (Replica Set,RS)

    Replica Set:Kubernetes集群中保证Pod高可用的API对象。 通过监控运行中的Pod来保证集群中运行指定数目的Pod副本 。指定的数目可以是多个也可以是1个;少于指定数目,RS 就会启动运行新的Pod副本;多于指定数目,RS就会杀死多 余的Pod副本。即使在指定数目为1的情况下,通过RS运行 Pod也比直接运行Pod更明智,因为RS也可以发挥它高可用 的能力,保证永远有1个Pod在运行。RS适用于长期伺服型的 业务类型,比如提供高可用的Web服务。

  • 服务 (Service)

    service

    RS只是保证了支撑服务的微服务Pod的 数量,但是没有解决如何访问这些服务 的问题。一个Pod只是一个运行服务的 实例,随时可能在一个节点上停止,在 另一个节点以一个新的IP启动一个新的 Pod,因此不能以确定的IP和端口号提 供服务。要稳定地提供服务需要服务发 现和负载均衡能力。服务发现完成的工 作,是针对客户端访问的服务,找到对 应的的后端服务实例。在K8集群中, 客户端需要访问的服务就是Service对象 。每个Service会对应一个集群内部有效 的虚拟IP,集群内部通过虚拟IP访问一 个服务。

    kubernetes 中的对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
status:
...
  • Metadata:标识API对象,每个对象都至少有3个元数据: namespacenameuid;除此以外还有各种各样的 标签 labels 用来标识和匹配不同的对象,例如用户可以 用标签env来标识区分不同的服务部署环境,分别用 env=devenv=testingenv=production来标识开发、 测试、生产的不同服务
  • Spec: 描述了用户期望Kubernetes集群中 的分布式系统达到的理想状态(Desired State),例如用户可以通过复制控制器 Replication Controller设置期望的Pod副本 数为3
  • Status:系统实际当前达到的状态(Status) ,例如系统当前实际的Pod副本数为2;那么复制 控制器当前的程序逻辑就是自动启动新的Pod, 争取达到副本数为3

对象分类

类别 名称
资源对象 Pod、ReplicaSet、ReplicationController、Deployment 、StatefulSet、DaemonSet、Job、CronJob、 HorizontalPodAutoscaling
配置对象 Node、Namespace、Service、Secret、ConfigMap、 Ingress、Label、ThirdPartyResource、 ServiceAccount
存储对象 Volume、Persistent Volume
策略对象 SecurityContext、ResourceQuota、LimitRange

业界各种安装方式对比

安装方式 安装前准备 特点 高可用 复杂度
kubeadm kubectl + kubelet 不对接节点,自己准备 节点环境,容易被集成 到其他工具链中 HA(T+)
kops kubectl 对接AWS/GCE/Vmware 帮助你管理虚机 HA
minikube kubectl 单机对接VM
rancher rancher 墙内加速,跨云能力 不清楚
手工 11+组件 任意集群 HA 疯狂

当pod创建时,k8s到底干了啥

pod_create

  • kubectl 提交创建请求,可以通过API Server的Restful API,也可以使用kubectl命令行工具。支持的数据类型包括JSON和YAML。
  • kube-apiserver 处理用户请求,存储Pod数据到etcd。
  • kube-scheduler 通过API Server查看未绑定的Pod。尝试为Pod分配主机。
    • 过滤主机 (调度预选):调度器用一组规则过滤掉不符合要求的主机。比如 Pod 指定了所需要的资源量,那么可用资源比 Pod 需要的资源量少的主机会被过滤掉。
    • 主机打分(调度优选):对第一步筛选出的符合要求的主机进行打分,在主机打分阶段,调度器会考虑一些整体优化策略,比如把容一个 Replication Controller 的副本分布到不同的主机上,使用最低负载的主机等。
  • kube-scheduler 选择主机:选择打分最高的主机,进行 binding 操作,这个操作本质是通过kube-apisever修改Pod的字段,结果存储到etcd中。

  • kubelet 根据调度结果执行Pod创建操作: 绑定成功后,pod.spec.nodeName有值了。运行在每个工作节点上的kubelet也会定期与etcd同步pod信息(属于自己这个node的)

  • docker接受到kubelet下发的命令,启动相应容器,至此,一个Pod启动完毕

kubectl自动补全:

1
2
3
> $ source <(kubectl completion bash) #bash下自动补全
> $ source <(kubectl completion zsh) # zsh下自动补全
>

k8s免费在线lab网站: https://labs.play-with-k8s.com/

kubectl 常见命令

详见:Cheat Sheet

kubectl上下文和配置

1
2
3
4
5
6
7
$ kubectl config view # 显示合并后的 kubeconfig 配置
$ kubectl config current-context # 显示当前的上下文
$ kubectl config use-context my-cluster-name # 设置默认上下文为 my-cluster- name
# 使用指定的用户名和 namespace 设置上下文
$ kubectl config set-context gce --user=cluster-admin --namespace=foo \
&& kubectl config use-context gce
# kubectl config current-context –h 帮助及例子

kubectl 创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ kubectl create -f ./my-manifest.yaml # 创建资源
$ kubectl create -f ./my1.yaml -f ./my2.yaml # 使用多个文件创建资源
$ kubectl create -f ./dir
$ kubectl create -f https://git.io/vPieo
$ kubectl run nginx --image=nginx
$ kubectl explain pods # 获取 pod 和 svc 的文档

# 创建包含几个 key 的 Secret
$ cat <<EOF | kubectl create -f - apiVersion: v1
kind: Secret
metadata:
name: mysecret type: Opaque data:
password: $(echo "s33msi4" | base64)
username: $(echo "jane" | base64) EOF

kubectl显示和查找资源

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
# Get commands with basic output $ kubectl get services

$ kubectl get pods --all-namespaces
# 列出所有 namespace 中的所有 service

$ kubectl get pods -o wide
# 列出所有 namespace 中的所有 pod

$ kubectl get deployment my-dep
# 列出所有 pod 并显示详细信息 # 列出指定 deployment

$ kubectl get pods --include-uninitialized # 列出该 namespace 中的所有 pod 包 括未初始化的


# 使用详细输出来描述命令
$ kubectl describe nodes my-node $ kubectl describe pods my-pod
$ kubectl get services --sort-by=.metadata.name # List Services Sorted by Name

# 根据重启次数排序列出 pod
$ kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'

# 获取所有具有 app=cassandra 的 pod 中的 version 标签 $ kubectl get pods --selector=app=cassandra rc -o \
jsonpath='{.items[*].metadata.labels.version}'

# 获取所有节点的 ExternalIP
$ kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'

# 根据重启次数排序列出 pod
$ kubectl get pods --sort-by='.status.containerStatuses[0].restartCount'

# 获取所有具有 app=cassandra 的 pod 中的 version 标签 $ kubectl get pods --selector=app=cassandra rc -o \
jsonpath='{.items[*].metadata.labels.version}'

# 获取所有节点的 ExternalIP
$ kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'

# 列出属于某个 PC 的 Pod 的名字
# “jq”命令用于转换复杂的 jsonpath,参考 https://stedolan.github.io/jq/
$ sel=${$(kubectl get rc my-rc --output=json | jq -j '.spec.selector | to_entries | .[] | "\(.key)=\(.value),"')%?}
$ echo $(kubectl get pods --selector=$sel --output=jsonpath={.items..metadata.name})

# 查看哪些节点已就绪
$ JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}' \
&& kubectl get nodes -o jsonpath="$JSONPATH" | grep "Ready=True"

# 列出当前 Pod 中使用的 Secret
$ kubectl get pods -o json | jq '.items[].spec.containers[].env[]?.valueFrom.secretKeyRef.name' | grep -v null | sort | uniq

kubectl编辑资源

1
2
$ kubectl edit svc/docker-registry # 编辑名为 docker-registry 的 service
$ KUBE_EDITOR="nano" kubectl edit svc/docker-registry # 使用其它编辑器

kubectl Scale 资源

1
2
3
4
$ kubectl scale --replicas=3 rs/foo # Scale a replicaset named 'foo' to 3
$ kubectl scale --replicas=3 -f foo.yaml # Scale a resource specified in "foo.yaml" to 3
$ kubectl scale --current-replicas=2 --replicas=3 deployment/mysql # If the deployment named mysql's current size is 2, scale mysql to 3
$ kubectl scale --replicas=5 rc/foo rc/bar rc/baz # Scale multiple replication controllers

kubectl 删除资源

1
2
3
4
5
$ kubectl delete -f ./pod.json                                              # Delete a pod using the type and name specified in pod.json
$ kubectl delete pod,service baz foo # Delete pods and services with same names "baz" and "foo"
$ kubectl delete pods,services -l name=myLabel # Delete pods and services with label name=myLabel
$ kubectl delete pods,services -l name=myLabel --include-uninitialized # Delete pods and services, including uninitialized ones, with label name=myLabel
$ kubectl -n my-ns delete po,svc --all # Delete all pods and services, including uninitialized ones, in namespace my-ns,

kubectl与运行中的 Pod 交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ kubectl logs my-pod
$ kubectl logs my-pod -c my-container pod 中有多个容器的情况下使用)
$ kubectl logs -f my-pod
$ kubectl logs -f my-pod -c my-container 中有多个容器的情况下使用)
# dump 输出 pod 的日志(stdout)
# dump 输出 pod 中容器的日志(stdout,
# 流式输出 pod 的日志(stdout)
# 流式输出 pod 中容器的日志(stdout,pod
$ kubectl run -i --tty busybox --image=busybox -- sh # 交互式 shell 的方式运行 pod
$ kubectl attach my-pod -i
$ kubectl port-forward my-pod 5000:6000 端口
$ kubectl exec my-pod -- ls / 下)
# 连接到运行中的容器
# 转发 pod 中的 6000 端口到本地的 5000
# 在已存在的容器中执行命令(只有一个容器的情况
$ kubectl exec my-pod -c my-container -- ls / # 在已存在的容器中执行命令(pod 中有多个 容器的情况下)
$ kubectl top pod POD_NAME --containers # 显示指定 pod 和容器的指标度量

kubectl与节点和集群交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ kubectl cordon my-node
$ kubectl drain my-node
$ kubectl uncordon my-node
$ kubectl top node my-node
$ kubectl cluster-info
$ kubectl cluster-info dump
# 标记 my-node 不可调度 # 清空 my-node 以待维护
# 标记 my-node 可调度
# 显示 my-node 的指标度量
# 显示 master 和服务的地址
# 将当前集群状态输出到 stdout
$ kubectl cluster-info dump --output-directory=/path/to/cluster-state # 将当前集群状态输出
到 /path/to/cluster-state
# 如果该键和影响的污点(taint)已存在,则使用指定的值替换
$ kubectl taint nodes foo dedicated=special-user:NoSchedule

其他获取帮助的途径

  • 使用命令

    1
    2
    > kubectl * -h
    >
  • 使用 python

    1
    2
    > pip install kube-shell
    >

k8s构建高可用集群

k8s_ha

k8s构建高可用集群方案介绍(点我跳转)

  • Establishing a redundant, reliable data storage layer
    • Clustering etcd
      • Validating your cluster
    • Even more reliable storage
  • Replicated API Servers
    • Installing configuration files  Starting the API Server
    • Load balancing
    • Endpoint reconciler
  • Master elected components
    • Installing configuration files

k8s应用管理

pod详解

pod

  • Pod就像是豌豆荚一样,它由一个或者多个容器组成
  • Pod中的容器共享IP地址和端口号,它们之间可以通过localhost互相发现 。它们之间可以通过进程间通信,例如SystemV信号或者POSIX共享内 存。不同Pod之间的容器具有不同的IP地址,不能直接通过IPC通信。
  • Pod中的容器也有访问共享volume的权限,这些volume会被定义成pod的 一部分并挂载到应用容器的文件系统中。

pod的设计动机

  • 容器介于操作系统和应用之间,容器的推荐玩法是每个容器运行一个进程。

    • 外部掌控多容器的组合和生命周期 — redhat和docker公司的控制权 之争
    • 单容器多进程玩法的案例 — 阿里的pouch项目
  • 对外,Pod作为一个独立的部署单位,支持横向扩展和复制。 共生(协同调度),命运共同体(例如被终结),协同复制 ,资源共享,依赖管理

  • 对内,Pod内容器互相协作

    • pod中的应用必须协调端口占用。每个pod都有一个唯一的IP地址, 跟物理机和其他pod都处于一个扁平的网络空间中,它们之间可以 直接连通。

    • Pod中应用容器的hostname被设置成Pod的名字。

    • Pod中的应用容器可以共享volume。Volume能够保证pod重启时使

      用的数据不丢失。

pod的非持久性

Pod在以下几种情况下都会被终结:

  • 调度失败
  • 节点故障
  • 缺少资源
  • 节点维护
  • 用户主动干掉Pod

init容器

  • Init 容器是一种专用的容器,在应用程序容器启动之前运行

  • init 容器总是运行到成功完成为止。

  • 每个 Init 容器都必须在下一个 Init 容器启动之前成功完 成。

  • Init 容器能做什么?

    • 等待一个 Service 创建完成,通过类似如下 shell 命令:

      for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; exit 1

    • 在启动应用容器之前等一段时间,使用类似 sleep 60 的命令。

Pod中容器镜像常见设置

pod.spec.containers[]. imagePullPolicy 镜像拉取策略
IfNotPresent(默认) 在没有此镜像时下载
Always 每次必下载
镜像tag设置 拉取策略
Image:latest 每次必下载
image 每次必下载
image:* 在没有此镜像时下载

扩展:

Pod.status.phase

容器状态 描述
挂起(Pending) Pod 已被 Kubernetes 系统接受,但有一个或者多个容器 镜像尚未创建。等待时间包括调度 Pod 的时间和通过网 络下载镜像的时间,这可能需要花点时间。
运行中(Running) 该 Pod 已经绑定到了一个节点上,Pod 中所有的容器都 已被创建。至少有一个容器正在运行,或者正处于启动 或重启状态。
成功(Successed) Pod 中的所有容器都被成功终止,并且不会再重启。
失败(Failed) Pod 中的所有容器都已终止了,并且至少有一个容器是 因为失败终止。也就是说,容器以非0状态退出或者被 系统终止。
未知(Unkonwn) 因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败。

容器探针(注意不是Pod探针)

  • 探针探查方式
    • ExecAction:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
    • TCPSocketAction:对指定端口上的容器的 IP 地址进行 TCP 检查。如果端口打开,则诊断被认为是成功的。
    • HTTPGetAction:对指定的端口和路径上的容器的 IP 地址执行 HTTP Get 请求。如果响应的状态码大于等于200 且小于 400,则诊断被认为是成功的。
  • 探针结果:成功、失败、未知
  • 探针引起外部动作
    • livenessProbe:指示容器是否正在运行。如果存活探测失败,则 kubelet 会杀死容器,并且容器将受到其 重启策略 的影响。如果容器 不提供存活探针,则默认状态为 Success。
    • readinessProbe:指示容器是否准备好服务请求。如果就绪探测失败 ,端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址。初始延迟之前的就绪状态默认为 Failure。如果容器不提供 就绪探针,则默认状态为 Success

容器重启策略

Pod.spec.restart Policy字段 行为:适用于Pod中所有失败的容器
Always(默认) exitCode=任何数字,执行重启操作
OnFailure exitCode!=0,执行重启操作
Never exitCode=任何数字,不重启
以五分钟为上限的指数退避延迟(10秒,20秒,40秒… )重新启动,并在成功执行十分钟后重置。

更多信息:

1
2
> kubectl run -h
>

pod状态与容器状态的关系

容器 退出状态 log event restartPolicy 行为 Pod.status.phase
1个 success completion Always 重启容器 Running
1个 success completion OnFailure Succeeded
1个 success completion Never Succeeded
1个 fail failure Always 重启容器 Running
1个 fail failure OnFailure 重启容器 Running
1个 fail failure Never Failed
2个 一个fail一个正常运行 failure Always 重启容器 Running
2个 一个fail一个正常运行 failure OnFailure 重启容器 Running
2个 一个fail一个正常运行 failure Never Running
2个 一个not running一个exit failure Always 重启容器 Running
2个 一个not running一个exit failure OnFailure 重启容器 Running
2个 一个not running一个exit failure Never Failed
1个 out of memeory OOM Always 重启容器 Running
1个 out of memeory OOM OnFailure 重启容器 Running
1个 out of memeory OOM Nerver Failed
n个 磁盘崩溃 failure A/O/N 杀死所有容器 如果pod被管 控,异地重建 Failed
n个 node被分区 failure A/O/N 等待node超时 如果pod被管 控,异地重建 Failed

ReplicaSet: 副本集

ReplicaSet是下一代复制控制器。现在ReplicaSetReplication Controller之间的唯一区别是选择器支持。ReplicaSet支持标签用户指南中描述的新的基于集合的选择器要求,而Replication Controller仅支持基于等同的选择器要求。

大多数kubectl支持复制控制器的命令也支持ReplicaSet。rolling-update命令是一个例外 。如果您想要滚动更新功能,请考虑使用Deployment。此外, rolling-update命令是必需的,而Deployments是声明性的,因此我们建议通过rollout命令使用Deployments 。

虽然ReplicaSet可以独立使用,但今天它主要被 Deployments用作协调pod创建,删除和更新的机制。使用“Deloyment”时,不必担心管理它们创建的副本集。部署拥有并管理其ReplicaSet。

ReplicaSet确保在任何给定时间运行指定数量的pod副本。但是,Deployment是一个更高级别的概念,它管理ReplicaSet并为pod提供声明性更新以及许多其他有用的功能。因此,除非您需要自定义更新编排或根本不需要更新,否则我们建议您使用部署而不是直接使用ReplicaSet。

这实际上意味着您可能永远不需要操作ReplicaSet对象:改为使用Deployment,并在spec部分中定义您的应用程序。

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
apiVersion: extensions/v1beta1 
kind: ReplicaSet
metadata:
name: frontend #自动插入pod template里面的label
spec:
replicas: 3 #维持3副本,可被修改
selector: #选择器,也是自动从pod template里面获取
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template: #pod template,作为后续创建所 有Pod的模板
metadata:
labels:
app: guestbook tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
resources:
requests:
cpu: 100m #资源限制
memory: 100Mi
env:
- name: GET_HOSTS_FROM
value: dns
# If your cluster config does not include a dns service, then to
# instead access environment variables to find service host
# info, comment out the 'value: dns' line above, and uncomment the
# line below.
# value: env
ports:
- containerPort: 80

更多信息参考:https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/

K8s容器资源限制可以参考这篇:https://my.oschina.net/HardySimpson/blog/1359276

Deployment的行为和定义

Deployment为Pod和Replica Set(下一代Replication Controller)提供声明式更新。

您只需要在 Deployment 中描述您想要的目标状态是什么,Deployment controller 就会帮您将 Pod 和ReplicaSet 的实际状态改变到您的目标状态。您可以定义一个全新的 Deployment 来创建 ReplicaSet 或者删除已有的 Deployment 并创建一个新的来替换。

注意:您不该手动管理由 Deployment 创建的 ReplicaSet,否则您就篡越了 Deployment controller 的职责!下文罗列了 Deployment 对象中已经覆盖了所有的用例。如果未有覆盖您所有需要的用例,请直接在 Kubernetes 的代码库中提 issue。

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

更多信息:

Deployment 文档:https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

Deployment 简述:https://jimmysong.io/kubernetes-handbook/concepts/deployment.html

Deployment常见操作
  • 扩容:

    1
    $ kubectl scale deployment nginx-deployment --replicas 10
  • 如果集群支持 horizontal pod autoscaling 的话, 还可以为Deployment设置自动扩展:

1
$ kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu- percent=80
  • 更新镜像也比较简单:
1
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
  • 回滚:
1
$ kubectl rollout undo deployment/nginx-deployment

Services

Kubernetes Pod 是有生命周期的,它们可以被创建,也可以被销毁,然而一旦被销毁生命就永远结束。 通过 ReplicationController 能够动态地创建和销毁 Pod(例如,需要进行扩缩容,或者执行 滚动升级)。 每个 Pod 都会获取它自己的 IP 地址,即使这些 IP 地址不总是稳定可依赖的。 这会导致一个问题:在 Kubernetes 集群中,如果一组 Pod(称为 backend)为其它 Pod (称为 frontend)提供服务,那么那些 frontend 该如何发现,并连接到这组 Pod 中的哪些 backend 呢?

关于 Service

Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。 这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector(查看下面了解,为什么可能需要没有 selector 的 Service)实现的。

举个例子,考虑一个图片处理 backend,它运行了3个副本。这些副本是可互换的 —— frontend 不需要关心它们调用了哪个 backend 副本。 然而组成这一组 backend 程序的 Pod 实际上可能会发生变化,frontend 客户端不应该也没必要知道,而且也不需要跟踪这一组 backend 的状态。 Service 定义的抽象能够解耦这种关联。

对 Kubernetes 集群中的应用,Kubernetes 提供了简单的 Endpoints API,只要 Service 中的一组 Pod 发生变更,应用程序就会被更新。 对非 Kubernetes 集群中的应用,Kubernetes 提供了基于 VIP 的网桥的方式访问 Service,再由 Service 重定向到 backend Pod

service

1
2
3
4
5
6
7
8
9
10
11
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376

应用流程:后端匹配→代理→发布→服务发现→客户端

Services → Pod → Endpoints

spe

由于userspace使用并不常见,因此不在此赘述。

更多详见:https://jimmysong.io/kubernetes-handbook/concepts/service.html

Service的发布类型

对一些应用(如 Frontend)的某些部分,可能希望通过外部(Kubernetes 集群外部)IP 地址暴露 Service。

Kubernetes ServiceTypes 允许指定一个需要的类型的 Service,默认是 ClusterIP 类型。

Type 的取值以及行为如下:

  • ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType
  • NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。
  • LoadBalancer:使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。
  • ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。 没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。
  • externalIPs:如果外部的 IP 路由到集群中一个或多个 Node 上,Kubernetes Service 会被暴露给这些 externalIPs。 通过外部 IP(作为目的 IP 地址) 进入到集群,打到 Service 的端口上的流量, 将会被路由到 Service 的 Endpoint 上。 externalIPs 不会被 Kubernetes 管理,它属于集 群管理员的职责范畴。

Services → 无Selector

sws

Services → Headless Services

shs

Services → Headless Services → 无Selector

shws

服务发现:DNS

  • 一个可选(尽管强烈推荐)集群插件 是 DNS 服务器。 DNS 服务器监视着创建新 Service 的 Kubernetes API,从而为每一 个 Service 创建一组 DNS 记录。 如果整个集群的 DNS 一直被 启用,那么所有的 Pod应该能够自动对 Service 进行名称解析 。
  • 例如,有一个名称为 “my-service” 的 Service,它在 Kubernetes 集群中名为 “my-ns” 的 Namespace中,为 “my-service.my-
    ns” 创建了一条 DNS 记录。 在名称为 “my-
    ns” 的 Namespace 中的 Pod 应该能够简单地通过名称查询找 到 “my-service”。 在另一个 Namespace 中的 Pod 必须限定名 称为 “my-service.my-ns”。 这些名称查询的结果是 Cluster IP。
  • Kubernetes 也支持对端口名称的 DNS SRV(Service)记录。 如果名称为 “my-service.my-ns” 的 Service 有一个名为 “http” 的 TCP 端口,可以对 “_http._tcp.my-service.my-ns” 执行 DNS SRV 查询,得到 “http” 的端口号。

DaemonSet

DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个 Pod 。当有 Node 从集群移除时 ,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。

使用 DaemonSet 的一些典型用法:

  • 运行集群存储 daemon,例如在每个 Node 上运行 glusterd、ceph。

  • 在每个 Node 上运行日志收集 daemon,例如fluentd、logstash。

  • 在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、

    collectd、Datadog 代理、New Relic 代理,或 Ganglia gmond。

DaemonSet ReplicaSet/Deployment
默认为每个Node上运行 不绑定Node,只保持指定个数
>1.6后自身支持滚动升级 通过Deployment支持滚动升级

Job

Job负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束。

Job Spec格式
  • spec.template格式同Pod
  • RestartPolicy仅支持Never或OnFailure
  • 单个Pod时,默认Pod成功运行后Job即结束
  • .spec.completions标志Job结束需要成功运行的Pod个数,默认为1
  • .spec.parallelism标志并行运行的Pod的个数,默认为1
  • spec.activeDeadlineSeconds标志失败Pod的重试最大时间,超过这个时间不会继续重试

    例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
metadata:
name: pi
spec:
containers:
- name: pi
image: perl
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
restartPolicy: Never

CronJob

Cron Job 管理基于时间的 Job,即:

  • 在给定时间点只运行一次
  • 周期性地在给定时间点运行

一个 CronJob 对象类似于 crontab (cron table)文件中的一行。它根据指定的预定计划周期性地运行一个 Job,格式可以参考 Cron

扩展阅读: 瓜子云任务调度系统

CronJob Spec
  • .spec.schedule调度,必需字段,指定任务运行周期,格式同 Cron

  • .spec.jobTemplateJob 模板,必需字段,指定需要运行的任务,格式同 Job

  • .spec.startingDeadlineSeconds启动 Job 的期限(秒级别),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的 Job 将被认为是失败的。如果没有指定,则没有期限

  • .spec.concurrencyPolicy并发策略,该字段也是可选的。它指定了如何处理被 Cron Job 创建的 Job 的并发执行。只允许指定下面策略中的一种:

    • Allow(默认):允许并发运行 Job
    • Forbid:禁止并发运行,如果前一个还没有完成,则直接跳过下一个
    • Replace:取消当前正在运行的 Job,用一个新的来替换

    注意,当前策略只能应用于同一个 Cron Job 创建的 Job。如果存在多个 Cron Job,它们创建的 Job 之间总是允许并发运行。

  • .spec.suspend挂起,该字段也是可选的。如果设置为 true,后续所有执行都会被挂起。它对已经开始执行的 Job 不起作用。默认值为 false

  • .spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit历史限制,是可选的字段。它们指定了可以保留多少完成和失败的 Job。

    默认没有限制,所有成功和失败的 Job 都会被保留。然而,当运行一个 Cron Job 时,Job 可以很快就堆积很多,推荐设置这两个字段的值。设置限制的值为 0,相关类型的 Job 完成后将不会被保留。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: batch/v2alpha1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
args:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
Cron Job 限制

Cron Job 在每次调度运行时间内 大概 会创建一个 Job 对象。我们之所以说 大概 ,是因为在特定的环境下可能会创建两个 Job,或者一个 Job 都没创建。我们尝试少发生这种情况,但却不能完全避免。因此,创建 Job 操作应该是 幂等的

Job 根据它所创建的 Pod 的并行度,负责重试创建 Pod,并就决定这一组 Pod 的成功或失败。Cron Job 根本就不会去检查 Pod。

Volume

容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次,在 Pod 中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes 中的 Volume抽象就很好的解决了这些问题。

背景

Docker 中也有一个 volume 的概念,尽管它稍微宽松一些,管理也很少。在 Docker 中,卷就像是磁盘或是另一个容器中的一个目录。它的生命周期不受管理,直到最近才有了 local-disk-backed 卷。Docker 现在提供了卷驱动程序,但是功能还非常有限(例如Docker1.7只允许每个容器使用一个卷驱动,并且无法给卷传递参数)。

另一方面,Kubernetes 中的卷有明确的寿命——与封装它的 Pod 相同。所以,卷的生命比 Pod 中的所有容器都长,当这个容器重启时数据仍然得以保存。当然,当 Pod 不再存在时,卷也将不复存在。也许更重要的是,Kubernetes 支持多种类型的卷,Pod 可以同时使用任意数量的卷。

卷的核心是目录,可能还包含了一些数据,可以通过 pod 中的容器来访问。该目录是如何形成的、支持该目录的介质以及其内容取决于所使用的特定卷类型。

要使用卷,需要为 pod 指定为卷(spec.volumes 字段)以及将它挂载到容器的位置(spec.containers.volumeMounts 字段)。

容器中的进程看到的是由其 Docker 镜像和卷组成的文件系统视图。 Docker 镜像位于文件系统层次结构的根目录,任何卷都被挂载在镜像的指定路径中。卷无法挂载到其他卷上或与其他卷有硬连接。Pod 中的每个容器都必须独立指定每个卷的挂载位置。

卷的类型

Kubernetes 支持以下类型的卷:

卷类型 说明 分类
awsElasticBlockStore 亚马逊块存储 云盘
azureDisk 微软azure DataDisk,单机挂载 云盘
azureFile 微软azure File,可多机挂载 云盘
cephfs 红帽推出的软件分布式存储 分布式存储
csi 建立一个行业标准接口的规范 接口
downwardAPI 从Pod元数据生成文件 k8s元数据
emptyDir 空白目录,在node上开辟的临时空间 本地盘
fc (fibre channel) 光纤通道 远程存储
flocker 第三方平台,已倒闭 平台
gcePersistentDisk 谷歌远程盘,一写多读 云盘
gitRepo Git仓库远程下载,无认证能力 版本控制
glusterfs 另一种红帽推出的软分布式存储 分布式存储
卷类型 说明 分类
hostPath 本机node节点路径,长期保存 本地盘
iscsi iSCSI(SCSI over IP)卷 远程存储
local 将本地盘划为PV,Pod自动调度过来 本地盘
nfs NFS远程存储 远程存储
persistentVolumeClaim 按需创建远程盘 接口
projected 把secret,downwardAPI,configMap放同个目录下 k8s元数据
portworxVolume 部署在k8s上的分布式存储,不成熟 分布式存储
quobyte 一种小众的云存储方案 远程存储
rbd Ceph’s RADOS Block Devices,块设备 分布式存储
scaleIO EMC的存储方案 远程存储
secret K8s的secret对象 k8s元数据
storageos 容器存储方案 分布式存储
vsphereVolume Vmware存储卷 云盘

这里以emptyDir为例,当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除 Pod 时,emptyDir 中的数据将被永久删除。

注意:容器崩溃不会从节点中移除 pod,因此 emptyDir 卷中的数据在容器崩溃时是安全的。

emptyDir 的用法有:

  • 暂存空间,例如用于基于磁盘的合并排序
  • 用作长时间计算崩溃恢复时的检查点
  • Web服务器容器提供数据时,保存内容管理器容器提取的文件

    Pod示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
  1. Volume -> volumeMounts对应
  2. 通过名称对应
  3. 1:n对应,在多个容器里面同时挂载
区分emptyDir与hostPath
emptyDir hostPath
生命周期 = Pod 生命周期 = Node
不能指定路径 指定路径
同Pod的多容器间可共 享 同Pod的多容器 同Node的多Pod

ConfigMap - k8s的应用配置解决方案

其实ConfigMap功能在Kubernetes1.2版本的时候就有了,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。这些配置信息需要与docker image解耦,你总不能每修改一个配置就重做一个image吧?ConfigMap API给我们提供了向容器中注入配置信息的机制,ConfigMap可以被用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制大对象。

概览

ConfigMap API资源用来保存key-value pair配置数据,这个数据可以在pods里使用,或者被用来为像controller一样的系统组件存储配置数据。虽然ConfigMap跟Secrets类似,但是ConfigMap更方便的处理不含敏感信息的字符串。 注意:ConfigMaps不是属性配置文件的替代品。ConfigMaps只是作为多个properties文件的引用。你可以把它理解为Linux系统中的/etc目录,专门用来存储配置文件的目录。下面举个例子,使用ConfigMap配置来创建Kuberntes Volumes,ConfigMap中的每个data项都会成为一个新文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
kind: ConfigMap
apiVersion: v1
metadata:
creationTimestamp: 2016-02-18T19:14:38Z
name: example-config
namespace: default
data:
example.property.1: hello
example.property.2: world
example.property.file: |-
property.1=value-1
property.2=value-2
property.3=value-3

data一栏包括了配置数据,ConfigMap可以被用来保存单个属性,也可以用来保存一个配置文件。 配置数据可以通过很多种方式在Pods里被使用。ConfigMaps可以被用来:

  1. 设置环境变量的值
  2. 在容器里设置命令行参数
  3. 在数据卷里面创建config文件

用户和系统组件两者都可以在ConfigMap里面存储配置数据。

Secret

Secret解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者Pod Spec中。Secret可以以Volume或者环境变量的方式使用。

Secret有三种类型:

  • Service Account :用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的/run/secrets/kubernetes.io/serviceaccount目录中;
  • Opaque :base64编码格式的Secret,用来存储密码、密钥等;
  • kubernetes.io/dockerconfigjson :用来存储私有docker registry的认证信息。
Opaque Secret

Opaque类型的数据是一个map类型,要求value是base64编码格式:

1
2
3
4
$ echo -n "admin" | base64
YWRtaW4=
$ echo -n "1f2d1e2e67df" | base64
MWYyZDFlMmU2N2Rm
secrets.yml
1
2
3
4
5
6
7
8
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
password: MWYyZDFlMmU2N2Rm
username: YWRtaW4=

接着,就可以创建secret了:kubectl create -f secrets.yml

创建好secret之后,有两种方式来使用它:

  • 以Volume方式
  • 以环境变量方式
将Secret挂载到Volume中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
labels:
name: db
name: db
spec:
volumes:
- name: secrets
secret:
secretName: mysecret
containers:
- image: gcr.io/my_project_id/pg:v1
name: db
volumeMounts:
- name: secrets
mountPath: "/etc/secrets"
readOnly: true
ports:
- name: cp
containerPort: 5432
hostPort: 5432
将Secret导出到环境变量中
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
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: wordpress-deployment
spec:
replicas: 2
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: wordpress
visualize: "true"
spec:
containers:
- name: "wordpress"
image: "wordpress"
ports:
- containerPort: 80
env:
- name: WORDPRESS_DB_USER
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password

Secret更多详细信息:Secret

Persistent Volume(持久化卷)

对于管理计算资源来说,管理存储资源明显是另一个问题。PersistentVolume 子系统为用户和管理员提供了一个 API,该 API 将如何提供存储的细节抽象了出来。为此,我们引入两个新的 API 资源:PersistentVolumePersistentVolumeClaim

PersistentVolume(PV)是由管理员设置的存储,它是群集的一部分。就像节点是集群中的资源一样,PV 也是集群中的资源。 PV 是 Volume 之类的卷插件,但具有独立于使用 PV 的 Pod 的生命周期。此 API 对象包含存储实现的细节,即 NFS、iSCSI 或特定于云供应商的存储系统。

PersistentVolumeClaim(PVC)是用户存储的请求。它与 Pod 相似。Pod 消耗节点资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU 和内存)。声明可以请求特定的大小和访问模式(例如,可以以读/写一次或 只读多次模式挂载)。

虽然 PersistentVolumeClaims 允许用户使用抽象存储资源,但用户需要具有不同性质(例如性能)的 PersistentVolume 来解决不同的问题。集群管理员需要能够提供各种各样的 PersistentVolume,这些PersistentVolume 的大小和访问模式可以各有不同,但不需要向用户公开实现这些卷的细节。对于这些需求,StorageClass 资源可以实现。

更多详细内容详见:PV

绑定块卷

如果用户通过使用 PersistentVolumeClaim 规范中的 volumeMode 字段指示此请求来请求原始块卷,则绑定规则与以前不认为该模式为规范一部分的版本略有不同。

下面是用户和管理员指定请求原始块设备的可能组合的表格。该表指示卷是否将被绑定或未给定组合。静态设置的卷的卷绑定矩阵:

PV volumeMode PVC volumeMode 结果
unspecified unspecified 绑定
unspecified Block 不绑定
unspecified Filesystem 绑定
Block unspecified 不绑定
Block Block 绑定
Block Filesystem 不绑定
Filesystem Filesystem 绑定
Filesystem Block 不绑定
Filesystem unspecified 绑定

Namespace

在一个Kubernetes集群中可以使用namespace创建多个“虚拟集群”,这些namespace之间可以完全隔离,也可以通过某种方式,让一个namespace中的service可以访问到其他的namespace中的服务,我们在CentOS中部署kubernetes1.6集群的时候就用到了好几个跨越namespace的服务,比如Traefik ingress和kube-systemnamespace下的service就可以为整个集群提供服务,这些都需要通过RBAC定义集群级别的角色来实现。

哪些情况下适合使用多个namespace

因为namespace可以提供独立的命名空间,因此可以实现部分的环境隔离。当你的项目和人员众多的时候可以考虑根据项目属性,例如生产、测试、开发划分不同的namespace。

Namespace使用

获取集群中有哪些namespace

1
kubectl get ns

集群中默认会有defaultkube-system这两个namespace。

在执行kubectl命令时可以使用-n指定操作的namespace。

用户的普通应用默认是在default下,与集群管理相关的为整个集群提供服务的应用一般部署在kube-system的namespace下,例如我们在安装kubernetes集群时部署的kubednsheapseterEFK等都是在这个namespace下面。

另外,并不是所有的资源对象都会对应namespace,nodepersistentVolume就不属于任何namespace。

K8S调度机制

scheduler

  • 预选 : 根据配置的 Predicates Policies(默认为 DefaultProvider 中定义的 default predicates policies 集合)过滤掉那些不满足 Policies的的Nodes,剩下的Nodes作为优选的输入。
  • 优选 : 根据配置的 Priorities Policies(默认为 DefaultProvider 中定义的 default priorities policies 集合)给预选后的Nodes进 行打分排名,得分最高的Node即作为最适合的Node,该Pod 就Bind到这个Node。

预选

pre

  • 系统当中有10个node,只有3台有GPU
  • 希望3个pod部署到这3个node上去
  • 当有GPU的机器扩容时,希望动态增加pod

优选

pri

亲和/反亲和

亲和 反亲和
传统应用进行容器化, 拆分微服务之后的部 署约束,需要按实例 逐一配对就近部署, 容器间通信就近路由, 减少网络消耗。 高可靠性考虑,同个 应用的多个实例反亲 和部署,减少宕机影 响 互相干扰的应用反亲 和部署,避免干扰。

K8S网络

Docker的网络模型

docker_net

其中:

  • 容器—虚拟网卡—tup设备—网桥—宿主机协议栈—宿主机网卡
  • 容器访问宿主机网络通过SNAT
  • 容器绑定宿主机端口,被外部访问,通过DNAT

K8S网络模型

k8s_net

设计理念
  • 所有容器不使用 NAT 就可以互相通信(这跟 Docker 的默认实现是不同的);
  • 所有节点跟容器之间不使用 NAT 就可以互相通信;
  • 容器自己看到的地址,跟其他人访问自己使用的地址应 该是一样的(其实还是在说不要有 NAT)。
Pod 内部

inpod

不同节点pod

diffpods

容器网络方案

方案 典型实现
隧道方案 ( Overlay Networking ) Flannel:UDP广播,VxLan
Weave:UDP广播,本机建立新的BR,通过PCAP互通
Open vSwitch(OVS):基于VxLan和GRE协议,但是 性能方面损失比较严重
Racher:IPsec
路由方案 (Underlay Networking) Calico:基于BGP协议的路由方案,支持很细致的ACL 控制,对混合云亲和度比较高。
Macvlan:从逻辑和Kernel层来看隔离性和性能最优的 方案,基于二层隔离,所以需要二层路由器支持,大多 数云服务商不支持,所以混合云上比较难以实现。

Flannel

flannel

  • Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来 说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集 群唯一的虚拟IP地址。
  • 在默认的Docker配置中,每个节点上的Docker服务会分别负责所在节点 容器的IP分配。这样导致的一个问题是,不同节点上容器可能获得相同 的内外IP地址。并使这些容器之间能够之间通过IP地址相互找到,也就 是相互ping通。
  • Flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则 ,从而使得不同节点上的容器能够获得“同属一个内网”且”不重复的 ”IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。
  • Flannel实质上是一种“覆盖网络(overlaynetwork)”,也就是将TCP数据包 装在另一种网络包里面进行路由转发和通信,目前已经支持udp、vxlan 、host-gw、aws-vpc、gce和alloc路由等数据转发方式,默认的节点间数 据通信方式是UDP转发。

Calico

calico

Calico是一个纯3层的数据中心网络方案,而且无缝集成像OpenStack 这种IaaS云架构,能够提供可控的VM、容器、裸机之间的IP通信。 Calico不使用重叠网络比如flannel和libnetwork重叠网络驱动,它是一 个纯三层的方法,使用虚拟路由代替虚拟交换,每一台虚拟路由通 过BGP协议传播可达信息(路由)到剩余数据中心。

  • Calico在每一个计算节点利用Linux Kernel实现了一个高效的vRouter 来负责数据转发,而每个vRouter通过BGP协议负责把自己上运行的 workload的路由信息像整个Calico网络内传播——小规模部署可以直 接互联,大规模下可通过指定的BGP route reflector来完成。

  • Calico节点组网可以直接利用数据中心的网络结构(无论是L2或者 L3),不需要额外的NAT,隧道或者Overlay Network。

  • Calico基于iptables还提供了丰富而灵活的网络Policy,保证通过各个 节点上的ACLs来提供Workload的多租户隔离、安全组以及其他可达 性限制等功能。
calico特性

calico_adv

网络性能对比

net_compare

网络方案总结

net_con

CNI vs CNM

CNI

cni

CNI ADD/DELETE

CNI的接口设计的非常简洁,只有两个接口ADD/DELETE。 以 ADD接口为例

Add container to network 参数主要包括:

  • Version. CNI版本号
  • Container ID. 这是一个可选的参数,提供容器的id
  • Network namespace path. 容器的命名空间的路径,比如 /proc/[pid]/ns/net
  • Network configuration. 这是一个json的文档,具体可以参看network-configuration
  • Extra arguments. 其他参数
  • Name of the interface inside the container. 容器内的网卡名 返回值:

  • IPs assigned to the interface. ipv4或者ipv6地址

  • DNS information. DNS相关信息

Server: KubeProxy

VIP与Service代理

在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。 在 Kubernetes v1.0 版本,代理完全在 userspace。在 Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。 从 Kubernetes v1.2 起,默认就是 iptables 代理。在Kubernetes v1.8.0-beta.0中,添加了ipvs代理。

在 Kubernetes v1.0 版本,Service 是 “4层”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了 Ingress API(beta 版),用来表示 “7层”(HTTP)服务。

iptables代理模式

这种模式,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会安装 iptables 规则,从而捕获到达该 ServiceclusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某个上面。 对于每个 Endpoints 对象,它也会安装 iptables 规则,这个规则会选择一个 backend Pod

默认的策略是,随机选择一个 backend。 实现基于客户端 IP 的会话亲和性,可以将 service.spec.sessionAffinity 的值设置为 "ClientIP" (默认值为 "None")。

和 userspace 代理类似,网络返回的结果是,任何到达 Service 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、Service、或 Pod 的任何信息。 这应该比 userspace 代理更快、更可靠。然而,不像 userspace 代理,如果初始选择的 Pod 没有响应,iptables 代理不能自动地重试另一个 Pod,所以它需要依赖 readiness probes

iptables

ipvs代理模式

这种模式,kube-proxy会监视Kubernetes Service对象和Endpoints,调用netlink接口以相应地创建ipvs规则并定期与Kubernetes Service对象和Endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod。

与iptables类似,ipvs基于netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs为负载均衡算法提供了更多选项,例如:

  • rr:轮询调度
  • lc:最小连接数
  • dh:目标哈希
  • sh:源哈希
  • sed:最短期望延迟
  • nq: 不排队调度

注意: ipvs模式假定在运行kube-proxy之前在节点上都已经安装了IPVS内核模块。当kube-proxy以ipvs代理模式启动时,kube-proxy将验证节点上是否安装了IPVS模块,如果未安装,则kube-proxy将回退到iptables代理模式。

ipvs

Kube-DNS

kube-dns

推荐阅读:kube-dns前世今生

Ingress

通常情况下,service和pod仅可在集群内部网络中通过IP地址访问。所有到达边界路由器的流量或被丢弃或被转发到其他地方。从概念上讲,可能像下面这样:

1
2
3
4
  internet
|
------------
[ Services ]

Ingress是授权入站连接到达集群服务的规则集合。

1
2
3
4
5
 internet
|
[ Ingress ]
--|-----|--
[ Services ]

你可以给Ingress配置提供外部可访问的URL、负载均衡、SSL、基于名称的虚拟主机等。用户通过POST Ingress资源到API server的方式来请求ingress。 Ingress controller负责实现Ingress,通常使用负载平衡器,它还可以配置边界路由和其他前端,这有助于以HA方式处理流量。

Ingress类型

单Service Ingress

Kubernetes中已经存在一些概念可以暴露单个service(查看替代方案),但是你仍然可以通过Ingress来实现,通过指定一个没有rule的默认backend的方式。

ingress.yaml定义文件:

1
2
3
4
5
6
7
8
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
spec:
backend:
serviceName: testsvc
servicePort: 80

使用kubectl create -f命令创建,然后查看ingress:

1
2
3
$ kubectl get ing
NAME RULE BACKEND ADDRESS
test-ingress - testsvc:80 107.178.254.228

107.178.254.228就是Ingress controller为了实现Ingress而分配的IP地址。RULE列表示所有发送给该IP的流量都被转发到了BACKEND所列的Kubernetes service上。

简单展开

如前面描述的那样,kubernete pod中的IP只在集群网络内部可见,我们需要在边界设置一个东西,让它能够接收ingress的流量并将它们转发到正确的端点上。这个东西一般是高可用的loadbalancer。使用Ingress能够允许你将loadbalancer的个数降低到最少,例如,假如你想要创建这样的一个设置:

1
2
foo.bar.com -> 178.91.123.132 -> / foo    s1:80
/ bar s2:80

你需要一个这样的ingress:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
backend:
serviceName: s1
servicePort: 80
- path: /bar
backend:
serviceName: s2
servicePort: 80

使用kubectl create -f创建完ingress后:

1
2
3
4
5
6
$ kubectl get ing
NAME RULE BACKEND ADDRESS
test -
foo.bar.com
/foo s1:80
/bar s2:80

只要服务(s1,s2)存在,Ingress controller就会将提供一个满足该Ingress的特定loadbalancer实现。 这一步完成后,您将在Ingress的最后一列看到loadbalancer的地址。

K8S 集群与监控

cAdvisor

cAdvisor其实就是如果你运行docker stats -all命令所获得 的信息的图形化版本

单容器运行:

1
2
3
4
5
docker run \
--volume=/:/rootfs:ro \ --volume=/var/run:/var/run:rw \ --volume=/sys:/sys:ro \ --volume=/var/lib/docker/:/var/lib/docker:ro \ --publish=8080:8080 \
--detach=true \
--name=cadvisor \
google/cadvisor:latest

Heapster

方案架构图

heapster

H1

Heapster作为kubernetes安装过程中默认安装的一个插件,见安装heapster插件。这对于集群监控十分有用,同时在Horizontal Pod Autoscaling中也用到了,HPA将Heapster作为Resource Metrics API,向其获取metric,做法是在kube-controller-manager 中配置--api-server指向kube-aggregator,也可以使用heapster来实现,通过在启动heapster的时候指定--api-server=true

Heapster可以收集Node节点上的cAdvisor数据,还可以按照kubernetes的资源类型来集合资源,比如Pod、Namespace域,可以分别获取它们的CPU、内存、网络和磁盘的metric。默认的metric数据聚合时间间隔是1分钟。

Kubernetes 1.11 不建议使用 Heapster ,就 Sig Instrumentation 而言,这是为了转向新的Kubernetes 监控模型的持续努力的一部分。仍应使用 Heapster 进行自动扩展的群集应迁移到metrics-server和自定义指标API。有关详细信息,请参阅 Kubernetes 1.11 版本日志

Prometheus

架构图

pm

Prometheus 是由 SoundCloud 开源监控告警解决方案,从 2012 年开始编写代码,再到 2015 年 github 上开源以来,已经吸引了 9k+ 关注,以及很多大公司的使用;2016 年 Prometheus 成为继 k8s 后,第二名 CNCF(Cloud Native Computing Foundation) 成员。

作为新一代开源解决方案,很多理念与 Google SRE 运维之道不谋而合。

主要功能

  • 多维 数据模型(时序由 metric 名字和 k/v 的 labels 构成)。
  • 灵活的查询语句(PromQL)。
  • 无依赖存储,支持 local 和 remote 不同模型。
  • 采用 http 协议,使用 pull 模式,拉取数据,简单易懂。
  • 监控目标,可以采用服务发现或静态配置的方式。
  • 支持多种统计数据模型,图形化友好。

核心组件

  • Prometheus Server, 主要用于抓取数据和存储时序数据,另外还提供查询和 Alert Rule 配置管理。
  • client libraries,用于对接 Prometheus Server, 可以查询和上报数据。
  • push gateway ,用于批量,短期的监控数据的汇总节点,主要用于业务数据汇报等。
  • 各种汇报数据的 exporters ,例如汇报机器数据的 node_exporter, 汇报 MongoDB 信息的 MongoDB exporter 等等。
  • 用于告警通知管理的 alertmanager

详细部署请参阅超哥的使用Prometheus监控kubernetes集群

身份与权限认证

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

Service Account

Service Account为Pod中的进程提供身份信息。

当您(真人用户)访问集群(例如使用kubectl命令)时,apiserver 会将您认证为一个特定的 User Account(目前通常是admin,除非您的系统管理员自定义了集群配置)。Pod 容器中的进程也可以与 apiserver 联系。 当它们在联系 apiserver 的时候,它们会被认证为一个特定的 Service Account(例如default)。

当您创建 pod 的时候,如果您没有指定一个 service account,系统会自动得在与该pod 相同的 namespace 下为其指派一个default service account。如果您获取刚创建的 pod 的原始 json 或 yaml 信息(例如使用kubectl get pods/podename -o yaml命令),您将看到spec.serviceAccountName字段已经被设置为 default

您可以在 pod 中使用自动挂载的 service account 凭证来访问 API,如 Accessing the Cluster 中所描述。

Service account 是否能够取得访问 API 的许可取决于您使用的 授权插件和策略

在 1.6 以上版本中,您可以选择取消为 service account 自动挂载 API 凭证,只需在 service account 中设置 automountServiceAccountToken: false

1
2
3
4
5
6
apiVersion: v1
kind: ServiceAccount
metadata:
name: build-robot
automountServiceAccountToken: false
...

在 1.6 以上版本中,您也可以选择只取消单个 pod 的 API 凭证自动挂载:

1
2
3
4
5
6
7
8
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
serviceAccountName: build-robot
automountServiceAccountToken: false
...

如果在 pod 和 service account 中同时设置了 automountServiceAccountToken , pod 设置中的优先级更高。

User Account & Service Account 区别

User Account Service Account
为人设计,为客户端设计 (kubectl/kubelet/controller/sc heduler) 为Pod中的进程调用k8s API 设计
跨namespace 仅当前namespace
.kube/config Kubectl get servcieaccount Kubectl get secret
《Kubernetes集群安全配置 案例》 《Kubernetes 认证》 《名词解释:Service Account》

RBAC——基于角色的访问控制

API概述

本节将介绍RBAC API所定义的四种顶级类型。用户可以像使用其他Kubernetes API资源一样 (例如通过kubectl、API调用等)与这些资源进行交互。例如,命令kubectl create -f (resource).yml 可以被用于以下所有的例子,当然,读者在尝试前可能需要先阅读以下相关章节的内容。

Role与ClusterRole

在RBAC API中,一个角色包含了一套表示一组权限的规则。 权限以纯粹的累加形式累积(没有”否定”的规则)。 角色可以由命名空间(namespace)内的Role对象定义,而整个Kubernetes集群范围内有效的角色则通过ClusterRole对象实现。

一个Role对象只能用于授予对某一单一命名空间中资源的访问权限。 以下示例描述了”default”命名空间中的一个Role对象的定义,用于授予对pod的读访问权限:

1
2
3
4
5
6
7
8
9
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # 空字符串""表明使用core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]

ClusterRole对象可以授予与Role对象相同的权限,但由于它们属于集群范围对象, 也可以使用它们授予对以下几种资源的访问权限:

  • 集群范围资源(例如节点,即node)
  • 非资源类型endpoint(例如”/healthz”)
  • 跨所有命名空间的命名空间范围资源(例如pod,需要运行命令kubectl get pods --all-namespaces来查询集群中所有的pod)

下面示例中的ClusterRole定义可用于授予用户对某一特定命名空间,或者所有命名空间中的secret(取决于其绑定方式)的读访问权限:

1
2
3
4
5
6
7
8
9
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
# 鉴于ClusterRole是集群范围对象,所以这里不需要定义"namespace"字段
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]

RoleBinding与ClusterRoleBinding

角色绑定将一个角色中定义的各种权限授予一个或者一组用户。 角色绑定包含了一组相关主体(即subject, 包括用户——User、用户组——Group、或者服务账户——Service Account)以及对被授予角色的引用。 在命名空间中可以通过RoleBinding对象授予权限,而集群范围的权限授予则通过ClusterRoleBinding对象完成。

RoleBinding可以引用在同一命名空间内定义的Role对象。 下面示例中定义的RoleBinding对象在”default”命名空间中将”pod-reader”角色授予用户”jane”。 这一授权将允许用户”jane”从”default”命名空间中读取pod。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 以下角色绑定定义将允许用户"jane"从"default"命名空间中读取pod。
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io

RoleBinding对象也可以引用一个ClusterRole对象用于在RoleBinding所在的命名空间内授予用户对所引用的ClusterRole中 定义的命名空间资源的访问权限。这一点允许管理员在整个集群范围内首先定义一组通用的角色,然后再在不同的命名空间中复用这些角色。

例如,尽管下面示例中的RoleBinding引用的是一个ClusterRole对象,但是用户”dave”(即角色绑定主体)还是只能读取”development” 命名空间中的secret(即RoleBinding所在的命名空间)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 以下角色绑定允许用户"dave"读取"development"命名空间中的secret。
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: read-secrets
namespace: development # 这里表明仅授权读取"development"命名空间中的资源。
subjects:
- kind: User
name: dave
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io

最后,可以使用ClusterRoleBinding在集群级别和所有命名空间中授予权限。下面示例中所定义的ClusterRoleBinding 允许在用户组”manager”中的任何用户都可以读取集群中任何命名空间中的secret。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 以下`ClusterRoleBinding`对象允许在用户组"manager"中的任何用户都可以读取集群中任何命名空间中的secret。
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io

RBAC——Role VS ClusterRole

Role: 角色/namespace内 ClusterRole: 角色/不区分namespace
RoleBinding 把role和 [user/serviceAccount/group]关 联起来 ClusterRoleBinding 把ClusterRole和 [user/serviceAccount/group]关 联起来

详细内容详见: RBAC——基于角色的访问控制

k8s技术栈的多租户

k8s-mult

Hypernetes简介——真正多租户的Kubernetes Distro

CATALOG
  1. 1. kubernetes 架构
    1. 1.1. 分层架构
    2. 1.2. 架构设计优缺点
  2. 2. k8s提供的抽象
    1. 2.0.1. kubernetes 中的对象
  • 3. 对象分类
  • 4. 业界各种安装方式对比
  • 5. 当pod创建时,k8s到底干了啥
  • 6. kubectl 常见命令
    1. 6.1. kubectl上下文和配置
    2. 6.2. kubectl 创建对象
    3. 6.3. kubectl显示和查找资源
    4. 6.4. kubectl编辑资源
    5. 6.5. kubectl Scale 资源
    6. 6.6. kubectl 删除资源
    7. 6.7. kubectl与运行中的 Pod 交互
    8. 6.8. kubectl与节点和集群交互
  • 7. k8s构建高可用集群
  • 8. k8s应用管理
    1. 8.1. pod详解
    2. 8.2. pod的设计动机
    3. 8.3. pod的非持久性
    4. 8.4. init容器
    5. 8.5. Pod中容器镜像常见设置
    6. 8.6. Pod.status.phase
    7. 8.7. 容器探针(注意不是Pod探针)
    8. 8.8. 容器重启策略
      1. 8.8.1. 以五分钟为上限的指数退避延迟(10秒,20秒,40秒… )重新启动,并在成功执行十分钟后重置。
    9. 8.9. pod状态与容器状态的关系
    10. 8.10. ReplicaSet: 副本集
    11. 8.11. Deployment的行为和定义
      1. 8.11.1. Deployment常见操作
    12. 8.12. Services
      1. 8.12.1. Services → Pod → Endpoints
    13. 8.13. Service的发布类型
    14. 8.14. Services → 无Selector
    15. 8.15. Services → Headless Services
    16. 8.16. Services → Headless Services → 无Selector
    17. 8.17. 服务发现:DNS
    18. 8.18. DaemonSet
    19. 8.19. Job
      1. 8.19.1. Job Spec格式
    20. 8.20. CronJob
      1. 8.20.1. CronJob Spec
      2. 8.20.2. Cron Job 限制
  • 9. Volume
    1. 9.1. 背景
    2. 9.2. 卷的类型
      1. 9.2.1. 区分emptyDir与hostPath
    3. 9.3. ConfigMap - k8s的应用配置解决方案
      1. 9.3.1. 概览
    4. 9.4. Secret
      1. 9.4.1. Opaque Secret
      2. 9.4.2. secrets.yml
      3. 9.4.3. 将Secret挂载到Volume中
      4. 9.4.4. 将Secret导出到环境变量中
    5. 9.5. Persistent Volume(持久化卷)
    6. 9.6. 绑定块卷
  • 10. Namespace
    1. 10.1. 哪些情况下适合使用多个namespace
      1. 10.1.1. Namespace使用
  • 11. K8S调度机制
    1. 11.1. 预选
    2. 11.2. 优选
    3. 11.3. 亲和/反亲和
  • 12. K8S网络
    1. 12.1. Docker的网络模型
    2. 12.2. K8S网络模型
      1. 12.2.1. 设计理念
      2. 12.2.2. Pod 内部
      3. 12.2.3. 不同节点pod
    3. 12.3. 容器网络方案
    4. 12.4. Flannel
    5. 12.5. Calico
      1. 12.5.1. calico特性
    6. 12.6. 网络性能对比
    7. 12.7. 网络方案总结
    8. 12.8. CNI vs CNM
      1. 12.8.1. CNI
      2. 12.8.2. CNI ADD/DELETE
    9. 12.9. Server: KubeProxy
      1. 12.9.1. VIP与Service代理
      2. 12.9.2. iptables代理模式
      3. 12.9.3. ipvs代理模式
    10. 12.10. Kube-DNS
    11. 12.11. Ingress
      1. 12.11.1. 单Service Ingress
      2. 12.11.2. 简单展开
  • 13. K8S 集群与监控
    1. 13.1. cAdvisor
    2. 13.2. Heapster
      1. 13.2.1. 方案架构图
    3. 13.3. Prometheus
      1. 13.3.1. 架构图
    4. 13.4. 主要功能
    5. 13.5. 核心组件
  • 14. 身份与权限认证
    1. 14.1. Service Account
    2. 14.2. User Account & Service Account 区别
    3. 14.3. RBAC——基于角色的访问控制
      1. 14.3.1. API概述
      2. 14.3.2. Role与ClusterRole
    4. 14.4. RBAC——Role VS ClusterRole
  • 15. k8s技术栈的多租户