Kubernetes 之APIServer组件简介

简介

在 kubernetes 集群中,API Server 有着非常重要的角色。API Server 负责和 etcd 交互(其他组件不会直接操作 etcd,只有 API Server 这么做),是整个 kubernetes 集群的数据中心,所有的交互都是以 API Server 为核心的。简单来说,API Server 提供了一下的功能:

  • 整个集群管理的 API 接口:所有对集群进行的查询和管理都要通过 API 来进行
  • 集群内部各个模块之间通信的枢纽:所有模块之前并不会之间互相调用,而是通过和 API Server 打交道来完成自己那部分的工作
  • 集群安全控制:API Server 提供的验证和授权保证了整个集群的安全

在这篇教程中,我们的系统架构将变成下面这个样子:

我们把要配置的 pod 通过 kubectl 发送给 API Server,里面已经手动指定了要运行的节点。API Server 解析并保存对应的资源,对应的 kubelet 定时拉取数据时候发现 pod 是分配给自己的,会下载对应的配置并执行去生成 pod。

参数介绍

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 的定义  

安装和运行

API Server 是通过提供的 kube-apiserver 二进制文件直接运行的,下面的例子指定了 service 分配的 ip 范围,etcd 的地址,和对外提供服务的 ip 地址:

 
  1. /usr/bin/kube-apiserver \

  2. --service-cluster-ip-range=10.20.0.1/24 \

  3. --etcd-servers=http://127.0.0.1:2379 \

  4. --advertise-address=192.168.8.100 \

  5. --bind-address=192.168.8.100 \

  6. --insecure-bind-address=192.168.8.100 \

  7. --v=4

这篇教程不会提供对 API Server 进行 HTTPS 的配置,所有的操作都是直接通过 8080 非安全端口访问的。

直接访问 8080 端口,API Server 会返回它提供了哪些接口:

 
  1. [root@localhost vagrant]# curl http://192.168.8.100:8080

  2. {

  3. "paths": [

  4. "/api",

  5. "/api/v1",

  6. "/apis",

  7. "/apis/apps",

  8. "/apis/apps/v1alpha1",

  9. "/apis/autoscaling",

  10. "/apis/autoscaling/v1",

  11. "/apis/batch",

  12. "/apis/batch/v1",

  13. "/apis/batch/v2alpha1",

  14. "/apis/extensions",

  15. "/apis/extensions/v1beta1",

  16. "/apis/policy",

  17. "/apis/policy/v1alpha1",

  18. "/apis/rbac.authorization.k8s.io",

  19. "/apis/rbac.authorization.k8s.io/v1alpha1",

  20. "/healthz",

  21. "/healthz/ping",

  22. "/logs/",

  23. "/metrics",

  24. "/swaggerapi/",

  25. "/ui/",

  26. "/version"

  27. ]

  28. }

而目前最重要的路径是 /api/v1,里面包含了 kubernetes 所有资源的操作,比如下面的 nodes:

 
  1. ➜ ~ http http://192.168.8.100:8080/api/v1/nodes

  2. HTTP/1.1 200 OK

  3. Content-Length: 112

  4. Content-Type: application/json

  5. Date: Thu, 08 Sep 2016 08:14:45 GMT

  6.  
  7. {

  8. "apiVersion": "v1",

  9. "items": [],

  10. "kind": "NodeList",

  11. "metadata": {

  12. "resourceVersion": "12",

  13. "selfLink": "/api/v1/nodes"

  14. }

  15. }

  16.  

API 以 json 的形式返回,会通过 apiVersion 来说明 API 版本号,kind 说明请求的是什么资源。不过这里面的内容是空的,因为目前还没有任何 kubelet 节点接入到我们的 API Server。对应的,pod 也是空的:

 
  1. ➜ ~ http http://192.168.8.100:8080/api/v1/pods

  2. HTTP/1.1 200 OK

  3. Content-Length: 110

  4. Content-Type: application/json

  5. Date: Thu, 08 Sep 2016 08:18:53 GMT

  6.  
  7. {

  8. "apiVersion": "v1",

  9. "items": [],

  10. "kind": "PodList",

  11. "metadata": {

  12. "resourceVersion": "12",

  13. "selfLink": "/api/v1/pods"

  14. }

  15. }

添加节点

添加节点也非常简单,启动 kubelet 的时候使用 --api-servers 指定要接入的 API Server 就行。kubelet 启动之后,会把自己注册到指定的 API Server,然后监听 API 对应 pod 的变化,根据 API 中 pod 的实际信息来管理节点上 pod 的生命周期。

现在访问 /api/v1/nodes 就能看到已经添加进来的节点:

 
  1. ➜ ~ http http://192.168.8.100:8080/api/v1/nodes

  2. HTTP/1.1 200 OK

  3. Content-Type: application/json

  4. Date: Thu, 08 Sep 2016 08:27:44 GMT

  5. Transfer-Encoding: chunked

  6.  
  7. {

  8. "apiVersion": "v1",

  9. "items": [

  10. {

  11. "metadata": {

  12. "annotations": {

  13. "volumes.kubernetes.io/controller-managed-attach-detach": "true"

  14. },

  15. "creationTimestamp": "2016-09-08T08:23:01Z",

  16. "labels": {

  17. "beta.kubernetes.io/arch": "amd64",

  18. "beta.kubernetes.io/os": "linux",

  19. "kubernetes.io/hostname": "192.168.8.100"

  20. },

  21. "name": "192.168.8.100",

  22. "resourceVersion": "65",

  23. "selfLink": "/api/v1/nodes/192.168.8.100",

  24. "uid": "74e16eba-759d-11e6-b463-080027c09e5b"

  25. },

  26. "spec": {

  27. "externalID": "192.168.8.100"

  28. },

  29. "status": {

  30. "addresses": [

  31. {

  32. "address": "192.168.8.100",

  33. "type": "LegacyHostIP"

  34. },

  35. {

  36. "address": "192.168.8.100",

  37. "type": "InternalIP"

  38. }

  39. ],

  40. "allocatable": {

  41. "alpha.kubernetes.io/nvidia-gpu": "0",

  42. "cpu": "1",

  43. "memory": "502164Ki",

  44. "pods": "110"

  45. },

  46. "capacity": {

  47. "alpha.kubernetes.io/nvidia-gpu": "0",

  48. "cpu": "1",

  49. "memory": "502164Ki",

  50. "pods": "110"

  51. },

  52. "conditions": [

  53. {

  54. "lastHeartbeatTime": "2016-09-08T08:27:36Z",

  55. "lastTransitionTime": "2016-09-08T08:23:01Z",

  56. "message": "kubelet has sufficient disk space available",

  57. "reason": "KubeletHasSufficientDisk",

  58. "status": "False",

  59. "type": "OutOfDisk"

  60. },

  61. {

  62. "lastHeartbeatTime": "2016-09-08T08:27:36Z",

  63. "lastTransitionTime": "2016-09-08T08:23:01Z",

  64. "message": "kubelet has sufficient memory available",

  65. "reason": "KubeletHasSufficientMemory",

  66. "status": "False",

  67. "type": "MemoryPressure"

  68. },

  69. {

  70. "lastHeartbeatTime": "2016-09-08T08:27:36Z",

  71. "lastTransitionTime": "2016-09-08T08:24:56Z",

  72. "message": "kubelet is posting ready status",

  73. "reason": "KubeletReady",

  74. "status": "True",

  75. "type": "Ready"

  76. }

  77. ],

  78. "daemonEndpoints": {

  79. "kubeletEndpoint": {

  80. "Port": 10250

  81. }

  82. },

  83. "images": [

  84. {

  85. "names": [

  86. "172.16.1.41:5000/nginx:latest"

  87. ],

  88. "sizeBytes": 425626718

  89. },

  90. {

  91. "names": [

  92. "172.16.1.41:5000/hyperkube:v0.18.2"

  93. ],

  94. "sizeBytes": 207121551

  95. },

  96. {

  97. "names": [

  98. "172.16.1.41:5000/etcd:v3.0.4"

  99. ],

  100. "sizeBytes": 43302056

  101. },

  102. {

  103. "names": [

  104. "172.16.1.41:5000/busybox:latest"

  105. ],

  106. "sizeBytes": 1092588

  107. },

  108. {

  109. "names": [

  110. "172.16.1.41:5000/google_containers/pause:0.8.0"

  111. ],

  112. "sizeBytes": 241656

  113. }

  114. ],

  115. "nodeInfo": {

  116. "architecture": "amd64",

  117. "bootID": "48955926-11dd-4ad3-8bb0-2585b1c9215d",

  118. "containerRuntimeVersion": "docker://1.10.3",

  119. "kernelVersion": "3.10.0-123.13.1.el7.x86_64",

  120. "kubeProxyVersion": "v1.3.1-beta.0.6+fbf3f3e5292fb0",

  121. "kubeletVersion": "v1.3.1-beta.0.6+fbf3f3e5292fb0",

  122. "machineID": "b9597c4ae5f24494833d35e806e00b29",

  123. "operatingSystem": "linux",

  124. "osImage": "CentOS Linux 7 (Core)",

  125. "systemUUID": "823EB67A-057E-4EFF-AE7F-A758140CD2F7"

  126. }

  127. }

  128. }

  129. ],

  130. "kind": "NodeList",

  131. "metadata": {

  132. "resourceVersion": "65",

  133. "selfLink": "/api/v1/nodes"

  134. }

  135. }

我们可以看到,kubelet 收集了很多关于自身节点的信息,这些信息也会不断更新。这些信息里面不仅包含节点的系统信息(系统架构,操作系统版本,内核版本等)、还有镜像信息(节点上有哪些已经下载的 docker 镜像)、资源信息(Memory 和 Disk 的总量和可用量)、以及状态信息(是否正常,可以分配 pod等)。

和 API Server 通信

下面我们就通过 API Server 来创建 pod,而不是像上篇文章那样用拷贝文件的方式。我们把编写的 yaml 文件转换成 json 格式,保存到文件里。主要注意的是,我们指定了 nodeName 的名字,这个名字必须和之前通过 /api/v1/nodes 得到的结果中 metadata.labels.kubernetes.io/hostname 保持一致:

 
  1. [root@localhost vagrant]# cat nginx_pod.yml

  2. apiVersion: v1

  3. kind: Pod

  4. metadata:

  5. name: nginx-server

  6. spec:

  7. nodeName: 192.168.8.100

  8. containers:

  9. - name: nginx-server

  10. image: 172.16.1.41:5000/nginx

  11. ports:

  12. - containerPort: 80

  13. volumeMounts:

  14. - mountPath: /var/log/nginx

  15. name: nginx-logs

  16. - name: log-output

  17. image: 172.16.1.41:5000/busybox

  18. command:

  19. - bin/sh

  20. args: [-c, 'tail -f /logdir/access.log']

  21. volumeMounts:

  22. - mountPath: /logdir

  23. name: nginx-logs

  24. volumes:

  25. - name: nginx-logs

  26. emptyDir: {}

使用 curl 执行 POST 请求,设置头部内容为 application/json,传过去文件中的 json 值,可以看到应答(其中 status 为 pending,表示以及接收到请求,正在准备处理):

 
  1. # curl -s -X POST -H "Content-Type: application/json" http://192.168.8.100:8080/api/v1/namespaces/default/pods --data @nginx_pod.json

  2. {

  3. "kind": "Pod",

  4. "apiVersion": "v1",

  5. "metadata": {

  6. "name": "nginx-server",

  7. "namespace": "default",

  8. "selfLink": "/api/v1/namespaces/default/pods/nginx-server",

  9. "uid": "888e95d0-75a9-11e6-b463-080027c09e5b",

  10. "resourceVersion": "573",

  11. "creationTimestamp": "2016-09-08T09:49:28Z"

  12. },

  13. "spec": {

  14. "volumes": [

  15. {

  16. "name": "nginx-logs",

  17. "emptyDir": {}

  18. }

  19. ],

  20. "containers": [

  21. {

  22. "name": "nginx-server",

  23. "image": "172.16.1.41:5000/nginx",

  24. "ports": [

  25. {

  26. "containerPort": 80,

  27. "protocol": "TCP"

  28. }

  29. ],

  30. "resources": {},

  31. "volumeMounts": [

  32. {

  33. "name": "nginx-logs",

  34. "mountPath": "/var/log/nginx"

  35. }

  36. ],

  37. "terminationMessagePath": "/dev/termination-log",

  38. "imagePullPolicy": "Always"

  39. }

  40. ],

  41. "restartPolicy": "Always",

  42. "terminationGracePeriodSeconds": 30,

  43. "dnsPolicy": "ClusterFirst",

  44. "nodeName": "192.168.8.100",

  45. "securityContext": {}

  46. },

  47. "status": {

  48. "phase": "Pending"

  49. }

  50. }

返回中包含了我们提交 pod 的信息,并且添加了 statusmetadata 等额外信息。

等一段时间去查询 pod,就可以看到 pod 的状态已经更新了:

 
  1. ➜ http http://192.168.8.100:8080/api/v1/namespaces/default/pods

  2. HTTP/1.1 200 OK

  3. Content-Type: application/json

  4. Date: Thu, 08 Sep 2016 09:51:29 GMT

  5. Transfer-Encoding: chunked

  6.  
  7. {

  8. "apiVersion": "v1",

  9. "items": [

  10. {

  11. "metadata": {

  12. "creationTimestamp": "2016-09-08T09:49:28Z",

  13. "name": "nginx-server",

  14. "namespace": "default",

  15. "resourceVersion": "592",

  16. "selfLink": "/api/v1/namespaces/default/pods/nginx-server",

  17. "uid": "888e95d0-75a9-11e6-b463-080027c09e5b"

  18. },

  19. "spec": {

  20. "containers": [

  21. {

  22. "image": "172.16.1.41:5000/nginx",

  23. "imagePullPolicy": "Always",

  24. "name": "nginx-server",

  25. "ports": [

  26. {

  27. "containerPort": 80,

  28. "protocol": "TCP"

  29. }

  30. ],

  31. "resources": {},

  32. "terminationMessagePath": "/dev/termination-log",

  33. "volumeMounts": [

  34. {

  35. "mountPath": "/var/log/nginx",

  36. "name": "nginx-logs"

  37. }

  38. ]

  39. },

  40. {

  41. "args": [

  42. "-c",

  43. "tail -f /logdir/access.log"

  44. ],

  45. "command": [

  46. "bin/sh"

  47. ],

  48. "image": "172.16.1.41:5000/busybox",

  49. "imagePullPolicy": "Always",

  50. "name": "log-output",

  51. "resources": {},

  52. "terminationMessagePath": "/dev/termination-log",

  53. "volumeMounts": [

  54. {

  55. "mountPath": "/logdir",

  56. "name": "nginx-logs"

  57. }

  58. ]

  59. }

  60. ],

  61. "dnsPolicy": "ClusterFirst",

  62. "nodeName": "192.168.8.100",

  63. "restartPolicy": "Always",

  64. "securityContext": {},

  65. "terminationGracePeriodSeconds": 30,

  66. "volumes": [

  67. {

  68. "emptyDir": {},

  69. "name": "nginx-logs"

  70. }

  71. ]

  72. },

  73. "status": {

  74. "conditions": [

  75. {

  76. "lastProbeTime": null,

  77. "lastTransitionTime": "2016-09-08T09:49:28Z",

  78. "status": "True",

  79. "type": "Initialized"

  80. },

  81. {

  82. "lastProbeTime": null,

  83. "lastTransitionTime": "2016-09-08T09:49:44Z",

  84. "status": "True",

  85. "type": "Ready"

  86. },

  87. {

  88. "lastProbeTime": null,

  89. "lastTransitionTime": "2016-09-08T09:49:44Z",

  90. "status": "True",

  91. "type": "PodScheduled"

  92. }

  93. ],

  94. "containerStatuses": [

  95. {

  96. "containerID": "docker://8b79eeea60f27b6d3f0a19cbd1b3ee3f83709bcf56574a6e1124c69a6376972d",

  97. "image": "172.16.1.41:5000/busybox",

  98. "imageID": "docker://sha256:8c566faa3abdaebc33d40c1b5e566374c975d17754c69370f78c00c162c1e075",

  99. "lastState": {},

  100. "name": "log-output",

  101. "ready": true,

  102. "restartCount": 0,

  103. "state": {

  104. "running": {

  105. "startedAt": "2016-09-08T09:49:43Z"

  106. }

  107. }

  108. },

  109. {

  110. "containerID": "docker://96e64cdba7b05d4e30710a20e958ff5b8f1f359c8d16d32622b36f0df0cb353c",

  111. "image": "172.16.1.41:5000/nginx",

  112. "imageID": "docker://sha256:51d764c1fd358ce81fd0e728436bd0175ff1f3fd85fc5d1a2f9ba3e7dc6bbaf6",

  113. "lastState": {},

  114. "name": "nginx-server",

  115. "ready": true,

  116. "restartCount": 0,

  117. "state": {

  118. "running": {

  119. "startedAt": "2016-09-08T09:49:36Z"

  120. }

  121. }

  122. }

  123. ],

  124. "hostIP": "192.168.8.100",

  125. "phase": "Running",

  126. "podIP": "172.17.0.2",

  127. "startTime": "2016-09-08T09:49:28Z"

  128. }

  129. }

  130. ],

  131. "kind": "PodList",

  132. "metadata": {

  133. "resourceVersion": "602",

  134. "selfLink": "/api/v1/namespaces/default/pods"

  135. }

  136. }

可以看到 pod 已经在运行,并且给分配了 ip:172.17.0.2,通过 curl 也可以访问它的服务:

  1. [root@localhost vagrant]# curl -s http://172.17.0.2 | head -n 5

  2. <!DOCTYPE html>

  3. <html>

  4. <head>

  5. <title>Welcome to nginx on Debian!</title>

  6. <style>

关于 API Server 提供的所有接口,可以参考官方的 [API 文档][http://kamalmarhubi.com/blog/2015/09/06/kubernetes-from-the-ground-up-the-api-server/]。

kubectl 命令

理论上,所有 kubernetes 提供的功能都能够直接通过 HTTP API 交互来实现,但是你也看到了,非常复杂。因此 kubernetes 提供了另外了 kubectl 命令行,它封装了 HTTP API 的交互过程,通过一系列的子命令来操作资源。比如我们上面创建 pod 的过程就可以通过

  1. kubectl create -f nginx_pod.yml

一行命令实现,查看 pod 的信息也很简单:

kubectl -s http://ip:8080 get pods

删除已经创建的 pod,可以使用 delete 命令:

  1. kubectl delete pods/nginx-server

kubectl 的用法也有详细的官方文档,这里就不多说了。

参考资料

猜你喜欢

转载自blog.csdn.net/bbwangj/article/details/81904421