API Server 是K8s重要的管理API 层。它负责提供restful api访问端点, 并且将数据持久化到etcd server中。Kubernetes 集群中,API Server 扮演着交互入口的位置。API Server 不仅负责和 etcd 交互(其他组件不会直接操作 etcd,只有 API Server 这么做),并切对外提供统一的API调用入口, 所有的交互都是以 API Server 为核心的。
整体组件功能
API Server 提供了一下的功能:
整个集群管理的 API 接口:所有对集群进行的查询和管理都要通过 API 来进行。集群内部的组件(如kubelet)也是通过Apiserver更新和同步数据。
集群内部各个模块之间通信的枢纽:所有模块之前并不会之间互相调用,而是通过和 API Server 打交道来完成自己那部分的工作。
集群安全控制:API Server 提供的验证和授权保证了整个集群的安全。
数据中心枢纽: API Server 负责和 Etcd 交互存放集群用到的运行数据。
结构分析
如下图, 从结构上来分析 API Server。
Resetful 是对外提供http(https)的接口,用来对外提供与集群统一的交互手段。
Cacher 是针对查询到的数据的缓存中心。
Watcher 模块 是负责从Etcd获取数据,其中可注册多个Watcher,即关注多个不同的数据。
Translate 模块 是负责将从Etcd获取到的数据转化为本地统一数据的接口,当Watcher获取到数据后就将其发送给Translate模块,Translate根据数据类型使用注册的对应的翻译接口进行翻译。
流程分析
如图所示,Apiserver可以左右两部分,左半部分是Apiserver使用观察者模式获取更新需要的数据,右半部分则是Apiserver接受外部调用并注册观察者Watcher,并从Watcher中最终获取到需要的数据。
外部调用会先通过Apiserver使用registerResourceHandlers注册的Api接口
该接口会调用ListResource解析收到的用户请求,调用watch接口向Apiserver注册观察者Watcher,并调用serverWatch等待返回的数据。
Watcher会由Apiserver 启动的携程调用dispatchEvents从通道中获取并注册到watcher集合中
Apiserver 启动时会携程调用startCaching,其主要协程启动了etcdWatch, translate,
process三个协程, 并在主流程中调用watchHandler
etchWatch根据注册的观察者watcher向etcd注册关注相关的数据,当获取到数据时则通过通道发送给translate。
translate负责解析从etcd获取到的数据, 并通过通道发送给watchHandler。
WatchHander 调用store.(add|delete|update)对本地缓存数据做更新。
translate解析完成数据时,发现有被watcher的数据会被filter并通过通道给process协程进行处
process协程调用sendWatchCacheEvent将获取到的数据通过通道发送给serveWatch。
serveWatch将数据返回给关注该资源的用户。
组件构成
kube-apiserver 共由 3 个组件构成(Aggregator. KubeAPIServer. APIExtensionServer),这些组件依次通过 Delegation 处理请求:
Aggregator:暴露的功能类似于一个七层负载均衡,将来自用户的请求拦截转发给其他服务器,并且负责整个 APIServer 的 Discovery 功能;也负责处理ApiService,注册对应的扩展api。
KubeAPIServer :负责对请求的一些通用处理,认证. 鉴权等,以及处理各个内建资源的 REST 服务;
APIExtensionServer:主要处理 CustomResourceDefinition(CRD)和 CustomResource(CR)的 REST 请求,也是 Delegation 的最后一环,如果对应 CR 不能被处理的话则会返回 404。
从上面的话有点难懂。我的理解是
KubeAPIServer
首先一个APIServer 和 部署的service 不同,他们都是API 层,但是API server 是提供的管理API, 而部署的Service 提供的用户的business API ,这两部分是完全不同的。因此APIServer 用到的API是APIService。 一个KubeAPIServer 就会启动一个APIService,细节在稍后介绍。
APIExtensionServer
在 Kubernetes 里,Resource 也叫资源,实际上是 Kubernetes API 里的一个 Endpoint 类型,比如内置的 Pod,也是一种资源类型。Custome Resources 自定义资源是在 Kubernetes 的默认配置下,对资源类型的扩展。
实际上,自定义资源类型只是让用户可以创建和查询到资源对象,当结合 Custom Controller 自定义控制器,才能像 Kubernetes 里其他对象一样去做 CRUD 的操作。通过自定义控制器,可以控制自定义资源的生命周期。通过这种贯穿于 Kubernetes 里的控制循环的思想,可以让集群里的资源对象按照预定义的状态,不断地去调整。
在官网里提示的,有两种方式来让用户去扩展 Kubernetes 的资源类型。
- CustomResourceDefinitions: 复用 Kubernetes 的 API Server,CRDs + CRD Controller
- API server aggregation: 需要编写 API Server,提供更细粒度的控制
这两种方式,其实挺不好理解的。简单理解的话,就是后者需要写更多的代码,去创建自己的 API Server,这里说的 API Server 跟 Master 上的 API Server 是一个类似的概念,都不完全相同,这里的 API Server 更想表达的意思是为了实现自定义资源来扩展 Kubernetes 的功能或者说工作负荷,用户需要按照 API Server 的模式来去创建自己的 API Server,然后通过 aggregate 这一层组件来实现聚合,也就是说,用户为了增加自定义资源,并且希望注册到 Core API Server 中,是需要写更多的代码的,但是他比前者带来一个更显著的好处就是,他的灵活性是更高的,毕竟你写的是类似 Core API Server 的东西,关于资源的存储、认证这些都可以做的更加灵活。
目前社区上看到很多的 Operator 多数是基于 CustomResourceDefinitions 来做的,例如 Spark Operator,Tf Operator 等等。前文讲的customized controller就是这个方法。
Operator 的模式,是由 coreos 提出的,结合自定义资源和自定义控制器的常见套路。定义一个自定义资源并不难,甚至非常简单,也就几行 yaml,或者代码里一个 struct 结构体就完事了,真正比价麻烦的是写一个自定义控制器来控制这个自定义资源对象
APIExtensionServer 就是来处理这个CustomResourceDefinitions的请求的。具体细节在以后介绍。
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: foos.samplecontroller.k8s.io
# for more information on the below annotation, please see
# https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/2337-k8s.io-group-protection/README.md
annotations:
"api-approved.kubernetes.io": "unapproved, experimental-only; please get an approval from Kubernetes API reviewers if you're trying to develop a CRD in the *.k8s.io or *.kubernetes.io groups"
spec:
group: samplecontroller.k8s.io
versions:
- name: v1alpha1
served: true
storage: true
schema:
# schema used for validation
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
deploymentName:
type: string
replicas:
type: integer
minimum: 1
maximum: 10
status:
type: object
properties:
availableReplicas:
type: integer
names:
kind: Foo
plural: foos
scope: Namespaced
Aggregator
那么Aggregator 是干啥的呢,Aggregator 是管理APIService的,就是上文说的处理扩展 Kubernetes 的资源类型第二种方式的。缺省是只有KubeAPIServer一个APIService,但是如果用户自己实现了一个APIServer,就需要创建一个APIService,这个APIService会绑定一个service 对象。Aggregator 就会自动注册这个用户创建的APIService,从而把APIService对象对应的service 加一个代理,当用户访问时, 把这个request 代理给自己创建的APIService对应的service 某个pod上。
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
name: v1alpha1.wardle.example.com
spec:
insecureSkipTLSVerify: true
group: wardle.example.com
groupPriorityMinimum: 1000
versionPriority: 15
service:
name: api
namespace: wardle
version: v1alpha1
再看service.yaml
apiVersion: v1
kind: Service
metadata:
name: api
namespace: wardle
spec:
ports:
- port: 443
protocol: TCP
targetPort: 443
selector:
apiserver: "true"
再看deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: wardle-server
namespace: wardle
labels:
apiserver: "true"
spec:
replicas: 1
selector:
matchLabels:
apiserver: "true"
template:
metadata:
labels:
apiserver: "true"
spec:
serviceAccountName: apiserver
containers:
- name: wardle-server
# build from staging/src/k8s.io/sample-apiserver/artifacts/simple-image/Dockerfile
# or
# docker pull k8s.gcr.io/e2e-test-images/sample-apiserver:1.17.4
# docker tag k8s.gcr.io/e2e-test-images/sample-apiserver:1.17.4 kube-sample-apiserver:latest
image: kube-sample-apiserver:latest
imagePullPolicy: Never
args: [ "--etcd-servers=http://localhost:2379" ]
- name: etcd
image: quay.io/coreos/etcd:v3.5.1
Aggregator类似于一个七层负载均衡,将来自用户的请求拦截转发给其他服务器,并且负责整个 APIServer 的 Discovery 功能。通过APIServices对象关联到某个Service来进行请求的转发,其关联的Service类型进一步决定了请求转发形式。Aggregator包括一个GenericAPIServer和维护自身状态的Controller。其中 GenericAPIServer主要处理apiregistration.k8s.io组下的APIService资源请求。主要controller包括:
- apiserviceRegistrationController:负责APIServices中资源的注册与删除;
- availableConditionController:维护APIServices的可用状态,包括其引用Service是否可用等;
- autoRegistrationController:用于保持API中存在的一组特定的APIServices;
- crdRegistrationController:负责将CRD GroupVersions自动注册到APIServices中;
- openAPIAggregationController:将APIServices资源的变化同步至提供的OpenAPI文档;
如何访问kubernetes API
k8s通过kube-apiserver这个进程提供服务,该进程运行在单个k8s-master节点上。默认有两个端口。
k8s通过kube-apiserver这个进程提供服务,该进程运行在单个k8s-master节点上。默认有两个端口。
1. 本地端口
- 该端口用于接收HTTP请求;
- 该端口默认值为8080,可以通过API Server的启动参数“--insecure-port”的值来修改默认值;
- 默认的IP地址为“localhost”,可以通过启动参数“--insecure-bind-address”的值来修改该IP地址;
- 非认证或授权的HTTP请求通过该端口访问API Server。
2.安全端口
- 该端口默认值为6443,可通过启动参数“--secure-port”的值来修改默认值;
- 默认IP地址为非本地(Non-Localhost)网络端口,通过启动参数“--bind-address”设置该值;
- 该端口用于接收HTTPS请求;
- 用于基于Tocken文件或客户端证书及HTTP Base的认证;
- 用于基于策略的授权;
- 默认不启动HTTPS安全访问控制。
kubernetes API访问方式
Kubernetes REST API可参考https://kubernetes.io/docs/api-reference/v1.6/
1. curl
curl localhost:``8080/api
curl localhost:``8080/api/v1/pods
curl localhost:``8080/api/v1/services
curl localhost:``8080/api/v1/replicationcontrollers
2. Kubectl Proxy
Kubectl Proxy代理程序既能作为API Server的反向代理,也能作为普通客户端访问API Server的代理。通过master节点的8080端口来启动该代理程序。
kubectl proxy --port=8080 &
具体见kubectl proxy --help
3. kubectl客户端
命令行工具kubectl客户端,通过命令行参数转换为对API Server的REST API调用,并将调用结果输出。
命令格式:kubectl [command] [options]
具体可参考Kubernetes常用命令
4. 编程方式调用
使用场景:
1、运行在Pod里的用户进程调用kubernetes API,通常用来实现分布式集群搭建的目标。
2、开发基于kubernetes的管理平台,比如调用kubernetes API来完成Pod、Service、RC等资源对象的图形化创建和管理界面。可以使用kubernetes提供的Client Library。
具体可参考https://github.com/kubernetes/client-go。
通过API Server访问Node、Pod和Service
k8s API Server最主要的REST接口是资源对象的增删改查,另外还有一类特殊的REST接口—k8s Proxy API接口,这类接口的作用是代理REST请求,即kubernetes API Server把收到的REST请求转发到某个Node上的kubelet守护进程的REST端口上,由该kubelet进程负责响应。
1. Node相关接口
关于Node相关的接口的REST路径为:/api/v1/proxy/nodes/{name},其中{name}为节点的名称或IP地址。
/api/v1/proxy/nodes/``{name}``/pods/
#列出指定节点内所有Pod的信息
/api/v1/proxy/nodes/``{name}``/stats/
#列出指定节点内物理资源的统计信息
/api/v1/prxoy/nodes/``{name}``/spec/
#列出指定节点的概要信息
这里获取的Pod信息来自Node而非etcd数据库,两者时间点可能存在偏差。如果在kubelet进程启动时加--enable-debugging-handles=true参数,那么kubernetes Proxy API还会增加以下接口:
/api/v1/proxy/nodes/{name}/run
#在节点上运行某个容器
/api/v1/proxy/nodes/{name}/exec
#在节点上的某个容器中运行某条命令
/api/v1/proxy/nodes/{name}/attach
#在节点上attach某个容器
/api/v1/proxy/nodes/{name}/portForward
#实现节点上的Pod端口转发
/api/v1/proxy/nodes/{name}/logs
#列出节点的各类日志信息
/api/v1/proxy/nodes/{name}/metrics
#列出和该节点相关的Metrics信息
/api/v1/proxy/nodes/{name}/runningpods
#列出节点内运行中的Pod信息
/api/v1/proxy/nodes/{name}/debug/pprof
#列出节点内当前web服务的状态,包括CPU和内存的使用情况
2. Pod相关接口
/api/v1/proxy/namespaces/{namespace}/pods/{name}/{path:*} ``#访问pod的某个服务
/api/v1/proxy/namespaces/{namespace}/pods/{name} ``#访问Pod
#以下写法不同,功能一样
/api/v1/namespaces/{namespace}/pods/{name}/proxy/``{path:*} ``#访问pod的某个服务接口
/api/v1/namespaces/{namespace}/pods/{name}/proxy
#访问Pod
3. Service相关接口
/api/v1/proxy/namespaces/{namespace}/services/{name}
Pod的proxy接口的作用:在kubernetes集群之外访问某个pod容器的服务(HTTP服务),可以用Proxy API实现,这种场景多用于管理目的,比如逐一排查Service的Pod副本,检查哪些Pod的服务存在异常问题。
集群功能模块之间的通信
kubernetes API Server作为集群的核心,负责集群各功能模块之间的通信,集群内各个功能模块通过API Server将信息存入etcd,当需要获取和操作这些数据时,通过API Server提供的REST接口(GET\LIST\WATCH方法)来实现,从而实现各模块之间的信息交互。
1. kubelet与API Server交互
每个Node节点上的kubelet定期就会调用API Server的REST接口报告自身状态,API Server接收这些信息后,将节点状态信息更新到etcd中。kubelet也通过API Server的Watch接口监听Pod信息,从而对Node机器上的POD进行管理。
监听信息 | kubelet动作 | 备注 |
---|---|---|
新的POD副本被调度绑定到本节点 | 执行POD对应的容器的创建和启动逻辑 | |
POD对象被删除 | 删除本节点上相应的POD容器 | |
修改POD信息 | 修改本节点的POD容器 |
2. kube-controller-manager与API Server交互
kube-controller-manager中的Node Controller模块通过API Server提供的Watch接口,实时监控Node的信息,并做相应处理。
3. kube-scheduler与API Server交互
Scheduler通过API Server的Watch接口监听到新建Pod副本的信息后,它会检索所有符合该Pod要求的Node列表,开始执行Pod调度逻辑。调度成功后将Pod绑定到目标节点上。
API Server参数介绍
API Server 主要是和 etcd 打交道,并且对外提供 HTTP 服务,以及进行安全控制,因此它的命令行提供的参数也主要和这几个方面有关。下面是一些比较重要的参数以及说明(不同版本参数可能会有不同):
参数 | 含义 | 默认值 |
---|---|---|
–advertise-address | 通过该 ip 地址向集群其他节点公布 api server 的信息,必须能够被其他节点访问 | nil |
–allow-privileged | 是否允许 privileged 容器运行 | false |
–admission-control | 准入控制 | AlwaysAdmit |
–authorization-mode | 授权模式 ,安全接口上的授权 | AlwaysAllow |
–bind-address | HTTPS 安全接口的监听地址 | 0.0.0.0 |
–secure-port | HTTPS 安全接口的监听端口 | 6443 |
–cert-dir | TLS 证书的存放目录 | /var/run/kubernetes |
–etcd-prefix | 信息存放在 etcd 中地址的前缀 | “/registry” |
–etcd-servers | 逗号分割的 etcd server 地址 | [] |
–insecure-bind-address | HTTP 访问的地址 | 127.0.0.1 |
–insecure-port | HTTP 访问的端口 | 8080 |
–log-dir | 日志存放的目录 | |
–service-cluster-ip-range | service 要使用的网段,使用 CIDR 格式,参考 kubernetes 中 service 的定义 |
restful api
k8s的api-server组件负责提供restful api访问端点, 并且将数据持久化到etcd server中. 那么k8s是如何组织它的restful api的?
namespaced resources
所谓的namespaced resources,就是这个resource是从属于某个namespace的, 也就是说它不是cluster-scoped的资源. 比如pod, deployment, service都属于namespaced resource. 那么我们看一下如何请求一个namespaced resources.
http://localhost:8080/api/v1/namespaces/default/pods/test-pod
可以看出, 该restful api的组织形式是:
api api版本 namespaces 所属的namespace 资源种类 所请求的资源名称
api v1 namespaces default pods test-pod
这里api version如果是v1的话,表示这是一个很稳定的版本了, 以后不会有大的修改,并且当前版本所支持的所有特性以后都会兼容. 而如果版本号是v1alpha1, v1beta1之类的,则不保证其稳定性.
non-namespaced resources
http://localhost:8080/apis/rbac.authorization.k8s.io/v1/clusterroles/test-clusterrole
这里可以观察到它clusterrole与pod不同, apis表示这是一个非核心api. rbac.authorization.k8s.io指代的是api-group, 另外它没有namespaces字段, 其他与namespaced resources类似.不再赘述.
non-resource url
这类资源和pod, clusterrole都不同. 例如
http://localhost:8080/healthz/etcd
这就是用来确认etcd服务是不是健康的.它不属于任何namespace,也不属于任何api版本.
总结, k8s的REST API的设计结构为:
[api/apis] / api-group / api-version / namespaces / namespace-name / resource-kind / resource-name
apis / rbac.authorization.k8s.io / v1 / namespaces / default / roles / test-role
所有的api结构都可以通过访问相关断点来获取信息, 最低一层为api-server的地址, 访问结果如下:
{
"paths": [
"/api",
"/api/v1",
"/apis",
"/apis/",
"/apis/admissionregistration.k8s.io",
"/apis/admissionregistration.k8s.io/v1beta1",
"/apis/apiextensions.k8s.io",
"/apis/apiextensions.k8s.io/v1beta1",
"/apis/apiregistration.k8s.io",
"/apis/apiregistration.k8s.io/v1",
"/apis/apiregistration.k8s.io/v1beta1",
"/apis/apps",
"/apis/apps/v1",
"/apis/apps/v1beta1",
"/apis/apps/v1beta2",
"/apis/authentication.k8s.io",
"/apis/authentication.k8s.io/v1",
"/apis/authentication.k8s.io/v1beta1",
"/apis/authorization.k8s.io",
"/apis/authorization.k8s.io/v1",
"/apis/authorization.k8s.io/v1beta1",
"/apis/autoscaling",
"/apis/autoscaling/v1",
"/apis/autoscaling/v2beta1",
"/apis/autoscaling/v2beta2",
"/apis/batch",
"/apis/batch/v1",
"/apis/batch/v1beta1",
"/apis/certificates.k8s.io",
"/apis/certificates.k8s.io/v1beta1",
"/apis/coordination.k8s.io",
"/apis/coordination.k8s.io/v1beta1",
"/apis/events.k8s.io",
"/apis/events.k8s.io/v1beta1",
"/apis/extensions",
"/apis/extensions/v1beta1",
"/apis/networking.k8s.io",
"/apis/networking.k8s.io/v1",
"/apis/policy",
"/apis/policy/v1beta1",
"/apis/rbac.authorization.k8s.io",
"/apis/rbac.authorization.k8s.io/v1",
"/apis/rbac.authorization.k8s.io/v1beta1",
"/apis/scheduling.k8s.io",
"/apis/scheduling.k8s.io/v1beta1",
"/apis/storage.k8s.io",
"/apis/storage.k8s.io/v1",
"/apis/storage.k8s.io/v1beta1",
"/healthz",
"/healthz/autoregister-completion",
"/healthz/etcd",
"/healthz/log",
"/healthz/ping",
"/healthz/poststarthook/apiservice-openapi-controller",
"/healthz/poststarthook/apiservice-registration-controller",
"/healthz/poststarthook/apiservice-status-available-controller",
"/healthz/poststarthook/bootstrap-controller",
"/healthz/poststarthook/ca-registration",
"/healthz/poststarthook/generic-apiserver-start-informers",
"/healthz/poststarthook/kube-apiserver-autoregistration",
"/healthz/poststarthook/rbac/bootstrap-roles",
"/healthz/poststarthook/scheduling/bootstrap-system-priority-classes",
"/healthz/poststarthook/start-apiextensions-controllers",
"/healthz/poststarthook/start-apiextensions-informers",
"/healthz/poststarthook/start-kube-aggregator-informers",
"/healthz/poststarthook/start-kube-apiserver-admission-initializer",
"/healthz/poststarthook/start-kube-apiserver-informers",
"/logs",
"/metrics",
"/openapi/v2",
"/swagger-2.0.0.json",
"/swagger-2.0.0.pb-v1",
"/swagger-2.0.0.pb-v1.gz",
"/swagger.json",
"/swaggerapi",
"/version"
]
}
关于Group、Version、Resource
Group
Group即资源组,在kubernetes对资源进行分组时,对应的数据结构就是Group,源码路径:k8s.io/apimachinery/pkg/apis/meta/v1/types.go ,如下,可见Group有自己的名称和版本:
type APIGroup struct {
TypeMeta `json:",inline"`
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
Versions []GroupVersionForDiscovery `json:"versions" protobuf:"bytes,2,rep,name=versions"`
PreferredVersion GroupVersionForDiscovery `json:"preferredVersion,omitempty" protobuf:"bytes,3,opt,name=preferredVersion"`
ServerAddressByClientCIDRs []ServerAddressByClientCIDR `json:"serverAddressByClientCIDRs,omitempty" protobuf:"bytes,4,rep,name=serverAddressByClientCIDRs"`
}
在kubernetes中有两种资源组:有组名资源组和无组名资源组(也叫核心资源组Core Groups),它们都很常见;
deployment有组名,pod没有组名,咱们把它俩的OpenAPI放在一起对比就一目了然了:
Version
Version即版本,这个好理解,kubernetes的版本分为三种:
Alpha:内部测试版本,如v1alpha1
Beta:经历了官方和社区测试的相对稳定版,如v1beta1
Stable:正式发布版,如v1、v2
如下图红框,资源组batch之下有v1和v2alpha1两个版本,每个版本下都有多个资源:
数据结构源码还是在types.go文件中,如下:
type APIVersions struct {
TypeMeta `json:",inline"`
Versions []string `json:"versions" protobuf:"bytes,1,rep,name=versions"`
ServerAddressByClientCIDRs []ServerAddressByClientCIDR `json:"serverAddressByClientCIDRs" protobuf:"bytes,2,rep,name=serverAddressByClientCIDRs"`
}
Resource
Resource资源在kubernetes中的重要性是不言而喻的,常见的pod、service、deployment这些都是资源,下面是关于资源的一些小结:
在kubernetes环境被实例化的资源即资源对象(ResourceObject);
资源被分为持久性(Persistent Entity)和非持久性(Ephemeral Entity),持久性如deployment,创建后会在etcd保存,非持久性如pod;
资源的源码:
type APIResource struct {
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
SingularName string `json:"singularName" protobuf:"bytes,6,opt,name=singularName"`
Namespaced bool `json:"namespaced" protobuf:"varint,2,opt,name=namespaced"`
Group string `json:"group,omitempty" protobuf:"bytes,8,opt,name=group"`
Version string `json:"version,omitempty" protobuf:"bytes,9,opt,name=version"`
Kind string `json:"kind" protobuf:"bytes,3,opt,name=kind"`
Verbs Verbs `json:"verbs" protobuf:"bytes,4,opt,name=verbs"`
ShortNames []string `json:"shortNames,omitempty" protobuf:"bytes,5,rep,name=shortNames"`
Categories []string `json:"categories,omitempty" protobuf:"bytes,7,rep,name=categories"`
StorageVersionHash string `json:"storageVersionHash,omitempty" protobuf:"bytes,10,opt,name=storageVersionHash"`
}
kubernetes为资源准备了8种操作:create、delete、deletecollection、get、list、patch、update、watch,每一种资源都支持其中的一部分,这在每个资源的API文档中可以看到;
资源支持以命名空间(namespace)进行隔离;
资源对象描述文件在日常操作中频繁用到,一共由五部分组成:apiVersion、kind、metadata、spec、status,下图是官方的deployment描述文件,用于创建3个nginx pod,对着红框和文字就了解每个部分的作用了:
上图并没有status,该部分是用来反应当前资源对象状态的,体现在资源的数据结构中,如下所示:
type Deployment struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Spec DeploymentSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
Status DeploymentStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
API Server安装和运行
API Server 是通过提供的 kube-apiserver
二进制文件直接运行的,下面的例子指定了 service 分配的 ip 范围,etcd 的地址,和对外提供服务的 ip 地址:
[root@localhost vagrant]# curl http://192.168.8.100:8080
{
"paths": [
"/api",
"/api/v1",
"/apis",
"/apis/apps",
"/apis/apps/v1alpha1",
"/apis/autoscaling",
"/apis/autoscaling/v1",
"/apis/batch",
"/apis/batch/v1",
"/apis/batch/v2alpha1",
"/apis/extensions",
"/apis/extensions/v1beta1",
"/apis/policy",
"/apis/policy/v1alpha1",
"/apis/rbac.authorization.k8s.io",
"/apis/rbac.authorization.k8s.io/v1alpha1",
"/healthz",
"/healthz/ping",
"/logs/",
"/metrics",
"/swaggerapi/",
"/ui/",
"/version"
]
直接访问 8080
端口,API Server 会返回它提供了哪些接口:
[root@localhost vagrant]# curl http://192.168.8.100:8080
{
"paths": [
"/api",
"/api/v1",
"/apis",
"/apis/apps",
"/apis/apps/v1alpha1",
"/apis/autoscaling",
"/apis/autoscaling/v1",
"/apis/batch",
"/apis/batch/v1",
"/apis/batch/v2alpha1",
"/apis/extensions",
"/apis/extensions/v1beta1",
"/apis/policy",
"/apis/policy/v1alpha1",
"/apis/rbac.authorization.k8s.io",
"/apis/rbac.authorization.k8s.io/v1alpha1",
"/healthz",
"/healthz/ping",
"/logs/",
"/metrics",
"/swaggerapi/",
"/ui/",
"/version"
]
而目前最重要的路径是 /api/v1
,里面包含了 kubernetes 所有资源的操作,比如下面的 nodes:
➜ ~ http http://192.168.8.100:8080/api/v1/nodes
HTTP/1.1 200 OK
Content-Length: 112
Content-Type: application/json
Date: Thu, 08 Sep 2016 08:14:45 GMT
{
"apiVersion": "v1",
"items": [],
"kind": "NodeList",
"metadata": {
"resourceVersion": "12",
"selfLink": "/api/v1/nodes"
}
API 以 json 的形式返回,会通过 apiVersion
来说明 API 版本号,kind
说明请求的是什么资源。不过这里面的内容是空的,因为目前还没有任何 kubelet 节点接入到我们的 API Server。对应的,pod 也是空的:
➜ ~ http http://192.168.8.100:8080/api/v1/pods
HTTP/1.1 200 OK
Content-Length: 110
Content-Type: application/json
Date: Thu, 08 Sep 2016 08:18:53 GMT
{
"apiVersion": "v1",
"items": [],
"kind": "PodList",
"metadata": {
"resourceVersion": "12",
"selfLink": "/api/v1/pods"
}
添加节点
添加节点也非常简单,启动 kubelet
的时候使用 --api-servers
指定要接入的 API Server 就行。kubelet 启动之后,会把自己注册到指定的 API Server,然后监听 API 对应 pod 的变化,根据 API 中 pod 的实际信息来管理节点上 pod 的生命周期。
现在访问 /api/v1/nodes
就能看到已经添加进来的节点:
➜ ~ http http://192.168.8.100:8080/api/v1/nodes
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 08 Sep 2016 08:27:44 GMT
Transfer-Encoding: chunked
{
"apiVersion": "v1",
"items": [
{
"metadata": {
"annotations": {
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
},
"creationTimestamp": "2016-09-08T08:23:01Z",
"labels": {
"beta.kubernetes.io/arch": "amd64",
"beta.kubernetes.io/os": "linux",
"kubernetes.io/hostname": "192.168.8.100"
},
"name": "192.168.8.100",
"resourceVersion": "65",
"selfLink": "/api/v1/nodes/192.168.8.100",
"uid": "74e16eba-759d-11e6-b463-080027c09e5b"
},
"spec": {
"externalID": "192.168.8.100"
},
"status": {
"addresses": [
{
"address": "192.168.8.100",
"type": "LegacyHostIP"
},
{
"address": "192.168.8.100",
"type": "InternalIP"
}
],
"allocatable": {
"alpha.kubernetes.io/nvidia-gpu": "0",
"cpu": "1",
"memory": "502164Ki",
"pods": "110"
},
"capacity": {
"alpha.kubernetes.io/nvidia-gpu": "0",
"cpu": "1",
"memory": "502164Ki",
"pods": "110"
},
"conditions": [
{
"lastHeartbeatTime": "2016-09-08T08:27:36Z",
"lastTransitionTime": "2016-09-08T08:23:01Z",
"message": "kubelet has sufficient disk space available",
"reason": "KubeletHasSufficientDisk",
"status": "False",
"type": "OutOfDisk"
},
{
"lastHeartbeatTime": "2016-09-08T08:27:36Z",
"lastTransitionTime": "2016-09-08T08:23:01Z",
"message": "kubelet has sufficient memory available",
"reason": "KubeletHasSufficientMemory",
"status": "False",
"type": "MemoryPressure"
},
{
"lastHeartbeatTime": "2016-09-08T08:27:36Z",
"lastTransitionTime": "2016-09-08T08:24:56Z",
"message": "kubelet is posting ready status",
"reason": "KubeletReady",
"status": "True",
"type": "Ready"
}
],
"daemonEndpoints": {
"kubeletEndpoint": {
"Port": 10250
}
},
"images": [
{
"names": [
"172.16.1.41:5000/nginx:latest"
],
"sizeBytes": 425626718
},
{
"names": [
"172.16.1.41:5000/hyperkube:v0.18.2"
],
"sizeBytes": 207121551
},
{
"names": [
"172.16.1.41:5000/etcd:v3.0.4"
],
"sizeBytes": 43302056
},
{
"names": [
"172.16.1.41:5000/busybox:latest"
],
"sizeBytes": 1092588
},
{
"names": [
"172.16.1.41:5000/google_containers/pause:0.8.0"
],
"sizeBytes": 241656
}
],
"nodeInfo": {
"architecture": "amd64",
"bootID": "48955926-11dd-4ad3-8bb0-2585b1c9215d",
"containerRuntimeVersion": "docker://1.10.3",
"kernelVersion": "3.10.0-123.13.1.el7.x86_64",
"kubeProxyVersion": "v1.3.1-beta.0.6+fbf3f3e5292fb0",
"kubeletVersion": "v1.3.1-beta.0.6+fbf3f3e5292fb0",
"machineID": "b9597c4ae5f24494833d35e806e00b29",
"operatingSystem": "linux",
"osImage": "CentOS Linux 7 (Core)",
"systemUUID": "823EB67A-057E-4EFF-AE7F-A758140CD2F7"
}
}
}
],
"kind": "NodeList",
"metadata": {
"resourceVersion": "65",
"selfLink": "/api/v1/nodes"
}
我们可以看到,kubelet 收集了很多关于自身节点的信息,这些信息也会不断更新。这些信息里面不仅包含节点的系统信息(系统架构,操作系统版本,内核版本等)、还有镜像信息(节点上有哪些已经下载的 docker 镜像)、资源信息(Memory 和 Disk 的总量和可用量)、以及状态信息(是否正常,可以分配 pod等)。
和 API Server 通信
编写的 yaml 文件转换成 json 格式,保存到文件里。主要注意的是,我们指定了 nodeName 的名字,这个名字必须和之前通过 /api/v1/nodes
得到的结果中 metadata.labels.kubernetes.io/hostname
保持一致:
[root@localhost vagrant]# cat nginx_pod.yml
apiVersion: v1
kind: Pod
metadata:
name: nginx-server
spec:
NodeName: 192.168.8.100
containers:
- name: nginx-server
image: 172.16.1.41:5000/nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: /var/log/nginx
name: nginx-logs
- name: log-output
image: 172.16.1.41:5000/busybox
command:
- bin/sh
args: [-c, 'tail -f /logdir/access.log']
volumeMounts:
- mountPath: /logdir
name: nginx-logs
volumes:
- name: nginx-logs
使用 curl 执行 POST 请求,设置头部内容为 application/json
,传过去文件中的 json 值,可以看到应答(其中 status
为 pending
,表示以及接收到请求,正在准备处理):
# curl -s -X POST -H "Content-Type: application/json" http://192.168.8.100:8080/api/v1/namespaces/default/pods --data @nginx_pod.json
{
"kind": "Pod",
"apiVersion": "v1",
"metadata": {
"name": "nginx-server",
"namespace": "default",
"selfLink": "/api/v1/namespaces/default/pods/nginx-server",
"uid": "888e95d0-75a9-11e6-b463-080027c09e5b",
"resourceVersion": "573",
"creationTimestamp": "2016-09-08T09:49:28Z"
},
"spec": {
"volumes": [
{
"name": "nginx-logs",
"emptyDir": {}
}
],
"containers": [
{
"name": "nginx-server",
"image": "172.16.1.41:5000/nginx",
"ports": [
{
"containerPort": 80,
"protocol": "TCP"
}
],
"resources": {},
"volumeMounts": [
{
"name": "nginx-logs",
"mountPath": "/var/log/nginx"
}
],
"terminationMessagePath": "/dev/termination-log",
"imagePullPolicy": "Always"
}
],
"restartPolicy": "Always",
"terminationGracePeriodSeconds": 30,
"dnsPolicy": "ClusterFirst",
"nodeName": "192.168.8.100",
"securityContext": {}
},
"status": {
"phase": "Pending"
}
}
返回中包含了我们提交 pod 的信息,并且添加了 status
、metadata
等额外信息。
等一段时间去查询 pod,就可以看到 pod 的状态已经更新了:
➜ http http://192.168.8.100:8080/api/v1/namespaces/default/pods
HTTP/1.1 200 OK
Content-Type: application/json
Date: Thu, 08 Sep 2016 09:51:29 GMT
Transfer-Encoding: chunked
{
"apiVersion": "v1",
"items": [
{
"metadata": {
"creationTimestamp": "2016-09-08T09:49:28Z",
"name": "nginx-server",
"namespace": "default",
"resourceVersion": "592",
"selfLink": "/api/v1/namespaces/default/pods/nginx-server",
"uid": "888e95d0-75a9-11e6-b463-080027c09e5b"
},
"spec": {
"containers": [
{
"image": "172.16.1.41:5000/nginx",
"imagePullPolicy": "Always",
"name": "nginx-server",
"ports": [
{
"containerPort": 80,
"protocol": "TCP"
}
],
"resources": {},
"terminationMessagePath": "/dev/termination-log",
"volumeMounts": [
{
"mountPath": "/var/log/nginx",
"name": "nginx-logs"
}
]
},
{
"args": [
"-c",
"tail -f /logdir/access.log"
],
"command": [
"bin/sh"
],
"image": "172.16.1.41:5000/busybox",
"imagePullPolicy": "Always",
"name": "log-output",
"resources": {},
"terminationMessagePath": "/dev/termination-log",
"volumeMounts": [
{
"mountPath": "/logdir",
"name": "nginx-logs"
}
]
}
],
"dnsPolicy": "ClusterFirst",
"nodeName": "192.168.8.100",
"restartPolicy": "Always",
"securityContext": {},
"terminationGracePeriodSeconds": 30,
"volumes": [
{
"emptyDir": {},
"name": "nginx-logs"
}
]
},
"status": {
"conditions": [
{
"lastProbeTime": null,
"lastTransitionTime": "2016-09-08T09:49:28Z",
"status": "True",
"type": "Initialized"
},
{
"lastProbeTime": null,
"lastTransitionTime": "2016-09-08T09:49:44Z",
"status": "True",
"type": "Ready"
},
{
"lastProbeTime": null,
"lastTransitionTime": "2016-09-08T09:49:44Z",
"status": "True",
"type": "PodScheduled"
}
],
"containerStatuses": [
{
"containerID": "docker://8b79eeea60f27b6d3f0a19cbd1b3ee3f83709bcf56574a6e1124c69a6376972d",
"image": "172.16.1.41:5000/busybox",
"imageID": "docker://sha256:8c566faa3abdaebc33d40c1b5e566374c975d17754c69370f78c00c162c1e075",
"lastState": {},
"name": "log-output",
"ready": true,
"restartCount": 0,
"state": {
"running": {
"startedAt": "2016-09-08T09:49:43Z"
}
}
},
{
"containerID": "docker://96e64cdba7b05d4e30710a20e958ff5b8f1f359c8d16d32622b36f0df0cb353c",
"image": "172.16.1.41:5000/nginx",
"imageID": "docker://sha256:51d764c1fd358ce81fd0e728436bd0175ff1f3fd85fc5d1a2f9ba3e7dc6bbaf6",
"lastState": {},
"name": "nginx-server",
"ready": true,
"restartCount": 0,
"state": {
"running": {
"startedAt": "2016-09-08T09:49:36Z"
}
}
}
],
"hostIP": "192.168.8.100",
"phase": "Running",
"podIP": "172.17.0.2",
"startTime": "2016-09-08T09:49:28Z"
}
}
],
"kind": "PodList",
"metadata": {
"resourceVersion": "602",
"selfLink": "/api/v1/namespaces/default/pods"
}
}
可以看到 pod 已经在运行,并且给分配了 ip:172.17.0.2
,通过 curl 也可以访问它的服务:
[root@localhost vagrant]# curl -s http://172.17.0.2 | head -n 5
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx on Debian!</title>
<style>
关于 API Server 提供的所有接口,可以参考官方的 [API 文档][http://kamalmarhubi.com/blog/2015/09/06/kubernetes-from-the-ground-up-the-api-server/]。
kubectl -s http://ip:8080 get pods