├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── OWNERS ├── README.md └── tutorials ├── images ├── deployment_initial.png ├── deployment_scale.png ├── deployment_service.png ├── deployment_update_1.png ├── deployment_update_2.png ├── deployment_update_done.png ├── deployment_update_initial.png ├── hpa.png ├── kubernetes_cluster.png └── labels.png ├── lab1-installation.md ├── lab2-application-and-service.md ├── lab3-manual-installtion.md ├── lab4-concepts.md └── resources ├── class.yaml ├── coredns.yaml ├── cronjob.yaml ├── daemonset.yaml ├── debian.yaml ├── debian_never_restart.yaml ├── deployment_nginx.yaml ├── deployment_php_cache.yaml ├── emptydir_pod.yaml ├── game.properties ├── job.yaml ├── kube-flannel.yaml ├── ns.yaml ├── pod.yaml ├── pod_configmap.yaml ├── pod_exceeds_quota.yaml ├── pod_health.yaml ├── pod_unhealth.yaml ├── pod_with_pvc.yaml ├── pv_hostpath.yaml ├── pvc.yaml ├── pvc_standard.yaml ├── quota.yaml └── ui.properties /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Is this a BUG REPORT or FEATURE REQUEST?**: 4 | 5 | > Uncomment only one, leave it on its own line: 6 | > 7 | > /kind bug 8 | > /kind feature 9 | 10 | **What happened**: 11 | 12 | **What you expected to happen**: 13 | 14 | **How to reproduce it (as minimally and precisely as possible)**: 15 | 16 | **Anything else we need to know?**: 17 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **What this PR does / why we need it**: 4 | 5 | Add your description 6 | 7 | **Which issue(s) this PR is related to** *(optional, link to 3rd issue(s))*: 8 | 9 | Fixes # 10 | 11 | Reference to # 12 | 13 | 14 | **Special notes for your reviewer**: 15 | 16 | /cc @your-reviewer 17 | 18 | 26 | 27 | **Release note**: 28 | 32 | 33 | ```release-note 34 | NONE 35 | ``` 36 | 37 | 47 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | approvers: 2 | - ddysher 3 | - bbbmj 4 | reviewers: 5 | - ddysher 6 | - bbbmj 7 | - gaocegege 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* 4 | 5 | - [Kubernetes 学习路径](#kubernetes-%E5%AD%A6%E4%B9%A0%E8%B7%AF%E5%BE%84) 6 | - [背景](#%E8%83%8C%E6%99%AF) 7 | - [学习路径](#%E5%AD%A6%E4%B9%A0%E8%B7%AF%E5%BE%84) 8 | - [第一阶段 炼气期(2-4 周,每周 3-5 小时)](#%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5-%E7%82%BC%E6%B0%94%E6%9C%9F2-4-%E5%91%A8%E6%AF%8F%E5%91%A8-3-5-%E5%B0%8F%E6%97%B6) 9 | - [目标](#%E7%9B%AE%E6%A0%87) 10 | - [路径](#%E8%B7%AF%E5%BE%84) 11 | - [心法](#%E5%BF%83%E6%B3%95) 12 | - [第二阶段 筑基期(4-6 周,每周 8-10 小时)](#%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5-%E7%AD%91%E5%9F%BA%E6%9C%9F4-6-%E5%91%A8%E6%AF%8F%E5%91%A8-8-10-%E5%B0%8F%E6%97%B6) 13 | - [目标](#%E7%9B%AE%E6%A0%87-1) 14 | - [路径](#%E8%B7%AF%E5%BE%84-1) 15 | - [心法](#%E5%BF%83%E6%B3%95-1) 16 | - [第三阶段 金丹期(2-4 周,每周 3-5 小时)](#%E7%AC%AC%E4%B8%89%E9%98%B6%E6%AE%B5-%E9%87%91%E4%B8%B9%E6%9C%9F2-4-%E5%91%A8%E6%AF%8F%E5%91%A8-3-5-%E5%B0%8F%E6%97%B6) 17 | - [目标](#%E7%9B%AE%E6%A0%87-2) 18 | - [路径](#%E8%B7%AF%E5%BE%84-2) 19 | - [心法](#%E5%BF%83%E6%B3%95-2) 20 | - [第四阶段 元婴期(4-6 周,每周 8-10 小时)](#%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5-%E5%85%83%E5%A9%B4%E6%9C%9F4-6-%E5%91%A8%E6%AF%8F%E5%91%A8-8-10-%E5%B0%8F%E6%97%B6) 21 | - [目标](#%E7%9B%AE%E6%A0%87-3) 22 | - [路径](#%E8%B7%AF%E5%BE%84-3) 23 | - [心法](#%E5%BF%83%E6%B3%95-3) 24 | - [第五阶段 化神期(3-5 周,每周 6-8 小时)](#%E7%AC%AC%E4%BA%94%E9%98%B6%E6%AE%B5-%E5%8C%96%E7%A5%9E%E6%9C%9F3-5-%E5%91%A8%E6%AF%8F%E5%91%A8-6-8-%E5%B0%8F%E6%97%B6) 25 | - [目标](#%E7%9B%AE%E6%A0%87-4) 26 | - [路径](#%E8%B7%AF%E5%BE%84-4) 27 | - [心法](#%E5%BF%83%E6%B3%95-4) 28 | - [第六阶段 练虚期(4-6 周,每周 8-10 小时)](#%E7%AC%AC%E5%85%AD%E9%98%B6%E6%AE%B5-%E7%BB%83%E8%99%9A%E6%9C%9F4-6-%E5%91%A8%E6%AF%8F%E5%91%A8-8-10-%E5%B0%8F%E6%97%B6) 29 | - [目标](#%E7%9B%AE%E6%A0%87-5) 30 | - [路径](#%E8%B7%AF%E5%BE%84-5) 31 | - [心法](#%E5%BF%83%E6%B3%95-5) 32 | - [第七阶段 大乘期(终身学习)](#%E7%AC%AC%E4%B8%83%E9%98%B6%E6%AE%B5-%E5%A4%A7%E4%B9%98%E6%9C%9F%E7%BB%88%E8%BA%AB%E5%AD%A6%E4%B9%A0) 33 | - [目标](#%E7%9B%AE%E6%A0%87-6) 34 | - [路径](#%E8%B7%AF%E5%BE%84-6) 35 | - [心法](#%E5%BF%83%E6%B3%95-6) 36 | - [许可协议](#%E8%AE%B8%E5%8F%AF%E5%8D%8F%E8%AE%AE) 37 | 38 | 39 | 40 | # Kubernetes 学习路径 41 | 42 | ## 背景 43 | 44 | **本文由 [才云科技(Caicloud)](https://caicloud.io/) 于 2019 年内部推出,现以开源的形式进行维护** 45 | 46 | 目前云计算行业对于 [Kubernetes](https://kubernetes.io/) 学习的需求日益增加,但市面上关于 Kubernetes 的资源良莠不齐,存在几个问题: 47 | 48 | - 官方文档缺少明确的"梯度",信息错综复杂 49 | - 资料较为分散,查找信息费时费力 50 | - Kubernetes 发展很快,书籍或者网上教程容易过时 51 | 52 | 本文档旨在为广大从业者提供一个 Kubernetes 学习路径,为大家提供一定的指引。我们最终的目标是让所有人剥茧抽丝般地了解 Kubernetes,不仅仅知道怎么用 Kubernetes,还知道 Kubernetes 各个功能是如何设计的。在学习路径后期,我们还可以很"自然"的联想到正确的设计思路。 53 | 54 | ## 学习路径 55 | 56 | **注意**: 57 | 58 | - 术语来自才云科技工程师 [gaocegege](https://github.com/gaocegege) 所著《[适合系统工程师的"机器学习"学习路径](https://github.com/caicloud/mlsys-ladder)》 59 | - 注意这是学习路径,不是一个教程! 60 | - 大多数概念都会给出官方链接,如果需要深入了解请自行查找! 61 | 62 | ### 第一阶段 炼气期(2-4 周,每周 3-5 小时) 63 | 64 | #### 目标 65 | 66 | - Kubernetes 的背景 67 | - 安装 Kubernetes 环境 68 | - Kubernetes 基本概念和使用方法 69 | 70 | #### 路径 71 | 72 | 学习任何系统的之前,了解其出现的背景和意义都是必不可少的,为什么会出现 Kubernetes?它解决了什么问题?有没有其他类似的系统?这里推荐阅读才云科技 CEO 张鑫在 2017 年文章《[从风口浪尖到十字路口,写在 Kubernetes 两周年之际](https://mp.weixin.qq.com/s/hrgXzt7YKVf6ZCFzJ-WTFA)》。 73 | 74 | 接下来,在了解 Kubernetes 系统本质之前,我们需要对 Kubernetes 有一个较为"感性"的认识,打消对 Kubernetes 的畏难情绪。这里,我们推荐使用 [minikube](https://github.com/kubernetes/minikube) 或 [kind](https://github.com/kubernetes-sigs/kind) 部署一个本地环境,然后开始部署一个"真实"的应用(minikube 安装需要使用科学上网,或使用[“国内版” minikube](https://yq.aliyun.com/articles/221687))。如果想一开始就挑战更高难度的安装方式(不推荐),可以使用 [kubeadm](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/) 或者手动部署所有组件。关于安装,可以参考文档 [lab1-installation](https://github.com/caicloud/kube-ladder/blob/master/tutorials/lab1-installation.md)。 75 | 76 | 在安装好环境之后,可以开始动手实践最基本的 Kubernetes 概念。在第一阶段,我们推荐熟练使用以下常用资源和概念:Pod、Node、Label、Event、Service、Configmap & Secret、Deployment、Namespace。相关学习可以参考文档 [lab2-application-and-service](https://github.com/caicloud/kube-ladder/blob/master/tutorials/lab2-application-and-service.md)。 77 | 78 | (可选)仅完成上述内容可能还不足以让我们非常熟悉 Kubernetes 的基本概念,下面列出其他可以参考的资料,大家也可以按照自己的方式去搜索相关的资料: 79 | - 官方 Tutorial:[Learn Kubernetes Basics](https://kubernetes.io/docs/tutorials/kubernetes-basics/) 80 | - 官方 Guestbook 样例:[Guestbook Example](https://kubernetes.io/docs/tutorials/stateless-application/guestbook/) 81 | - [Kubernetes Tutorial for Beginners](https://spacelift.io/blog/kubernetes-tutorial) 82 | 83 | #### 心法 84 | 85 |

🤨

86 | 87 | - 请反复加深对上面资源的操作熟练度。如果你是第一次接触 Kubernetes,或者仅了解过一点 Kubernetes 的知识,那么基(ken)本(ding)是不明白 Kubernetes 底层到底发生了什么。请不要心急,姑且把它当成一个黑盒工具即可 🛠。 88 | - 你可能会在网上看到更多的概念,如 PVC、Ingress、Priority 等。炼气阶段,请不要尝试学习过多的资源类型。Kubernetes 有非常多的概念和类似的资源,我们这里熟悉最核心的概念即可,否则易走火入魔 👻,切记。当我们打通任督二脉之时,所有的新概念都不过尔尔。 89 | 90 | ### 第二阶段 筑基期(4-6 周,每周 8-10 小时) 91 | 92 | #### 目标 93 | 94 | - Kubernetes 的基本架构 95 | - Kubernetes 容器调度的基本流程 96 | 97 | #### 路径 98 | 99 | 短暂接触 Kubernetes 概念之后,我们需要知其然并且知其所以然,因此在第二阶段我们开始学习 Kubernetes 基本架构。学习 Kubernetes 基本架构至少需要了解以下内容: 100 | 101 | - Master & Node 102 | - 知道什么是 Kubernetes Master,什么是 [Node](https://kubernetes.io/docs/concepts/architecture/nodes/) 103 | - 知道两者的关系,知道它们是如何通信的 104 | - Master 组件 105 | - API Server。Kubernetes 如何接收请求,又是如何将结果返回至客户端。 106 | - [Etcd](https://etcd.io/docs)。了解 Etcd 主要功能机制。 107 | - Controller Manager。Kubernetes 控制器是其架构中最为核心的一环,我们需要了解控制器的原理,List-Watch 的基本原理,知道 Kubernetes 默认情况下大致包含哪些类型的控制器。 108 | - [Scheduler](https://kubernetes.io/docs/concepts/scheduling/kube-scheduler/)。熟悉 Kubernetes 的调度流程是怎样的,调度器在整个调度流程中的角色。 109 | - Node 组件 110 | - Kubelet。知道 Kubelet 是如何接受调度请求并启动容器的。 111 | - Kube-proxy。了解 Kube-proxy 的作用,提供的能力是什么。 112 | - Container Runtime。了解都有哪些 Container Runtime,主要了解 [Docker](https://docs.docker.com/) 一些基本操作与实现原理。 113 | - 核心 Addons & Plugins 114 | - DNS。DNS 为集群的服务发现提供的支持,Kubernetes 1.13 开始默认使用 [CoreDNS](https://coredns.io/)。 115 | - Network Plugin。Kubernetes 多节点环境需要部署网络插件才可以使用,默认情况下使用 [flannel](https://github.com/coreos/flannel) 即可。 116 | 117 | 首先可以阅读书籍或网上博客,推荐阅读: 118 | 119 | - 官方文档:[Kubernetes Components](https://kubernetes.io/docs/concepts/overview/components/) 120 | - feisky 的博客:[Kubernetes 指南之核心原理](https://kubernetes.feisky.xyz/concepts/index) 121 | - kubectl run 的背后流程(难):[What happens when I type kubectl run?](https://github.com/jamiehannaford/what-happens-when-k8s) 122 | - kubectl run 的背后流程中文版:[kubectl 创建 Pod 背后到底发生了什么?](https://mp.weixin.qq.com/s/ctdvbasKE-vpLRxDJjwVMw) 123 | 124 | 接下来,推荐从 0 开始部署一个 Kubernetes 集群(不使用任何工具),来加深对各个组件的理解:解决部署中出现的各种问题,查看组件启动日志等等。如果时间有限,也可以尝试使用 kubeadm 等工具来部署集群。目前 Kubernetes 集群部署自动化已经做得比较完善,但出于学习目的,再次墙裂推荐手动安装。关于手动安装集群,可以参考文档 [lab3-manual-installtion](https://github.com/caicloud/kube-ladder/blob/master/tutorials/lab3-manual-installtion.md)。 125 | 126 | 在本阶段修炼结束后,我们至少应该对以下问题了如指掌:Kubernetes 组件是如何交互,来启动容器,并对外提供服务的? 127 | 128 | #### 心法 129 | 130 |

💪

131 | 132 | - 请不要死记硬背 Kubernetes 架构,要开动大脑 🧠去理解其背后设计的原因。 133 | - 筑基期是比较困难的一个阶段,如果感觉一头雾水,请不要气馁,你不是一个人。当你感觉进入了瓶颈时,可以尝试寻找身边的战友,总结一些你的问题并寻求答案 🍻。 134 | 135 | ### 第三阶段 金丹期(2-4 周,每周 3-5 小时) 136 | 137 | #### 目标 138 | 139 | - Kubernetes API 结构 140 | - 熟悉 Kubernetes 各个子系统 141 | - 熟悉 Kubernetes 排错相关内容 142 | 143 | #### 路径 144 | 145 | 当我们可以熟练使用 Kubernetes 的基本资源,并且对 Kubernetes 的基本架构有了充足了认识,接下来需要对 Kubernetes 的 API 结构和子系统要有一个比较全面的认识,同时也要开始更加系统的了解排查问题相关的内容。 146 | 147 | 要知道 [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/) 是其最引以为傲的设计,掌握 API 的关键是需要了解: 148 | 149 | - Kubernetes 的 API 是如何控制版本的 150 | - Kubernetes 的 API 是如何分组的 151 | - [Kubernetes 对象](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/)的表示方法与设计理念 152 | - [Kubernetes API 的访问控制](https://kubernetes.io/docs/reference/access-authn-authz/controlling-access/) 153 | 154 | 我们可以通过浏览 [Kubernetes API](https://github.com/kubernetes/api) 代码仓库来了解 Kubernetes API 组(Group)的信息。所有的资源定义代码都遵循 `//types.go` 的规范,例如上述 Deployment 资源是定义在 [apps group](https://github.com/kubernetes/api/tree/master/apps) 中。我们可以在 [apps/v1/types.go](https://github.com/kubernetes/api/blob/master/apps/v1/types.go) 中查找到关于 Deployment 的定义。 155 | 156 | 接下来,我们可以通过浏览 [Kubernetes/Community](https://github.com/kubernetes/community) 代码仓库来了解各个兴趣小组(SIG),"SIG" 是 Special Interest Group 的简称。Kubernetes 的演进都是通过 SIG 来推动的,因此了解 SIG 的分工对我们理解 Kubernetes 非常重要。一般来讲,一个 SIG 对应着一个 Kubernetes 子系统,例如,[sig-apps](https://github.com/kubernetes/community/tree/master/sig-apps) 负责决定是否引入新的 API,或者现有 API 是否需要升级等等。我们通过查看 Community 中带有 "sig-" 前缀的目录来了解 SIG 的工作内容、会议纪要等等。这里简单列举 Kubernetes 重要的子系统: 157 | 158 | - 架构 Architecture 159 | - 应用 Apps 160 | - 存储 Storage 161 | - 网络 Network 162 | - 权限 Auth 163 | - 节点 Node 164 | - 调度 Scheduling 165 | - 命令行 CLI 166 | - 多集群 MultiCluster 167 | - 云平台 CloudProvider 168 | - 扩展性 Scalability 169 | - 弹性伸缩 Autoscaling 170 | - 监控日志 Instrumentation 171 | 172 | (可选)细心的你可能会发现以 "wg-" 开头的目录,例如 "wg-resource-management"。"wg" 是 working group 的简称,是针对需要涉及多个 SIG 之间合作而展开的一种工作组,独立于任何 SIG 。例如 resource management 会涉及到 Node、Storage、Scheduling 等 SIGs。 173 | 174 | 作为一个承上启下的阶段,我们需要总结一些 Kubernetes 排错的能力,推荐阅读: 175 | 176 | - 官方文档:[Kubernetes Troubleshooting](https://kubernetes.io/docs/tasks/debug-application-cluster/troubleshooting/) 177 | - feisky 的博客:[Kubernetes 集群排错指南](https://feisky.gitbooks.io/kubernetes/troubleshooting/) 178 | 179 | #### 心法 180 | 181 |

🌱

182 | 183 | - 本阶段的重点是"耳听六路 :see_no_evil: 眼观八方 :hear_no_evil:"。前两个阶段接触到了很多 Kubernetes 的细节,本阶段需要对 Kubernetes 的全貌有个更加清晰的认识。很多内容可能看不太懂,但请在你的心中埋下一颗种子。 184 | 185 | ### 第四阶段 元婴期(4-6 周,每周 8-10 小时) 186 | 187 | #### 目标 188 | 189 | - 加深对各个资源的理解 190 | - 学习更多 Kubernetes 概念和知识 191 | 192 | #### 路径 193 | 194 | 不出意外,你现在对 Kubernetes 基本的资源已经很熟练了,对 Kubernetes 内部组件和它们的交互比较清晰,还对 Kubernetes 的 API 全貌和组织结构也有一定的了解。如果出了意外 🤔,请重新回顾你的学习过程。 195 | 196 | 本阶段,我们围绕几个关键方向来学习 Kubernetes,加深对其各个技术点的认识(这里只列出本阶段需要学习的核心能力,其他功能请量力而学): 197 | 198 | - 计算 199 | - [Pod Lifecycle & Healthcheck & RestartPolicy](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/) 200 | - [Pod Init-Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) 201 | - [Horizontal Pod Autoscaler (HPA)](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) 202 | - 更多控制器:[Job](https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/)、[CronJob](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/)、[Daemonset](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/)、[StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/)、[ReplicaSet](https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/) 203 | 204 | - 网络 205 | - [Service](https://kubernetes.io/docs/concepts/services-networking/service/) 实现,主要了解 [iptables](https://linux.die.net/man/8/iptables) 的实现(可选:[ipvs](https://kubernetes.io/blog/2018/07/09/ipvs-based-in-cluster-load-balancing-deep-dive/) 模式) 206 | - [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) 原理和常用的 [nginx ingress](https://github.com/kubernetes/ingress-nginx) 操作 207 | - [DNS](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/) 服务的原理,以及 [CoreDNS](https://coredns.io/) 方案 208 | 209 | - 存储 210 | - [Volume](https://kubernetes.io/docs/concepts/storage/volumes/) 以及底层存储类型 211 | - [PV/PVC](https://kubernetes.io/docs/concepts/storage/persistent-volumes/)、[StorageClass](https://kubernetes.io/docs/concepts/storage/storage-classes/) 212 | 213 | - 安全 214 | - [AuthN](https://kubernetes.io/docs/reference/access-authn-authz/authentication/), [AuthZ](https://kubernetes.io/docs/reference/access-authn-authz/authorization/) & [Admission Control](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/) 215 | - [NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/) 216 | - [ServiceAccount](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) 217 | - [Pod/Container SecurityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) 218 | - [Pod Security Policy (PSP)](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) 219 | 220 | - 调度 221 | - [Pod/Node Affinity & Anti-affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity) 222 | - [Taint & Toleration](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) 223 | - [Priority & Preemption](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/) 224 | - [Pod Disruption Budget](https://kubernetes.io/docs/concepts/workloads/pods/disruptions/) 225 | 226 | 总结一下,1)本阶段我们接触到更多的资源,包括:HPA、Job、CronJob、DaemonSet、StatefulSet、Ingress、Volume、PV/PVC、StorageClass、NetworkPolicy、PSP。2)更加深入了解已学资源的使用,例如 Init-Container、SecurityContext、Affinity 等。这些能力最终都会体现在各个资源的 API 上,例如 Affinity 是 Pod API 结构的一个字段,Scheduler 通过解析这个字段来进行合理的调度。未来如果有更多的能力,我们都可以通过解读不同资源的 API 字段来一探究竟。 227 | 228 | 本阶段相关学习可以参考文档 [lab4](https://github.com/caicloud/kube-ladder/blob/master/tutorials/lab4-concepts.md)。 229 | 230 | #### 心法 231 | 232 |

🧘‍♂️🧘‍♀️

233 | 234 | - 本阶段难度指数高,请合理调整你的心境。渡劫 :volcano: 成功后,你对 Kubernetes 的掌握将会进入一个新的台(tian)阶(keng)。 235 | - 学习相关功能时,可以回顾其所在 SIG,看看能不能发现有用的资源。 236 | 237 | ### 第五阶段 化神期(3-5 周,每周 6-8 小时) 238 | 239 | #### 目标 240 | 241 | - 学习更多 Kubernetes 集群层面的功能 242 | - 更加深入学习 Kubernetes 架构和组件能力 243 | 244 | #### 路径 245 | 246 | 当我们了解了 Kubernetes API 的设计理念,学习到了足够多的 API 资源及其使用方法之后,让我们再回顾一下 Kubernetes Master & Node 架构,以及它们运行的组件。事实上,Kubernetes 的每个组件都有很强的可配置性和能力,我们可以围绕 Kubernetes 的每个组件,来学习 Kubernetes 较为“隐晦”的功能。 247 | 248 | 推荐通过 Kubernetes Command Line Reference 来了解这些组件的配置: 249 | - [kube-api-server](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/) 250 | - [kube-scheduler](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-scheduler/) 251 | - [kube-controller-manager](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/) 252 | - [kube-proxy](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/) 253 | - [kubelet](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) 254 | - [kubectl](https://kubernetes.io/docs/reference/kubectl/kubectl/) 255 | 256 | 同时,Kubernetes 提供了 [FeatureGate](https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/) 来控制不同的特性开关,我们可以通过 FeatureGate 来了解 Kubernetes 的新特性。此外,为了方便开发者和配置管理,Kubernetes 把所有配置都挪到了相对应的 GitHub 代码仓库中,即: 257 | - https://github.com/kubernetes/kube-scheduler 258 | - https://github.com/kubernetes/kube-controller-manager 259 | - https://github.com/kubernetes/kube-proxy 260 | - https://github.com/kubernetes/kubelet 261 | - https://github.com/kubernetes/kubectl 262 | 263 | 当然,直接裸看配置有点硬核。为方便入手,下面我们简单总结部分功能(笼统的分为 Master 和 Node): 264 | 265 | *Master* 266 | 267 | - [Dynamic Admission Control](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/) 268 | - 动态准入控制(在练虚期阶段需要更加深入的了解) 269 | - 对应 API Server `--admission-control-config-file` 参数 270 | - [Advanced Auditing](https://kubernetes.io/docs/tasks/debug-application-cluster/audit) 271 | - 提供可动态配置的审计功能 272 | - 对应 API Server 带有 `--audit-` 前缀的参数 273 | - [Etcd Configuration](https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/configuration.md) 274 | - 提供各种与 Etcd 相关的配置,例如 Kubernetes event TTL 275 | - 对应 API Server 带有 `--etcd-` 前缀的参数 276 | - [All Admission Controllers](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/) 277 | - 列举所有 Kubernetes 所支持的 Admission Controllers,每个 Admission 都与 Kubernetes 特定的功能相关联 278 | - 对应 API Server `--enable-admission-plugins` 参数,该参数注释列举了所有的默认 Admission Controllers 279 | - [Garbage Collection](https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/) 280 | - 启用后,Kubernetes 会自动根据 `OwnerReferences` 来回收 API 资源 281 | - 对应 Controller-Manager `--enable-garbage-collector` 参数 282 | - Concurrent Sync Limiting 283 | - 避免过多的资源同步导致集群资源的消耗 284 | - 对应 Controller-Manager 带有 `--concurrent` 前缀的参数 285 | - All Controllers 286 | - 列举所有 Kubernetes 所支持的 Controllers,每个 Controller 都与 Kubernetes 特定的功能相关联 287 | - 对应 Controller-Manager `--controllers`,该参数注释列举了所有的默认 Controllers 288 | 289 | 其它值得注意的参数包括: 290 | - API-Server `--max-requests-inflight`, `--min-request-timeout` 291 | - API-Server `--watch-cache`, `--watch-cache-sizes` 292 | - Controller-Manager `--node-eviction-rate` 293 | - Controller-Manager `--pod-eviction-timeout` 294 | - Controller-Manager `--terminated-pod-gc-threshold` 295 | - Controller-Manager `--pv-recycler-minimum-timeout-*` 296 | 297 | *Node* 298 | 299 | - [Kubelet Eviction](https://kubernetes.io/docs/tasks/administer-cluster/out-of-resource/) 300 | - 当节点资源不足时,Kubernetes 通过驱逐 Pods 的方式确保节点的稳定性 301 | - 对应 Kubelet 带有 `--eviction-` 前缀的参数,例如 `--eviction-hard` 302 | - [Image GC](https://kubernetes.io/docs/concepts/cluster-administration/kubelet-garbage-collection/) 303 | - 清理容器镜像占用的磁盘空间 304 | - 对应 Kubelet 带有 `--image-gc-` 前缀的参数,以及 `--minimum-image-ttl-duration` 等参数 305 | - [Resource Reserve](https://kubernetes.io/docs/tasks/administer-cluster/reserve-compute-resources/) 306 | - 为系统资源预留一定的资源,确保节点的稳定性 307 | - 对应 Kubelet `--kube-reserved`、`--kube-reserved-cgroup` 等参数 308 | - [CPU Manager](https://kubernetes.io/docs/tasks/administer-cluster/cpu-management-policies/) 309 | - 提供更多的 CPU 管理能力,例如静态 CPU 亲和性 310 | - 对应 Kubelet `--cpu-manager-*` 前缀的参数 311 | - [Storage Limit](https://kubernetes.io/docs/concepts/storage/storage-limits) 312 | - 避免节点过度挂载数据卷 313 | - 对应 FeatureGate `AttachVolumeLimit` 314 | 315 | 其它值得注意的参数包括: 316 | - Kubelet & Kubeproxy `--hostname-override` 317 | - Kubelet `--cgroups-per-qos` 318 | - Kubelet `--fail-swap-on` 319 | - Kubelet `--host-*` 320 | - Kubelet `--max-pods`, `--pods-per-core` 321 | - Kubelet `--resolv-conf` 322 | - Kubeproxy `--nodeport-addresses` 323 | 324 | #### 心法 325 | 326 |

😇

327 | 328 | - 通过组件配置学习 Kubernetes 功能是我们需要具备的一个常规能力,或许比较枯燥,但对我们的修炼大有裨益。 329 | - 如果你对 Kubernetes “无穷无尽”的功能感到有点迷茫,这是一个很正常的现象。除非是深度参与 Kubernetes 的开发,否则一定会有很多遗漏的地方。我们只要保持两个基本点不动摇:1. 懂 Kubernetes 架构和最核心的能力;2. 懂得怎么快速定位我们需要的能力。关于第二点,我们将在大乘期介绍,stay tuned! 330 | 331 | ### 第六阶段 练虚期(4-6 周,每周 8-10 小时) 332 | 333 | #### 目标 334 | 335 | - 对 Kubernetes 的扩展机制了如指掌 336 | - 可以编写 Kubernetes 控制器,能够基于扩展机制灵活地二次开发 337 | 338 | #### 路径 339 | 340 | 本阶段我们可以开始了解 Kubernetes [各种扩展机制](https://kubernetes.io/docs/concepts/extend-kubernetes/extend-cluster/)。如果说 Kubernetes 的 API 和架构设计是其重要的基石,那么扩展机制使得 Kubernetes 在各个生态领域开花结果。下面我们尝试列举出所有的扩展方式,每一种扩展都有其优势和局限性,请自行思考。注意这里提到的扩展机制指的是架构上的扩展,而非功能层面的扩展,例如 Pod 支持各种 Probe 来进行健康检查,包括自定义,这里我们不归为扩展机制的能力。 341 | 342 | *API 资源扩展能力* 343 | 344 | - [Annotation](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/):保存少量非结构化第三方数据 345 | - [Finalizer](https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#finalizers):资源删除时,用户调用外部系统的钩子 346 | - [CustomResourceDefinition](https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/):自定义 Kubernetes API 347 | - [API Aggregation](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/):多个 API Server 聚合,适用于较大量的 API 定制 348 | 349 | 学习 API 资源扩展的一个重要方式是创建一个扩展资源,或者编写一个自己的控制器。强烈推荐自行编写一个控制器,这里列出几个常见的工具: 350 | 351 | - [kubebuilder](https://book.kubebuilder.io/):来自 Kubernetes 官方的 API 扩展项目 352 | - [sample-controller](https://github.com/kubernetes/sample-controller):来自 Kubernetes 官方的一个样例 353 | - [operator-sdk](https://github.com/operator-framework/operator-sdk):来自红帽的一个 operator 库 354 | - [shell-operator](https://github.com/flant/shell-operator):适合运维开发使用的 shell operator 库 355 | - [meta-controller](https://metacontroller.app/):来自 Google 的一个更加"傻瓜"式编写控制器的库 356 | 357 | *API 访问扩展能力* 358 | 359 | - [认证 Webhook](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication):用户认证时,调用外部服务,仅支持静态配置 360 | - [鉴权 Webhook](https://kubernetes.io/docs/reference/access-authn-authz/webhook/):用户鉴权时,调用外部服务,仅支持静态配置 361 | - [动态访问控制 Webhook](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/):请求访问控制时,调用外部服务,支持动态增加外部服务 362 | 363 | Kubernetes API 访问扩展主要是通过 Webhook 来实现。注意只有访问控制支持动态增加外部服务,认证鉴权的外部服务在启动 API Server 的时候就注册完毕,无法在后续增加,主要原因是动态增加外部认证鉴权服务,带来的安全风险过大。 364 | 365 | *调度器扩展能力* 366 | 367 | - [扩展接口(Scheduler Extender)](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/scheduling/scheduler_extender.md):类似 Webhook,调用外部服务进行调度决策 368 | - [多调度器](https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/):支持在 Kubernetes 运行多个调度器调度不同作业 369 | - [调度器框架](https://kubernetes.io/docs/concepts/configuration/scheduling-framework/):定义一套 Go API,使用户无需 fork Kubernetes Scheduler 代码即可完成“代码级”的定制 370 | 371 | 针对简单场景,我们可以直接使用 Scheduler Extender 即可,例如按 GPU 型号调度。复杂调度场景可以使用多调度器或调度器框架,例如基于流图的调度器 [poseidon](https://kubernetes.io/docs/concepts/extend-kubernetes/poseidon-firmament-alternate-scheduler/),批处理调取器 [kube-batch](https://github.com/kubernetes-sigs/kube-batch) 等。一般而言,使用 Extender 即可满足大多数场景。 372 | 373 | *网络扩展能力* 374 | 375 | - [网络插件 CNI](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/):使用 CNI 插件,可以选择任何我们需要的[网络方案](https://kubernetes.io/docs/concepts/cluster-administration/networking/) 376 | - [自定义 Ingress 控制器](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/):Ingress 定义了一套 API 接口,我们可以选择任意实现 377 | - [自定义 NetworkPolicy 控制器](https://kubernetes.io/docs/concepts/services-networking/network-policies/):同上,可选实现包括 [Calico](https://kubernetes.io/docs/tasks/administer-cluster/network-policy-provider/calico-network-policy/)、[Cilium](https://kubernetes.io/docs/tasks/administer-cluster/network-policy-provider/cilium-network-policy/) 等 378 | - [自定义 DNS 控制器](https://github.com/kubernetes/dns/blob/master/docs/specification.md):Kubernetes 定义了一套 DNS 规范,我们可以选择任意实现 379 | 380 | 网络插件 CNI 是容器网络标准,Kubernetes 提供了良好的支持,常用插件包括 flannel、Calico 等等。对于 Ingress、NetworkPolicy、DNS,相信到目前为止大家应该可以理解,其本质上是 Kubernetes 定义的一套 API,底层实现可插拔,用户可以有自己的选择。 381 | 382 | *存储扩展能力* 383 | 384 | - [FlexVolume](https://kubernetes.io/docs/concepts/storage/volumes/#flexVolume):Kubernetes 提供的一种动态对接存储方案,支持用户自定义存储后端 385 | - [存储插件 CSI](https://kubernetes-csi.github.io):使用 CSI 插件,可以选择任何我们需要的存储方案 386 | 387 | FlexVolume 是 Kubernetes 自带的对接外部存储的方案,用户编写少量的代码即可加入自定义存储后端,适用于简单场景。存储插件 CSI 是容器网络标准,Kubernetes 提供了良好的支持,同时为方便第三方实现,还提供了一整套 SDK 解决方案。所有底层存储相关的能力都与 CSI 密切相关。 388 | 389 | *运行时扩展能力* 390 | 391 | - [运行时接口 CRI](https://kubernetes.io/docs/setup/production-environment/container-runtimes/):使用 CRI 插件,可以选择任何我们需要的运行时方案 392 | 393 | 运行时接口 CRI 是 Kubernetes 提出,为解决支持多种运行时而提供的方案。任何运行时,只需实现 CRI 相关的接口,即可接入 Kubernetes 中,包括 Docker、Containerd、gVisor、Kata 等。 394 | 395 | *特殊硬件或资源扩展能力* 396 | 397 | - [扩展资源 Extended Resource](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#extended-resources):通过 Kubernetes 原生 API 方式支持添加自定义资源 398 | - [设备插件 Device Plugin](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/):使用 Device Plugin 插件,可以对接任何我们需要的硬件 399 | 400 | 对于简单场景,例如静态汇报资源数量,可以直接使用 Extended Resource 扩展 Kubernetes 所支持的硬件。Device Plugin 的核心是自动接入各种特殊硬件如 Nvidia、Infiniband、FPGA 等。在资源汇报层面 Device Plugin 目前也使用了 Extended Resource 的能力,但由于 Extended Resource 的局限性,Device Plugin 未来也可以与其他 API 对接。目前使用最多的 Device Plugin 主要是 Nvidia 的 [GPU device plugin](https://github.com/NVIDIA/k8s-device-plugin)。 401 | 402 | *监控扩展能力* 403 | 404 | - [自定义监控](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-metrics-apis):支持使用自定义监控组件如 Prometheus 提供监控指标 405 | 406 | 自定义监控包括 Custom Metrics 和 External Metrics,例如 [Prometheus adaptor](https://github.com/DirectXMan12/k8s-prometheus-adapter)。 407 | 408 | *云供应商扩展能力* 409 | 410 | - [云控制器 Cloud Controller Manager](https://kubernetes.io/docs/concepts/architecture/cloud-controller/):支持可插拔云服务提供商 411 | 412 | 云扩展能力的目标是使各个云供应商可以在不改变 Kubernetes 源码的情况下,接入其服务。每个云供应商都有[独立的项目](https://github.com/kubernetes?utf8=%E2%9C%93&q=cloud-provider&type=&language=)。 413 | 414 | *命令行插件* 415 | 416 | - [Kubectl Plugin](https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/):kubectl plugin 支持扩展 kubectl 子命令,与 API 扩展能力结合可以提供近乎原生的使用方法。 417 | 418 | #### 心法 419 | 420 |

:godmode:

421 | 422 | - 推荐实现一个端到端的 Kubernetes 控制器,可以对整个 Kubernetes 的二次开发有更加深入的了解。此外,针对所有的扩展能力,建议先建立一个全面的认识,再根据需要深入某一项能力。 423 | - 我们除了通过用户手册来学习上面的技术,也可多参考 Kubernetes 的花式设计文档,主要是 [Design Proposals](https://github.com/kubernetes/community/tree/master/contributors/design-proposals)、[KEPs](https://github.com/kubernetes/enhancements/tree/master/keps)。 424 | 425 | ### 第七阶段 大乘期(终身学习) 426 | 427 | #### 目标 428 | 429 | - 了解 Kubernetes 生态项目 430 | - 跟踪 Kubernetes 社区发展 431 | - 跟踪 CNCF 社区发展 432 | 433 | #### 路径 434 | 435 | 目前为止,我们学习了很多 Kubernetes 的概念,但也只是其最重要的部分。在本阶段,我们需要专注以下几个问题: 436 | 437 | - 如何跟进 Kubernetes 的新功能,以及现有功能的更多细节? 438 | - 如何了解 Kubernetes 整个生态环境的发展? 439 | 440 | 首先,让我们一起来学习几个重要的项目。围绕 Kubernetes 的生态环境建设是其成为容器标准的关键。 441 | 442 | - [Helm](https://github.com/helm/helm):作为 Kubernetes 生态里的 brew、dnf、dpkg,Helm 为 Kubernetes 提供了包管理能力,方便用户快速部署安装各种服务。 443 | - [Harbor](https://github.com/goharbor/harbor):Harbor 与 Kubernetes 无直接关系,但作为云原生环境下最常用的镜像仓库解决方案,了解 Harbor 十分重要。 444 | - [Prometheus](https://prometheus.io/):Prometheus 是云原生环境下最重要的监控组件。 445 | - [Istio](https://istio.io/):Istio 是服务网格的关键项目,但较为复杂,可以尝试简单了解。 446 | 447 | 以上,我们仅列出了极少量的重要项目,Kubernetes 周边的项目十分之多,令人咂舌 😱。因此大乘期的你,需要开始持续跟踪 Kubernetes 及其生态的发展,甚至可以推动其发展,接下来我们列举一些靠谱资源: 448 | 449 | *GitHub 仓库* 450 | 451 | - [Kubernetes Enhancement](https://github.com/kubernetes/enhancements/):关注新特性的讨论 452 | - [Kubernetes Community](https://github.com/kubernetes/community):关注社区组织情况 453 | - [CNCF TOC](https://github.com/cncf/toc/):关注 CNCF 进展,各种新项目讨论等 454 | - [Awesome Kubernetes](https://github.com/ramitsurana/awesome-kubernetes):Kubernetes 项目之学不动系列 455 | 456 | 关注 GitHub 仓库可以让你了解最一手的进展,但是信息量一般较大,讨论很多难度也比较大。不过对于大乘期的你来讲,应该不是问题 😉。另外,这里还包含很多 Kubernetes 系统内部的设计,例如调度器的优化方案、资源垃圾回收方案等,值得了解和学习。 457 | 458 | *Twitter 账号* 459 | 460 | 下面推荐几个 Kubernetes 项目的核心人员。大牛都喜欢用 Twitter 交(si)流(bi),可以关注一波。感兴趣的话题可以去交流,大牛都十分耐撕(nice。 461 | 462 | - [Tim Hockin](https://twitter.com/thockin) 463 | - [Clayton Coleman](https://twitter.com/smarterclayton) 464 | - [Daniel Smith](https://twitter.com/originalavalamp) 465 | - [Brian Grant](https://twitter.com/bgrant0607) 466 | - [Vishnu Kannan](https://twitter.com/vishnukanan) 467 | - [Saad Ali](https://twitter.com/the_saad_ali) 468 | - [Kelsey Hightower](https://twitter.com/kelseyhightower) 469 | - [Joe Beda](https://twitter.com/jbeda) 470 | - [Brendan Burns](https://twitter.com/brendandburns) 471 | - [Michelle Noorali](https://twitter.com/michellenoorali) 472 | 473 | 除此之外,Twitter 上还有不少项目和其他 Weekly 性质的 Twitter,推荐几个账号关注: 474 | 475 | - [Kube Weekly](https://twitter.com/kubeweekly) 476 | - [Kube List](https://twitter.com/readkubelist) 477 | 478 | Twitter 会根据你的喜好推荐其他相关内容,接下来就自由发挥。 479 | 480 | *Blog 账号* 481 | 482 | - [Kubernetes Blog](https://kubernetes.io/blog/) 483 | - [CNCF Blog](https://www.cncf.io/category/blog/) 484 | - [Caicloud Blog](https://caicloud.io/blog) 485 | 486 | 可以关注的优秀 Blog 很多,这里就不一一列举。 487 | 488 | #### 心法 489 | 490 |

🐲

491 |

请坚持学习!送上一句黑鸡汤:

492 |

"The last thing you want is to look back on your life and wonder... if only." 493 |

494 | 495 | ## 许可协议 496 | 497 | - 本文遵守[创作共享 CC BY-NC-SA 3.0 协议](https://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 498 | - 商业目的转载,请联系 499 | - 如有任何版权问题,请联系 500 | -------------------------------------------------------------------------------- /tutorials/images/deployment_initial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caicloud/kube-ladder/7fb40dcfc4f863b473b1e110158875de44550595/tutorials/images/deployment_initial.png -------------------------------------------------------------------------------- /tutorials/images/deployment_scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caicloud/kube-ladder/7fb40dcfc4f863b473b1e110158875de44550595/tutorials/images/deployment_scale.png -------------------------------------------------------------------------------- /tutorials/images/deployment_service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caicloud/kube-ladder/7fb40dcfc4f863b473b1e110158875de44550595/tutorials/images/deployment_service.png -------------------------------------------------------------------------------- /tutorials/images/deployment_update_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caicloud/kube-ladder/7fb40dcfc4f863b473b1e110158875de44550595/tutorials/images/deployment_update_1.png -------------------------------------------------------------------------------- /tutorials/images/deployment_update_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caicloud/kube-ladder/7fb40dcfc4f863b473b1e110158875de44550595/tutorials/images/deployment_update_2.png -------------------------------------------------------------------------------- /tutorials/images/deployment_update_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caicloud/kube-ladder/7fb40dcfc4f863b473b1e110158875de44550595/tutorials/images/deployment_update_done.png -------------------------------------------------------------------------------- /tutorials/images/deployment_update_initial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caicloud/kube-ladder/7fb40dcfc4f863b473b1e110158875de44550595/tutorials/images/deployment_update_initial.png -------------------------------------------------------------------------------- /tutorials/images/hpa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caicloud/kube-ladder/7fb40dcfc4f863b473b1e110158875de44550595/tutorials/images/hpa.png -------------------------------------------------------------------------------- /tutorials/images/kubernetes_cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caicloud/kube-ladder/7fb40dcfc4f863b473b1e110158875de44550595/tutorials/images/kubernetes_cluster.png -------------------------------------------------------------------------------- /tutorials/images/labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caicloud/kube-ladder/7fb40dcfc4f863b473b1e110158875de44550595/tutorials/images/labels.png -------------------------------------------------------------------------------- /tutorials/lab1-installation.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* 4 | 5 | - [单机部署](#%E5%8D%95%E6%9C%BA%E9%83%A8%E7%BD%B2) 6 | - [安装 kubectl](#%E5%AE%89%E8%A3%85-kubectl) 7 | - [使用 Minikube 部署 Kubernetes](#%E4%BD%BF%E7%94%A8-minikube-%E9%83%A8%E7%BD%B2-kubernetes) 8 | - [安装](#%E5%AE%89%E8%A3%85) 9 | - [验证](#%E9%AA%8C%E8%AF%81) 10 | - [使用 Kind 部署 Kubernetes](#%E4%BD%BF%E7%94%A8-kind-%E9%83%A8%E7%BD%B2-kubernetes) 11 | - [安装](#%E5%AE%89%E8%A3%85-1) 12 | - [验证](#%E9%AA%8C%E8%AF%81-1) 13 | - [其它开源安装工具](#%E5%85%B6%E5%AE%83%E5%BC%80%E6%BA%90%E5%AE%89%E8%A3%85%E5%B7%A5%E5%85%B7) 14 | 15 | 16 | 17 | # 单机部署 18 | 19 | ## 安装 kubectl 20 | 21 | Kubectl 是 Kubernetes 自带的命令行工具,可以用它直接操作 Kubernetes。 22 | 23 | macOS,执行: 24 | 25 | ```bash 26 | # using brew https://brew.sh/ 27 | brew install kubernetes-cli 28 | ``` 29 | 30 | Linux,执行: 31 | 32 | ```bash 33 | curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl 34 | && chmod +x kubectl && sudo mv kubectl /usr/local/bin/ 35 | ``` 36 | 37 | Windows,执行: 38 | 39 | ```bash 40 | curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/windows/amd64/kubectl.exe 41 | ``` 42 | 43 | ## 使用 Minikube 部署 Kubernetes 44 | 45 | [Minikube](https://github.com/kubernetes/minikube) 用于本地部署 kubernetes 集群,支持 macOS,Linux,和 Windows。 46 | 47 | **注意**:**科学上网**是必须的,否则 minikube iso 镜像文件,gcr.io 的 Docker 镜像等将无法下载。 48 | 49 | ### 安装 50 | 51 | **下载依赖**: 52 | 53 | * *macOS 10.12 (Sierra)* 54 | * 要求安装 hypervisor,比如 [hyperkit](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#hyperkit-driver) (推荐)或 [VirtualBox](https://www.virtualbox.org/wiki/Downloads) 55 | * 使用 [brew](https://brew.sh/) : `brew cask install minikube` 56 | * 或者使用 curl: `curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-amd64 && sudo install minikube-darwin-amd64 /usr/local/bin/minikube` 57 | 58 | * *Windows 10* 59 | * 要求安装 hypervisor,比如 [VirtualBox](https://www.virtualbox.org/wiki/Downloads) (推荐)或 [HyperV](https://docs.docker.com/machine/drivers/hyper-v/) 60 | * BIOS 中必须开启 VT-x/AMD-v virtualization 61 | * 使用 [chocolatey](https://chocolatey.org/) `choco install minikube` 62 | * 或者通过链接下载: Download and run the [installer](https://storage.googleapis.com/minikube/releases/latest/minikube-installer.exe) 63 | 64 | * *Linux* 65 | * 要求安装 [kvm2 driver](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#kvm2-driver) (推荐)或 [VirtualBox](https://www.virtualbox.org/wiki/Downloads) 66 | * BIOS 中必须开启 VT-x/AMD-v virtualization 67 | * 使用 curl: `curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 && sudo install minikube-linux-amd64 /usr/local/bin/minikube` 68 | 69 | **确认你的 minikube 至少是 v1.2.0**: 70 | 71 | ```sh 72 | $ minikube version 73 | minikube version: v1.2.0 74 | ``` 75 | 76 | **启动 Minikube**: 77 | 78 | **注意**: 这里我们使用的是 VirtualBox,如果你用的其它,可能会需要另外的配置,请按照上一节 👆 的链接查找。 79 | 80 | ```sh 81 | $ minikube start 82 | 😄 minikube v1.2.0 on darwin (amd64) 83 | 🔥 Creating virtualbox VM (CPUs=2, Memory=2048MB, Disk=20000MB) ... 84 | 🐳 Configuring environment for Kubernetes v1.15.0 on Docker 18.09.6 85 | 💾 Downloading kubeadm v1.15.0 86 | 💾 Downloading kubelet v1.15.0 87 | 🚜 Pulling images ... 88 | 🚀 Launching Kubernetes ... 89 | ⌛ Verifying: apiserver proxy etcd scheduler controller dns 90 | 🏄 Done! kubectl is now configured to use "minikube" 91 | ``` 92 | 93 | ### 验证 94 | 95 | 执行下面的命令: 96 | 97 | ```sh 98 | $ kubectl get nodes 99 | NAME STATUS ROLES AGE VERSION 100 | minikube Ready master 4m5s v1.15.0 101 | ``` 102 | 103 | 若输出正常,则表示创建成功。 104 | 105 | ## 使用 Kind 部署 Kubernetes 106 | 107 | [Kind](https://github.com/kubernetes-sigs/kind) 是另一个 Kubernetes 集群部署工具,通过 Docker 容器 "nodes" 完成部署。 108 | 109 | **注意**: 在这之前,你必须安装 [go](https://golang.org/) 和 [docker](https://www.docker.com/),并且 go 的版本至少是 1.12.6。 110 | 111 | ### 安装 112 | 113 | ```sh 114 | $ GO111MODULE="on" go get sigs.k8s.io/kind && kind create cluster 115 | ... 116 | Creating cluster "kind" ... 117 | ✓ Ensuring node image (kindest/node:v1.15.0) 🖼 118 | ✓ Preparing nodes 📦 119 | ✓ Creating kubeadm config 📜 120 | ✓ Starting control-plane 🕹️ 121 | ✓ Installing CNI 🔌 122 | ✓ Installing StorageClass 💾 123 | Cluster creation complete. You can now use the cluster with: 124 | 125 | export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" 126 | kubectl cluster-info 127 | ``` 128 | 129 | **注意**: 请务必执行输出中的命令,以配置 kubeconfig。 130 | 131 | ### 验证 132 | 133 | 执行下面的命令: 134 | 135 | ```sh 136 | $ kubectl get nodes 137 | NAME STATUS ROLES AGE VERSION 138 | kind-control-plane Ready master 2m54s v1.15.0 139 | ``` 140 | 141 | ## 其它开源安装工具 142 | 143 | - https://github.com/bsycorp/kind 144 | - https://github.com/ubuntu/microk8s 145 | - https://github.com/kinvolk/kube-spawn 146 | - https://github.com/danderson/virtuakube 147 | - https://github.com/kubernetes-sigs/kubeadm-dind-cluster 148 | -------------------------------------------------------------------------------- /tutorials/lab2-application-and-service.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | - [Introduction](#introduction) 6 | - [Kubectl command line](#kubectl-command-line) 7 | - [kubectl version](#kubectl-version) 8 | - [kubectl cluster-info](#kubectl-cluster-info) 9 | - [kubectl -h](#kubectl--h) 10 | - [kubectl command format](#kubectl-command-format) 11 | - [kubectl config file](#kubectl-config-file) 12 | - [Refereneces](#refereneces) 13 | - [Kubernetes node](#kubernetes-node) 14 | - [Node information](#node-information) 15 | - [Node details](#node-details) 16 | - [Readings](#readings) 17 | - [Kubernetes Namespace](#kubernetes-namespace) 18 | - [Namespaces](#namespaces) 19 | - [Readings](#readings-1) 20 | - [Kubernetes Pod & Deployment](#kubernetes-pod--deployment) 21 | - [Create Deployment](#create-deployment) 22 | - [Get Deployment](#get-deployment) 23 | - [Get Pods](#get-pods) 24 | - [Get Pod Logs](#get-pod-logs) 25 | - [Execute command in Pod](#execute-command-in-pod) 26 | - [Readings](#readings-2) 27 | - [Kubernetes Service](#kubernetes-service) 28 | - [Create service](#create-service) 29 | - [Get service](#get-service) 30 | - [Query service](#query-service) 31 | - [NodePort service](#nodeport-service) 32 | - [Readings](#readings-3) 33 | - [Kubernetes Label](#kubernetes-label) 34 | - [View selector & label](#view-selector--label) 35 | - [Label operations](#label-operations) 36 | - [Readings](#readings-4) 37 | - [Kubernetes Deployment Operations](#kubernetes-deployment-operations) 38 | - [Scale up using kubectl](#scale-up-using-kubectl) 39 | - [View service](#view-service) 40 | - [Scale down using kubectl](#scale-down-using-kubectl) 41 | - [Update deployment](#update-deployment) 42 | - [Update via setting image](#update-via-setting-image) 43 | - [Deployment rollout](#deployment-rollout) 44 | - [Kubernetes Yaml/Json File](#kubernetes-yamljson-file) 45 | - [Get resource yaml](#get-resource-yaml) 46 | - [Create resource using yaml](#create-resource-using-yaml) 47 | - [Update resource yaml](#update-resource-yaml) 48 | - [Readings](#readings-5) 49 | - [Kubernetes Events](#kubernetes-events) 50 | - [Kubernetes Pod Lifecycle](#kubernetes-pod-lifecycle) 51 | - [Restart policy](#restart-policy) 52 | - [Container probes](#container-probes) 53 | - [Readings](#readings-6) 54 | - [Kubernetes ConfigMap & Secret](#kubernetes-configmap--secret) 55 | - [Readings](#readings-7) 56 | - [Summary](#summary) 57 | - [Exercise](#exercise) 58 | 59 | 60 | 61 | # Introduction 62 | 63 | 本节我们将学习基于 Kubernetes 的应用管理,包括 kubectl, pod, deployment, service, label 等等。针对每个内容都会给出 Reading List。注意,本节所有的操作都基于 [Lab1](./lab1-installation.md) 中创建的 Minikube 集群。 64 | 65 | # Kubectl command line 66 | 67 | ## kubectl version 68 | 69 | 在 kubernetes 中,我们使用 kubectl 命令与 kubernetes 交互,在下面的试验中,我们将逐渐熟悉并了解更多 70 | kubectl 相关的命令,我们可以使用 `kubectl version` 检查 kubernetes 的版本信息。 71 | 72 | ``` 73 | $ kubectl version 74 | Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.0", GitCommit:"e8462b5b5dc2584fdcd18e6bcfe9f1e4d970a529", GitTreeState:"clean", BuildDate:"2019-06-20T04:52:26Z", GoVersion:"go1.12.6", Compiler:"gc", Platform:"darwin/amd64"} 75 | Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.0", GitCommit:"e8462b5b5dc2584fdcd18e6bcfe9f1e4d970a529", GitTreeState:"clean", BuildDate:"2019-06-19T16:32:14Z", GoVersion:"go1.12.5", Compiler:"gc", Platform:"linux/amd64"} 76 | ``` 77 | 78 | 从以上输出可以看出,kubectl 客户端的版本是 v1.15.0,kubernetes 集群的版本是 v1.15.0。同时,该命令还会输出 kubernetes 编译信息。 79 | 80 | ## kubectl cluster-info 81 | 82 | 除了版本信息,我们还可以通过 kubectl 获取更多 kubernetes 集群的相关信息: 83 | 84 | ``` 85 | $ kubectl cluster-info 86 | Kubernetes master is running at https://192.168.99.100:8443 87 | KubeDNS is running at https://192.168.99.100:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy 88 | 89 | To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. 90 | ``` 91 | 92 | 上述信息给出了集群 API 服务的地址,如果使用 `kubectl cluster-info dump`,可以看到更多集群的信息。 93 | 94 | ## kubectl -h 95 | 96 | kubectl 提供了非常好的帮助信息,我们可以通过 `kubectl -h` 来获取帮助信息。 97 | 98 | ``` 99 | $ kubectl -h 100 | kubectl controls the Kubernetes cluster manager. 101 | 102 | Find more information at: https://kubernetes.io/docs/reference/kubectl/overview/ 103 | 104 | Basic Commands (Beginner): 105 | create Create a resource from a file or from stdin. 106 | expose 使用 replication controller, service, deployment 或者 pod 并暴露它作为一个 新的 107 | Kubernetes Service 108 | run 在集群中运行一个指定的镜像 109 | set 为 objects 设置一个指定的特征 110 | 111 | Basic Commands (Intermediate): 112 | explain 查看资源的文档 113 | get 显示一个或更多 resources 114 | edit 在服务器上编辑一个资源 115 | delete Delete resources by filenames, stdin, resources and names, or by resources and label selector 116 | 117 | Deploy Commands: 118 | rollout Manage the rollout of a resource 119 | scale 为 Deployment, ReplicaSet, Replication Controller 或者 Job 设置一个新的副本数量 120 | autoscale 自动调整一个 Deployment, ReplicaSet, 或者 ReplicationController 的副本数量 121 | 122 | Cluster Management Commands: 123 | certificate 修改 certificate 资源. 124 | cluster-info 显示集群信息 125 | top Display Resource (CPU/Memory/Storage) usage. 126 | cordon 标记 node 为 unschedulable 127 | uncordon 标记 node 为 schedulable 128 | drain Drain node in preparation for maintenance 129 | taint 更新一个或者多个 node 上的 taints 130 | 131 | Troubleshooting and Debugging Commands: 132 | describe 显示一个指定 resource 或者 group 的 resources 详情 133 | logs 输出容器在 pod 中的日志 134 | attach Attach 到一个运行中的 container 135 | exec 在一个 container 中执行一个命令 136 | port-forward Forward one or more local ports to a pod 137 | proxy 运行一个 proxy 到 Kubernetes API server 138 | cp 复制 files 和 directories 到 containers 和从容器中复制 files 和 directories. 139 | auth Inspect authorization 140 | 141 | Advanced Commands: 142 | diff Diff live version against would-be applied version 143 | apply 通过文件名或标准输入流(stdin)对资源进行配置 144 | patch 使用 strategic merge patch 更新一个资源的 field(s) 145 | replace 通过 filename 或者 stdin替换一个资源 146 | wait Experimental: Wait for a specific condition on one or many resources. 147 | convert 在不同的 API versions 转换配置文件 148 | kustomize Build a kustomization target from a directory or a remote url. 149 | 150 | Settings Commands: 151 | label 更新在这个资源上的 labels 152 | annotate 更新一个资源的注解 153 | completion Output shell completion code for the specified shell (bash or zsh) 154 | 155 | Other Commands: 156 | api-resources Print the supported API resources on the server 157 | api-versions Print the supported API versions on the server, in the form of "group/version" 158 | config 修改 kubeconfig 文件 159 | plugin Provides utilities for interacting with plugins. 160 | version 输出 client 和 server 的版本信息 161 | 162 | Usage: 163 | kubectl [flags] [options] 164 | 165 | Use "kubectl --help" for more information about a given command. 166 | Use "kubectl options" for a list of global command-line options (applies to all commands). 167 | ``` 168 | 169 | 当我们找到需要的子命令时,可以进一步使用 -h 来查看该子命令的帮助信息: 170 | 171 | ``` 172 | $ kubectl get -h 173 | ... 174 | ``` 175 | 176 | ## kubectl command format 177 | 178 | kubectl 命令的基本格式是 `kubectl `,其中 `action` 可以是 `create`, `delete`, `get` 等等,`resource` 你可以使用 `kubectl api-resources` 获得完整列表。 179 | 180 | 例如,你可以通过 `kubectl get nodes` 获取节点信息,可以通过 `kubectl describe nodes ${NODENAME}` 获取节点详细信息。 181 | 182 | ## kubectl config file 183 | 184 | kubectl 通过读取配置文件信息与 kubernetes 集群交互,默认配置文件路径是 `~/.kube/config`,内容可能如下: 185 | 186 | ``` 187 | $ cat ~/.kube/config 188 | apiVersion: v1 189 | clusters: 190 | - cluster: 191 | certificate-authority: /Users/deyuandeng/.minikube/ca.crt 192 | server: https://192.168.99.100:8443 193 | name: minikube 194 | contexts: 195 | - context: 196 | cluster: minikube 197 | user: minikube 198 | name: minikube 199 | current-context: minikube 200 | kind: Config 201 | preferences: {} 202 | users: 203 | - name: minikube 204 | user: 205 | client-certificate: /Users/deyuandeng/.minikube/apiserver.crt 206 | client-key: /Users/deyuandeng/.minikube/apiserver.key 207 | ``` 208 | 209 | 这里有三个重要的顶级概念: `clusters`, `users` 和 `contexts`。 210 | 211 | 我们使用的集群名为 `minikube`,其服务器地址为 `https://192.168.99.100:8443`,其认证证书位于 `${HOME}/.minikube/ca.crt`。 212 | 213 | 当我们使用该 kubeconfig 发送请求时,我们充当用户 `minikube` (确切地说,真正的用户名来自证书通用名,但是我们暂时跳过它)。 214 | 215 | 最后,`context` 是各种配置的组合,例如,存在两个 `context`,一个用于集群 `minikube` 以及用户 `minikube`,另一个用于集群 `example` 和用户 `minikube`,即这意味着用户 `minikube` 可以同时访问 `minikube` 和 `example` 集群。 216 | 217 | ## Refereneces 218 | 219 | * [kubectl overview](https://kubernetes.io/docs/user-guide/kubectl-overview/) 220 | 221 | # Kubernetes node 222 | 223 | 节点(Node)是物理机或虚拟机,他们组成了 kubernetes 的资源池。Master 节点负责资源调度、集群状态控制等, 224 | Node 节点负责运行用户应用,承接负载。我们会在后续的章节中介绍具体的架构,现在我们只关心如何通过命令行根据 kubectl 与 kubernetes 225 | 接口交互管理节点。 226 | 227 | ## Node information 228 | 229 | 在 kubernetes 中,我们可以通过 `kubectl get nodes` 命令来获取所有节点信息: 230 | 231 | ``` 232 | $ kubectl get nodes 233 | NAME STATUS ROLES AGE VERSION 234 | minikube Ready master 27h v1.15.0 235 | ``` 236 | 237 | 以上输出表明当前集群有 1 个节点。注意,我们无法完全从命令行区分 master 节点和 node 节点。这里 minikube 238 | 节点即是 master 也是 node,也就是说,minikube 主机即负责调度和管理,也负责用户容器的运行时。一般在生产环境中,我们不会在 master 上运行用户容器。但是如果主机数量非常少的情况下可以考虑,前提是预留足够的资源给 master。 239 | 240 | 不承载用户容器的主机需要加上 SchedulingDisabled 的状态(通过 `kubectl cordon ` 实现),例如: 241 | 242 | ``` 243 | $ kubectl get nodes 244 | NAME STATUS AGE 245 | i-2ze0tfg75y5plzvnd29h Ready,SchedulingDisabled 2d 246 | i-2ze0woc5l1230xs5zxry Ready 2d 247 | i-2ze14a3m7riw0l18oemg Ready 2d 248 | i-2ze14a3m7riw0l18oemh Ready 2d 249 | i-2ze1nwnt9tc3wg83rsru Ready 2d 250 | ``` 251 | 252 | ## Node details 253 | 254 | 我们可以通过 `kubectl describe nodes` 来了解节点的详情。下面显示了 minikube 节点的详情,我们可以暂时不关心输出内容的细节。 255 | 256 | ``` 257 | $ kubectl describe nodes minikube 258 | Name: minikube 259 | Roles: master 260 | Labels: beta.kubernetes.io/arch=amd64 261 | beta.kubernetes.io/os=linux 262 | kubernetes.io/arch=amd64 263 | kubernetes.io/hostname=minikube 264 | kubernetes.io/os=linux 265 | node-role.kubernetes.io/master= 266 | Annotations: kubeadm.alpha.kubernetes.io/cri-socket: /var/run/dockershim.sock 267 | node.alpha.kubernetes.io/ttl: 0 268 | volumes.kubernetes.io/controller-managed-attach-detach: true 269 | CreationTimestamp: Thu, 27 Jun 2019 11:03:46 +0800 270 | Taints: 271 | Unschedulable: false 272 | Conditions: 273 | Type Status LastHeartbeatTime LastTransitionTime Reason Message 274 | ---- ------ ----------------- ------------------ ------ ------- 275 | MemoryPressure False Fri, 28 Jun 2019 14:42:41 +0800 Thu, 27 Jun 2019 11:03:40 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available 276 | DiskPressure False Fri, 28 Jun 2019 14:42:41 +0800 Thu, 27 Jun 2019 11:03:40 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure 277 | PIDPressure False Fri, 28 Jun 2019 14:42:41 +0800 Thu, 27 Jun 2019 11:03:40 +0800 KubeletHasSufficientPID kubelet has sufficient PID available 278 | Ready True Fri, 28 Jun 2019 14:42:41 +0800 Thu, 27 Jun 2019 11:03:40 +0800 KubeletReady kubelet is posting ready status 279 | Addresses: 280 | InternalIP: 10.0.2.15 281 | Hostname: minikube 282 | Capacity: 283 | cpu: 2 284 | ephemeral-storage: 17784772Ki 285 | hugepages-2Mi: 0 286 | memory: 2038624Ki 287 | pods: 110 288 | Allocatable: 289 | cpu: 2 290 | ephemeral-storage: 16390445849 291 | hugepages-2Mi: 0 292 | memory: 1936224Ki 293 | pods: 110 294 | System Info: 295 | Machine ID: d4ca037d392045e1814f59e5c9b51c1d 296 | System UUID: 3EE4C717-994B-4FF3-8025-11D5E5E822CD 297 | Boot ID: 6ca21b67-8cf1-430d-8a97-ddcc8dbc397b 298 | Kernel Version: 4.15.0 299 | OS Image: Buildroot 2018.05.3 300 | Operating System: linux 301 | Architecture: amd64 302 | Container Runtime Version: docker://18.9.6 303 | Kubelet Version: v1.15.0 304 | Kube-Proxy Version: v1.15.0 305 | Non-terminated Pods: (15 in total) 306 | Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits AGE 307 | --------- ---- ------------ ---------- --------------- ------------- --- 308 | default nginx-77cd46f788-bd5kk 0 (0%) 0 (0%) 0 (0%) 0 (0%) 23h 309 | kube-system coredns-5c98db65d4-5cnpp 100m (5%) 0 (0%) 70Mi (3%) 170Mi (8%) 27h 310 | kube-system coredns-5c98db65d4-brqbq 100m (5%) 0 (0%) 70Mi (3%) 170Mi (8%) 27h 311 | kube-system default-http-backend-59f7ff8999-nnzvc 20m (1%) 20m (1%) 30Mi (1%) 30Mi (1%) 27h 312 | kube-system etcd-minikube 0 (0%) 0 (0%) 0 (0%) 0 (0%) 27h 313 | kube-system freshpod-v6z22 0 (0%) 0 (0%) 0 (0%) 0 (0%) 27h 314 | kube-system heapster-4d8gm 0 (0%) 0 (0%) 0 (0%) 0 (0%) 27h 315 | kube-system influxdb-grafana-9v876 0 (0%) 0 (0%) 0 (0%) 0 (0%) 27h 316 | kube-system kube-addon-manager-minikube 5m (0%) 0 (0%) 50Mi (2%) 0 (0%) 27h 317 | kube-system kube-apiserver-minikube 250m (12%) 0 (0%) 0 (0%) 0 (0%) 27h 318 | kube-system kube-controller-manager-minikube 200m (10%) 0 (0%) 0 (0%) 0 (0%) 25m 319 | kube-system kube-proxy-726vx 0 (0%) 0 (0%) 0 (0%) 0 (0%) 27h 320 | kube-system kube-scheduler-minikube 100m (5%) 0 (0%) 0 (0%) 0 (0%) 27h 321 | kube-system metrics-server-84bb785897-x6rz6 0 (0%) 0 (0%) 0 (0%) 0 (0%) 27h 322 | kube-system nginx-ingress-controller-7b465d9cf8-rv9k5 0 (0%) 0 (0%) 0 (0%) 0 (0%) 27h 323 | Allocated resources: 324 | (Total limits may be over 100 percent, i.e., overcommitted.) 325 | Resource Requests Limits 326 | -------- -------- ------ 327 | cpu 775m (38%) 20m (1%) 328 | memory 220Mi (11%) 370Mi (19%) 329 | ephemeral-storage 0 (0%) 0 (0%) 330 | Events: 331 | Type Reason Age From Message 332 | ---- ------ ---- ---- ------- 333 | Normal Starting 19h kubelet, minikube Starting kubelet. 334 | Normal NodeHasSufficientMemory 19h (x8 over 19h) kubelet, minikube Node minikube status is now: NodeHasSufficientMemory 335 | Normal NodeHasNoDiskPressure 19h (x8 over 19h) kubelet, minikube Node minikube status is now: NodeHasNoDiskPressure 336 | Normal NodeHasSufficientPID 19h (x7 over 19h) kubelet, minikube Node minikube status is now: NodeHasSufficientPID 337 | Normal NodeAllocatableEnforced 19h kubelet, minikube Updated Node Allocatable limit across pods 338 | Normal Starting 19h kube-proxy, minikube Starting kube-proxy. 339 | Normal NodeAllocatableEnforced 26m kubelet, minikube Updated Node Allocatable limit across pods 340 | Normal Starting 26m kubelet, minikube Starting kubelet. 341 | Normal NodeHasNoDiskPressure 26m (x8 over 26m) kubelet, minikube Node minikube status is now: NodeHasNoDiskPressure 342 | Normal NodeHasSufficientPID 26m (x7 over 26m) kubelet, minikube Node minikube status is now: NodeHasSufficientPID 343 | Normal NodeHasSufficientMemory 26m (x8 over 26m) kubelet, minikube Node minikube status is now: NodeHasSufficientMemory 344 | Normal Starting 25m kube-proxy, minikube Starting kube-proxy. 345 | Normal NodeNotSchedulable 3m54s kubelet, minikube Node minikube status is now: NodeNotSchedulable 346 | ``` 347 | 348 | ## Readings 349 | 350 | * [kubernetes nodes](https://kubernetes.io/docs/concepts/architecture/nodes/) 351 | 352 | # Kubernetes Namespace 353 | 354 | Kubernetes namespace 是用来构建虚拟的资源池;使用 kubernetes namespace,管理员可以将 kubernetes 355 | 划分成多个虚拟的区域,不同的项目或者团队可以使用不同的 namespace,达到了共享 kubernetes 集群资源的目的。此外, 356 | namespace 也被用来划分命名空间,即不同 namespace 里的资源可以取相同的名字,相同 namespace 内的资源不能重名。 357 | 358 | ## Namespaces 359 | 360 | 通过 `kubectl create -f`,我们可以轻松地创建一个 namespace: 361 | 362 | ``` 363 | $ kubectl create -f resources/ns.yaml 364 | namespace "tutorial" created 365 | ``` 366 | 367 | 然后通过 `kubectl get ns`,可以看到刚才创建的 namespace 368 | 369 | ``` 370 | $ kubectl get ns 371 | NAME STATUS AGE 372 | default Active 27h 373 | kube-node-lease Active 27h 374 | kube-public Active 27h 375 | kube-system Active 27h 376 | tutorial Active 7s 377 | ``` 378 | 379 | 这里 `ns` 是 `namespace` 的缩写。输出内容中的 `default`, `kube-node-lease`, `kube-public` 和 `kube-system`,都是 kubernetes 380 | 默认创建的 namespace,用来放置系统相关的资源。 381 | 382 | ```sh 383 | $ kubectl describe ns tutorial 384 | Name: tutorial 385 | Labels: 386 | Annotations: 387 | Status: Active 388 | 389 | No resource quota. 390 | 391 | No resource limits. 392 | ``` 393 | 394 | ## Readings 395 | 396 | * [kubernetes namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) 397 | * [namespace walkthrough](https://kubernetes.io/docs/tasks/administer-cluster/namespaces-walkthrough/) 398 | 399 | # Kubernetes Pod & Deployment 400 | 401 | 当我们有一个 kubernetes 集群之后,可以开始部署应用了。在 kubernetes 的世界里,Pod 是运行应用的载体。 402 | Pod 是由多个容器组成、是 kubernetes 的最小调度单元、Pod 共享底层资源、由 kubernetes 来管理生命周期。然而,一般情况下,我们并不直接创建 Pod,而是通过 Deployment 来创建 Pod,由 Deployment 来负责创建、更新、维护其所管理的所有 Pods。 403 | 404 | 一旦我们通过 Deployment 创建 Pod,会有一个 Deployment 控制器不断监控所有 Pod 的状态。例如,如果 Pod 405 | 运行的机器宕机了,那么 Deployment 控制器会在另一台机器上重新启动一个 Pod。接下来,我们将部署一个应用,部署之后,集群将会达到下图所示的状态。 406 | 407 |

408 |

Image source: kubernetes guide


409 | 410 | 这里六边形方框代表一个 kubernetes 节点,正方体代表一个 Pod。后面,我们将通过 kubectl 来管理应用。 411 | 412 | ## Create Deployment 413 | 414 | 我们可以通过 yaml 文件创建 Deployment,从而创建应用。 415 | 416 | ``` 417 | $ kubectl apply -f resources/deployment_nginx.yaml -n tutorial 418 | deployment.apps/nginx-deployment created 419 | ``` 420 | 421 | 执行该命令之后,kubernetes 在集群中寻找一台满足需求的机器运行节点,然后该节点上的 agent 启动该应用。 422 | 423 | ## Get Deployment 424 | 425 | 创建好后,我们可以通过 `kubectl get deployment` 来查看刚刚创建的 Deployment: 426 | 427 | ``` 428 | $ kubectl get deployment -n tutorial 429 | NAME READY UP-TO-DATE AVAILABLE AGE 430 | nginx 1/1 1 1 67s 431 | ``` 432 | 433 | 从输出可以看出,我们请求创建一个 Pod (replica),kubernetes 已经帮我们成功创建了一个。 434 | 435 | ## Get Pods 436 | 437 | 上面我们讲到,真正运行应用的载体是 Pod,Deployment 是用来管理 Pod 的资源(比如重启失败的 Pod),我们可以通过 438 | `kubectl get pods` 来查看当前创建的 Pod。 439 | 440 | ``` 441 | $ kubectl get pods -n tutorial 442 | NAME READY STATUS RESTARTS AGE 443 | nginx-646b46d648-hbwg2 1/1 Running 0 101s 444 | ``` 445 | 446 | 可以看到,现在有一个 Pod 在运行中;我们可以通过 `kubectl get pods -o wide` 来查看 Pod 运行的主机,或者通过 `kubectl describe pods nginx-77cd46f788-bd5kk` 来查看 Pod 更加详细的信息(describe 447 | 中包含非常多的信息,我们将在后面介绍)。 448 | 449 | ``` 450 | $ kubectl get pods -n tutorial -o wide 451 | NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES 452 | nginx-646b46d648-hbwg2 1/1 Running 0 2m23s 172.17.0.11 minikube 453 | ``` 454 | 455 | ## Get Pod Logs 456 | 457 | 当我们部署要应用之后,可以通过 `kubectl logs ` 和 `kubectl exec ` 与 Pod 交互。 458 | 459 | ``` 460 | $ kubectl logs nginx-646b46d648-hbwg2 -n tutorial 461 | ``` 462 | 463 | 由于没有任何请求,nginx pod 日志暂时为空。现在我们尝试访问 nginx pod。由于 `minikube` 本身是运行在虚拟机中,因此我们需要登录虚拟机访问 nginx pod (nginx pod ip: 172.17.0.11)。 464 | 465 | ``` 466 | $ minikube ssh 467 | _ _ 468 | _ _ ( ) ( ) 469 | ___ ___ (_) ___ (_)| |/') _ _ | |_ __ 470 | /' _ ` _ `\| |/' _ `\| || , < ( ) ( )| '_`\ /'__`\ 471 | | ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )( ___/ 472 | (_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____) 473 | 474 | # in minikube vm 475 | $ curl 172.17.0.11 476 | 477 | 478 | ... 479 | 480 | ``` 481 | 482 | 此时再查看 nginx,可以看到 nginx 的访问日志。 483 | 484 | ``` 485 | $ kubectl logs nginx-3035859230-d2sfd -n tutorial 486 | 172.17.0.1 - - [28/Jun/2019:07:03:09 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.61.1" "-" 487 | ``` 488 | 489 | ## Execute command in Pod 490 | 491 | 有时候,我们需要在 Pod 中执行命令,可以通过 `kubectl exec`: 492 | 493 | ``` 494 | $ kubectl exec nginx-646b46d648-hbwg2 -n tutorial -- ls -l 495 | total 64 496 | drwxr-xr-x 2 root root 4096 Dec 4 2015 bin 497 | drwxr-xr-x 2 root root 4096 Aug 26 2015 boot 498 | drwxr-xr-x 5 root root 360 Jun 28 06:57 dev 499 | drwxr-xr-x 1 root root 4096 Jun 28 06:57 etc 500 | drwxr-xr-x 2 root root 4096 Aug 26 2015 home 501 | drwxr-xr-x 9 root root 4096 Nov 27 2014 lib 502 | drwxr-xr-x 2 root root 4096 Dec 4 2015 lib64 503 | drwxr-xr-x 2 root root 4096 Dec 4 2015 media 504 | drwxr-xr-x 2 root root 4096 Dec 4 2015 mnt 505 | drwxr-xr-x 2 root root 4096 Dec 4 2015 opt 506 | dr-xr-xr-x 226 root root 0 Jun 28 06:57 proc 507 | drwx------ 2 root root 4096 Dec 4 2015 root 508 | drwxr-xr-x 1 root root 4096 Jun 28 06:57 run 509 | drwxr-xr-x 2 root root 4096 Dec 4 2015 sbin 510 | drwxr-xr-x 2 root root 4096 Dec 4 2015 srv 511 | dr-xr-xr-x 12 root root 0 Jun 28 06:56 sys 512 | drwxrwxrwt 1 root root 4096 Dec 5 2015 tmp 513 | drwxr-xr-x 1 root root 4096 Dec 5 2015 usr 514 | drwxr-xr-x 1 root root 4096 Dec 5 2015 var 515 | ``` 516 | 517 | 注意,我们通过双横线(“--”)区分本地终端命令和容器中执行的命令;当执行的命令只有一个单词的时候,可以省略。如果容器中有 `shell`,我们也可以启动一个远程终端: 518 | 519 | ``` 520 | $ kubectl exec -it nginx-646b46d648-hbwg2 -n tutorial bash 521 | root@nginx-646b46d648-hbwg2:/# ls -l 522 | total 64 523 | drwxr-xr-x 2 root root 4096 Dec 4 2015 bin 524 | drwxr-xr-x 2 root root 4096 Aug 26 2015 boot 525 | drwxr-xr-x 5 root root 360 Jun 28 06:57 dev 526 | drwxr-xr-x 1 root root 4096 Jun 28 06:57 etc 527 | drwxr-xr-x 2 root root 4096 Aug 26 2015 home 528 | drwxr-xr-x 9 root root 4096 Nov 27 2014 lib 529 | drwxr-xr-x 2 root root 4096 Dec 4 2015 lib64 530 | drwxr-xr-x 2 root root 4096 Dec 4 2015 media 531 | drwxr-xr-x 2 root root 4096 Dec 4 2015 mnt 532 | drwxr-xr-x 2 root root 4096 Dec 4 2015 opt 533 | dr-xr-xr-x 226 root root 0 Jun 28 06:57 proc 534 | drwx------ 2 root root 4096 Dec 4 2015 root 535 | drwxr-xr-x 1 root root 4096 Jun 28 06:57 run 536 | drwxr-xr-x 2 root root 4096 Dec 4 2015 sbin 537 | drwxr-xr-x 2 root root 4096 Dec 4 2015 srv 538 | dr-xr-xr-x 12 root root 0 Jun 28 06:56 sys 539 | drwxrwxrwt 1 root root 4096 Dec 5 2015 tmp 540 | drwxr-xr-x 1 root root 4096 Dec 5 2015 usr 541 | drwxr-xr-x 1 root root 4096 Dec 5 2015 var 542 | ``` 543 | 544 | 使用 `ctrl + d` 可以退出远程终端。 545 | 546 | ## Readings 547 | 548 | * [kubernetes pod](https://kubernetes.io/docs/concepts/workloads/pods/pod/) 549 | 550 | # Kubernetes Service 551 | 552 | 接下来我们通过一连串的命令学习 kubernetes service。kubernetes service 有以下几个作用: 553 | 554 | * 提供固定的 IP。由于 Pod 可以随时启停,Pod IP 可能随时都会变化,例如上面 nginx pod 重启之后 IP 555 | 可能不再是 172.17.0.11。Service 为 Pods 提供的固定 IP,其他服务可以通过 Service IP 找到提供服务的 556 | Pods。 557 | * 提供负载均衡。Service 由多个 Pods 组成,kubernetes 对组成 Service 的 Pods 提供的负载均衡方案,例如随机访问、基于 Client IP 的 session affinity。 558 | * 服务发现。集群中其他服务可以通过 Service 名字访问后端服务(DNS),也可以通过环境变量访问。 559 | 560 | 下图是 kubernetes Pods, Service 的典型关系。下图有两个 Deployment: A 和 B。其中 Deployment A 561 | 创建了一个 Pods(黄色),Deployment B 创建了三个 Pod(绿色)。我们可以创建两个 Service: A 和 B。 562 | Service A 管理由 Deployment A 创建的 Pods,Service B 管理 Deployment B 创建的 Pods。可以看到, 563 | Service A 和 Service B 都有自己独立的 IP。无论他们所管理的容器如何变化, Service 的 IP 都不会变化。 564 | 565 |

566 |

Image source: kubernetes guide


567 | 568 | ## Create service 569 | 570 | 与其他资源相同,我们可以通过 `kubectl create -f` 加文件名创建 Service。但类似 Deployment,kubernetes 571 | 提供了快捷命令让我们能快速创建 Service。 572 | 573 | ``` 574 | $ kubectl expose deployment nginx --port 80 -n tutorial 575 | service "nginx" exposed 576 | ``` 577 | 578 | ## Get service 579 | 580 | 通过 `kubectl get service` 命令可以查看 service 的详细信息: 581 | 582 | ``` 583 | $ kubectl get svc nginx -n tutorial 584 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 585 | nginx ClusterIP 10.96.6.136 80/TCP 15s 586 | ``` 587 | 588 | 可以看到,Service 具有一个固定的 IP 10.96.6.136。同样,通过 describe 可以看到更多详细的信息: 589 | 590 | ``` 591 | $ kubectl describe svc nginx -n tutorial 592 | Name: nginx 593 | Namespace: tutorial 594 | Labels: run=nginx 595 | Annotations: 596 | Selector: run=nginx 597 | Type: ClusterIP 598 | IP: 10.96.6.136 599 | Port: 80/TCP 600 | TargetPort: 80/TCP 601 | Endpoints: 172.17.0.11:80 602 | Session Affinity: None 603 | Events: 604 | ``` 605 | 606 | 其中,Endpoint 表明 Service 所选中的 PodIP:PodPort。我们可以查看 Pod 信息来验证: 607 | 608 | ``` 609 | $ kubectl get pods -o wide -n tutorial 610 | NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES 611 | nginx-646b46d648-hbwg2 1/1 Running 0 14m 172.17.0.11 minikube 612 | ``` 613 | 614 | ## Query service 615 | 616 | 创建 Service 后,我们可以在主机上直接访问该 Service。下面两条命令实际上访问的都是同一个后端。第一个命令通过 Service IP 访问,第二个命令通过 Pod IP 访问。 617 | 618 | 通过 Service IP 访问: 619 | 620 | ``` 621 | $ minikube ssh 622 | $ curl 10.96.6.136 623 | 624 | 625 | ... 626 | 627 | ``` 628 | 629 | 通过 Pod IP 访问: 630 | 631 | ``` 632 | $ minikube ssh 633 | $ curl 172.17.0.11 634 | 635 | 636 | ... 637 | 638 | ``` 639 | 640 | 上面的命令创建了一个名为 nginx 的 Service,并使用 80 作为服务端口。这里,我们的 nginx 容器监听的是容器的 80 端口,该端口是 Pod IP 所监听的端口;我们可以在 Service 上使用不同的端口。例如,若我们想暴露的服务端口是 8080 端口,需要使用 port 和 targetPort 选项。 641 | 642 | 首先,删除已经创建的 Service: 643 | 644 | ``` 645 | $ kubectl delete svc nginx -n tutorial 646 | service "nginx" deleted 647 | ``` 648 | 649 | 之后,创建 Service: 650 | 651 | ``` 652 | $ kubectl expose deployment nginx --port 8080 --target-port 80 -n tutorial 653 | service "nginx" exposed 654 | ``` 655 | 656 | 尝试用 8080 端口访问服务 657 | 658 | ``` 659 | $ kubectl get svc nginx -n tutorial 660 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 661 | nginx ClusterIP 10.98.125.20 8080/TCP 6s 662 | 663 | $ minikube ssh 664 | $ curl 10.98.125.20:8080 665 | 666 | 667 | ... 668 | 669 | ``` 670 | 671 | ## NodePort service 672 | 673 | 上述创建的 Service 只能被集群内部的节点和 Pod 访问,并不能被外部访问。我们可以通过两种方式暴露服务:`NodePort` 和 `LoadBalancer`。`NodePort` 通过在每个节点打开一个端口对外提供服务,`LoadBalancer` 通过创建一个外部负载均衡器(例如公有云负载均衡器)来对外提供服务。这里我们尝试使用 `NodePort`。 674 | 675 | 首先,删除已有的 Service: 676 | 677 | ``` 678 | $ kubectl delete svc nginx -n tutorial 679 | service "nginx" deleted 680 | ``` 681 | 682 | 通过 NodePort 暴露服务,注意这里使用了 `--type NodePort`: 683 | 684 | ``` 685 | $ kubectl expose deployment nginx --port 80 --type NodePort -n tutorial 686 | service "nginx" exposed 687 | ``` 688 | 689 | 查看 Service 的细节: 690 | 691 | ``` 692 | $ kubectl get svc nginx -n tutorial 693 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 694 | nginx NodePort 10.107.97.57 80:32542/TCP 5s 695 | ``` 696 | 697 | ``` 698 | $ kubectl describe svc nginx -n tutorial 699 | Name: nginx 700 | Namespace: tutorial 701 | Labels: run=nginx 702 | Annotations: 703 | Selector: run=nginx 704 | Type: NodePort 705 | IP: 10.107.97.57 706 | Port: 80/TCP 707 | TargetPort: 80/TCP 708 | NodePort: 32542/TCP 709 | Endpoints: 172.17.0.11:80 710 | Session Affinity: None 711 | External Traffic Policy: Cluster 712 | Events: 713 | ``` 714 | 715 | 从以上输出可以看到,nginx 服务打开了节点的 32542 端口(每个节点),我们可以通过 `NodeIP:NodePort` 访问服务。 716 | 717 | ``` 718 | $ curl $(minikube ip):32542 719 | 720 | 721 | ... 722 | 723 | ``` 724 | 725 | ## Readings 726 | 727 | * [kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). Please read as much as you can, we'll come back to service again. 728 | * [run application with service](https://kubernetes.io/docs/tasks/access-application-cluster/connecting-frontend-backend/). 729 | * [ports in kubernetes](https://speakerdeck.com/thockin/kubernetes-a-very-brief-explanation-of-ports) 730 | 731 | # Kubernetes Label 732 | 733 | Service 通过 selector & label 来选取它所管理的 Pod,同样 Deployment 也是通过 selector & label 734 | 选取它所管理的 Pod。因为我们是通过 Deployment 创建的 Pod,因此 Deployment 的 selector 一定是匹配 735 | Pod 的 label。如果我们想让 Service 选择与 Deployment 相同的 Pods,我们需要将 Service 的 selector 736 | 设为与 Deployment 相同。在上面的实验中,我们使用 `kubectl expose deployment nginx` 的时候,kubernetes 737 | 默认将 Service 的 selector 设置成与 Deployment 相同的 selector。下图对 label 做了详细的标注。 738 | 739 |

740 |

Image source: kubernetes guide


741 | 742 | 由上图可以看出: 743 | 744 | | Resource | selector | 745 | | ------------------------------ | -------- | 746 | | Deployment A, Service A, Pod A | app=A | 747 | | Deployment B, Service B, Pod B | app=B | 748 | 749 | Label 可以在创建时添加,也可以在运行时添加或修改。在运行时修改会影响集群的状态,因为现有的 selector & label 750 | 结构会被改变。 751 | 752 | ## View selector & label 753 | 754 | 从下面的输出可以看到,上述创建的 Deployment 和 Service 的 Selector 都是 `run=nginx`。Pod 具有 Label 755 | `pod-template-hash=646b46d648,run=nginx`,因此他们都选中了 `nginx-646b46d648-hbwg2` 这个 Pod (只要 756 | Pod label 的子集满足即可;这里的 `pod-template-hash=646b46d648` Label 是 kubernetes 自动创建)。 757 | 758 | ``` 759 | $ kubectl describe deployment nginx -n tutorial 760 | Name: nginx 761 | Namespace: tutorial 762 | CreationTimestamp: Fri, 28 Jun 2019 14:56:58 +0800 763 | Labels: run=nginx 764 | Annotations: deployment.kubernetes.io/revision: 1 765 | Selector: run=nginx 766 | ... 767 | ``` 768 | 769 | ``` 770 | $ kubectl describe svc nginx -n tutorial 771 | Name: nginx 772 | Namespace: tutorial 773 | Labels: run=nginx 774 | Annotations: 775 | Selector: run=nginx 776 | ... 777 | ``` 778 | 779 | ``` 780 | $ kubectl describe pods nginx-646b46d648-hbwg2 -n tutorial 781 | Name: nginx-646b46d648-hbwg2 782 | Namespace: tutorial 783 | Priority: 0 784 | Node: minikube/10.0.2.15 785 | Start Time: Fri, 28 Jun 2019 14:56:59 +0800 786 | Labels: pod-template-hash=646b46d648 787 | run=nginx 788 | ``` 789 | 790 | ## Label operations 791 | 792 | kubectl 支持对资源的 label 进行管理,比如我们可以通过 -l 选项查看仅具有某个 label 的资源。 793 | 794 | ``` 795 | $ kubectl get pods -l run=nginx -n tutorial 796 | NAME READY STATUS RESTARTS AGE 797 | nginx-646b46d648-hbwg2 1/1 Running 0 26m 798 | ``` 799 | 800 | ``` 801 | $ kubectl get svc -l run=nginx -n tutorial 802 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 803 | nginx NodePort 10.107.97.57 80:32542/TCP 7m38s 804 | ``` 805 | 806 | 当没有任何资源满足 label 时,输出为空: 807 | 808 | ``` 809 | $ kubectl get svc -l run=apache -n tutorial 810 | No resources found. 811 | ``` 812 | 813 | kubectl 支持对资源的 label 进行操作,如下所示: 814 | 815 | ``` 816 | $ kubectl get pods -n tutorial 817 | NAME READY STATUS RESTARTS AGE 818 | nginx-646b46d648-hbwg2 1/1 Running 0 26m 819 | 820 | $ kubectl label pod nginx-646b46d648-hbwg2 app=v1 -n tutorial 821 | pod/nginx-646b46d648-hbwg2 labeled 822 | 823 | $ kubectl describe pods nginx-646b46d648-hbwg2 -n tutorial 824 | Name: nginx-646b46d648-hbwg2 825 | Namespace: tutorial 826 | Priority: 0 827 | Node: minikube/10.0.2.15 828 | Start Time: Fri, 28 Jun 2019 14:56:59 +0800 829 | Labels: app=v1 830 | pod-template-hash=646b46d648 831 | run=nginx 832 | Annotations: 833 | Status: Running 834 | IP: 172.17.0.11 835 | Controlled By: ReplicaSet/nginx-646b46d648 836 | ``` 837 | 838 | 注意这里我们是对 Pod 添加了一个 label,并不会影响 Deployment 与 Pod 之间的关系。因为 Pod 保持 Label 839 | `run=nginx`,依然会被 Deployment 选中。 840 | 841 | ## Readings 842 | 843 | * [label and selector](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) 844 | 845 | # Kubernetes Deployment Operations 846 | 847 | ## Scale up using kubectl 848 | 849 | 接下来我们将学习 kubernetes Deployment 的另外两个操作:水平扩展应用和更新应用。下图中,Deployment A 850 | 有一个 Pod 在运行,Service A 管理该 Pod。 851 | 852 |

853 |

Image source: kubernetes guide


854 | 855 | 通过调整 Deployment 的副本数量,我们可以将 Pod 的数量调整到 4 个。与此同时,Service 会感知到同样 label 的 856 | Pod 被扩容到了 4 个,会将流量导到所有 Pod(而不是只有最开始的 Pod)。 857 | 858 |

859 |

Image source: kubernetes guide


860 | 861 | 首先创建 Deployment 和 Service(确保前面教程的 Deployment 和 Service 已经被删除): 862 | 863 | ``` 864 | kubectl apply -f resources/deployment_nginx -n tutorial 865 | kubectl expose deployment nginx --port 80 --name=nginx -n tutorial 866 | ``` 867 | 868 | 接下来,我们可以通过 `kubectl scale` 子命令将 Pod 数量扩容到四个: 869 | 870 | ``` 871 | $ kubectl get deployments -n tutorial 872 | NAME READY UP-TO-DATE AVAILABLE AGE 873 | nginx 1/1 1 1 13s 874 | 875 | $ kubectl scale deployments nginx --replicas=4 -n tutorial 876 | deployment.extensions/nginx scaled 877 | 878 | $ kubectl get deployments -n tutorial 879 | NAME READY UP-TO-DATE AVAILABLE AGE 880 | nginx 4/4 4 4 49s 881 | 882 | $ kubectl get pods -n tutorial -o wide 883 | NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES 884 | nginx-d6b94d6f6-lggxr 1/1 Running 0 28s 172.17.0.11 minikube 885 | nginx-d6b94d6f6-lkr8m 1/1 Running 0 59s 172.17.0.8 minikube 886 | nginx-d6b94d6f6-npntj 1/1 Running 0 28s 172.17.0.13 minikube 887 | nginx-d6b94d6f6-rh42k 1/1 Running 0 28s 172.17.0.12 minikube 888 | ``` 889 | 890 | 可以看到目前有四个 Pods,其中 `AGE` 较小的是新生成的。 891 | 892 | ## View service 893 | 894 | 之前提到,Service 会感知到 Pods 的变化,在所有的 Pods 中负载均衡,我们可以通过 kubectl 查看。 895 | 896 | ``` 897 | $ kubectl describe service nginx -n tutorial 898 | Name: nginx 899 | Namespace: tutorial 900 | Labels: run=nginx 901 | Annotations: 902 | Selector: run=nginx 903 | Type: ClusterIP 904 | IP: 10.103.221.162 905 | Port: 80/TCP 906 | TargetPort: 80/TCP 907 | Endpoints: 172.17.0.11:80,172.17.0.12:80,172.17.0.13:80 + 1 more... 908 | Session Affinity: None 909 | Events: 910 | ``` 911 | 912 | Service nginx 已经将后端 Endpoints 扩容到所有的 4 个 Pods。 913 | 914 | ## Scale down using kubectl 915 | 916 | 我们也可以通过同样的命令缩容(kubectl scale)。Deployment 不会区分是扩容命令或是缩容命令,它只关心将实例的数量调整到指定的数量。 917 | 918 | ``` 919 | $ kubectl scale deployments nginx --replicas=2 -n tutorial 920 | deployment.extensions/nginx scaled 921 | 922 | $ kubectl get pods -n tutorial 923 | NAME READY STATUS RESTARTS AGE 924 | nginx-d6b94d6f6-lkr8m 1/1 Running 0 4m58s 925 | nginx-d6b94d6f6-rh42k 1/1 Running 0 4m27s 926 | ``` 927 | 928 | ## Update deployment 929 | 930 | 接下来,我们将了解 kubernetes 如何进行应用更新。见下图,我们目前有四个运行中的应用: 931 | 932 |

933 |

Image source: kubernetes guide


934 | 935 | 当我们更新容器镜像时,kubernetes 会启动一个新 Pod 并关闭一个老 Pod。下图中,紫色的 Pod 为 kubernetes 936 | 新创建的 Pod,淡绿色 Pod 为老 Pod。Service 会停止向老 Pod 导流。 937 | 938 |

939 |

Image source: kubernetes guide


940 | 941 | 第一个 Pod 更新成功后,Deployment 会更新第二个 Pod。如下图所示,紫色两个 Pod 为 Deployment 创建的新 Pod。 942 | 943 |

944 |

Image source: kubernetes guide


945 | 946 | 最后,Deployment 将所有的 Pod 都更新完毕。 947 | 948 |

949 |

Image source: kubernetes guide


950 | 951 | ## Update via setting image 952 | 953 | 接下来,我们通过命令行了解 kubernetes 更新应用的过程。 954 | 955 | ``` 956 | $ kubectl set image deployments nginx nginx=cargo.caicloud.io/caicloud/nginx:1.9.3 -n tutorial 957 | deployment.extensions/nginx image updated 958 | 959 | $ kubectl get pods -n tutorial 960 | NAME READY STATUS RESTARTS AGE 961 | nginx-d6b94d6f6-lkr8m 0/1 Terminating 0 5m58s 962 | nginx-d6b94d6f6-rh42k 1/1 Running 0 5m27s 963 | nginx-86d4667764-gxwkb 1/1 Running 0 4s 964 | nginx-86d4667764-hr6r7 0/1 ContainerCreating 0 2s 965 | ``` 966 | 967 | 过一段时间后,所有 Pod 都已经更新了: 968 | 969 | ``` 970 | $ kubectl get pods -n tutorial 971 | NAME READY STATUS RESTARTS AGE 972 | nginx-86d4667764-gxwkb 1/1 Running 0 67s 973 | nginx-86d4667764-hr6r7 1/1 Running 0 65s 974 | ``` 975 | 976 | 分析一下上述命令,`kubectl set image` 将 Deployment 中的 nginx 镜像版本改为 1.9.3;运行该命令之后,发现 977 | kubernetes 删掉了一个现有的 Pod,然后重新启动了两个新的 Pod(我们可以从一串数字中看出,"86d4667764" 是新 Pod 的 Hash 值,"d6b94d6f6" 是老 Pod 的 Hash 值)。等待一段时间后再次查询 Pods,发现所有新的 Pods 已经上线。整个过程中,我们都可以尝试去访问 nginx 服务,注意其版本的变化。 978 | 979 | ``` 980 | $ curl $(minikube ip):31658/version 981 | 982 | ... 983 |
nginx/1.9.3
984 | 985 | ``` 986 | 987 | 其中 31658 是 Service 暴露的 NodePort 端口。 988 | 989 | ## Deployment rollout 990 | 991 | rollout 子命令可以用来查询部署的状态,以及回滚等操作。使用 `kubectl rollout status` 可以查询部署的状态。 992 | 993 | ``` 994 | $ kubectl rollout status deployment nginx -n tutorial 995 | deployment "nginx" successfully rolled out 996 | ``` 997 | 998 | 上面的状态说明之前部署的 Deployment 已经正常部署了。如果我们想要回滚到之前的版本,可以使用 999 | `kubectl rollout undo` 命令。 1000 | 1001 | 首先,当前 nginx 处于 1.9.3 版本: 1002 | 1003 | ``` 1004 | $ curl $(minikube ip):31658/version 1005 | 1006 | ... 1007 |
nginx/1.9.3
1008 | 1009 | 1010 | ``` 1011 | 1012 | 接下来使用回滚操作: 1013 | 1014 | ``` 1015 | $ kubectl rollout undo deployment nginx -n tutorial 1016 | deployment.extensions/nginx rolled back 1017 | 1018 | $ kubectl get pods -n tutorial 1019 | NAME READY STATUS RESTARTS AGE 1020 | nginx-646b46d648-7c457 1/1 Running 0 9s 1021 | nginx-646b46d648-9wpdp 1/1 Running 0 6s 1022 | ``` 1023 | 1024 | 使用 rollout undo 之后,nginx 的版本回到了 set image 之前的版本: 1025 | 1026 | ``` 1027 | $ curl $(minikube ip):31658/version 1028 | 1029 | ... 1030 |
nginx/1.9.7
1031 | 1032 | 1033 | ``` 1034 | 1035 | # Kubernetes Yaml/Json File 1036 | 1037 | 在 Kubernetes 101 实验中,我们都是通过 kubectl 提供的快捷命令来创建、管理资源。实际上,对于 kubernetes 1038 | 而言,所有的操作都是以 yaml 文件为主。我们之前所使用的命令,只是方便用户快速修改 yaml 中经常需要修改的字段。接下来,我们学习 kubernetes yaml/json 文件格式和使用方法。首先,kubernetes yaml 文件的基本格式如下代码所示(这里展示的是一个 Pod 的 yaml 文件,并且有部分裁剪)。kubernetes yaml 整体分为 5 个部分:apiVersion, 1039 | kind, metadata, spec, status;其中 apiVersion 表明当前 kubernetes API 的分组;kind 表明当前操作的资源类型; 1040 | metadata 是资源的元数据,对于每种资源都是固定的,例如资源的名字,所处的 namespace, label 等;spec 1041 | 是用户对资源的 “说明书”,即用户对资源的各种配置信息;status 是资源当前的状态,kubernetes 会尽最大努力使 1042 | spec 和 status 相匹配。 1043 | 1044 | ```yaml 1045 | apiVersion: v1 1046 | kind: Pod 1047 | metadata: 1048 | creationTimestamp: "2019-06-28T08:16:45Z" 1049 | generateName: nginx-646b46d648- 1050 | labels: 1051 | name: nginx-646b46d648-7c457 1052 | namespace: tutorial 1053 | ownerReferences: 1054 | resourceVersion: "28549" 1055 | selfLink: /api/v1/namespaces/tutorial/pods/nginx-646b46d648-7c457 1056 | uid: e4312b93-9570-45a9-b981-8b26644f096c 1057 | spec: 1058 | containers: 1059 | dnsPolicy: ClusterFirst 1060 | enableServiceLinks: true 1061 | nodeName: minikube 1062 | priority: 0 1063 | restartPolicy: Always 1064 | schedulerName: default-scheduler 1065 | securityContext: {} 1066 | serviceAccount: default 1067 | serviceAccountName: default 1068 | terminationGracePeriodSeconds: 30 1069 | tolerations: 1070 | volumes: 1071 | status: 1072 | conditions: 1073 | containerStatuses: 1074 | hostIP: 10.0.2.15 1075 | phase: Running 1076 | podIP: 172.17.0.8 1077 | qosClass: Burstable 1078 | startTime: "2019-06-28T08:16:46Z" 1079 | ``` 1080 | 1081 | 接下来我们学习使用 yaml 文件。 1082 | 1083 | ## Get resource yaml 1084 | 1085 | 用户可以通过 kubectl get -o yaml 来获取已经部署的资源的 Yaml 文件,我们可以尝试获取之前通过 1086 | `kubectl apply`, `kubectl expose` 等命令部署的 Deployment 和 Service。 1087 | 1088 | ```yaml 1089 | $ kubectl get pods nginx-646b46d648-7c457 -n tutorial -o yaml 1090 | apiVersion: v1 1091 | kind: Pod 1092 | metadata: 1093 | creationTimestamp: "2019-06-28T08:16:45Z" 1094 | generateName: nginx-646b46d648- 1095 | ... 1096 | spec: 1097 | containers: 1098 | - image: cargo.caicloud.io/caicloud/nginx:1.9.7 1099 | imagePullPolicy: IfNotPresent 1100 | name: nginx 1101 | resources: 1102 | limits: 1103 | cpu: 200m 1104 | memory: 512Mi 1105 | requests: 1106 | cpu: 100m 1107 | memory: 256Mi 1108 | ... 1109 | status: 1110 | hostIP: 10.0.2.15 1111 | phase: Running 1112 | podIP: 172.17.0.8 1113 | qosClass: Burstable 1114 | startTime: "2019-06-28T08:16:46Z" 1115 | ... 1116 | ``` 1117 | 1118 | ```yaml 1119 | $ kubectl get svc nginx -n tutorial -o yaml 1120 | apiVersion: v1 1121 | kind: Service 1122 | metadata: 1123 | creationTimestamp: "2019-06-28T08:13:20Z" 1124 | labels: 1125 | run: nginx 1126 | name: nginx 1127 | namespace: tutorial 1128 | resourceVersion: "28252" 1129 | selfLink: /api/v1/namespaces/tutorial/services/nginx 1130 | uid: 4586f1da-59bf-4c9c-90fb-1d4974a0e04e 1131 | spec: 1132 | clusterIP: 10.103.177.222 1133 | externalTrafficPolicy: Cluster 1134 | ports: 1135 | - nodePort: 31658 1136 | port: 80 1137 | protocol: TCP 1138 | targetPort: 80 1139 | selector: 1140 | run: nginx 1141 | sessionAffinity: None 1142 | type: NodePort 1143 | status: 1144 | loadBalancer: {} 1145 | ``` 1146 | 1147 | ## Create resource using yaml 1148 | 1149 | 用户可通过 kubectl create -f 创建 kubernetes 资源。用户不需要填入更多的信息,所有信息都已经在 1150 | yaml 文件中。我们在 101 中已经通过 yaml 文件创建过 namespace。这里我们创建一个 Pod。 1151 | 1152 | ``` 1153 | $ kubectl create -f resources/pod.yaml -n tutorial 1154 | pod "nginx" created 1155 | 1156 | $ kubectl get pods -n tutorial 1157 | NAME READY STATUS RESTARTS AGE 1158 | nginx 1/1 Running 0 6s 1159 | nginx-646b46d648-7c457 1/1 Running 0 9m3s 1160 | nginx-646b46d648-9wpdp 1/1 Running 0 9m 1161 | ``` 1162 | 1163 | ## Update resource yaml 1164 | 1165 | 创建应用之后,我们可以使用 kubectl 更新 yaml 文件并将更新返回给 kubernetes。 1166 | 1167 | ``` 1168 | # Change image from 1.9.3 to 1.9.7 1169 | $ vim resources/pod.yaml 1170 | 1171 | $ kubectl apply -f resources/pod.yaml -n tutorial 1172 | Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply 1173 | pod/nginx configured 1174 | ``` 1175 | 1176 | 更新后查询更新结果: 1177 | 1178 | ``` 1179 | $ kubectl get pods -n tutorial 1180 | NAME READY STATUS RESTARTS AGE 1181 | nginx 1/1 Running 1 77s 1182 | nginx-646b46d648-7c457 1/1 Running 0 10m 1183 | nginx-646b46d648-9wpdp 1/1 Running 0 10m 1184 | ``` 1185 | 1186 | ``` 1187 | $ kubectl describe pods nginx -n tutorial | grep "Image:" -C 5 1188 | Status: Running 1189 | IP: 172.17.0.11 1190 | Containers: 1191 | nginx: 1192 | Container ID: docker://c3609cd17db682f38434502b23442cfac7c8defa4390b44e90139df0563d7979 1193 | Image: cargo.caicloud.io/caicloud/nginx:1.9.7 1194 | Image ID: docker-pullable://cargo.caicloud.io/caicloud/nginx@sha256:d33ae7b1dc9326bcaa91febdf37a8ea1a7340f7d6da0e2fde365a89a11201c62 1195 | Port: 1196 | Host Port: 1197 | State: Running 1198 | Started: Fri, 28 Jun 2019 16:26:40 +0800 1199 | ``` 1200 | 1201 | 注意镜像已经修改为 1.9.7。kubernetes 同时支持在线编辑 yaml 文件: 1202 | 1203 | ``` 1204 | $ kubectl get pods -n tutorial 1205 | NAME READY STATUS RESTARTS AGE 1206 | nginx 1/1 Running 1 113s 1207 | nginx-646b46d648-7c457 1/1 Running 0 10m 1208 | nginx-646b46d648-9wpdp 1/1 Running 0 10m 1209 | 1210 | # Change image from 1.9.7 to 1.9.3 1211 | $ kubectl edit pods nginx -n tutorial 1212 | pod "nginx" edited 1213 | ``` 1214 | 1215 | 同样,镜像被修改为 1.9.3。 1216 | 1217 | ``` 1218 | $ kubectl describe pods nginx -n tutorial | grep "Image:" -C 5 1219 | Status: Running 1220 | IP: 172.17.0.11 1221 | Containers: 1222 | nginx: 1223 | Container ID: docker://dc044ead5de8eb126a59c44d4477d68fb17ff0836318d5bcc3bbac0b699af44f 1224 | Image: cargo.caicloud.io/caicloud/nginx:1.9.3 1225 | Image ID: docker-pullable://cargo.caicloud.io/caicloud/nginx@sha256:ece399fcec0b3d8afa3a5abbe85e965a1f22c5f36a788396b86b541eb7e714a8 1226 | Port: 1227 | Host Port: 1228 | State: Running 1229 | Started: Fri, 28 Jun 2019 16:28:21 +0800 1230 | ``` 1231 | 1232 | ## Readings 1233 | 1234 | * [kubernetes deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) 1235 | * [run application with deployment](https://kubernetes.io/docs/tasks/run-application/run-stateless-application-deployment/) 1236 | 1237 | # Kubernetes Events 1238 | 1239 | Kubernetes events 显示了 kubernetes 集群中所有的事件。不同于其他资源,kubernetes events 并不是由用户创建的资源,而是由 kubernetes 系统组件创建,用以提示用户集群发生的各种事件。我们可以通过 kubectl get 命令来查询集群的事件。默认情况下,event 会有 TTL,超过 TTL 之后 kubernetes 会将事件删掉。 1240 | 1241 | ``` 1242 | $ kubectl get events -n tutorial 1243 | LAST SEEN TYPE REASON OBJECT MESSAGE 1244 | 12m Normal Scheduled pod/nginx-646b46d648-7c457 Successfully assigned tutorial/nginx-646b46d648-7c457 to minikube 1245 | 12m Normal Pulled pod/nginx-646b46d648-7c457 Container image "cargo.caicloud.io/caicloud/nginx:1.9.7" already present on machine 1246 | 12m Normal Created pod/nginx-646b46d648-7c457 Created container nginx 1247 | 12m Normal Started pod/nginx-646b46d648-7c457 Started container nginx 1248 | 50m Normal Scheduled pod/nginx-646b46d648-8kbk9 Successfully assigned tutorial/nginx-646b46d648-8kbk9 to minikube 1249 | 50m Normal Pulled pod/nginx-646b46d648-8kbk9 Container image "cargo.caicloud.io/caicloud/nginx:1.9.7" already present on machine 1250 | 50m Normal Created pod/nginx-646b46d648-8kbk9 Created container nginx 1251 | 50m Normal Started pod/nginx-646b46d648-8kbk9 Started container nginx 1252 | 49m Normal Killing pod/nginx-646b46d648-8kbk9 Stopping container nginx 1253 | ... 1254 | ``` 1255 | 1256 | Event 与资源是相联系的,因此单独查询 Event 并不是非常有用,我们可以通过获取资源的详细信息来查看 Event 信息。例如, 1257 | `kubectl describe pod ` 会返回 Pod 的 event 信息。 1258 | 1259 | ``` 1260 | $ kubectl describe pod nginx -n tutorial 1261 | Name: nginx 1262 | Namespace: tutorial 1263 | Priority: 0 1264 | Node: minikube/10.0.2.15 1265 | Start Time: Fri, 28 Jun 2019 16:25:42 +0800 1266 | Labels: 1267 | ... 1268 | Events: 1269 | Type Reason Age From Message 1270 | ---- ------ ---- ---- ------- 1271 | Normal Scheduled 4m20s default-scheduler Successfully assigned tutorial/nginx to minikube 1272 | Normal Pulled 3m22s kubelet, minikube Container image "cargo.caicloud.io/caicloud/nginx:1.9.7" already present on machine 1273 | Normal Pulled 101s (x2 over 4m19s) kubelet, minikube Container image "cargo.caicloud.io/caicloud/nginx:1.9.3" already present on machine 1274 | Normal Created 101s (x3 over 4m19s) kubelet, minikube Created container nginx 1275 | Normal Started 101s (x3 over 4m18s) kubelet, minikube Started container nginx 1276 | Normal Killing 101s (x2 over 3m23s) kubelet, minikube Container nginx definition changed, will be restarted 1277 | ``` 1278 | 1279 | # Kubernetes Pod Lifecycle 1280 | 1281 | Pod 生命周期主要包括: 1282 | 1283 | * Pod Phase 1284 | * Pod Condition 1285 | * Restart Policy 1286 | * Container probes 1287 | 1288 | 用户可以通过 `kubectl describe pods` 查看以上所有信息。Pod Phase 和 Pod Condition 比较简单,我们可以实时看到 1289 | kubernetes 的反馈。这里我们主要实践 Restart Policy 和 Container probes。 1290 | 1291 | ## Restart policy 1292 | 1293 | Restart Policy 指定当 Pod 内容器出错或执行完毕后,是否重启。下面的 Pod 使用了 debian 镜像,该镜像并不会长期运行,因此如果我们直接创建,kubernetes 会认为 Pod 出错。 1294 | 1295 | ``` 1296 | $ kubectl create -f resources/debian.yaml -n tutorial 1297 | pod/debian created 1298 | ``` 1299 | 1300 | 注,若提示资源不足,可以删掉现有的 Deployment 或 Pod 资源。创建之后,等待 kubernetes 拉取镜像。几分钟后, 1301 | kubernetes 提示 redis-django 进入 Crash 状态,且有多次重启: 1302 | 1303 | ``` 1304 | $ kubectl get pods -n tutorial 1305 | NAME READY STATUS RESTARTS AGE 1306 | debian 0/1 CrashLoopBackOff 1 32s 1307 | nginx 1/1 Running 2 14m 1308 | nginx-646b46d648-7c457 1/1 Running 0 23m 1309 | nginx-646b46d648-9wpdp 1/1 Running 0 23m 1310 | ``` 1311 | 1312 | 现在,我们为该 Pod 添加 Restart Policy,使 kubernetes 不再不断重启 debian 容器,从而得到以下结果: 1313 | 1314 | ``` 1315 | $ kubectl delete pods debian -n tutorial 1316 | pod "debian" deleted 1317 | 1318 | $ kubectl create -f resources/debian_never_restart.yaml -n tutorial 1319 | pod "debian" created 1320 | ``` 1321 | 1322 | debian 容器变成 `Completed` 状态: 1323 | 1324 | ``` 1325 | $ kubectl get pods -n tutorial 1326 | NAME READY STATUS RESTARTS AGE 1327 | debian 0/1 Completed 0 33s 1328 | nginx 1/1 Running 2 15m 1329 | nginx-646b46d648-7c457 1/1 Running 0 24m 1330 | nginx-646b46d648-9wpdp 1/1 Running 0 24m 1331 | ``` 1332 | 1333 | ## Container probes 1334 | 1335 | Container probes 分为两种:LivenessProbe 和 ReadinessProbe。Liveness 检查应用是否依然健康无错,若有错,则 1336 | kubernetes 会根据 policy 重启或仅更新状态。ReadinessCheck 检查应用是否可以对外提供服务,若应用 Readiness 1337 | 检查不通过,则 kubernetes 会将 Pod 从服务池中剔除。两者的使用方法都相同,这里我们来看看 Container probes。 1338 | 1339 | 打开 [pod_health.yaml](./resources/pod_health.yaml),可以看到里面定义了 livenessProbe。当我们运行创建该 Pod 的时候,kubernetes 1340 | 就开始为我们监控该 Pod 的 liveness 信息。 1341 | 1342 | ``` 1343 | $ kubectl delete pods nginx -n tutorial 1344 | pod "nginx" deleted 1345 | 1346 | $ kubectl create -f resources/pod_health.yaml -n tutorial 1347 | pod "nginx" created 1348 | ``` 1349 | 1350 | Pod 会一直处于 Running 状态: 1351 | 1352 | ``` 1353 | $ kubectl get pods -n tutorial 1354 | NAME READY STATUS RESTARTS AGE 1355 | nginx 1/1 Running 0 7s 1356 | nginx-646b46d648-7c457 1/1 Running 0 29m 1357 | nginx-646b46d648-9wpdp 1/1 Running 0 29m 1358 | ``` 1359 | 1360 | 我们可以分别尝试将 livenessProbe 的 http 80 端口改为 8080,观察 Pod 的状态。 1361 | 1362 | ``` 1363 | $ kubectl delete pods nginx -n tutorial 1364 | pod "nginx" deleted 1365 | 1366 | $ kubectl create -f resources/pod_unhealth.yaml -n tutorial 1367 | pod "nginx" created 1368 | ``` 1369 | 1370 | Pod 会首先处于 Running 状态,但是在经过一段时间之后,Pod 会变为 Crash 状态,事件里会汇报健康检查错误: 1371 | 1372 | ``` 1373 | $ kubectl describe pod nginx -n tutorial 1374 | Name: nginx 1375 | Namespace: tutorial 1376 | Priority: 0 1377 | Node: minikube/10.0.2.15 1378 | Start Time: Fri, 28 Jun 2019 16:46:22 +0800 1379 | Labels: app=nginx 1380 | Annotations: 1381 | Status: Running 1382 | IP: 172.17.0.11 1383 | Containers: 1384 | nginx: 1385 | ... 1386 | Liveness: http-get http://:8080/ delay=5s timeout=1s period=5s #success=1 #failure=3 1387 | ... 1388 | Events: 1389 | Type Reason Age From Message 1390 | ---- ------ ---- ---- ------- 1391 | Normal Scheduled 14s default-scheduler Successfully assigned tutorial/nginx to minikube 1392 | Normal Pulled 13s kubelet, minikube Container image "cargo.caicloud.io/caicloud/nginx:1.9.7" already present on machine 1393 | Normal Created 13s kubelet, minikube Created container nginx 1394 | Normal Started 13s kubelet, minikube Started container nginx 1395 | Warning Unhealthy 3s (x2 over 8s) kubelet, minikube Liveness probe failed: Get http://172.17.0.11:8080/: dial tcp 172.17.0.11:8080: connect: connection refused 1396 | ``` 1397 | 1398 | ## Readings 1399 | 1400 | * [kubernetes lifecycle](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/) 1401 | * [define probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/) 1402 | 1403 | # Kubernetes ConfigMap & Secret 1404 | 1405 | ConfigMap 是 kubernetes 用来管理配置信息的资源类型。我们通过单独创建 ConfigMap,再将 ConfigMap 挂载到 1406 | Pod 内的方式分离配置和应用。我们通过一个实验来学习如何正确使用 ConfigMap。 1407 | 1408 | 创建 ConfigMap 可以通过 yaml 文件,也可以从文件直接创建。通过 yaml 文件的方式与创建其他资源类似。这里,我们采用文件的方式。在 `resources` 目录下,有两个文件:[game.properties](./resources/game.properties) 和 [ui.properties](./resources/ui.properties)。我们通过 1409 | kubectl 命令创建: 1410 | 1411 | ``` 1412 | $ kubectl create configmap game-config --from-file=resources/game.properties --from-file=resources/ui.properties -n tutorial 1413 | configmap/game-config created 1414 | ``` 1415 | 1416 | 创建之后,通过 kubectl get configmap 来查看创建的 ConfigMap: 1417 | 1418 | ``` 1419 | $ kubectl get configmap game-config -o wide -n tutorial 1420 | NAME DATA AGE 1421 | game-config 2 2m 1422 | ``` 1423 | 1424 | ``` 1425 | $ kubectl describe configmap game-config -n tutorial 1426 | Name: game-config 1427 | Namespace: tutorial 1428 | Labels: 1429 | Annotations: 1430 | 1431 | Data 1432 | ==== 1433 | game.properties: 1434 | ---- 1435 | enemies=aliens 1436 | lives=3 1437 | enemies.cheat=true 1438 | enemies.cheat.level=noGoodRotten 1439 | secret.code.passphrase=UUDDLRLRBABAS 1440 | secret.code.allowed=true 1441 | secret.code.lives=30 1442 | ui.properties: 1443 | ---- 1444 | color.good=purple 1445 | color.bad=yellow 1446 | allow.textmode=true 1447 | how.nice.to.look=fairlyNice 1448 | Events: 1449 | ``` 1450 | 1451 | 查看详情: 1452 | 1453 | ``` 1454 | $ kubectl get configmap game-config -o yaml -n tutorial 1455 | apiVersion: v1 1456 | data: 1457 | game.properties: |- 1458 | enemies=aliens 1459 | lives=3 1460 | enemies.cheat=true 1461 | enemies.cheat.level=noGoodRotten 1462 | secret.code.passphrase=UUDDLRLRBABAS 1463 | secret.code.allowed=true 1464 | secret.code.lives=30 1465 | ui.properties: |- 1466 | color.good=purple 1467 | color.bad=yellow 1468 | allow.textmode=true 1469 | how.nice.to.look=fairlyNice 1470 | kind: ConfigMap 1471 | metadata: 1472 | creationTimestamp: "2019-06-28T08:49:20Z" 1473 | name: game-config 1474 | namespace: tutorial 1475 | resourceVersion: "31335" 1476 | selfLink: /api/v1/namespaces/tutorial/configmaps/game-config 1477 | uid: 134781b7-5565-4037-b0b2-be42767255a0 1478 | ``` 1479 | 1480 | 创建 ConfigMap 之后,我们可以创建 Pod 来使用该 ConfigMap: 1481 | 1482 | ``` 1483 | $ kubectl create -f resources/pod_configmap.yaml -n tutorial 1484 | pod/pod-configmap created 1485 | ``` 1486 | 1487 | 查看: 1488 | 1489 | ``` 1490 | $ kubectl get pods -n tutorial 1491 | NAME READY STATUS RESTARTS AGE 1492 | pod-configmap 0/1 Completed 0 2m 1493 | 1494 | $ kubectl logs pod-configmap -n tutorial 1495 | enemies=aliens 1496 | lives=3 1497 | enemies.cheat=true 1498 | enemies.cheat.level=noGoodRotten 1499 | secret.code.passphrase=UUDDLRLRBABAS 1500 | secret.code.allowed=true 1501 | secret.code.lives=30color.good=purple 1502 | color.bad=yellow 1503 | allow.textmode=true 1504 | how.nice.to.look=fairlyNice 1505 | ``` 1506 | 1507 | 这里我们看到了通过挂载文件的方式使用 configmap,kubernetes 同时也支持通过环境变量的方式使用 configmap。此外,Secret 的使用方式与 Configmap 类似,但内容会被加密。 1508 | 1509 | ## Readings 1510 | 1511 | * [kubernetes configmap](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/) 1512 | * [distribute secret](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/) 1513 | 1514 | 1515 | # Summary 1516 | 1517 | 本节介绍了 Kubernetes deployment 的更多操作, Yaml 文件规范,事件,Pod 生命周期等,接下来会介绍其他应用相关模块,存储。为确保已经掌握上面知识,请思考下面的问题。 1518 | 1519 | 目前为止简单介绍了 Kubernetes command line, Pod, Deployment, Service 和 Label。这些都是 kubernetes 1520 | 中最为核心的概念,接下来我们将深入了解 Deployment 的功能,然后开始涉及其他模块。本节暂无实验内容。 1521 | 1522 | # Exercise 1523 | 1524 | 以下问题是在 kubernetes 使用或运维中较常使用的命令或方式方法。 1525 | 1526 | 1. 学习 kubectl proxy 命令及其含义。回答如何通过 proxy 访问 kubernetes 集群? 1527 | 1. 学习 kubectl port-forward 命令及其含义。回答如何通过 port-forward 访问应用? 1528 | 1. 修改 Pod label 使其与 Deployment 不相符,集群有什么变化? 1529 | 1. 进一步学习 kubectl rollout。回答如何通过 kubectl rollout 将应用回滚到指定版本? 1530 | 1. Pod LivenessProbe 实验中,检查方式采用的是 http 模式。回答如何使用 exec 进行健康检查?请写出 yaml 文件。 1531 | 1. 进一步学习 Pod Lifecycle。回答如何使用 PostStart Hook?请写出 yaml 文件。 1532 | 1. 登录宿主机,使用 docker ps 查看 Pod,如何理解 docker ps 输出? 1533 | 1. 学习使用 Secret,然后创建一个 Secret 并在 Pod 内访问。请写出 secret 和 pod 的 yaml 文件。 1534 | 1. ConfigMap 实验中,我们采用文件加载的方式使用 ConfigMap。请写出利用环境变量加载 configmap 的例子。 1535 | -------------------------------------------------------------------------------- /tutorials/lab3-manual-installtion.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* 4 | 5 | - [Kubernetes The Hard Way](#kubernetes-the-hard-way) 6 | - [目标](#%E7%9B%AE%E6%A0%87) 7 | - [假定](#%E5%81%87%E5%AE%9A) 8 | - [准备环境](#%E5%87%86%E5%A4%87%E7%8E%AF%E5%A2%83) 9 | - [安装必要工具](#%E5%AE%89%E8%A3%85%E5%BF%85%E8%A6%81%E5%B7%A5%E5%85%B7) 10 | - [安装 CFSSL](#%E5%AE%89%E8%A3%85-cfssl) 11 | - [Linux](#linux) 12 | - [验证](#%E9%AA%8C%E8%AF%81) 13 | - [安装 Kubectl](#%E5%AE%89%E8%A3%85-kubectl) 14 | - [Linux](#linux-1) 15 | - [验证](#%E9%AA%8C%E8%AF%81-1) 16 | - [配置 CA 并创建 TLS 证书](#%E9%85%8D%E7%BD%AE-ca-%E5%B9%B6%E5%88%9B%E5%BB%BA-tls-%E8%AF%81%E4%B9%A6) 17 | - [Certificate Authority](#certificate-authority) 18 | - [client 与 server 凭证](#client-%E4%B8%8E-server-%E5%87%AD%E8%AF%81) 19 | - [Admin 客户端凭证](#admin-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%87%AD%E8%AF%81) 20 | - [Kubelet 客户端凭证](#kubelet-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%87%AD%E8%AF%81) 21 | - [Kube-controller-manager 客户端凭证](#kube-controller-manager-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%87%AD%E8%AF%81) 22 | - [Kube-proxy 客户端凭证](#kube-proxy-%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%87%AD%E8%AF%81) 23 | - [kube-scheduler 证书](#kube-scheduler-%E8%AF%81%E4%B9%A6) 24 | - [Kubernetes API Server 证书](#kubernetes-api-server-%E8%AF%81%E4%B9%A6) 25 | - [Service Account 证书](#service-account-%E8%AF%81%E4%B9%A6) 26 | - [分发客户端和服务器证书](#%E5%88%86%E5%8F%91%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%92%8C%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%AF%81%E4%B9%A6) 27 | - [配置和生成 Kubernetes 配置文件](#%E9%85%8D%E7%BD%AE%E5%92%8C%E7%94%9F%E6%88%90-kubernetes-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 28 | - [客户端认证配置](#%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%AE%A4%E8%AF%81%E9%85%8D%E7%BD%AE) 29 | - [kubelet 配置文件](#kubelet-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 30 | - [kube-proxy 配置文件](#kube-proxy-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 31 | - [kube-controller-manager 配置文件](#kube-controller-manager-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 32 | - [kube-scheduler 配置文件](#kube-scheduler-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 33 | - [Admin 配置文件](#admin-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 34 | - [分发配置文件](#%E5%88%86%E5%8F%91%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 35 | - [配置和生成密钥](#%E9%85%8D%E7%BD%AE%E5%92%8C%E7%94%9F%E6%88%90%E5%AF%86%E9%92%A5) 36 | - [加密密钥](#%E5%8A%A0%E5%AF%86%E5%AF%86%E9%92%A5) 37 | - [加密配置文件](#%E5%8A%A0%E5%AF%86%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 38 | - [部署 etcd 群集](#%E9%83%A8%E7%BD%B2-etcd-%E7%BE%A4%E9%9B%86) 39 | - [下载并安装 etcd 二进制文件](#%E4%B8%8B%E8%BD%BD%E5%B9%B6%E5%AE%89%E8%A3%85-etcd-%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%96%87%E4%BB%B6) 40 | - [配置 etcd Server](#%E9%85%8D%E7%BD%AE-etcd-server) 41 | - [启动 etcd Server](#%E5%90%AF%E5%8A%A8-etcd-server) 42 | - [验证](#%E9%AA%8C%E8%AF%81-2) 43 | - [部署 Kubernetes 控制节点](#%E9%83%A8%E7%BD%B2-kubernetes-%E6%8E%A7%E5%88%B6%E8%8A%82%E7%82%B9) 44 | - [创建 Kubernetes 配置目录](#%E5%88%9B%E5%BB%BA-kubernetes-%E9%85%8D%E7%BD%AE%E7%9B%AE%E5%BD%95) 45 | - [下载并安装 Kubernetes Controller 二进制文件](#%E4%B8%8B%E8%BD%BD%E5%B9%B6%E5%AE%89%E8%A3%85-kubernetes-controller-%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%96%87%E4%BB%B6) 46 | - [配置 Kubernetes API Server](#%E9%85%8D%E7%BD%AE-kubernetes-api-server) 47 | - [配置 Kubernetes Controller Manager](#%E9%85%8D%E7%BD%AE-kubernetes-controller-manager) 48 | - [配置 Kubernetes Scheduler](#%E9%85%8D%E7%BD%AE-kubernetes-scheduler) 49 | - [启动控制器服务](#%E5%90%AF%E5%8A%A8%E6%8E%A7%E5%88%B6%E5%99%A8%E6%9C%8D%E5%8A%A1) 50 | - [验证](#%E9%AA%8C%E8%AF%81-3) 51 | - [Kubelet RBAC 授权](#kubelet-rbac-%E6%8E%88%E6%9D%83) 52 | - [部署 Kubernetes Workers 节点](#%E9%83%A8%E7%BD%B2-kubernetes-workers-%E8%8A%82%E7%82%B9) 53 | - [安装依赖](#%E5%AE%89%E8%A3%85%E4%BE%9D%E8%B5%96) 54 | - [配置 Kubelet](#%E9%85%8D%E7%BD%AE-kubelet) 55 | - [配置 Kube-Proxy](#%E9%85%8D%E7%BD%AE-kube-proxy) 56 | - [启动 worker 服务](#%E5%90%AF%E5%8A%A8-worker-%E6%9C%8D%E5%8A%A1) 57 | - [配置 Kubectl](#%E9%85%8D%E7%BD%AE-kubectl) 58 | - [admin kubeconfig](#admin-kubeconfig) 59 | - [验证](#%E9%AA%8C%E8%AF%81-4) 60 | - [配置 Pod 网络路由](#%E9%85%8D%E7%BD%AE-pod-%E7%BD%91%E7%BB%9C%E8%B7%AF%E7%94%B1) 61 | - [安装](#%E5%AE%89%E8%A3%85) 62 | - [部署 DNS 扩展](#%E9%83%A8%E7%BD%B2-dns-%E6%89%A9%E5%B1%95) 63 | - [DNS 扩展](#dns-%E6%89%A9%E5%B1%95) 64 | - [验证](#%E9%AA%8C%E8%AF%81-5) 65 | - [烟雾测试](#%E7%83%9F%E9%9B%BE%E6%B5%8B%E8%AF%95) 66 | - [数据加密](#%E6%95%B0%E6%8D%AE%E5%8A%A0%E5%AF%86) 67 | - [部署](#%E9%83%A8%E7%BD%B2) 68 | - [端口转发](#%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91) 69 | - [容器日志](#%E5%AE%B9%E5%99%A8%E6%97%A5%E5%BF%97) 70 | - [执行容器命令](#%E6%89%A7%E8%A1%8C%E5%AE%B9%E5%99%A8%E5%91%BD%E4%BB%A4) 71 | - [服务(Service)](#%E6%9C%8D%E5%8A%A1service) 72 | 73 | 74 | 75 | # Kubernetes The Hard Way 76 | 77 | 本文主要翻译自 [Kubernetes The Hard Way](https://github.com/kelseyhightower/kubernetes-the-hard-way),并做了部分调整,针对裸机(Bare-metal)部署。 78 | 79 | ## 目标 80 | 81 | 本教程将指引你在裸机(Bare-metal)上手动部署一套 Kubernetes 集群。它不适用于想要一键自动化部署 Kubernetes 集群的人。如果你想要自动化部署,请参考 [lab1](./lab1-installation.md) 或者 [kubeadm](https://github.com/kubernetes/kubeadm) 82 | 83 | 本文的主要目的是学习, 也就是说它会花很多时间来保障读者可以真正理解搭建 Kubernetes 的每个步骤。 84 | 85 | ## 假定 86 | 87 | - 本文基于 Kubernetes v1.15.0 88 | - Cluster Pod CIDR: 10.244.0.0/16 89 | - Cluster Service CIDR: 10.250.0.0/24 90 | - `kubernetes` service: 10.250.0.1 91 | - `dns` service: 10.250.0.10 92 | 93 | ## 准备环境 94 | 95 | - 至少两台 2C 4G 的 VM,装有 CentOS 7 96 | - 确保 VM 可以访问 Internet 97 | - 确保 VM 间网络并关闭了防火墙 98 | - 确保 VM 间可以通过 hostname 互相访问,即,假如,VM1 的 hostname 是 master,VM2 的 hostname 是 worker-1,那么 VM1 需要能够成功执行 `ping worker-1`。(或许你需要修改 hostname & `/etc/hosts`) 99 | 100 | **并在准备好的所有节点上,执行以下操作:** 101 | 102 | 如果各个主机启用了防火墙,需要开放 k8s 各个组件所需要的端口,可以查看 [Installing kubeadm - Check required ports](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#check-required-ports)。 103 | 这里简单起见直接禁用防火墙: 104 | 105 | ```bash 106 | systemctl stop firewalld 107 | systemctl disable firewalld 108 | ``` 109 | 110 | 禁用 SELinux: 111 | 112 | ```bash 113 | # Set SELinux in permissive mode (effectively disabling it) 114 | setenforce 0 115 | sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config 116 | ``` 117 | 118 | 关闭 swap: 119 | 120 | ```bash 121 | swapoff -a 122 | ``` 123 | 124 | CentOS 7 用户还需要执行: 125 | 126 | ```bash 127 | cat < /etc/sysctl.d/k8s.conf 128 | net.bridge.bridge-nf-call-ip6tables = 1 129 | net.bridge.bridge-nf-call-iptables = 1 130 | EOF 131 | sysctl --system 132 | ``` 133 | 134 | **除非另有说明,以下所有操作都是在 master 上执行。** 135 | 136 | ## 安装必要工具 137 | 138 | 本次实验你将会安装一些实用的命令行工具, 用来完成这份指南,这包括 [cfssl](https://github.com/cloudflare/cfssl)、[cfssljson](https://github.com/cloudflare/cfssl) 以及 [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl)。 139 | 140 | ### 安装 CFSSL 141 | 142 | cfssl 和 cfssljson 命令行工具用于提供 PKI Infrastructure 基础设施与生成 TLS 证书。 143 | 144 | #### Linux 145 | 146 | ```bash 147 | # 或许你需要先执行 `yum install -y wget` 以安装 wget 148 | wget --timestamping \ 149 | https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 \ 150 | https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 151 | chmod +x cfssl_linux-amd64 cfssljson_linux-amd64 152 | sudo mv cfssl_linux-amd64 /usr/local/bin/cfssl 153 | sudo mv cfssljson_linux-amd64 /usr/local/bin/cfssljson 154 | ``` 155 | 156 | #### 验证 157 | 158 | 验证 cfssl 的版本为 1.2.0 或是更高 159 | 160 | ``` 161 | $ cfssl version 162 | Version: 1.2.0 163 | Revision: dev 164 | Runtime: go1.6 165 | ``` 166 | 167 | **注意**:cfssljson 命令行工具没有提供查询版本的方法。 168 | 169 | ### 安装 Kubectl 170 | 171 | kubectl 命令行工具用来与 Kubernetes API Server 交互,可以在 Kubernetes 官方网站下载并安装 kubectl。 172 | 173 | #### Linux 174 | 175 | ```bash 176 | wget https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/linux/amd64/kubectl 177 | chmod +x kubectl 178 | sudo mv kubectl /usr/local/bin/ 179 | ``` 180 | 181 | #### 验证 182 | 183 | 验证 kubectl 的安装版本为 1.15.0 或是更高 184 | 185 | ```bash 186 | $ kubectl version --client 187 | Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.0", GitCommit:"e8462b5b5dc2584fdcd18e6bcfe9f1e4d970a529", GitTreeState:"clean", BuildDate:"2019-06-19T16:40:16Z", GoVersion:"go1.12.5", Compiler:"gc", Platform:"linux/amd64"} 188 | ``` 189 | 190 | ## 配置 CA 并创建 TLS 证书 191 | 192 | 我们将使用 CloudFlare's PKI 工具 [cfssl](https://github.com/cloudflare/cfssl) 来配置 [PKI Infrastructure](https://en.wikipedia.org/wiki/Public_key_infrastructure 193 | ),然后使用它去创建 Certificate Authority(CA),并为 etcd、kube-apiserver、kubelet 以及 kube-proxy 创建 TLS 证书。 194 | 195 | ### Certificate Authority 196 | 197 | 本节创建用于生成其他 TLS 证书的 Certificate Authority。 198 | 199 | 新建 CA 配置文件 200 | 201 | ```sh 202 | cat > ca-config.json < ca-csr.json < admin-csr.json <` 的凭证来证明它属于 `system:nodes` 用户组。本节将会给每台 worker 节点创建符合 Node Authorizer 要求的凭证。 307 | 308 | 给每台 worker 节点创建凭证和私钥: 309 | 310 | ```sh 311 | # 此处只给 worker-1 创建,若你有多个 worker 节点,请替换相应的值并继续操作 312 | export WORKER1_HOSTNAME=worker-1 313 | # 请替换为你所用节点的 IP 314 | export WORKER1_IP=192.168.133.42 315 | 316 | cat > worker-1-csr.json < kube-controller-manager-csr.json < kube-proxy-csr.json < kube-scheduler-csr.json < kubernetes-csr.json < service-account-csr.json < `kube-proxy`、`kube-controller-manager`、`kube-scheduler` 和 `kubelet` 客户端凭证将会在下一节中用来创建客户端签发请求文件。 564 | 565 | ## 配置和生成 Kubernetes 配置文件 566 | 567 | 本部分内容将会创建 [kubeconfig 配置文件](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/),它们是 Kubernetes 客户端与 API Server 认证与鉴权的保证。 568 | 569 | ### 客户端认证配置 570 | 571 | 本节将会创建用于 `kube-proxy`、`kube-controller-manager`、`kube-scheduler` 和 `kubelet` 的 kubeconfig 文件。 572 | 573 | #### kubelet 配置文件 574 | 575 | 为了确保 [Node Authorizer](https://kubernetes.io/docs/admin/authorization/node/) 授权,Kubelet 配置文件中的客户端证书必需匹配 Node 名字。 576 | 577 | 为每个 worker 节点创建 kubeconfig 配置: 578 | 579 | ```sh 580 | kubectl config set-cluster kubernetes-training \ 581 | --certificate-authority=ca.pem \ 582 | --embed-certs=true \ 583 | --server=https://${MASTER_IP}:6443 \ 584 | --kubeconfig=worker-1.kubeconfig 585 | 586 | kubectl config set-credentials system:node:worker-1 \ 587 | --client-certificate=worker-1.pem \ 588 | --client-key=worker-1-key.pem \ 589 | --embed-certs=true \ 590 | --kubeconfig=worker-1.kubeconfig 591 | 592 | kubectl config set-context default \ 593 | --cluster=kubernetes-training \ 594 | --user=system:node:worker-1 \ 595 | --kubeconfig=worker-1.kubeconfig 596 | 597 | kubectl config use-context default --kubeconfig=worker-1.kubeconfig 598 | ``` 599 | 600 | 结果将会生成以下文件: 601 | 602 | ```sh 603 | worker-1.kubeconfig 604 | ``` 605 | 606 | #### kube-proxy 配置文件 607 | 608 | 为 kube-proxy 服务生成 kubeconfig 配置文件: 609 | 610 | ```sh 611 | kubectl config set-cluster kubernetes-training \ 612 | --certificate-authority=ca.pem \ 613 | --embed-certs=true \ 614 | --server=https://${MASTER_IP}:6443 \ 615 | --kubeconfig=kube-proxy.kubeconfig 616 | 617 | kubectl config set-credentials system:kube-proxy \ 618 | --client-certificate=kube-proxy.pem \ 619 | --client-key=kube-proxy-key.pem \ 620 | --embed-certs=true \ 621 | --kubeconfig=kube-proxy.kubeconfig 622 | 623 | kubectl config set-context default \ 624 | --cluster=kubernetes-training \ 625 | --user=system:kube-proxy \ 626 | --kubeconfig=kube-proxy.kubeconfig 627 | 628 | kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig 629 | ``` 630 | 631 | #### kube-controller-manager 配置文件 632 | 633 | ```sh 634 | kubectl config set-cluster kubernetes-training \ 635 | --certificate-authority=ca.pem \ 636 | --embed-certs=true \ 637 | --server=https://127.0.0.1:6443 \ 638 | --kubeconfig=kube-controller-manager.kubeconfig 639 | 640 | kubectl config set-credentials system:kube-controller-manager \ 641 | --client-certificate=kube-controller-manager.pem \ 642 | --client-key=kube-controller-manager-key.pem \ 643 | --embed-certs=true \ 644 | --kubeconfig=kube-controller-manager.kubeconfig 645 | 646 | kubectl config set-context default \ 647 | --cluster=kubernetes-training \ 648 | --user=system:kube-controller-manager \ 649 | --kubeconfig=kube-controller-manager.kubeconfig 650 | 651 | kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig 652 | ``` 653 | 654 | #### kube-scheduler 配置文件 655 | 656 | ```sh 657 | kubectl config set-cluster kubernetes-training \ 658 | --certificate-authority=ca.pem \ 659 | --embed-certs=true \ 660 | --server=https://127.0.0.1:6443 \ 661 | --kubeconfig=kube-scheduler.kubeconfig 662 | 663 | kubectl config set-credentials system:kube-scheduler \ 664 | --client-certificate=kube-scheduler.pem \ 665 | --client-key=kube-scheduler-key.pem \ 666 | --embed-certs=true \ 667 | --kubeconfig=kube-scheduler.kubeconfig 668 | 669 | kubectl config set-context default \ 670 | --cluster=kubernetes-training \ 671 | --user=system:kube-scheduler \ 672 | --kubeconfig=kube-scheduler.kubeconfig 673 | 674 | kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig 675 | ``` 676 | 677 | #### Admin 配置文件 678 | 679 | ```sh 680 | kubectl config set-cluster kubernetes-training \ 681 | --certificate-authority=ca.pem \ 682 | --embed-certs=true \ 683 | --server=https://127.0.0.1:6443 \ 684 | --kubeconfig=admin.kubeconfig 685 | 686 | kubectl config set-credentials admin \ 687 | --client-certificate=admin.pem \ 688 | --client-key=admin-key.pem \ 689 | --embed-certs=true \ 690 | --kubeconfig=admin.kubeconfig 691 | 692 | kubectl config set-context default \ 693 | --cluster=kubernetes-training \ 694 | --user=admin \ 695 | --kubeconfig=admin.kubeconfig 696 | 697 | kubectl config use-context default --kubeconfig=admin.kubeconfig 698 | ``` 699 | 700 | #### 分发配置文件 701 | 702 | 将 `kubelet` 与 `kube-proxy` kubeconfig 配置文件复制到每个 worker 节点上: 703 | 704 | ```sh 705 | scp worker-1.kubeconfig kube-proxy.kubeconfig worker-1:~/ 706 | ``` 707 | 708 | 将 `admin`、`kube-controller-manager` 与 `kube-scheduler` kubeconfig 配置文件复制到每个控制节点上: 709 | 710 | ```sh 711 | scp admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig master:~/ 712 | ``` 713 | 714 | ## 配置和生成密钥 715 | 716 | Kubernetes 存储了集群状态、应用配置和密钥等很多不同的数据。而 Kubernetes 也支持集群数据的加密存储。 717 | 718 | 本部分将会创建加密密钥以及一个用于加密 Kubernetes Secrets 的 [加密配置文件](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/#understanding-the-encryption-at-rest-configuration)。 719 | 720 | ### 加密密钥 721 | 722 | 建立加密密钥: 723 | 724 | ```sh 725 | ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64) 726 | ``` 727 | 728 | ## 加密配置文件 729 | 730 | 生成名为 `encryption-config.yaml` 的加密配置文件: 731 | 732 | ```sh 733 | cat > encryption-config.yaml < 输出 836 | 837 | ```sh 838 | 45ad218d293b8803, started, master, https://192.168.133.41:2380, http://127.0.0.1:2379,https://192.168.133.41:2379 839 | ``` 840 | 841 | ## 部署 Kubernetes 控制节点 842 | 843 | 本部分将会在控制节点上部署 Kubernetes 控制服务。每个控制节点上需要部署的服务包括:Kubernetes API Server、Scheduler 以及 Controller Manager 等。 844 | 845 | ### 创建 Kubernetes 配置目录 846 | 847 | ```sh 848 | sudo mkdir -p /etc/kubernetes/config 849 | ``` 850 | 851 | ### 下载并安装 Kubernetes Controller 二进制文件 852 | 853 | ```sh 854 | wget --timestamping \ 855 | "https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/linux/amd64/kube-apiserver" \ 856 | "https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/linux/amd64/kube-controller-manager" \ 857 | "https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/linux/amd64/kube-scheduler" \ 858 | "https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/linux/amd64/kubectl" 859 | 860 | chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl 861 | sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/ 862 | ``` 863 | 864 | ### 配置 Kubernetes API Server 865 | 866 | ```sh 867 | sudo mkdir -p /var/lib/kubernetes/ 868 | 869 | sudo mv ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \ 870 | service-account-key.pem service-account.pem \ 871 | encryption-config.yaml /var/lib/kubernetes/ 872 | ``` 873 | 874 | 生成 `kube-apiserver.service` systemd 配置文件: 875 | 876 | ```sh 877 | cat < 请等待 10 秒以便 Kubernetes API Server 初始化。 998 | 999 | ### 验证 1000 | 1001 | ```sh 1002 | kubectl get componentstatuses --kubeconfig admin.kubeconfig 1003 | ``` 1004 | 1005 | 将输出结果 1006 | 1007 | ```sh 1008 | NAME STATUS MESSAGE ERROR 1009 | controller-manager Healthy ok 1010 | scheduler Healthy ok 1011 | etcd-0 Healthy {"health":"true"} 1012 | ``` 1013 | 1014 | ### Kubelet RBAC 授权 1015 | 1016 | 本节将会配置 API Server 访问 Kubelet API 的 RBAC 授权。访问 Kubelet API 是获取 metrics、日志以及执行容器命令所必需的。 1017 | 1018 | > 这里设置 Kubelet `--authorization-mode` 为 `Webhook` 模式。Webhook 模式使用 [SubjectAccessReview](https://kubernetes.io/docs/admin/authorization/#checking-api-access) API 来决定授权。 1019 | 1020 | 创建 `system:kube-apiserver-to-kubelet` [ClusterRole](https://kubernetes.io/docs/admin/authorization/rbac/#role-and-clusterrole) 以允许请求 Kubelet API 和执行大部分来管理 Pods 的任务: 1021 | 1022 | ```sh 1023 | cat < socat 命令用于支持 `kubectl port-forward` 命令。 1086 | 1087 | 安装 Docker: 1088 | 1089 | ```sh 1090 | sudo yum install -y yum-utils device-mapper-persistent-data lvm2 1091 | 1092 | sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 1093 | 1094 | sudo yum install -y docker-ce docker-ce-cli containerd.io 1095 | 1096 | sudo systemctl enable docker 1097 | sudo systemctl start docker 1098 | ``` 1099 | 1100 | 下载 worker 二进制文件: 1101 | 1102 | ```sh 1103 | wget --timestamping \ 1104 | https://github.com/containernetworking/plugins/releases/download/v0.6.0/cni-plugins-amd64-v0.6.0.tgz \ 1105 | https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/linux/amd64/kubectl \ 1106 | https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/linux/amd64/kube-proxy \ 1107 | https://storage.googleapis.com/kubernetes-release/release/v1.15.0/bin/linux/amd64/kubelet 1108 | ``` 1109 | 1110 | 创建安装目录: 1111 | 1112 | ```sh 1113 | sudo mkdir -p \ 1114 | /opt/cni/bin \ 1115 | /var/lib/kubelet \ 1116 | /var/lib/kube-proxy \ 1117 | /var/lib/kubernetes \ 1118 | /var/run/kubernetes 1119 | ``` 1120 | 1121 | 安装 worker 二进制文件 1122 | 1123 | ```sh 1124 | chmod +x kubectl kube-proxy kubelet 1125 | sudo mv kubectl kube-proxy kubelet /usr/local/bin/ 1126 | ``` 1127 | 1128 | ### 配置 Kubelet 1129 | 1130 | ```sh 1131 | sudo cp worker-1-key.pem worker-1.pem /var/lib/kubelet/ 1132 | sudo cp worker-1.kubeconfig /var/lib/kubelet/kubeconfig 1133 | sudo cp ca.pem /var/lib/kubernetes/ 1134 | sudo tar -xvf cni-plugins-amd64-v0.6.0.tgz -C /opt/cni/bin/ 1135 | ``` 1136 | 1137 | 生成 `kubelet.service` systemd 配置文件: 1138 | 1139 | ```sh 1140 | # The resolvConf configuration is used to avoid loops 1141 | # when using CoreDNS for service discovery on systems running systemd-resolved. 1142 | cat < 记得在所有 worker 节点上面都运行一遍。 1229 | 1230 | ## 配置 Kubectl 1231 | 1232 | 本部分将生成一个用于 admin 用户的 kubeconfig 文件。 1233 | 1234 | > 注意:在生成 admin 客户端证书的目录来运行本部分的指令。 1235 | 1236 | ### admin kubeconfig 1237 | 1238 | 为 `admin` 用户生成 kubeconfig 文件: 1239 | 1240 | ```sh 1241 | # 请替换为你所用节点的 IP 1242 | export MASTER_IP=192.168.133.41 1243 | 1244 | kubectl config set-cluster kubernetes-training \ 1245 | --certificate-authority=ca.pem \ 1246 | --embed-certs=true \ 1247 | --server=https://${MASTER_IP}:6443 1248 | 1249 | kubectl config set-credentials admin \ 1250 | --client-certificate=admin.pem \ 1251 | --client-key=admin-key.pem 1252 | 1253 | kubectl config set-context kubernetes-training \ 1254 | --cluster=kubernetes-training \ 1255 | --user=admin 1256 | 1257 | kubectl config use-context kubernetes-training 1258 | ``` 1259 | 1260 | ### 验证 1261 | 1262 | 检查远端 Kubernetes 集群的健康状况: 1263 | 1264 | ```sh 1265 | kubectl get componentstatuses 1266 | ``` 1267 | 1268 | 输出为 1269 | 1270 | ```sh 1271 | NAME STATUS MESSAGE ERROR 1272 | controller-manager Healthy ok 1273 | scheduler Healthy ok 1274 | etcd-0 Healthy {"health":"true"} 1275 | ``` 1276 | 1277 | 列出远端 kubernetes cluster 的节点: 1278 | 1279 | ```sh 1280 | kubectl get nodes 1281 | ``` 1282 | 1283 | 输出为 1284 | 1285 | ```sh 1286 | NAME STATUS ROLES AGE VERSION 1287 | worker-1 NotReady 9m59s v1.15.0 1288 | ``` 1289 | 1290 | 此时节点已经注册成功了,但节点的状态仍然是 `NotReady`,我们需要继续安装集群网络以使其 `Ready`。 1291 | 1292 | ## 配置 Pod 网络路由 1293 | 1294 | 每个 Pod 都会从所在 Node 的 Pod CIDR 中分配一个 IP 地址。由于网络还没有配置,跨节点的 Pod 之间还无法通信。 1295 | 1296 | 我们可以选择 [Cluster Networking](https://kubernetes.io/docs/concepts/cluster-administration/networking/#how-to-achieve-this) 来实现 Kubernetes 网络模型。 1297 | 1298 | ### 安装 1299 | 1300 | 这里我们选择安装 [flannel](https://github.com/coreos/flannel): 1301 | 1302 | ```sh 1303 | kubectl apply -f ./resources/kube-flannel.yaml 1304 | ``` 1305 | 1306 | ## 部署 DNS 扩展 1307 | 1308 | 本部分将部署 [DNS 扩展](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/),用于为集群内的应用提供服务发现。 1309 | 1310 | ### DNS 扩展 1311 | 1312 | 部属 `kube-dns` 群集扩展: 1313 | 1314 | ```sh 1315 | kubectl apply -f ./resources/coredns.yaml 1316 | ``` 1317 | 1318 | 列出 `kube-dns` 部署的 Pod 列表: 1319 | 1320 | ```sh 1321 | kubectl get pods -l k8s-app=kube-dns -n kube-system 1322 | ``` 1323 | 1324 | 输出为 1325 | 1326 | ```sh 1327 | NAME READY STATUS RESTARTS AGE 1328 | coredns-699f8ddd77-94qv9 1/1 Running 0 20s 1329 | coredns-699f8ddd77-gtcgb 1/1 Running 0 20s 1330 | ``` 1331 | 1332 | ### 验证 1333 | 1334 | 建立一个 `busybox` 部署: 1335 | 1336 | ```sh 1337 | kubectl run busybox --image=busybox:1.28.3 --command -- sleep 3600 1338 | ``` 1339 | 1340 | 列出 `busybox` 部署的 Pod: 1341 | 1342 | ```sh 1343 | kubectl get pods -l run=busybox 1344 | ``` 1345 | 1346 | 输出为 1347 | 1348 | ```sh 1349 | NAME READY STATUS RESTARTS AGE 1350 | busybox-d967695b6-29hfh 1/1 Running 0 61s 1351 | ``` 1352 | 1353 | 查询 `busybox` Pod 的全名: 1354 | 1355 | ```sh 1356 | POD_NAME=$(kubectl get pods -l run=busybox -o jsonpath="{.items[0].metadata.name}") 1357 | ``` 1358 | 1359 | 在 `busybox` Pod 中查询 DNS: 1360 | 1361 | ```sh 1362 | kubectl exec -ti $POD_NAME -- nslookup kubernetes 1363 | ``` 1364 | 1365 | 输出为 1366 | 1367 | ```sh 1368 | Server: 10.32.0.10 1369 | Address 1: 10.32.0.10 kube-dns.kube-system.svc.cluster.local 1370 | 1371 | Name: kubernetes 1372 | Address 1: 10.32.0.1 kubernetes.default.svc.cluster.local 1373 | ``` 1374 | 1375 | ## 烟雾测试 1376 | 1377 | 本部分将会运行一系列的测试来验证 Kubernetes 集群的功能正常。 1378 | 1379 | ### 数据加密 1380 | 1381 | 本节将会验证 [encrypt secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/#verifying-that-data-is-encrypted) 的功能。 1382 | 1383 | 创建一个 Secret: 1384 | 1385 | ```sh 1386 | kubectl create secret generic kubernetes-training \ 1387 | --from-literal="mykey=mydata" 1388 | ``` 1389 | 1390 | 查询存在 etcd 里 16 进位编码的 `kubernetes-training` secret: 1391 | 1392 | ```sh 1393 | ETCDCTL_API=3 etcdctl get \ 1394 | --endpoints=http://127.0.0.1:2379 \ 1395 | --cacert=/etc/etcd/ca.pem \ 1396 | --cert=/etc/etcd/kubernetes.pem \ 1397 | --key=/etc/etcd/kubernetes-key.pem\ 1398 | /registry/secrets/default/kubernetes-training | hexdump -C 1399 | ``` 1400 | 1401 | 输出为 1402 | 1403 | ```sh 1404 | 00000000 2f 72 65 67 69 73 74 72 79 2f 73 65 63 72 65 74 |/registry/secret| 1405 | 00000010 73 2f 64 65 66 61 75 6c 74 2f 6b 75 62 65 72 6e |s/default/kubern| 1406 | 00000020 65 74 65 73 2d 74 72 61 69 6e 69 6e 67 0a 6b 38 |etes-training.k8| 1407 | 00000030 73 3a 65 6e 63 3a 61 65 73 63 62 63 3a 76 31 3a |s:enc:aescbc:v1:| 1408 | 00000040 6b 65 79 31 3a 3e 0e e6 d2 26 d8 26 1a a1 bf 15 |key1:>...&.&....| 1409 | 00000050 69 d1 b9 dc dc da 54 26 6f 14 a3 48 4a 5d 00 22 |i.....T&o..HJ]."| 1410 | 00000060 99 98 02 2a a0 e5 00 62 cc fa 49 de d4 d9 0c 35 |...*...b..I....5| 1411 | 00000070 4c ed 66 1f 95 98 20 20 a1 0a 6d 1a b9 78 38 db |L.f... ..m..x8.| 1412 | 00000080 14 68 29 5c b0 fb f1 1f 34 f4 c9 10 28 8c 38 09 |.h)\....4...(.8.| 1413 | 00000090 05 81 81 91 f6 f2 84 5f a2 8c cd 51 b8 2d 50 c8 |......._...Q.-P.| 1414 | 000000a0 b2 2f a0 2a 25 0d fa 2a 5f d6 e5 8f 26 9d 67 0e |./.*%..*_...&.g.| 1415 | 000000b0 6f 95 85 8c f8 2c ff b6 f1 81 93 cf fa e8 9c ac |o....,..........| 1416 | 000000c0 15 1b 39 24 f0 0d 99 dd 70 f9 45 45 d4 3c 36 ee |..9$....p.EE.<6.| 1417 | 000000d0 3e d0 19 70 3c 81 1e 15 93 ad af 87 84 e7 64 ed |>..p<.........d.| 1418 | 000000e0 21 76 6a ce 0d 0a |!vj...| 1419 | 000000e6 1420 | ``` 1421 | 1422 | Etcd 的密钥以 `k8s:enc:aescbc:v1:key1` 为前缀, 表示使用密钥为 `key1` 的 `aescbc` 加密数据。 1423 | 1424 | ### 部署 1425 | 1426 | 本节将会验证建立与管理 [Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) 的功能。 1427 | 1428 | 创建一个 Deployment 用来搭建 [nginx](https://nginx.org/en/) Web 服务: 1429 | 1430 | ```sh 1431 | kubectl run nginx --image=nginx 1432 | ``` 1433 | 1434 | 列出 `nginx` deployment 的 pods: 1435 | 1436 | ```sh 1437 | kubectl get pods -l run=nginx 1438 | ``` 1439 | 1440 | 输出为 1441 | 1442 | ```sh 1443 | NAME READY STATUS RESTARTS AGE 1444 | nginx-7bb7cd8db5-hqlf5 1/1 Running 0 6s 1445 | ``` 1446 | 1447 | #### 端口转发 1448 | 1449 | 本节将会验证使用 [port forwarding](https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/) 从远端进入容器的功能。 1450 | 1451 | 查询 `nginx` pod 的全名: 1452 | 1453 | ```sh 1454 | POD_NAME=$(kubectl get pods -l run=nginx -o jsonpath="{.items[0].metadata.name}") 1455 | ``` 1456 | 1457 | 将本地机器的 8080 端口转发到 nginx pod 的 80 端口: 1458 | 1459 | ```sh 1460 | kubectl port-forward $POD_NAME 8080:80 1461 | ``` 1462 | 1463 | 输出为 1464 | 1465 | ```txt 1466 | Forwarding from 127.0.0.1:8080 -> 80 1467 | Forwarding from [::1]:8080 -> 80 1468 | ``` 1469 | 1470 | 开一个新的终端来做 HTTP 请求测试: 1471 | 1472 | ```sh 1473 | curl --head http://127.0.0.1:8080 1474 | ``` 1475 | 1476 | 输出为 1477 | 1478 | ```sh 1479 | HTTP/1.1 200 OK 1480 | Server: nginx/1.17.1 1481 | Date: Fri, 05 Jul 2019 09:58:49 GMT 1482 | Content-Type: text/html 1483 | Content-Length: 612 1484 | Last-Modified: Tue, 25 Jun 2019 12:19:45 GMT 1485 | Connection: keep-alive 1486 | ETag: "5d121161-264" 1487 | Accept-Ranges: bytes 1488 | ``` 1489 | 1490 | 回到前面的终端并按下 `Ctrl + C` 停止 port forwarding 命令: 1491 | 1492 | ```sh 1493 | Forwarding from 127.0.0.1:8080 -> 80 1494 | Forwarding from [::1]:8080 -> 80 1495 | Handling connection for 8080 1496 | ^C 1497 | ``` 1498 | 1499 | #### 容器日志 1500 | 1501 | 本节会验证 [获取容器日志](https://kubernetes.io/docs/concepts/cluster-administration/logging/) 的功能。 1502 | 1503 | 输出 nginx Pod 的容器日志: 1504 | 1505 | ```sh 1506 | kubectl logs $POD_NAME 1507 | ``` 1508 | 1509 | 输出为 1510 | 1511 | ```sh 1512 | 127.0.0.1 - - [05/Jul/2019:09:56:26 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.54.0" "-" 1513 | ``` 1514 | 1515 | #### 执行容器命令 1516 | 1517 | 本节将验证 [在容器里执行命令](https://kubernetes.io/docs/tasks/debug-application-cluster/get-shell-running-container/#running-individual-commands-in-a-container) 的功能。 1518 | 1519 | 使用 `nginx -v` 命令在 `nginx` Pod 中输出 nginx 的版本: 1520 | 1521 | ```sh 1522 | kubectl exec -ti $POD_NAME -- nginx -v 1523 | ``` 1524 | 1525 | 输出为 1526 | 1527 | ```sh 1528 | nginx version: nginx/1.17.1 1529 | ``` 1530 | 1531 | ### 服务(Service) 1532 | 1533 | 本节将验证 Kubernetes [Service](https://kubernetes.io/docs/concepts/services-networking/service/)。 1534 | 1535 | 将 `nginx` 部署导出为 [NodePort](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) 类型的 Service: 1536 | 1537 | ```sh 1538 | kubectl expose deployment nginx --port 80 --type NodePort 1539 | ``` 1540 | 1541 | > LoadBalancer 类型的 Service 不能使用是因为没有设置 [cloud provider 集成](https://kubernetes.io/docs/getting-started-guides/scratch/#cloud-provider)。 设定 cloud provider 不在本教程范围之内。 1542 | 1543 | 查询 `nginx` 服务分配的 Node Port: 1544 | 1545 | ```sh 1546 | NODE_PORT=$(kubectl get svc nginx \ 1547 | --output=jsonpath='{range .spec.ports[0]}{.nodePort}') 1548 | ``` 1549 | 1550 | 使用 Node IP 地址 + nginx 服务的 Node Port 做 HTTP 请求测试: 1551 | 1552 | ```sh 1553 | curl -I http://${NODE_IP}:${NODE_PORT} 1554 | ``` 1555 | 1556 | 输出为 1557 | 1558 | ```sh 1559 | HTTP/1.1 200 OK 1560 | Server: nginx/1.17.1 1561 | Date: Sun, 07 Jul 2019 03:22:44 GMT 1562 | Content-Type: text/html 1563 | Content-Length: 612 1564 | Last-Modified: Tue, 25 Jun 2019 12:19:45 GMT 1565 | Connection: keep-alive 1566 | ETag: "5d121161-264" 1567 | Accept-Ranges: bytes 1568 | ``` 1569 | -------------------------------------------------------------------------------- /tutorials/lab4-concepts.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* 4 | 5 | - [Introduction](#introduction) 6 | - [Kubernetes Job](#kubernetes-job) 7 | - [Using Job](#using-job) 8 | - [Readings](#readings) 9 | - [Kubernetes CronJob](#kubernetes-cronjob) 10 | - [Using CronJob](#using-cronjob) 11 | - [Readings](#readings-1) 12 | - [Kubernetes Daemonset](#kubernetes-daemonset) 13 | - [Using Daemonset](#using-daemonset) 14 | - [Readings](#readings-2) 15 | - [Resource quota](#resource-quota) 16 | - [Using Resource quota](#using-resource-quota) 17 | - [Readings](#readings-3) 18 | - [Kubernetes Horizontal Pod Autoscaler](#kubernetes-horizontal-pod-autoscaler) 19 | - [Using HPA](#using-hpa) 20 | - [Readings](#readings-4) 21 | - [Kubernetes Volume](#kubernetes-volume) 22 | - [Using volume](#using-volume) 23 | - [Readings](#readings-5) 24 | - [Kubernetes PV & PVC](#kubernetes-pv--pvc) 25 | - [Using PV & PVC](#using-pv--pvc) 26 | - [Readings](#readings-6) 27 | - [Kubernetes StorageClass](#kubernetes-storageclass) 28 | - [Using StorageClass](#using-storageclass) 29 | - [Default StorageClass](#default-storageclass) 30 | - [StorageClass Provisioner](#storageclass-provisioner) 31 | - [Readings](#readings-7) 32 | - [Exercise](#exercise) 33 | 34 | 35 | 36 | # Introduction 37 | 38 | 本节我们将了解更多 kubernetes 的概念,主要包括水平扩容和存储模型。 39 | 40 | # Kubernetes Job 41 | 42 | ## Using Job 43 | 44 | Kubernetes Job 通过创建 Pod 来批量执行一次性任务;不同于单独跑一个 Bare Pod,由 Job 运行起来的 Pod 在机器故障等问题下会重新调度 Pod,因此更加健壮。 45 | 46 | 下面,我们通过创建一个 Pod 来感受一下 Job 的使用: 47 | 48 | ```sh 49 | $ kubectl create -f resources/job.yaml 50 | job "pi" created 51 | 52 | $ kubectl get job 53 | NAME COMPLETIONS DURATION AGE 54 | pi 1/5 29s 29s 55 | 56 | $ kubectl get pods -l job-name=pi 57 | NAME READY STATUS RESTARTS AGE 58 | pi-76h5p 1/1 Running 0 12s 59 | pi-fhww6 0/1 Completed 0 36s 60 | ``` 61 | 62 | 一段时间之后,Pod 全部运行结束,我们可以通过 `kubectl get pods` 查看: 63 | 64 | ```sh 65 | $ kubectl get pods -l job-name=pi 66 | NAME READY STATUS RESTARTS AGE 67 | pi-6lgqw 0/1 Completed 0 45s 68 | pi-76h5p 0/1 Completed 0 68s 69 | pi-fhww6 0/1 Completed 0 92s 70 | pi-mf96j 0/1 Completed 0 2m17s 71 | pi-w9v4l 0/1 Completed 0 115s 72 | 73 | $ kubectl get job 74 | NAME COMPLETIONS DURATION AGE 75 | pi 5/5 117s 3m8s 76 | ``` 77 | 78 | 观察上述 Pod 的 AGE 列,可以发现 Job 内的 Pod 都是依次运行的(总共 5 个 Pod)。Job 支持并发运行等多种控制,我们在后续任务中实现。 79 | 80 | Job 运行完之后,删除 Job 会将所有运行结束的 Pods 也同时删掉。 81 | 82 | ```sh 83 | $ kubectl delete job pi 84 | job "pi" deleted 85 | 86 | $ kubectl get pods -l job-name=pi 87 | No resources found. 88 | ``` 89 | 90 | ## Readings 91 | 92 | * [kubernetes job](https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/) 93 | 94 | # Kubernetes CronJob 95 | 96 | ## Using CronJob 97 | 98 | Kubernetes CronJob 即定时任务,就类似于 Linux 的 crontab,在指定的时间周期运行指定的作业。 99 | 100 | 这里我们通过 kubectl create 创建一个 CronJob: 101 | 102 | ```sh 103 | $ kubectl create -f resources/cronjob.yaml 104 | cronjob.batch/hello created 105 | ``` 106 | 107 | 查看 CronJob: 108 | 109 | ```sh 110 | $ kubectl get cronjob 111 | NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE 112 | hello */1 * * * * False 0 57s 69m 113 | $ kubectl get jobs 114 | NAME COMPLETIONS DURATION AGE 115 | hello-1566286260 1/1 11s 46s 116 | ``` 117 | 118 | 删除 CronJob: 119 | 120 | ```sh 121 | # 删除 CronJob 会删除它创建的所有 job 和 pod,并停止正在创建的 job 122 | $ kubectl delete cronjob hello 123 | cronjob.batch "hello" deleted 124 | ``` 125 | 126 | ## Readings 127 | 128 | * [kubernetes cronjob](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) 129 | 130 | # Kubernetes Daemonset 131 | 132 | ## Using Daemonset 133 | 134 | DaemonSet 默认在每台主机上运行 Pod,典型的场景包括日志收集、存储进程等。这里,我们通过 kubectl create 135 | 来创建一个 DaemonSet: 136 | 137 | ```sh 138 | $ kubectl create -f resources/daemonset.yaml 139 | daemonset "ds-nginx" created 140 | 141 | $ kubectl get ds 142 | NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE 143 | ds-nginx 1 1 1 1 1 4s 144 | 145 | $ kubectl get pods 146 | NAME READY STATUS RESTARTS AGE 147 | ds-nginx-hnndc 1/1 Running 0 8s 148 | ``` 149 | 150 | 可以看到,minikube 节点都运行了一个 DaemonSet。由于目前只有一个节点,很难观测到效果,但当我们向集群中增加新的节点, 151 | DaemonSet 会自动在新的节点中启动 Pod。 152 | 153 | 我们可以通过 kubectl delete 删除 DaemonSet: 154 | 155 | ```sh 156 | $ kubectl delete daemonset ds-nginx 157 | daemonset.extensions "ds-nginx" deleted 158 | ``` 159 | 160 | DaemonSet 相对于其他控制器(Deployment, Job 等)有几个特点: 161 | 162 | **由 DaemonSet controller 调度 (1.12 以前)**: 163 | 164 | * DaemonSet controller 创建 pod 时会将指定 nodeName(.spec.nodeName),所以 scheduler 会忽视它。因此: 165 | * 即使调度器没有启动,DaemonSet 的 pod 也会运行 166 | * 即时一个节点被标记为不可调度,由 DaemonSet 启动的容器也会运行在某个节点上 167 | * DaemonSet 启动的 Pod 会无视优先级 168 | 169 | **由 default scheduler 调度(从 1.12 开始默认开启)**: 170 | 171 | * DaemonSet controller 通过给 pod 添加 NodeAffinity,然后 default scheduler 将其绑定到目标节点,即指定 nodeName(.spec.nodeName) 172 | 173 | ## Readings 174 | 175 | * [kubernetes daemonset](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) 176 | 177 | # Resource quota 178 | 179 | 默认情况下,namespace 只提供名字的隔离,并没有提供资源的限制(Quota)。也就是说一个 namespace 180 | 的用户可以创建任意多的资源,直到将集群的所有资源用光。为了解决这个问题,我们可以通过创建 ResourceQuota 181 | 资源来限制某个 namespace 可以使用的资源量,即: 182 | 183 | - 计算资源,包括 cpu 和 memory 184 | - cpu, limits.cpu, requests.cpu 185 | - memory, limits.memory, requests.memory 186 | - 存储资源,包括存储资源的总量以及指定 storage class 的总量 187 | - requests.storage:存储资源总量,如 500Gi 188 | - persistentvolumeclaims:pvc 的个数 189 | - .storageclass.storage.k8s.io/requests.storage 190 | - .storageclass.storage.k8s.io/persistentvolumeclaims 191 | - requests.ephemeral-storage 和 limits.ephemeral-storage (需要 v1.8+) 192 | - 对象数量,即可创建的对象的个数 193 | - pods, replicationcontrollers, configmaps, secrets 194 | - resourcequotas, persistentvolumeclaims 195 | - services, services.loadbalancers, services.nodeports 196 | - custom resources (需要 v1.15+) 197 | 198 | ## Using Resource quota 199 | 200 | 我们可以通过 `kubectl create -f` 创建 ResourceQuota,该 quota 限制了 tutorial namespace 201 | 只能请求 1 个 CPU 和 1Gi 内存,最多只能使用 2 个 CPU 和 2Gi 内存。这里,请求指的是 kubernetes 202 | 在调度时容器时会寻找至少有 1 个 CPU 和 1Gi 内存的节点。最多使用指的是当容器可以使用超过其请求的 1 个 CPU 203 | 数量,但是一定不可能超过 2 个 CPU。kubernetes 的这个设计目的是提供更加灵活的资源模型,支持超卖。 204 | 205 | ```sh 206 | # 这里用到了前面 lab 创建的 namespace tutorial,若已删除,需重新创建 207 | $ kubectl create namespace tutorial 208 | namespace/tutorial created 209 | 210 | $ kubectl create -f resources/quota.yaml 211 | resourcequota/tutorial-quota created 212 | ``` 213 | 214 | 查看 ResourceQuota 细节: 215 | 216 | ```sh 217 | $ kubectl describe quota -n tutorial 218 | Name: tutorial-quota 219 | Namespace: tutorial 220 | Resource Used Hard 221 | -------- ---- ---- 222 | limits.cpu 500m 2 223 | limits.memory 1124Mi 2Gi 224 | requests.cpu 300m 1 225 | requests.memory 612Mi 1Gi 226 | ``` 227 | 228 | 如果我们尝试在 tutorial namespace 下创建超过配额的资源,kubernetes 会直接返回错误: 229 | 230 | ```sh 231 | $ kubectl create -f resources/pod_exceeds_quota.yaml -n tutorial 232 | Error from server (Forbidden): error when creating "resources/pod_exceeds_quota.yaml": pods "pod" is forbidden: exceeded quota: default-quota, requested: limits.cpu=2,requests.cpu=2,requests.memory=1Gi, used: limits.cpu=200m,requests.cpu=100m,requests.memory=256Mi, limited: limits.cpu=2,requests.cpu=1,requests.memory=1Gi 233 | ``` 234 | 235 | ## Readings 236 | 237 | * [Kubernetes Resource Quotas](https://kubernetes.io/docs/concepts/policy/resource-quotas/) 238 | 239 | # Kubernetes Horizontal Pod Autoscaler 240 | 241 | HorizontalPodAutoscaler,简称 HPA,通过监控应用的 CPU 使用率或应用自定义 metrics 自动扩展 Pod 数量(支持 replication controller、deployment 和 replica set )。 242 | 243 | - 控制管理器每隔 15s(可以通过 controller manager 的参数 --horizontal-pod-autoscaler-sync-period 修改)查询 metrics 的资源使用情况 244 | - 支持三种 metrics 类型 245 | - 预定义 metrics(比如 Pod 的 CPU)以利用率的方式计算 246 | - 自定义的 Pod metrics,以原始值(raw value)的方式计算 247 | - 自定义的 object metrics 248 | - 支持两种 metrics 查询方式:Heapster 和自定义的 REST API 249 | - 支持多 metrics 250 | 251 | **注意**:在使用 HPA 之前需要 确保已部署好 [metrics-server](https://github.com/kubernetes-incubator/metrics-server/) 252 | 253 | ```sh 254 | # minikube 中已默认部署 metrics-server,可通过命令查看 255 | # 若未部署,你也可以通过命令 `minikube addons enable metrics-server` 完成部署 256 | $ minikube addons list 257 | ... 258 | - metrics-server: enabled 259 | ... 260 | ``` 261 | 262 | 下图表明了 HPA 的工作原理: 263 | 264 |

265 |

Image source: kubernetes guide


266 | 267 | 可以看到,HPA 通过调整 Deployment 的副本数量来动态调整 Pod 数量。 268 | 269 | ## Using HPA 270 | 271 | 使用 HPA 需要设置 CPU 使用率阈值、最大最小 Pod 数量等。这里我们先创建一个只有一个副本的 Deployment,然后逐渐加压,观察副本的变化情况: 272 | 273 | ```sh 274 | # 创建 deployment 和 service 275 | $ kubectl apply -f resources/deployment_php_cache.yaml 276 | $ kubectl expose deployment php-apache --port 80 277 | service "php-apache" created 278 | deployment "php-apache" created 279 | 280 | # 创建 autoscaler 281 | $ kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10 282 | deployment "php-apache" autoscaled 283 | 284 | # 查看 hpa 285 | $ kubectl get hpa 286 | NAME REFERENCE TARGET MINPODS MAXPODS REPLICAS AGE 287 | php-apache Deployment/php-apache/scale 0% / 50% 1 10 1 18s 288 | 289 | # 增加负载 290 | $ kubectl run -i --tty load-generator --image=busybox /bin/sh 291 | Hit enter for command prompt 292 | $ while true; do wget -q -O- http://php-apache.default.svc.cluster.local; done 293 | 294 | # 过一会就可以看到负载升高了 295 | $ kubectl get hpa 296 | NAME REFERENCE TARGET CURRENT MINPODS MAXPODS REPLICAS AGE 297 | php-apache Deployment/php-apache/scale 305% / 50% 305% 1 10 1 3m 298 | 299 | # autoscaler 将这个 deployment 扩展为 7 个 pod 300 | $ kubectl get deployment php-apache 301 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 302 | php-apache 7 7 7 7 19m 303 | 304 | # 停止增加负载 305 | # 删除刚才创建的负载增加 pod 后会发现负载降低,并且 pod 数量也自动降回 1 个 306 | $ kubectl get hpa 307 | NAME REFERENCE TARGET MINPODS MAXPODS REPLICAS AGE 308 | php-apache Deployment/php-apache/scale 0% / 50% 1 10 1 11m 309 | 310 | $ kubectl get deployment php-apache 311 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 312 | php-apache 1 1 1 1 27m 313 | ``` 314 | 315 | 本节只介绍了基于 CPU 的自动扩缩容,如想了解基于 Memory 和自定义指标的扩缩容,请深入阅读 :point_down:。 316 | 317 | ## Readings 318 | 319 | * [kubernetes horizontal pod autoscale](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) 320 | * [horizontal pod autoscale walkthrough](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale-walkthrough/) 321 | 322 | # Kubernetes Volume 323 | 324 | ## Using volume 325 | 326 | 用户可以通过 Volume 来创建数据卷,使用数据卷之后,用户创建的 Pod 可以外部存储空间。Volume 是一个广义的概念,在 kubernetes 中至少包含三种类型: 327 | 328 | * 临时存储,比如 EmptyDir。当 Pod 删除后,EmptyDir Volume 也会被随之删除。 329 | * 持久化存储,比如 Ceph。kubernetes 通过 Volume 的方式挂载外部持久化存储。 330 | * 映射类存储,比如 ConfigMap。此类存储基于其他存储类型,为 Pod 提供元数据,配置等信息。 331 | 332 | 无论是那种类型,都可以在 Pod 中直接指定 Volume 的信息。接下来我们创建一个最简单的 Pod,使用 EmptyDir 作为 333 | Volume。可以看到,kubernetes 根据 yaml 文件的内容,为该 Pod 挂载了一个 cache 目录。对于 EmptyDir 而言,该目录的本质就是宿主机上的一个目录。 334 | 335 | ```sh 336 | $ kubectl create -f resources/emptydir_pod.yaml 337 | pod "emptydir-pod" created 338 | 339 | $ kubectl get pods 340 | NAME READY STATUS RESTARTS AGE 341 | emptydir-pod 1/1 Running 0 5s 342 | 343 | # 进入 emptydir-pod 并创建一个文件 344 | $ kubectl exec -it emptydir-pod bash 345 | root@emptydir-pod:/# ls cache/ 346 | root@emptydir-pod:/# touch cache/abc 347 | 348 | # 按 'Ctrl + D' 退出 pod 349 | $ kubectl get pods emptydir-pod -o yaml | grep uid 350 | uid: 15ccac29-773b-41d2-bd54-8d746a009396 351 | 352 | # 我们将会在宿主机上看见之前创建的文件 353 | $ minikube ssh 354 | $ sudo ls /var/lib/kubelet/pods/15ccac29-773b-41d2-bd54-8d746a009396/volumes/kubernetes.io~empty-dir/cache-volume/abc 355 | ``` 356 | 357 | 可以验证,当 Pod 被删除后,数据也就丢失了。若我们想使存储与 Pod 的生命周期解耦,需要使用网络存储。如果我们查看 358 | emptydir_pod.yaml 文件,可以看到使用方法和 ConfigMap 和 Secret 完全类似。 359 | 360 | ## Readings 361 | 362 | * [kubernetes volumes](https://kubernetes.io/docs/concepts/storage/volumes/) 363 | 364 | # Kubernetes PV & PVC 365 | 366 | ## Using PV & PVC 367 | 368 | 对于持久化存储,Kubernetes 抽象出了 PersistentVolume (PV) 和 PersistentVolumeClaim (PVC) 两类资源。类似于 Node 与 Pod 在计算资源上的关系,PV/PVC 提供了存储的抽象。管理员创建可用的存储资源(PV),用户通过 PVC 369 | 请求需要的资源再与 Pod 进行绑定。 370 | 371 | Volume 的生命周期包括 5 个阶段: 372 | 373 | - Provisioning,即 PV 的创建,可以直接创建 PV(静态方式),也可以使用 StorageClass 动态创建 374 | - Binding,将 PV 分配给 PVC 375 | - Using,Pod 通过 PVC 使用该 Volume,并可以通过 Admission Controller StorageObjectInUseProtection(1.9 及以前版本为 PVCProtection)阻止删除正在使用的 PVC 376 | - Releasing,Pod 释放 Volume 并删除 PVC 377 | - Reclaiming,回收 PV,可以保留 PV 以便下次使用,也可以直接从存储中删除 378 | - Deleting,删除 PV 并从存储中删除后端存储 379 | 380 | 根据这 5 个阶段,Volume 的状态有以下 4 种: 381 | 382 | - Available:可用 383 | - Bound:已经分配给 PVC 384 | - Released:PVC 解绑但还未执行回收策略 385 | - Failed:发生错误 386 | 387 | 接下来,我们感受一下如何使用 PV 和 PVC。 388 | 389 | 首先,我们需要创建新的 PV。类比于节点,可以理解为是向集群中添加节点,这里我们创建一个基于 hostPath 的 PV。基于 390 | hostPath 的 PV 主要用来做测试,生产环境中一般采用其他存储方案如 NFS, GlusterFS, Ceph 等。 391 | 392 | ```sh 393 | $ kubectl create -f resources/pv_hostpath.yaml 394 | persistentvolume "pv-hostpath" created 395 | 396 | $ kubectl get pv 397 | NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE 398 | pv-hostpath 10Gi RWX Recycle Available standard 7s 399 | ``` 400 | 401 | 创建 PV 之后,我们需要申请使用 PV,因此需要在 kubernetes 中创建 PVC。 402 | 403 | ```sh 404 | $ kubectl create -f resources/pvc.yaml 405 | persistentvolumeclaim "myclaim" created 406 | ``` 407 | 408 | kubernetes 根据 PVC 所需的容量(resources.requests.storage)和访问方式(accessMode)来调度存储资源。如下所示,我们创建的 PV 和 PVC 被 kubernetes 绑定在了一起。 409 | 410 | ```sh 411 | $ kubectl get pv 412 | NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE 413 | pv-hostpath 10Gi RWX Recycle Bound default/myclaim standard 45s 414 | 415 | $ kubectl get pvc 416 | NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE 417 | myclaim Bound pv-hostpath 10Gi RWX standard 26s 418 | ``` 419 | 420 | PV 和 PVC 的封装主要作用是达到了平台可移植性。应用不再需要关系底层存储的细节,只需要使用 PVC 即可。接下来我们使用上面的 PVC 来创建应用。 421 | 422 | ```sh 423 | $ kubectl create -f resources/pod_with_pvc.yaml 424 | pod "pod-with-pvc" created 425 | 426 | $ kubectl get pods 427 | NAME READY STATUS RESTARTS AGE 428 | pod-with-pvc 1/1 Running 0 5s 429 | 430 | $ kubectl exec -it pod-with-pvc bash 431 | root@pod-with-pvc:/# touch /var/www/html/index.html 432 | 433 | $ minikube ssh 434 | $ ls /tmp/data1/ 435 | index.html 436 | ``` 437 | 438 | 当我们删除 Pod 时,PV 和 PVC 的绑定不受任何影响,意味着我们可以重新创建 Pod 使用 PVC,数据仍然存在。 439 | 440 | ```sh 441 | $ kubectl delete pod pod-with-pvc 442 | pod "pod-with-pvc" deleted 443 | 444 | $ kubectl get pvc 445 | NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE 446 | myclaim Bound pv-hostpath 10Gi RWX standard 2m26s 447 | 448 | $ kubectl get pv 449 | NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE 450 | pv-hostpath 10Gi RWX Recycle Bound default/myclaim standard 3m7s 451 | ``` 452 | 453 | 如果进一步删除 PVC,可以看到 PV 进入了 `Released` 状态,这意味着 PV 已经被释放,但是还无法被重新使用,需要管理员手动清理 PV 数据后,将 PV 状态修改为 `Available`。PV 进入 `Release` 状态的原因是 PV 454 | 的回收策略被指定为 `Recycle`(spec.persistentVolumeReclaimPolicy: Recycle)。如果将回收策略改为 455 | `Delete`,那么 PV 会被 kubernetes 直接删除。 456 | 457 | ```sh 458 | $ kubectl delete pvc myclaim 459 | persistentvolumeclaim "myclaim" deleted 460 | 461 | $ kubectl get pv 462 | NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE 463 | pv-hostpath 10Gi RWX Recycle Released default/myclaim standard 3m55s 464 | ``` 465 | 466 | PV 可以被手动删除,kubernetes 不会自动清理底层的数据。 467 | 468 | ```sh 469 | $ kubectl delete pv pv-hostpath 470 | persistentvolume "pv-hostpath" deleted 471 | ``` 472 | 473 | ## Readings 474 | 475 | * [Kubernetes Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) 476 | * [How to Use Persistent Volume](https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/) 477 | 478 | # Kubernetes StorageClass 479 | 480 | ## Using StorageClass 481 | 482 | 从上述操作的步骤可以看出,管理员需要首先创建 PV 才能让用户使用底层存储。其次,用户在使用 PVC 申请存储的时候,只能指定存储空间大小和访问模式。假如底层同时提供 GlusterFS 和 NFS,或者是不同规格的 GlusterFS,用户并没有能力指定特定的存储系统。从 1.2 版本开始,Kubernetes 逐渐引入了 StorageClass 的概念来解决上述两个问题。 483 | 484 | 首先,我们需要创建 StorageClass。minikube 自带一个基于 hostPath 类型的 StorageClass,如下所示。 485 | 486 | ```sh 487 | $ kubectl get storageclass standard -o yaml 488 | apiVersion: storage.k8s.io/v1 489 | kind: StorageClass 490 | metadata: 491 | annotations: 492 | storageclass.kubernetes.io/is-default-class: "true" 493 | creationTimestamp: "2019-06-27T03:03:57Z" 494 | labels: 495 | addonmanager.kubernetes.io/mode: EnsureExists 496 | name: standard 497 | resourceVersion: "356" 498 | selfLink: /apis/storage.k8s.io/v1/storageclasses/standard 499 | uid: c9e7cc0f-2829-4adc-a313-63ea3a4950b7 500 | provisioner: k8s.io/minikube-hostpath 501 | reclaimPolicy: Delete 502 | volumeBindingMode: Immediate 503 | ``` 504 | 505 | minikube 自带的 StorageClass 的名字为 `standard`,provisioner 名字为 `k8s.io/minikube-hostpath`。当我们创建一个 PVC 时,需要指定所需要的 StorageClass 名字,例如: 506 | 507 | ```sh 508 | kind: PersistentVolumeClaim 509 | apiVersion: v1 510 | metadata: 511 | name: claim-standard 512 | spec: 513 | storageClassName: standard 514 | accessModes: 515 | - ReadWriteOnce 516 | resources: 517 | requests: 518 | storage: 8Gi 519 | ``` 520 | 521 | 上述 PVC 将会使用 `standard` StorageClass 创建一个 PV,如下所示: 522 | 523 | ```sh 524 | $ kubectl get pvc 525 | NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE 526 | myclaim Bound pvc-207c1e0b-a341-11e7-bf8e-0800277a7b6e 8Gi RWX standard 6s 527 | 528 | $ kubectl get pv 529 | NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE 530 | pvc-207c1e0b-a341-11e7-bf8e-0800277a7b6e 8Gi RWX Delete Bound default/myclaim standard 8s 531 | ``` 532 | 533 | 可以看到 Kubernetes 自动生成了一个 PV。注意,动态生成的 PV 其回收属性是 `Delete`,即删除 PVC 会将 PV 也同时删除: 534 | 535 | ```sh 536 | $ kubectl delete pvc myclaim 537 | persistentvolumeclaim "myclaim" deleted 538 | 539 | $ kubectl get pvc 540 | No resources found. 541 | 542 | $ kubectl get pv 543 | No resources found. 544 | ``` 545 | 546 | ## Default StorageClass 547 | 548 | kubernetes 集群中可以创建多个 StorageClass,其中有且仅有一个默认 StorageClass。当创建的 PVC 没有指定需要什么类型的存储时(即 pvc.spec.storageClassName 为空),Kubernetes 使用默认 StorageClass。 549 | 550 | 在 minikube 环境中,`standard` 即为默认的 StorageClass。我们在 PV & PVC 章节中创建的 PVC 没有指定 551 | storageClassName,因此默认使用 `standard`。 552 | 553 | ```sh 554 | $ kubectl get storageclass 555 | NAME PROVISIONER AGE 556 | standard (default) k8s.io/minikube-hostpath 9d 557 | ``` 558 | 559 | ## StorageClass Provisioner 560 | 561 | 除了名字之外,每一个 StorageClass 都必须指明 provisioner。 provisioner 是真正创建底层存储的组件。provisioner 562 | 负责监听一个特定的事件 - “用户创建了一个 PVC,该 PVC 使用某个 StorageClass,该 StorageClass 的 provisioner 563 | 是我自己”。当发生该事件,provisioner 将会创建底层存储。例如: 564 | 565 | ```sh 566 | apiVersion: storage.k8s.io/v1 567 | kind: StorageClass 568 | metadata: 569 | name: gluster-class 570 | annotations: 571 | storageclass.kubernetes.io/is-default-class: "false" 572 | provisioner: example.com/gluster 573 | ``` 574 | 575 | 当用户创建一个 PVC 使用 `gluster-class`,名为 `example.com/gluster` 的 provisioner 需要创建基于 GlusterFS 576 | 的 PV 供 PVC 使用。一般情况下,provisioner 以 Pod 形式运行在 kubernetes 集群中,长运行并监听上述事件。 577 | provisioner 的稳定性和健壮性是极为重要的,否则动态创建会失效。 578 | 579 | ## Readings 580 | 581 | * [Kubernetes Storage Classes](https://kubernetes.io/docs/concepts/storage/storage-classes/) 582 | * [Dynamic Volume Provisioning](https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/) 583 | 584 | # Exercise 585 | 586 | 1. 查找资料,学习思考基于内存或自定义指标实现扩缩容的方案 587 | 2. 搭建一个 nfs 服务器,创建 StorageClass,学习使用 PV & PVC 588 | -------------------------------------------------------------------------------- /tutorials/resources/class.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: storage.k8s.io/v1 2 | kind: StorageClass 3 | metadata: 4 | name: gluster-class 5 | annotations: 6 | storageclass.kubernetes.io/is-default-class: "false" 7 | provisioner: example.com/gluster 8 | -------------------------------------------------------------------------------- /tutorials/resources/coredns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: coredns 5 | namespace: kube-system 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1beta1 8 | kind: ClusterRole 9 | metadata: 10 | labels: 11 | kubernetes.io/bootstrapping: rbac-defaults 12 | name: system:coredns 13 | rules: 14 | - apiGroups: 15 | - "" 16 | resources: 17 | - endpoints 18 | - services 19 | - pods 20 | - namespaces 21 | verbs: 22 | - list 23 | - watch 24 | --- 25 | apiVersion: rbac.authorization.k8s.io/v1beta1 26 | kind: ClusterRoleBinding 27 | metadata: 28 | annotations: 29 | rbac.authorization.kubernetes.io/autoupdate: "true" 30 | labels: 31 | kubernetes.io/bootstrapping: rbac-defaults 32 | name: system:coredns 33 | roleRef: 34 | apiGroup: rbac.authorization.k8s.io 35 | kind: ClusterRole 36 | name: system:coredns 37 | subjects: 38 | - kind: ServiceAccount 39 | name: coredns 40 | namespace: kube-system 41 | --- 42 | apiVersion: v1 43 | kind: ConfigMap 44 | metadata: 45 | name: coredns 46 | namespace: kube-system 47 | data: 48 | Corefile: | 49 | .:53 { 50 | errors 51 | health 52 | kubernetes cluster.local in-addr.arpa ip6.arpa { 53 | pods insecure 54 | upstream 55 | fallthrough in-addr.arpa ip6.arpa 56 | } 57 | prometheus :9153 58 | proxy . /etc/resolv.conf 59 | cache 30 60 | loop 61 | reload 62 | loadbalance 63 | } 64 | --- 65 | apiVersion: extensions/v1beta1 66 | kind: Deployment 67 | metadata: 68 | name: coredns 69 | namespace: kube-system 70 | labels: 71 | k8s-app: kube-dns 72 | kubernetes.io/name: "CoreDNS" 73 | spec: 74 | replicas: 2 75 | strategy: 76 | type: RollingUpdate 77 | rollingUpdate: 78 | maxUnavailable: 1 79 | selector: 80 | matchLabels: 81 | k8s-app: kube-dns 82 | template: 83 | metadata: 84 | labels: 85 | k8s-app: kube-dns 86 | spec: 87 | serviceAccountName: coredns 88 | tolerations: 89 | - key: node-role.kubernetes.io/master 90 | effect: NoSchedule 91 | - key: "CriticalAddonsOnly" 92 | operator: "Exists" 93 | containers: 94 | - name: coredns 95 | image: cargo.caicloud.io/caicloud/coredns:1.2.2 96 | imagePullPolicy: IfNotPresent 97 | resources: 98 | limits: 99 | memory: 170Mi 100 | requests: 101 | cpu: 100m 102 | memory: 70Mi 103 | args: [ "-conf", "/etc/coredns/Corefile" ] 104 | volumeMounts: 105 | - name: config-volume 106 | mountPath: /etc/coredns 107 | readOnly: true 108 | ports: 109 | - containerPort: 53 110 | name: dns 111 | protocol: UDP 112 | - containerPort: 53 113 | name: dns-tcp 114 | protocol: TCP 115 | - containerPort: 9153 116 | name: metrics 117 | protocol: TCP 118 | securityContext: 119 | allowPrivilegeEscalation: false 120 | capabilities: 121 | add: 122 | - NET_BIND_SERVICE 123 | drop: 124 | - all 125 | readOnlyRootFilesystem: true 126 | livenessProbe: 127 | httpGet: 128 | path: /health 129 | port: 8080 130 | scheme: HTTP 131 | initialDelaySeconds: 60 132 | timeoutSeconds: 5 133 | successThreshold: 1 134 | failureThreshold: 5 135 | dnsPolicy: Default 136 | volumes: 137 | - name: config-volume 138 | configMap: 139 | name: coredns 140 | items: 141 | - key: Corefile 142 | path: Corefile 143 | --- 144 | apiVersion: v1 145 | kind: Service 146 | metadata: 147 | name: kube-dns 148 | namespace: kube-system 149 | annotations: 150 | prometheus.io/port: "9153" 151 | prometheus.io/scrape: "true" 152 | labels: 153 | k8s-app: kube-dns 154 | kubernetes.io/cluster-service: "true" 155 | kubernetes.io/name: "CoreDNS" 156 | spec: 157 | selector: 158 | k8s-app: kube-dns 159 | clusterIP: 10.250.0.10 160 | ports: 161 | - name: dns 162 | port: 53 163 | protocol: UDP 164 | - name: dns-tcp 165 | port: 53 166 | protocol: TCP 167 | -------------------------------------------------------------------------------- /tutorials/resources/cronjob.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1beta1 2 | kind: CronJob 3 | metadata: 4 | name: hello 5 | spec: 6 | schedule: "*/1 * * * *" # At every minute. 7 | jobTemplate: 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: hello 13 | image: cargo.caicloud.io/caicloud/busybox:latest 14 | args: 15 | - /bin/sh 16 | - -c 17 | - date; echo Hello from the Kubernetes cluster 18 | restartPolicy: OnFailure 19 | -------------------------------------------------------------------------------- /tutorials/resources/daemonset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: DaemonSet 3 | metadata: 4 | name: ds-nginx 5 | labels: 6 | name: ds-nginx 7 | spec: 8 | template: 9 | metadata: 10 | labels: 11 | name: ds-nginx 12 | spec: 13 | containers: 14 | - name: ds-nginx 15 | image: cargo.caicloud.io/caicloud/nginx:1.9.3 16 | resources: 17 | requests: 18 | cpu: "0.1" 19 | memory: "100Mi" 20 | limits: 21 | cpu: "0.1" 22 | memory: "100Mi" 23 | -------------------------------------------------------------------------------- /tutorials/resources/debian.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: debian 5 | labels: 6 | app: web 7 | spec: 8 | containers: 9 | - name: debian 10 | image: cargo.caicloud.io/caicloud/debian:jessie 11 | resources: 12 | requests: 13 | cpu: "0.1" 14 | memory: "100Mi" 15 | limits: 16 | cpu: "0.1" 17 | memory: "100Mi" 18 | -------------------------------------------------------------------------------- /tutorials/resources/debian_never_restart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: debian 5 | labels: 6 | app: web 7 | spec: 8 | restartPolicy: Never 9 | containers: 10 | - name: debian 11 | image: cargo.caicloud.io/caicloud/debian:jessie 12 | resources: 13 | requests: 14 | cpu: "0.1" 15 | memory: "100Mi" 16 | limits: 17 | cpu: "0.1" 18 | memory: "100Mi" 19 | -------------------------------------------------------------------------------- /tutorials/resources/deployment_nginx.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: nginx 5 | labels: 6 | app: nginx 7 | spec: 8 | replicas: 1 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: cargo.caicloud.io/caicloud/nginx:1.9.7 20 | ports: 21 | - containerPort: 80 22 | -------------------------------------------------------------------------------- /tutorials/resources/deployment_php_cache.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | run: php-apache 6 | name: php-apache 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | run: php-apache 12 | template: 13 | metadata: 14 | labels: 15 | run: php-apache 16 | spec: 17 | containers: 18 | - image: cargo.caicloud.io/caicloud/hpa-example:latest 19 | name: php-apache 20 | ports: 21 | - containerPort: 80 22 | protocol: TCP 23 | resources: 24 | requests: 25 | cpu: 200m 26 | -------------------------------------------------------------------------------- /tutorials/resources/emptydir_pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: emptydir-pod 5 | spec: 6 | containers: 7 | - image: cargo.caicloud.io/caicloud/nginx:1.9.3 8 | name: emptydir-pod 9 | volumeMounts: 10 | - mountPath: /cache 11 | name: cache-volume 12 | volumes: 13 | - name: cache-volume 14 | emptyDir: {} 15 | -------------------------------------------------------------------------------- /tutorials/resources/game.properties: -------------------------------------------------------------------------------- 1 | enemies=aliens 2 | lives=3 3 | enemies.cheat=true 4 | enemies.cheat.level=noGoodRotten 5 | secret.code.passphrase=UUDDLRLRBABAS 6 | secret.code.allowed=true 7 | secret.code.lives=30 -------------------------------------------------------------------------------- /tutorials/resources/job.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: pi 5 | spec: 6 | completions: 5 7 | template: 8 | metadata: 9 | name: pi 10 | spec: 11 | containers: 12 | - name: pi 13 | image: cargo.caicloud.io/caicloud/perl:latest 14 | command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /tutorials/resources/kube-flannel.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: extensions/v1beta1 3 | kind: PodSecurityPolicy 4 | metadata: 5 | name: psp.flannel.unprivileged 6 | annotations: 7 | seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default 8 | seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default 9 | apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default 10 | apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default 11 | spec: 12 | privileged: false 13 | volumes: 14 | - configMap 15 | - secret 16 | - emptyDir 17 | - hostPath 18 | allowedHostPaths: 19 | - pathPrefix: "/etc/cni/net.d" 20 | - pathPrefix: "/etc/kube-flannel" 21 | - pathPrefix: "/run/flannel" 22 | readOnlyRootFilesystem: false 23 | # Users and groups 24 | runAsUser: 25 | rule: RunAsAny 26 | supplementalGroups: 27 | rule: RunAsAny 28 | fsGroup: 29 | rule: RunAsAny 30 | # Privilege Escalation 31 | allowPrivilegeEscalation: false 32 | defaultAllowPrivilegeEscalation: false 33 | # Capabilities 34 | allowedCapabilities: ['NET_ADMIN'] 35 | defaultAddCapabilities: [] 36 | requiredDropCapabilities: [] 37 | # Host namespaces 38 | hostPID: false 39 | hostIPC: false 40 | hostNetwork: true 41 | hostPorts: 42 | - min: 0 43 | max: 65535 44 | # SELinux 45 | seLinux: 46 | # SELinux is unsed in CaaSP 47 | rule: 'RunAsAny' 48 | --- 49 | kind: ClusterRole 50 | apiVersion: rbac.authorization.k8s.io/v1beta1 51 | metadata: 52 | name: flannel 53 | rules: 54 | - apiGroups: ['extensions'] 55 | resources: ['podsecuritypolicies'] 56 | verbs: ['use'] 57 | resourceNames: ['psp.flannel.unprivileged'] 58 | - apiGroups: 59 | - "" 60 | resources: 61 | - pods 62 | verbs: 63 | - get 64 | - apiGroups: 65 | - "" 66 | resources: 67 | - nodes 68 | verbs: 69 | - list 70 | - watch 71 | - apiGroups: 72 | - "" 73 | resources: 74 | - nodes/status 75 | verbs: 76 | - patch 77 | --- 78 | kind: ClusterRoleBinding 79 | apiVersion: rbac.authorization.k8s.io/v1beta1 80 | metadata: 81 | name: flannel 82 | roleRef: 83 | apiGroup: rbac.authorization.k8s.io 84 | kind: ClusterRole 85 | name: flannel 86 | subjects: 87 | - kind: ServiceAccount 88 | name: flannel 89 | namespace: kube-system 90 | --- 91 | apiVersion: v1 92 | kind: ServiceAccount 93 | metadata: 94 | name: flannel 95 | namespace: kube-system 96 | --- 97 | kind: ConfigMap 98 | apiVersion: v1 99 | metadata: 100 | name: kube-flannel-cfg 101 | namespace: kube-system 102 | labels: 103 | tier: node 104 | app: flannel 105 | data: 106 | cni-conf.json: | 107 | { 108 | "name": "cbr0", 109 | "plugins": [ 110 | { 111 | "type": "flannel", 112 | "delegate": { 113 | "hairpinMode": true, 114 | "isDefaultGateway": true 115 | } 116 | }, 117 | { 118 | "type": "portmap", 119 | "capabilities": { 120 | "portMappings": true 121 | } 122 | } 123 | ] 124 | } 125 | net-conf.json: | 126 | { 127 | "Network": "10.244.0.0/16", 128 | "Backend": { 129 | "Type": "vxlan" 130 | } 131 | } 132 | --- 133 | apiVersion: extensions/v1beta1 134 | kind: DaemonSet 135 | metadata: 136 | name: kube-flannel-ds-amd64 137 | namespace: kube-system 138 | labels: 139 | tier: node 140 | app: flannel 141 | spec: 142 | template: 143 | metadata: 144 | labels: 145 | tier: node 146 | app: flannel 147 | spec: 148 | hostNetwork: true 149 | nodeSelector: 150 | beta.kubernetes.io/arch: amd64 151 | tolerations: 152 | - operator: Exists 153 | effect: NoSchedule 154 | serviceAccountName: flannel 155 | initContainers: 156 | - name: install-cni 157 | image: cargo.caicloud.io/caicloud/flannel:v0.11.0-amd64 158 | command: 159 | - cp 160 | args: 161 | - -f 162 | - /etc/kube-flannel/cni-conf.json 163 | - /etc/cni/net.d/10-flannel.conflist 164 | volumeMounts: 165 | - name: cni 166 | mountPath: /etc/cni/net.d 167 | - name: flannel-cfg 168 | mountPath: /etc/kube-flannel/ 169 | containers: 170 | - name: kube-flannel 171 | image: cargo.caicloud.io/caicloud/flannel:v0.11.0-amd64 172 | command: 173 | - /opt/bin/flanneld 174 | args: 175 | - --ip-masq 176 | - --kube-subnet-mgr 177 | resources: 178 | requests: 179 | cpu: "100m" 180 | memory: "50Mi" 181 | limits: 182 | cpu: "100m" 183 | memory: "50Mi" 184 | securityContext: 185 | privileged: false 186 | capabilities: 187 | add: ["NET_ADMIN"] 188 | env: 189 | - name: POD_NAME 190 | valueFrom: 191 | fieldRef: 192 | fieldPath: metadata.name 193 | - name: POD_NAMESPACE 194 | valueFrom: 195 | fieldRef: 196 | fieldPath: metadata.namespace 197 | volumeMounts: 198 | - name: run 199 | mountPath: /run/flannel 200 | - name: flannel-cfg 201 | mountPath: /etc/kube-flannel/ 202 | volumes: 203 | - name: run 204 | hostPath: 205 | path: /run/flannel 206 | - name: cni 207 | hostPath: 208 | path: /etc/cni/net.d 209 | - name: flannel-cfg 210 | configMap: 211 | name: kube-flannel-cfg 212 | --- 213 | apiVersion: extensions/v1beta1 214 | kind: DaemonSet 215 | metadata: 216 | name: kube-flannel-ds-arm64 217 | namespace: kube-system 218 | labels: 219 | tier: node 220 | app: flannel 221 | spec: 222 | template: 223 | metadata: 224 | labels: 225 | tier: node 226 | app: flannel 227 | spec: 228 | hostNetwork: true 229 | nodeSelector: 230 | beta.kubernetes.io/arch: arm64 231 | tolerations: 232 | - operator: Exists 233 | effect: NoSchedule 234 | serviceAccountName: flannel 235 | initContainers: 236 | - name: install-cni 237 | image: cargo.caicloud.io/caicloud/flannel:v0.11.0-arm64 238 | command: 239 | - cp 240 | args: 241 | - -f 242 | - /etc/kube-flannel/cni-conf.json 243 | - /etc/cni/net.d/10-flannel.conflist 244 | volumeMounts: 245 | - name: cni 246 | mountPath: /etc/cni/net.d 247 | - name: flannel-cfg 248 | mountPath: /etc/kube-flannel/ 249 | containers: 250 | - name: kube-flannel 251 | image: cargo.caicloud.io/caicloud/flannel:v0.11.0-arm64 252 | command: 253 | - /opt/bin/flanneld 254 | args: 255 | - --ip-masq 256 | - --kube-subnet-mgr 257 | resources: 258 | requests: 259 | cpu: "100m" 260 | memory: "50Mi" 261 | limits: 262 | cpu: "100m" 263 | memory: "50Mi" 264 | securityContext: 265 | privileged: false 266 | capabilities: 267 | add: ["NET_ADMIN"] 268 | env: 269 | - name: POD_NAME 270 | valueFrom: 271 | fieldRef: 272 | fieldPath: metadata.name 273 | - name: POD_NAMESPACE 274 | valueFrom: 275 | fieldRef: 276 | fieldPath: metadata.namespace 277 | volumeMounts: 278 | - name: run 279 | mountPath: /run/flannel 280 | - name: flannel-cfg 281 | mountPath: /etc/kube-flannel/ 282 | volumes: 283 | - name: run 284 | hostPath: 285 | path: /run/flannel 286 | - name: cni 287 | hostPath: 288 | path: /etc/cni/net.d 289 | - name: flannel-cfg 290 | configMap: 291 | name: kube-flannel-cfg 292 | --- 293 | apiVersion: extensions/v1beta1 294 | kind: DaemonSet 295 | metadata: 296 | name: kube-flannel-ds-arm 297 | namespace: kube-system 298 | labels: 299 | tier: node 300 | app: flannel 301 | spec: 302 | template: 303 | metadata: 304 | labels: 305 | tier: node 306 | app: flannel 307 | spec: 308 | hostNetwork: true 309 | nodeSelector: 310 | beta.kubernetes.io/arch: arm 311 | tolerations: 312 | - operator: Exists 313 | effect: NoSchedule 314 | serviceAccountName: flannel 315 | initContainers: 316 | - name: install-cni 317 | image: cargo.caicloud.io/caicloud/flannel:v0.11.0-arm 318 | command: 319 | - cp 320 | args: 321 | - -f 322 | - /etc/kube-flannel/cni-conf.json 323 | - /etc/cni/net.d/10-flannel.conflist 324 | volumeMounts: 325 | - name: cni 326 | mountPath: /etc/cni/net.d 327 | - name: flannel-cfg 328 | mountPath: /etc/kube-flannel/ 329 | containers: 330 | - name: kube-flannel 331 | image: cargo.caicloud.io/caicloud/flannel:v0.11.0-arm 332 | command: 333 | - /opt/bin/flanneld 334 | args: 335 | - --ip-masq 336 | - --kube-subnet-mgr 337 | resources: 338 | requests: 339 | cpu: "100m" 340 | memory: "50Mi" 341 | limits: 342 | cpu: "100m" 343 | memory: "50Mi" 344 | securityContext: 345 | privileged: false 346 | capabilities: 347 | add: ["NET_ADMIN"] 348 | env: 349 | - name: POD_NAME 350 | valueFrom: 351 | fieldRef: 352 | fieldPath: metadata.name 353 | - name: POD_NAMESPACE 354 | valueFrom: 355 | fieldRef: 356 | fieldPath: metadata.namespace 357 | volumeMounts: 358 | - name: run 359 | mountPath: /run/flannel 360 | - name: flannel-cfg 361 | mountPath: /etc/kube-flannel/ 362 | volumes: 363 | - name: run 364 | hostPath: 365 | path: /run/flannel 366 | - name: cni 367 | hostPath: 368 | path: /etc/cni/net.d 369 | - name: flannel-cfg 370 | configMap: 371 | name: kube-flannel-cfg 372 | --- 373 | apiVersion: extensions/v1beta1 374 | kind: DaemonSet 375 | metadata: 376 | name: kube-flannel-ds-ppc64le 377 | namespace: kube-system 378 | labels: 379 | tier: node 380 | app: flannel 381 | spec: 382 | template: 383 | metadata: 384 | labels: 385 | tier: node 386 | app: flannel 387 | spec: 388 | hostNetwork: true 389 | nodeSelector: 390 | beta.kubernetes.io/arch: ppc64le 391 | tolerations: 392 | - operator: Exists 393 | effect: NoSchedule 394 | serviceAccountName: flannel 395 | initContainers: 396 | - name: install-cni 397 | image: cargo.caicloud.io/caicloud/flannel:v0.11.0-ppc64le 398 | command: 399 | - cp 400 | args: 401 | - -f 402 | - /etc/kube-flannel/cni-conf.json 403 | - /etc/cni/net.d/10-flannel.conflist 404 | volumeMounts: 405 | - name: cni 406 | mountPath: /etc/cni/net.d 407 | - name: flannel-cfg 408 | mountPath: /etc/kube-flannel/ 409 | containers: 410 | - name: kube-flannel 411 | image: cargo.caicloud.io/caicloud/flannel:v0.11.0-ppc64le 412 | command: 413 | - /opt/bin/flanneld 414 | args: 415 | - --ip-masq 416 | - --kube-subnet-mgr 417 | resources: 418 | requests: 419 | cpu: "100m" 420 | memory: "50Mi" 421 | limits: 422 | cpu: "100m" 423 | memory: "50Mi" 424 | securityContext: 425 | privileged: false 426 | capabilities: 427 | add: ["NET_ADMIN"] 428 | env: 429 | - name: POD_NAME 430 | valueFrom: 431 | fieldRef: 432 | fieldPath: metadata.name 433 | - name: POD_NAMESPACE 434 | valueFrom: 435 | fieldRef: 436 | fieldPath: metadata.namespace 437 | volumeMounts: 438 | - name: run 439 | mountPath: /run/flannel 440 | - name: flannel-cfg 441 | mountPath: /etc/kube-flannel/ 442 | volumes: 443 | - name: run 444 | hostPath: 445 | path: /run/flannel 446 | - name: cni 447 | hostPath: 448 | path: /etc/cni/net.d 449 | - name: flannel-cfg 450 | configMap: 451 | name: kube-flannel-cfg 452 | --- 453 | apiVersion: extensions/v1beta1 454 | kind: DaemonSet 455 | metadata: 456 | name: kube-flannel-ds-s390x 457 | namespace: kube-system 458 | labels: 459 | tier: node 460 | app: flannel 461 | spec: 462 | template: 463 | metadata: 464 | labels: 465 | tier: node 466 | app: flannel 467 | spec: 468 | hostNetwork: true 469 | nodeSelector: 470 | beta.kubernetes.io/arch: s390x 471 | tolerations: 472 | - operator: Exists 473 | effect: NoSchedule 474 | serviceAccountName: flannel 475 | initContainers: 476 | - name: install-cni 477 | image: cargo.caicloud.io/caicloud/flannel:v0.11.0-s390x 478 | command: 479 | - cp 480 | args: 481 | - -f 482 | - /etc/kube-flannel/cni-conf.json 483 | - /etc/cni/net.d/10-flannel.conflist 484 | volumeMounts: 485 | - name: cni 486 | mountPath: /etc/cni/net.d 487 | - name: flannel-cfg 488 | mountPath: /etc/kube-flannel/ 489 | containers: 490 | - name: kube-flannel 491 | image: cargo.caicloud.io/caicloud/flannel:v0.11.0-s390x 492 | command: 493 | - /opt/bin/flanneld 494 | args: 495 | - --ip-masq 496 | - --kube-subnet-mgr 497 | resources: 498 | requests: 499 | cpu: "100m" 500 | memory: "50Mi" 501 | limits: 502 | cpu: "100m" 503 | memory: "50Mi" 504 | securityContext: 505 | privileged: false 506 | capabilities: 507 | add: ["NET_ADMIN"] 508 | env: 509 | - name: POD_NAME 510 | valueFrom: 511 | fieldRef: 512 | fieldPath: metadata.name 513 | - name: POD_NAMESPACE 514 | valueFrom: 515 | fieldRef: 516 | fieldPath: metadata.namespace 517 | volumeMounts: 518 | - name: run 519 | mountPath: /run/flannel 520 | - name: flannel-cfg 521 | mountPath: /etc/kube-flannel/ 522 | volumes: 523 | - name: run 524 | hostPath: 525 | path: /run/flannel 526 | - name: cni 527 | hostPath: 528 | path: /etc/cni/net.d 529 | - name: flannel-cfg 530 | configMap: 531 | name: kube-flannel-cfg 532 | -------------------------------------------------------------------------------- /tutorials/resources/ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: tutorial 5 | -------------------------------------------------------------------------------- /tutorials/resources/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: nginx 5 | namespace: tutorial 6 | spec: 7 | containers: 8 | - name: nginx 9 | image: cargo.caicloud.io/caicloud/nginx:1.9.3 10 | resources: 11 | requests: 12 | cpu: "0.1" 13 | memory: "100Mi" 14 | limits: 15 | cpu: "0.1" 16 | memory: "100Mi" 17 | -------------------------------------------------------------------------------- /tutorials/resources/pod_configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | namespace: tutorial 5 | name: pod-configmap 6 | spec: 7 | restartPolicy: Never 8 | containers: 9 | - name: test-container 10 | image: cargo.caicloud.io/caicloud/busybox:1.26 11 | command: ["/bin/sh"] 12 | args: ["-c", "cat /etc/config/game.properties && cat /etc/config/ui.properties"] 13 | volumeMounts: 14 | - name: config-volume 15 | mountPath: /etc/config 16 | resources: 17 | requests: 18 | cpu: "0.1" 19 | memory: "100Mi" 20 | limits: 21 | cpu: "0.1" 22 | memory: "100Mi" 23 | volumes: 24 | - name: config-volume 25 | configMap: 26 | name: game-config 27 | -------------------------------------------------------------------------------- /tutorials/resources/pod_exceeds_quota.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: pod 5 | namespace: tutorial 6 | spec: 7 | containers: 8 | - name: nginx 9 | image: nginx:1.13 10 | resources: 11 | requests: 12 | cpu: "2" 13 | memory: "1Gi" 14 | limits: 15 | cpu: "2" 16 | memory: "1Gi" 17 | -------------------------------------------------------------------------------- /tutorials/resources/pod_health.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | namespace: tutorial 5 | name: nginx 6 | labels: 7 | app: nginx 8 | spec: 9 | containers: 10 | - name: nginx 11 | image: cargo.caicloud.io/caicloud/nginx:1.9.7 12 | resources: 13 | requests: 14 | cpu: "0.1" 15 | memory: "100Mi" 16 | limits: 17 | cpu: "0.1" 18 | memory: "100Mi" 19 | livenessProbe: 20 | httpGet: 21 | path: / 22 | port: 80 23 | initialDelaySeconds: 5 24 | periodSeconds: 5 25 | -------------------------------------------------------------------------------- /tutorials/resources/pod_unhealth.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | namespace: tutorial 5 | name: nginx 6 | labels: 7 | app: nginx 8 | spec: 9 | containers: 10 | - name: nginx 11 | image: cargo.caicloud.io/caicloud/nginx:1.9.7 12 | resources: 13 | requests: 14 | cpu: "0.1" 15 | memory: "100Mi" 16 | limits: 17 | cpu: "0.1" 18 | memory: "100Mi" 19 | livenessProbe: 20 | httpGet: 21 | path: / 22 | port: 8080 23 | initialDelaySeconds: 5 24 | periodSeconds: 5 25 | -------------------------------------------------------------------------------- /tutorials/resources/pod_with_pvc.yaml: -------------------------------------------------------------------------------- 1 | kind: Pod 2 | apiVersion: v1 3 | metadata: 4 | name: pod-with-pvc 5 | spec: 6 | containers: 7 | - name: nginx 8 | image: cargo.caicloud.io/caicloud/nginx:1.9.3 9 | volumeMounts: 10 | - mountPath: "/var/www/html" 11 | name: mypd 12 | volumes: 13 | - name: mypd 14 | persistentVolumeClaim: 15 | claimName: myclaim 16 | -------------------------------------------------------------------------------- /tutorials/resources/pv_hostpath.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: pv-hostpath 5 | spec: 6 | storageClassName: standard 7 | capacity: 8 | storage: 10Gi 9 | accessModes: 10 | - ReadWriteMany 11 | persistentVolumeReclaimPolicy: Recycle 12 | hostPath: 13 | path: /tmp/data1 14 | -------------------------------------------------------------------------------- /tutorials/resources/pvc.yaml: -------------------------------------------------------------------------------- 1 | kind: PersistentVolumeClaim 2 | apiVersion: v1 3 | metadata: 4 | name: myclaim 5 | spec: 6 | accessModes: 7 | - ReadWriteMany 8 | resources: 9 | requests: 10 | storage: 8Gi 11 | -------------------------------------------------------------------------------- /tutorials/resources/pvc_standard.yaml: -------------------------------------------------------------------------------- 1 | kind: PersistentVolumeClaim 2 | apiVersion: v1 3 | metadata: 4 | name: myclaim 5 | spec: 6 | storageClassName: standard 7 | accessModes: 8 | - ReadWriteMany 9 | resources: 10 | requests: 11 | storage: 8Gi 12 | -------------------------------------------------------------------------------- /tutorials/resources/quota.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ResourceQuota 3 | metadata: 4 | name: tutorial-quota 5 | namespace: tutorial 6 | spec: 7 | hard: 8 | # Across all pods in a non-terminal state, the sum of CPU requests cannot exceed this value. 9 | requests.cpu: "1" 10 | # Across all pods in a non-terminal state, the sum of memory requests cannot exceed this value. 11 | requests.memory: 1Gi 12 | # Across all pods in a non-terminal state, the sum of CPU limits cannot exceed this value. 13 | limits.cpu: "2" 14 | # Across all pods in a non-terminal state, the sum of memory limits cannot exceed this value. 15 | limits.memory: 2Gi 16 | -------------------------------------------------------------------------------- /tutorials/resources/ui.properties: -------------------------------------------------------------------------------- 1 | color.good=purple 2 | color.bad=yellow 3 | allow.textmode=true 4 | how.nice.to.look=fairlyNice --------------------------------------------------------------------------------