├── .gitignore
├── FOREWORD.md
├── INTRODUCTION.md
├── README.md
├── SUMMARY.md
├── chapter02
├── 2.3-kubernetes_API.md
└── images
│ ├── kubernetes_api_format.drawio
│ └── kubernetes_api_format.png
├── chapter03
├── 1.1-pod_overview.md
├── 1.2-pod_quick_start.md
├── 2.1-replicationcontroller_overview.md
├── 2.2-replicationcontroller_quick_start.md
├── 3.1-replicaset_overview.md
├── 3.2-replicaset_quick_start.md
├── 4.1-deployment_overview.md
├── 4.2-deployment_quickstart.md
├── 5.1-daemonset_overview.md
├── 5.2-daemonset_quickstart.md
├── images
│ ├── daemonset_overview.drawio
│ ├── daemonset_overview.png
│ ├── pod_overview.drawio
│ ├── pod_overview.png
│ ├── replication_controller_backgroud.drawio
│ ├── replication_controller_backgroud.png
│ ├── replication_controller_overview.drawio
│ └── replication_controller_overview.png
└── manifests
│ ├── daemonset.yaml
│ ├── deployment.yaml
│ ├── pod_simple.yaml
│ ├── replicaset.yaml
│ └── replication_controller_simple.yaml
├── chapter04
├── 1.1-service_overview.md
├── 1.2-service_quickstart.md
├── images
│ ├── service_overview.drawio
│ └── service_overview.png
└── manifests
│ ├── busybox.yaml
│ └── service.yaml
├── chapter06
├── 1.1-secret_overview.md
└── manifests
│ └── secret.yaml
├── chapter07
├── 1.1-certificate.md
├── 1.2-certificate-sign.md
└── images
│ ├── cert_digital_signature.drawio
│ ├── cert_digital_signature.png
│ ├── cert_http_protocol.drawio
│ ├── cert_http_protocol.png
│ ├── cert_http_unsafe.drawio
│ ├── cert_http_unsafe.png
│ ├── cert_https_asymmetric_encrypt.drawio
│ ├── cert_https_asymmetric_encrypt.png
│ ├── cert_https_asymmetric_encrypt_risk.drawio
│ ├── cert_https_asymmetric_encrypt_risk.png
│ ├── cert_https_whole_encrypt.drawio
│ ├── cert_https_whole_encrypt.png
│ ├── cert_sign.drawio
│ └── cert_sign.png
├── chapter09
├── 1.1-admissioncontroller_overview.md
├── 1.2.1-admissioncontroller_namespacelifecycle.md
├── 1.2.15-admissioncontroller_mutatingadmissionwebhook.md
└── images
│ ├── admission-controller-phases.png
│ ├── mutatingadmissionwebhook.drawio
│ └── mutatingadmissionwebhook.png
├── chapter10
├── 1.1-resourcequota_overview.md
└── manifests
│ └── resourcequota.yaml
├── chapter16
├── 1.1-api_convention_optional_vs_required.md
├── 1.2-api_convention_condition.md
└── 1.3-api_convention_event.md
└── chapter19
├── 1.2-mapping-ports-to-host.md
├── 1.3-port-forward.md
└── images
├── kind-default-port.drawio
└── kind-default-port.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Dependency directories (remove the comment below to include it)
15 | # vendor/
16 |
--------------------------------------------------------------------------------
/FOREWORD.md:
--------------------------------------------------------------------------------
1 | # 前言
2 |
3 | TODO
--------------------------------------------------------------------------------
/INTRODUCTION.md:
--------------------------------------------------------------------------------
1 | # 内容简介
2 |
3 | TODO
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 《Kubernetes设计与实现》
2 |
3 | 深入浅出地阐述Kubernetes特性、设计和实现细节。
4 |
5 | ## What
6 | TODO
7 |
8 | ## Why
9 | 笔者一直在思考`Kubernetes`学习曲线陡峭的原因以及破解之法。
10 |
11 | 关于Kubernetes的认知,笔者认为大体上分为以下三个阶段:
12 | - 第一阶段:会用
13 | - 第二阶段:理解
14 | - 第三阶段:精通
15 |
16 | 第一阶段主要满足工具使用的问题,可以按照文档构建自已的集群并部署应用。
17 |
18 | 第二阶段主要是准确理解各资源对象的概念以及各组件之间的协作,比如部署一个`Deployment`,脑子中有一个动态的流程图,
19 | 它是如何创建副本、Pod如何调度等Pod如何运行等。
20 |
21 | 第三阶段进一步深化,能够理解各种资源对象引入的背景以及其尝试解决的痛点,同时也可以从源码层面分析问题。
22 |
23 | TODO...
--------------------------------------------------------------------------------
/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 | * [内容简介](INTRODUCTION.md)
3 | * [前言](FOREWORD.md)
4 | * [第二章:Kubernetes基础]
5 | * [2.3 Kubernetes API](chapter02/2.3-kubernetes_API.md)
6 | * [第三章:工作负载管理]
7 | * [3.1 Pod]
8 | * [3.1.1 Pod概述](chapter03/1.1-pod_overview.md)
9 | * [3.1.1 Pod初体验](chapter03/1.2-pod_quick_start.md)
10 | * [3.2 ReplicationController]
11 | * [3.2.1 ReplicationController概述](chapter03/2.1-replicationcontroller_overview.md)
12 | * [3.2.2 ReplicationController初体验](chapter03/2.2-replicationcontroller_quick_start.md)
13 | * [3.3 ReplicaSet]
14 | * [3.3.1 ReplicaSet概述](chapter03/3.1-replicaset_overview.md)
15 | * [3.3.2 ReplicaSet初体验](chapter03/3.2-replicaset_quick_start.md)
16 | * [3.4 Deployment]
17 | * [3.4.1 Deployment概述](chapter03/4.1-deployment_overview.md)
18 | * [3.4.2 Deployment初体验](chapter03/4.2-deployment_quickstart.md)
19 | * [3.5 DaemonSet]
20 | * [3.5.1 DaemonSet概述](chapter03/5.1-daemonset_overview.md)
21 | * [3.5.2 DaemonSet初体验](chapter03/5.2-daemonset_quickstart.md)
22 | * [第四章:服务]
23 | * [4.1 Service]
24 | * [4.1.1 Service概述](chapter04/1.1-service_overview.md)
25 | * [4.1.1 Service上手体验](chapter04/1.2-service_quickstart.md)
26 | * [第六章:配置管理]
27 | * [6.1 Secret--机密信息管理]
28 | * [6.1.1 Secret概述](chapter06/1.1-secret_overview.md)
29 | * [第七章:集群认证]
30 | * [7.1 证书]
31 | * [7.1.1 证书基础](chapter07/1.1-certificate.md)
32 | * [7.1.2 证书签发流程](chapter07/1.2-certificate-sign.md)
33 | * [第九章:准入控制器]
34 | * [9.1 准入控制器概述]
35 | * [9.1.1 概述](chapter09/1.1-admissioncontroller_overview.md)
36 | * [9.1.2 内置默认启动的插件]
37 | * [9.1.2.1 NamespaceLifecycle](chapter09/1.2.1-admissioncontroller_namespacelifecycle.md)
38 | * [9.1.2.15 MutatingAdmissionWebhook](chapter09/1.2.15-admissioncontroller_mutatingadmissionwebhook.md)
39 | * [第十章:ResourceQuota]
40 | * [10.1 ResourceQuota概述](chapter10/1.1-resourcequota_overview.md)
41 | * [第十六章:API设计约定]
42 | * [16.1 字段可选性设计约定](chapter16/1.1-api_convention_optional_vs_required.md)
43 | * [16.2 condition设计约定](chapter16/1.2-api_convention_condition.md)
44 | * [16.3 event设计约定](chapter16/1.3-api_convention_event.md)
45 | * [第十九章:Kubernetes生态]
46 | * [19.1 Kind]
47 | * [19.1.1 Kind概述]
48 | * [19.1.2 映射端口到主机](chapter19/1.2-mapping-ports-to-host.md)
49 | * [19.1.3 配置端口转发](chapter19/1.3-port-forward.md)
50 |
--------------------------------------------------------------------------------
/chapter02/2.3-kubernetes_API.md:
--------------------------------------------------------------------------------
1 | Kubernetes把其管理的资源均视为API对象,并对外提供`REST`风格的API来操纵这些对象。Kubernetes API由`kube-apiserver`组件提供,Kubernetes内部各组件与`kube-apiserver`通信也是使用API来驱动的,除此之外,命令行工具比如`kubectl`以及各种Kubernetes客户端程序均是使用API与Kubernetes通信。
2 |
3 | ## API格式
4 | Kubernetes API格式为`prefix/group/version/resource`,比如表示`Deployment`资源列表的API为`/apis/apps/v1/deployments`,其中`apis`表示前缀,`apps`表示API组,`v1`表示API组的版本,`deployments`表示资源名称,如下图所示:
5 |
6 | 
7 |
8 | ## API前缀
9 | API中的前缀无疑会增加URL的长度,这不由得会让我们思考以下几个问题:
10 | - 前缀存在的意义是什么呢?
11 | - Kubernetes API都有哪些前缀?
12 |
13 | 要回答这个问题就要从API的定义来说起,API英文全称是`Application Programming Interface`,即应用程序编程接口。那么从广义上说,Kubernetes 的`kube-apiserver`组件对外暴露的所有端点都可以称作API,比如:
14 | ```
15 | "/api/v1",
16 | "/apis/admissionregistration.k8s.io",
17 | "/apis/apps",
18 | "/apis/authentication.k8s.io",
19 | "/healthz",
20 | "/livez",
21 | "/metrics",
22 | "/version"
23 | ```
24 | 应用程序可以使用这些接口来实现特定的功能,比如使用`/api/v1`或`apis/apps`接口来创建资源,使用`/healthz`来查询集群健康状态,所以这些接口都是API。
25 |
26 | 但是,往往我们所说的Kubernetes API是狭义上的概念,即专指那些表示Kubernetes资源的API,所以为了与其他API有所区分,Kubernetes特意加了`api`前缀,该前缀表示这些API用于管理Kubernetes资源。
27 |
28 | 用于管理Kubernetes资源的API前缀除了`api`外,还有`apis`,在Kubernetes早期的设计中,只有`api`前缀,后来为了引入API组的设计,又添加了`apis`前缀,简单地说,使用`api`前缀的API是Kubernetes最核心的一组API,而使用`apis`前缀的API是Kubernetes发展过程中引入的分组API。
29 |
30 | ## API组
31 | 前文提到Kubernetes早期只有以`api`为前缀的API,这些API提供了Kubernetes最核心的功能。随着Kubernetes的不断演进,Kubernetes需要引入更过的功能,也即需要提供更多的API,这不仅给Kubernetes带来了沉重的负担,也给用户带了困扰。
32 |
33 | 一方面,随着Kubernetes提供的功能增多,Kubernetes自身开发和维护难度越来越大,另一方面,用户往往只需要Kubernetes提供的部分功能。所以为了使Kubernetes更容易扩展和演进,同时可以让用户有选择性地开启和关闭非核心功能,Kubernetes设计者们提出了API分组的理念。
34 |
35 | 所谓API分组理念是指把Kubernetes的API按照功能分组,该理念被提出时Kubernetes已经有了以`api`为前缀的一组核心API,考虑到兼容策略,这组API不适宜修改,所以API分组实际上针对非核心的扩展API,后续新加的功能统一使用`apis`为前缀,并把API按组区分,部分API组如下所示:
36 | ```
37 | "/apis/apps"
38 | "/apis/autoscaling"
39 | "/apis/rbac.authorization.k8s.io"
40 | ...
41 | ```
42 | 出现在前缀`apis`后面的就是API组,比如`apps`表示一组用于应用管理的API,`autoscaling`表示一组用于应用自动扩展的API,`rbac.authorization.k8s.io`表示一组用于基于角色控制的API。
43 |
44 | 把API分组最大的好处在于用户可以自由地开启和关闭相应的非核心功能。用户可以使用`kube-apiserver`组件提供的`--runtime-config`参数来显式地控制某项功能是否开启。比如:
45 | ```
46 | --runtime-config=autoscaling/v1=false,rbac.authorization.k8s.io/v1=true
47 | ```
48 | 上面的配置显式地将`autoscaling`功能关闭,同时把`rbac.authorization.k8s.io`功能开启。需要说明的是,相当大一部分API组默认是开启的,比如默认情况下`rbac.authorization.k8s.io`这组API是开启的,这意味着上面配置中`rbac.authorization.k8s.io/v1=true`是多余的,出现在本例中仅用于说明API组可以显式地控制开启和关闭。
49 |
50 | 把API分组另一非常重要的好处在于,它可以给每组API按照功能成熟度划分成不同的版本,比如`v1alpha1`,`v1beta1`,`v1`等。
51 |
52 | ## API版本
53 | 每组API都有相应的版本表示其成熟度,比如`autoscaling`就有多个版本:
54 | ```
55 | "/apis/autoscaling/v1",
56 | "/apis/autoscaling/v2beta1",
57 | "/apis/autoscaling/v2beta2",
58 | ```
59 | 为API提供版本并且多版本共存的意义在于为用户提供清晰的成熟度参考,比如版本名包含`alpha`表示该功能正在实验过程中,不推荐应用在生产环境中,因此Kubernetes默认不会开启这些功能,版本名包含`beta`表示该功能基本可用,希望用户尝试并提供反馈,因此Kubernetes往往默认启用这些功能,版本名为`vx`表示功能已固定,相应的API也不会再修改,用户可以放心使用。
60 |
61 | 为API分组同时为每个API提供多个版本,这允许每组功能可以不同的速度演进,而不必互相影响。
62 |
63 | ## 小结
64 | 了解一个应用的功能,从API入手往往能快速地掌握住该应用的概况,包括它是什么,它能用来做什么以及怎么使用它。本节简要地介绍了Kubernetes的API设计,借此读者可以从宏观上对Kubernetes API有个基本的了解,为将来详细了解每个功能点,也就是每个具体的API打下基础。
65 |
66 | Kubernetes API的分组设计为其提供了无限的扩展能力,借此机制可以轻松地为Kubernetes提供扩展功能,用户不仅可以使用CRD(Custom Resource Definition)功能来提供新的API,还可以通过扩展apiserver来扩展功能。
67 |
68 | 前文也提到,提出API分组理念以前,Kubernetes就存在了以`api`为前缀的一组API,这了保持兼容性,这组核心API并没有划分到特定的组,它的API格式则是`prefix/version/resource`(少了group名字),比如`/api/v1/Pods`。通常我们在说API版本时往往是指`group/version`,即带上组名和版本,为了描述上的方便,社区开发者日常交流时往往称这组特别的API为`core`组。
69 |
70 | ## 延伸阅读
71 | - 《The Kubernetes API》https://kubernetes.io/docs/concepts/overview/kubernetes-api/
72 | - 《Supporting multiple API groups》 https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
73 |
--------------------------------------------------------------------------------
/chapter02/images/kubernetes_api_format.drawio:
--------------------------------------------------------------------------------
1 | 7VjJbtswEP0aHxNYu3O0nQ0oWgRI0TRHxppIRClRoWhbztd3KFK73ey1i+YkzuNwSL73RBEaOfOkuBAki7/yENjIHofFyDkd2bY1nvj4UMhGI8HE00AkaGiSGuCaPkI10qBLGkLeSZScM0mzLrjgaQoL2cGIEHzdTbvnrDtrRiIYANcLwoboDQ1lrNGJHTT4JdAorma2/BPdk5Aq2ewkj0nI1y3IORs5c8G51K2kmANT5FW86HHnO3rrhQlI5XMGPNxG0c3d6fTiYfrjm3f53bv4cnVkqqwIW5oNYwGS0bx8ZOqxwpzzEDLGNwlOlZvdyE1FkYQCFzCLZcIQsLCZS8F/wZwzLhBJeYqZs3vKWA8ijEYphgusC4jPViAkRfKnpiOhYaimma1jKuE6Iws15xqthpjgyzQEtbuxKs9TaezjqNhsDAtCsZMxq9YBDQw8ASk2mFIN8I10xrtWJeW6cYLrGixuuWBiMGLMF9WlG32wYSR6gVzuFrl8ptjPM5J2ZPEflspZJS9HeUnMFBNsKytKdqp+bEXqmQm4p0VVDlenK+rOw9fcVmuIuaCPiJFqUe9hArtrAtsdmmCyxQPuR3nAex8PDAwQIbfZp/59/WshD0V//4P0R5ZyytNPB/Qd4PgH5oBg4AABOV8KpOn/VcmdHJhKJ1ve0546kIZTdUdVNDOS53TR1arPmy4A4eDK+iRJLRK8LSRUmABGJF11y29jxsxwxSlO3Hwrgx0aVCW0S82o9l21V8jufXStk14hSUQEclCoFKre9uu1s7bdi98oHhRU/lQvxLFnolvzeqj2adEONiZ4ruC4MP3+P3Ft2Jcx6jpvNYYz3rMx7A8wRopr0s5wPa8CSnMcB3XcGKSMNu3oCgTFnalj+oW20XT9acP7tI3zXueJu+/zxDmM86RltcAO/q7Vnjyhgn/Bkm7fks4rLen1Ldn/afBqS2LY/FXS6c2/OefsNw==
--------------------------------------------------------------------------------
/chapter02/images/kubernetes_api_format.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter02/images/kubernetes_api_format.png
--------------------------------------------------------------------------------
/chapter03/1.1-pod_overview.md:
--------------------------------------------------------------------------------
1 | ## Pod的概念
2 | 尽管Kubernetes是容器编排系统,但它并不直接管理容器,它管理的却是名为Pod的对象。
3 |
4 | Pod是对容器的高级抽像,Pod单词英文含义为豆荚,非常形象地揭示了其于容器的关系,就像一个豆荚中可以含一个或多个豆子一样,一个Pod也可以包含一个或多个容器。
5 |
6 | 在Kubernetes中,Pod是最基础的对象,不管Pod中包含多少容器,Pod的创建和销毁对应的是其包含的所有容器一并创建和销毁。
7 |
8 | ## Pod的优势
9 | 在部署容器化应用时,有时我们会希望多个密切相关的容器能够部署在同一节点,这样这些容器之间可以方便地共享本地存储,也可以方便地互相通信,甚至我们也希望这些容器能够一并创建和销毁。
10 |
11 | 比如,我们有一个容器用于运行Web 服务器,此外还有一个容器用于管理Web服务器的数据,考虑到两个容器需要共享存储,那么将这两个容器中封装到同一个Pod中就比较合适,如下图所示:
12 |
13 | 
14 |
15 | 与直接管理多个容器相比,Pod中的容器除了享有相同的生命周期以外,它们的网络和存储环境也有所不同。
16 |
17 | Kubernetes会给Pod分配一个唯一的IP地址,Pod为容器提供了相对隔离的网络环境,Pod中的容器与外部通信时都使用该IP地址,而Pod中的容器之间通信则可以直接使用`localhost`通信,此时只需要给互相通信的容器指定不同的端口即可。
18 |
19 | 在创建Pod时如果指定了存储卷,Kubernetes就会把存储卷挂载到每个容器,供容器共享使用。
20 |
21 | Pod的概念源于Kubernetes对实际应用场景的准确把握,当需要单独部署容器时,只需要在Pod中指定一个容器即可,当需要多个容器一并部署时,只需要在Pod中指定多个容器。
22 |
23 | ## Pod的局限性
24 | 尽管Pod可以封装容器,借此我们可以在一定程度上完成容器的批量管理,但实际上直接创建Pod的场景非常罕见。
25 |
26 | Kubernetes将Pod视为一种不可靠的资源,它没有自愈能力,当遇到node异常或因资源不足而被驱逐时,Pod将会被删除。
27 | 为了满足各种场景下管理Pod的诉求,Kubernetes在Pod之上又提供了多种控制器资源,比如`Deployment`、`StatefulSet`和`DaemonSet`等,这些控制器可以帮助我们更好的管理Pod,确保Pod总是按照我们预期的行为在运行。
28 |
29 | ## 小结
30 | Pod在Kubernetes系统中只是一个资源,它不是一个进程,它是基于容器的抽象,它为一个或多个容器准备运行环境,运行一个Pod,最终还是把容器一个个交给容器运行时来运行。
31 |
32 | 尽管在Kubernetes系统中我们一般不直接创建Pod,但它是最基础的资源,Kubernetes大部分特性都是围绕如何更好地运行、管理Pod而展开,所以必须对Pod有一定的了解才可以开始后面的学习。
33 |
34 | ## 延伸阅读
35 | - 《Pod Overview》https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/
--------------------------------------------------------------------------------
/chapter03/1.2-pod_quick_start.md:
--------------------------------------------------------------------------------
1 | 本节,我们通过一个简单的例子,来演示Pod资源的增删改查操作,而对Pod高级配置的介绍则留给后面的章节。
2 |
3 | ## 创建Pod
4 | 首先将如下配置保存到名为`pod_simple.yaml`的文件中:
5 | ```yaml
6 | apiVersion: v1
7 | kind: Pod
8 | metadata:
9 | name: pod-runs-nginx
10 | spec:
11 | containers:
12 | - name: nginx
13 | image: nginx:latest
14 | ```
15 | 通过配置可知,我们将创建一个类型为`Pod`的资源,资源的版本为`v1`,Pod名称为`pod-runs-nginx`,Pod中容器名字为`nginx`,容器镜像为`nginx:latest`。
16 |
17 | 使用`kubectl create`命令创建资源,如下所示:
18 | ```
19 | [root@ecs-d8b6 ~]# kubectl create -f pod_simple.yaml
20 | pod/pod-runs-nginx created
21 | ```
22 | 通过命令行输出可知,Kubernetes已经安照我们的配置创建了一个Pod资源。
23 |
24 | ## 查看Pod
25 | 使用命令`kubectl get`命令查看Pod,如下所示:
26 | ```
27 | [root@ecs-d8b6 ~]# kubectl get pods
28 | NAME READY STATUS RESTARTS AGE
29 | pod-runs-nginx 1/1 Running 0 26s
30 | ```
31 | 可以看到名为`pod-runs-nginx`的Pod已经处于`Running`状态。
32 |
33 | 我们还可以给`kubectl get` 命令增加`-o yaml`参数,来查看Pod更详细的信息,如下所示:
34 | ```
35 | [root@ecs-d8b6 ~]# kubectl get pods pod-runs-nginx -o yaml
36 | apiVersion: v1
37 | kind: Pod
38 | metadata:
39 | name: pod-runs-nginx
40 | namespace: default
41 | uid: 6a946bac-e288-4e19-b743-7ee0eb04aa73
42 | ...
43 | spec:
44 | containers:
45 | - image: nginx:latest
46 | imagePullPolicy: Always
47 | name: nginx
48 | restartPolicy: Always
49 | ...
50 | status:
51 | phase: Running
52 | podIP: 172.17.0.6
53 | ...
54 | ```
55 | Kubernetes在创建资源对象时会增加很多默认属性,受限于篇幅,上面只展了一部分信息。
56 |
57 | 我们知道,Kubernetes会为每个Pod分配一个IP,从上面的输出中可以看到该Pod的IP为`172.17.0.6`,我们可以使用该IP来访问Pod中的容器。
58 |
59 | 由于nginx默认监听在`80`端口,我们便可以使用Pod IP和端口来访问nginx,如下所示:
60 | ```
61 | [root@ecs-d8b6 ~]# curl 172.17.0.6:80
62 |
63 |
64 |
65 | Welcome to nginx!
66 | ...
67 |
68 |
69 | Welcome to nginx!
70 | If you see this page, the nginx web server is successfully installed and
71 | working. Further configuration is required.
72 | ...
73 | Thank you for using nginx.
74 |
75 |
76 | ```
77 | 通过输出内容,可以看到nginx已经可以正常工作了。
78 |
79 | ## 更新Pod
80 | 在上面的例子中,我们给容器指定的镜像版本为`nginx:latest`,我们可以修改镜像版本为`nginx:1.19.0`,于是我们把配置文件做如下修改:
81 | ```
82 | apiVersion: v1
83 | kind: Pod
84 | metadata:
85 | name: pod-runs-nginx
86 | spec:
87 | containers:
88 | - name: nginx
89 | image: nginx:1.19.0 # 修改镜像版本
90 | ```
91 |
92 | 然后使用命令`kubectl apply` 来提交修改后的配置,如下所示:
93 | ```
94 | [root@ecs-d8b6 ~]# kubectl apply -f pod_simple.yaml
95 | pod/pod-runs-nginx configured
96 | ```
97 |
98 | 然后再次查看Pod信息,可以看到Pod中的容器镜像版本已经更新了:
99 | ```
100 | [root@ecs-d8b6 ~]# kubectl get pods pod-runs-nginx -o yaml
101 | apiVersion: v1
102 | kind: Pod
103 | metadata:
104 | name: pod-runs-nginx
105 | namespace: default
106 | uid: 6a946bac-e288-4e19-b743-7ee0eb04aa73
107 | spec:
108 | containers:
109 | - image: nginx:1.19.0 # 镜像版本已更新
110 | imagePullPolicy: Always
111 | name: nginx
112 | restartPolicy: Always
113 | ...
114 | status:
115 | phase: Running
116 | podIP: 172.17.0.6
117 | ...
118 | ```
119 |
120 | ## 删除Pod
121 | 使用命令`kubectl delete`命令可以删除Pod,如下所示:
122 | ```
123 | [root@ecs-d8b6 ~]# kubectl delete pods pod-runs-nginx
124 | pod "pod-runs-nginx" deleted
125 | ```
126 | 删除Pod时会相应地停止并删除其所包含的容器。
127 |
128 | ## 小结
129 | 本节通过例子快速地演示了Pod的基本操作,Pod作为最基础的资源,与其他Kubernetes资源一样,都可以使用kubectl来操作。我们在上面的例子中刻意忽略了部分细节,比如如何控制镜像拉取行为、容器重启策略等,我们将这些内容放在后面的章节逐渐展开。
--------------------------------------------------------------------------------
/chapter03/2.1-replicationcontroller_overview.md:
--------------------------------------------------------------------------------
1 | 在介绍`ReplicationController`之前,我们先思考一下下图所示的场景,Kubernetes集群包含2个Node,每个Node上均运行一个同类型的Pod来做负载均衡,如果其中某个Node被管理员强制关机或者Node意外宕机时,会发生什么呢?
2 |
3 | 
4 |
5 | 由于Pod被调度到某个Node后就与Node绑定,当Node宕机后,Node中的所有Pod也都停止运行。
6 | 上图所示场景中,Node2被关闭后,相应的Pod-2也会停止,Pod-2并不会重新被调度到Node1。
7 |
8 | 实际应用场景中,维持稳定的Pod副本数是非常必要的,因此Kubernetes引入了`ReplicationController`。
9 |
10 | ## 工作机制
11 | `ReplicationController`用于定义指定Pod的副本数,与创建多个Pod相比,它可以保证Pod意外终止后,集群中仍会有指定个数的Pod副本在运行。运行于`kube-controller-manager`组件中的`ReplicationController`控制器(控制器和资源名相同)会监控集群中Pod的副本数:
12 |
13 | - 如果Pod数量已经超出预期,那么ReplicationController将会删除部分Pod,使Pod数量符合预期。
14 | - 如果Pod数量低于预期,那么ReplicationController将会创建新的Pod,使用Pod数量符合预期。
15 |
16 | `ReplicationController`控制器会时刻监控Pod的副本数量,一旦发现Pod数量不符合预期(Pod数量过多或过少),均会通过增加或删除Pod的手段来让Pod维持在预期数量。
17 |
18 | `ReplicationController`控制器更像是一个Pod监管者,它监管的是整个集群范围的Pod。在本节开头中所引用的场景中,如果使用`ReplicationController`创建两个Pod的副本,当其中一个Pod意外终止后,新的Pod会被创建出来,从而保证集群中仍有两个副本在运行,整体工作机制如下图所示:
19 |
20 | 
21 |
22 | 通过示意图可以看到,通过`ReplicationController`创建两个Pod情况下,当Node2被关闭后,运行于其上的Pod被重新调度到Node1中运行,集群中总的Pod数始终保持在2个。
23 |
24 | ## ReplicationController配置
25 | 一个简单的`ReplicationController`资源配置如下所示:
26 | ```
27 | apiVersion: v1
28 | kind: ReplicationController
29 | metadata:
30 | name: replication-controller-runs-pod
31 | spec:
32 | replicas: 3
33 | selector:
34 | app: nginx
35 | template:
36 | metadata:
37 | labels:
38 | app: nginx
39 | spec:
40 | containers:
41 | - name: nginx
42 | image: nginx:1.19.0
43 | ```
44 | 其中有三个关键的项:
45 | - `spec.replicas`指定了期望的Pod副本数;
46 | - `spec.selector`指定了Selector,`ReplicationController`正是通过该Selector来查找Pod对象;
47 | - `spec.template`指定了Pod的模版,当`ReplicationController`发现Pod数量低于预期时将使用该模版创建新的Pod。
48 |
49 | ### Pod 模板
50 | Pod模版用于Kubernetes内部动态地创建Pod,它广泛应用于各种控制器中,包括本节中介绍的`ReplicationController`,以及后续将要介绍的`Deployments`、`Jobs`和`DaemonSets`等等。
51 |
52 | 从数据结构上看,Pod模版(PodTemplateSpec)可以理解为简化版的Pod,它只保留了Pod的Metadata和Spec,如下所示:
53 | ```
54 | type PodTemplateSpec struct {
55 | // Metadata of the pods created from this template.
56 | // +optional
57 | metav1.ObjectMeta
58 |
59 | // Spec defines the behavior of a pod.
60 | // +optional
61 | Spec PodSpec
62 | }
63 | ```
64 | ## ReplicationController小结
65 | `ReplicationController`设计初衷是维持集群中指定类型Pod的副本数,但它只支持等值Selector,不支持基于集合的Selector。为了不违背API兼容性原则,Kubernetes不得已提供了另一种制器`ReplicaSet`来替换它。
66 |
67 | 所以,实际场景中几乎不会用到`ReplicationController`,虽然它是一个稳定的API。
68 |
69 | ## 延伸阅读
70 | - 《Pod templates》https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/#pod-templates
71 | - 《ReplicationController》https://kubernetes.io/docs/concepts/workloads/controllers/replicationcontroller/
--------------------------------------------------------------------------------
/chapter03/2.2-replicationcontroller_quick_start.md:
--------------------------------------------------------------------------------
1 | 本节,我们通过一些例子来演示ReplicationController的创建、查看、更新、删除等操作。
2 |
3 | ## 创建
4 | 首先我们准备一个名为`replication_controller_simple.yaml`的配置文件,其内容如下所示:
5 | ```
6 | [root@ecs-d8b6 manifests]# cat replication_controller_simple.yaml
7 | apiVersion: v1
8 | kind: ReplicationController
9 | metadata:
10 | name: replication-controller-runs-pod
11 | spec:
12 | replicas: 3
13 | selector:
14 | app: nginx
15 | template:
16 | metadata:
17 | labels:
18 | app: nginx
19 | spec:
20 | containers:
21 | - name: nginx
22 | image: nginx:1.19.0
23 | ```
24 | 该`ReplicationController`配置保证同时有3个Pod副本在运行。
25 |
26 | 使用`kubectl create`命令创建资源,如下所示:
27 | ```
28 | [root@ecs-d8b6 manifests]# kubectl create -f replication_controller_simple.yaml
29 | replicationcontroller/replication-controller-runs-pod created
30 | ```
31 |
32 | ## 查看
33 | 我们先查看刚刚创建的`ReplicationController`资源:
34 | ```
35 | [root@ecs-d8b6 manifests]# kubectl get replicationcontrollers
36 | NAME DESIRED CURRENT READY AGE
37 | replication-controller-runs-pod 3 3 3 3m4s
38 | ```
39 | 可以看到期望的Pod数量(DESIRED)为3,当前已创建的Pod数量(CURRENT)也为3,并且处于运行状态的Pod数量(READY)同样是3,正是我们所期望的状态。
40 |
41 | 接着,我们查看实际运行的Pod:
42 | ```
43 | [root@ecs-d8b6 manifests]# kubectl get pods
44 | NAME READY STATUS RESTARTS AGE
45 | replication-controller-runs-pod-gv4l6 1/1 Running 0 6m11s
46 | replication-controller-runs-pod-pcpmb 1/1 Running 0 6m11s
47 | replication-controller-runs-pod-qxrhz 1/1 Running 0 6m11s
48 | ```
49 | 可以看到Pod运行也是正常的。
50 |
51 | 最后,我们查看`ReplicationController`的详细信息:
52 | ```
53 | [root@ecs-d8b6 manifests]# kubectl describe replicationcontrollers replication-controller-runs-pod
54 | Name: replication-controller-runs-pod
55 | Namespace: default
56 | Selector: app=nginx
57 | Labels: app=nginx
58 | Annotations:
59 | Replicas: 3 current / 3 desired
60 | Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
61 | Pod Template:
62 | Labels: app=nginx
63 | Containers:
64 | nginx:
65 | Image: nginx:1.19.0
66 | Port:
67 | Host Port:
68 | Environment:
69 | Mounts:
70 | Volumes:
71 | Events:
72 | Type Reason Age From Message
73 | ---- ------ ---- ---- -------
74 | Normal SuccessfulCreate 10m replication-controller Created pod: replication-controller-runs-pod-gv4l6
75 | Normal SuccessfulCreate 10m replication-controller Created pod: replication-controller-runs-pod-qxrhz
76 | Normal SuccessfulCreate 10m replication-controller Created pod: replication-controller-runs-pod-pcpmb
77 | ```
78 | 通过最后部分的`Events`信息可知,上面显示的Pod确实是名为`replication-controller-runs-pod`的`ReplicationController`创建的。
79 |
80 | ## 更新
81 | #### 改变副本个数
82 | 我们把上面`replication_controller_simple.yaml`配置文件中的副本数由`3`改为`5`,然后使用`kubectl apply`提交更改:
83 | ```
84 | [root@ecs-d8b6 manifests]# kubectl apply -f replication_controller_simple.yaml
85 | replicationcontroller/replication-controller-runs-pod configured
86 | ```
87 |
88 | 然后再次查看Pod,可以发现Pod数量也会相应地调整为`5`。
89 | ```
90 | [root@ecs-d8b6 manifests]# kubectl get pods
91 | NAME READY STATUS RESTARTS AGE
92 | replication-controller-runs-pod-gv4l6 1/1 Running 0 25m
93 | replication-controller-runs-pod-pcpmb 1/1 Running 0 25m
94 | replication-controller-runs-pod-qtd4b 1/1 Running 0 6s
95 | replication-controller-runs-pod-qxrhz 1/1 Running 0 25m
96 | replication-controller-runs-pod-zz6fj 1/1 Running 0 6s
97 | ```
98 | 同样地,当副本数变小时,多出的Pod会被删除。
99 |
100 | #### 模拟Pod异常
101 | 假定当前名为`replication-controller-runs-pod`的`ReplicationController`维护的Pod副本数为3,我们通过删除Pod的方式模拟Pod异常,如下所示:
102 | ```
103 | [root@ecs-d8b6 manifests]# kubectl get pods
104 | NAME READY STATUS RESTARTS AGE
105 | replication-controller-runs-pod-gv4l6 1/1 Running 0 30m
106 | replication-controller-runs-pod-pcpmb 1/1 Running 0 30m
107 | replication-controller-runs-pod-qxrhz 1/1 Running 0 30m
108 | [root@ecs-d8b6 manifests]#
109 | [root@ecs-d8b6 manifests]#
110 | [root@ecs-d8b6 manifests]#
111 | [root@ecs-d8b6 manifests]# kubectl delete pods replication-controller-runs-pod-gv4l6
112 | pod "replication-controller-runs-pod-gv4l6" deleted
113 | [root@ecs-d8b6 manifests]# kubectl get pods
114 | NAME READY STATUS RESTARTS AGE
115 | replication-controller-runs-pod-4598b 1/1 Running 0 10s
116 | replication-controller-runs-pod-pcpmb 1/1 Running 0 31m
117 | replication-controller-runs-pod-qxrhz 1/1 Running 0 31m
118 | ```
119 | 可以看到,当我们删除了名为`replication-controller-runs-pod-gv4l6`的Pod后,新的Pod对象`replication-controller-runs-pod-4598b`马上被创建了出来,从而保证副本数维持不变。
120 |
121 | ## 删除
122 | 当删除`ReplicationController`对象时,由该对象创建的Pod默认也会被删除,如下所示:
123 | ```
124 | [root@ecs-d8b6 manifests]# kubectl delete -f replication_controller_simple.yaml
125 | replicationcontroller "replication-controller-runs-pod" deleted
126 | [root@ecs-d8b6 manifests]# kubectl get pods
127 | NAME READY STATUS RESTARTS AGE
128 | replication-controller-runs-pod-ktrxr 0/1 Terminating 0 18s
129 | replication-controller-runs-pod-rcdhk 0/1 Terminating 0 18s
130 | replication-controller-runs-pod-sc9mj 0/1 Terminating 0 18s
131 | [root@ecs-d8b6 manifests]# kubectl get pods
132 | No resources found in default namespace.
133 | ```
134 |
135 |
--------------------------------------------------------------------------------
/chapter03/3.1-replicaset_overview.md:
--------------------------------------------------------------------------------
1 | `ReplicaSet`与`ReplicationController`类似,它也用于管理一类Pod对象,保证Pod副本数量始终维持在期望值。
2 |
3 | ## ReplicaSet配置
4 | 一个简单的`ReplicaSet`配置如下所示:
5 | ```
6 | apiVersion: apps/v1
7 | kind: ReplicaSet
8 | metadata:
9 | name: replicaset-runs-pod
10 | spec:
11 | replicas: 3
12 | selector:
13 | matchLabels:
14 | app: nginx
15 | template:
16 | metadata:
17 | labels:
18 | app: nginx
19 | spec:
20 | containers:
21 | - name: nginx
22 | image: nginx:1.19.0
23 | ```
24 | 该`ReplicaSet`配置确保集群中始终运行3个拥有`app: nginx`标签的Pod副本,如果副本数量不足3个,则使用Pod模版`spec.template`创建Pod,如果副本数量多于3个,则删除多余的副本。
25 |
26 | `ReplicaSet`配置中最核心的三个要素如下:
27 | - `spec.selector`用于查找集群中存在的Pod;
28 | - `spec.replicas`用于指定期望的Pod副本数;
29 | - `spec.template`用于指定Pod模版,当副本数不足时将使用该模版创建新的Pod。
30 |
31 | ## ReplicaSet与ReplicationController的区别
32 | `ReplicationController`和`ReplicaSet`都属于Pod控制器,其设计初衷几乎完全相同,都是确保指定类型的Pod副本数维持在期望值,`ReplicationController`出现时间比`ReplicaSet`要早,那么为什么已经有了`ReplicationController`的情况下,还要再设计一个`ReplicaSet`呢?二者到底有什么区别呢?
33 |
34 | 二者的主要区别在于标签选择器,`ReplicaSet`拥有更先进的标签选择器,`ReplicationController`只支持旧式的标签选择器,而`ReplicaSet`不仅支持旧式选择器,还支持新式选择器。
35 |
36 | `ReplicationController`支持的选择器称为`Equality-based`选择器,即基于等值的选择器:
37 | ```
38 | Selector map[string]string
39 | ```
40 |
41 | `ReplicaSet`不仅支持`Equality-based`选择器,还支持`Set-based`选择器,即基于集合的选择器:
42 | ```
43 | type LabelSelector struct {
44 | MatchLabels map[string]string
45 | MatchExpressions []LabelSelectorRequirement
46 | }
47 | ```
48 |
49 | `ReplicationController`特性演进到V1时还没有支持`Set-based`选择器,而Kubernetes又希望推出一款支持`Set-based`选择器的Pod控制器,为了不破坏API兼容性,不得已才推出了`ReplicaSet`控制器来替代`ReplicationController`。
50 |
51 | ## 延伸阅读
52 | - 《ReplicaSet》https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
--------------------------------------------------------------------------------
/chapter03/3.2-replicaset_quick_start.md:
--------------------------------------------------------------------------------
1 | 本节,我们通过一些例子来演示`ReplicaSet`的创建、查看、更新、删除等操作。
2 |
3 | ## 创建
4 | 首先我们做准备一个名为`replicaset.yaml`的配置文件,其内容如下所示:
5 | ```yaml
6 | apiVersion: apps/v1
7 | kind: ReplicaSet
8 | metadata:
9 | name: replicaset-runs-pod
10 | spec:
11 | replicas: 3
12 | selector:
13 | matchLabels:
14 | app: nginx
15 | template:
16 | metadata:
17 | labels:
18 | app: nginx
19 | spec:
20 | containers:
21 | - name: nginx
22 | image: nginx:1.19.0
23 | ```
24 | 该`ReplicaSet`配置保证同时有3个Pod副本在运行。
25 |
26 | 使用`kubectl create`命令创建`ReplicaSet`控制器,如下所示:
27 | ```
28 | [root@ecs-d8b6 manifests]# kubectl create -f replicaset.yaml
29 | replicaset.apps/replicaset-runs-pod created
30 | ```
31 | 命令行输出显示,一个名为`replicaset-runs-pod`的`ReplicaSet`控制器已经创建完成。
32 |
33 | ## 查看
34 | 我们先查看刚创建的`ReplicaSet`控制器:
35 | ```
36 | [root@ecs-d8b6 manifests]# kubectl get replicaset
37 | NAME DESIRED CURRENT READY AGE
38 | replicaset-runs-pod 3 3 3 90s
39 | ```
40 | 可以看到控制器`replicaset-runs-pod`期望的Pod数量(DESIRED)为3,当前已创建的Pod数量(CURRENT)也为3,并且处于READY状态的Pod数量(READY)同样是3,正是我们期望的状态。
41 |
42 | 接着再查看实际运行的Pod:
43 | ```
44 | [root@ecs-d8b6 manifests]# kubectl get pods
45 | NAME READY STATUS RESTARTS AGE
46 | replicaset-runs-pod-dzhqp 1/1 Running 0 5m
47 | replicaset-runs-pod-hpbs8 1/1 Running 0 5m
48 | replicaset-runs-pod-xhx9j 1/1 Running 0 5m
49 | ```
50 | 可以看到有3个Pod在运行,Pod名字统一使用其控制器名字为前缀,再加一个随机字符串。
51 |
52 | 使用`kubectl describe`命令查看控制器,可以看到其创建Pod的记录:
53 | ```
54 | [root@ecs-d8b6 manifests]# kubectl describe replicaset replicaset-runs-pod
55 | Name: replicaset-runs-pod
56 | Namespace: default
57 | Selector: app=nginx
58 | Labels:
59 | Annotations:
60 | Replicas: 3 current / 3 desired
61 | Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
62 | Pod Template:
63 | Labels: app=nginx
64 | Containers:
65 | nginx:
66 | Image: nginx:1.19.0
67 | Port:
68 | Host Port:
69 | Environment:
70 | Mounts:
71 | Volumes:
72 | Events:
73 | Type Reason Age From Message
74 | ---- ------ ---- ---- -------
75 | Normal SuccessfulCreate 8m5s replicaset-controller Created pod: replicaset-runs-pod-xhx9j
76 | Normal SuccessfulCreate 8m5s replicaset-controller Created pod: replicaset-runs-pod-dzhqp
77 | Normal SuccessfulCreate 8m5s replicaset-controller Created pod: replicaset-runs-pod-hpbs8
78 | ```
79 | 通过最后的`Events`信息可知,控制器`replicaset-runs-pod`运行过程中分别创建了上面的3个Pod。
80 |
81 | ## 更新
82 | #### 改变副本个数
83 | 我们使用`kubectl edit`命令修改控制器`replicaset-runs-pod`,把`spec.replicas` 数量由3调整为5:
84 | ```
85 | [root@ecs-d8b6 manifests]# kubectl edit replicaset replicaset-runs-pod
86 | replicaset.apps/replicaset-runs-pod edited
87 | ```
88 |
89 | 然后再次查看Pod:
90 | ```
91 | [root@ecs-d8b6 manifests]# kubectl get pods
92 | NAME READY STATUS RESTARTS AGE
93 | replicaset-runs-pod-5mtdk 1/1 Running 0 37s
94 | replicaset-runs-pod-6rqqp 1/1 Running 0 37s
95 | replicaset-runs-pod-dzhqp 1/1 Running 0 21m
96 | replicaset-runs-pod-hpbs8 1/1 Running 0 21m
97 | replicaset-runs-pod-xhx9j 1/1 Running 0 21m
98 | ```
99 | 可以看到两个新的Pod被创建了出来,Pod副本数与配置中`spec.replicas`设定置一致。
100 |
101 | 类似地,当我们再次修改控制器`replicaset-runs-pod`配置,将`spec.replicas` 数量由5调整为3,多出的Pod数量会被自动删除。
102 |
103 | #### 模拟Pod异常
104 | `ReplicaSet`控制器将始终确保集群中运行的Pod副本数满足期望值,当Pod被删除时,控制器将马上创建新的Pod填补空缺:
105 | ```
106 | [root@ecs-d8b6 manifests]# kubectl delete pods replicaset-runs-pod-dzhqp
107 | pod "replicaset-runs-pod-dzhqp" deleted
108 | [root@ecs-d8b6 manifests]# kubectl get pods
109 | NAME READY STATUS RESTARTS AGE
110 | replicaset-runs-pod-hpbs8 1/1 Running 0 31m
111 | replicaset-runs-pod-tbccg 1/1 Running 0 9s
112 | replicaset-runs-pod-xhx9j 1/1 Running 0 31m
113 | ```
114 | 上面的例子中,当手动删除某个`ReplicaSet`控制器管理的Pod时,新的Pod马上被创建了出来,通过Pod运行时间或者控制器事件信息都可以看出。
115 |
116 | ## 删除
117 | 当删除`ReplicaSet`控制器时,其管理的Pod默认也会相应的删除,如下所示:
118 | ```
119 | [root@ecs-d8b6 manifests]# kubectl delete -f replicaset.yaml
120 | replicaset.apps "replicaset-runs-pod" deleted
121 | [root@ecs-d8b6 manifests]# kubectl get pods
122 | No resources found in default namespace.
123 | ```
124 | ## 小结
125 | `ReplicaSet`控制器的能力侧重于对副本数量的把控,它能保证集群中时刻运行指定数量的Pod副本,当配置中Pod副本数量变化时,可以动态地调整Pod数量。
126 |
--------------------------------------------------------------------------------
/chapter03/4.1-deployment_overview.md:
--------------------------------------------------------------------------------
1 | `Deployment`是继`ReplicationController`和`ReplicaSet`之后推出的更高级的控制器,它通过`Deployment`对象来声明`Pod`的期望状态,这些状态包括`Pod`的副本数和`Pod`的模版等,运行于`kube-controller-manager`组件中的`Deployment Controller`(Deployment控制器)时刻监控`Deployment`对象的变化,并根据`Deployment`对象中的配置来调整`Pod`,最终保证`Pod`以期望的形态在运行。
2 |
3 | ## 配置格式
4 | 我们先看一个简单的`Deployment`配置:
5 | ```
6 | apiVersion: apps/v1
7 | kind: Deployment
8 | metadata:
9 | name: nginx-deployment
10 | labels:
11 | app: nginx
12 | spec:
13 | replicas: 3
14 | selector:
15 | matchLabels:
16 | app: nginx
17 | template:
18 | metadata:
19 | labels:
20 | app: nginx
21 | spec:
22 | containers:
23 | - name: nginx
24 | image: nginx:1.19.0
25 | ```
26 | - `metadata.name`:指定Deployment的名称;
27 | - `spec.replicas`:指定期望的副本数;
28 | - `spec.selector`:指定查找Pod的标签;
29 | - `spec.template`:指定Pod的模板,当`Deployment`控制器需要创建Pod时会根据此模版创建Pod。
30 |
31 | 上面这份`Deployment`配置和之前介绍的`ReplicaSet`非常类似,主要也是声明Pod的副本数以及Pod的模版,将该配置提交给`kube-apiserver`后,运行于`kube-controller-manager`组件中的`Deployment Controller`也会创建3个Pod副本,这个行为跟`ReplicaSet`还是一样,但`Deployment`更强大的功能体现在配置发生变化时的行为,比如修改配置中Pod模板中的镜像版本,`Deployment Controller`会自动地创建新的Pod来运行新镜像并逐步删除旧的Pod,从而达到自动升级的目的。
32 |
33 | 本节我们先从简单的配置看起,对`Deployment`更高级的功能会在后面的章节中陆续展开。
34 |
--------------------------------------------------------------------------------
/chapter03/4.2-deployment_quickstart.md:
--------------------------------------------------------------------------------
1 | 本节,我们通过一些简单的例子来演示`Deployment`的创建、查看、更新、删除等操作,以期快速地掌握`Deployment`的用法。
2 |
3 | ## 创建
4 | 首先我们先将以下配置保存到名为`deployment.yaml`的文件中。
5 | ```
6 | apiVersion: apps/v1
7 | kind: Deployment
8 | metadata:
9 | name: nginx-deployment
10 | labels:
11 | app: nginx
12 | spec:
13 | replicas: 3
14 | selector:
15 | matchLabels:
16 | app: nginx
17 | template:
18 | metadata:
19 | labels:
20 | app: nginx
21 | spec:
22 | containers:
23 | - name: nginx
24 | image: nginx:1.19.0
25 | ```
26 | 该份配置将创建一个名为`nginx-deployment`的`Deployment`资源对象,根据`spec.replicas`中的信息可知,它期望有3个Pod副本。
27 |
28 | 接下来使用`kubectl create`命令将该配置提交给`kube-apiserver`,如下所示:
29 | ```
30 | [root@ecs-d8b6 manifests]# kubectl create -f deployment.yaml
31 | deployment.apps/nginx-deployment created
32 | ```
33 | 根据命令行输出内容可知,名为`nginx-deployment`的`Deployment`已创建完成。
34 |
35 | ## 查看
36 | 当创建一个`Deployment`资源对象时,`Deployment`控制器不会直接创建Pod,而是通过创建`ReplicaSet`来间接创建Pod。
37 |
38 | #### 查看Deployment
39 | 查看新创建的`Deployment`资源:
40 | ```
41 | [root@ecs-d8b6 manifests]# kubectl get deployment
42 | NAME READY UP-TO-DATE AVAILABLE AGE
43 | nginx-deployment 3/3 3 3 90s
44 | ```
45 | 命令行输出中各字段含义如下:
46 | - NAME:`Deployment`资源名称,同配置中的`metadata.name`;
47 | - READY:<已处于Reday状态的Pod数量>/<期望的Pod数量>;
48 | - UP-TO-DATE:处于最新状态的Pod数量;
49 | - AVAILABLE:可用的Pod数量;
50 | - AGE:应用运行时间,也是资源自创建至今经过的时间。
51 |
52 | 此处`READY`和`AVAILABLE`有一些细微的区别,当`Pod`中指定的容器全部运行起来后,就可以认定该`Pod`为`READY`,所以`READY`含义更倾向于`Running`,而`AVAILABLE`要求会更严格一些,只有当`Pod`处于`READY`状态且持续一段时间后才可被认定为`AVAILABLE`,其含义更倾向于`可用`的数量。
53 |
54 | `UP-TO-DATE`表示运行最新配置的Pod数量,当修改`Deployment`配置中Pod模版时,新版的Pod会被创建且短期内会与旧版的Pod并存,`UP-TO-DATE`正是表示已创建的新版Pod数量。
55 |
56 | #### 查看ReplicaSet
57 | 接着查看ReplicaSet:
58 | ```
59 | [root@ecs-d8b6 manifests]# kubectl get replicaset
60 | NAME DESIRED CURRENT READY AGE
61 | nginx-deployment-6fd78f555b 3 3 3 104s
62 | ```
63 | 由`Deployment`创建的`ReplicaSet`名称由`Deployment`的名称和一个随机字符串组成。
64 |
65 | #### 查看Pod
66 | 接着查看最终生成的Pod:
67 | ```
68 | [root@ecs-d8b6 manifests]# kubectl get pods
69 | NAME READY STATUS RESTARTS AGE
70 | nginx-deployment-6fd78f555b-clf9w 1/1 Running 0 110s
71 | nginx-deployment-6fd78f555b-vlvp6 1/1 Running 0 110s
72 | nginx-deployment-6fd78f555b-wjslg 1/1 Running 0 110s
73 | ```
74 | 这些Pod都是由`ReplicaSet`自动创建出来的,其名称由`ReplicaSet`名称和一个随机字符串组成。
75 |
76 | ## 更新
77 | 由于`Deployment`控制器通过`ReplicaSet`来创建并管理Pod,当修改`Deployment`配置中的`spec.replicas`时,`Deployment`控制器相应地也会修改`Replicaset`中`spec.replicas`值,最终由`Replicaset`控制器来调整Pod的副本数,该部分功能我们已经在介绍`ReplicaSet`时演示过,此处不再演示。
78 |
79 | 我们在此演示一种`Deployment`相较于`ReplicaSet`更高阶的功能,即应用版本的升降级。当前应用使用的nginx镜像版本为`1.19.0`,我们希望把该版本降为`1.18.0`。
80 |
81 | 通过以下命令,将`spec.template.spec.containers.image`值由`nginx:1.19.0`改为`nginx:1.18.0`:
82 | ```
83 | [root@ecs-d8b6 manifests]# kubectl edit deployments nginx-deployment
84 | ```
85 |
86 | 然后再次查看`Deployment`、`ReplicaSet`和`Pod`:
87 | ```
88 | [root@ecs-d8b6 manifests]# kubectl get deployment
89 | NAME READY UP-TO-DATE AVAILABLE AGE
90 | nginx-deployment 3/3 3 3 33m
91 | [root@ecs-d8b6 manifests]# kubectl get replicaset
92 | NAME DESIRED CURRENT READY AGE
93 | nginx-deployment-6fd78f555b 0 0 0 33m
94 | nginx-deployment-7df9bc6ff5 3 3 3 32s
95 | [root@ecs-d8b6 manifests]# kubectl get pods
96 | NAME READY STATUS RESTARTS AGE
97 | nginx-deployment-7df9bc6ff5-4rz8l 1/1 Running 0 40s
98 | nginx-deployment-7df9bc6ff5-tmxwl 1/1 Running 0 44s
99 | nginx-deployment-7df9bc6ff5-vmdzp 1/1 Running 0 42s
100 | ```
101 | 可以看到,之前的`ReplicaSet`(nginx-deployment-6fd78f555b)副本数已被修改为0,新的`ReplicaSet`(nginx-deployment-7df9bc6ff5)被创建了出来,进而创建了新的Pod。新的Pod中运行的容器镜像版本就是我们修改后的容器镜像版本,读者可以自行验证。
102 |
103 | ## 删除
104 | 当删除`Deployment`资源时,由`Deployment`控制器创建的`ReplicaSet`也会被删除,因此由`ReplicaSet`创建的Pod也会被删除。
105 |
106 | ```
107 | [root@ecs-d8b6 manifests]# kubectl delete deployment nginx-deployment
108 | deployment.apps "nginx-deployment" deleted
109 | [root@ecs-d8b6 manifests]# kubectl get replicaset
110 | No resources found in default namespace.
111 | [root@ecs-d8b6 manifests]# kubectl get pods
112 | No resources found in default namespace.
113 | ```
114 |
--------------------------------------------------------------------------------
/chapter03/5.1-daemonset_overview.md:
--------------------------------------------------------------------------------
1 | `DaemonSet`是一种面向特定应用场景的`Pod`控制器,尽管它也可以管理`Pod`的多个副本,但它主要用于保证一个`Node`上只运行一个`Pod`的场景,如下图所示:
2 |
3 | 
4 |
5 | `DaemonSet`可以确保一个`Node`上最多只运行一个`Pod`副本,进一步说,`DaemonSet`可以选择特定类型的`Node`来部署`Pod`。此处,当选定类型的`Node`加入集群时,该`Node`会自动运行一个新的`Pod`副本,并且当该`Node`被删除时,相应的`Pod`也会被删除,而不会在其他`Node`上重建。
6 |
7 | ## 应用场景
8 | `DaemonSet`可以确保每个工作节点上最多运行一个应用副本,这个应用副本类似于Linux操作系统中的`daemon`进程,这也正是`DaemonSet`名称的由来。
9 |
10 | `DaemonSet`通常用于管理那些执行系统级的应用,比如:
11 | - 每个工作节点运行一个存储服务,供该工作节点上其他应用使用;
12 | - 每个工作节点运行一个日志收集服务,用于收集该节点上的运行日志;
13 | - 每个工作节点运行一个监控指标收集服务,用于提供该节点的监控信息;
14 |
15 | ## 配置格式
16 | 我们先看一个简单的`DaemonSet`配置:
17 | ```
18 | apiVersion: apps/v1
19 | kind: DaemonSet
20 | metadata:
21 | name: nginx-daemonset
22 | labels:
23 | app: nginx
24 | spec:
25 | selector:
26 | matchLabels:
27 | app: nginx
28 | template:
29 | metadata:
30 | labels:
31 | app: nginx
32 | spec:
33 | containers:
34 | - name: nginx
35 | image: nginx:1.19.0
36 | ```
37 | 初步看,这份配置跟`Deployment`基本类似,唯一一个显著的差异是`DaemonSet`不需要指定副本数,因为它的副本数取决于工作节点数。
38 |
39 | `DaemonSet`配置中`spec.selector`和`spec.template`作用我们已在介绍`Deployment`时介绍过,在此不再赘述。
--------------------------------------------------------------------------------
/chapter03/5.2-daemonset_quickstart.md:
--------------------------------------------------------------------------------
1 | 本节,我们通过几个简单的例子来快速体验`DaemonSet`控制器。
2 |
3 | ## 环境准备
4 | 我们使用`Kind`工具来创建一个包含三个节点的集群,使用的配置如下所示:
5 | ```
6 | [root@ecs-d8b6 book]# cat config_kind.yaml
7 | kind: Cluster
8 | apiVersion: kind.x-k8s.io/v1alpha4
9 | nodes:
10 | - role: control-plane
11 | - role: worker
12 | - role: worker
13 | - role: worker
14 | ```
15 | 然后,创建一 个v1.18.0版本的Kubernetes集群:
16 | ```
17 | [root@ecs-d8b6 book]# kind create cluster --config config_kind.yaml --image=kindest/node:v1.18.0
18 | Creating cluster "kind" ...
19 | ✓ Ensuring node image (kindest/node:v1.18.0) 🖼
20 | ✓ Preparing nodes 📦 📦 📦 📦
21 | ✓ Writing configuration 📜
22 | ✓ Starting control-plane 🕹️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️️
23 | ✓ Installing CNI 🔌
24 | ✓ Installing StorageClass 💾
25 | ✓ Joining worker nodes 🚜
26 | Set kubectl context to "kind-kind"
27 | You can now use your cluster with:
28 |
29 | kubectl cluster-info --context kind-kind
30 | ```
31 |
32 | 集群创建完成后,检查各节点是否已正常工作:
33 | ```
34 | [root@ecs-d8b6 book]# kubectl get nodes
35 | NAME STATUS ROLES AGE VERSION
36 | kind-control-plane Ready master 3m6s v1.18.0
37 | kind-worker Ready 2m30s v1.18.0
38 | kind-worker2 Ready 2m31s v1.18.0
39 | kind-worker3 Ready 2m30s v1.18.0
40 | ```
41 | 可以看到所有节点都已处于`Ready`状态,接下来我们就创建一个`DaemonSet`对象,它可以保证在每个工作节点上创建一个`Pod`副本。
42 |
43 | ## 创建
44 | 首先我们先将以下配置保存到名为`daemonset.yaml`的文件中。
45 | ```
46 | [root@ecs-d8b6 manifests]# cat daemonset.yaml
47 | apiVersion: apps/v1
48 | kind: DaemonSet
49 | metadata:
50 | name: nginx-daemonset
51 | labels:
52 | app: nginx
53 | spec:
54 | selector:
55 | matchLabels:
56 | app: nginx
57 | template:
58 | metadata:
59 | labels:
60 | app: nginx
61 | spec:
62 | containers:
63 | - name: nginx
64 | image: nginx:1.19.0
65 | ```
66 | 该份配置将创建一个`DaemonSet`对象,然后`DaemonSet`控制器会根据该对象信息分别在每个节点上创建一个Pod副本。
67 |
68 | 接下来使用`kubectl create`命令将该配置提次给`kube-apiserver`,如下所示:
69 | ```
70 | [root@ecs-d8b6 manifests]# kubectl create -f daemonset.yaml
71 | daemonset.apps/nginx-daemonset created
72 | ```
73 |
74 | ## 查看
75 | 首先查看刚刚创建的`DaemonSet`对象:
76 | ```
77 | [root@ecs-d8b6 manifests]# kubectl get daemonset
78 | NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
79 | nginx-daemonset 3 3 3 3 3 5m6s
80 | ```
81 | 命令行输出中各字段含义如下:
82 | - NAME: `DaemonSet`对象名称,同配置中的`metadata.name`;
83 | - DESIRED:需求副本个数,由于没有刻意筛选节点,所以副本个数等同于节点个数;
84 | - CURRENT:当前已创建的副本个数;
85 | - READY:处于`Ready`状态的副本个数;
86 | - UP-TO-DATE:已按最新`Pod`模版创建的Pod个数;
87 | - AVAILABLE:可用的副本个数;
88 | - NODE SELECTOR:节点选择器,本例中我们没有选择,值为空;
89 | - AGE:创建至今经历的时间。
90 |
91 | 上面的字段中,除了`NODE SELECTOR`以外,我们已在前面的章节中介绍过。其实`Node Selector`并不是`DaemonSet`对象特有的配置,它是`Pod`模版中用于为Pod匹配节点的配置,`DaemonSet`控制器使用该`Node Selector`来筛选需要创建副本的节点,如果没有指定,则默认选择全部节点。
92 |
93 | 接着,查看`DaemonSet`控制器所创建的Pod副本信息:
94 | ```
95 | [root@ecs-d8b6 manifests]# kubectl get pods -o wide
96 | NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
97 | nginx-daemonset-78dbc 1/1 Running 0 5m13s 10.244.3.2 kind-worker3
98 | nginx-daemonset-gmpdg 1/1 Running 0 5m13s 10.244.1.2 kind-worker2
99 | nginx-daemonset-l6wn4 1/1 Running 0 5m13s 10.244.2.2 kind-worker
100 | ```
101 | 可以看到,每个节点上均创建了一个副本,符合预期。
102 |
103 | ## 更新
104 | 接下来我们试图调整Pod部署策略,我们只希望Pod运行在名为`kind-worker`的节点上,这样我们只需要配置`DaemonSet`对象的`spec.template.spec.nodeSelector`来选择节点即可。
105 |
106 | 在`kind-worker`的节点中存在一个标识节点的label:
107 | ```
108 | kubernetes.io/hostname: kind-worker
109 | ```
110 | 所以`DaemonSet`对象的`spec.template.spec.nodeSelector`配置如下:
111 | ```
112 | apiVersion: apps/v1
113 | kind: DaemonSet
114 | metadata:
115 | ...
116 | spec:
117 | ...
118 | template:
119 | ...
120 | spec:
121 | ...
122 | nodeSelector:
123 | kubernetes.io/hostname: kind-worker
124 | ```
125 | 使用`kubectl edit`命令修改配置,然后再次观察`DaemonSet`对象和Pod副本:
126 | ```
127 | [root@ecs-d8b6 manifests]# kubectl get daemonsets
128 | NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
129 | nginx-daemonset 1 1 1 1 1 kubernetes.io/hostname=kind-worker 37m
130 | [root@ecs-d8b6 manifests]# kubectl get pods -o wide
131 | NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
132 | nginx-daemonset-66gk2 1/1 Running 0 10s 10.244.2.3 kind-worker
133 | ```
134 | 可以发现`DaemonSet`状态中,`NODE SELECTOR`正确地体现了我们的修改,而且需求的Pod副本数也变成了1个,符合预期。
135 | 原来运行的3个Pod副本减少到1个,而且只在我们选定的节点上运行。
136 |
137 | ## 删除
138 | 像其他Pod控制器一样,当删除`DaemonSet`对象时,其所管理的Pod默认也会被删除,操作如下所示:
139 | ```
140 | [root@ecs-d8b6 ~]# kubectl delete daemonsets nginx-daemonset
141 | daemonset.apps "nginx-daemonset" deleted
142 | [root@ecs-d8b6 ~]# kubectl get pods
143 | No resources found in default namespace.
144 | ```
--------------------------------------------------------------------------------
/chapter03/images/daemonset_overview.drawio:
--------------------------------------------------------------------------------
1 | 3Vhdb5swFP01PHbChhDyuHysq9RVk/KwdW8WOODVcJHjFNivnykmfCZLpXSgPMX3+NrY59wTbAxrFWX3giThN/ApN7DpZ4a1NjBGpuuonwLJS2TuzkogEMzXSTWwZX9oNVKjB+bTfStRAnDJkjboQRxTT7YwIgSk7bQd8PZTExLQHrD1CO+jP5gvwxJ18bzGv1IWhNWTkbMoeyJSJeud7EPiQ9qArI1hrQSALFtRtqK8IK/ipRz35UTvcWGCxvKSAS/3j+l6NXtwHhCLPfMpE3e/7qxyllfCD3rDerEyrxhIQybpNiFeEadKZcNahjLiKkKqSfZJyfuOZVQ9aqlnpELS7ORS0ZEAVTkUIipFrlKqAY7mTBeNq8O0oYCGwgb5FUa05sFx4poW1dDMvIMlu8fSd/B7RKmJVFWqYDkGZW6bMmT2ObMHOLM/ijOnx9mT+nNAPdbUlmWbnr0U8EJXwEEoJIa4oHTHOO9AhLMgVqGnKKIKXxYEMmXdz7ojYr7PT+kh4BD7Bftr8zoKYLOjgHOZAvijFJhP0tvYnZa33el72+pW1tjeXgx6u19eN+NtC0/M29XxaGLmts1pmRuh6bvb7tbW2O5GeNDe1u3a27anZm+3X6O+upfoEIQMIYCY8E2Ndmipcx4BEq3Pbyplri9Z5CChrR7NmPxZDP+EZzp81rMV7XXWDPIW/cXqzpOvNgMH4dFzu9ZnRklEQOWZRHtYTUE5key1vZDra7MYV5vLpVECiLwxqAifq/mKoB72Fo0pqTumpMcr+kiSzm9TU3TiX/c/+bR/A10TGkG8VYvuyl2Lif59CrjCGwd3vnAMnZQQHnjlOO9/5aiw/sr01tf4Vmdt/gI=
--------------------------------------------------------------------------------
/chapter03/images/daemonset_overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter03/images/daemonset_overview.png
--------------------------------------------------------------------------------
/chapter03/images/pod_overview.drawio:
--------------------------------------------------------------------------------
1 | 5Vldk6MoFP01PM5W/Aw+amJ2H3p3u6qrdmr3jShRZoikkEyS+fULihrBdHpm2pqu6X5pvF4ucg73cCHAW+3Pv3N0KP9kOabAXeRn4K2B6zoLGMp/ynJpLUsYtIaCk1w7DYYn8hV3PbX1SHJcjxwFY1SQw9iYsarCmRjZEOfsNHbbMToe9YAKbBmeMkRt60eSi7K1Qnc52P/ApCi7kZ0wat/sUeesZ1KXKGenK5OXAm/FGRNta39eYarA63Bp+21uvO0/jONKvKRDvfSr8vxvQNzgn08Q/rW9LP/7oKN8QfSoJ6w/Vlw6BE4lEfjpgDL1fJIsAy8pxZ7KJ0c2UX1ocd+RM5ZDJToi5gKfb36q0wMgVw5meyz4RbroDu5CY6YXjdNheBoo8Duf8gr+3og07UUfe0BGNjQ43wCUawOVBiBegwiqBnRBDC3oJOUH1cwulFQ55hKdO2hu2VE65g/b3oCyzwVX1r+PQkbB2v4KKHveGGXfs1F2/AmUg7lA9idADkEUArgEqQ+SBCQhSJcgTkHsqEa0AEpgQioUVFy2CtUC6QYkKwBh01328nWcZDEVsHWOLPp4S4ZG/A5zr7HsDUL6NLgmxJ0gBM5FSGAR8hFvpaFBUWK2btZ+3LARgChqkuAZNiCINwp11WujeHjDbPj+W2Mjsth4ZLmFk5yxGINRC84+4xWjTCrQumJKRJIdodQwIUqKSsmVRKgRK4UfkXthrF/sSZ6rYSbRH/hZqPCsEno3d/zXISRwDb2CNiFwclOYiY+uRjE3hRQk0VU+vBd+PMdImHBq17b5cWfjZ6K6ecf8mPnz8/nxbH5ulVA7is+xquclFrjKdXOdUVTXJJsSf5xblf1dpK6QCJ4pfDimSJAv4/BT8OgRHhmRAw/7PDSIiAyEa3bkGda9rkt6I5AXGoroGIEE4gUWVqCGrX7aP0DgRMH2Hgg0SwML95cSGCzurIS5CbTlT6H+pB8ZFyUrWIVoOlgNoRp8Hhg7aAI/YSEuWr7QUbAxvdPadp9u+Z0Nrs9NSFesLW73Klt7/bx4YfyY8NmVXFMJb0Ac6CI5tjNJAfSAtpgaB/AX7zoc1+Qr2jbxFHUHtayauQUJCNbfsuvo6xYdDPR7xDVnz6y4m3vUh8VvUh7DUVJ0lxTfm7SdC9vtajxPGtnnpOZ06qv6QhIrjz7J8qYyHvc0zgS7pq6h+ZHVRBCmKNwyIdheOlD1IunvBLoCBLjervmboF8w42zE2ouEVX9jNlO9sTTFzbHrDW9CpMO56o1eRX4Vteuy7q7a+T9T7bqiwFS78b3ML6l2dzJEqR30vHGWvHWxc6dOvdadnDpeOepy9L2pnlnbzyd68nH49aBld/gNxkv/Bw==
--------------------------------------------------------------------------------
/chapter03/images/pod_overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter03/images/pod_overview.png
--------------------------------------------------------------------------------
/chapter03/images/replication_controller_backgroud.drawio:
--------------------------------------------------------------------------------
1 | 3VnLUtswFP0aLenYlu3Yy7xoZwptZlgASxErtopspbJMkn59JSzHDzkQSiBp2KB79T7nvuQAOE7XXzlaJtcswhQ4VrQGcAIcx7YCX/5Tmk2pGQReqYg5ifSgWnFD/uBqptYWJMJ5a6BgjAqybCvnLMvwXLR0iHO2ag9bMNredYlibChu5oia2lsSiaTUBs6g1n/DJE6qnW0/LHtSVA3WN8kTFLFVQwWnAI45Y6Jspesxpgq8Cpdy3uWO3u3BOM7EXhNgsYB0dvvT+R0NBbv8nnqzC6dc5QnRQl/4sXjAF2hJcsyfMNdHF5sKj1VCBL5ZormSV5JzAEeJSKmUbNlE+bJkYUHWWG480utjLvB658HtLRzSjjBLseAbOURPcAKNoDYh29fyqkGIViUNLiod0iYQb1euUZINDdQbQIMGaKcAk+20YYLOkWFyTxIm1z0xmDwDpmuUix7Xk3cWbXxywdkjHjPKuNRkLJMjRwtCaUeFKIkzKc4lRnJhOFIIEhnkhrojJVGkthn1EcJZkUUK/ol1GAqg1abAcU0K3B4KnI+iwDco+CETmX2+DNgdJ3CtIzMw6GXADBhnw4DrnxgDVcH1UrjGkayNtMi4SFjMMkSntbaDUz3mirGlJuwXFmKjCz1UCNams9xTbfQysPJcrOBz/MKNwn4COKZIkKf2+n1w6qkzRuTOW+I8e0fwqpYQiMdY6FkdUrbHeAdPZrAyecqioap9a+tvICxx5Js7zdCzcN/smaybXZNNJa2JuKvGyfZ9Q19PUUI144BM6oKrRPa1atZkvOFSXo9LVbp3GoZj7ygsqiXKaxqGYS7UqXe3B/wsCzOD8RstTNuKtbetKGGGOZFHV7G5tr+7pmnuY6cHtDr3f7A6F3aszv5Hq4MD+7hWFxpWN2PRhVmE1SnG3pGsjXRy0FoJwv0ydTcvHCxTO2amVkiZTvrZSHVrmuMjFR6jhOkPXNYX7/NClxPsGbvcfkL3DkrvoycwE810AIZTMLRVI7RA4IOpB0IbhIFBZZ6gpWoWKR3OBWvW9FfoAdMZy4kgTNX2D0wIlsoBVHWM0Pwxfqa5eiMABy6e/3reBYJ1PIUVgpJMPjmqL44HehL41uvuA3vcx/8o94HmpwnFRuCBEQRTH4RjMBoatJzNE83rBv5jf6aAZmGWxSRbny8FRu4dHJsCM2SdOQVGUv84CqRY/xxS1pX1j0pw+hc=
--------------------------------------------------------------------------------
/chapter03/images/replication_controller_backgroud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter03/images/replication_controller_backgroud.png
--------------------------------------------------------------------------------
/chapter03/images/replication_controller_overview.drawio:
--------------------------------------------------------------------------------
1 | 5Vpdd6I4GP41XDaHJBDgUltn9pyd2fVsL6ZzSSVqtpG4GEfdX79BghBC1bZa0e2FhZd88TzvZ4iD72frr1k8n34XCeUOcpO1gx8chKAbEvUvl2wKSRD6hWCSsUQ3qgSP7F9a9tTSJUvowmgoheCSzU3hSKQpHUlDFmeZWJnNxoKbs87jCbUEj6OY29IfLJHTQhqioJL/RtlkWs4MSVQ8mcVlY/0mi2mciFVNhAcOvs+EkMXVbH1PeQ5eiUvR78srT3cLy2gqj+qAl2PMhz/+RP8kPSm+/D7zh3eKoGKcXzFf6lfWy5WbEoNMLNOE5sO4Du6vpkzSx3k8yp+uFOtKNpUzru6gutTD0UzS9asrhbv3V4pDxYzKbKOa6A7IhQDqdWm18UsUVxUJsJRNawQQLYs175Pd6BU06kKj8yakDgNFE6U7+lZkciomIo35oJL2TSirNt+EmGsA/6ZSbrQhxEsp2uDNJ9oPrlqXWGYjuueNonYSMspjyX6Z47fBqbsOBVMz78jzS6A0c8hrMCLjbEKl7tUgZbeMj/DkHyZK2eI8vxxzuu7lXkIhS9NEXz6MeLxYsNH7kLcRrems36KypeyDwO9UXwOPUQP4QiEs4A8O5Lmfy2BkW9rL8pnexXO2oJnyLBadB1xSvJgXoWHM1rntncRHYQQiD8KAeIHrhyHC7ZjVuUcAuwGJkP6FLarQ3uTkzizCFsTf44VsgVYhJE00FzITL/RecJEpSSrS3K+NGecNUczZJM2NSSGqBsb9HG+mImtPP5ixJNk6xTb6TEd5CsK8ABRE+WhLXGAS1oTeog+FYNc3J53Y9EEM6kO45Ryn58+z+PtDpV7w1umrrM037c0PXeC6FYFht+mzk66hSO5s+ioY4edkXjAEYc2n+chEGROggAk9Av38911Ggggoeu9+z4Vy1Iryvvz2k1AO7PxWIUuiChXUaWSha8ePdMLS9Q37nwOWQXLfUTkPr9P+B7p2/Lh1/lpsjiAQetcS8iEkFmcWXbUaRtNQY04hlW2eNKTbm5/1Jw/r+qOHjYF9l8uehiXCd5c9UFl4ldE3EnoSgSiqmEbEnOXMNRGyuL+6kgiSTpdEdkTrAqa7Pa5yN4V4gNSDvB1oOoSpHWW6gCn2fFCrA4lHrhhie7Prf17IK5MxkX9XKv1pYd0O6jdeyEPsgXqM8D2DPgy3JWYFfqfZC1rZs73czbCHm5vMV0XXhVJoumbyqWynrn/W5FWX/OatSfcRX3l0XlPkp4cyzEsl8TDEoKYjyjEcFZLfnOLvz1C3Nb5Xn8ic5txfrmxv8kbt1HrmHq1n+c2QZkwtPXcule4+1dX6lGXiERrrXYPGYqx0pe7YfFOV/BDsSf+OVtgoAPDVTe9L62v7/urld7EPpBjbrSD/WvZakf01tBu72M1U4MpgjWzfev7TG+2+1QX+53lXFB7pXr129o/2mx+jp+VY0iBwegOnB/OLyHVC4gx8J4JO+TWt5XzHcsZ7IynqefO3+JnyoVgwyUSePz8LKcVMNeD5g348eplsaS7zcAfh8favJfeWomFWYik5S1VaXx5GO1Ha7fn7o81h04NGHFFxxbY9HAFkVM5n4ha3HNBRTIa+08fOgDjRvdPvWZTeTAnlqZKpRoQHm9EpAC21UEcrKmznrDf+IelQcqEytus5R4JtN3vj9FlZy1XxBVtqxL/onCs883Cm4o5ihXOFc9DPCrlagnKmLgIAOMHDGYjldCwvTiuKTFphYH9s2p0pNxLOt58XVrfVqe2imKrOvuPBfw==
--------------------------------------------------------------------------------
/chapter03/images/replication_controller_overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter03/images/replication_controller_overview.png
--------------------------------------------------------------------------------
/chapter03/manifests/daemonset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: DaemonSet
3 | metadata:
4 | name: nginx-daemonset
5 | labels:
6 | app: nginx
7 | spec:
8 | selector:
9 | matchLabels:
10 | app: nginx
11 | template:
12 | metadata:
13 | labels:
14 | app: nginx
15 | spec:
16 | containers:
17 | - name: nginx
18 | image: nginx:1.19.0
19 |
--------------------------------------------------------------------------------
/chapter03/manifests/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: nginx-deployment
5 | labels:
6 | app: nginx
7 | spec:
8 | replicas: 3
9 | selector:
10 | matchLabels:
11 | app: nginx
12 | template:
13 | metadata:
14 | labels:
15 | app: nginx
16 | spec:
17 | containers:
18 | - name: nginx
19 | image: nginx:1.19.0
20 |
--------------------------------------------------------------------------------
/chapter03/manifests/pod_simple.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: pod-runs-nginx
5 | spec:
6 | containers:
7 | - name: nginx
8 | image: nginx:1.19.0
--------------------------------------------------------------------------------
/chapter03/manifests/replicaset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: ReplicaSet
3 | metadata:
4 | name: replicaset-runs-pod
5 | spec:
6 | replicas: 3
7 | selector:
8 | matchLabels:
9 | app: nginx
10 | template:
11 | metadata:
12 | labels:
13 | app: nginx
14 | spec:
15 | containers:
16 | - name: nginx
17 | image: nginx:1.19.0
18 |
--------------------------------------------------------------------------------
/chapter03/manifests/replication_controller_simple.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ReplicationController
3 | metadata:
4 | name: replication-controller-runs-pod
5 | spec:
6 | replicas: 3
7 | selector:
8 | app: nginx
9 | template:
10 | metadata:
11 | labels:
12 | app: nginx
13 | spec:
14 | containers:
15 | - name: nginx
16 | image: nginx:1.19.0
17 |
18 |
--------------------------------------------------------------------------------
/chapter04/1.1-service_overview.md:
--------------------------------------------------------------------------------
1 | `Service`是一种抽像资源,用于暴露运行在`Pod`中的服务并提供一定的负载均衡能力。
2 |
3 | ## Service的出现背景
4 | 通常,我们希望把服务部署在`Pod`中,往往会通过`Pod`控制器(如`Deployment`)来创建并管理多个`Pod`副本,例如,我们通过`Deployment`创建了3个`Pod`副本,每个`Pod`中均运行一个`nginx`服务,如下所示:
5 | ```
6 | [root@ecs-d8b6 manifests]# kubectl get pods -o wide
7 | NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
8 | nginx-deployment-5f67bd6bb-bvx2w 1/1 Running 0 10s 172.17.0.5 127.0.0.1
9 | nginx-deployment-5f67bd6bb-g9kkp 1/1 Running 0 10s 172.17.0.4 127.0.0.1
10 | nginx-deployment-5f67bd6bb-sr2w4 1/1 Running 0 10s 172.17.0.6 127.0.0.1
11 | ```
12 | `Kubernetes`会为每个`Pod`分配一个IP地址,`Pod`使用该IP地址与外界通信,在上面的例子中三个`Pod`的IP地址分别为`172.17.0.5`、`172.17.0.4`和`172.17.0.6`。用户或集群中的其他`Pod`都可以使用这些IP地址访问`nginx`服务,如下所示:
13 | ```
14 | [root@ecs-d8b6 manifests]# curl 172.17.0.4
15 |
16 |
17 |
18 | ...
19 |
20 |
21 | Welcome to nginx!
22 | ...
23 |
24 |
25 | ```
26 |
27 | 这样的部署方式仅仅可以保证基础的服务能力,从实际的用户体验角度来看,存在一些无法回避的问题。
28 |
29 | 首先,`Pod`的IP地址是随机分配的,其他`Pod`无法提前知晓服务的IP地址。
30 |
31 | 其次,`Pod`是一种“易逝”的资源,它随时都有可能被重新创建或被调度到其他节点,而每次都会获得一个新的随机IP地址。
32 |
33 | 再次,多个`Pod`副本之间没有联系,如果用户需要为服务提供负载均衡的能力,用户需要动态地管理这些`Pod`并处理流量分发。
34 |
35 | `Service`正是为了解决这些痛点而推出的一种解决方案,它管理一组`Pod`副本,为这些副本提供可靠的访问入口以及负载均衡能力。
36 |
37 | ## Service配置
38 | 像其他对象(如`Pod`)一样,`Service`也是一个REST对象,你可以通过相应的API或配置文件来创建`Service`对象,一个简单的`Service`配置文件如下所示:
39 | ```
40 | apiVersion: v1
41 | kind: Service
42 | metadata:
43 | name: nginx-service
44 | spec:
45 | selector:
46 | app: nginx
47 | ports:
48 | - protocol: TCP
49 | port: 80
50 | targetPort: 80
51 | ```
52 | 这份配置将创建一个名为`nginx-service`的`Service`对象,其主要配置如下:
53 | - `spec.selector`:指定`Pod`选择器,该`Service`将查找包含`app: nginx`标签的`Pod`作为流量分发对象;
54 | - `spec.ports`:该`Service`对外暴露的端口列表,支持暴露多个端口;
55 | - `spec.ports.protocol`:该端口对应的IP协议,支持`TCP`、`UDP`和`SCTP`;
56 | - `spec.ports.port`:该端口对外暴露的端口号;
57 | - `spec.ports.targetPort`:后端`Pod`暴露的端口;
58 |
59 | 简单地说,`Service`通过`spec.selector`来查找`Pod`,并把这些`Pod`提供的服务“聚合”起来,对外提供一个统一入口。创建`Service`对象时,`Kubernetes`默认会给`Service`分配一个IP(称为`Cluster IP`),例如`10.0.0.165`,`Service`通过该IP对外提供服务,当请求流量到来时,再把流量转发到后端的`Pod`,并提供一定的负载均衡能力。整体工作流程如下所示:
60 |
61 | 
62 |
63 | 访问`Service`的Cluster IP,效果与直接访问`Pod`的IP地址一样,但使用`Service`可以屏蔽后端`Pod`细节,对外提供固定的访问入口,当后端的`Pod`有变动时,`Service`会自动更新转发列表。
--------------------------------------------------------------------------------
/chapter04/1.2-service_quickstart.md:
--------------------------------------------------------------------------------
1 | 本节,我们将通过实际的例子来体验`Service`所提供的功能。
2 |
3 | ## 创建
4 | 创建`Service`对象时,`Kubernetes`会根据`spec.selector`来查找拥有指定标签的`Pod`,查找到`Pod`就维护一组拓扑关系,如果查找不到也不会自动创建`Pod`(配置中没有`Pod`模版),所以本例中用到的`Pod`对象需要单独创建,在开始之前,假定我们已使用前面介绍`Deployment`时使用的配置创建了一组label为`app: nginx`的`Pod`对象,这些`Pod`通过端口`80`对外提供服务。
5 |
6 | 首先,我们将以下配置保存到名为`service.yaml`的文件中:
7 | ```
8 | apiVersion: v1
9 | kind: Service
10 | metadata:
11 | name: nginx-service
12 | spec:
13 | selector:
14 | app: nginx
15 | ports:
16 | - protocol: TCP
17 | port: 80
18 | targetPort: 80
19 | ```
20 | 然后,创建`Service`对象:
21 | ```
22 | [root@ecs-d8b6 manifests]# kubectl create -f service.yaml
23 | service/nginx-service created
24 | ```
25 | ## 查看
26 | 接着查看刚刚创建的`Service`对象:
27 | ```
28 | [root@ecs-d8b6 manifests]# kubectl get services nginx-service -o wide
29 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
30 | nginx-service ClusterIP 10.0.0.83 80/TCP 56s app=nginx
31 | ```
32 | 命令行输出中各字段含义如下:
33 | - NAME:`Service`对象名称,对应配置中的`metadata.name`;
34 | - TYPE:`Service`类型,默认为`ClusterIP`类型,更多的类型将在后面的章节中介绍;
35 | - CLUSTER-IP:自动分配的`Cluster IP`;
36 | - EXTERNAL-IP:外部IP地址,用于接收集群外部流量的地址,在后面介绍`Service`类型时详细介绍;
37 | - PORT(S):`Service`对外暴露的端口列表,本例中只对外暴露一个端口,对应配置中的`spec.ports`;
38 | - AGE:创建至今经历的时间;
39 | - SELECTOR:标签选择器,`Service`根据此选择器查看后端`Pod`,对应配置中的`spec.selector`。
40 |
41 | 当前`Kubernetes`支持多种`Service`类型,来应对不同的使用场景:
42 | - ClusterIP:Service通过一个只能在集群内部访问的 Cluster IP来暴露服务;
43 | - NodePort:Service通过Node上的某个端口来暴露服务;
44 | - LoadBalancer:Service通过具体云厂商提供的负载均衡器来暴露服务;
45 | - ExternalName:Service仅对外暴露一个域名;
46 |
47 | ### 查看Pod 拓扑
48 | 尽管`Service`会通过`selector`来查找`Pod`,但查找到的`Pod`信息并不直接记录到`Service`对象中,而是记录到一个`Endpoints`对象中,进一步说当创建`Service`对象时,`Kubernetes`还会创建一个同名的`Endpoints`对象,来记录后端的`Pod`拓扑。
49 | 关于`Endpoints`,我们会在后续的章节中详细介绍,此处仅做初步介绍。
50 |
51 | 查看随`Service`一并创建的`Endpoints`对象:
52 | ```
53 | [root@ecs-d8b6 manifests]# kubectl get endpoints nginx-service
54 | NAME ENDPOINTS AGE
55 | nginx-service 172.17.0.4:80,172.17.0.5:80,172.17.0.6:80 20m
56 | ```
57 | 可以看到,该`Endpoints`对象记录了`Service`匹配到的所有`Pod`地址。
58 |
59 | ### 访问Service
60 | 在集群内部,可以直接访问`Service`的`Cluster IP`,流量将会被自动转发到后端的某个`Pod`中:
61 | ```
62 | [root@ecs-d8b6 manifests]# curl 10.0.0.83
63 |
64 |
65 |
66 | ...
67 |
68 |
69 | Welcome to nginx!
70 | ...
71 |
72 |
73 | ```
74 |
75 | ## 更新
76 | 当更新`Service`的`spec.selector`时,`Kubernetes`会自动按照新的`spec.selector`配置查找`Pod`,并更新`Endpoints`对象。
77 |
78 | 使用`kubectl edit service nginx-service`命令来修改`Service`,并指定一个无法匹配到任何`Pod`的`spec.selector`,可以看到
79 | `Endpoints`对象中的`Pod`拓扑信息也会相应地消失掉,如下所示:
80 | ```
81 | [root@ecs-d8b6 manifests]# kubectl get endpoints nginx-service
82 | NAME ENDPOINTS AGE
83 | nginx-service 31m
84 | ```
85 | ## 删除
86 | 当删除`Service`对象时,随`Service`对象创建而自动创建的`Endpoints`对象也会一并删除,后端的`Pod`不会被删除,它仍然受相应的`Pod`控制器管理。
87 |
88 | ```
89 | [root@ecs-d8b6 manifests]# kubectl delete service nginx-service
90 | service "nginx-service" deleted
91 | [root@ecs-d8b6 manifests]# kubectl get endpoints nginx-service
92 | Error from server (NotFound): endpoints "nginx-service" not found
93 | [root@ecs-d8b6 manifests]# kubectl get pods
94 | NAME READY STATUS RESTARTS AGE
95 | nginx-deployment-5f67bd6bb-9nspj 1/1 Running 0 37m
96 | nginx-deployment-5f67bd6bb-hl8xw 1/1 Running 0 37m
97 | nginx-deployment-5f67bd6bb-pkv7h 1/1 Running 0 37m
98 | ```
99 |
--------------------------------------------------------------------------------
/chapter04/images/service_overview.drawio:
--------------------------------------------------------------------------------
1 | 7VnbcpswEP0aHptBAmPn0bHTdjLJNDNup+mjAjKoEYgR8oV+faUgrsKu0zq4udh+QKuVkM45uyyy5czi7SeO0uiGBZha0A62ljO3IAQuhJb62UFeWCY2KAwhJ4F2qg0L8gtro62tKxLgrOUoGKOCpG2jz5IE+6JlQ5yzTdttyWj7rikKsWFY+Iia1u8kEJHeBRzX9s+YhFF5Z+CdFz0xKp31TrIIBWzTMDmXljPjjIniKt7OMFXglbgU4z7u6K0WxnEiDhnwRUTR3ZWXr7/F1zdf2f2aw6sPepY1oiu94QXma+JjvWaRl0BwtkoCrOYClnOxiYjAixT5qncjqZe2SMRUd+tZMRd4u3O5oAJBqgezGAueSxc9QCqlGJJ32puaBlBiGzUo8LQNaebDauoaHHmh8XkCVo6BVYap1BzjljNVN0wlCtMkJMnWQE/iINoQZYKzBzxjVA2fJyyRnhdLQmnHhCgJE9mkeKlmUJgSKc+pNsckCNRNehmpObOPQ4oD26Q47oGkwOcixTVIuWXBycU78to4Ae/U4h0bOFF0L3P1W1GuZ3fSCexhxBtSuecvQrmnT7tlJfAu3X1Jd1DpArNs+B+169on1y58126bkr4H4bDaNcs4YJ+pL/BGz0CDL5HC/OREVEmjTCI9z79heTArNzCGZ2AsmXBfLw9ep4KGPTlqWB5Gu3l4xfHQ5eH08eDt5sF7Ozy4PcXNsDxMDLBxEOKFbjIuIhayBNHL2tqBpfa5ZizV/PzEQuT6fAutBGuzJ9Hi+Z0e/9j4oRoyAnVzvm12zvMWA2qB+/GX+2Er7uN9G9eviALxEIt9jv2EckyRIOv2Qo5Pj/ki+y2TOu5ylkUoVZermE6LI5pK6NeqArtlGRGEKcHfMyFYLB0eS7ML5D+Ej3yWgWNBZ/n46QkWwTplLFsJShIZh+VxpG2WZEcImwMOx5whC11o0mKGURJM1YmsSj4UZRnxD4kBcLQI+KOw3X7IG5iOejAtbQfrX9/hlhG5wIpRt/uWBzpcFQGsR8HGOW93olFHG13SCxyMiSQ3KG+4pcohM5RRAfMPYpkcQSxbIu4a1410KVu1VlQjbwrnL5JsgLKoeo09ZsY9UJfnJ9XleMdL9VN1OepWXKPDdHk01ZkHbk9WXUsJO+Q0XL4CO54RLyxheaAz0eRYwpDN+v+1wr3+l9K5/A0=
--------------------------------------------------------------------------------
/chapter04/images/service_overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter04/images/service_overview.png
--------------------------------------------------------------------------------
/chapter04/manifests/busybox.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: busybox
5 | namespace: default
6 | spec:
7 | containers:
8 | - name: busybox
9 | image: gcr.io/google_containers/busybox
10 | command:
11 | - sleep
12 | - "3600"
--------------------------------------------------------------------------------
/chapter04/manifests/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: nginx-service
5 | spec:
6 | selector:
7 | app: nginx
8 | ports:
9 | - protocol: TCP
10 | port: 80
11 | targetPort: 80
--------------------------------------------------------------------------------
/chapter06/1.1-secret_overview.md:
--------------------------------------------------------------------------------
1 | ## 引入背景
2 | 当容器中的应用需要访问容器以外的服务(比如数据库、代码仓库和`kube-apiserver`等)时,那么不可避免地需要使用相应服务的帐号、密码等凭据(取决于具体服务的要求)。所以如何为容器提供凭据、以及容器中的应用如何使用这些凭据就成了不得不思考的问题。
3 |
4 | 在没有`Secret`之前,为容器提供凭据主要有如下几种方式:
5 | - 将凭据硬编码;
6 | - 将凭据保存到文件,并最终放置到镜像;
7 | - 将凭据保存到文件中,并通过volume的形式挂载到容器;
8 |
9 | 首先,将凭据硬编码的方式当然不可取,每次凭据变更都需要重新生成二进制文件。其次将私密的凭据放入镜像,
10 | 会在镜像分发过程中泄露凭据,同时凭据变更也需要重新制做镜像,变更周期过长。最后,将凭据使用volume的形式挂载到容器这种方式,需要保证volume在多个集群节点共享(或每个节点拥有一个拷贝副本),难以保证凭据的私密性,同时在集群扩容时还需要保证新增节点上能够访问到凭据,维护成本比较大。
11 |
12 | 针对以上问题,`Kubernetes`设计了一个新的名为`Secrets`的资源类型,专门用来保存私密的凭据,它以键值方式存储凭据,并支持在 Pod 资源中通过环境变量或volume引用。
13 |
14 | ## 延伸阅读
15 | - 《Secrets设计提案》https://github.com/kubernetes/community/blob/1c3dcb393d96618ba59bde57cee4e3b212e42c33/contributors/design-proposals/auth/secrets.md
16 | - 《Secrets官方文档》https://kubernetes.io/docs/concepts/configuration/secret/
17 |
--------------------------------------------------------------------------------
/chapter06/manifests/secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Secret
3 | metadata:
4 | name: mysecret
5 | type: Opaque
6 | stringData:
7 | username: admin
8 | password: pswd123
--------------------------------------------------------------------------------
/chapter07/1.1-certificate.md:
--------------------------------------------------------------------------------
1 | 使用HTTPS通信时,需要服务端提供证书,比如使用浏览器访问网站,浏览器就会验证网站的证书以确保网站是安全可靠的。在Web应用系统中,客户端访问服务端时,服务端也可以要求客户端提供证书,以确保客户端是合法的。
2 |
3 | 本节介绍证书以及与其相关的公钥、私钥和签名等基础概念,希望通过本节的学习能够回答以下问题:
4 | - 什么是非对称加密?
5 | - 什么是公钥和私钥?
6 | - 什么是数字签名?
7 | - 什么是数字证书?
8 |
9 | ## HTTP为什么不安全
10 | HTTP协议位于TCP/IP四层模型中的应用层,其本身并没有提供任何数据加密机制。
11 |
12 | 
13 |
14 | 客户端和服务端的通信全部是明文的,假如有中间人抓取网络包就会造成信息泄露,甚至还会篡改数据以及假冒服务器身份达到钓鱼的目的,如下图所示:
15 |
16 | 
17 |
18 | ## HTTPS
19 | HTTPS协议正是为了解决HTTP安全问题而推出的。为了增强数据的保密性,使用HTTPS通信时会对数据加密,加密算法可分为对称加密和非对称加密两类。
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 | 这个漏洞的核心是客户端不知道收到的服务端的公钥是真的还是被篡改过的。在现实生活中,我们收到一份文件,可能也不能辨别真假,但是如果它加盖了政府部门的公章,就比较容易辨别了,因为我们可以到政府部门核对真伪。在使用HTTPS协议通信时,证书的作用正是证明公钥的合法性。
46 |
47 | #### 证书
48 |
49 | 在密码学领域,证书是一种证明公钥归属的电子文档,有时也被称为数字证书。
50 | 证书包含公钥信息、公钥所有者信息以及权威机构的签名。
51 |
52 | 权威CA( Certificate Authority)机构可以颁发证书,服务端申请证书时需要提供自身身份信息及公钥。
53 | 对于网站用的证书而言,自身身份信息包括网站域名、公司名称、城市、省份和国家,公钥是网站管理员事先生成的。
54 |
55 | CA机构签发证书过程如下所示:
56 |
57 | 
58 |
59 | 证书中的数字签名类似于日常生活中政府的公章,是鉴别证书真伪的关键。
60 |
61 | CA机制生成数字签名时,先使用公开的哈希算法对身份信息和公钥进行哈希运算得到证书的摘要信息,然后再使用CA机构自身的私钥对摘要信息进行加密,如下图所示:
62 |
63 | 
64 |
65 | 之所以说数字签名是鉴别证书真伪的关键,是因为客户端可以使用CA机构的公钥解密数字签名得到证书摘要,客户端还可以从证书中提取身份信息和公钥,并对其进行哈希运算再得到一份证书摘要,如果两个证书摘要一致,说明证书内容没有被篡改过。
66 |
67 | 如果身份信息和公钥只要被篡改,证书摘要就会变化,而如果数字签名被篡改就不能使用CA机构的公钥解密。
68 |
69 | 综上,证书是用来证明公钥真实可信的,这就解决了前面提到的客户端无法辨别公钥真伪的问题。
70 |
71 | 客户端和服务端协商对称密钥时,不再直接给客户端发送公钥,而是发送包含公钥的证书,客户端先验证证书的真伪,证书为真时才会从证书中提取公钥,完整的通信过程如下所示:
72 |
73 | 
74 |
75 | ## 小结
76 | 正所谓道高一尺,魔高一丈,众多科学家努力提升网络安全性的同时,黑客入侵和攻击技术也在不断地演进,当前使用的HTTPS协议也不是绝对安全的。
77 |
78 | 比如浏览器为了支持HTTPS协议,会预安装各个权威CA机构的公钥证书,用于验证服务端的证书,如果发现钓鱼网站,浏览器会给出安全提醒甚至拒绝访问。但如果浏览器误装了黑客的证书,钓鱼网站就会骗过浏览器,从而窃取用户隐私数据。
79 |
80 | 参考文档:
81 | - 《Public key certificate》https://en.wikipedia.org/wiki/Public_key_certificate
82 | - 《Certificate signing request》https://en.wikipedia.org/wiki/Certificate_signing_request
--------------------------------------------------------------------------------
/chapter07/1.2-certificate-sign.md:
--------------------------------------------------------------------------------
1 | 对于网站类的应用,网站管理员需要向权威证书签发机构(CA)申请证书,这通常需要花费一定的费用,也有非营利的证书签发机构,比如”Let's Encrypt“可以为用户免费签发证书。但对于`Kubernetes`这类应用来讲,它通常部署在企业内部,其管理面组件不需要暴露到公网,所以就不需要向外部的证书签发机构申请证书,系统管理员就可以自已签发证书供内部使用。
2 |
3 | 本节我们使用简单的例子,介绍一下如何使用`openssl`签发证书,侧重介绍签发证书流程,具体证书配置还需要管理员根据实际情况填写。
4 |
5 | 以`kube-apiserver`为例,它的启动参数有3处需要配置证书:
6 | ```
7 | --client-ca-file=/yourdirectory/ca.crt
8 | --tls-cert-file=/yourdirectory/server.crt
9 | --tls-private-key-file=/yourdirectory/server.key
10 | ```
11 | 其中`ca.crt`即CA的证书,通常Kubernetes各个组件都配置相同的CA证书,`server.crt`即`kube-apiserver`的证书,它将在与客户端建立连接时发送给客户端,由客户端进行验证,`server.key`即`kube-apiserver`的私钥,它不会发送给客户端,仅用于解密客户端发送的数据。
12 |
13 | 为了便于理解,我们假设有两位管理员参与证书签发流程,一位CA管理员负责管理CA的凭证并为他人提供签发证书的服务,一位管理员负责为`kube-apiserver`申请证书。
14 |
15 | ## 生成CA凭证
16 | CA凭证包括一个私钥和证书,私钥由CA机构保存,不会对外公开,证书则是对外公开的。生成证书前面要先为CA机构创建一个私钥。
17 |
18 | ### 生成CA私钥
19 | 使用`openssl genrsa` 命令便可以生成一个私钥:
20 | ```
21 | # openssl genrsa -out ca.key 2048
22 | Generating RSA private key, 2048 bit long modulus
23 | .....................................................................+++
24 | ........................................+++
25 | e is 65537 (0x10001)
26 | ```
27 |
28 | 生成的私钥存在`ca.key`文件中,可以使用`cat`命令查看:
29 | ```
30 | # ls
31 | ca.key
32 | # cat ca.key
33 | -----BEGIN RSA PRIVATE KEY-----
34 | MIIEog...// 省略若干内容
35 | -----END RSA PRIVATE KEY-----
36 | ```
37 |
38 | ### 生成CA证书
39 | 接着使用`openssl req`命令生成一个证书:
40 | ```
41 | # openssl req -x509 -new -nodes -key ca.key -subj "/CN=ca" -days 10000 -out ca.crt
42 |
43 | ```
44 | 生成的证书存在`ca.crt`文件中,可以使用`openssl x509`命令查看:
45 | ```
46 | # openssl x509 -in ca.crt -text -noout
47 | Certificate:
48 | Data:
49 | Version: 3 (0x2)
50 | ...
51 | ```
52 | 到此为止,CA管理员已经拥有了一个私钥和证书,可以为`kube-apiserver`签发证书了。
53 | ## 生成kube-apiserver凭证
54 | 要申请证书,`kube-apiserver`管理员需要准备一个证书签发请求(申请书),为此,`kube-apiserver`管理员需要先为`kube-apiserver`生成一个私钥。
55 |
56 | ### 生成kube-apiserver私钥
57 | 为`kube-apiserver`生成私钥与前面为CA生成私钥的方法完全一致,同样可以使用`openssl genrsa`完成:
58 | ```
59 | # openssl genrsa -out server.key 2048
60 | ```
61 | 生成的私钥存放于`server.key`中。
62 |
63 | ### 生成kube-apiserver证书请求
64 | 接着`kube-apiserver`管理员需要使用`kube-apiserver`的私钥生成一个证书签发请求,才可以提交给CA管理员进行签发。
65 |
66 | 使用`openssl req -new`命令可以创建一个证书请求文件:
67 | ```
68 | # openssl req -new -key server.key -out server.csr
69 | ```
70 | 创建证书请求文件需要提供私钥,然后根据命令行提示输入相关信息,生成的请求文件存放于`server.csr`文件中。
71 |
72 | ### 生成kube-apiserver证书
73 | 当`kube-apiserver`管理员创建好证书请求文件后,即可提次给CA管理员进行证书签发了。CA管理员在签发时
74 | 需要使用CA的私钥和证书:
75 | ```
76 | # openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 10000
77 | Signature ok
78 | ```
79 | 这样签发完的证书就会保存在`server.crt`文件中。
80 |
81 | ## 证书文件后缀
82 | 关于证书及私钥文件,常常会使用约定俗成的文件名后缀。
83 |
84 | - *.key: 往往表示私钥文件;
85 | - *.crt: certificate的缩写,往往表示证书文件;
86 |
87 | 此外,`*.csr`文件为证书签名请求文件,“Certificate Signing Request”的缩写,该文件内含公钥及公钥所属者信息,用于向CA机构申请签名。
88 |
89 | ## 参考资料
90 | - 《Self-signed_certificate》by Wikipedia: https://en.wikipedia.org/wiki/Self-signed_certificate
91 | - 《Certificates》by k8s.io: https://kubernetes.io/docs/concepts/cluster-administration/certificates/
92 |
--------------------------------------------------------------------------------
/chapter07/images/cert_digital_signature.drawio:
--------------------------------------------------------------------------------
1 | 5ZdNc5swEIZ/zR7TQWDzceQr7UyTHppD06MGFKARyJXl2O6v7wqEMYa46UxSd9qTta8khJ53vRLgxPXuvaSr8lbkjINt5TtwErBtYvku/mhl3ymev+yEQla5GTQId9UP1s806qbK2Xo0UAnBVbUai5loGpapkUalFNvxsAfBx6uuaMEmwl1G+VT9UuWq7FTf9gb9A6uKsl+ZuEHXU9N+sNnJuqS52B5JTgpOLIVQXavexYxreD2Xbt71M72HF5OsUS+ZcCOCT7dFFYSPRU12H5Pv98HnK/OUJ8o3ZsOQ+hCFEEaQLiCKIEraxjWEBFIXfALhtdmQ2veUpNg0OdMLWeBE27JS7G5FM927xbxArVQ1x4hg0yzJpGK7Z/dCDoQwtZiomZJ7HGIm+IapSSrimng7WER67uWRPY7RqMmK4vDkARw2DLvf4GjPcFwC5noYQxpAYEO4/Nuo2dalqQUTJCzHf58JhVSlKERDeTqo0RjaMOZGiJVB9Y0ptTelhG6UGINEWHJ/b+a3wVcdvFv2YbI77kz2JureVb/gefy4H7GRGTuzb8cUMioLps6M8+btlIxTVT2N3+PVzXGmKX3qFpa0lW5mG8n3kaTZo97OrzJ5cLCLFG5GNBheEf+VMv1wfJwpEHOZ7r1VphMygfd/pLr3wlT3L5nq3uwpiOcc5qM+/AIIXX34BQQCv+1qD8JL13NnQcZZ7l+6oPszIBHbEiJLn4dhAoEHqacbUaqVwAI/uTjI5el94uIgyUz1HS4UyDMG3yrpumxppi1WFyJHsz6liVzUGNlaSfHIYsGFRKURja42DxXnJxLlVaELc4YcGeqRplzh5Tg0HXWV522pmvNo7OJrVHXnxCZvalNApi7Zb+bSYuJSHLaOeG3p6C9+2jm8Unf/Aawq7r/r0cL7Yx5hOHw9tX1H36BO+hM=
--------------------------------------------------------------------------------
/chapter07/images/cert_digital_signature.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter07/images/cert_digital_signature.png
--------------------------------------------------------------------------------
/chapter07/images/cert_http_protocol.drawio:
--------------------------------------------------------------------------------
1 | zZVNb6MwEIZ/zRwr8ZEScwSHbHuoFG2i3b16gwtsDY4cJyT99TsGE0BJpVRqlfgA43dm/PHMCMCn5eGHYpv8RaZcgOekB/Bn4HmuQwJ8GeXYKlPy2AqZKlIb1AvL4p13mVbdFSnfjgK1lEIXm7G4llXF13qkMaVkPQ57lWK864Zl/ExYrpk4V38Xqc5blXjTXn/iRZZ3O7tB2HpK1gXbm2xzlsp6IPkJ+FRJqVurPFAuDLyOS5s3/8B7Opjilb4mgc5LXu33cvfv1/viofqz50H0YFfZM7GzF4ZkCvEMQrcxYggpJI8Qu0AwMBC4VfxXoZUZC5I5xBQIeV7gxtHPRSdYCFt97EAquatSbs7iYGKdF5ovN2xtvDW2Dmq5LgXOXLNueyquND98eF33BBG7j8uSa3XEEJvgOZa7bTxvYud1X0a3q00+KGEXx2znZKele7hoWL6fYO1dYB1AiHAdY5AZRAkkIYQ+xGgQiKcQzQf0740ouTVR/wLRiem/yGn4JYblFd27oqZ9n++5e51bs55cYI1kIwgn5kuBz4hcw/pptfoqzq+FEFQKqZpcnzYD9a1W8o0PPEEzTIas9EBvx/dUzA2+r2I47f8bjW/w9/WT/w==
--------------------------------------------------------------------------------
/chapter07/images/cert_http_protocol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter07/images/cert_http_protocol.png
--------------------------------------------------------------------------------
/chapter07/images/cert_http_unsafe.drawio:
--------------------------------------------------------------------------------
1 | 7Zhtb5swEMc/jV9uChgCvCRpunXqpErR1r11wQWvhkPGNEk//exgnmm3SlnXba0iFf6+s/H97nwJCK+z/QdBivQzxJQjexHvET5Dtm0t/KX6p5VDrXi+WwuJYLEx6oQte6CNp1ErFtNyYCgBuGTFUIwgz2kkBxoRAnZDs1vgw1ULktCJsI0In6rXLJZprfq21+kfKUvSZmVrGdQjGWmMzU7KlMSw60l4g/BaAMj6KtuvKdfBa+JS+50/Mto+mKC5/BWH4uHu4jr88rX4tK1EcHG1zL/577DBU8pDs2MaqwCYWxAyhQRywjeduhJQ5THV0y7UXWdzCVAo0VLidyrlwdAklQQlpTLjZrReUy/06F6MVEIlIvrEBmxskoKIhMqnduq0MVfJSiGjUhyUo6CcSHY/fBJisiZp7YxrKAQ59AwKYLksezNfaUEZNAXgGPom/S13BOl59uqifoLmrreVTjqCf0YSNDG8J7wycUAbF4UbFKqLJfJ9tPLQxkPhCoXnk4RRiV3oyyrjYSRBKMD3VEimiuiS3FB+BSWTDHJlcgNSQqYMuB5YkeguOSbTGrj2U7Ph2+Nfb46Qs0T7Sp1c/SwiZiBSSUNFm1baj+6fTqxpGhgHfxT+pnh3Xe1jI6W9sl8uHs+bHrvno2kWG6BZomCN/DPNyA9RaP0faHAQvDI21gwbB618FCo2AQo8tHKOSqg//zIbG9vv3QGdtpL+GB3vL+9sp2lYkw6Dg/kO005Rd1LjNYJwgmaD/Tcuc1xsxxuXkPfCZJzpeTZBxbn65q2RNMdXxKFSs652KZN0W5Bj7Hbqx8ALnDqLn3frwH3BM8eZ69au7giq6HS3Pj9+kXJ1q1Y/jHTbXiDfmkRZhUQOw1dKAXe0Oe9zyDWCW8b5SJqEedwOMhbHx5Ka4zUssxMQsvCIEJ4Scme6gv3bCM317P+ZELZfGyH7jdDwlHOHhNpXI6cnpG679xF1W+ne6uDNDw==
--------------------------------------------------------------------------------
/chapter07/images/cert_http_unsafe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter07/images/cert_http_unsafe.png
--------------------------------------------------------------------------------
/chapter07/images/cert_https_asymmetric_encrypt.drawio:
--------------------------------------------------------------------------------
1 | 7Vptc+I2EP41+piMbdnG/mjzcr2Z3My1aSd3nzqKrYCLsFwjAvTXdyXL+AUTSArjKxzDB+1aWkv7PNpdCRAeLjafcpLNvvCYMmQZ8QbhEbIs07YsJL9GvC00HnYKxTRPYt2pUjwm/1CtNLR2lcR02egoOGciyZrKiKcpjURDR/Kcr5vdXjhrvjUjU7qneIwI29c+JbGY6VVYg0r/C02ms/LNpusXTxak7KxXspyRmK9rKjxGeJhzLorWYjOkTDqv9EsxbnLg6W5iOU3FKQO+uX//KjbzlfnnfGH8Mc/unh6iO23llbCVXjAaOygYowAaLvI8FA7QeICCEAUTvQ6xLZ0DS8pkc7VgD8kLZUkKUghTEkmUZETNC54FkeC5fEDzZEEFhfaI6f5fK124niWCPmYkkjbXwCfQzcSCgWRCEyAWBIbkO5kxki2TZzUdAzQ5jVb5Mnmlv9FlwSSpfaVyPoQFLJmmoBNcGl7Ce5J0+rsURtgFDSPPlIUkmk9zvkrjIWdy1rBO/KI+0IWvhJz0cEc2aV+7EN5CNwexMXeIw1ahHJacb6GLHmAZepvoXWKWpFlXnCtVsxrdbEMriab5dGe6YgI0NBneQQy8T4w2+jVoMp6kQk3BCZEzamHNczHjU54SVkf7JLcdJu1BXzpHPWl2eNK9lCPNa/Gk1bcnLf9KPIl792RH1HeRP0TeSIZ/L0CB+TPqXzzq2577g0V9+wfeYdb/Kupfiyd7j/rYuBJP9h71nT2/0RjOOlqsXDOutKGKiTTWMa/q88BVBJUe/4sKsdUHN7ISvBm+wX/59pscf78TvyvRKcXRRpsvpG1dqsFUi7py3u+NuTllRECSaIzr8rEe+lUSqRar/WaFbjktE4LkUyr0qPoxrGXIMgdvG1ryVR7RPUMK8t16Ps6Cwbu2UzuLPnMh+EKhEwfyeC2Zk1F4FMZkOVNMKXAuz/KelDaJkBy4M+4tLRYc8BwtVhyQwrYmfJQBsCLlyGPZ7uxMwa2zHDaNkwDeZ4rVopx/GuXOxRSzI/IWdwIwEdnAKMRorK4I/Aka2ygcocBQFwgTFPryETR8qClBhOJyqDXySsGVVSZ8ZbkJ/eGRj2CLBY6sO8MQ+X71rjY/IUyLZpRZipzPaVm7pVxVpC8JYy0V0TSOgCsq9Lf5vUjiWAW+rqq0GQxPyhpHjv2DFsK4I0W4HTnCuljd0nUhBAhPNCC+ArBiASAPMNodMN4OaIPeQRv8zOwfj9fthIxPTMhHS4Q9Q5eO1++7Hzl7asfN1A5u7TG3m5dJ7rbXwtg/U3LfM3SALIAM2da66fPO4Qm73e+puFdYPCsTLfetysGW+V2mClUwQGqRWcRGgVerHAYogDKgrCU8d5dXNpvN7aQW3Hs9YHlvIenIUi60upBUAHpmrayDUaHq46iflQw1KlDYHof9hjDvvZzAXr/lxDUVE7btf7SYMN82dOFiAvdZTOiKsqolcFlb9FJLYOMiXLGd5s63z1VL7Bm6MFfsrosCiPITlSR85BkqExQ/LNWDO5wnHRXuXfnLUzAuLwq8L58/j1+n9/f3pca/nQRg95707e5/hfwHPD/5T8bzjcJ5wXwOYvX/oWI3V//CwuN/AQ==
--------------------------------------------------------------------------------
/chapter07/images/cert_https_asymmetric_encrypt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter07/images/cert_https_asymmetric_encrypt.png
--------------------------------------------------------------------------------
/chapter07/images/cert_https_asymmetric_encrypt_risk.drawio:
--------------------------------------------------------------------------------
1 | 7VpLc+I4EP41OiZlW7axjzaQnUO2anazWzNz2lJsBTwIxBoRYH/9tmQZPwmEwfFMAuGgbkttqftTvwjCw/n2t5Qsp7/zmDJkGfEW4RGyLNO2LCS/RrzLOB52MsYkTWI9qWA8JP9RzTQ0d53EdFWZKDhnIllWmRFfLGgkKjySpnxTnfbEWfWtSzKhDcZDRFiT+yWJxVSfwhoU/E80mUzzN5uunz2Zk3yyPslqSmK+KbHwGOFhyrnIRvPtkDKpvFwv2bq7A0/3G0vpQpyy4Kv77x9iO1ub/8zmxt+z5c2X++hGS3kmbK0PjMYOCsYogIGLPA+FAzQeoCBEwZ0+h9jlyoEjLeVwPWf3yRNlyQKoELYkkihZErUveBZEgqfyAU2TORUUxiOm538ueOFmmgj6sCSRlLkBPAFvKuYMKBOGYGJBYEm6pxkjy1XyqLZjACel0TpdJc/0T7rKkCS5z1Tuh7CAJZMF8ASXglfwnmQx+UsSI+wCh5FHykISzSYpXy/iIWdy13BO/KQ+MIWvhdz0cA82KV+rEN5CtwdtY+4tDleFcjhyuoMpeoGnV+hLYuaY2RSQy1nTEtqwoZlEo3yyl1wAAQYaC6/ABW7iom78kmWWPFkItQUnRM6oZmqeiimf8AVhZWOfpLXDmD2oSueoJs0WTbpdKdJ8L5q0+tak1eKqbBR6KBihsY/8AQptxQnk9+qq2lzVU8JYafJQfS7jwrB7/Oa9rQ+zO795NXWOXfn3QzfS+qV8W/cafhtN9u7bnIbeaAxpqCYL1YwLbqhuPo31zS7m3HPlJ6TGv1MhdjqnJmvBq04K9Jfuvsr1t3vymyKdnBxttfiM2pWpkplKaZDc92s9SEoZEeAKq8l7i4710s8SSCXPM6hmT5ZTEyFIOqFCrypnyDVBvvuynBVfpxFtyFEW3x/nfBAMXnWb6qHikQvB58o4cSALHwmcJYVHYUxWUwWUzMx5leVJapsICYEb49bSZAYBz9FkAQFJ7ErEuQCAEylFHnPdFweKWUuzsWmcZOCmIKOGOP80xF0KKfm+K7mQqtZgI3KAUYjRWBVv/p1KikYoMFRpd4dCXz6CgQ+JE5BD5A01RxZ7sDyQX5gMIAiGKrmyUODIOjAMke8X76rjE7y0qDqZlUj5jOZBcsFV2lWOm5pFNIwjwIry/HV8z5M4Vn6vLfWq+sILZDOmU7MwbokQbkuIsDoLtm2l+uH8t2m9j2OrQe+2Glzj+fluuhaG8Ylh+Ghe0BDUtZf2+w3ouBrQAZQ9RnSzm5CO3ZqN/QuF9IagA2ABy5BdaZoucg5v2G5/T4G9TOJFkWi5L+ULtozqMlKoNCG8k3Het1HglfKFAQogvuQZhOfuw8p2u/04kQX3ngVY3kuWdGQCF1ptllQGhPy3SOZgVajmOKrNb+jcQdr2uNk/kM17zybslhYyWM+HvH2krBegwLz+SNP5jzSubVSQ8RN0OH/mBlwG21+ll2k770WVvTcz7V6qH9RXuVJ3C2e3H7F3RFDH/Uf7xdzi2lX68Qhi16HSez5p91Gj9nhVrar+z60WsXdEUMetBae9AXwoJfxAzcDGFes9fXeaDu09h8N6FDu7fVePq2/dvnOsj+wacf0nrdO7rn5NkHHreydZ7rW9NNc8+KpO22lOS9l1baed4Jtx7Ub3nv44LVXftZ3Wqc07jMdAFv9OnV324p/S8fh/
--------------------------------------------------------------------------------
/chapter07/images/cert_https_asymmetric_encrypt_risk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter07/images/cert_https_asymmetric_encrypt_risk.png
--------------------------------------------------------------------------------
/chapter07/images/cert_https_whole_encrypt.drawio:
--------------------------------------------------------------------------------
1 | 7VrbcuI4EP0aPSblO/ajDSaTraRqZpOdZJ+2FFsBD8JijQiwX78tWcZXCEkgpJJJURV3Wzf3Oe4+EiCzP11dZHg2vmYxocjQ4hUyB8gwdM114J/wrHNPz7VzxyhLYtWodNwk/5Gip/IukpjMaw05Y5Qns7ozYmlKIl7z4Sxjy3qzR0brs87wiLQcNxGmbe9dEvNx7nWNXun/RpLRuJhZd7z8zhQXjdWTzMc4ZsuKywyR2c8Y4/nVdNUnVASviEveb7jl7mZhGUn5Ph3unX9/8NVkof8zmWp/TWZnd1fRmRrlCdOFemAU2sgPkQ8XDnJdFPRQ2EN+gPyheg6+LoIDjzQTl4spvUoeCU1SsAJYEk+iZIbluuCeH3GWiRskS6aEE7geUNX+e+kLluOEk5sZjsSYS+AT+MZ8SsHS4RIg5hi6ZBubUjybJw9yORp4MhItsnnyRP4k85xJwvtExHow9WkySsHHmRh4DvMk6ehWGAPTAQ/FD4QGOJqMMrZI4z6jYtXwnOaj/IMmbMHFovsbsonx20AUUYWJyariUsBcEAaPnK2hibpraEbeRb0lekGaZcm5wjWu0M3SlBMrmo82Q5dMgAtFhhcQw2wTo4l+BZoZS1Iul2AHyB40sGYZH7MRSzGtot0O206G7h1L+9lI6h2RdI4VSP2zRNI4dSQN75NE0nzHSI6DPy5vftxf8p/LM/8We7fr6/DM6Mj6HnI9FFgi/UPWh3wfuiLluzoKLRR4yHdEHfD6yBtKTx/5vmgMtwJHNHZDWSug+xB5jryAStIXI3sG8u0WViSGSqvMEo6w9FaTP1bJm5JHXmbvK2kNIPIBSWNf1HswHyiLJqIciDRO4lqaFpNuLaDbwW4jmxGKOZSauuLoQEp1/S7oWDKiwLtIU24D6zlbZBFRnUq44RHxutJMkXzrNKbdOU1JnnzALWt0uxNAMTnH2Yjw1holJTeResP73sFSRzDQHUh2+cjXf2uTo2sTq5DwH0abWB+lDhgvjeVH0yafJZIn1yam9kki+Z7apHP9r9EJ9UJftrliMoOKiP8inK/V8QJecFZP3xDSbH0v+p9vzL+laRfmYKWGz6111arAtDvrNtTHwVWF5dX3kYbdGGJLyW4NZOi93QNt0SeHqv29F71OzSr6wDhn07ooZDMCt4IYz8eSKTnOxYmTK6xVwgUHzrRzQ5k5B1xbmSUHhLGuGHszII/b88Xt2EwxGycOpr6fAG0zxWhQztuPcodiit6RefOTK1iIuDBRYIr9CWxO1NZlgHxNHnMNxe4l3+d4oCmHYlfj9pVHbGYcoTLh07kXCgLkeeVcTX5C+uX1LDPnGZuQQrulTCrSx4TShqvY60RAHpn6m/yeJnEsE1+XKu3a9bztcKrXQNjsKBFOR41o7hcOp1u6ji0B4aECxJNwlSwA5AFGqwPGrwNa7+Sg9X5X9tfn62ZBNvcsyM9KhNZAx87XLzvFO3hpN+ulHcL6frVdf6fibrkNjL0DFffWQFvI8tJjKsvpnqfznOpg50vOLuVgifouji+lYIDSIqqIhXy3ohx6yAcZUGgJ19mcda5Wq69TWsyT6wHD3YWkLaRcYHQhKQEUeqA4oxa9AtlGHoMLbKGXL7F9HvYvhPnJ5YTpnlZOfCYxYVnea8WEvnugI4sJ85RiQinKUkuYhbZ4Dy1RJN2jawm7/uZbh9ISrYGOzBWr66Ag/7ZSFgBXk5Ug/2KpmtxhP2nLdO+Ib578sDgocK8vL8On0fn5eeHxvk4BsE5e9K3u3y69Ac8L7057+KJwHrGeg1n+yi1/m8vfCprh/w==
--------------------------------------------------------------------------------
/chapter07/images/cert_https_whole_encrypt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter07/images/cert_https_whole_encrypt.png
--------------------------------------------------------------------------------
/chapter07/images/cert_sign.drawio:
--------------------------------------------------------------------------------
1 | 5ZjLcpswFIafRst0DJjbklvaZtKZdDLTTpcyyKBWICpEbOfpKxkBxuBL2jhup9lE/EhH4vsPOsLACPL1ewbL7BNNEAH6LFkDIwS6rs0cS/yTyqZRbMdshJThRHXqhUf8jNqRSq1xgqpBR04p4bgcijEtChTzgQYZo6thtyUlw1lLmKKR8BhDMla/4oRnjerodq9/QDjN2pk1y23u5LDtrJ6kymBCVzuSEQEjYJTyppWvA0QkvJZLM+72wN1uYQwV/JwB/M41v+ifv1d6Gi4/3n376cQ3N3oT5QmSWj1w4Knl8k3LQKy8lM06J17MKQOG/4QYx4LSPVwg8kArzDEtRJcF5ZzmogORN3wY/0gZrYskoESOE9GM5fZvJ4ZHcCrHcloKNeM5EReaaNKaE1ygoLN2JkS1YDEWrQ+S0Dq+IjERzRFnG9FFDTBsZYnKSc1V16veYUNJ2Y65ltKgyqm0i9xjFw1F/gUuaNrIBhA5wPeA54NoDnwf+OG2cQs8DUQWcDTg3R40KqFxnW9h+KsMc/RYwljqK/GODhEvpDkouV+0wivg1fQ9vNYYrzOB17wYXmMCrwnETuQFIHKBqwPP/Edg6vNrw5yPYR5CF9eMbHwmdgF0Bj7W4OuuOFSbyo3mvNKb35WVI6mpT9DsXHh9nOb5OJcErT1Z0wQMVCSqGcYEVhWOhzCbICgZVbeTqHZQmEcSiyEi/Hkahp/Co2Z4oFhM3Dth7eW15r4zh0EqWrMYqXG7hW0vlDE/GYpDliI+CrV1rHv0PzDR/j9NnJ8mf66J5v4+9/YmOuebeN2aYDqndzFtchub2xfaxlpWgxJrAdcE/kzWWi8Eri3PNOLUIs4u8ijjAs8aARZQ+BAgVKfDWMBBbOLYmOMkkcN9hir8DBfbULJelDJTtg9q+sAMZayai6Pq9hPjbCuO5MoRg4b+tF8LO/5YE/Zcrsi4E+78xvmyr9Cz05l/zUQ3LkWynexlR8mrc5v4wHljblNfOOP9wZYNP5KKWLIT/nUku3f0AiTFZf9rQFPV+t9UjOgX
--------------------------------------------------------------------------------
/chapter07/images/cert_sign.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter07/images/cert_sign.png
--------------------------------------------------------------------------------
/chapter09/1.1-admissioncontroller_overview.md:
--------------------------------------------------------------------------------
1 | ## 什么是准入控制器
2 | 每个针对`Kubernetes`资源对象的操作请求,都要经过`kube-apiserver`的层层审查才会被放行。对于读操作而言,需要经过认证(是否是合法用户)和鉴权(是否拥有权限);而对于写操作而言,除了要经过认证和鉴权外,还要检查请求是合乎要求,只有顺利通过这些审查才会被持久化到`etcd`存储中。
3 |
4 | 准入控制器(Admission Controller)是用于审查请求的组件,它会劫持发往`kube-apiserver`的请求,对请求进行审查(必要时也会修改请求内容),它是`kube-apiserver`审查链中重要的一环,不合理的请求会被拒绝。
5 |
6 | `kube-apiserver`支持配置多个准入控制器,准入控制器分为修改型(Mutating)控制器和校验型(Validating)控制器。修改型控制器会自动根据指定的策略对请求进行修改,而校验型控制器则只是单纯地检查请求是否合乎要求,充当“看门狗”的角色。
7 |
8 | 多个准入控制器以插件(Webhook Plugin)的形式被组织起来,`kube-apiserver`在审查请求时会先把请求交给修改型控制器对请求进行必要的修改,然后再将请求交给校验型控制器进行审查。下图展示了请求的审查完整路径,以及准入控制器所在的位置:
9 |
10 | 
11 |
12 | API请求到达`kube-apiserver`后会先进行认证(Authentication)和鉴权(Authorization),然后把请求交给修改型准入控制器进行必要的修改(多个修改型准入控制器串行执行),当所有修改型准入控制器执行完毕后,再使用`OpenAPI` 校验功能进行初步的语法校验,接着再把请求交给校验型准入控制器进行语法或语义的校验(多个修改型准入控制器并行执行),最后再写入`etcd`。上面中的任何一个审查环节、任何一个准入控制器返回失败,都会造成请求被拒绝。
13 |
14 | ## 准入控制器配置
15 |
16 | 准入控制器根据其部署形式可分为内置控制器和动态控制器两种。内置控制器集成在`kube-apiserver`中,以插件的形式提供,每个插件都可以通过参数控制启用或禁止;而动态控制器则是按一定标准实现的服务。关于动态控制器的更多内容将会在后续章节中展开介绍,本节主要介绍内置控制器的配置方法。
17 |
18 | `kube-apiserver`提供了数十个准入控制器插件,其中一些是默认开启的,也可以通过参数显式地控制启用或禁用的插件。
19 |
20 | #### 开启控制器插件
21 | 通过`kube-apiserver`的`--enable-admission-plugins`参数可以设置除默认启用的控制器插件以外需要额外启用的插件,多个插件名字以逗号分隔,例如以下参数开启`NodeRestriction`和`ResourceQuota`两个插件。
22 | ```
23 | --enable-admission-plugins=NodeRestriction,ResourceQuota
24 | ```
25 |
26 | 该参数主要在需要启用默认禁用的插件时使用。
27 |
28 | #### 关闭控制插件
29 | 通过`kube-apiserver`的`--disable-admission-plugins`参数可以设置禁用的控制器插件,同样多个插件名字以逗号分隔,例如以下参数关闭`PodNodeSelector`和`AlwaysDeny`两个插件。
30 | ```
31 | --disable-admission-plugins=PodNodeSelector,AlwaysDeny
32 | ```
33 | 该参数主要在需要禁用默认启用的插件时使用。
34 |
35 | ## 参考资料:
36 | - 《Using Admission Controllers》 https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers
37 | - 《A Guide to Kubernetes Admission Controllers》https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers
--------------------------------------------------------------------------------
/chapter09/1.2.1-admissioncontroller_namespacelifecycle.md:
--------------------------------------------------------------------------------
1 | `NamespaceLifecycle`是一款内置且默认启用的准入控制器插件,其主要职责如下:
2 | 1. 阻止系统预留的namespace(`default`、`kube-system`和`kube-public`)被删除;
3 | 2. 阻止在不存在的namespace中创建资源;
4 | 3. 阻止在处于删除过程中的namespace中创建资源;
5 |
6 | 其中前两条职责比较容易理解,第三条职责主要是为了配合其他控制器工作。当一个namespace被删除时,
7 | 该namespace中的所有资源都会被清理(级连删除),这往往会耗费较长的时间,
8 | 在这期间namespace对象会处于`Terminating`状态(status.phase值为“Terminating”),为了不影响其他控制器清理资源,
9 | 所以当namespace处于删除过程中时,禁止再创建资源。
10 |
--------------------------------------------------------------------------------
/chapter09/1.2.15-admissioncontroller_mutatingadmissionwebhook.md:
--------------------------------------------------------------------------------
1 | `MutatingAdmissionWebhook`是一款系统内置且默认启用的准入控制器插件,它在`kube-apiserver`审查请求(Mutating admission)阶段被调用,用于审查请求。
2 |
3 | 与其他准备控制器插件不同的是,`MutatingAdmissionWebhook`本身并不直接审查请求,而是将任务转发给相应的`webhook`(多个`webhook`串行调用),如果任何一个`webhook`返回失败,`MutatingAdmissionWebhook`将会立即拒绝请求。`MutatingAdmissionWebhook` 与`webhook`的关系如下图所示:
4 |
5 | 
6 |
7 | `webhook`通常是一个专门负责审查资源对象的web服务,`webhook`根据是否会修改请求分为`Mutating`(修改型)和`Validating`(校验型)两类。`MutatingAdmissionWebhook`负责管理并调用`Mutating`类型的`webhook`,该类型`webhook`通过`MutatingWebhookConfiguration`对象注册到系统中,`MutatingWebhookConfiguration`对象中描述了`webhook`的服务地址、关心的资源对象类型等信息。`MutatingAdmissionWebhook`正是根据`MutatingWebhookConfiguration`对象来获取`webhook`列表,并在API 请求来到时筛选并调用`webhook`。关于`MutatingWebhookConfiguration`对象的更多信息将在后续章节展开介绍。
8 |
9 | `MutatingAdmissionWebhook`是`Kubernetes`一个重要扩展机制,常用于对扩展的`CRD`(CustomResourceDefination)对象进行审查和改写,当然它也可以用于`Kubernetes`原生资源对象,比如当`Pod`创建时自动添加`label`。
10 |
--------------------------------------------------------------------------------
/chapter09/images/admission-controller-phases.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter09/images/admission-controller-phases.png
--------------------------------------------------------------------------------
/chapter09/images/mutatingadmissionwebhook.drawio:
--------------------------------------------------------------------------------
1 | 1VhNb6MwEP01HLsKkBByhCTtVmqlSjl09+iCA94aD2tMQvbXrw3mq5C2adVEPeF59ozxe2OPwbCXSXHDURrfQ4ipYU3CwrBXhmWZE9eRD4UcKmTuziog4iTUg1pgQ/7h2lOjOQlx1hsoAKggaR8MgDEciB6GOId9f9gWaH/WFEV4AGwCRIfoIwlFXKGuNW/xn5hEcT2z6SyqngTVg/VKshiFsO9A9tqwlxxAVK2kWGKqyKt5qfyuj/Q2L8YxE+9xuIkPtx4O/dWNmDvYWwV3qXtlV1F2iOZ6wfplxaFmALPQU0RKK6Aoy0hg2H4sEioBUzYrBxwOeGxfzGyWK/MEQ4IFP8gh+5bQmSYp7nBZYxxTJMiuHx5pXaMmXDPDAxA5sTXROejqMDoBG7uOkEHOA6yduvy9iGM6bwQSiEdYDALJRmfVLVTKc4JUs4FU3sOtYTkoSaUK7ClTD2PtGt614c+NtWP4puEO9RS4EH0FM8HhGS+BApcIAyZH+ltC6QsIURIxlQZSWCxxf4e5IHKveLojIWGopvH3MRF4k6JAzbmXJ4PEOOQsxGqFkyZrVABcnJo32mHxQo9an05aOSNpZU2OZ1BPs1MFMmfDvSN3xUabwEUMETBE1y3qt+gdQKoF+YOFOOizEOUC+nLhgohfnfZvRemPmbZWhWa4NA61weQKf3WNjpcyW7fSqv2CnO9KzU7Z6ZKCck+9wpWuCtWWeWXcYjwD3n0kfE5Q5wOC9hj7gLoXINmcXJTl+QVYPuceuoSiR07O8yjqjFwqHKqKTpYi1pPa+Zur+4+/BSautighVLLmGeqM1oXNsGx7qhQHloG8fww6ysHLMZdEpkQAJzggFqqLn7y8ATvFL3/Kmcjf9FO9WVkUrSovJuWyszJJ1aJNOy2aPlWnr3TJVZ0Ub0VLmGxF6nmfC6koi7wwIfJuBuwRP8UAzzXjUsCK9Gr4YKu1Vdk8UrcHZ9OnivbgFjVStU13pGxPv6psLwbZuq8pvDBX02mfq4aXLlfWObmqv8q+AVmjiXVessxvQ5Y9/TqypNl+5FYfQ+2vAnv9Hw==
--------------------------------------------------------------------------------
/chapter09/images/mutatingadmissionwebhook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter09/images/mutatingadmissionwebhook.png
--------------------------------------------------------------------------------
/chapter10/1.1-resourcequota_overview.md:
--------------------------------------------------------------------------------
1 | `ResourceQuota`是用于设定资源配额的对象,集群管理员可以为每个`namespace`创建`ResourceQuota`对象来限定资源的使用额度,
2 | 从而可以精准且合理地分配集群资源,避免多个`namespace`中的对象争抢共享的集群资源。
3 |
4 | ## ResourceQuota的出现背景
5 | 实际应用场景中,经常出现多用户或者多团队共用同一个集群的情况,管理员往往会为不同用户或团队分配不同的`namespace`,
6 | 从而将彼此隔离,但`namespace`只能做到逻辑上的隔离,多个`namespace`中的应用仍然会共享集群的硬件资源,
7 | 比如`CPU`、内存和存储等,如果某个`namespace`下的应用大量消耗这些共享资源,那么势必会影响其他`namespace`下的的应用。
8 |
9 | `ResourceQuota`正是针对这种问题而提供的一个解决方案。例如,用户`A`和`B`共享某个含有16核CPU以及32G内存资源的集群,
10 | 并且用户`A`和`B`分别使用`namespace-a`和`namespace-b`,那么管理员可以分别在`namespace-a`和`namespace-b`中创建一个
11 | `ResourceQuota`对象,并指定CPU和内存配额(比如平分集群资源),那么用户`A`和`B`后续创建的应用资源总消耗量将不会超过该配额,
12 | 一旦超过该配额,`Kubernetes`将拒绝创建新的应用。
13 |
14 | ## ResourceQuota功能启用
15 | 若要启用`ResourceQuota`功能,需要把字符串“ResourceQuota”加到`kube-apiserver`的`--enable-admission-plugins`参数列表中。比如:
16 | ```
17 | # kube-apiserver --enable-admission-plugins="ResourceQuota,"
18 | ```
19 | 在绝大多数`Kubernetes`发行版中,`ResourceQuota`功能都是默认开启的。
20 |
21 | ## ResourceQuota资源配置
22 | 一个简单的`ResourceQuota`配置,如下所示:
23 | ```
24 | apiVersion: v1
25 | kind: ResourceQuota
26 | metadata:
27 | name: pod-count
28 | namespace: default
29 | spec:
30 | hard:
31 | pods: "0"
32 | ```
33 | 这份配置将在名为`default`的`namespace`中创建一个`ResourceQuota`对象,该对象将确保在该`namespace`中“禁止”创建`Pod`对象。其主要配置如下:
34 | - `metadata.namespace`:`ResourceQuota`对象所属的`namespace`,也是该对象作用的`namespace`;
35 | - `spec.hard`:指定硬性配额列表;
36 | - `spec.hard.pods`:为`Pod`对象个数设置配额;
37 |
38 | `ResourceQuota`还支持其他更丰富的配置,比如支持对特性状态的资源实施限额、对特定优先级的资源实施限额等,这部分内容我们将在后绪的章节中陆续介绍。
39 |
40 | ## ResourceQuota可限制的资源
41 |
42 | `ResourceQuota`支持为多种类型的资源设置限额:
43 | - 计算类资源,比如CPU、内存等;
44 | - 扩展类资源,比如GPU;
45 | - 存储类资源,比如持久卷;
46 |
47 | 除了这些资源类型以外,还支持限定对象个数,这些对象包括:
48 | - configmaps
49 | - persistentvolumeclaims
50 | - pods
51 | - replicationcontrollers
52 | - resourcequotas
53 | - services
54 | - services.loadbalancers
55 | - services.nodeports
56 | - secrets
57 |
58 | 更多的信息,我们将在后面的章节中再详细展开。
--------------------------------------------------------------------------------
/chapter10/manifests/resourcequota.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ResourceQuota
3 | metadata:
4 | name: pod-count
5 | namespace: default
6 | spec:
7 | hard:
8 | pods: "0"
--------------------------------------------------------------------------------
/chapter16/1.1-api_convention_optional_vs_required.md:
--------------------------------------------------------------------------------
1 | ## 导读
2 | 在阅读 `Kubernetes` API 或 其他项目的 API时,细心的读者会发现这些 API 中有些字段包含了 `// +optional` 标记(下面简称`optional`标记),比如 `Deployment` API中的 `Replicas` 字段就包含这个标记:
3 | ```
4 | // DeploymentSpec is the specification of the desired behavior of the Deployment.
5 | type DeploymentSpec struct {
6 | // Number of desired pods. This is a pointer to distinguish between explicit
7 | // zero and not specified. Defaults to 1.
8 | // +optional
9 | Replicas *int32 `json:"replicas,omitempty"`
10 | ...
11 | // Template describes the pods that will be created.
12 | Template v1.PodTemplateSpec `json:"template"`
13 | ...
14 | }
15 | ```
16 |
17 | 作为对比,`Deployment` API中的 `Template` 字段就没有`optional`标记,你知道他们的区别吗?
18 |
19 | 读者可能会说,这还不简单,包含`optional`标记的字段是可选的,反之就是必选的。事实确实如此,不过,对于API设计者还需要思考下面的问题:
20 | - `optional`标记除了提高可读性以外,还有什么作用?
21 | - 在设计API时,字段是否应该包含`omitempty` 标签?
22 | - 在设计API时,字段是否应该定义为指针类型?
23 |
24 | 笔者最初没有深入地了解`optional`标记,直到自己设计API时走了一些弯路才意识到这里面大有学问,更准确地说是前人经验的总结。本文站在API设计者角度来介绍应该如何处理字段的可选性。
25 |
26 | 本节内容由Kubernetes社区相关讨论、案例中总结而来,需要说明的是,在Kubernetes项目早期,关于API字段的可选性设计并没有统一的原则,这也导致了目前仍有部分API并不是十分规范。
27 |
28 | ## optional标记的作用
29 | `optional`标记本身是一个特殊格式的注释,其特殊性体现在两方面:
30 | - 该标记占用单行注释
31 | - 注释以空格开始,然后附加以“+”为前缀的标记(类似Golang语言中的build 标签)。
32 |
33 | 该标记除了提高代码可读性以外,主要用于生成OpenAPI文档以及相应的校验规则。比如`controller-gen` 工具就会根据这个标记生成CRD的校验规则。
34 |
35 | `optional`标记仅用于标记字段的可选性,除此之外,API设计者还需要了解一些字段设计的约定,或者说是经验之谈。
36 |
37 | ## 字段可选性约定
38 | 可选字段通常具备以下特征:
39 | - 注释中包含`optional`标记;
40 | - 字段类型通常为指针、map、slice;
41 | - 字段的`Tag`中通常包含`omitempty`标记;
42 |
43 | 必选字段通常具备以下特征:
44 | - 注释中没有`optional`标记;
45 | - 字段类型不是指针;
46 | - 字段的`Tag`中没有`omitempty`标记;
47 |
48 | ### 关于omitempty标记
49 | 在`optional`标记出现以前,Kubernetes的API中广泛依赖字段的`omitempty`标记来判断字段的可选性,拥有`omitempty`标记的被自动识别的可选字段,反之则为必选字段。现在慢慢过渡到使用`optional`标记来识别可选性。
50 |
51 | ### 如何区别空值和零值
52 | 对于下面的可选字段而言,如果用户设置字段为`0`(空值),由于该值等同于类型的零值,开发者无法区别出用户到底有没有设置。
53 | ```
54 | // +optional
55 | Foo int32 `json:"foo,omitempty"`
56 | ```
57 |
58 | 所以,对于可选字段,建议使用指针,如果指针为nil表示用户没有设置,反之则代表用户显式地设置了字段值。
59 |
60 | 除此之外,如果可选字段类型为自定义结构体类型,使用指针还可以简化JSON编码。参考下面的例子:
61 | ```
62 | type DummyStruct struct {
63 | // +optional
64 | Foo *int `json:"foo,omitempty"`
65 | }
66 |
67 | type MyStruct struct {
68 | // +optional
69 | Dummy DummyStruct `json:"dummy,omitempty"`
70 | }
71 | ```
72 | 尽管`Dummy`字段标签中包含`omitempty`标记,在将其JSON编码(json.Marshal)时,仍然为出现一个空的JSON键,如下所示
73 | ```
74 | {"dummy":{}}
75 | ```
76 | 如果API中包含大量这样的字段,则在JSON编码时会比较丑陋,而将其定义为指针类型可消除这个问题。
77 |
78 | 参考资料:
79 | - 《API Conventions》https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#optional-vs-required
--------------------------------------------------------------------------------
/chapter16/1.2-api_convention_condition.md:
--------------------------------------------------------------------------------
1 | ## 导读
2 | 绝大部分`Kubernetes`资源对象都包含`status.conditions`字段,用来表示资源状态,比如`deployment`资源中的`status.conditions`如下所示:
3 | ```
4 | conditions:
5 | - lastTransitionTime: "2020-12-15T01:52:53Z"
6 | lastUpdateTime: "2020-12-15T01:52:53Z"
7 | message: Deployment has minimum availability.
8 | reason: MinimumReplicasAvailable
9 | status: "True"
10 | type: Available
11 | - lastTransitionTime: "2020-12-15T01:52:39Z"
12 | lastUpdateTime: "2020-12-15T01:52:53Z"
13 | message: ReplicaSet "nginx-deployment-85b45874d9" has successfully progressed.
14 | reason: NewReplicaSetAvailable
15 | status: "True"
16 | type: Progressing
17 | ```
18 | 以上`conditions`的信息是很容易读懂的(后面还会详细解释),然而有个问题曾经令笔者非常困惑,那就是:
19 | - `conditions`和`status`到底有什么区别?
20 | - `conditions`的设计原则是什么?在设计API扩展时,该如何定义`conditions`?
21 |
22 | 本节会先从`conditions`的设计原则讲起,再介绍一些设计`conditions`时的一些经验总结。
23 |
24 | ## conditions设计原则
25 | `conditions`寄居于资源对象的`status`字段中,与`status`其他字段值一样都用来表示资源的状态。不过`conditions`的设计初衷是提供一种通用的数据结构表示资源的状态,它通常能够提供比`status`其他字段更详细的信息(比如状态切换时间、更新时间),`conditions`实际上是一种扩展机制,外部监控程序可以根据`conditions`无差别地监控各种资源的状态,而不必过分关注资源对象`status`中的其他信息。
26 |
27 | 通常`status.conditions`字段类型为切片,切片中的每个元素表示资源的某个状态,该状态由特定的控制器更新,比如`Deployment`控制器会更新`deployment`对象的`status.conditions`信息。`conditions`作为扩展机制,它还支持第三方控制器增加新的状态。通常`status.conditions`中的信息由控制器根据资源的`status`其他字段计算出来。
28 |
29 | ## condition字段内容
30 | 通常一个`condition`必须包含`type`(状态类型)和`status`(状态值)两个信息。在`Kubernetes` v1.19版之前,关于`condition`并没有统一的标准,导致众多API都自行定义了`condition`。比如:
31 | - Deployment使用的Condition为`type DeploymentCondition struct`;
32 | - Pod使用的Condition为`type PodCondition struct`;
33 |
34 | 庆幸的是,在`Kubernetes`v1.19版本社区提供了一个标准的`condition`类型定义,由于兼容性考虑,`Kubernetes`既有的API不一定能采用这一标准类型,但对于后续新增的API将会使用这一标准类型,并且笔者建议用户设计的CRD扩展也应使用这一标准类型。
35 |
36 | 标准的`condition`类型定义如下所示:
37 | ```golang
38 | type ConditionStatus string
39 |
40 | const (
41 | ConditionTrue ConditionStatus = "True"
42 | ConditionFalse ConditionStatus = "False"
43 | ConditionUnknown ConditionStatus = "Unknown"
44 | )
45 |
46 | type Condition struct {
47 | // 类型(使用驼峰风格),如”Available“。
48 | Type string
49 | // 状态(枚举值:”True“、”False“和”Unknown“)。
50 | Status ConditionStatus
51 | // 观察到的generation。
52 | // 如果ObservedGeneration 比metada.generation小,说明不是最新状态。
53 | // +optional
54 | ObservedGeneration int64
55 | // 上次变化时间
56 | LastTransitionTime Time
57 | // 状态变化原因(使用驼峰风格),如”NewReplicaSetAvailable“
58 | Reason string
59 | // 描述信息,如”Deployment has minimum availability“
60 | Message string `json:"message" protobuf:"bytes,6,opt,name=message"`
61 | }
62 | ```
63 |
64 | 标准的`condition`类型定义对`condition`的各个字段做了进一步约定。除了`ObservedGeneration`是可选的以外,其他字段都是必填的。
65 |
66 | ## condition设计约定
67 | 为了让`condition`起到最大的作用,需要遵守一定的设计约定:
68 |
69 | ### 约定一:condition定义要有明确的信息
70 | 每个`condition`需要传递一个用户关心的明确信息,用户读取到该信息不需要再根据资源的其他状态来揣测和计算。
71 |
72 | ### 约定二:condition需要保持兼容
73 | `condition`一旦被定义,它就成了API中的一部分,需要跟API一样提供稳定性保证。如果需要新的`condition`仍然可以增加(没有破坏兼容性),但既有的`condition`是否能够改变就需要根据API成熟度来决定,比如`stable`的API不可以改变,`alpha`的API可以改变。
74 |
75 | ## 约定三:condition需要控制器第一次处理资源时更新
76 | 控制器需要尽快地更新`condition`状态值(`condition.status`),即便该状态值为`Unknown`,这么做的好处是可以让其他组件了解到控制器正在`调谐`这个资源。
77 |
78 | 然而,并不是所有的控制器都能遵守这个约定,即控制器并不会报告特定的`condition`(此时该`condition`状态值可能为`Unknown`),可能该`condition`还无法确定,需要在下一次`调谐`时决定。此种情况下,外部组件无法读取到特定的`condition`,可以假设该`condition`为`Unknown`。
79 |
80 | ## 约定四:condition类型名需要确保可读性
81 | `condition`类型名要使用人类可读的名称,而且尽量避免出现双重否定的语境。例如,类型名`Ready`比使用`Failed`要好,因为`condition`状态为`False`时,`Failed = False`将出现双重否定,不如`Ready = False`容易理解。
82 |
83 | ## 约定五:condition不要定义成状态机
84 | `condition`需要描述当前资源的确定状态,而不是当前资源状态机中的状态。通俗地讲,`condition`类型需要使用形容词(如`Ready`)或过去动词(`Succeeded`),而不是使用当前运行时(如`Deploying`)。
--------------------------------------------------------------------------------
/chapter16/1.3-api_convention_event.md:
--------------------------------------------------------------------------------
1 | ## 导读
2 | 当设计API扩展及实现其控制器时,如何设计`events`和`status`?什么样的信息需要放到`status`中,什么样的信息需要放到`events`中?
3 |
4 | ## Event设计约定
5 | `event`的设计初衷是为`status`提供一个补充,与`status`不同的是,`event`可以提供历史信息。与`status`类似的是,`event`也由控制器维护。那么什么时候需要报告`event`?需要报告什么信息呢?
6 |
7 | 当控制器观测到资源的某个状态(正常状态或异常状态)时,如果需要用户或管理员注意,那就需要报告事件。
8 | 报告的事件,通常包含3个信息:
9 | - reason:事件名称
10 | - type:事件类型(或事件等级),可以为”Normal“或”Warning“
11 | - message: 事件详细描述信息
12 |
13 | 事件类型当前只有`Normal`和`Warning`可选,将来如有必要可以扩展出新的类型,比如`Critical`来表示更严重的信息。事件名称使用驼峰风格表示,名字尽量简短,但信息量也要充足,避免使用极简的名称,比如"Failed",至少也要使用”xxxFailed“,指出到底是什么出错了。
--------------------------------------------------------------------------------
/chapter19/1.2-mapping-ports-to-host.md:
--------------------------------------------------------------------------------
1 | 默认情况下,`kind`集群只映射控制面(control-plane)节点的一个端口(6443)到主机的随机端口,用于从主机访问`kube-apiserver`。
2 |
3 | 
4 |
5 | 上图展示了两个`kind`集群,集群`Cluster 1` 映射了`kube-apiserver`的端口到主机的`40515`端口,集群`Cluster 2`映射了`kube-apiserver`的端口到主机的`40516`端口,这样在主机上可以通过`40515`和`40516`端口分别访问这两个集群。
6 |
7 | 某些场景下,我们会在`Kind`集群中运行`NodePort`类型的`Service`,并希望在主机上直接能够访问。此时可以在启动`Kind`集群时通过`extraPortMappings`配置`额外`需要映射的端口。
8 |
9 | 接下来,通过一个简单的例子来演示其用法。
10 |
11 | ## 集群配置
12 | 例如,我们需要访问的服务`NodePort` 为`30000`,并且希望把这个端口映射到主机上的`30001`端口,那么可以准备如下配置:
13 | ```yaml
14 | apiVersion: kind.x-k8s.io/v1alpha4
15 | kind: Cluster
16 | nodes:
17 | - role: control-plane
18 | extraPortMappings:
19 | - containerPort: 30000
20 | hostPort: 30001
21 | listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0"
22 | protocol: tcp # Optional, defaults to tcp
23 | - role: worker
24 | ```
25 | 该配置将从`control-plane`的节点上映射容器端口`30000`到主机的`30001`端口。将配置保存在文件中,如`kind.config`。
26 |
27 | 接着使用`kind`命令行工具创建集群:
28 | ```bash
29 | # kind create cluster --name rainbow --config kind.config
30 | ```
31 | 此时可以通过`docker ps`命令查看到映射的端口:
32 | ``` bash
33 | # docker ps --format "table {{.ID}}\t{{.Names}}\t{{.Ports}}"
34 | CONTAINER ID NAMES PORTS
35 | 6c723f417eee rainbow-worker
36 | 09c63c3d283c rainbow-control-plane 127.0.0.1:43890->6443/tcp, 0.0.0.0:30001->30000/tcp
37 | ```
38 |
39 | ## 部署服务
40 | 在本例中,我们部署一个`nginx`应用:
41 | ```yaml
42 | apiVersion: apps/v1
43 | kind: Deployment
44 | metadata:
45 | name: nginx
46 | labels:
47 | app: nginx
48 | spec:
49 | replicas: 1
50 | selector:
51 | matchLabels:
52 | app: nginx
53 | template:
54 | metadata:
55 | labels:
56 | app: nginx
57 | spec:
58 | containers:
59 | - image: nginx
60 | name: nginx
61 | ports:
62 | - containerPort: 80
63 | ```
64 |
65 | 接着,创建一个`NodePort`类型的`Service`:
66 | ```
67 | apiVersion: v1
68 | kind: Service
69 | metadata:
70 | name: hostname-service
71 | spec:
72 | type: NodePort
73 | selector:
74 | app: nginx
75 | ports:
76 | - protocol: TCP
77 | port: 80
78 | targetPort: 80
79 | nodePort: 30000
80 | ```
81 |
82 | ## 访问服务
83 |
84 | 此时可以在主机上通过映射到主机的`30001`端口访问服务:
85 | ```bash
86 | # curl localhost:30001
87 |
88 |
89 |
90 | Welcome to nginx!
91 |
92 | ...
93 | ```
94 |
95 | 此外,也可以在主机外,通过`主机IP:端口`方式访问服务,例如使用浏览器访问:
96 | ```
97 | http://189.139.131.166:30001/
98 | ```
99 |
100 | ## 小结
101 | 上面的例子中,我们使用`extraPortMappings`映射了一个端口到主机,也可以同时映射多个端口到主机。同时还可以从`kind`集群中任意节点上映射端口到主机,对于`NodePort`类型的服务,无论从哪个结点上映射效果都是相同的。
--------------------------------------------------------------------------------
/chapter19/1.3-port-forward.md:
--------------------------------------------------------------------------------
1 | 集群中的`Service`(服务)默认为`ClusterIP`类型,该类型的`Service`只能在集群内部访问。有时需要对Service进行调试,在不改变`Service`配置的情况下,还可以借助`kubectl`提供的端口转发能力。
2 |
3 | 下面我们在一个`kind`集群中部署一个`ClusterIP`类型的`nginx`服务,然后借助`kubectl`的端口转发能力实现从主机上访问。
4 |
5 | ## 创建集群
6 |
7 | 首先使用常规的`kind create cluster`命令创建一个名字为`rainbow`的集群:
8 | ```bash
9 | # kind create cluster --name rainbow
10 | ```
11 |
12 | ## 部署服务
13 | 接着在集群中分别部署一个`nginx`的`Deployment`和`Service`,配置如下所示。
14 |
15 | `Deployment`的配置:
16 | ```yaml
17 | apiVersion: apps/v1
18 | kind: Deployment
19 | metadata:
20 | name: nginx
21 | labels:
22 | app: nginx
23 | spec:
24 | replicas: 1
25 | selector:
26 | matchLabels:
27 | app: nginx
28 | template:
29 | metadata:
30 | labels:
31 | app: nginx
32 | spec:
33 | containers:
34 | - image: nginx
35 | name: nginx
36 | ports:
37 | - containerPort: 80
38 | ```
39 |
40 | `Service`的配置:
41 | ```yaml
42 | apiVersion: v1
43 | kind: Service
44 | metadata:
45 | name: nginx
46 | spec:
47 | type: ClusterIP
48 | selector:
49 | app: nginx
50 | ports:
51 | - protocol: TCP
52 | port: 80
53 | targetPort: 80
54 | ```
55 | 该服务的端口为`80`,类型为`ClusterIP`,此时是无法在主机上直接访问的。
56 |
57 | ## 配置端口转发
58 | 接着使用`kubectl port-forward`命令启动端口转发:
59 | ```
60 | # kubectl port-forward --namespace default svc/nginx --address 0.0.0.0 3001:80
61 | Forwarding from 0.0.0.0:3001 -> 80
62 | ```
63 | 其参数解释如下:
64 | - `--namespace`:指定`Service`所在的namespace(默认为default);
65 | - `svc/nginx`:指定`Service`的类型和名字,也可以使用全称`service/nginx`;
66 | - `--address`:指定绑定主机的IP;
67 | - `3001:80`:指定监听主机上的`3001`端口,并将消息转发到服务的`80`口上;
68 |
69 | 该命令执行后不会自动退出,显示以上信息就表示成功地在本机和服务的容器之间建立了一条链接,命令终止执行时,该链接会自动中断。
70 |
71 | 此时访问本机的`3001`端口即可访问服务。例如访问`localhost` IP:
72 | ```
73 | # curl localhost:3001
74 |
75 |
76 |
77 | Welcome to nginx!
78 | Welcome to nginx!
79 | If you see this page, the nginx web server is successfully installed and
80 | working. Further configuration is required.
81 |
82 | ...
83 | ```
84 |
85 | 如果本机配置了外部IP,还可以通过浏览器访问,比如`http://x.x.x.x:3001`.
86 |
87 | ## 小结
88 | `kubectl`的`port-forward`本质上是在本地主机和集群中的`Pod`之间建立一个用于流量转发的连接,在上面的例子中,我们也可以直接指定`Pod`建立连接,比如:
89 | ```
90 | # kubectport-forward pod/nginx-7848d4b86f-c7fbr --address 0.0.0.0 8888:80
91 | ```
92 | 上面的例子中可以指定`Service`建立连接,是因为`kubectl`会根据`Service`查找到后端的`Pod`,并最终与Pod建立建立连接。类似的,除了`Service`,还可以指定`Deployment`,比如:
93 | ```
94 | # kubectl port-forward deployment/nginx 8888:80
95 | ```
96 |
97 | 更多的例子,有兴趣的读者可以查看命令的帮助手册:`kubectl port-forward --help`。
98 |
99 | ## 参考链接:
100 | - port-forward官方文档:https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/
101 | - port-forward实现原理(英文):https://prefetch.net/blog/2018/02/03/how-the-kubectl-port-forward-command-works/
102 | - port-forward实现原理(译文):https://vflong.github.io/sre/k8s/2020/03/15/how-the-kubectl-port-forward-command-works.html
--------------------------------------------------------------------------------
/chapter19/images/kind-default-port.drawio:
--------------------------------------------------------------------------------
1 | 5Vldb9sgFP01eewUjL/ymKTtKk2TNvWh6yOzqY2KTYZxk/TXD2r8gXHSVktWy1Uf6nu5XOCcE7jYM7jOdl852qTfWYzpzJnHuxm8nDkOmIe+/Kc8+8oThF7lSDiJdVDruCXPuO6pvSWJcWEECsaoIBvTGbE8x5EwfIhztjXDHhg1R92gBFuO2whR23tHYpFW3tAJWv8NJklajwz8RdWSoTpYr6RIUcy2HRe8msE1Z0xUT9lujakCr8al6nd9oLWZGMe5eEuHoLxf8Z/4bvmNhsnzH+cmzNAF0GmeEC31ivVsxb6GgLMyj7HKMp/B1TYlAt9uUKRat5J06UtFRqUF5KNOh7nAu4MTBc3ypW4wy7DgexmiO4QasL1pblv4XVf70g70TSDSlCdN5hYV+aCBeQ9Ir2P0Ciqo2FTifCA7heQpYAKOiVNjd4AC/gBQjfPkQDkWUGtaFgJz6QQWZnLpwoSpEJw94jWjjEtPznIZuXoglPZciJIkl2YkoZLJ4UoBSeSPdqkbMhLHaphBtZp6PgUTvsmEs7CZCIcUey4e4CgFC8cnWO+IYG3MJiNYODbB+hYPj+VvfIE2pMD8SdHRo0LmlBXAIcBOex4B1zN1Gy5s3YIBuNxzwRVYcM2/vPzN4NJ3XThd5QLYU64zwMVgdXAuLsIxSxeOTrqLTytdODrpArteS4VQF6ulSuxcAyd4IUeivnTnHvCmy06/mm7Yeq04OR87dhV3nB1/uuz0S8cRsONa7EQsF4jkA1v+ZHhw5r09zP/gwhHYFfwnoMEdHQ12AW+hH5WyHIo18DiPl+rtnMKUoqIgkUkM3hHxSwH2JfC0ea/xU8+Xu66xr41crqXbS9n33ca234u1NyjBsfUqsEeIXA8reYTfcKYKxBMsjgQGwwx3GPQGGKx9HFMkyJM53yFa9Qg/GJEraffTRW8/7dd71Tp1L6fzUrGXqHkT179J1okqHKxEkny074RtVEBxZMLh8DitaKuMrYQbTP9B1fY9a4qq/igRutC8nDSH+rtFeKiyPrEIvcCc8P8RoX3BTFkhJny4hQf2pu4HgNMcbtJsv79UhLVfseDVXw==
--------------------------------------------------------------------------------
/chapter19/images/kind-default-port.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RainbowMango/KubernetesDesign/422b1ab86b76cd6a2b004f5b5868398a9c32d023/chapter19/images/kind-default-port.png
--------------------------------------------------------------------------------