<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Kubernetes 入门实战课 on GoClub</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/</link><description>Recent content in Kubernetes 入门实战课 on GoClub</description><generator>Hugo</generator><language>zh-cn</language><atom:link href="https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/index.xml" rel="self" type="application/rss+xml"/><item><title>00 开篇词 迎难而上，做云原生时代的弄潮儿</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/001/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/001/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/00%20%e5%bc%80%e7%af%87%e8%af%8d%20%e8%bf%8e%e9%9a%be%e8%80%8c%e4%b8%8a%ef%bc%8c%e5%81%9a%e4%ba%91%e5%8e%9f%e7%94%9f%e6%97%b6%e4%bb%a3%e7%9a%84%e5%bc%84%e6%bd%ae%e5%84%bf.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="00-开篇词-迎难而上-做云原生时代的弄潮儿"&gt;00 开篇词 迎难而上，做云原生时代的弄潮儿&lt;/h1&gt;
&lt;p&gt;你好，我是罗剑锋，不过更愿意你称呼我“Chrono”。&lt;/p&gt;
&lt;p&gt;先来简单介绍一下我自己吧。作为一个有着近二十年工作经验的“技术老兵”，我一直奋斗在开发第一线，从Windows到Linux、从硬件到软件，从单机到集群、云，开发了各种形式的应用，也经历了许多大小不一的公司，现在是在API管理和微服务平台公司Kong，基于Nginx/OpenResty研发Kong Gateway、Kong ingress Controller等产品。&lt;/p&gt;
&lt;p&gt;其实我应该算是极客时间的老朋友了，在2019年开了&lt;a href="https://study.disign.me/document/%E9%80%8F%E8%A7%86HTTP%E5%8D%8F%E8%AE%AE" rel="nofollow noreferrer noopener"&gt;《透视HTTP协议》&lt;/a&gt;的课程，在2020年开了《C++实战笔记》的课程，然后因为工作上的事情比较多，“消失”了近两年的时间。&lt;/p&gt;
&lt;p&gt;不过这段日子里我倒没有“两耳不闻窗外事”，而是一直在关注业界的新技术新动向，所以今天，我再次回到了极客时间的这个大讲堂，想和你聊聊如今风头正劲的Kubernetes。&lt;/p&gt;
&lt;h2 id="现在的kubernetes"&gt;现在的Kubernetes&lt;/h2&gt;
&lt;p&gt;你一定听说过Kubernetes吧，也许更熟悉一点的，是许多人总挂在嘴边的缩写——“K8s”。&lt;/p&gt;
&lt;p&gt;自从2013年Docker诞生以来，容器一跃成为了IT界最热门的话题。而Kubernetes则趁着容器的“东风”，借助Google和CNCF的强力“背书”，击败了Docker Swarm和Apache Mesos，成为了“容器编排”领域的王者至尊。&lt;/p&gt;
&lt;p&gt;换一个更通俗易懂的说法，那就是：&lt;strong&gt;现在Kubernetes已经没有了实际意义上的竞争对手，它的地位就如同Linux一样，成为了事实上的云原生操作系统，是构建现代应用的基石&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;毕竟，现代应用是什么？是微服务，是服务网格，这些统统要围绕着容器来开发、部署和运行，而使用容器就必然要用到容器编排技术，在现在只有唯一的选项，那就是Kubernetes。&lt;/p&gt;
&lt;p&gt;不管你是研发、测试、还是运维，不管你是前台、后台、还是中台，不管你用的是C++、Java还是Python，不管你是搞数据库、区块链、还是人工智能，不管你是从事网站、电商、还是音视频，在这个“云原生”时代，Kubernetes都是一个绕不过去的产品，是我们工作中迟早要面对的“坎儿”。&lt;/p&gt;
&lt;p&gt;你也许会有疑惑：我现在的工作和“云”毫不沾边，而且Kubernetes都“火”了这么久，现在才开始学，会不会有点晚了？值不值呢？&lt;/p&gt;
&lt;p&gt;这里我就要引用一句老话了：“艺多不压身”，还有另一句：“机遇总是偏爱有准备的人”。&lt;/p&gt;
&lt;p&gt;“云原生”已经是现在IT界的普遍共识，是未来的大势所趋。也许这个“浪潮”暂时还没有打到你这里来，但一旦它真正来临，只有你提前做好了知识储备，才能够迎难而进，站上浪头成为“弄潮儿”，否则就可能会被“拍在沙滩上”。&lt;/p&gt;
&lt;p&gt;我和你说一下我自己的亲身经历吧。&lt;/p&gt;
&lt;p&gt;早在Docker和Kubernetes发布之初，我就对它们有过关注。不过因为我的主要工作语言是C/C++，而Docker和Kubernetes用的都是Go，当时Go的性能还比较差（比如垃圾回收机制导致的著名Stop the World），所以我只是简单了解了，没有去特别研究。&lt;/p&gt;
&lt;p&gt;过了几年，一个偶然的机会，我们要在客户的环境里部署自研应用，但依赖库差异太大，很难搞定。这个时候我又想起了Docker，经过一个多星期的折腾，艰难地啃下了一大堆资料之后，总算是把系统正常上线了。&lt;/p&gt;
&lt;p&gt;虽然任务完成了，但也让我意识到自己从前对Docker的轻视是非常错误的，于是就痛下决心，开始从头、系统地学习整理容器知识，之后也就很自然地搭上了Kubernetes这条“大船”。&lt;/p&gt;
&lt;p&gt;再后来，我想换新工作，面试的时候Boss出了道“偏门”题，讲Kubernetes的容器和环境安全。虽然我不熟悉这个方向，但凭借着之前的积累，只用了一个晚上就赶出了20多页的PPT，第二天面对几位评委侃侃而谈，最终顺利拿下了Offer。&lt;/p&gt;
&lt;p&gt;你看，如果我当时一味固执己见，只呆在自己的“舒适区”里，不主动去学习容器技术和Kubernetes，当机遇不期而至的时候，很可能就会因为手足无措而错失了升职加薪的良机。&lt;/p&gt;
&lt;p&gt;所以也希望你不要犯我当初的错误，我们应当看清楚时代的走向，尽可能超前于时代，越早掌握Kubernetes，将来自己成功的几率就越大。&lt;/p&gt;
&lt;h2 id="学习kubernetes有哪些难点"&gt;学习Kubernetes有哪些难点&lt;/h2&gt;
&lt;p&gt;那么，我们应该怎么来学习Kubernetes呢？&lt;/p&gt;
&lt;p&gt;其实今天学习Kubernetes的难度，比起前几年来说，已经是极大地下降了，网上资料非常多，有博客、专题、视频等各种形式，而且Kubernetes为了推广自身，在官网上还放出了非常详细的教程和参考手册，只要你肯花时间，完全可以“自学成才”。&lt;/p&gt;
&lt;p&gt;不过，“理想很丰满，现实很骨感”。&lt;strong&gt;理论上讲，学习Kubernetes只要看资料就足够了，但实际情况却是学习起来仍然困难重重，我们会遇到很多意想不到的问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是因为Kubernetes是一个分布式、集群化、云时代的系统，有许多新概念和新思维方式，和我们以往经验、认知的差异很大。&lt;/p&gt;
&lt;p&gt;我觉得，Kubernetes技术栈的特点可以用四个字来概括，那就是“&lt;strong&gt;新、广、杂、深&lt;/strong&gt;”。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“新”是指Kubernetes用到的基本上都是比较前沿、陌生的技术，而且版本升级很快，经常变来变去。&lt;/li&gt;
&lt;li&gt;“广”是指Kubernetes涉及的应用领域很多、覆盖面非常广，不太好找到合适的切入点或者突破口。&lt;/li&gt;
&lt;li&gt;“杂”是指Kubernetes的各种实现比较杂乱，谁都可以上来“掺和”一下，让人看的眼晕。&lt;/li&gt;
&lt;li&gt;“深”是指Kubernetes面对的每个具体问题和方向，都需要有很深的技术背景和底蕴，想要吃透很不容易。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这四个特点就导致Kubernetes的“门槛”相当高，学习曲线非常陡峭，学习成本非常昂贵，有可能花费了大量的时间和精力却南辕北辙、收效甚微，这点我确实是深有体会。&lt;/p&gt;
&lt;p&gt;比如在初学的过程中我就遇到过这些疑问，不知道你有没有同感：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker、Containerd、K8s、K3s、MicroK8s、Minikube……这么多项目，该如何选择？&lt;/li&gt;
&lt;li&gt;容器的概念太抽象了，怎么才能够快速准确地理解？&lt;/li&gt;
&lt;li&gt;镜像的命名稀奇古怪，里面的“bionic”“buster”等都是什么意思？&lt;/li&gt;
&lt;li&gt;不知道怎么搭建出Kubernetes环境，空有理论知识，无法联系实际。&lt;/li&gt;
&lt;li&gt;YAML文件又长又乱，到哪里能找到说明，能否遵循什么简单规律写出来？&lt;/li&gt;
&lt;li&gt;Pod、Deployment、StatefulSet……这么多的对象，有没有什么内在的脉络和联系？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;遗憾的是，这些问题很难在现有的Kubernetes资料里找到答案。&lt;/p&gt;
&lt;p&gt;我个人感觉，它们往往“站得太高”，没有为“零基础”的初学者考虑，总会预设一些前提，比如熟悉Linux系统、知道编程语言、了解网络技术等等，有时候还会因为版本过时而失效，或者是忽略一些关键的细节。&lt;/p&gt;
&lt;p&gt;这就让我们初学者经常“卡”在一些看似无关紧要却又非常现实的难点上，这样的点越积越多，最后就让人逐渐丧失了学习Kubernetes的信心和勇气。&lt;/p&gt;
&lt;p&gt;所以，我就想以自己的学习经历为基础，融合个人感悟、经验教训和心得技巧，整理出一个初学者面对Kubernetes这门新技术的入门路线和系统思路，让你在学习时有捷径可走，不再有迷茫和困惑，能快速高效地迈入Kubernetes的宏伟殿堂。&lt;/p&gt;
&lt;h2 id="在这个专栏里你会怎么学习kubernetes"&gt;在这个专栏里你会怎么学习Kubernetes&lt;/h2&gt;
&lt;p&gt;讲到这里，你一定很想知道，这个专栏有什么特点，和别的课程有哪些不一样，结合刚才讲的Kubernetes技术栈四个特点“&lt;strong&gt;技术新、领域广、实现杂、方向深&lt;/strong&gt;”，我来仔细说一说我的想法和考量。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一，没有太多前提，不会Go你也可以学。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在这门课里，我不会要求你学习Go语言，也不会去讲Kubernetes的源码。&lt;/p&gt;
&lt;p&gt;虽然是研发出身，但我并没有特别深入地了解Go语言，但是，我认为这反而是一个优势。因为面对Kubernetes的时候我和你是“平等”的，不会“下意识”地去从源码层次来讲解它的运行原理，更能够设身处地为你着想。&lt;/p&gt;
&lt;p&gt;讲源码虽然会很透彻，但它的前置条件实在是太高了，不是所有的人都具备这个基础的。&lt;strong&gt;为了学习Kubernetes要先了解Go语言，有点“本末倒置”&lt;/strong&gt;，如同钱钟书老先生所说：“如果你吃了个鸡蛋，觉得味道不错，何必去认识那个下蛋的母鸡呢?”（我觉得这方面也可以对比一下Linux操作系统，它是用C语言写的，但几乎没有人要求我们在学习Linux之前需要事先掌握C语言。）&lt;/p&gt;
&lt;p&gt;不过如果你真想做Kubernetes开发，等学会了Kubernetes的基本概念和用法，再回头去学Go语言也完全来得及。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第二，这个专栏我会定位在“入门”，也就是说，不会去讲那些高深的大道理和复杂的工作流程，语言也尽量朴素平实，少用专业术语和缩略词。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;毕竟Kubernetes系统涉及的领域太过庞大，对于初次接触的人来说直接“抠”内部细节不太合适，那样很容易会“跑偏”“钻牛角尖”。&lt;/p&gt;
&lt;p&gt;我觉得学习Kubernetes最好的方式是&lt;strong&gt;尽快建立一个全局观和大局观，等到你对这个陌生领域的全貌有了粗略但完整的认识之后，再挑选一个自己感兴趣的方向去研究，才是性价比最高的做法。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;而且前面也说过，Kubernetes版本更新很快，有的功能点或许一段时间之后就成了废弃的特性（比如ComponentStatus在1.19被废弃、PodSecurityPolicy在1.21被废弃），如果讲得太细，万一今后它过时无用，就实在是太尴尬了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第三，课程会以实战为导向，强调眼手脑结合，鼓励你多动手、多实际操作，我认为这是这个课程最大的特点。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kubernetes一般每年都会发布一个大版本，大版本又会有很多的小版本，每个版本都会持续改进功能特性，&lt;strong&gt;但一味求新，不符合当前的实际情况，毕竟生产环境里稳定是最重要的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;所以，我就选择了今年（2022年）初发布的Kubernetes 1.23.3，它是最后一个支持Docker的大版本，承上启下，具有很多的新特性，同时也保留了足够的历史兼容性，非常适合用来学习Kubernetes。&lt;/p&gt;</description></item><item><title>00 课前准备 动手实践才是最好的学习方式</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/002/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/002/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/00%20%e8%af%be%e5%89%8d%e5%87%86%e5%a4%87%20%e5%8a%a8%e6%89%8b%e5%ae%9e%e8%b7%b5%e6%89%8d%e6%98%af%e6%9c%80%e5%a5%bd%e7%9a%84%e5%ad%a6%e4%b9%a0%e6%96%b9%e5%bc%8f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="00-课前准备-动手实践才是最好的学习方式"&gt;00 课前准备 动手实践才是最好的学习方式&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono，今天我们的任务是搭建实验环境。&lt;/p&gt;
&lt;p&gt;如果你看过我的另外两个极客时间专栏（&lt;a href="https://study.disign.me/document/%E9%80%8F%E8%A7%86HTTP%E5%8D%8F%E8%AE%AE" rel="nofollow noreferrer noopener"&gt;《透视HTTP协议》&lt;/a&gt;和《C++实战笔记》）就会知道，我一直都很强调实验环境的重要程度，毕竟计算机这门学科的实践性要大于理论性，而且有一个能够上手操作的实际环境，对学习理论也非常有帮助。&lt;/p&gt;
&lt;p&gt;落到我们的这个Kubernetes学习课上，实验环境更是必不可少的，因为和网络协议、编程语言不同，Kubernetes是一个更贴近于生产环境的庞大系统，如果“光说不练”，即使你掌握了再多的知识，但不能和实际相结合，也只能是“纸上谈兵”。&lt;/p&gt;
&lt;p&gt;俗话说：“&lt;strong&gt;工欲善其事，必先利其器&lt;/strong&gt;”，所以在正式学习之前，我们必须要有一个基本的实验环境，要能够在环境中熟悉Kubernetes的操作命令、验证测试Kubernetes的各种特性，有这样的“帮手”作为辅助，我们的学习才能够事半功倍。&lt;/p&gt;
&lt;h2 id="选择什么样的实验环境"&gt;选择什么样的实验环境&lt;/h2&gt;
&lt;p&gt;但想要得到一个完整的Kubernetes环境不那么容易，因为它太复杂了，对软硬件的要求都比较高，安装部署过程中还有许多的小细节，这些都会成为学习过程中的“拦路虎”。&lt;/p&gt;
&lt;p&gt;那么，应该怎么搭建出符合我们要求的实验环境呢？&lt;/p&gt;
&lt;p&gt;你也许会说：现在的云厂商到处都是，去网上申请一个就好了。&lt;/p&gt;
&lt;p&gt;这也许是一个比较便捷的获取途径，不过我有一些不同的意见。&lt;/p&gt;
&lt;p&gt;首先，这些网上的“云主机”很少是免费的，都需要花钱，而且想要好配置还要花更多的钱，对于我们的学习来说性价比不高。其次，“云主机”都是在“云”上，免不了会受网络和厂商的限制，存在不稳定因素。再次，这些“云主机”都是厂商已经为我们配好了的，很多软硬件都是固定的，不能随意定制，特别是很难真正“从零搭建”。&lt;/p&gt;
&lt;p&gt;考虑上面的这三点，我建议还是在本地搭建实验环境最好，不会受制于人，完全自主可控。&lt;/p&gt;
&lt;p&gt;不过，Kubernetes通常都运行在集群环境下，由多台服务器组成，难道我们还要自己置办几台电脑来组网吗？&lt;/p&gt;
&lt;p&gt;这倒大可不必。因为现在的&lt;strong&gt;虚拟机软件&lt;/strong&gt;已经非常成熟可靠了，能够在一台电脑里虚拟出多台主机，这些虚拟主机用起来和真实的物理主机几乎没有什么差异，只要你的电脑配置不是太差，组成一个三四台虚拟服务器的小集群是毫无问题的，而且虚拟机的创建删除都非常简单，成本极低。&lt;/p&gt;
&lt;p&gt;使用虚拟机软件还有一点额外的好处，由于很多云服务商内部也在大量使用虚拟服务器，Kubernetes里的容器技术也与虚拟机有很多相似之处，通过使用虚拟机，我们还能顺便对比这些技术的异同点，加深对Kubernetes的理解。&lt;/p&gt;
&lt;p&gt;所以综合来看，我建议你挑选一台配置不算太差的笔记本或者台式机，在里面使用虚拟机来搭建我们这门课程的实验环境。&lt;/p&gt;
&lt;p&gt;作为宿主机电脑的CPU和硬盘的要求不高，4核、300G就可以了，关键是内存要足够大，因为虚拟机和Kubernetes都很能“吃”内存，最少要有8G，这样起码能够支持开两个虚拟机组成最小的集群。&lt;/p&gt;
&lt;h2 id="选择什么样的虚拟机软件"&gt;选择什么样的虚拟机软件&lt;/h2&gt;
&lt;p&gt;确定了我们的实验环境大方向——虚拟机之后，我们就要选择虚拟机软件了。&lt;/p&gt;
&lt;p&gt;目前市面上的主流虚拟机软件屈指可数，所以选择起来并不算困难，我个人推荐的有两个：&lt;strong&gt;VirtualBox&lt;/strong&gt;和&lt;strong&gt;VMWare Fusion&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我们先讲适用面广的VirtualBox。&lt;/p&gt;
&lt;p&gt;VirtualBox是Oracle推出的一款虚拟机软件，历史很悠久，一直坚持免费政策，使用条款上也没有什么限制，是一个难得的精品软件。&lt;/p&gt;
&lt;p&gt;VirtualBox支持Windows和macOS，但有一个小缺点，它只能运行在Intel（x86_64）芯片上，不支持Apple新出的M1（arm64/aarch64）芯片，这导致它无法在新款Mac上使用，不得不说是一大遗憾。&lt;/p&gt;
&lt;p&gt;所以，如果你手里是Apple M1 Mac，就只能选择其他的虚拟机软件了。在macOS上，虚拟机最出名的应该是Parallel Desktop和VMWare Fusion这两个了，都需要付费。这里我比较推荐VMWare Fusion。&lt;/p&gt;
&lt;p&gt;不过对于VMWare Fusion来说，它对M1的支持进展比较迟缓，所以在正式的付费版出来之前，公布了一个“技术预览版”，是完全免费的，而且功能、性能也比较好，虽然有使用时间的限制（大约300天），但对于我们的学习来说是足够了。&lt;/p&gt;
&lt;p&gt;这里我给出VirtualBox（&lt;a href="https://www.virtualbox.org/wiki/Downloads" rel="nofollow noreferrer noopener"&gt;https://www.virtualbox.org/wiki/Downloads&lt;/a&gt;）和VMWare Fusion（&lt;a href="https://communities.vmware.com/t5/Fusion-for-Apple-Silicon-Tech/ct-p/3022" rel="nofollow noreferrer noopener"&gt;https://communities.vmware.com/t5/Fusion-for-Apple-Silicon-Tech/ct-p/3022&lt;/a&gt;）的网址，你可以课后去看一下，尽快下载。&lt;/p&gt;
&lt;h2 id="选择哪种linux发行版"&gt;选择哪种Linux发行版&lt;/h2&gt;
&lt;p&gt;有了虚拟机软件之后，我们就要在上面安装操作系统，在这方面毫无疑问只能是Linux，因为Kubernetes只能运行在Linux之上。&lt;/p&gt;
&lt;p&gt;不过麻烦的是，Linux世界又分裂成很多不同的发行版，流行的有CentOS/Fedora、 Ubuntu/Debian、SUSE等等，没有一个占据绝对统治地位的系统。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/764885b45fd3604a1091.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;那选哪个比较好呢？&lt;/p&gt;
&lt;p&gt;我们的主要目的是学习，所以易用性应该是首要关注点，另外系统还应该能够同时支持x86_64和arm64。筛选下来我建议选择Ubuntu 22.04 Jammy Jellyfish 桌面版（&lt;a href="https://ubuntu.com/download/desktop" rel="nofollow noreferrer noopener"&gt;https://ubuntu.com/download/desktop&lt;/a&gt;），它有足够新的特性，非常适合运行Kubernetes，而内置的浏览器、终端等工具也很方便我们的调试和测试。&lt;/p&gt;
&lt;p&gt;但对Apple M1用户来说，有一个不太好的消息，Ubuntu 22.04在内核由5.13升级到5.15的时候引入了一个小Bug，导致VMWare Fusion无法正常安装启动，这个问题直到4月份的正式版发布还没有解决。&lt;/p&gt;
&lt;p&gt;好在我当初为了测试，下载了一个较早的“daily build”版本，它可以在VMWare Fusion里正常安装，我把它上传到了云盘（&lt;a href="https://www.aliyundrive.com/s/FRmnawAURKu" rel="nofollow noreferrer noopener"&gt;https://www.aliyundrive.com/s/FRmnawAURKu&lt;/a&gt;），你可以下载后使用。&lt;/p&gt;
&lt;p&gt;需要注意一点，由于网站的限制，文件的后缀名被改成了 .mov ，你必须去掉这个后缀，还原成原始的 .iso 才能使用。&lt;/p&gt;</description></item><item><title>01 初识容器：万事开头难</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/003/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/003/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/01%20%e5%88%9d%e8%af%86%e5%ae%b9%e5%99%a8%ef%bc%9a%e4%b8%87%e4%ba%8b%e5%bc%80%e5%a4%b4%e9%9a%be.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="01-初识容器-万事开头难"&gt;01 初识容器：万事开头难&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在课前准备里，我们使用VirtualBox/VMWare搭建了Linux虚拟机环境，有了这个基础，今天我们就开始正式的学习。&lt;/p&gt;
&lt;p&gt;俗话说：“万事开头难”，对于Kubernetes这个庞大而陌生的领域来说更是如此，如何迈出学习的第一步非常关键，所以，今天我们先从最简单、最基本的知识入手，聊聊最流行的容器技术Docker，先搭建实验环境，再动手操作一下，进而破除它的神秘感。&lt;/p&gt;
&lt;h2 id="docker的诞生"&gt;Docker的诞生&lt;/h2&gt;
&lt;p&gt;现在我们都已经对Container、Kubernetes这些技术名词耳熟能详了，但你知道这一切的开端——Docker，第一次在世界上的亮相是什么样子的吗？&lt;/p&gt;
&lt;p&gt;九年前，也就是2013年3月15日，在北美的圣克拉拉市召开了一场Python开发者社区的主题会议PyCon，研究和探讨各种Python开发技术和应用，与我们常说的“云”“PaaS”“SaaS”根本毫不相关。&lt;/p&gt;
&lt;p&gt;在当天的会议日程快结束时，有一个“闪电演讲”（lighting talk）的小环节。其中有一位开发者，用了5分钟的时间，做了题为 “The future of Linux Containers” 的演讲，不过临近末尾因为超时而被主持人赶下了台，场面略显尴尬（你可以在&lt;a href="https://www.youtube.com/watch?v=wW9CAH9nSLs" rel="nofollow noreferrer noopener"&gt;这里&lt;/a&gt;回看这段具有历史意义的视频）。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/a7c5ab8abbc14a551dec.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;相信你一定猜到了，这个只有短短5分钟的技术演示，就是我们目前所看到的、席卷整个业界的云原生大潮的开端。正是在这段演讲里，Solomon Hykes（dotCloud公司，也就是Docker公司的创始人）首次向全世界展示了Docker技术。&lt;/p&gt;
&lt;p&gt;5分钟的时间非常短，但演讲里却包含了几个现在已经普及，但当时却非常新奇的概念，比如容器、镜像、隔离运行进程等，信息量非常大。&lt;/p&gt;
&lt;p&gt;PyCon2013大会之后，许多人都意识到了容器的价值和重要性，发现它能够解决困扰了云厂商多年的打包、部署、管理、运维等问题，Docker也就迅速流行起来，成为了GitHub上的明星项目。然后在几个月的时间里，Docker更是吸引了Amazon、Google、Red Hat等大公司的关注，这些公司利用自身的技术背景，纷纷在容器概念上大做文章，最终成就了我们今天所看到的至尊王者Kubernetes的出现。&lt;/p&gt;
&lt;h2 id="docker的形态"&gt;Docker的形态&lt;/h2&gt;
&lt;p&gt;好了，下面我们就要来一个“情境再现”，在我们的Linux虚拟机上搭建一个容器运行环境，模拟一下当年Solomon Hykes初次展示Docker的场景。&lt;/p&gt;
&lt;p&gt;当然，如今的Docker经过了九年的发展，已经远不是当初的“吴下阿蒙”了，不过最核心的那些概念和操作还是保持了一贯性，没有太大的变化。&lt;/p&gt;
&lt;p&gt;首先，我们需要对Docker的形态有所了解。目前使用Docker基本上有两个选择：&lt;strong&gt;Docker Desktop&lt;/strong&gt;和&lt;strong&gt;Docker Engine&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/d3f42d99b828942110fb.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;Docker Desktop是专门针对个人使用而设计的，支持Mac和Windows快速安装，具有直观的图形界面，还集成了许多周边工具，方便易用。&lt;/p&gt;
&lt;p&gt;不过，我个人不是太推荐使用Docker Desktop，原因有两个。第一个，它是商业产品，难免会带有Docker公司的“私人气息”，有一些自己的、非通用的东西，不利于我们后续的Kubernetes学习。第二个，它只是对个人学习免费，受条款限制不能商用，我们在日常工作中难免会“踩到雷区”。&lt;/p&gt;
&lt;p&gt;Docker Engine则和Docker Desktop正好相反，完全免费，但只能在Linux上运行，只能使用命令行操作，缺乏辅助工具，需要我们自己动手DIY运行环境。不过要是较起真来，它才是Docker当初的真正形态，“血脉”最纯正，也是现在各个公司在生产环境中实际使用的Docker产品，毕竟机房里99%的服务器跑的都是Linux。&lt;/p&gt;
&lt;p&gt;所以，在接下来的学习过程里，我推荐使用Docker Engine，之后在本专栏内，如果没有什么特别的声明，Docker这个词通常指的就是Docker Engine。&lt;/p&gt;
&lt;h2 id="docker的安装"&gt;Docker的安装&lt;/h2&gt;
&lt;p&gt;在课前准备里，我们已经在Linux虚拟机里安装了一些常用软件，用的是Ubuntu的包管理工具apt，所以，我们仍然可以使用同样的方式来安装Docker。&lt;/p&gt;
&lt;p&gt;先让我们尝试输入命令 docker ，会得到“命令未找到”的提示，还有如何安装的建议：&lt;/p&gt;
&lt;p&gt;所以，你只需要按照系统的提示，“照葫芦画瓢”输入命令，安装 docker.io 就可以了。为了方便，你还可以使用 -y 参数来避免确认，实现自动化操作：&lt;/p&gt;
&lt;p&gt;刚才说过，Docker Engine不像Docker Desktop那样可以安装后就直接使用，必须要做一些手工调整才能用起来，所以你还要在安装完毕后执行下面的两条命令：&lt;/p&gt;
&lt;p&gt;第一个 service docker start 是启动Docker的后台服务，第二个 usermod -aG 是把当前的用户加入Docker的用户组。这是因为操作Docker必须要有root权限，而直接使用root用户不够安全，&lt;strong&gt;加入Docker用户组是一个比较好的选择，这也是Docker官方推荐的做法&lt;/strong&gt;。当然，如果只是为了图省事，你也可以直接切换到root用户来操作Docker。&lt;/p&gt;
&lt;p&gt;上面的三条命令执行完之后，我们还需要退出系统（命令 exit ），再重新登录一次，这样才能让修改用户组的命令 usermod 生效。&lt;/p&gt;</description></item><item><title>02 被隔离的进程：一起来看看容器的本质</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/004/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/004/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/02%20%e8%a2%ab%e9%9a%94%e7%a6%bb%e7%9a%84%e8%bf%9b%e7%a8%8b%ef%bc%9a%e4%b8%80%e8%b5%b7%e6%9d%a5%e7%9c%8b%e7%9c%8b%e5%ae%b9%e5%99%a8%e7%9a%84%e6%9c%ac%e8%b4%a8.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="02-被隔离的进程-一起来看看容器的本质"&gt;02 被隔离的进程：一起来看看容器的本质&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在上一次课里，我们初步了解了容器技术，在Linux虚拟机里安装了当前最流行的容器Docker，还使用 docker ps、docker run等命令简单操作了容器。&lt;/p&gt;
&lt;p&gt;广义上来说，容器技术是动态的容器、静态的镜像和远端的仓库这三者的组合。不过，“容器”这个术语作为容器技术里的核心概念，不仅是大多数初次接触这个领域的人，即使是一些已经有使用经验的人，想要准确地把握它们的内涵、本质都是比较困难的。&lt;/p&gt;
&lt;p&gt;那么今天，我们就一起来看看究竟什么是容器（即狭义的、动态的容器）。&lt;/p&gt;
&lt;h2 id="容器到底是什么"&gt;容器到底是什么&lt;/h2&gt;
&lt;p&gt;从字面上来看，容器就是Container，一般把它形象地比喻成现实世界里的集装箱，它也正好和Docker的现实含义相对应，因为码头工人（那只可爱的小鲸鱼）就是不停地在搬运集装箱。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/893f5dda86aabd97cf0f.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;集装箱的作用是标准化封装各种货物，一旦打包完成之后，就可以从一个地方迁移到任意的其他地方。相比散装形式而言，集装箱隔离了箱内箱外两个世界，保持了货物的原始形态，避免了内外部相互干扰，极大地简化了商品的存储、运输、管理等工作。&lt;/p&gt;
&lt;p&gt;再回到我们的计算机世界，容器也发挥着同样的作用，不过它封装的货物是运行中的应用程序，也就是进程，同样它也会把进程与外界隔离开，让进程与外部系统互不影响。&lt;/p&gt;
&lt;p&gt;我们还是来实际操作一下吧，来看看在容器里运行的进程是个什么样子。&lt;/p&gt;
&lt;p&gt;首先，我们使用 docker pull 命令，拉取一个新的镜像——操作系统Alpine：&lt;/p&gt;
&lt;p&gt;然后我们使用 docker run 命令运行它的Shell程序：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意我们在这里多加了一个 -it 参数&lt;/strong&gt;，这样我们就会暂时离开当前的Ubuntu操作系统，进入容器内部。&lt;/p&gt;
&lt;p&gt;现在，让我们执行 cat /etc/os-release ，还有 ps 这两个命令，最后再使用 exit 退出，看看容器里与容器外有什么不同：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/0956c5d32b566efaa84e.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;就像这张截图里所显示的，在容器里查看系统信息，会发现已经不再是外面的Ubuntu系统了，而是变成了Alpine Linux 3.15，使用 ps 命令也只会看到一个完全“干净”的运行环境，除了Shell（即sh）没有其他的进程存在。&lt;/p&gt;
&lt;p&gt;也就是说，在容器内部是一个全新的Alpine操作系统，在这里运行的应用程序完全看不到外面的Ubuntu系统，两个系统被互相“隔离”了，就像是一个“世外桃源”。&lt;/p&gt;
&lt;p&gt;我们还可以再拉取一个Ubuntu 18.04的镜像，用同样的方式进入容器内部，然后执行 apt update、apt install 等命令来看看：&lt;/p&gt;
&lt;p&gt;这里我就不截图了，具体的结果留给你课下去实际操作体会。可以看到的是，容器里是另一个完整的Ubuntu 18.04 系统，我们可以在这个“世外桃源”做任意的事情，比如安装应用、运行Redis服务等。但无论我们在容器里做什么，都不会影响外面的Ubuntu系统（当然不是绝对的）。&lt;/p&gt;
&lt;p&gt;到这里，我们就可以得到一个初步的结论：&lt;strong&gt;容器，就是一个特殊的隔离环境，它能够让进程只看到这个环境里的有限信息，不能对外界环境施加影响&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;那么，很自然地，我们会产生另外一个问题：为什么需要创建这样的一个隔离环境，直接让进程在系统里运行不好吗？&lt;/p&gt;
&lt;h2 id="为什么要隔离"&gt;为什么要隔离&lt;/h2&gt;
&lt;p&gt;相信因为这两年疫情，你对“隔离”这个词不会感觉到太陌生。为了防止疫情蔓延，我们需要建立方舱、定点医院，把患病人群控制在特定的区域内，更进一步还会实施封闭小区、关停商场等行动。虽然这些措施带来了一些不便，但都是为了整个社会更大范围的正常运转。&lt;/p&gt;
&lt;p&gt;同样的，在计算机世界里的隔离也是出于同样的考虑，也就是&lt;strong&gt;系统安全&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;对于Linux操作系统来说，一个不受任何限制的应用程序是十分危险的。这个进程能够看到系统里所有的文件、所有的进程、所有的网络流量，访问内存里的任何数据，那么恶意程序很容易就会把系统搞瘫痪，正常程序也可能会因为无意的Bug导致信息泄漏或者其他安全事故。虽然Linux提供了用户权限控制，能够限制进程只访问某些资源，但这个机制还是比较薄弱的，和真正的“隔离”需求相差得很远。&lt;/p&gt;
&lt;p&gt;而现在，&lt;strong&gt;使用容器技术，我们就可以让应用程序运行在一个有严密防护的“沙盒”（Sandbox）环境之内&lt;/strong&gt;，就好像是把进程请进了“隔离酒店”，它可以在这个环境里自由活动，但绝不允许“越界”，从而保证了容器外系统的安全。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/cb0766849f0825b9cda5.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;另外，在计算机里有各种各样的资源，CPU、内存、硬盘、网卡，虽然目前的高性能服务器都是几十核CPU、上百GB的内存、数TB的硬盘、万兆网卡，但这些资源终究是有限的，而且考虑到成本，也不允许某个应用程序无限制地占用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;容器技术的另一个本领就是为应用程序加上资源隔离，在系统里切分出一部分资源，让它只能使用指定的配额&lt;/strong&gt;，比如只能使用一个CPU，只能使用1GB内存等等，就好像在隔离酒店里保证一日三餐，但想要吃山珍海味那是不行的。这样就可以避免容器内进程的过度系统消耗，充分利用计算机硬件，让有限的资源能够提供稳定可靠的服务。&lt;/p&gt;
&lt;p&gt;所以，虽然进程被“关”在了容器里，损失了一些自由，但却保证了整个系统的安全。而且只要进程遵守隔离规定，不做什么出格的事情，也完全是可以正常运行的。&lt;/p&gt;
&lt;h2 id="与虚拟机的区别是什么"&gt;与虚拟机的区别是什么&lt;/h2&gt;
&lt;p&gt;你也许会说，这么看来，容器不过就是常见的“沙盒”技术中的一种，和虚拟机差不了多少，那么它与虚拟机的区别在哪里呢？又有什么样的优势呢？&lt;/p&gt;
&lt;p&gt;在我看来，其实容器和虚拟机面对的都是相同的问题，使用的也都是虚拟化技术，只是所在的层次不同，我们可以参考Docker官网上的两张图，把这两者对比起来会更利于学习理解。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/c9fdf6703d1c89085d0f.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;（Docker官网的&lt;a href="https://www.docker.com/resources/what-container" rel="nofollow noreferrer noopener"&gt;图示&lt;/a&gt;其实并不太准确，容器并不直接运行在Docker上，Docker只是辅助建立隔离环境，让容器基于Linux操作系统运行）&lt;/p&gt;</description></item><item><title>03 容器化的应用：会了这些你就是Docker高手</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/005/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/005/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/03%20%e5%ae%b9%e5%99%a8%e5%8c%96%e7%9a%84%e5%ba%94%e7%94%a8%ef%bc%9a%e4%bc%9a%e4%ba%86%e8%bf%99%e4%ba%9b%e4%bd%a0%e5%b0%b1%e6%98%afDocker%e9%ab%98%e6%89%8b.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="03-容器化的应用-会了这些你就是docker高手"&gt;03 容器化的应用：会了这些你就是Docker高手&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在上一次课里，我们了解了容器技术中最核心的概念：容器，知道它就是一个系统中被隔离的特殊环境，进程可以在其中不受干扰地运行。我们也可以把这段描述再简化一点：&lt;strong&gt;容器就是被隔离的进程&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;相比笨重的虚拟机，容器有许多优点，那我们应该如何创建并运行容器呢？是要用Linux内核里的namespace、cgroup、chroot三件套吗？&lt;/p&gt;
&lt;p&gt;当然不会，那样的方式实在是太原始了，所以今天，我们就以Docker为例，来看看什么是容器化的应用，怎么来操纵容器化的应用。&lt;/p&gt;
&lt;h2 id="什么是容器化的应用"&gt;什么是容器化的应用&lt;/h2&gt;
&lt;p&gt;之前我们运行容器的时候，显然不是从零开始的，而是要先拉取一个“镜像”（image），再从这个“镜像”来启动容器，像[第一节课]这样：&lt;/p&gt;
&lt;p&gt;那么，这个“镜像”到底是什么东西呢？它又和“容器”有什么关系呢？&lt;/p&gt;
&lt;p&gt;其实我们在其他场合中也曾经见到过“镜像”这个词，比如最常见的光盘镜像，重装电脑时使用的硬盘镜像，还有虚拟机系统镜像。这些“镜像”都有一些相同点：只读，不允许修改，以标准格式存储了一系列的文件，然后在需要的时候再从中提取出数据运行起来。&lt;/p&gt;
&lt;p&gt;容器技术里的镜像也是同样的道理。因为容器是由操作系统动态创建的，那么必然就可以用一种办法把它的初始环境给固化下来，保存成一个静态的文件，相当于是把容器给“拍扁”了，这样就可以非常方便地存放、传输、版本化管理了。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/1bd6bb8a53b75acf0330.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;如果还拿之前的“小板房”来做比喻的话，那么镜像就可以说是一个“样板间”，把运行进程所需要的文件系统、依赖库、环境变量、启动参数等所有信息打包整合到了一起。之后镜像文件无论放在哪里，操作系统都能根据这个“样板间”快速重建容器，应用程序看到的就会是一致的运行环境了。&lt;/p&gt;
&lt;p&gt;从功能上来看，镜像和常见的tar、rpm、deb等安装包一样，都打包了应用程序，&lt;strong&gt;但最大的不同点在于它里面不仅有基本的可执行文件，还有应用运行时的整个系统环境。这就让镜像具有了非常好的跨平台便携性和兼容性&lt;/strong&gt;，能够让开发者在一个系统上开发（例如Ubuntu），然后打包成镜像，再去另一个系统上运行（例如CentOS），完全不需要考虑环境依赖的问题，是一种更高级的应用打包方式。&lt;/p&gt;
&lt;p&gt;理解了这一点，我们再回过头来看看第一节课里运行的Docker命令。&lt;/p&gt;
&lt;p&gt;docker pull busybox ，就是获取了一个打包了busybox应用的镜像，里面固化了busybox程序和它所需的完整运行环境。&lt;/p&gt;
&lt;p&gt;docker run busybox echo hello world ，就是提取镜像里的各种信息，运用namespace、cgroup、chroot技术创建出隔离环境，然后再运行busybox的 echo 命令，输出 hello world 的字符串。&lt;/p&gt;
&lt;p&gt;这两个步骤，由于是基于标准的Linux系统调用和只读的镜像文件，所以，无论是在哪种操作系统上，或者是使用哪种容器实现技术，都会得到完全一致的结果。&lt;/p&gt;
&lt;p&gt;推而广之，任何应用都能够用这种形式打包再分发后运行，这也是无数开发者梦寐以求的“一次编写，到处运行（Build once, Run anywhere）”的至高境界。所以，&lt;strong&gt;所谓的“容器化的应用”，或者“应用的容器化”，就是指应用程序不再直接和操作系统打交道，而是封装成镜像，再交给容器环境去运行&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;现在你就应该知道了，镜像就是静态的应用容器，容器就是动态的应用镜像，两者互相依存，互相转化，密不可分。&lt;/p&gt;
&lt;p&gt;之前的那张Docker官方架构图你还有印象吧，我们在第一节课曾经简单地介绍过。可以看到，在Docker里的核心处理对象就是镜像（image）和容器（container）：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/e28f749056d8fd2dc828.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;好，理解了什么是容器化的应用，接下来我们再来学习怎么操纵容器化的应用。因为镜像是容器运行的根本，先有镜像才有容器，所以先来看看关于镜像的一些常用命令。&lt;/p&gt;
&lt;h2 id="常用的镜像操作有哪些"&gt;常用的镜像操作有哪些&lt;/h2&gt;
&lt;p&gt;在前面的课程里你应该已经了解了两个基本命令，docker pull 从远端仓库拉取镜像，docker images 列出当前本地已有的镜像。&lt;/p&gt;
&lt;p&gt;docker pull 的用法还是比较简单的，和普通的下载非常像，不过我们需要知道镜像的命名规则，这样才能准确地获取到我们想要的容器镜像。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;镜像的完整名字由两个部分组成，名字和标签，中间用 : 连接起来。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;名字表明了应用的身份，比如busybox、Alpine、Nginx、Redis等等。标签（tag）则可以理解成是为了区分不同版本的应用而做的额外标记，任何字符串都可以，比如3.15是纯数字的版本号、jammy是项目代号、1.21-alpine是版本号加操作系统名等等。其中有一个比较特殊的标签叫“latest”，它是默认的标签，如果只提供名字没有附带标签，那么就会使用这个默认的“latest”标签。&lt;/p&gt;
&lt;p&gt;那么现在，你就可以把名字和标签组合起来，使用 docker pull 来拉取一些镜像了：&lt;/p&gt;
&lt;p&gt;有了这些镜像之后，我们再用 docker images 命令来看看它们的具体信息：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/92405f448882c92452cb.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;在这个列表里，你可以看到，REPOSITORY列就是镜像的名字，TAG就是这个镜像的标签，那么第三列“IMAGE ID”又是什么意思呢？&lt;/p&gt;
&lt;p&gt;它可以说是镜像唯一的标识，就好像是身份证号一样。比如这里我们可以用“ubuntu:jammy”来表示Ubuntu 22.04镜像，同样也可以用它的ID“d4c2c……”来表示。&lt;/p&gt;
&lt;p&gt;另外，你可能还会注意到，截图里的两个镜像“nginx:1.21-alpine”和“nginx:alpine”的IMAGE ID是一样的，都是“a63aa……”。这其实也很好理解，这就像是人的身份证号码是唯一的，但可以有大名、小名、昵称、绰号，同一个镜像也可以打上不同的标签，这样应用在不同的场合就更容易理解。&lt;/p&gt;</description></item><item><title>04 创建容器镜像：如何编写正确、高效的Dockerfile</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/006/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/006/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/04%20%e5%88%9b%e5%bb%ba%e5%ae%b9%e5%99%a8%e9%95%9c%e5%83%8f%ef%bc%9a%e5%a6%82%e4%bd%95%e7%bc%96%e5%86%99%e6%ad%a3%e7%a1%ae%e3%80%81%e9%ab%98%e6%95%88%e7%9a%84Dockerfile.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="04-创建容器镜像-如何编写正确-高效的dockerfile"&gt;04 创建容器镜像：如何编写正确、高效的Dockerfile&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;上一次的课程里我们一起学习了容器化的应用，也就是被打包成镜像的应用程序，然后再用各种Docker命令来运行、管理它们。&lt;/p&gt;
&lt;p&gt;那么这又会带来一个疑问：这些镜像是怎么创建出来的？我们能不能够制作属于自己的镜像呢？&lt;/p&gt;
&lt;p&gt;所以今天，我就来讲解镜像的内部机制，还有高效、正确地编写Dockerfile制作容器镜像的方法。&lt;/p&gt;
&lt;h2 id="镜像的内部机制是什么"&gt;镜像的内部机制是什么&lt;/h2&gt;
&lt;p&gt;现在你应该知道，镜像就是一个打包文件，里面包含了应用程序还有它运行所依赖的环境，例如文件系统、环境变量、配置参数等等。&lt;/p&gt;
&lt;p&gt;环境变量、配置参数这些东西还是比较简单的，随便用一个manifest清单就可以管理，真正麻烦的是文件系统。为了保证容器运行环境的一致性，镜像必须把应用程序所在操作系统的根目录，也就是rootfs，都包含进来。&lt;/p&gt;
&lt;p&gt;虽然这些文件里不包含系统内核（因为容器共享了宿主机的内核），但如果每个镜像都重复做这样的打包操作，仍然会导致大量的冗余。可以想象，如果有一千个镜像，都基于Ubuntu系统打包，那么这些镜像里就会重复一千次Ubuntu根目录，对磁盘存储、网络传输都是很大的浪费。&lt;/p&gt;
&lt;p&gt;很自然的，我们就会想到，应该把重复的部分抽取出来，只存放一份Ubuntu根目录文件，然后让这一千个镜像以某种方式共享这部分数据。&lt;/p&gt;
&lt;p&gt;这个思路，也正是容器镜像的一个重大创新点：分层，术语叫“&lt;strong&gt;Layer&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;容器镜像内部并不是一个平坦的结构，而是由许多的镜像层组成的，每层都是只读不可修改的一组文件，相同的层可以在镜像之间共享，然后多个层像搭积木一样堆叠起来，再使用一种叫“&lt;strong&gt;Union FS联合文件系统&lt;/strong&gt;”的技术把它们合并在一起，就形成了容器最终看到的文件系统（&lt;a href="https://linoxide.com/wp-content/uploads/2015/03/docker-filesystems-busyboxrw.png" rel="nofollow noreferrer noopener"&gt;图片来源&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/252742fd89224f413420.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;我来拿大家都熟悉的千层糕做一个形象的比喻吧。&lt;/p&gt;
&lt;p&gt;千层糕也是由很多层叠加在一起的，从最上面可以看到每层里面镶嵌的葡萄干、核桃、杏仁、青丝等，每一层糕就相当于一个Layer，干果就好比是Layer里的各个文件。但如果某两层的同一个位置都有干果，也就是有文件同名，那么我们就只能看到上层的文件，而下层的就被屏蔽了。&lt;/p&gt;
&lt;p&gt;你可以用命令 docker inspect 来查看镜像的分层信息，比如nginx:alpine镜像：&lt;/p&gt;
&lt;p&gt;它的分层信息在“RootFS”部分：-&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/25ed6f246f2124c6377c.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;通过这张截图就可以看到，nginx:alpine镜像里一共有6个Layer。&lt;/p&gt;
&lt;p&gt;相信你现在也就明白，之前在使用 docker pull、docker rmi 等命令操作镜像的时候，那些“奇怪”的输出信息是什么了，其实就是镜像里的各个Layer。Docker会检查是否有重复的层，如果本地已经存在就不会重复下载，如果层被其他镜像共享就不会删除，这样就可以节约磁盘和网络成本。&lt;/p&gt;
&lt;h2 id="dockerfile是什么"&gt;Dockerfile是什么&lt;/h2&gt;
&lt;p&gt;知道了容器镜像的内部结构和基本原理，我们就可以来学习如何自己动手制作容器镜像了，也就是自己打包应用。&lt;/p&gt;
&lt;p&gt;在之前我们讲容器的时候，曾经说过容器就是“小板房”，镜像就是“样板间”。那么，要造出这个“样板间”，就必然要有一个“施工图纸”，由它来规定如何建造地基、铺设水电、开窗搭门等动作。这个“施工图纸”就是“&lt;strong&gt;Dockerfile&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;比起容器、镜像来说，Dockerfile非常普通，它就是一个纯文本，里面记录了一系列的构建指令，比如选择基础镜像、拷贝文件、运行脚本等等，每个指令都会生成一个Layer，而Docker顺序执行这个文件里的所有步骤，最后就会创建出一个新的镜像出来。&lt;/p&gt;
&lt;p&gt;我们来看一个最简单的Dockerfile实例：&lt;/p&gt;
&lt;p&gt;这个文件里只有两条指令。&lt;/p&gt;
&lt;p&gt;第一条指令是 FROM，所有的Dockerfile都要从它开始，表示选择构建使用的基础镜像，相当于“打地基”，这里我们使用的是busybox。&lt;/p&gt;
&lt;p&gt;第二条指令是 CMD，它指定 docker run 启动容器时默认运行的命令，这里我们使用了echo命令，输出“hello world”字符串。&lt;/p&gt;
&lt;p&gt;现在有了Dockerfile这张“施工图纸”，我们就可以请出“施工队”了，用 docker build 命令来创建出镜像：&lt;/p&gt;
&lt;p&gt;你需要特别注意命令的格式，用 -f 参数指定Dockerfile文件名，后面必须跟一个文件路径，叫做“&lt;strong&gt;构建上下文&lt;/strong&gt;”（build’s context），这里只是一个简单的点号，表示当前路径的意思。&lt;/p&gt;
&lt;p&gt;接下来，你就会看到Docker会逐行地读取并执行Dockerfile里的指令，依次创建镜像层，再生成完整的镜像。&lt;/p&gt;
&lt;p&gt;新的镜像暂时还没有名字（用 docker images 会看到是 &amp;amp;lt;none&amp;amp;gt;），但我们可以直接使用“IMAGE ID”来查看或者运行：&lt;/p&gt;
&lt;h2 id="怎样编写正确-高效的dockerfile"&gt;怎样编写正确、高效的Dockerfile&lt;/h2&gt;
&lt;p&gt;大概了解了Dockerfile之后，我再来讲讲编写Dockerfile的一些常用指令和最佳实践，帮你在今后的工作中把它写好、用好。&lt;/p&gt;
&lt;p&gt;首先因为构建镜像的第一条指令必须是 FROM，所以基础镜像的选择非常关键。如果关注的是镜像的安全和大小，那么一般会选择Alpine；如果关注的是应用的运行稳定性，那么可能会选择Ubuntu、Debian、CentOS。&lt;/p&gt;
&lt;p&gt;我们在本机上开发测试时会产生一些源码、配置等文件，需要打包进镜像里，这时可以使用 COPY 命令，它的用法和Linux的cp差不多，不过拷贝的源文件必须是“&lt;strong&gt;构建上下文&lt;/strong&gt;”路径里的，不能随意指定文件。也就是说，如果要从本机向镜像拷贝文件，就必须把这些文件放到一个专门的目录，然后在 docker build 里指定“构建上下文”到这个目录才行。&lt;/p&gt;</description></item><item><title>05 镜像仓库：该怎样用好Docker Hub这个宝藏</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/007/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/007/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/05%20%e9%95%9c%e5%83%8f%e4%bb%93%e5%ba%93%ef%bc%9a%e8%af%a5%e6%80%8e%e6%a0%b7%e7%94%a8%e5%a5%bdDocker%20Hub%e8%bf%99%e4%b8%aa%e5%ae%9d%e8%97%8f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="05-镜像仓库-该怎样用好docker-hub这个宝藏"&gt;05 镜像仓库：该怎样用好Docker Hub这个宝藏&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;上一次课里我们学习了“Dockerfile”和“docker build”的用法，知道了如何创建自己的镜像。那么镜像文件应该如何管理呢，具体来说，应该如何存储、检索、分发、共享镜像呢？不解决这些问题，我们的容器化应用还是无法顺利地实施。&lt;/p&gt;
&lt;p&gt;今天，我就来谈一下这个话题，聊聊什么是镜像仓库，还有该怎么用好镜像仓库。&lt;/p&gt;
&lt;h2 id="什么是镜像仓库-registry"&gt;什么是镜像仓库（Registry）&lt;/h2&gt;
&lt;p&gt;之前我们已经用过 docker pull 命令拉取镜像，也说过有一个“镜像仓库”（Registry）的概念，那到底什么是镜像仓库呢？&lt;/p&gt;
&lt;p&gt;还是来看Docker的官方架构图（它真的非常重要）：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/e28f749056d8fd2dc828.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;图里右边的区域就是镜像仓库，术语叫Registry，直译就是“注册中心”，意思是所有镜像的Repository都在这里登记保管，就像是一个巨大的档案馆。&lt;/p&gt;
&lt;p&gt;然后我们再来看左边的“docker pull”，虚线显示了它的工作流程，先到“Docker daemon”，再到Registry，只有当Registry里存有镜像才能真正把它下载到本地。&lt;/p&gt;
&lt;p&gt;当然了，拉取镜像只是镜像仓库最基本的一个功能，它还会提供更多的功能，比如上传、查询、删除等等，是一个全面的镜像管理服务站点。&lt;/p&gt;
&lt;p&gt;你也可以把镜像仓库类比成手机上的应用商店，里面分门别类存放了许多容器化的应用，需要什么去找一下就行了。有了它，我们使用镜像才能够免除后顾之忧。&lt;/p&gt;
&lt;h2 id="什么是docker-hub"&gt;什么是Docker Hub&lt;/h2&gt;
&lt;p&gt;不过，你有没有注意到，在使用 docker pull 获取镜像的时候，我们并没有明确地指定镜像仓库。在这种情况下，Docker就会使用一个默认的镜像仓库，也就是大名鼎鼎的“&lt;strong&gt;Docker Hub&lt;/strong&gt;”（&lt;a href="https://hub.docker.com/" rel="nofollow noreferrer noopener"&gt;https://hub.docker.com/&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;Docker Hub是Docker公司搭建的官方Registry服务，创立于2014年6月，和Docker 1.0同时发布。它号称是世界上最大的镜像仓库，和GitHub一样，几乎成为了容器世界的基础设施。&lt;/p&gt;
&lt;p&gt;Docker Hub里面不仅有Docker自己打包的镜像，而且还对公众免费开放，任何人都可以上传自己的作品。经过这8年的发展，Docker Hub已经不再是一个单纯的镜像仓库了，更应该说是一个丰富而繁荣的容器社区。&lt;/p&gt;
&lt;p&gt;你可以看看下面的这张截图，里面列出的都是下载量超过10亿次（1 Billion）的最受欢迎的应用程序，比如Nginx、MongoDB、Node.js、Redis、OpenJDK等等。显然，把这些容器化的应用引入到我们自己的系统里，就像是站在了巨人的肩膀上，一开始就会有一个高水平的起点。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/5705caf42c78e3f17d7e.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;但和GitHub、App Store一样，面向所有人公开的Docker Hub也有一个不可避免的缺点，就是“良莠不齐”。&lt;/p&gt;
&lt;p&gt;在Docker Hub搜索框里输入关键字，比如Nginx、MySQL，它立即就会给出几百几千个搜索结果，有点“乱花迷人眼”的感觉，这么多镜像，应该如何挑选出最适合自己的呢？下面我就来说说自己在这方面的一些经验。&lt;/p&gt;
&lt;h2 id="如何在docker-hub上挑选镜像"&gt;如何在Docker Hub上挑选镜像&lt;/h2&gt;
&lt;p&gt;首先，你应该知道，在Docker Hub上有&lt;strong&gt;官方镜像&lt;/strong&gt;、&lt;strong&gt;认证镜像&lt;/strong&gt;和&lt;strong&gt;非官方镜像&lt;/strong&gt;的区别。&lt;/p&gt;
&lt;p&gt;官方镜像是指Docker公司官方提供的高质量镜像（&lt;a href="https://github.com/docker-library/official-images" rel="nofollow noreferrer noopener"&gt;https://github.com/docker-library/official-images&lt;/a&gt;），都经过了严格的漏洞扫描和安全检测，支持x86_64、arm64等多种硬件架构，还具有清晰易读的文档，一般来说是我们构建镜像的首选，也是我们编写Dockerfile的最佳范例。&lt;/p&gt;
&lt;p&gt;官方镜像目前有大约100多个，基本上囊括了现在的各种流行技术，下面就是官方的Nginx镜像网页截图：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/aa47698207b8e94960b0.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;你会看到，官方镜像会有一个特殊的“&lt;strong&gt;Official image&lt;/strong&gt;”的标记，这就表示这个镜像经过了Docker公司的认证，有专门的团队负责审核、发布和更新，质量上绝对可以放心。&lt;/p&gt;
&lt;p&gt;第二类是认证镜像，标记是“&lt;strong&gt;Verified publisher&lt;/strong&gt;”，也就是认证发行商，比如Bitnami、Rancher、Ubuntu等。它们都是颇具规模的大公司，具有不逊于Docker公司的实力，所以就在Docker Hub上开了个认证账号，发布自己打包的镜像，有点类似我们微博上的“大V”。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/b919be43bed31bf6456c.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;这些镜像有公司背书，当然也很值得信赖，不过它们难免会带上一些各自公司的“烙印”，比如Bitnami的镜像就统一以“minideb”为基础，灵活性上比Docker官方镜像略差，有的时候也许会不符合我们的需求。&lt;/p&gt;
&lt;p&gt;除了官方镜像和认证镜像，剩下的就都属于非官方镜像了，不过这里面也可以分出两类。&lt;/p&gt;
&lt;p&gt;第一类是“&lt;strong&gt;半官方&lt;/strong&gt;”镜像。因为成为“Verified publisher”是要给Docker公司交钱的，而很多公司不想花这笔“冤枉钱”，所以只在Docker Hub上开了公司账号，但并不加入认证。&lt;/p&gt;
&lt;p&gt;这里我以OpenResty为例，看一下它的Docker Hub页面，可以看到显示的是OpenResty官方发布，但并没有经过Docker正式认证，所以难免就会存在一些风险，有被“冒名顶替”的可能，需要我们在使用的时候留心鉴别一下。不过一般来说，这种“半官方”镜像也是比较可靠的。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/c1311167b628e381d265.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;第二类就是纯粹的“&lt;strong&gt;民间&lt;/strong&gt;”镜像了，通常是个人上传到Docker Hub的，因为条件所限，测试不完全甚至没有测试，质量上难以得到保证，下载的时候需要小心谨慎。&lt;/p&gt;</description></item><item><title>06 打破次元壁：容器该如何与外界互联互通</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/008/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/008/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/06%20%e6%89%93%e7%a0%b4%e6%ac%a1%e5%85%83%e5%a3%81%ef%bc%9a%e5%ae%b9%e5%99%a8%e8%af%a5%e5%a6%82%e4%bd%95%e4%b8%8e%e5%a4%96%e7%95%8c%e4%ba%92%e8%81%94%e4%ba%92%e9%80%9a.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="06-打破次元壁-容器该如何与外界互联互通"&gt;06 打破次元壁：容器该如何与外界互联互通&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在前面的几节课里，我们已经学习了容器、镜像、镜像仓库的概念和用法，也知道了应该如何创建镜像，再以容器的形式启动应用。&lt;/p&gt;
&lt;p&gt;不过，用容器来运行“busybox”“hello world”这样比较简单的应用还好，如果是Nginx、Redis、MySQL这样的后台服务应用，因为它们运行在容器的“沙盒”里，完全与外界隔离，无法对外提供服务，也就失去了价值。这个时候，容器的隔离环境反而成为了一种负面特性。&lt;/p&gt;
&lt;p&gt;所以，容器的这个“小板房”不应该是一个完全密闭的铁屋子，而是应该给它开几扇门窗，让应用在“足不出户”的情况下，也能够与外界交换数据、互通有无，这样“有限的隔离”才是我们真正所需要的运行环境。&lt;/p&gt;
&lt;p&gt;那么今天，我就以Docker为例，来讲讲有哪些手段能够在容器与外部系统之间沟通交流。&lt;/p&gt;
&lt;h2 id="如何拷贝容器内的数据"&gt;如何拷贝容器内的数据&lt;/h2&gt;
&lt;p&gt;我们首先来看看Docker提供的 cp 命令，它可以在宿主机和容器之间拷贝文件，是最基本的一种数据交换功能。&lt;/p&gt;
&lt;p&gt;试验这个命令需要先用 docker run 启动一个容器，就用Redis吧：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/77bb180e2bd124e367a7.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;注意这里使用了 -d、–rm 两个参数，表示运行在后台，容器结束后自动删除，然后使用 docker ps 命令可以看到Redis容器正在运行，容器ID是“062”。&lt;/p&gt;
&lt;p&gt;docker cp 的用法很简单，很类似Linux的“cp”“scp”，指定源路径（src path）和目标路径（dest path）就可以了。如果源路径是宿主机那么就是把文件拷贝进容器，如果源路径是容器那么就是把文件拷贝出容器，注意需要用&lt;strong&gt;容器名或者容器ID&lt;/strong&gt;来指明是哪个容器的路径。&lt;/p&gt;
&lt;p&gt;假设当前目录下有一个“a.txt”的文件，现在我们要把它拷贝进Redis容器的“/tmp”目录，如果使用容器ID，命令就会是这样：&lt;/p&gt;
&lt;p&gt;接下来我们可以使用 docker exec 命令，进入容器看看文件是否已经正确拷贝了：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/f46e8f7e7da08693eb4c.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;可以看到，在“/tmp”目录下，确实已经有了一个“a.txt”。&lt;/p&gt;
&lt;p&gt;现在让我们再来试验一下从容器拷贝出文件，只需要把 docker cp 后面的两个路径调换一下位置：&lt;/p&gt;
&lt;p&gt;这样，在宿主机的当前目录里，就会多出一个新的“b.txt”，也就是从容器里拿到的文件。&lt;/p&gt;
&lt;h2 id="如何共享主机上的文件"&gt;如何共享主机上的文件&lt;/h2&gt;
&lt;p&gt;docker cp 的用法模仿了操作系统的拷贝命令，偶尔一两次的文件共享还可以应付，如果容器运行时经常有文件来往互通，这样反复地拷来拷去就显得很麻烦，也很容易出错。&lt;/p&gt;
&lt;p&gt;你也许会联想到虚拟机有一种“共享目录”的功能。它可以在宿主机上开一个目录，然后把这个目录“挂载”进虚拟机，这样就实现了两者共享同一个目录，一边对目录里文件的操作另一边立刻就能看到，没有了数据拷贝，效率自然也会高很多。&lt;/p&gt;
&lt;p&gt;沿用这个思路，容器也提供了这样的共享宿主机目录的功能，效果也和虚拟机几乎一样，用起来很方便，只需要在 docker run 命令启动容器的时候使用 -v 参数就行，具体的格式是“&lt;strong&gt;宿主机路径:容器内路径&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;我还是以Redis为例，启动容器，使用 -v 参数把本机的“/tmp”目录挂载到容器里的“/tmp”目录，也就是说让容器共享宿主机的“/tmp”目录：&lt;/p&gt;
&lt;p&gt;然后我们再用 docker exec 进入容器，查看一下容器内的“/tmp”目录，应该就可以看到文件与宿主机是完全一致的。&lt;/p&gt;
&lt;p&gt;你也可以在容器里的“/tmp”目录下随便做一些操作，比如删除文件、建立新目录等等，再回头观察一下宿主机，会发现修改会即时同步，这就表明容器和宿主机确实已经共享了这个目录。&lt;/p&gt;
&lt;p&gt;-v 参数挂载宿主机目录的这个功能，对于我们日常开发测试工作来说非常有用，我们可以在不变动本机环境的前提下，使用镜像安装任意的应用，然后直接以容器来运行我们本地的源码、脚本，非常方便。&lt;/p&gt;
&lt;p&gt;这里我举一个简单的例子。比如我本机上只有Python 2.7，但我想用Python 3开发，如果同时安装Python 2和Python 3很容易就会把系统搞乱，所以我就可以这么做：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;先使用 docker pull 拉取一个Python 3的镜像，因为它打包了完整的运行环境，运行时有隔离，所以不会对现有系统的Python 2.7产生任何影响。&lt;/p&gt;</description></item><item><title>07 实战演练：玩转Docker</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/009/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/009/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/07%20%e5%ae%9e%e6%88%98%e6%bc%94%e7%bb%83%ef%bc%9a%e7%8e%a9%e8%bd%acDocker.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="07-实战演练-玩转docker"&gt;07 实战演练：玩转Docker&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;学到今天的这次课，我们的“入门篇”就算是告一段落了，有这些容器知识作为基础，很快我们就要正式开始学习Kubernetes。不过在那之前，来对前面的课程做一个回顾和实践，把基础再夯实一下。&lt;/p&gt;
&lt;p&gt;要提醒你的是，Docker相关的内容很多很广，在入门篇中，我只从中挑选出了一些最基本最有用的介绍给你。而且在我看来，我们不需要完全了解Docker的所有功能，我也不建议你对Docker的内部架构细节和具体的命令行参数做过多的了解，太浪费精力，只要会用够用，需要的时候能够查找官方手册就行。&lt;/p&gt;
&lt;p&gt;毕竟我们这门课程的目标是Kubernetes，而Docker只不过是众多容器运行时（Container Runtime）中最出名的一款而已。当然，如果你当前的工作是与Docker深度绑定，那就另当别论了。&lt;/p&gt;
&lt;p&gt;好下面我先把容器技术做一个简要的总结，然后演示两个实战项目：使用Docker部署Registry和WordPress。&lt;/p&gt;
&lt;h2 id="容器技术要点回顾"&gt;容器技术要点回顾&lt;/h2&gt;
&lt;p&gt;容器技术是后端应用领域的一项重大创新，它彻底变革了应用的开发、交付与部署方式，是“云原生”的根本（[01讲]）。&lt;/p&gt;
&lt;p&gt;容器基于Linux底层的namespace、cgroup、chroot等功能，虽然它们很早就出现了，但直到Docker“横空出世”，把它们整合在一起，容器才真正走近了大众的视野，逐渐为广大开发者所熟知（[02讲]）。&lt;/p&gt;
&lt;p&gt;容器技术中有三个核心概念：&lt;strong&gt;容器（Container）&lt;/strong&gt;、&lt;strong&gt;镜像（Image）&lt;/strong&gt;，以及&lt;strong&gt;镜像仓库（Registry）&lt;/strong&gt;（[03讲]）。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/e28f749056d8fd2dc828.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;从本质上来说，容器属于虚拟化技术的一种，和虚拟机（Virtual Machine）很类似，都能够分拆系统资源，隔离应用进程，但容器更加轻量级，运行效率更高，比虚拟机更适合云计算的需求。&lt;/p&gt;
&lt;p&gt;镜像是容器的静态形式，它把应用程序连同依赖的操作系统、配置文件、环境变量等等都打包到了一起，因而能够在任何系统上运行，免除了很多部署运维和平台迁移的麻烦。&lt;/p&gt;
&lt;p&gt;镜像内部由多个层（Layer）组成，每一层都是一组文件，多个层会使用Union FS技术合并成一个文件系统供容器使用。这种细粒度结构的好处是相同的层可以共享、复用，节约磁盘存储和网络传输的成本，也让构建镜像的工作变得更加容易（[04讲]）。&lt;/p&gt;
&lt;p&gt;为了方便管理镜像，就出现了镜像仓库，它集中存放各种容器化的应用，用户可以任意上传下载，是分发镜像的最佳方式（[05讲]）。&lt;/p&gt;
&lt;p&gt;目前最知名的公开镜像仓库是Docker Hub，其他的还有quay.io、gcr.io，我们可以在这些网站上找到许多高质量镜像，集成到我们自己的应用系统中。&lt;/p&gt;
&lt;p&gt;容器技术有很多具体的实现，Docker是最初也是最流行的容器技术，它的主要形态是运行在Linux上的“Docker Engine”。我们日常使用的 docker 命令其实只是一个前端工具，它必须与后台服务“Docker daemon”通信才能实现各种功能。&lt;/p&gt;
&lt;p&gt;操作容器的常用命令有 docker ps、docker run、docker exec、docker stop 等；操作镜像的常用命令有 docker images、docker rmi、docker build、docker tag 等；操作镜像仓库的常用命令有 docker pull、docker push 等。&lt;/p&gt;
&lt;p&gt;好简单地回顾了容器技术，下面我们就来综合运用在“入门篇”所学到的各个知识点，开始实战演练，玩转Docker。&lt;/p&gt;
&lt;h2 id="搭建私有镜像仓库"&gt;搭建私有镜像仓库&lt;/h2&gt;
&lt;p&gt;在第5节课讲Docker Hub的时候曾经说过，在离线环境里，我们可以自己搭建私有仓库。但因为镜像仓库是网络服务的形式，当时还没有学到容器网络相关的知识，所以只有到了现在，我们具备了比较完整的Docker知识体系，才能够搭建私有仓库。&lt;/p&gt;
&lt;p&gt;私有镜像仓库有很多现成的解决方案，今天我只选择最简单的Docker Registry，而功能更完善的CNCF Harbor留到后续学习Kubernetes时再介绍。&lt;/p&gt;
&lt;p&gt;你可以在Docker Hub网站上搜索“registry”，找到它的官方页面（&lt;a href="https://registry.hub.docker.com/_/registry/" rel="nofollow noreferrer noopener"&gt;https://registry.hub.docker.com/_/registry/&lt;/a&gt;）：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/cb0989f90cd9fe8f623b.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;Docker Registry的网页上有很详细的说明，包括下载命令、用法等，我们可以完全照着它来操作。&lt;/p&gt;
&lt;p&gt;首先，你需要使用 docker pull 命令拉取镜像：&lt;/p&gt;
&lt;p&gt;然后，我们需要做一个端口映射，对外暴露端口，这样Docker Registry才能提供服务。它的容器内端口是5000，简单起见，我们在外面也使用同样的5000端口，所以运行命令就是 docker run -d -p 5000:5000 registry ：&lt;/p&gt;</description></item><item><title>08 视频：入门篇实操总结</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/010/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/010/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/08%20%e8%a7%86%e9%a2%91%ef%bc%9a%e5%85%a5%e9%97%a8%e7%af%87%e5%ae%9e%e6%93%8d%e6%80%bb%e7%bb%93.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="08-视频-入门篇实操总结"&gt;08 视频：入门篇实操总结&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;今天的课程和前面的不太一样，变成了视频的形式。之前也讲过很多次学习Kubernetes要以动手实操为主，加上专栏里单纯的文字配图的形式还是不太直观，所以每到一个学习阶段，我们就会来一个视频总结，把之前学习的内容以视频的形式展现出来，这样也许会让学习的效果更好。&lt;/p&gt;
&lt;p&gt;这次视频课程的主要内容和第7讲差不多，是对“入门篇”的回顾与总结，但侧重点是对Docker的实际操作，不会再重复讲那些理论知识。每个视频后都会附上操作要点，供你快速定位和做笔记。&lt;/p&gt;
&lt;p&gt;好了，我们正式开始吧。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一-熟悉docker的使用"&gt;一. 熟悉Docker的使用&lt;/h2&gt;
&lt;p&gt;视频操作要点：&lt;/p&gt;
&lt;p&gt;首先来操作一下Docker Engine。&lt;/p&gt;
&lt;p&gt;（有了[课前准备]的基础）在这台机器上，Docker已经安装好了，我给你用 docker version 和 docker info 看一下它的信息。&lt;/p&gt;
&lt;p&gt;docker version 显示的是Docker Engine 20.10.12，系统是Linux，硬件架构是arm64，也就是Apple M1。&lt;/p&gt;
&lt;p&gt;docker info 显示的是当前系统相关的信息，例如CPU、内存、容器数量、镜像数量、容器运行时、存储文件系统等等。这里存储用的文件系统是overlay2，Linux内核是5.13，操作系统是Ubuntu 22.04 Jammy Jellyfish，硬件是aarch64，两个CPU，内存4G。&lt;/p&gt;
&lt;p&gt;现在我们用 docker ps 看一下容器列表，应该是空的。&lt;/p&gt;
&lt;p&gt;然后用 docker pull 拉取busybox镜像，再用 docker images 看镜像列表。&lt;/p&gt;
&lt;p&gt;使用 docker run 启动busybox镜像，执行最简单的hello world：&lt;/p&gt;
&lt;p&gt;然后再用 docker ps -a 查看已经结束的容器列表，应该可以看到刚刚运行完毕的Busybox容器，可以用 docker rm 再加上容器ID删除它。&lt;/p&gt;
&lt;h2 id="二-镜像和容器"&gt;二. 镜像和容器&lt;/h2&gt;
&lt;p&gt;视频操作要点：&lt;/p&gt;
&lt;p&gt;我们再来拉取另一个镜像，操作系统Alpine：&lt;/p&gt;
&lt;p&gt;然后用 docker run，加上it参数，运行它里面的shell：&lt;/p&gt;
&lt;p&gt;这样就暂时离开当前的Ubuntu操作系统，进入了容器内部的Alpine系统，可以在里面执行任意的命令，比如 cat /etc/os-release 。&lt;/p&gt;
&lt;p&gt;这个容器环境与外面是完全隔离的，进程、文件系统都独立，不过也有没有隔离的部分，比如时间和内核。&lt;/p&gt;
&lt;p&gt;使用exit退出容器，然后在宿主机环境执行date、uname -a，你就可以看到它与容器里是一致的。&lt;/p&gt;
&lt;p&gt;让我们再运行一个容器：&lt;/p&gt;</description></item><item><title>09 走近云原生：如何在本机搭建小巧完备的Kubernetes环境</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/011/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/011/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/09%20%e8%b5%b0%e8%bf%91%e4%ba%91%e5%8e%9f%e7%94%9f%ef%bc%9a%e5%a6%82%e4%bd%95%e5%9c%a8%e6%9c%ac%e6%9c%ba%e6%90%ad%e5%bb%ba%e5%b0%8f%e5%b7%a7%e5%ae%8c%e5%a4%87%e7%9a%84Kubernetes%e7%8e%af%e5%a2%83.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="09-走近云原生-如何在本机搭建小巧完备的kubernetes环境"&gt;09 走近云原生：如何在本机搭建小巧完备的Kubernetes环境&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在前面的“入门篇”里，我们学习了以Docker为代表的容器技术，做好了充分的准备，那么今天我们就来看看什么是容器编排、什么是Kubernetes，还有应该怎么在自己的电脑上搭建出一个小巧完善的Kubernetes环境，一起走近云原生。&lt;/p&gt;
&lt;h2 id="什么是容器编排"&gt;什么是容器编排&lt;/h2&gt;
&lt;p&gt;容器技术的核心概念是容器、镜像、仓库，使用这三大基本要素我们就可以轻松地完成应用的打包、分发工作，实现“一次开发，到处运行”的梦想。&lt;/p&gt;
&lt;p&gt;不过，当我们熟练地掌握了容器技术，信心满满地要在服务器集群里大规模实施的时候，却会发现容器技术的创新只是解决了运维部署工作中一个很小的问题。现实生产环境的复杂程度实在是太高了，除了最基本的安装，还会有各式各样的需求，比如服务发现、负载均衡、状态监控、健康检查、扩容缩容、应用迁移、高可用等等。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/552c5f8d95774a31ee28.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;虽然容器技术开启了云原生时代，但它也只走出了一小步，再继续前进就无能为力了，因为这已经不再是隔离一两个进程的普通问题，而是要隔离数不清的进程，还有它们之间互相通信、互相协作的超级问题，困难程度可以说是指数级别的上升。&lt;/p&gt;
&lt;p&gt;这些容器之上的管理、调度工作，就是这些年最流行的词汇：“&lt;strong&gt;容器编排&lt;/strong&gt;”（Container Orchestration）。&lt;/p&gt;
&lt;p&gt;容器编排这个词听起来好像挺高大上，但如果你理解了之后就会发现其实也并不神秘。像我们在上次课里使用Docker部署WordPress网站的时候，把Nginx、WordPress、MariaDB这三个容器理清次序、配好IP地址去运行，就是最初级的一种“容器编排”，只不过这是纯手工操作，比较原始、粗糙。&lt;/p&gt;
&lt;p&gt;面对单机上的几个容器，“人肉”编排调度还可以应付，但如果规模上到几百台服务器、成千上万的容器，处理它们之间的复杂联系就必须要依靠计算机了，而目前计算机用来调度管理的“事实标准”，就是我们专栏的主角：Kubernetes。&lt;/p&gt;
&lt;h2 id="什么是kubernetes"&gt;什么是Kubernetes&lt;/h2&gt;
&lt;p&gt;现在大家谈到容器都会说是Docker，但其实早在Docker之前，Google在公司内部就使用了类似的技术（cgroup就是Google开发再提交给Linux内核的），只不过不叫容器。&lt;/p&gt;
&lt;p&gt;作为世界上最大的搜索引擎，Google拥有数量庞大的服务器集群，为了提高资源利用率和部署运维效率，它专门开发了一个集群应用管理系统，代号Borg，在底层支持整个公司的运转。&lt;/p&gt;
&lt;p&gt;2014年，Google内部系统要“升级换代”，从原来的Borg切换到Omega，于是按照惯例，Google会发表公开论文。&lt;/p&gt;
&lt;p&gt;因为之前在发表MapReduce、BigTable、GFS时吃过亏（被Yahoo开发的Hadoop占领了市场），所以Google决定借着Docker的“东风”，在发论文的同时，把C++开发的Borg系统用Go语言重写并开源，于是Kubernetes就这样诞生了。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/3ff3dde2a89561405c12.webp" alt="image"&gt;&lt;/p&gt;
&lt;p&gt;由于Kubernetes背后有Borg系统十多年生产环境经验的支持，技术底蕴深厚，理论水平也非常高，一经推出就引起了轰动。然后在2015年，Google又联合Linux基金会成立了CNCF（Cloud Native Computing Foundation，云原生基金会），并把Kubernetes捐献出来作为种子项目。&lt;/p&gt;
&lt;p&gt;有了Google和Linux这两大家族的保驾护航，再加上宽容开放的社区，作为CNCF的“头把交椅”，Kubernetes旗下很快就汇集了众多行业精英，仅用了两年的时间就打败了同期的竞争对手Apache Mesos和Docker Swarm，成为了这个领域的唯一霸主。&lt;/p&gt;
&lt;p&gt;那么，Kubernetes到底能够为我们做什么呢？&lt;/p&gt;
&lt;p&gt;简单来说，Kubernetes就是一个&lt;strong&gt;生产级别的容器编排平台和集群管理系统&lt;/strong&gt;，不仅能够创建、调度容器，还能够监控、管理服务器，它凝聚了Google等大公司和开源社区的集体智慧，从而让中小型公司也可以具备轻松运维海量计算节点——也就是“云计算”的能力。&lt;/p&gt;
&lt;h2 id="什么是minikube"&gt;什么是minikube&lt;/h2&gt;
&lt;p&gt;Kubernetes一般都运行在大规模的计算集群上，管理很严格，这就对我们个人来说造成了一定的障碍，没有实际操作环境怎么能够学好用好呢？&lt;/p&gt;
&lt;p&gt;好在Kubernetes充分考虑到了这方面的需求，提供了一些快速搭建Kubernetes环境的工具，在官网（&lt;a href="https://kubernetes.io/zh/docs/tasks/tools/" rel="nofollow noreferrer noopener"&gt;https://kubernetes.io/zh/docs/tasks/tools/&lt;/a&gt;）上推荐的有两个：&lt;strong&gt;kind&lt;/strong&gt;和&lt;strong&gt;minikube&lt;/strong&gt;，它们都可以在本机上运行完整的Kubernetes环境。&lt;/p&gt;
&lt;p&gt;我说一下对这两个工具的个人看法，供你参考。&lt;/p&gt;
&lt;p&gt;kind基于Docker，意思是“Kubernetes in Docker”。它功能少，用法简单，也因此运行速度快，容易上手。不过它缺少很多Kubernetes的标准功能，例如仪表盘、网络插件，也很难定制化，所以我认为它比较适合有经验的Kubernetes用户做快速开发测试，不太适合学习研究。&lt;/p&gt;
&lt;p&gt;不选kind还有一个原因，它的名字与Kubernetes YAML配置里的字段 kind 重名，会对初学者造成误解，干扰学习。&lt;/p&gt;
&lt;p&gt;再来看minikube，从名字就能够看出来，它是一个“迷你”版本的Kubernetes，自从2016年发布以来一直在积极地开发维护，紧跟Kubernetes的版本更新，同时也兼容较旧的版本（最多只到之前的6个小版本）。&lt;/p&gt;
&lt;p&gt;minikube最大特点就是“小而美”，可执行文件仅有不到100MB，运行镜像也不过1GB，但就在这么小的空间里却集成了Kubernetes的绝大多数功能特性，不仅有核心的容器编排功能，还有丰富的插件，例如Dashboard、GPU、Ingress、Istio、Kong、Registry等等，综合来看非常完善。&lt;/p&gt;
&lt;p&gt;所以，我建议你在这个专栏里选择minikube来学习Kubernetes。&lt;/p&gt;
&lt;h2 id="如何搭建minikube环境"&gt;如何搭建minikube环境&lt;/h2&gt;
&lt;p&gt;minikube支持Mac、Windows、Linux这三种主流平台，你可以在它的官网（&lt;a href="https://minikube.sigs.k8s.io/docs" rel="nofollow noreferrer noopener"&gt;https://minikube.sigs.k8s.io&lt;/a&gt;）找到详细的安装说明，当然在我们这里就只用虚拟机里的Linux了。&lt;/p&gt;
&lt;p&gt;minikube的最新版本是1.25.2，支持的Kubernetes版本是1.23.3，所以我们就选定它作为我们初级篇的学习工具。&lt;/p&gt;
&lt;p&gt;minikube不包含在系统自带的apt/yum软件仓库里，我们只能自己去网上找安装包。不过因为它是用Go语言开发的，整体就是一个二进制文件，没有多余的依赖，所以安装过程也非常简单，只需要用curl或者wget下载就行。&lt;/p&gt;
&lt;p&gt;minikube的官网提供了各种系统的安装命令，通常就是下载、拷贝这两步，不过你需要注意一下本机电脑的硬件架构，Intel芯片要选择带“&lt;strong&gt;amd64&lt;/strong&gt;”后缀，Apple M1芯片要选择“&lt;strong&gt;arm64&lt;/strong&gt;”后缀，选错了就会因为CPU指令集不同而无法运行：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/aa1e25b286ff7ce81770.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;我也把官网上Linux系统安装的命令抄在了这里，你可以直接拷贝后安装：&lt;/p&gt;
&lt;p&gt;安装完成之后，你可以执行命令 minikube version，看看它的版本号，验证是否安装成功：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/3840ec5412d7390ea4df.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;不过minikube只能够搭建Kubernetes环境，要操作Kubernetes，还需要另一个专门的客户端工具“&lt;strong&gt;kubectl&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;kubectl的作用有点类似之前我们学习容器技术时候的工具“docker”，它也是一个命令行工具，作用也比较类似，同样是与Kubernetes后台服务通信，把我们的命令转发给Kubernetes，实现容器和集群的管理功能。&lt;/p&gt;
&lt;p&gt;kubectl是一个与Kubernetes、minikube彼此独立的项目，所以不包含在minikube里，但minikube提供了安装它的简化方式，你只需执行下面的这条命令：&lt;/p&gt;
&lt;p&gt;它就会把与当前Kubernetes版本匹配的kubectl下载下来，存放在内部目录（例如 .minikube/cache/linux/arm64/v1.23.3），然后我们就可以使用它来对Kubernetes“发号施令”了。&lt;/p&gt;
&lt;p&gt;所以，在minikube环境里，我们会用到两个客户端：minikube管理Kubernetes集群环境，kubectl操作实际的Kubernetes功能，和Docker比起来有点复杂。&lt;/p&gt;
&lt;p&gt;我画了一个简单的minikube环境示意图，方便你理解它们的关系。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/6bb3c0f1b88a22dba4a9.webp" alt="图片"&gt;&lt;/p&gt;
&lt;h2 id="实际验证minikube环境"&gt;实际验证minikube环境&lt;/h2&gt;
&lt;p&gt;前面的工作都做完之后，我们就可以在本机上运行minikube，创建Kubernetes实验环境了。&lt;/p&gt;</description></item><item><title>10 自动化的运维管理：探究Kubernetes工作机制的奥秘</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/012/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/012/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/10%20%e8%87%aa%e5%8a%a8%e5%8c%96%e7%9a%84%e8%bf%90%e7%bb%b4%e7%ae%a1%e7%90%86%ef%bc%9a%e6%8e%a2%e7%a9%b6Kubernetes%e5%b7%a5%e4%bd%9c%e6%9c%ba%e5%88%b6%e7%9a%84%e5%a5%a5%e7%a7%98.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="10-自动化的运维管理-探究kubernetes工作机制的奥秘"&gt;10 自动化的运维管理：探究Kubernetes工作机制的奥秘&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在上一次课里，我们看到容器技术只实现了应用的打包分发，到运维真正落地实施的时候仍然会遇到很多困难，所以就需要用容器编排技术来解决这些问题，而Kubernetes是这个领域的唯一霸主，已经成为了“事实标准”。&lt;/p&gt;
&lt;p&gt;那么，Kubernetes凭什么能担当这样的领军重任呢？难道仅仅因为它是由Google主导开发的吗？&lt;/p&gt;
&lt;p&gt;今天我就带你一起来看看Kubernetes的内部架构和工作机制，了解它能够傲视群雄的秘密所在。&lt;/p&gt;
&lt;h2 id="云计算时代的操作系统"&gt;云计算时代的操作系统&lt;/h2&gt;
&lt;p&gt;前面我曾经说过，Kubernetes是&lt;strong&gt;一个生产级别的容器编排平台和集群管理系统&lt;/strong&gt;，能够创建、调度容器，监控、管理服务器。&lt;/p&gt;
&lt;p&gt;容器是什么？容器是软件，是应用，是进程。服务器是什么？服务器是硬件，是CPU、内存、硬盘、网卡。那么，既可以管理软件，也可以管理硬件，这样的东西应该是什么？&lt;/p&gt;
&lt;p&gt;你也许会脱口而出：这就是一个操作系统（Operating System）！&lt;/p&gt;
&lt;p&gt;没错，从某种角度来看，Kubernetes可以说是一个集群级别的操作系统，主要功能就是资源管理和作业调度。但Kubernetes不是运行在单机上管理单台计算资源和进程，而是运行在多台服务器上管理几百几千台的计算资源，以及在这些资源上运行的上万上百万的进程，规模要大得多。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/b4f4582309990d49bbd5.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;所以，你可以把Kubernetes与Linux对比起来学习，而这个新的操作系统里自然会有一系列新名词、新术语，你也需要使用新的思维方式来考虑问题，必要的时候还得和过去的习惯“说再见”。&lt;/p&gt;
&lt;p&gt;Kubernetes这个操作系统与Linux还有一点区别你值得注意。Linux的用户通常是两类人：&lt;strong&gt;Dev&lt;/strong&gt;和&lt;strong&gt;Ops&lt;/strong&gt;，而在Kubernetes里则只有一类人：&lt;strong&gt;DevOps&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在以前的应用实施流程中，开发人员和运维人员分工明确，开发完成后需要编写详细的说明文档，然后交给运维去部署管理，两者之间不能随便“越线”。&lt;/p&gt;
&lt;p&gt;而在Kubernetes这里，开发和运维的界限变得不那么清晰了。由于云原生的兴起，开发人员从一开始就必须考虑后续的部署运维工作，而运维人员也需要在早期介入开发，才能做好应用的运维监控工作。&lt;/p&gt;
&lt;p&gt;这就会导致很多Kubernetes的新用户会面临身份的转变，一开始可能会有点困难。不过不用担心，这也非常正常，任何的学习过程都有个适应期，只要过了最初的概念理解阶段就好了。&lt;/p&gt;
&lt;h2 id="kubernetes的基本架构"&gt;Kubernetes的基本架构&lt;/h2&gt;
&lt;p&gt;操作系统的一个重要功能就是抽象，从繁琐的底层事务中抽象出一些简洁的概念，然后基于这些概念去管理系统资源。&lt;/p&gt;
&lt;p&gt;Kubernetes也是这样，它的管理目标是大规模的集群和应用，必须要能够把系统抽象到足够高的层次，分解出一些松耦合的对象，才能简化系统模型，减轻用户的心智负担。&lt;/p&gt;
&lt;p&gt;所以，Kubernetes扮演的角色就如同一个“大师级别”的系统管理员，具有丰富的集群运维经验，独创了自己的一套工作方式，不需要太多的外部干预，就能够自主实现原先许多复杂的管理工作。&lt;/p&gt;
&lt;p&gt;下面我们就来看看这位资深管理员的“内功心法”。&lt;/p&gt;
&lt;p&gt;Kubernetes官网上有一张架构图，但我觉得不是太清晰、重点不突出，所以另外找了一份（&lt;a href="https://medium.com/@keshiha/k8s-architecture-bb6964767c12" rel="nofollow noreferrer noopener"&gt;图片来源&lt;/a&gt;）。虽然这张图有点“老”，但对于我们初学Kubernetes还是比较合适的。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/27a1b73d6beed9c232b1.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;Kubernetes采用了现今流行的“&lt;strong&gt;控制面/数据面&lt;/strong&gt;”（Control Plane / Data Plane）架构，集群里的计算机被称为“&lt;strong&gt;节点&lt;/strong&gt;”（Node），可以是实机也可以是虚机，少量的节点用作控制面来执行集群的管理维护工作，其他的大部分节点都被划归数据面，用来跑业务应用。&lt;/p&gt;
&lt;p&gt;控制面的节点在Kubernetes里叫做&lt;strong&gt;Master Node&lt;/strong&gt;，一般简称为&lt;strong&gt;Master&lt;/strong&gt;，它是整个集群里最重要的部分，可以说是Kubernetes的大脑和心脏。&lt;/p&gt;
&lt;p&gt;数据面的节点叫做&lt;strong&gt;Worker Node&lt;/strong&gt;，一般就简称为&lt;strong&gt;Worker&lt;/strong&gt;或者&lt;strong&gt;Node&lt;/strong&gt;，相当于Kubernetes的手和脚，在Master的指挥下干活。&lt;/p&gt;
&lt;p&gt;Node的数量非常多，构成了一个资源池，Kubernetes就在这个池里分配资源，调度应用。因为资源被“池化”了，所以管理也就变得比较简单，可以在集群中任意添加或者删除节点。&lt;/p&gt;
&lt;p&gt;在这张架构图里，我们还可以看到有一个kubectl，它就是Kubernetes的客户端工具，用来操作Kubernetes，但它位于集群之外，理论上不属于集群。&lt;/p&gt;
&lt;p&gt;你可以使用命令 kubectl get node 来查看Kubernetes的节点状态：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/05c214735cc6afe17b9d.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;可以看到当前的minikube集群里只有一个Master，那Node怎么不见了？&lt;/p&gt;
&lt;p&gt;这是因为Master和Node的划分不是绝对的。当集群的规模较小，工作负载较少的时候，Master也可以承担Node的工作，就像我们搭建的minikube环境，它就只有一个节点，这个节点既是Master又是Node。&lt;/p&gt;
&lt;h2 id="节点内部的结构"&gt;节点内部的结构&lt;/h2&gt;
&lt;p&gt;Kubernetes的节点内部也具有复杂的结构，是由很多的模块构成的，这些模块又可以分成组件（Component）和插件（Addon）两类。&lt;/p&gt;
&lt;p&gt;组件实现了Kubernetes的核心功能特性，没有这些组件Kubernetes就无法启动，而插件则是Kubernetes的一些附加功能，属于“锦上添花”，不安装也不会影响Kubernetes的正常运行。&lt;/p&gt;
&lt;p&gt;接下来我先来讲讲Master和Node里的组件，然后再捎带提一下插件，理解了它们的工作流程，你就会明白为什么Kubernetes有如此强大的自动化运维能力。&lt;/p&gt;
&lt;h3 id="master里的组件有哪些"&gt;Master里的组件有哪些&lt;/h3&gt;
&lt;p&gt;Master里有4个组件，分别是&lt;strong&gt;apiserver&lt;/strong&gt;、&lt;strong&gt;etcd&lt;/strong&gt;、&lt;strong&gt;scheduler&lt;/strong&gt;、&lt;strong&gt;controller-manager&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/c15ba32d1a91ebb92baf.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;apiserver是Master节点——同时也是整个Kubernetes系统的唯一入口，它对外公开了一系列的RESTful API，并且加上了验证、授权等功能，所有其他组件都只能和它直接通信，可以说是Kubernetes里的联络员。&lt;/p&gt;
&lt;p&gt;etcd是一个高可用的分布式Key-Value数据库，用来持久化存储系统里的各种资源对象和状态，相当于Kubernetes里的配置管理员。注意它只与apiserver有直接联系，也就是说任何其他组件想要读写etcd里的数据都必须经过apiserver。&lt;/p&gt;
&lt;p&gt;scheduler负责容器的编排工作，检查节点的资源状态，把Pod调度到最适合的节点上运行，相当于部署人员。因为节点状态和Pod信息都存储在etcd里，所以scheduler必须通过apiserver才能获得。&lt;/p&gt;
&lt;p&gt;controller-manager负责维护容器和节点等资源的状态，实现故障检测、服务迁移、应用伸缩等功能，相当于监控运维人员。同样地，它也必须通过apiserver获得存储在etcd里的信息，才能够实现对资源的各种操作。&lt;/p&gt;
&lt;p&gt;这4个组件也都被容器化了，运行在集群的Pod里，我们可以用kubectl来查看它们的状态，使用命令：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/1642e64a9786a9d60d73.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;注意命令行里要用 -n kube-system 参数，表示检查“kube-system”名字空间里的Pod，至于名字空间是什么，我们后面会讲到。&lt;/p&gt;
&lt;h3 id="node里的组件有哪些"&gt;Node里的组件有哪些&lt;/h3&gt;
&lt;p&gt;Master里的apiserver、scheduler等组件需要获取节点的各种信息才能够作出管理决策，那这些信息该怎么来呢？&lt;/p&gt;
&lt;p&gt;这就需要Node里的3个组件了，分别是&lt;strong&gt;kubelet&lt;/strong&gt;、&lt;strong&gt;kube-proxy&lt;/strong&gt;、&lt;strong&gt;container-runtime&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;kubelet是Node的代理，负责管理Node相关的绝大部分操作，Node上只有它能够与apiserver通信，实现状态报告、命令下发、启停容器等功能，相当于是Node上的一个“小管家”。&lt;/p&gt;</description></item><item><title>11 YAML：Kubernetes世界里的通用语</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/013/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/013/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/11%20YAML%ef%bc%9aKubernetes%e4%b8%96%e7%95%8c%e9%87%8c%e7%9a%84%e9%80%9a%e7%94%a8%e8%af%ad.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="11-yaml-kubernetes世界里的通用语"&gt;11 YAML：Kubernetes世界里的通用语&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在上次课里，我们一起研究了Kubernetes的内部架构和组成，知道它分为控制面和数据面。控制面管理集群，数据面跑业务应用，节点内部又有apiserver、etcd、scheduler、kubelet、kube-proxy等组件，它们互相协作来维护整个集群的稳定运行。&lt;/p&gt;
&lt;p&gt;这套独特的Master/Node架构是Kubernetes得以安身立命的根本，但仅依靠这套“内功心法”是不是就能够随意仗剑走天涯了呢？&lt;/p&gt;
&lt;p&gt;显然不行。就像许多武侠、玄幻作品里的人物一样，Kubernetes也需要一份“招式秘籍”才能把自己的“内功”完全发挥出来，只有内外兼修才能够达到笑傲江湖的境界。&lt;/p&gt;
&lt;p&gt;而这份“招式秘籍”，就是Kubernetes世界里的标准工作语言YAML，所以今天，我就来讲讲为什么要有YAML、它是个什么样子、该怎么使用。&lt;/p&gt;
&lt;h2 id="声明式与命令式是怎么回事"&gt;声明式与命令式是怎么回事&lt;/h2&gt;
&lt;p&gt;Kubernetes使用的YAML语言有一个非常关键的特性，叫“声明式”（Declarative），对应的有另外一个词：“命令式”（Imperative）。&lt;/p&gt;
&lt;p&gt;所以在详细了解YAML之前，我们得先来看看“&lt;strong&gt;声明式&lt;/strong&gt;”与“&lt;strong&gt;命令式&lt;/strong&gt;”这两种工作方式，它们在计算机世界里的关系有点像小说里的“剑宗”与“气宗”。&lt;/p&gt;
&lt;p&gt;我们在入门篇里学习的Docker命令和Dockerfile就属于“命令式”，大多数编程语言也属于命令式，它的特点是交互性强，注重顺序和过程，你必须“告诉”计算机每步该做什么，所有的步骤都列清楚，这样程序才能够一步步走下去，最后完成任务，显得计算机有点“笨”。&lt;/p&gt;
&lt;p&gt;“声明式”，在Kubernetes出现之前比较少见，它与“命令式”完全相反，不关心具体的过程，更注重结果。我们不需要“教”计算机该怎么做，只要告诉它一个目标状态，它自己就会想办法去完成任务，相比起来自动化、智能化程度更高。&lt;/p&gt;
&lt;p&gt;这两个概念比较抽象，不太好理解，也是Kubernetes初学者经常遇到的障碍之一。Kubernetes官网上特意以空调为例，解说“声明式”的原理，但我感觉还是没有说得太清楚，所以这里我就再以“打车”来形象地解释一下“命令式”和“声明式”的区别。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/ae3014f18801950ca20c.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;假设你要打车去高铁站，但司机不熟悉路况，你就只好不厌其烦地告诉他该走哪条路、在哪个路口转向、在哪里进出主路、停哪个站口。虽然最后到达了目的地，但这一路上也费了很多口舌，发出了无数的“命令”。很显然，这段路程就属于“命令式”。&lt;/p&gt;
&lt;p&gt;现在我们来换一种方式，同样是去高铁站，但司机经验丰富，他知道哪里有拥堵、哪条路的红绿灯多、哪段路有临时管控、哪里可以抄小道，此时你再多嘴无疑会干扰他的正常驾驶，所以，你只要给他一个“声明”：我要去高铁站，接下来就可以舒舒服服地躺在后座上休息，顺利到达目的地了。&lt;/p&gt;
&lt;p&gt;在这个“打车”的例子里，Kubernetes就是这样的一位熟练的司机，Master/Node架构让它对整个集群的状态了如指掌，内部的众多组件和插件也能够自动监控管理应用。&lt;/p&gt;
&lt;p&gt;这个时候我们再用“命令式”跟它打交道就不太合适了，因为它知道的信息比我们更多更全面，不需要我们这个外行去指导它这个内行，所以我们最好是做一个“&lt;strong&gt;甩手掌柜&lt;/strong&gt;”，用“声明式”把任务的目标告诉它，比如使用哪个镜像、什么时候运行，让它自己去处理执行过程中的细节。&lt;/p&gt;
&lt;p&gt;那么，该用什么方式去给Kubernetes发出一个“声明”呢？&lt;/p&gt;
&lt;p&gt;容器技术里的Shell脚本和Dockerfile可以很好地描述“命令式”，但对于“声明式”就不太合适了，这个时候，我们需要使用专门的YAML语言。&lt;/p&gt;
&lt;h2 id="什么是yaml"&gt;什么是YAML&lt;/h2&gt;
&lt;p&gt;YAML语言创建于2001年，比XML晚了三年。XML你应该知道吧，它是一种类似HTML的标签式语言，有很多繁文缛节。而YAML虽然在名字上模仿了XML，但实质上与XML完全不同，更适合人类阅读，计算机解析起来也很容易。&lt;/p&gt;
&lt;p&gt;YAML的官网（&lt;a href="https://yaml.org/" rel="nofollow noreferrer noopener"&gt;https://yaml.org/&lt;/a&gt;）有对语言规范的完整介绍，所以我就不在这里列举语言的细节了，只讲一些与Kubernetes相关的要点，帮助你快速掌握。&lt;/p&gt;
&lt;p&gt;你需要知道，&lt;strong&gt;YAML是JSON的超集&lt;/strong&gt;，支持整数、浮点数、布尔、字符串、数组和对象等数据类型。也就是说，任何合法的JSON文档也都是YAML文档，如果你了解JSON，那么学习YAML会容易很多。&lt;/p&gt;
&lt;p&gt;但和JSON比起来，YAML的语法更简单，形式也更清晰紧凑，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用空白与缩进表示层次（有点类似Python），可以不使用花括号和方括号。&lt;/li&gt;
&lt;li&gt;可以使用 # 书写注释，比起JSON是很大的改进。&lt;/li&gt;
&lt;li&gt;对象（字典）的格式与JSON基本相同，但Key不需要使用双引号。&lt;/li&gt;
&lt;li&gt;数组（列表）是使用 - 开头的清单形式（有点类似MarkDown）。&lt;/li&gt;
&lt;li&gt;表示对象的 : 和表示数组的 - 后面都必须要有空格。&lt;/li&gt;
&lt;li&gt;可以使用 — 在一个文件里分隔多个YAML对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面我们来看几个YAML的简单示例。&lt;/p&gt;
&lt;p&gt;首先是数组，它使用 - 列出了三种操作系统：&lt;/p&gt;
&lt;p&gt;这段YAML对应的JSON如下：&lt;/p&gt;
&lt;p&gt;对比可以看到YAML形式上很简单，没有闭合花括号、方括号的麻烦，每个元素后面也不需要逗号。&lt;/p&gt;
&lt;p&gt;再来看一个YAML对象，声明了1个Master节点，3个Worker节点：&lt;/p&gt;
&lt;p&gt;它等价的JSON如下：&lt;/p&gt;
&lt;p&gt;注意到了吗YAML里的Key都不需要使用双引号，看起来更舒服。&lt;/p&gt;
&lt;p&gt;把YAML的数组、对象组合起来，我们就可以描述出任意的Kubernetes资源对象，第三个例子略微复杂点，你可以自己尝试着解释一下：&lt;/p&gt;
&lt;p&gt;关于YAML语言的其他知识点我就不再一一细说了，都整理在了这张图里，你可以参考&lt;a href="https://yaml.org" rel="nofollow noreferrer noopener"&gt;YAML官网&lt;/a&gt;，在今后的课程中慢慢体会。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/d146a0e3b301cb61d0e6.webp" alt="图片"&gt;&lt;/p&gt;
&lt;h2 id="什么是api对象"&gt;什么是API对象&lt;/h2&gt;
&lt;p&gt;学到这里还不够，因为YAML语言只相当于“语法”，要与Kubernetes对话，我们还必须有足够的“词汇”来表示“语义”。&lt;/p&gt;
&lt;p&gt;那么应该声明Kubernetes里的哪些东西，才能够让Kubernetes明白我们的意思呢？&lt;/p&gt;
&lt;p&gt;作为一个集群操作系统，Kubernetes归纳总结了Google多年的经验，在理论层面抽象出了很多个概念，用来描述系统的管理运维工作，这些概念就叫做“&lt;strong&gt;API对象&lt;/strong&gt;”。说到这个名字，你也许会联想到上次课里讲到的Kubernetes组件&lt;strong&gt;apiserver&lt;/strong&gt;。没错，它正是来源于此。&lt;/p&gt;
&lt;p&gt;因为apiserver是Kubernetes系统的唯一入口，外部用户和内部组件都必须和它通信，而它采用了HTTP协议的URL资源理念，API风格也用RESTful的GET/POST/DELETE等等，所以，这些概念很自然地就被称为是“API对象”了。&lt;/p&gt;
&lt;p&gt;那都有哪些API对象呢？&lt;/p&gt;
&lt;p&gt;你可以使用 kubectl api-resources 来查看当前Kubernetes版本支持的所有对象：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/0dc2eafd1835918c3efb.webp" alt="图片"&gt;&lt;/p&gt;</description></item><item><title>12 Pod：如何理解这个Kubernetes里最核心的概念？</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/014/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/014/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/12%20Pod%ef%bc%9a%e5%a6%82%e4%bd%95%e7%90%86%e8%a7%a3%e8%bf%99%e4%b8%aaKubernetes%e9%87%8c%e6%9c%80%e6%a0%b8%e5%bf%83%e7%9a%84%e6%a6%82%e5%bf%b5%ef%bc%9f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="12-pod-如何理解这个kubernetes里最核心的概念"&gt;12 Pod：如何理解这个Kubernetes里最核心的概念？&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;前两天我们学习了Kubernetes世界里的工作语言YAML，还编写了一个简短的YAML文件，描述了一个API对象：Pod，它在spec字段里包含了容器的定义。&lt;/p&gt;
&lt;p&gt;那么为什么Kubernetes不直接使用已经非常成熟稳定的容器？为什么要再单独抽象出一个Pod对象？为什么几乎所有人都说Pod是Kubernetes里最核心最基本的概念呢？&lt;/p&gt;
&lt;p&gt;今天我就来逐一解答这些问题，希望你学完今天的这次课，心里面能够有明确的答案。&lt;/p&gt;
&lt;h2 id="为什么要有pod"&gt;为什么要有Pod&lt;/h2&gt;
&lt;p&gt;Pod这个词原意是“豌豆荚”，后来又延伸出“舱室”“太空舱”等含义，你可以看一下这张图片，形象地来说Pod就是包含了很多组件、成员的一种结构。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/1ff8b6b73eeb61948d32.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;容器技术我想你现在已经比较熟悉了，它让进程在一个“沙盒”环境里运行，具有良好的隔离性，对应用是一个非常好的封装。&lt;/p&gt;
&lt;p&gt;不过，当容器技术进入到现实的生产环境中时，这种隔离性就带来了一些麻烦。因为很少有应用是完全独立运行的，经常需要几个进程互相协作才能完成任务，比如在“入门篇”里我们搭建WordPress网站的时候，就需要Nginx、WordPress、MariaDB三个容器一起工作。&lt;/p&gt;
&lt;p&gt;WordPress例子里的这三个应用之间的关系还是比较松散的，它们可以分别调度，运行在不同的机器上也能够以IP地址通信。&lt;/p&gt;
&lt;p&gt;但还有一些特殊情况，多个应用结合得非常紧密以至于无法把它们拆开。比如，有的应用运行前需要其他应用帮它初始化一些配置，还有就是日志代理，它必须读取另一个应用存储在本地磁盘的文件再转发出去。这些应用如果被强制分离成两个容器，切断联系，就无法正常工作了。&lt;/p&gt;
&lt;p&gt;那么把这些应用都放在一个容器里运行可不可以呢？&lt;/p&gt;
&lt;p&gt;当然可以，但这并不是一种好的做法。因为容器的理念是对应用的独立封装，它里面就应该是一个进程、一个应用，如果里面有多个应用，不仅违背了容器的初衷，也会让容器更难以管理。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;为了解决这样多应用联合运行的问题，同时还要不破坏容器的隔离，就需要在容器外面再建立一个“收纳舱”&lt;/strong&gt;，让多个容器既保持相对独立，又能够小范围共享网络、存储等资源，而且永远是“绑在一起”的状态。&lt;/p&gt;
&lt;p&gt;所以，Pod的概念也就呼之欲出了，容器正是“豆荚”里那些小小的“豌豆”，你可以在Pod的YAML里看到，“spec.containers”字段其实是一个数组，里面允许定义多个容器。&lt;/p&gt;
&lt;p&gt;如果再拿之前讲过的“小板房”来比喻的话，Pod就是由客厅、卧室、厨房等预制房间拼装成的一个齐全的生活环境，不仅同样具备易于拆装易于搬迁的优点，而且要比单独的“一居室”功能强大得多，能够让进程“住”得更舒服。&lt;/p&gt;
&lt;h2 id="为什么pod是kubernetes的核心对象"&gt;为什么Pod是Kubernetes的核心对象&lt;/h2&gt;
&lt;p&gt;因为Pod是对容器的“打包”，里面的容器是一个整体，总是能够一起调度、一起运行，绝不会出现分离的情况，而且Pod属于Kubernetes，可以在不触碰下层容器的情况下任意定制修改。所以有了Pod这个抽象概念，Kubernetes在集群级别上管理应用就会“得心应手”了。&lt;/p&gt;
&lt;p&gt;Kubernetes让Pod去编排处理容器，然后把Pod作为应用调度部署的&lt;strong&gt;最小单位&lt;/strong&gt;，Pod也因此成为了Kubernetes世界里的“原子”（当然这个“原子”内部是有结构的，不是铁板一块），基于Pod就可以构建出更多更复杂的业务形态了。&lt;/p&gt;
&lt;p&gt;下面的这张图你也许在其他资料里见过，它从Pod开始，扩展出了Kubernetes里的一些重要API对象，比如配置信息ConfigMap、离线作业Job、多实例部署Deployment等等，它们都分别对应到现实中的各种实际运维需求。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/0b88acb0fe45adbff9be.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;不过这张图虽然很经典，参考价值很高，但毕竟有些年头了，随着Kubernetes的发展，它已经不能够全面地描述Kubernetes的资源对象了。&lt;/p&gt;
&lt;p&gt;受这张图的启发，我自己重新画了一份以Pod为中心的Kubernetes资源对象关系图，添加了一些新增的Kubernetes概念，今后我们就依据这张图来探索Kubernetes的各项功能。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/43d053ac5d48d3aa969a.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;从这两张图中你也应该能够看出来，所有的Kubernetes资源都直接或者间接地依附在Pod之上，所有的Kubernetes功能都必须通过Pod来实现，所以Pod理所当然地成为了Kubernetes的核心对象。&lt;/p&gt;
&lt;h2 id="如何使用yaml描述pod"&gt;如何使用YAML描述Pod&lt;/h2&gt;
&lt;p&gt;既然Pod这么重要，那么我们就很有必要来详细了解一下Pod，理解了Pod概念，我们的Kubernetes学习之旅就成功了一半。&lt;/p&gt;
&lt;p&gt;还记得吧，我们始终可以用命令 kubectl explain 来查看任意字段的详细说明，所以接下来我就只简要说说写YAML时Pod里的一些常用字段。&lt;/p&gt;
&lt;p&gt;因为Pod也是API对象，所以它也必然具有&lt;strong&gt;apiVersion&lt;/strong&gt;、&lt;strong&gt;kind&lt;/strong&gt;、&lt;strong&gt;metadata、spec&lt;/strong&gt;这四个基本组成部分。&lt;/p&gt;
&lt;p&gt;“apiVersion”和“kind”这两个字段很简单，对于Pod来说分别是固定的值 v1 和 Pod，而一般来说，“metadata”里应该有 name 和 labels 这两个字段。&lt;/p&gt;
&lt;p&gt;我们在使用Docker创建容器的时候，可以不给容器起名字，但在Kubernetes里，Pod必须要有一个名字，这也是Kubernetes里所有资源对象的一个约定。在课程里，我通常会为Pod名字统一加上 pod 后缀，这样可以和其他类型的资源区分开。&lt;/p&gt;
&lt;p&gt;name 只是一个基本的标识，信息有限，所以 labels 字段就派上了用处。它可以添加任意数量的Key-Value，给Pod“贴”上归类的标签，结合 name 就更方便识别和管理了。&lt;/p&gt;
&lt;p&gt;比如说，我们可以根据运行环境，使用标签 env=dev/test/prod，或者根据所在的数据中心，使用标签 region: north/south，还可以根据应用在系统中的层次，使用 tier=front/middle/back ……如此种种，只需要发挥你的想象力。&lt;/p&gt;
&lt;p&gt;下面这段YAML代码就描述了一个简单的Pod，名字是“busy-pod”，再附加上一些标签：&lt;/p&gt;
&lt;p&gt;“metadata”一般写上 name 和 labels 就足够了，而“spec”字段由于需要管理、维护Pod这个Kubernetes的基本调度单元，里面有非常多的关键信息，今天我介绍最重要的“&lt;strong&gt;containers&lt;/strong&gt;”，其他的hostname、restartPolicy等字段你可以课后自己查阅文档学习。&lt;/p&gt;
&lt;p&gt;“containers”是一个数组，里面的每一个元素又是一个container对象，也就是容器。&lt;/p&gt;
&lt;p&gt;和Pod一样，container对象也必须要有一个 name 表示名字，然后当然还要有一个 image 字段来说明它使用的镜像，这两个字段是必须要有的，否则Kubernetes会报告数据验证错误。&lt;/p&gt;</description></item><item><title>13 Job_CronJob：为什么不直接用Pod来处理业务？</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/015/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/015/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/13%20Job_CronJob%ef%bc%9a%e4%b8%ba%e4%bb%80%e4%b9%88%e4%b8%8d%e7%9b%b4%e6%8e%a5%e7%94%a8Pod%e6%9d%a5%e5%a4%84%e7%90%86%e4%b8%9a%e5%8a%a1%ef%bc%9f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="13-job-cronjob-为什么不直接用pod来处理业务"&gt;13 Job_CronJob：为什么不直接用Pod来处理业务？&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在上次的课里我们学习了Kubernetes的核心对象Pod，用来编排一个或多个容器，让这些容器共享网络、存储等资源，总是共同调度，从而紧密协同工作。&lt;/p&gt;
&lt;p&gt;因为Pod比容器更能够表示实际的应用，所以Kubernetes不会在容器层面来编排业务，而是把Pod作为在集群里调度运维的最小单位。&lt;/p&gt;
&lt;p&gt;前面我们也看到了一张Kubernetes的资源对象关系图，以Pod为中心，延伸出了很多表示各种业务的其他资源对象。那么你会不会有这样的疑问：Pod的功能已经足够完善了，为什么还要定义这些额外的对象呢？为什么不直接在Pod里添加功能，来处理业务需求呢？&lt;/p&gt;
&lt;p&gt;这个问题体现了Google对大规模计算集群管理的深度思考，今天我就说说Kubernetes基于Pod的设计理念，先从最简单的两种对象——Job和CronJob讲起。&lt;/p&gt;
&lt;h2 id="为什么不直接使用pod"&gt;为什么不直接使用Pod&lt;/h2&gt;
&lt;p&gt;现在你应该知道，Kubernetes使用的是RESTful API，把集群中的各种业务都抽象为HTTP资源对象，那么在这个层次之上，我们就可以使用面向对象的方式来考虑问题。&lt;/p&gt;
&lt;p&gt;如果你有一些编程方面的经验，就会知道面向对象编程（OOP），它把一切都视为高内聚的对象，强调对象之间互相通信来完成任务。&lt;/p&gt;
&lt;p&gt;虽然面向对象的设计思想多用于软件开发，但它放到Kubernetes里却意外地合适。因为Kubernetes使用YAML来描述资源，把业务简化成了一个个的对象，内部有属性，外部有联系，也需要互相协作，只不过我们不需要编程，完全由Kubernetes自动处理（其实Kubernetes的Go语言内部实现就大量应用了面向对象）。&lt;/p&gt;
&lt;p&gt;面向对象的设计有许多基本原则，其中有两条我认为比较恰当地描述了Kubernetes对象设计思路，一个是“&lt;strong&gt;单一职责&lt;/strong&gt;”，另一个是“&lt;strong&gt;组合优于继承&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;“单一职责”的意思是对象应该只专注于做好一件事情，不要贪大求全，保持足够小的粒度才更方便复用和管理。&lt;/p&gt;
&lt;p&gt;“组合优于继承”的意思是应该尽量让对象在运行时产生联系，保持松耦合，而不要用硬编码的方式固定对象的关系。&lt;/p&gt;
&lt;p&gt;应用这两条原则，我们再来看Kubernetes的资源对象就会很清晰了。因为Pod已经是一个相对完善的对象，专门负责管理容器，那么我们就不应该再“画蛇添足”地盲目为它扩充功能，而是要保持它的独立性，容器之外的功能就需要定义其他的对象，把Pod作为它的一个成员“组合”进去。&lt;/p&gt;
&lt;p&gt;这样每种Kubernetes对象就可以只关注自己的业务领域，只做自己最擅长的事情，其他的工作交给其他对象来处理，既不“缺位”也不“越位”，既有分工又有协作，从而以最小成本实现最大收益。&lt;/p&gt;
&lt;h2 id="为什么要有job-cronjob"&gt;为什么要有Job/CronJob&lt;/h2&gt;
&lt;p&gt;现在我们来看看Kubernetes里的两种新对象：Job和CronJob，它们就组合了Pod，实现了对离线业务的处理。&lt;/p&gt;
&lt;p&gt;上次课讲Pod的时候我们运行了两个Pod：Nginx和busybox，它们分别代表了Kubernetes里的两大类业务。一类是像Nginx这样长时间运行的“&lt;strong&gt;在线业务&lt;/strong&gt;”，另一类是像busybox这样短时间运行的“&lt;strong&gt;离线业务&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;“在线业务”类型的应用有很多，比如Nginx、Node.js、MySQL、Redis等等，一旦运行起来基本上不会停，也就是永远在线。&lt;/p&gt;
&lt;p&gt;而“离线业务”类型的应用也并不少见，它们一般不直接服务于外部用户，只对内部用户有意义，比如日志分析、数据建模、视频转码等等，虽然计算量很大，但只会运行一段时间。“离线业务”的特点是&lt;strong&gt;必定会退出&lt;/strong&gt;，不会无期限地运行下去，所以它的调度策略也就与“在线业务”存在很大的不同，需要考虑运行超时、状态检查、失败重试、获取计算结果等管理事项。&lt;/p&gt;
&lt;p&gt;而这些业务特性与容器管理没有必然的联系，如果由Pod来实现就会承担不必要的义务，违反了“单一职责”，所以我们应该把这部分功能分离到另外一个对象上实现，让这个对象去控制Pod的运行，完成附加的工作。&lt;/p&gt;
&lt;p&gt;“离线业务”也可以分为两种。一种是“&lt;strong&gt;临时任务&lt;/strong&gt;”，跑完就完事了，下次有需求了说一声再重新安排；另一种是“&lt;strong&gt;定时任务&lt;/strong&gt;”，可以按时按点周期运行，不需要过多干预。&lt;/p&gt;
&lt;p&gt;对应到Kubernetes里，“临时任务”就是API对象&lt;strong&gt;Job&lt;/strong&gt;，“定时任务”就是API对象&lt;strong&gt;CronJob&lt;/strong&gt;，使用这两个对象你就能够在Kubernetes里调度管理任意的离线业务了。&lt;/p&gt;
&lt;p&gt;由于Job和CronJob都属于离线业务，所以它们也比较相似。我们先学习通常只会运行一次的Job对象以及如何操作。&lt;/p&gt;
&lt;h3 id="如何使用yaml描述job"&gt;如何使用YAML描述Job&lt;/h3&gt;
&lt;p&gt;Job的YAML“文件头”部分还是那几个必备字段，我就不再重复解释了，简单说一下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;apiVersion不是 v1，而是 batch/v1。&lt;/li&gt;
&lt;li&gt;kind是 Job，这个和对象的名字是一致的。&lt;/li&gt;
&lt;li&gt;metadata里仍然要有 name 标记名字，也可以用 labels 添加任意的标签。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果记不住这些也不要紧，你还可以使用命令 kubectl explain job 来看它的字段说明。不过想要生成YAML样板文件的话不能使用 kubectl run，因为 kubectl run 只能创建Pod，要创建Pod以外的其他API对象，需要使用命令 kubectl create，再加上对象的类型名。&lt;/p&gt;
&lt;p&gt;比如用busybox创建一个“echo-job”，命令就是这样的：&lt;/p&gt;
&lt;p&gt;会生成一个基本的YAML文件，保存之后做点修改，就有了一个Job对象：&lt;/p&gt;
&lt;p&gt;你会注意到Job的描述与Pod很像，但又有些不一样，主要的区别就在“spec”字段里，多了一个 template 字段，然后又是一个“spec”，显得有点怪。&lt;/p&gt;
&lt;p&gt;如果你理解了刚才说的面向对象设计思想，就会明白这种做法的道理。它其实就是在Job对象里应用了组合模式，template 字段定义了一个“&lt;strong&gt;应用模板&lt;/strong&gt;”，里面嵌入了一个Pod，这样Job就可以从这个模板来创建出Pod。&lt;/p&gt;
&lt;p&gt;而这个Pod因为受Job的管理控制，不直接和apiserver打交道，也就没必要重复apiVersion等“头字段”，只需要定义好关键的 spec，描述清楚容器相关的信息就可以了，可以说是一个“无头”的Pod对象。&lt;/p&gt;
&lt;p&gt;为了辅助你理解，我把Job对象重新组织了一下，用不同的颜色来区分字段，这样你就能够很容易看出来，其实这个“echo-job”里并没有太多额外的功能，只是把Pod做了个简单的包装：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/cd60625d23efbcfffb36.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;总的来说，这里的Pod工作非常简单，在 containers 里写好名字和镜像，command 执行 /bin/echo，输出“hello world”。&lt;/p&gt;
&lt;p&gt;不过，因为Job业务的特殊性，所以我们还要在 spec 里多加一个字段 restartPolicy，确定Pod运行失败时的策略，OnFailure 是失败原地重启容器，而 Never 则是不重启容器，让Job去重新调度生成一个新的Pod。&lt;/p&gt;</description></item><item><title>14 ConfigMap_Secret：怎样配置、定制我的应用</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/016/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/016/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/14%20ConfigMap_Secret%ef%bc%9a%e6%80%8e%e6%a0%b7%e9%85%8d%e7%bd%ae%e3%80%81%e5%ae%9a%e5%88%b6%e6%88%91%e7%9a%84%e5%ba%94%e7%94%a8.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="14-configmap-secret-怎样配置-定制我的应用"&gt;14 ConfigMap_Secret：怎样配置、定制我的应用&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;前两节课里我们学习了Kubernetes里的三种API对象：Pod、Job和CronJob，虽然还没有讲到更高级的其他对象，但使用它们也可以在集群里编排运行一些实际的业务了。&lt;/p&gt;
&lt;p&gt;不过想让业务更顺利地运行，有一个问题不容忽视，那就是应用的配置管理。&lt;/p&gt;
&lt;p&gt;配置文件，你应该有所了解吧，通常来说应用程序都会有一个，它把运行时需要的一些参数从代码中分离出来，让我们在实际运行的时候能更方便地调整优化，比如说Nginx有nginx.conf、Redis有redis.conf、MySQL有my.cnf等等。&lt;/p&gt;
&lt;p&gt;我们在“入门篇”里学习容器技术的时候讲过，可以选择两种管理配置文件的方式。第一种是编写Dockerfile，用 COPY 指令把配置文件打包到镜像里；第二种是在运行时使用 docker cp 或者 docker run -v，把本机的文件拷贝进容器。&lt;/p&gt;
&lt;p&gt;但这两种方式都存在缺陷。第一种方法相当于是在镜像里固定了配置文件，不好修改，不灵活，第二种方法则显得有点“笨拙”，不适合在集群中自动化运维管理。&lt;/p&gt;
&lt;p&gt;对于这个问题Kubernetes有它自己的解决方案，你也应该能够猜得到，当然还是使用YAML语言来定义API对象，再组合起来实现动态配置。&lt;/p&gt;
&lt;p&gt;今天我就来讲解Kubernetes里专门用来管理配置信息的两种对象：&lt;strong&gt;ConfigMap&lt;/strong&gt;和&lt;strong&gt;Secret&lt;/strong&gt;，使用它们来灵活地配置、定制我们的应用。&lt;/p&gt;
&lt;h2 id="configmap-secret"&gt;ConfigMap/Secret&lt;/h2&gt;
&lt;p&gt;首先你要知道，应用程序有很多类别的配置信息，但从数据安全的角度来看可以分成两类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一类是明文配置，也就是不保密，可以任意查询修改，比如服务端口、运行参数、文件路径等等。&lt;/li&gt;
&lt;li&gt;另一类则是机密配置，由于涉及敏感信息需要保密，不能随便查看，比如密码、密钥、证书等等。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两类配置信息本质上都是字符串，只是由于安全性的原因，在存放和使用方面有些差异，所以Kubernetes也就定义了两个API对象，&lt;strong&gt;ConfigMap&lt;/strong&gt;用来保存明文配置，&lt;strong&gt;Secret&lt;/strong&gt;用来保存秘密配置。&lt;/p&gt;
&lt;h3 id="什么是configmap"&gt;什么是ConfigMap&lt;/h3&gt;
&lt;p&gt;先来看ConfigMap，我们仍然可以用命令 kubectl create 来创建一个它的YAML样板。注意，它有简写名字“&lt;strong&gt;cm&lt;/strong&gt;”，所以命令行里没必要写出它的全称：&lt;/p&gt;
&lt;p&gt;得到的样板文件大概是这个样子：&lt;/p&gt;
&lt;p&gt;你可能会有点惊讶，ConfigMap的YAML和之前我们学过的Pod、Job不一样，除了熟悉的“apiVersion”“kind”“metadata”，居然就没有其他的了，最重要的字段“spec”哪里去了？这是因为ConfigMap存储的是配置数据，是静态的字符串，并不是容器，所以它们就不需要用“spec”字段来说明运行时的“规格”。&lt;/p&gt;
&lt;p&gt;既然ConfigMap要存储数据，我们就需要用另一个含义更明确的字段“&lt;strong&gt;data&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;要生成带有“data”字段的YAML样板，你需要在 kubectl create 后面多加一个参数 –from-literal ，表示从字面值生成一些数据：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意，因为在ConfigMap里的数据都是Key-Value结构，所以 –from-literal 参数需要使用 k=v 的形式。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;把YAML样板文件修改一下，再多增添一些Key-Value，就得到了一个比较完整的ConfigMap对象：&lt;/p&gt;
&lt;p&gt;现在就可以使用 kubectl apply 把这个YAML交给Kubernetes，让它创建ConfigMap对象了：&lt;/p&gt;
&lt;p&gt;创建成功后，我们还是可以用 kubectl get、kubectl describe 来查看ConfigMap的状态：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/c66d3eb2476e80f293a6.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/7a8d14b4754828438fe0.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;你可以看到，现在ConfigMap的Key-Value信息就已经存入了etcd数据库，后续就可以被其他API对象使用。&lt;/p&gt;
&lt;h3 id="什么是secret"&gt;什么是Secret&lt;/h3&gt;
&lt;p&gt;了解了ConfigMap对象，我们再来看Secret对象就会容易很多，它和ConfigMap的结构和用法很类似，不过在Kubernetes里Secret对象又细分出很多类，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;访问私有镜像仓库的认证信息&lt;/li&gt;
&lt;li&gt;身份识别的凭证信息&lt;/li&gt;
&lt;li&gt;HTTPS通信的证书和私钥&lt;/li&gt;
&lt;li&gt;一般的机密信息（格式由用户自行解释）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;前几种我们现在暂时用不到，所以就只使用最后一种，创建YAML样板的命令是 kubectl create secret generic ，同样，也要使用参数 –from-literal 给出Key-Value值：&lt;/p&gt;</description></item><item><title>15 实战演练：玩转Kubernetes（1）</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/017/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/017/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/15%20%e5%ae%9e%e6%88%98%e6%bc%94%e7%bb%83%ef%bc%9a%e7%8e%a9%e8%bd%acKubernetes%ef%bc%881%ef%bc%89.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="15-实战演练-玩转kubernetes-1"&gt;15 实战演练：玩转Kubernetes（1）&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;经过两个星期的学习，到今天我们的“初级篇”也快要结束了。&lt;/p&gt;
&lt;p&gt;和之前的“入门篇”一样，在这次课里，我也会对前面学过的知识做一个比较全面的回顾，毕竟Kubernetes领域里有很多新名词、新术语、新架构，知识点多且杂，这样的总结复习就更有必要。&lt;/p&gt;
&lt;p&gt;接下来我还是先简要列举一下“初级篇”里讲到的Kubernetes要点，然后再综合运用这些知识，演示一个实战项目——还是搭建WordPress网站，不过这次不是在Docker里，而是在Kubernetes集群里。&lt;/p&gt;
&lt;h2 id="kubernetes技术要点回顾"&gt;Kubernetes技术要点回顾&lt;/h2&gt;
&lt;p&gt;容器技术开启了云原生的大潮，但成熟的容器技术，到生产环境的应用部署的时候，却显得“步履维艰”。因为容器只是针对单个进程的隔离和封装，而实际的应用场景却是要求许多的应用进程互相协同工作，其中的各种关系和需求非常复杂，在容器这个技术层次很难掌控。&lt;/p&gt;
&lt;p&gt;为了解决这个问题，&lt;strong&gt;容器编排&lt;/strong&gt;（Container Orchestration）就出现了，它可以说是以前的运维工作在云原生世界的落地实践，本质上还是在集群里调度管理应用程序，只不过管理的主体由人变成了计算机，管理的目标由原生进程变成了容器和镜像。&lt;/p&gt;
&lt;p&gt;而现在，容器编排领域的王者就是——Kubernetes。&lt;/p&gt;
&lt;p&gt;Kubernetes源自Borg系统，它凝聚了Google的内部经验和CNCF的社区智慧，所以战胜了竞争对手Apache Mesos和Docker Swarm，成为了容器编排领域的事实标准，也成为了云原生时代的基础操作系统，学习云原生就必须要掌握Kubernetes。&lt;/p&gt;
&lt;p&gt;（[10讲]）Kubernetes的&lt;strong&gt;Master/Node架构&lt;/strong&gt;是它具有自动化运维能力的关键，也对我们的学习至关重要，这里我再用另一张参考架构图来简略说明一下它的运行机制（&lt;a href="https://kubernetes.io/blog/2018/07/18/11-ways-not-to-get-hacked" rel="nofollow noreferrer noopener"&gt;图片来源&lt;/a&gt;）：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/0b7c298f813efc2085ef.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;Kubernetes把集群里的计算资源定义为节点（Node），其中又划分成控制面和数据面两类。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;控制面是Master节点，负责管理集群和运维监控应用，里面的核心组件是&lt;strong&gt;apiserver、etcd、scheduler、controller-manager&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;数据面是Worker节点，受Master节点的管控，里面的核心组件是&lt;strong&gt;kubelet、kube-proxy、container-runtime&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此外，Kubernetes还支持插件机制，能够灵活扩展各项功能，常用的插件有DNS和Dashboard。&lt;/p&gt;
&lt;p&gt;为了更好地管理集群和业务应用，Kubernetes从现实世界中抽象出了许多概念，称为“&lt;strong&gt;API对象&lt;/strong&gt;”，描述这些对象就需要使用&lt;strong&gt;YAML&lt;/strong&gt;语言。&lt;/p&gt;
&lt;p&gt;YAML是JSON的超集，但语法更简洁，表现能力更强，更重要的是它以“&lt;strong&gt;声明式&lt;/strong&gt;”来表述对象的状态，不涉及具体的操作细节，这样Kubernetes就能够依靠存储在etcd里集群的状态信息，不断地“调控”对象，直至实际状态与期望状态相同，这个过程就是Kubernetes的自动化运维管理（[11讲]）。&lt;/p&gt;
&lt;p&gt;Kubernetes里有很多的API对象，其中最核心的对象是“&lt;strong&gt;Pod&lt;/strong&gt;”，它捆绑了一组存在密切协作关系的容器，容器之间共享网络和存储，在集群里必须一起调度一起运行。通过Pod这个概念，Kubernetes就简化了对容器的管理工作，其他的所有任务都是通过对Pod这个最小单位的再包装来实现的（[12讲]）。&lt;/p&gt;
&lt;p&gt;除了核心的Pod对象，基于“单一职责”和“对象组合”这两个基本原则，我们又学习了4个比较简单的API对象，分别是&lt;strong&gt;Job/CronJob&lt;/strong&gt;和&lt;strong&gt;ConfigMap&lt;/strong&gt;/&lt;strong&gt;Secret&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Job/CronJob对应的是离线作业，它们逐层包装了Pod，添加了作业控制和定时规则（[13讲]）。&lt;/li&gt;
&lt;li&gt;ConfigMap/Secret对应的是配置信息，需要以环境变量或者存储卷的形式注入进Pod，然后进程才能在运行时使用（[14讲]）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;和Docker类似，Kubernetes也提供一个客户端工具，名字叫“&lt;strong&gt;kubectl&lt;/strong&gt;”，它直接与Master节点的apiserver通信，把YAML文件发送给RESTful接口，从而触发Kubernetes的对象管理工作流程。&lt;/p&gt;
&lt;p&gt;kubectl的命令很多，查看自带文档可以用 api-resources、explain ，查看对象状态可以用 get、describe、logs ，操作对象可以用 run、apply、exec、delete 等等（[09讲]）。&lt;/p&gt;
&lt;p&gt;使用YAML描述API对象也有固定的格式，必须写的“头字段”是“&lt;strong&gt;apiVersion&lt;/strong&gt;”“&lt;strong&gt;kind&lt;/strong&gt;”“&lt;strong&gt;metadata&lt;/strong&gt;”，它们表示对象的版本、种类和名字等元信息。实体对象如Pod、Job、CronJob会再有“&lt;strong&gt;spec&lt;/strong&gt;”字段描述对象的期望状态，最基本的就是容器信息，非实体对象如ConfigMap、Secret使用的是“&lt;strong&gt;data&lt;/strong&gt;”字段，记录一些静态的字符串信息。&lt;/p&gt;
&lt;p&gt;好了，“初级篇”里的Kubernetes知识要点我们就基本总结完了，如果你发现哪部分不太清楚，可以课后再多复习一下前面的课程加以巩固。&lt;/p&gt;
&lt;h2 id="wordpress网站基本架构"&gt;WordPress网站基本架构&lt;/h2&gt;
&lt;p&gt;下面我们就在Kubernetes集群里再搭建出一个WordPress网站，用的镜像还是“入门篇”里的那三个应用：WordPress、MariaDB、Nginx，不过当时我们是直接以容器的形式来使用它们，现在要改成Pod的形式，让它们运行在Kubernetes里。&lt;/p&gt;
&lt;p&gt;我还是画了一张简单的架构图，来说明这个系统的内部逻辑关系：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/318c3ceb59dc6a124452.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;从这张图中你可以看到，网站的大体架构是没有变化的，毕竟应用还是那三个，它们的调用依赖关系也必然没有变化。&lt;/p&gt;
&lt;p&gt;那么Kubernetes系统和Docker系统的区别又在哪里呢？&lt;/p&gt;
&lt;p&gt;关键就在&lt;strong&gt;对应用的封装&lt;/strong&gt;和&lt;strong&gt;网络环境&lt;/strong&gt;这两点上。&lt;/p&gt;
&lt;p&gt;现在WordPress、MariaDB这两个应用被封装成了Pod（由于它们都是在线业务，所以Job/CronJob在这里派不上用场），运行所需的环境变量也都被改写成ConfigMap，统一用“声明式”来管理，比起Shell脚本更容易阅读和版本化管理。&lt;/p&gt;
&lt;p&gt;另外，Kubernetes集群在内部维护了一个自己的专用网络，这个网络和外界隔离，要用特殊的“端口转发”方式来传递数据，还需要在集群之外用Nginx反向代理这个地址，这样才能实现内外沟通，对比Docker的直接端口映射，这里略微麻烦了一些。&lt;/p&gt;
&lt;h2 id="wordpress网站搭建步骤"&gt;WordPress网站搭建步骤&lt;/h2&gt;
&lt;p&gt;了解基本架构之后，接下来我们就逐步搭建这个网站系统，总共需要4步。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一步&lt;/strong&gt;当然是要编排MariaDB对象，它的具体运行需求可以参考“入门篇”的实战演练课，这里我就不再重复了。&lt;/p&gt;
&lt;p&gt;MariaDB需要4个环境变量，比如数据库名、用户名、密码等，在Docker里我们是在命令行里使用参数 –env，而在Kubernetes里我们就应该使用ConfigMap，为此需要定义一个 maria-cm 对象：&lt;/p&gt;
&lt;p&gt;然后我们定义Pod对象 maria-pod，把配置信息注入Pod，让MariaDB运行时从环境变量读取这些信息：&lt;/p&gt;
&lt;p&gt;注意这里我们使用了一个新的字段“&lt;strong&gt;envFrom&lt;/strong&gt;”，这是因为ConfigMap里的信息比较多，如果用 env.valueFrom 一个个地写会非常麻烦，容易出错，而 envFrom 可以一次性地把ConfigMap里的字段全导入进Pod，并且能够指定变量名的前缀（即这里的 MARIADB_），非常方便。&lt;/p&gt;
&lt;p&gt;使用 kubectl apply 创建这个对象之后，可以用 kubectl get pod 查看它的状态，如果想要获取IP地址需要加上参数 -o wide ：&lt;/p&gt;</description></item><item><title>16 视频：初级篇实操总结</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/018/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/018/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/16%20%e8%a7%86%e9%a2%91%ef%bc%9a%e5%88%9d%e7%ba%a7%e7%af%87%e5%ae%9e%e6%93%8d%e6%80%bb%e7%bb%93.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="16-视频-初级篇实操总结"&gt;16 视频：初级篇实操总结&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;学完了前面的7节文字+音频的课程之后，今天又是一节视频课程，对“初级篇”里Kubernetes知识做一个回顾与总结，同样还是注重实际操作的演示，而不会讲解理论知识。&lt;/p&gt;
&lt;p&gt;首先我们会启动本机环境里的minikube，登录虚拟节点看看apiserver、etcd、scheduler等组件，然后使用命令行工具kubectl操作Kubernetes，创建Pod、Job、CronJob、ConfigMap、Secret等API对象。&lt;/p&gt;
&lt;p&gt;接下来我们视频上见。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="一-minikube环境"&gt;一. minikube环境&lt;/h3&gt;
&lt;p&gt;视频操作要点：&lt;/p&gt;
&lt;p&gt;我们从minikube环境开始，安装的过程就不演示了，我假设你已经按照[第9讲]里的步骤成功下载了minikube和kubectl的二进制文件。&lt;/p&gt;
&lt;p&gt;先看一下minikube的版本号：&lt;/p&gt;
&lt;p&gt;显示的是当前的最新版本1.25.2，再看一下它的状态：&lt;/p&gt;
&lt;p&gt;可以看到Kubernetes的组件都没有启动，集群处于停止状态。&lt;/p&gt;
&lt;p&gt;现在让我们用 minikube start 启动minikube集群：&lt;/p&gt;
&lt;p&gt;稍等一小会，看看它提示里的表情符号，一个本地的微型Kubernetes集群就创建好了。从提示信息里可以看到，这个Kubernetes集群的版本是v1.23.3，运行在Docker 20.10.12上。&lt;/p&gt;
&lt;p&gt;现在我们再看一下minikube的状态：&lt;/p&gt;
&lt;p&gt;可以看到Kubernetes的核心组件kubelet、apiserver都是运行状态了。&lt;/p&gt;
&lt;p&gt;命令 minikube node list 可以查看这个微型集群的节点列表：&lt;/p&gt;
&lt;p&gt;默认情况下，minikube只会创建一个节点，这里显示它的IP地址是“192.168.49.2”。&lt;/p&gt;
&lt;p&gt;我们可以使用命令 minikube ssh 直接登录到这个节点，是虚拟机里的一个虚拟机，在里面可以执行任意的Linux操作：&lt;/p&gt;
&lt;p&gt;看完了minikube集群的状态，让我们用kubectl来操作Kubernetes，首先是看版本号：&lt;/p&gt;
&lt;p&gt;显示版本是1.23.3。用 kubectl get pod 看看当前集群里运行的运行应用，会发现是空的。&lt;/p&gt;
&lt;p&gt;让我们用 run 命令运行一个Nginx Pod：&lt;/p&gt;
&lt;p&gt;这个时候我们再查看Pod， kubectl get pod 就会看到有一个Pod运行了。&lt;/p&gt;
&lt;p&gt;这个Nginx pod属于default名字空间，而apiserver等核心组件是在kube-system名字空间，你可以用 -n 参数查看在kube-system里运行的Pod：&lt;/p&gt;
&lt;p&gt;可以看到apiserver、etcd、scheduler、controller manager、coredns、kube-proxy等组件都是以Pod的形式在这里运行。&lt;/p&gt;
&lt;h3 id="二-用kubectl操作api对象"&gt;二. 用kubectl操作API对象&lt;/h3&gt;
&lt;p&gt;视频操作要点：&lt;/p&gt;
&lt;p&gt;下面我们把注意力集中在kubectl操作Kubernetes API对象上。&lt;/p&gt;
&lt;p&gt;先来查看当前Kubernetes版本支持的所有api对象，命令是 kubectl api-resources：&lt;/p&gt;
&lt;p&gt;它的输出信息很多，你可以看到Pod的简写是po、api version是v1、CronJob的简写是cj、api version是batch/v1，这些信息在我们编写YAML描述文件的时候非常有用。&lt;/p&gt;
&lt;p&gt;再来看另一个常用的命令 kubectl explain，它能够给出api对象字段的详细信息，比如查看Pod：&lt;/p&gt;
&lt;p&gt;有了这个随手可得的文档，我们在编写YAML文件的时候就不会不知所措了。&lt;/p&gt;
&lt;p&gt;创建YAML样板要用到两个特殊参数“–dry-run=client”和“-o yaml”，我把它定义成一个环境变量：&lt;/p&gt;</description></item><item><title>17 更真实的云原生：实际搭建多节点的Kubernetes集群</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/019/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/019/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/17%20%e6%9b%b4%e7%9c%9f%e5%ae%9e%e7%9a%84%e4%ba%91%e5%8e%9f%e7%94%9f%ef%bc%9a%e5%ae%9e%e9%99%85%e6%90%ad%e5%bb%ba%e5%a4%9a%e8%8a%82%e7%82%b9%e7%9a%84Kubernetes%e9%9b%86%e7%be%a4.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="17-更真实的云原生-实际搭建多节点的kubernetes集群"&gt;17 更真实的云原生：实际搭建多节点的Kubernetes集群&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;到今天，你学习这个专栏的进度就已经过半了，在前面的“入门篇”我们了解了Docker和容器技术，在“初级篇”我们掌握了Kubernetes的基本对象、原理和操作方法，一路走下来收获很多。&lt;/p&gt;
&lt;p&gt;现在你应该对Kubernetes和容器编排有了一些初步的认识，那么接下来，让我们继续深入研究Kubernetes的其他API对象，也就是那些在Docker中不存在的但对云计算、集群管理至关重要的概念。&lt;/p&gt;
&lt;p&gt;不过在那之前，我们还需要有一个比minikube更真实的Kubernetes环境，它应该是一个多节点的Kubernetes集群，这样更贴近现实中的生产系统，能够让我们尽快地拥有实际的集群使用经验。&lt;/p&gt;
&lt;p&gt;所以在今天的这节课里，我们就来暂时忘掉minikube，改用kubeadm（&lt;a href="https://kubernetes.io/zh/docs/reference/setup-tools/kubeadm/" rel="nofollow noreferrer noopener"&gt;https://kubernetes.io/zh/docs/reference/setup-tools/kubeadm/&lt;/a&gt;）搭建出一个新的Kubernetes集群，一起来看看更真实的云原生环境。&lt;/p&gt;
&lt;h2 id="什么是kubeadm"&gt;什么是kubeadm&lt;/h2&gt;
&lt;p&gt;前面的几节课里我们使用的都是minikube，它非常简单易用，不需要什么配置工作，就能够在单机环境里创建出一个功能完善的Kubernetes集群，给学习、开发、测试都带来了极大的便利。&lt;/p&gt;
&lt;p&gt;不过minikube还是太“迷你”了，方便的同时也隐藏了很多细节，离真正生产环境里的计算集群有一些差距，毕竟许多需求、任务只有在多节点的大集群里才能够遇到，相比起来，minikube真的只能算是一个“玩具”。&lt;/p&gt;
&lt;p&gt;那么，多节点的Kubernetes集群是怎么从无到有地创建出来的呢？&lt;/p&gt;
&lt;p&gt;[第10讲]说过Kubernetes是很多模块构成的，而实现核心功能的组件像apiserver、etcd、scheduler等本质上都是可执行文件，所以也可以采用和其他系统差不多的方式，使用Shell脚本或者Ansible等工具打包发布到服务器上。&lt;/p&gt;
&lt;p&gt;不过Kubernetes里的这些组件的配置和相互关系实在是太复杂了，用Shell、Ansible来部署的难度很高，需要具有相当专业的运维管理知识才能配置、搭建好集群，而且即使这样，搭建的过程也非常麻烦。&lt;/p&gt;
&lt;p&gt;为了简化Kubernetes的部署工作，让它能够更“接地气”，社区里就出现了一个专门用来在集群中安装Kubernetes的工具，名字就叫“&lt;strong&gt;kubeadm&lt;/strong&gt;”，意思就是“Kubernetes管理员”。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/b250c17979b108267d32.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;kubeadm，原理和minikube类似，也是用容器和镜像来封装Kubernetes的各种组件，但它的目标不是单机部署，而是要能够轻松地在集群环境里部署Kubernetes，并且让这个集群接近甚至达到生产级质量。&lt;/p&gt;
&lt;p&gt;而在保持这个高水准的同时，kubeadm还具有了和minikube一样的易用性，只要很少的几条命令，如 init、join、upgrade、reset 就能够完成Kubernetes集群的管理维护工作，这让它不仅适用于集群管理员，也适用于开发、测试人员。&lt;/p&gt;
&lt;h2 id="实验环境的架构是什么样的"&gt;实验环境的架构是什么样的&lt;/h2&gt;
&lt;p&gt;在使用kubeadm搭建实验环境之前，我们先来看看集群的架构设计，也就是说要准备好集群所需的硬件设施。&lt;/p&gt;
&lt;p&gt;这里我画了一张系统架构图，图里一共有3台主机，当然它们都是使用虚拟机软件VirtualBox/VMWare虚拟出来的，下面我来详细说明一下：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/788b3c35e5f26c480929.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;所谓的多节点集群，要求服务器应该有两台或者更多，为了简化我们只取最小值，所以这个Kubernetes集群就只有两台主机，一台是Master节点，另一台是Worker节点。当然，在完全掌握了kubeadm的用法之后，你可以在这个集群里添加更多的节点。&lt;/p&gt;
&lt;p&gt;Master节点需要运行apiserver、etcd、scheduler、controller-manager等组件，管理整个集群，所以对配置要求比较高，至少是2核CPU、4GB的内存。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/fe3df9a5f181b54e0d17.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;而Worker节点没有管理工作，只运行业务应用，所以配置可以低一些，为了节省资源我给它分配了1核CPU和1GB的内存，可以说是低到不能再低了。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/67cfbbf50d309580352d.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;基于模拟生产环境的考虑，在Kubernetes集群之外还需要有一台起辅助作用的服务器。&lt;/p&gt;
&lt;p&gt;它的名字叫Console，意思是控制台，我们要在上面安装命令行工具kubectl，所有对Kubernetes集群的管理命令都是从这台主机发出去的。这也比较符合实际情况，因为安全的原因，集群里的主机部署好之后应该尽量少直接登录上去操作。&lt;/p&gt;
&lt;p&gt;要提醒你的是，Console这台主机只是逻辑上的概念，不一定要是独立，你在实际安装部署的时候完全可以复用之前minikube的虚拟机，或者直接使用Master/Worker节点作为控制台。&lt;/p&gt;
&lt;p&gt;这3台主机共同组成了我们的实验环境，所以在配置的时候要注意它们的网络选项，必须是在同一个网段，你可以再回顾一下[课前准备]，保证它们使用的是同一个“Host-Only”（VirtualBox）或者“自定”（VMWare Fusion）网络。&lt;/p&gt;
&lt;h2 id="安装前的准备工作"&gt;安装前的准备工作&lt;/h2&gt;
&lt;p&gt;不过有了架构图里的这些主机之后，我们还不能立即开始使用kubeadm安装Kubernetes，因为Kubernetes对系统有一些特殊要求，我们必须还要在Master和Worker节点上做一些准备。&lt;/p&gt;
&lt;p&gt;这些工作的详细信息你都可以在Kubernetes的官网上找到，但它们分散在不同的文档里，比较凌乱，所以我把它们整合到了这里，包括改主机名、改Docker配置、改网络设置、改交换分区这四步。&lt;/p&gt;
&lt;p&gt;第一，由于Kubernetes使用主机名来区分集群里的节点，所以每个节点的hostname必须不能重名。你需要修改“&lt;strong&gt;/etc/hostname&lt;/strong&gt;”这个文件，把它改成容易辨识的名字，比如Master节点就叫 master，Worker节点就叫 worker：&lt;/p&gt;
&lt;p&gt;第二，虽然Kubernetes目前支持多种容器运行时，但Docker还是最方便最易用的一种，所以我们仍然继续使用Docker作为Kubernetes的底层支持，使用 apt 安装Docker Engine（可参考[第1讲]）。&lt;/p&gt;
&lt;p&gt;安装完成后需要你再对Docker的配置做一点修改，在“&lt;strong&gt;/etc/docker/daemon.json&lt;/strong&gt;”里把cgroup的驱动程序改成 systemd ，然后重启Docker的守护进程，具体的操作我列在了下面：&lt;/p&gt;
&lt;p&gt;第三，为了让Kubernetes能够检查、转发网络流量，你需要修改iptables的配置，启用“br_netfilter”模块：&lt;/p&gt;
&lt;p&gt;第四，你需要修改“&lt;strong&gt;/etc/fstab&lt;/strong&gt;”，关闭Linux的swap分区，提升Kubernetes的性能：&lt;/p&gt;
&lt;p&gt;完成之后，最好记得重启一下系统，然后给虚拟机拍个快照做备份，避免后续的操作失误导致重复劳动。&lt;/p&gt;
&lt;h2 id="安装kubeadm"&gt;安装kubeadm&lt;/h2&gt;
&lt;p&gt;好，现在我们就要安装kubeadm了，在Master节点和Worker节点上都要做这一步。&lt;/p&gt;
&lt;p&gt;kubeadm可以直接从Google自己的软件仓库下载安装，但国内的网络不稳定，很难下载成功，需要改用其他的软件源，这里我选择了国内的某云厂商：&lt;/p&gt;
&lt;p&gt;更新了软件仓库，我们就可以用 apt install 获取kubeadm、kubelet和kubectl这三个安装必备工具了。apt默认会下载最新版本，但我们也可以指定版本号，比如使用和minikube相同的“1.23.3”：&lt;/p&gt;
&lt;p&gt;安装完成之后，你可以用 kubeadm version、kubectl version 来验证版本是否正确：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/d8b4212768f3ca062e79.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;另外按照Kubernetes官网的要求，我们最好再使用命令 apt-mark hold ，锁定这三个软件的版本，避免意外升级导致版本错误：&lt;/p&gt;</description></item><item><title>18 Deployment：让应用永不宕机</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/020/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/020/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/18%20Deployment%ef%bc%9a%e8%ae%a9%e5%ba%94%e7%94%a8%e6%b0%b8%e4%b8%8d%e5%ae%95%e6%9c%ba.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="18-deployment-让应用永不宕机"&gt;18 Deployment：让应用永不宕机&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在上一节课里，我们使用kubeadm搭建了一个由两个节点组成的小型Kubernetes集群，比起单机的minikube，它更接近真实环境，在这里面做实验我们今后也更容易过渡到生产系统。&lt;/p&gt;
&lt;p&gt;有了这个Kubernetes环境，接下来我们就在“初级篇”里学习的Pod知识基础上，深入研究一些由Pod衍生出来的其他API对象。&lt;/p&gt;
&lt;p&gt;今天要看的API对象名字叫“&lt;strong&gt;Deployment&lt;/strong&gt;”，顾名思义，它是专门用来部署应用程序的，能够让应用永不宕机，多用来发布无状态的应用，是Kubernetes里最常用也是最有用的一个对象。&lt;/p&gt;
&lt;h2 id="为什么要有deployment"&gt;为什么要有Deployment&lt;/h2&gt;
&lt;p&gt;在[第13讲]里，我们学习了API对象Job和CronJob，它们代表了生产环境中的离线业务，通过对Pod的包装，向Pod添加控制字段，实现了基于Pod运行临时任务和定时任务的功能。&lt;/p&gt;
&lt;p&gt;那么，除了“离线业务”，另一大类业务——也就是“在线业务”，在Kubernetes里应该如何处理呢？&lt;/p&gt;
&lt;p&gt;我们先看看用Pod是否就足够了。因为它在YAML里使用“&lt;strong&gt;containers&lt;/strong&gt;”就可以任意编排容器，而且还有一个“&lt;strong&gt;restartPolicy&lt;/strong&gt;”字段，默认值就是 Always，可以监控Pod里容器的状态，一旦发生异常，就会自动重启容器。&lt;/p&gt;
&lt;p&gt;不过，“restartPolicy”只能保证容器正常工作。不知你有没有想到，如果容器之外的Pod出错了该怎么办呢？比如说，有人不小心用 kubectl delete 误删了Pod，或者Pod运行的节点发生了断电故障，那么Pod就会在集群里彻底消失，对容器的控制也就无从谈起了。&lt;/p&gt;
&lt;p&gt;还有我们也都知道，在线业务远不是单纯启动一个Pod这么简单，还有多实例、高可用、版本更新等许多复杂的操作。比如最简单的多实例需求，为了提高系统的服务能力，应对突发的流量和压力，我们需要创建多个应用的副本，还要即时监控它们的状态。如果还是只使用Pod，那就会又走回手工管理的老路，没有利用好Kubernetes自动化运维的优势。&lt;/p&gt;
&lt;p&gt;其实，解决的办法也很简单，因为Kubernetes已经给我们提供了处理这种问题的思路，就是“单一职责”和“对象组合”。既然Pod管理不了自己，那么我们就再创建一个新的对象，由它来管理Pod，采用和Job/CronJob一样的形式——“对象套对象”。&lt;/p&gt;
&lt;p&gt;这个用来管理Pod，实现在线业务应用的新API对象，就是Deployment。&lt;/p&gt;
&lt;h2 id="如何使用yaml描述deployment"&gt;如何使用YAML描述Deployment&lt;/h2&gt;
&lt;p&gt;我们先用命令 kubectl api-resources 来看看Deployment的基本信息：&lt;/p&gt;
&lt;p&gt;从它的输出信息里可以知道，Deployment的简称是“&lt;strong&gt;deploy&lt;/strong&gt;”，它的apiVersion是“&lt;strong&gt;apps/v1&lt;/strong&gt;”，kind是“&lt;strong&gt;Deployment&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;所以，依据前面学习Pod、Job的经验，你就应该知道Deployment的YAML文件头该怎么写了：&lt;/p&gt;
&lt;p&gt;当然了，我们还是可以使用命令 kubectl create 来创建Deployment的YAML样板，免去反复手工输入的麻烦。&lt;/p&gt;
&lt;p&gt;创建Deployment样板的方式和Job也差不多，先指定类型是&lt;strong&gt;Deployment&lt;/strong&gt;（简写&lt;strong&gt;deploy&lt;/strong&gt;），然后是它的名字，再用 –image 参数指定镜像名字。&lt;/p&gt;
&lt;p&gt;比如下面的这条命令，我就创建了一个名字叫 ngx-dep 的对象，使用的镜像是 nginx:alpine：&lt;/p&gt;
&lt;p&gt;得到的Deployment样板大概是下面的这个样子：&lt;/p&gt;
&lt;p&gt;把它和Job/CronJob对比一下，你会发现有相似也有不同。相似的地方是都有“&lt;strong&gt;spec&lt;/strong&gt;”“&lt;strong&gt;template&lt;/strong&gt;”字段，“template”字段里也是一个Pod；不同的地方在于它的“spec”部分多了 replicas、selector 这两个新字段，聪明的你应该会猜到，这或许就会是Deployment特殊能力的根本。&lt;/p&gt;
&lt;p&gt;没错，这两个新字段就是Deployment实现多实例、高可用等功能的关键所在。&lt;/p&gt;
&lt;h2 id="deployment的关键字段"&gt;Deployment的关键字段&lt;/h2&gt;
&lt;p&gt;先看 replicas 字段。它的含义比较简单明了，就是“副本数量”的意思，也就是说，指定要在Kubernetes集群里运行多少个Pod实例。&lt;/p&gt;
&lt;p&gt;有了这个字段，就相当于为Kubernetes明确了应用部署的“期望状态”，Deployment对象就可以扮演运维监控人员的角色，自动地在集群里调整Pod的数量。&lt;/p&gt;
&lt;p&gt;比如，Deployment对象刚创建出来的时候，Pod数量肯定是0，那么它就会根据YAML文件里的Pod模板，逐个创建出要求数量的Pod。&lt;/p&gt;
&lt;p&gt;接下来Kubernetes还会持续地监控Pod的运行状态，万一有Pod发生意外消失了，数量不满足“期望状态”，它就会通过apiserver、scheduler等核心组件去选择新的节点，创建出新的Pod，直至数量与“期望状态”一致。&lt;/p&gt;
&lt;p&gt;这里面的工作流程很复杂，但对于我们这些外部用户来说，设置起来却是非常简单，只需要一个 replicas 字段就搞定了，不需要再用人工监控管理，整个过程完全自动化。&lt;/p&gt;
&lt;p&gt;下面我们再来看另一个关键字段 selector，它的作用是“筛选”出要被Deployment管理的Pod对象，下属字段“&lt;strong&gt;matchLabels&lt;/strong&gt;”定义了Pod对象应该携带的label，它必须和“template”里Pod定义的“labels”完全相同，否则Deployment就会找不到要控制的Pod对象，apiserver也会告诉你YAML格式校验错误无法创建。&lt;/p&gt;
&lt;p&gt;这个 selector 字段的用法初看起来好像是有点多余，为了保证Deployment成功创建，我们必须在YAML里把label重复写两次：一次是在“&lt;strong&gt;selector.matchLabels&lt;/strong&gt;”，另一次是在“&lt;strong&gt;template.matadata&lt;/strong&gt;”。像在这里，你就要在这两个地方连续写 app: ngx-dep ：&lt;/p&gt;
&lt;p&gt;你也许会产生疑问：为什么要这么麻烦？为什么不能像Job对象一样，直接用“template”里定义好的Pod就行了呢？&lt;/p&gt;
&lt;p&gt;这是因为在线业务和离线业务的应用场景差异很大。离线业务中的Pod基本上是一次性的，只与这个业务有关，紧紧地绑定在Job对象里，一般不会被其他对象所使用。&lt;/p&gt;
&lt;p&gt;而在线业务就要复杂得多了，因为Pod永远在线，除了要在Deployment里部署运行，还可能会被其他的API对象引用来管理，比如负责负载均衡的Service对象。&lt;/p&gt;
&lt;p&gt;所以Deployment和Pod实际上是一种松散的组合关系，Deployment实际上并不“持有”Pod对象，它只是帮助Pod对象能够有足够的副本数量运行，仅此而已。如果像Job那样，把Pod在模板里“写死”，那么其他的对象再想要去管理这些Pod就无能为力了。&lt;/p&gt;
&lt;p&gt;好明白了这一点，那我们该用什么方式来描述Deployment和Pod的组合关系呢？&lt;/p&gt;
&lt;p&gt;Kubernetes采用的是这种“贴标签”的方式，通过在API对象的“metadata”元信息里加各种标签（labels），我们就可以使用类似关系数据库里查询语句的方式，筛选出具有特定标识的那些对象。&lt;strong&gt;通过标签这种设计，Kubernetes就解除了Deployment和模板里Pod的强绑定，把组合关系变成了“弱引用”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;虽然话是这么说，但对于很多Kubernetes的初学者来说，理解Deployment里的spec定义还是一个难点。&lt;/p&gt;
&lt;p&gt;所以我还是画了一张图，用不同的颜色来区分Deployment YAML里的字段，并且用虚线特别标记了 matchLabels 和 labels 之间的联系，希望能够帮助你理解Deployment与被它管理的Pod的组合关系。&lt;/p&gt;</description></item><item><title>19 Daemonset：忠实可靠的看门狗</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/021/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/021/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/19%20Daemonset%ef%bc%9a%e5%bf%a0%e5%ae%9e%e5%8f%af%e9%9d%a0%e7%9a%84%e7%9c%8b%e9%97%a8%e7%8b%97.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="19-daemonset-忠实可靠的看门狗"&gt;19 Daemonset：忠实可靠的看门狗&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;上一次课里我们学习了Kubernetes里的一个新API对象Deployment，它代表了在线业务，能够管理多个Pod副本，让应用永远在线，还能够任意扩容缩容。&lt;/p&gt;
&lt;p&gt;虽然Deployment非常有用，但是，它并没有完全解决运维部署应用程序的所有难题。因为和简单的离线业务比起来，在线业务的应用场景太多太复杂，Deployment的功能特性只覆盖了其中的一部分，无法满足其他场景的需求。&lt;/p&gt;
&lt;p&gt;今天我们就来看看另一类代表在线业务API对象：&lt;strong&gt;DaemonSet&lt;/strong&gt;，它会在Kubernetes集群的每个节点上都运行一个Pod，就好像是Linux系统里的“守护进程”（Daemon）。&lt;/p&gt;
&lt;h2 id="为什么要有daemonset"&gt;为什么要有DaemonSet&lt;/h2&gt;
&lt;p&gt;想知道为什么Kubernetes会引入DaemonSet对象，那就得知道Deployment有哪些不足。&lt;/p&gt;
&lt;p&gt;我们先简单复习一下Deployment，它能够创建任意多个的Pod实例，并且维护这些Pod的正常运行，保证应用始终处于可用状态。&lt;/p&gt;
&lt;p&gt;但是，Deployment并不关心这些Pod会在集群的哪些节点上运行，&lt;strong&gt;在它看来，Pod的运行环境与功能是无关的，只要Pod的数量足够，应用程序应该会正常工作&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这个假设对于大多数业务来说是没问题的，比如Nginx、WordPress、MySQL，它们不需要知道集群、节点的细节信息，只要配置好环境变量和存储卷，在哪里“跑”都是一样的。&lt;/p&gt;
&lt;p&gt;但是有一些业务比较特殊，它们不是完全独立于系统运行的，而是与主机存在“绑定”关系，必须要依附于节点才能产生价值，比如说：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;网络应用（如kube-proxy），必须每个节点都运行一个Pod，否则节点就无法加入Kubernetes网络。&lt;/li&gt;
&lt;li&gt;监控应用（如Prometheus），必须每个节点都有一个Pod用来监控节点的状态，实时上报信息。&lt;/li&gt;
&lt;li&gt;日志应用（如Fluentd），必须在每个节点上运行一个Pod，才能够搜集容器运行时产生的日志数据。&lt;/li&gt;
&lt;li&gt;安全应用，同样的，每个节点都要有一个Pod来执行安全审计、入侵检查、漏洞扫描等工作。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些业务如果用Deployment来部署就不太合适了，因为Deployment所管理的Pod数量是固定的，而且可能会在集群里“漂移”，但，实际的需求却是要在集群里的每个节点上都运行Pod，也就是说Pod的数量与节点数量保持同步。&lt;/p&gt;
&lt;p&gt;所以，Kubernetes就定义了新的API对象DaemonSet，它在形式上和Deployment类似，都是管理控制Pod，但管理调度策略却不同。DaemonSet的目标是在集群的每个节点上运行且仅运行一个Pod，就好像是为节点配上一只“看门狗”，忠实地“守护”着节点，这就是DaemonSet名字的由来。&lt;/p&gt;
&lt;h2 id="如何使用yaml描述daemonset"&gt;如何使用YAML描述DaemonSet&lt;/h2&gt;
&lt;p&gt;DaemonSet和Deployment都属于在线业务，所以它们也都是“apps”组，使用命令 kubectl api-resources 可以知道它的简称是 ds ，YAML文件头信息应该是：&lt;/p&gt;
&lt;p&gt;不过非常奇怪，kubectl 不提供自动创建DaemonSet YAML样板的功能，也就是说，我们不能用命令 kubectl create 直接创建出一个DaemonSet对象。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/7607eecfdcef7a1746c7.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;这个缺点对于我们使用DaemonSet的确造成了不小的麻烦，毕竟如果用 kubectl explain 一个个地去查字段再去写YAML实在是太辛苦了。&lt;/p&gt;
&lt;p&gt;不过，Kubernetes不给我们生成样板文件的机会，我们也可以自己去“抄”。你可以在Kubernetes的官网（&lt;a href="https://kubernetes.io/zh/docs/concepts/workloads/controllers/daemonset/" rel="nofollow noreferrer noopener"&gt;https://kubernetes.io/zh/docs/concepts/workloads/controllers/daemonset/&lt;/a&gt;）上找到一份DaemonSet的YAML示例，把它拷贝下来，再去掉多余的部分，就可以做成自己的一份样板文件，大概是下面的这个样子：&lt;/p&gt;
&lt;p&gt;这个DaemonSet对象的名字是 redis-ds，镜像是 redis:5-alpine，使用了流行的NoSQL数据库Redis（你也许对它很熟悉）。&lt;/p&gt;
&lt;p&gt;把这份YAML和上节课里的Deployment对象简单对比一下，你会发现：&lt;/p&gt;
&lt;p&gt;前面的 kind、metadata 是对象独有的信息，自然是不同的，但下面的 spec 部分，DaemonSet也有 selector 字段，匹配 template 里Pod的 labels 标签，和Deployment对象几乎一模一样。&lt;/p&gt;
&lt;p&gt;再仔细观察，我们就会看到，DaemonSet在 spec 里没有 replicas 字段，这是它与Deployment的一个关键不同点，意味着它不会在集群里创建多个Pod副本，而是要在每个节点上只创建出一个Pod实例。&lt;/p&gt;
&lt;p&gt;也就是说，DaemonSet仅仅是在Pod的部署调度策略上和Deployment不同，其他的都是相同的，某种程度上我们也可以把DaemonSet看做是Deployment的一个特例。&lt;/p&gt;
&lt;p&gt;我还是把YAML描述文件画了一张图，好让你看清楚与Deployment的差异：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/7be8bbac8b3fe0e1c9dd.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;了解到这些区别，现在，我们就可以用变通的方法来创建DaemonSet的YAML样板了，你只需要用 kubectl create 先创建出一个Deployment对象，然后把 kind 改成 DaemonSet，再删除 spec.replicas 就行了，比如：&lt;/p&gt;</description></item><item><title>20 Service：微服务架构的应对之道</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/022/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/022/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/20%20Service%ef%bc%9a%e5%be%ae%e6%9c%8d%e5%8a%a1%e6%9e%b6%e6%9e%84%e7%9a%84%e5%ba%94%e5%af%b9%e4%b9%8b%e9%81%93.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="20-service-微服务架构的应对之道"&gt;20 Service：微服务架构的应对之道&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在前面的课里我们学习了Deployment和DaemonSet这两个API对象，它们都是在线业务，只是以不同的策略部署应用，Deployment创建任意多个实例，Daemon为每个节点创建一个实例。&lt;/p&gt;
&lt;p&gt;这两个API对象可以部署多种形式的应用，而在云原生时代，微服务无疑是应用的主流形态。为了更好地支持微服务以及服务网格这样的应用架构，Kubernetes又专门定义了一个新的对象：Service，它是集群内部的负载均衡机制，用来解决服务发现的关键问题。&lt;/p&gt;
&lt;p&gt;今天我们就来看看什么是Service、如何使用YAML来定义Service，以及如何在Kubernetes里用好Service。&lt;/p&gt;
&lt;h2 id="为什么要有service"&gt;为什么要有Service&lt;/h2&gt;
&lt;p&gt;有了Deployment和DaemonSet，我们在集群里发布应用程序的工作轻松了很多。借助Kubernetes强大的自动化运维能力，我们可以把应用的更新上线频率由以前的月、周级别提升到天、小时级别，让服务质量更上一层楼。&lt;/p&gt;
&lt;p&gt;不过，在应用程序快速版本迭代的同时，另一个问题也逐渐显现出来了，就是“&lt;strong&gt;服务发现&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;在Kubernetes集群里Pod的生命周期是比较“短暂”的，虽然Deployment和DaemonSet可以维持Pod总体数量的稳定，但在运行过程中，难免会有Pod销毁又重建，这就会导致Pod集合处于动态的变化之中。&lt;/p&gt;
&lt;p&gt;这种“动态稳定”对于现在流行的微服务架构来说是非常致命的，试想一下，后台Pod的IP地址老是变来变去，客户端该怎么访问呢？如果不处理好这个问题，Deployment和DaemonSet把Pod管理得再完善也是没有价值的。&lt;/p&gt;
&lt;p&gt;其实，这个问题也并不是什么难事，业内早就有解决方案来针对这样“不稳定”的后端服务，那就是“&lt;strong&gt;负载均衡&lt;/strong&gt;”，典型的应用有LVS、Nginx等等。它们在前端与后端之间加入了一个“中间层”，屏蔽后端的变化，为前端提供一个稳定的服务。&lt;/p&gt;
&lt;p&gt;但LVS、Nginx毕竟不是云原生技术，所以Kubernetes就按照这个思路，定义了新的API对象：&lt;strong&gt;Service&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;所以估计你也能想到，Service的工作原理和LVS、Nginx差不多，Kubernetes会给它分配一个静态IP地址，然后它再去自动管理、维护后面动态变化的Pod集合，当客户端访问Service，它就根据某种策略，把流量转发给后面的某个Pod。&lt;/p&gt;
&lt;p&gt;下面的这张图来自Kubernetes&lt;a href="https://kubernetes.io/zh/docs/concepts/services-networking/service/" rel="nofollow noreferrer noopener"&gt;官网文档&lt;/a&gt;，比较清楚地展示了Service的工作原理：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/cdd1f83c8f5fad548408.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;你可以看到，这里Service使用了iptables技术，每个节点上的kube-proxy组件自动维护iptables规则，客户不再关心Pod的具体地址，只要访问Service的固定IP地址，Service就会根据iptables规则转发请求给它管理的多个Pod，是典型的负载均衡架构。&lt;/p&gt;
&lt;p&gt;不过Service并不是只能使用iptables来实现负载均衡，它还有另外两种实现技术：性能更差的userspace和性能更好的ipvs，但这些都属于底层细节，我们不需要刻意关注。&lt;/p&gt;
&lt;h2 id="如何使用yaml描述service"&gt;如何使用YAML描述Service&lt;/h2&gt;
&lt;p&gt;知道了Service的基本工作原理，我们来看看怎么为Service编写YAML描述文件。&lt;/p&gt;
&lt;p&gt;照例我们还是可以用命令 kubectl api-resources 查看它的基本信息，可以知道它的简称是svc，apiVersion是 v1。&lt;strong&gt;注意，这说明它与Pod一样，属于Kubernetes的核心对象，不关联业务应用，与Job、Deployment是不同的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;现在，相信你很容易写出Service的YAML文件头了吧：&lt;/p&gt;
&lt;p&gt;同样的，能否让Kubernetes为我们自动创建Service的YAML样板呢？还是使用命令 kubectl create 吗？&lt;/p&gt;
&lt;p&gt;这里Kubernetes又表现出了行为上的不一致。&lt;strong&gt;虽然它可以自动创建YAML样板，但不是用命令&lt;/strong&gt; kubectl create&lt;strong&gt;，而是另外一个命令&lt;/strong&gt; kubectl expose，也许Kubernetes认为“expose”能够更好地表达Service“暴露”服务地址的意思吧。&lt;/p&gt;
&lt;p&gt;因为在Kubernetes里提供服务的是Pod，而Pod又可以用Deployment/DaemonSet对象来部署，所以 kubectl expose 支持从多种对象创建服务，Pod、Deployment、DaemonSet都可以。&lt;/p&gt;
&lt;p&gt;使用 kubectl expose 指令时还需要用参数 –port 和 –target-port 分别指定映射端口和容器端口，而Service自己的IP地址和后端Pod的IP地址可以自动生成，用法上和Docker的命令行参数 -p 很类似，只是略微麻烦一点。&lt;/p&gt;
&lt;p&gt;比如，如果我们要为[第18讲]里的ngx-dep对象生成Service，命令就要这么写：&lt;/p&gt;
&lt;p&gt;生成的Service YAML大概是这样的：&lt;/p&gt;
&lt;p&gt;你会发现，Service的定义非常简单，在“spec”里只有两个关键字段，selector 和 ports。&lt;/p&gt;
&lt;p&gt;selector 和Deployment/DaemonSet里的作用是一样的，用来过滤出要代理的那些Pod。因为我们指定要代理Deployment，所以Kubernetes就为我们自动填上了ngx-dep的标签，会选择这个Deployment对象部署的所有Pod。&lt;/p&gt;
&lt;p&gt;从这里你也可以看到，Kubernetes的这个标签机制虽然很简单，却非常强大有效，很轻松就关联上了Deployment的Pod。&lt;/p&gt;
&lt;p&gt;ports 就很好理解了，里面的三个字段分别表示外部端口、内部端口和使用的协议，在这里就是内外部都使用80端口，协议是TCP。&lt;/p&gt;
&lt;p&gt;当然，你在这里也可以把 ports 改成“8080”等其他的端口，这样外部服务看到的就是Service给出的端口，而不会知道Pod的真正服务端口。&lt;/p&gt;
&lt;p&gt;为了让你看清楚Service与它引用的Pod的关系，我把这两个YAML对象画在了下面的这张图里，需要重点关注的是 selector、targetPort 与Pod的关联：&lt;/p&gt;</description></item><item><title>21 Ingress：集群进出流量的总管</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/023/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/023/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/21%20Ingress%ef%bc%9a%e9%9b%86%e7%be%a4%e8%bf%9b%e5%87%ba%e6%b5%81%e9%87%8f%e7%9a%84%e6%80%bb%e7%ae%a1.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="21-ingress-集群进出流量的总管"&gt;21 Ingress：集群进出流量的总管&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;上次课里我们学习了Service对象，它是Kubernetes内置的负载均衡机制，使用静态IP地址代理动态变化的Pod，支持域名访问和服务发现，是微服务架构必需的基础设施。&lt;/p&gt;
&lt;p&gt;Service很有用，但也只能说是“基础设施”，它对网络流量的管理方案还是太简单，离复杂的现代应用架构需求还有很大的差距，所以Kubernetes就在Service之上又提出了一个新的概念：Ingress。&lt;/p&gt;
&lt;p&gt;比起Service，Ingress更接近实际业务，对它的开发、应用和讨论也是社区里最火爆的，今天我们就来看看Ingress，还有与它关联的Ingress Controller、Ingress Class等对象。&lt;/p&gt;
&lt;h2 id="为什么要有ingress"&gt;为什么要有Ingress&lt;/h2&gt;
&lt;p&gt;通过上次课程的讲解，我们知道了Service的功能和运行机制，它本质上就是一个由kube-proxy控制的四层负载均衡，在TCP/IP协议栈上转发流量（&lt;a href="https://kubernetes.io/zh/docs/concepts/services-networking/service/" rel="nofollow noreferrer noopener"&gt;Service工作原理示意图&lt;/a&gt;）：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/cdd1f83c8f5fad548408.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;但在四层上的负载均衡功能还是太有限了，只能够依据IP地址和端口号做一些简单的判断和组合，而我们现在的绝大多数应用都是跑在七层的HTTP/HTTPS协议上的，有更多的高级路由条件，比如主机名、URI、请求头、证书等等，而这些在TCP/IP网络栈里是根本看不见的。&lt;/p&gt;
&lt;p&gt;Service还有一个缺点，它比较适合代理集群内部的服务。如果想要把服务暴露到集群外部，就只能使用NodePort或者LoadBalancer这两种方式，而它们都缺乏足够的灵活性，难以管控，这就导致了一种很无奈的局面：我们的服务空有一身本领，却没有合适的机会走出去大展拳脚。&lt;/p&gt;
&lt;p&gt;该怎么解决这个问题呢？&lt;/p&gt;
&lt;p&gt;Kubernetes还是沿用了Service的思路，既然Service是四层的负载均衡，那么我再引入一个新的API对象，在七层上做负载均衡是不是就可以了呢？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不过除了七层负载均衡，这个对象还应该承担更多的职责，也就是作为流量的总入口，统管集群的进出口数据&lt;/strong&gt;，“扇入”“扇出”流量（也就是我们常说的“南北向”），让外部用户能够安全、顺畅、便捷地访问内部服务（&lt;a href="https://bishoylabib.com/exposing-your-application-to-the-public-ingress/" rel="nofollow noreferrer noopener"&gt;图片来源&lt;/a&gt;）：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/b3c35d02e2fc357aa81a.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;所以，这个API对象就顺理成章地被命名为 Ingress，意思就是集群内外边界上的入口。&lt;/p&gt;
&lt;h2 id="为什么要有ingress-controller"&gt;为什么要有Ingress Controller&lt;/h2&gt;
&lt;p&gt;再对比一下Service我们就能更透彻地理解Ingress。&lt;/p&gt;
&lt;p&gt;Ingress可以说是在七层上另一种形式的Service，它同样会代理一些后端的Pod，也有一些路由规则来定义流量应该如何分配、转发，只不过这些规则都使用的是HTTP/HTTPS协议。&lt;/p&gt;
&lt;p&gt;你应该知道，Service本身是没有服务能力的，它只是一些iptables规则，&lt;strong&gt;真正配置、应用这些规则的实际上是节点里的kube-proxy组件&lt;/strong&gt;。如果没有kube-proxy，Service定义得再完善也没有用。&lt;/p&gt;
&lt;p&gt;同样的，Ingress也只是一些HTTP路由规则的集合，相当于一份静态的描述文件，真正要把这些规则在集群里实施运行，还需要有另外一个东西，这就是 Ingress Controller，它的作用就相当于Service的kube-proxy，能够读取、应用Ingress规则，处理、调度流量。&lt;/p&gt;
&lt;p&gt;按理来说，Kubernetes应该把Ingress Controller内置实现，作为基础设施的一部分，就像kube-proxy一样。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不过Ingress Controller要做的事情太多，与上层业务联系太密切，所以Kubernetes把Ingress Controller的实现交给了社区&lt;/strong&gt;，任何人都可以开发Ingress Controller，只要遵守Ingress规则就好。&lt;/p&gt;
&lt;p&gt;这就造成了Ingress Controller“百花齐放”的盛况。&lt;/p&gt;
&lt;p&gt;由于Ingress Controller把守了集群流量的关键入口，掌握了它就拥有了控制集群应用的“话语权”，所以众多公司纷纷入场，精心打造自己的Ingress Controller，意图在Kubernetes流量进出管理这个领域占有一席之地。&lt;/p&gt;
&lt;p&gt;这些实现中最著名的，就是老牌的反向代理和负载均衡软件Nginx了。从Ingress Controller的描述上我们也可以看到，HTTP层面的流量管理、安全控制等功能其实就是经典的反向代理，而Nginx则是其中稳定性最好、性能最高的产品，所以它也理所当然成为了Kubernetes里应用得最广泛的Ingress Controller。&lt;/p&gt;
&lt;p&gt;不过，因为Nginx是开源的，谁都可以基于源码做二次开发，所以它又有很多的变种，比如社区的Kubernetes Ingress Controller（&lt;a href="https://github.com/kubernetes/ingress-nginx" rel="nofollow noreferrer noopener"&gt;https://github.com/kubernetes/ingress-nginx&lt;/a&gt;）、Nginx公司自己的Nginx Ingress Controller（&lt;a href="https://github.com/nginxinc/kubernetes-ingress" rel="nofollow noreferrer noopener"&gt;https://github.com/nginxinc/kubernetes-ingress&lt;/a&gt;）、还有基于OpenResty的Kong Ingress Controller（&lt;a href="https://github.com/Kong/kubernetes-ingress-controller" rel="nofollow noreferrer noopener"&gt;https://github.com/Kong/kubernetes-ingress-controller&lt;/a&gt;）等等。&lt;/p&gt;
&lt;p&gt;根据Docker Hub上的统计，&lt;strong&gt;Nginx公司的开发实现是下载量最多的Ingress Controller&lt;/strong&gt;，所以我将以它为例，讲解Ingress和Ingress Controller的用法。&lt;/p&gt;
&lt;p&gt;下面的&lt;a href="https://www.nginx.com/products/nginx-ingress-controller/" rel="nofollow noreferrer noopener"&gt;这张图&lt;/a&gt;就来自Nginx官网，比较清楚地展示了Ingress Controller在Kubernetes集群里的地位：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/80951f11ca3a4b27374a.webp" alt="图片"&gt;&lt;/p&gt;
&lt;h2 id="为什么要有ingressclass"&gt;为什么要有IngressClass&lt;/h2&gt;
&lt;p&gt;那么到现在，有了Ingress和Ingress Controller，我们是不是就可以完美地管理集群的进出流量了呢？&lt;/p&gt;</description></item><item><title>22 实战演练：玩转Kubernetes（2）</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/024/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/024/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/22%20%e5%ae%9e%e6%88%98%e6%bc%94%e7%bb%83%ef%bc%9a%e7%8e%a9%e8%bd%acKubernetes%ef%bc%882%ef%bc%89.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="22-实战演练-玩转kubernetes-2"&gt;22 实战演练：玩转Kubernetes（2）&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;我们的“中级篇”到今天马上就要结束了，感谢你这段时间坚持不懈的学习。&lt;/p&gt;
&lt;p&gt;作为“中级篇”的收尾课程，我照例还是会对前面学过的内容做一个全面的回顾和总结，把知识点都串联起来，加深你对它们的印象。&lt;/p&gt;
&lt;p&gt;下面我先梳理一下“中级篇”里讲过的Kubernetes知识要点，然后是实战演示，搭建WordPress网站。当然这次比前两次又有进步，不用Docker，也不用裸Pod，而是用我们新学习的Deployment、Service、Ingress等对象。&lt;/p&gt;
&lt;h2 id="kubernetes技术要点回顾"&gt;Kubernetes技术要点回顾&lt;/h2&gt;
&lt;p&gt;Kubernetes是云原生时代的操作系统，它能够管理大量节点构成的集群，让计算资源“池化”，从而能够自动地调度运维各种形式的应用。&lt;/p&gt;
&lt;p&gt;搭建多节点的Kubernetes集群是一件颇具挑战性的工作，好在社区里及时出现了kubeadm这样的工具，可以“一键操作”，使用 kubeadm init、kubeadm join 等命令从无到有地搭建出生产级别的集群（[17讲]）。&lt;/p&gt;
&lt;p&gt;kubeadm使用容器技术封装了Kubernetes组件，所以只要节点上安装了容器运行时（Docker、containerd等），它就可以自动从网上拉取镜像，然后以容器的方式运行组件，非常简单方便。&lt;/p&gt;
&lt;p&gt;在这个更接近实际生产环境的Kubernetes集群里，我们学习了&lt;strong&gt;Deployment、DaemonSet、Service、Ingress、Ingress Controller&lt;/strong&gt;等API对象。&lt;/p&gt;
&lt;p&gt;（[18讲]）Deployment是用来管理Pod的一种对象，它代表了运维工作中最常见的一类在线业务，在集群中部署应用的多个实例，而且可以很容易地增加或者减少实例数量，从容应对流量压力。&lt;/p&gt;
&lt;p&gt;Deployment的定义里有两个关键字段：一个是 replicas，它指定了实例的数量；另一个是 selector，它的作用是使用标签“筛选”出被Deployment管理的Pod，这是一种非常灵活的关联机制，实现了API对象之间的松耦合。&lt;/p&gt;
&lt;p&gt;（[19讲]）DaemonSet是另一种部署在线业务的方式，它很类似Deployment，但会在集群里的每一个节点上运行一个Pod实例，类似Linux系统里的“守护进程”，适合日志、监控等类型的应用。&lt;/p&gt;
&lt;p&gt;DaemonSet能够任意部署Pod的关键概念是“污点”（taint）和“容忍度”（toleration）。Node会有各种“污点”，而Pod可以使用“容忍度”来忽略“污点”，合理使用这两个概念就可以调整Pod在集群里的部署策略。&lt;/p&gt;
&lt;p&gt;（[20讲]）由Deployment和DaemonSet部署的Pod，在集群中处于“动态平衡”的状态，总数量保持恒定，但也有临时销毁重建的可能，所以IP地址是变化的，这就为微服务等应用架构带来了麻烦。&lt;/p&gt;
&lt;p&gt;Service是对Pod IP地址的抽象，它拥有一个固定的IP地址，再使用iptables规则把流量负载均衡到后面的Pod，节点上的kube-proxy组件会实时维护被代理的Pod状态，保证Service只会转发给健康的Pod。&lt;/p&gt;
&lt;p&gt;Service还基于DNS插件支持域名，所以客户端就不再需要关心Pod的具体情况，只要通过Service这个稳定的中间层，就能够访问到Pod提供的服务。&lt;/p&gt;
&lt;p&gt;（[21讲]）Service是四层的负载均衡，但现在的绝大多数应用都是HTTP/HTTPS协议，要实现七层的负载均衡就要使用Ingress对象。&lt;/p&gt;
&lt;p&gt;Ingress定义了基于HTTP协议的路由规则，但要让规则生效，还需要&lt;strong&gt;Ingress Controller&lt;/strong&gt;和&lt;strong&gt;Ingress Class&lt;/strong&gt;来配合工作。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ingress Controller是真正的集群入口，应用Ingress规则调度、分发流量，此外还能够扮演反向代理的角色，提供安全防护、TLS卸载等更多功能。&lt;/li&gt;
&lt;li&gt;Ingress Class是用来管理Ingress和Ingress Controller的概念，方便我们分组路由规则，降低维护成本。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不过Ingress Controller本身也是一个Pod，想要把服务暴露到集群外部还是要依靠Service。Service支持NodePort、LoadBalancer等方式，但NodePort的端口范围有限，LoadBalancer又依赖于云服务厂商，都不是很灵活。&lt;/p&gt;
&lt;p&gt;折中的办法是用少量NodePort暴露Ingress Controller，用Ingress路由到内部服务，外部再用反向代理或者LoadBalancer把流量引进来。&lt;/p&gt;
&lt;h2 id="wordpress网站基本架构"&gt;WordPress网站基本架构&lt;/h2&gt;
&lt;p&gt;简略回顾了Kubernetes里这些API对象，下面我们就来使用它们再搭建出WordPress网站，实践加深理解。&lt;/p&gt;
&lt;p&gt;既然我们已经掌握了Deployment、Service、Ingress这些Pod之上的概念，网站自然会有新变化，架构图我放在了这里：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/4faf47706b16f2a75aac.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;这次的部署形式比起Docker、minikube又有了一些细微的差别，&lt;strong&gt;重点是我们已经完全舍弃了Docker，把所有的应用都放在Kubernetes集群里运行，部署方式也不再是裸Pod，而是使用Deployment，稳定性大幅度提升&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;原来的Nginx的作用是反向代理，那么在Kubernetes里它就升级成了具有相同功能的Ingress Controller。WordPress原来只有一个实例，现在变成了两个实例（你也可以任意横向扩容），可用性也就因此提高了不少。而MariaDB数据库因为要保证数据的一致性，暂时还是一个实例。&lt;/p&gt;
&lt;p&gt;还有，因为Kubernetes内置了服务发现机制Service，我们再也不需要去手动查看Pod的IP地址了，只要为它们定义Service对象，然后使用域名就可以访问MariaDB、WordPress这些服务。&lt;/p&gt;
&lt;p&gt;网站对外提供服务我选择了两种方式。&lt;/p&gt;
&lt;p&gt;一种是让WordPress的Service对象以NodePort的方式直接对外暴露端口30088，方便测试；另一种是给Nginx Ingress Controller添加“hostNetwork”属性，直接使用节点上的端口号，类似Docker的host网络模式，好处是可以避开NodePort的端口范围限制。&lt;/p&gt;
&lt;p&gt;下面我们就按照这个基本架构来逐步搭建出新版本的WordPress网站，编写YAML声明。&lt;/p&gt;
&lt;p&gt;这里有个小技巧，在实际操作的时候你一定要记得善用 kubectl create、kubectl expose 创建样板文件，节约时间的同时，也能避免低级的格式错误。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WordPress网站部署MariaDB————————
首先我们还是要部署MariaDB，这个步骤和在[第15讲]里做的也差不多。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;先要用ConfigMap定义数据库的环境变量，有 DATABASE、USER、PASSWORD、ROOT_PASSWORD：&lt;/p&gt;
&lt;p&gt;然后我们需要&lt;strong&gt;把MariaDB由Pod改成Deployment的方式&lt;/strong&gt;，replicas设置成1个，template里面的Pod部分没有任何变化，还是要用 envFrom把配置信息以环境变量的形式注入Pod，相当于把Pod套了一个Deployment的“外壳”：&lt;/p&gt;
&lt;p&gt;我们还需要再&lt;strong&gt;为MariaDB定义一个Service对象&lt;/strong&gt;，映射端口3306，让其他应用不再关心IP地址，直接用Service对象的名字来访问数据库服务：&lt;/p&gt;
&lt;p&gt;因为这三个对象都是数据库相关的，所以可以在一个YAML文件里书写，对象之间用 — 分开，这样用 kubectl apply 就可以一次性创建好：&lt;/p&gt;</description></item><item><title>23 视频：中级篇实操总结</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/025/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/025/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/23%20%e8%a7%86%e9%a2%91%ef%bc%9a%e4%b8%ad%e7%ba%a7%e7%af%87%e5%ae%9e%e6%93%8d%e6%80%bb%e7%bb%93.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="23-视频-中级篇实操总结"&gt;23 视频：中级篇实操总结&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;到今天我们“中级篇”的学习也告一段落了，在这段时间里我们使用kubeadm搭建了更接近生产环境的多节点Kubernetes集群，然后学习了Deployment、DaemonSet、Service、Ingress这4个非常重要的API对象。&lt;/p&gt;
&lt;p&gt;这节课作为对“中级篇”的总结和回顾，仍然是视频的形式，把前面几节课里的一些操作都实际演示出来，方便你对照着操作上下文来学习。&lt;/p&gt;
&lt;p&gt;首先我会带你看看kubeadm搭建集群的完整过程，然后编写YAML文件，示范Deployment、DaemonSet、Service、Ingress等对象的具体用法，最后是使用这些对象搭建WordPress网站。&lt;/p&gt;
&lt;p&gt;让我们开始视频课程的学习吧。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="一-安装kubeadm"&gt;一. 安装kubeadm&lt;/h3&gt;
&lt;p&gt;我们先来安装Master节点，当然在这之前你需要保证已经安装好了Docker（安装参考[第1讲]）。&lt;/p&gt;
&lt;p&gt;首先四步准备工作：改主机名、改Docker配置、改网络设置、改交换分区。&lt;/p&gt;
&lt;p&gt;修改主机名，把它改成 master：&lt;/p&gt;
&lt;p&gt;修改Docker的配置文件，把cgroup的驱动程序改成 systemd，然后是修改iptables的配置，启用 br_netfilter 模块，还有修改“&lt;strong&gt;/etc/fstab&lt;/strong&gt;”，关闭Linux的swap分区。&lt;/p&gt;
&lt;p&gt;这些操作步骤都已经列在了[第17讲]里了，因为一个个文件修改很麻烦，我就写了一个脚本文件，把这些操作都自动化了，大概看一下：&lt;/p&gt;
&lt;p&gt;第一部分是修改Docker配置文件然后重启，第二部分是修改iptables配置，第三部分是关闭Linux的swap分区。我们执行这个脚本，安装的准备工作就做好了。&lt;/p&gt;
&lt;p&gt;接下来，下载kubeadm可执行文件，同样我也写成了一个方便的脚本文件：&lt;/p&gt;
&lt;p&gt;基本流程和Kubernetes官方文档是一样的，只是改成了国内的软件源。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意在使用 apt install 的时候，应该明确指定要安装的版本号&lt;/strong&gt;，像这里我就使用的是Kubernetes 1.23.3，如果不指定就会使用当前的最新版本，也就是1.24。&lt;/p&gt;
&lt;p&gt;安装完成之后，我们用 kubeadm version、kubectl version 来看一下版本是否正确：&lt;/p&gt;
&lt;p&gt;可以看到确实是安装了我们刚才指定的1.23.3版本。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="二-安装kubernetes"&gt;二. 安装Kubernetes&lt;/h3&gt;
&lt;p&gt;有了kubeadm，我们就可以开始正式安装Kubernetes了。安装时需要从gcr.io拉取镜像，我这里从国内镜像网站上提前下载好了镜像，给你看一下：&lt;/p&gt;
&lt;p&gt;这些镜像中包括Kubernetes的核心组件，比如etcd、apiserver、controller-manager。&lt;/p&gt;
&lt;p&gt;现在我们来执行 kubeadm init 初始化Master节点，看一下脚本文件：&lt;/p&gt;
&lt;p&gt;初始化的时候我使用了参数 pod-network-cidr 指定Pod的IP地址范围是“10.10.0.0/16”，然后Kubernetes版本也指定使用1.23.3。&lt;/p&gt;
&lt;p&gt;为了让kubectl在安装完成后能正常工作，脚本里还会把Kubernetes的配置文件拷贝到当前用户的 .kube 目录下。&lt;/p&gt;
&lt;p&gt;好了，现在我们就运行这个脚本。&lt;/p&gt;
&lt;p&gt;注意Master节点初始化完成后，kubeadm会给出一些重要的提示信息，一个是拷贝配置文件，这个我们已经在脚本里做过了，&lt;strong&gt;另一个是Worker节点加入集群的 kubeadm join 命令，一定要保存好。我们可以新建一个文件，比如k.txt，然后把它存下来。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我们再用 kubectl version、kubectl get node 来检查Kubernetes的版本和集群的节点状态了：&lt;/p&gt;
&lt;p&gt;Master节点的状态是“NotReady”，接下来还要安装网络插件Flannel。&lt;/p&gt;
&lt;p&gt;Flannel的安装很简单，记得要修改它的YAML文件，“&lt;strong&gt;net-conf.json&lt;/strong&gt;”里的网络范围必须和 kubeadm init 里的一样：&lt;/p&gt;
&lt;p&gt;现在我们用 kubectl apply 来安装Flannel网络：&lt;/p&gt;
&lt;p&gt;稍等一小会，我们再执行 kubectl get node 来看节点状态：&lt;/p&gt;</description></item><item><title>24 PersistentVolume：怎么解决数据持久化的难题？</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/026/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/026/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/24%20PersistentVolume%ef%bc%9a%e6%80%8e%e4%b9%88%e8%a7%a3%e5%86%b3%e6%95%b0%e6%8d%ae%e6%8c%81%e4%b9%85%e5%8c%96%e7%9a%84%e9%9a%be%e9%a2%98%ef%bc%9f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="24-persistentvolume-怎么解决数据持久化的难题"&gt;24 PersistentVolume：怎么解决数据持久化的难题？&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;经过了“初级篇”和“中级篇”的学习，相信你对Kubernetes的认识已经比较全面了，那么在接下来的“高级篇”里，我们再进一步，探索Kubernetes更深层次的知识点和更高级的应用技巧。&lt;/p&gt;
&lt;p&gt;今天就先从PersistentVolume讲起。&lt;/p&gt;
&lt;p&gt;早在[第14讲]介绍ConfigMap/Secret的时候，我们就遇到过Kubernetes里的Volume存储卷的概念，它使用字段 volumes 和 volumeMounts，相当于是给Pod挂载了一个“虚拟盘”，把配置信息以文件的形式注入进Pod供进程使用。&lt;/p&gt;
&lt;p&gt;不过，那个时候的Volume只能存放较少的数据，离真正的“虚拟盘”还差得很远。&lt;/p&gt;
&lt;p&gt;今天我们就一起来了解Volume的高级用法，看看Kubernetes管理存储资源的API对象PersistentVolume、PersistentVolumeClaim、StorageClass，然后使用本地磁盘来创建实际可用的存储卷。&lt;/p&gt;
&lt;h2 id="什么是persistentvolume"&gt;什么是PersistentVolume&lt;/h2&gt;
&lt;p&gt;在刚完成的“中级篇”实战中（[22讲]），我们在Kubernetes集群里搭建了WordPress网站，但其中存在一个很严重的问题：Pod没有持久化功能，导致MariaDB无法“永久”存储数据。&lt;/p&gt;
&lt;p&gt;因为Pod里的容器是由镜像产生的，而镜像文件本身是只读的，进程要读写磁盘只能用一个临时的存储空间，一旦Pod销毁，临时存储也就会立即回收释放，数据也就丢失了。&lt;/p&gt;
&lt;p&gt;为了保证即使Pod销毁后重建数据依然存在，我们就需要找出一个解决方案，让Pod用上真正的“虚拟盘”。怎么办呢？&lt;/p&gt;
&lt;p&gt;其实，Kubernetes的Volume对数据存储已经给出了一个很好的抽象，它只是定义了有这么一个“存储卷”，而这个“存储卷”是什么类型、有多大容量、怎么存储，我们都可以自由发挥。Pod不需要关心那些专业、复杂的细节，只要设置好 volumeMounts，就可以把Volume加载进容器里使用。&lt;/p&gt;
&lt;p&gt;所以，Kubernetes就顺着Volume的概念，延伸出了&lt;strong&gt;PersistentVolume&lt;/strong&gt;对象，它专门用来表示持久存储设备，但隐藏了存储的底层实现，我们只需要知道它能安全可靠地保管数据就可以了（由于PersistentVolume这个词很长，一般都把它简称为PV）。&lt;/p&gt;
&lt;p&gt;那么，集群里的PV都从哪里来呢？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;作为存储的抽象，PV实际上就是一些存储设备、文件系统&lt;/strong&gt;，比如Ceph、GlusterFS、NFS，甚至是本地磁盘，管理它们已经超出了Kubernetes的能力范围，所以，一般会由系统管理员单独维护，然后再在Kubernetes里创建对应的PV。&lt;/p&gt;
&lt;p&gt;要注意的是，PV属于集群的系统资源，是和Node平级的一种对象，Pod对它没有管理权，只有使用权。&lt;/p&gt;
&lt;h2 id="什么是persistentvolumeclaim-storageclass"&gt;什么是PersistentVolumeClaim/StorageClass&lt;/h2&gt;
&lt;p&gt;现在有了PV，我们是不是可以直接在Pod里挂载使用了呢？&lt;/p&gt;
&lt;p&gt;还不行。因为不同存储设备的差异实在是太大了：有的速度快，有的速度慢；有的可以共享读写，有的只能独占读写；有的容量小，只有几百MB，有的容量大到TB、PB级别……&lt;/p&gt;
&lt;p&gt;这么多种存储设备，只用一个PV对象来管理还是有点太勉强了，不符合“单一职责”的原则，让Pod直接去选择PV也很不灵活。于是Kubernetes就又增加了两个新对象，&lt;strong&gt;PersistentVolumeClaim&lt;/strong&gt;和&lt;strong&gt;StorageClass&lt;/strong&gt;，用的还是“中间层”的思想，把存储卷的分配管理过程再次细化。&lt;/p&gt;
&lt;p&gt;我们看这两个新对象。&lt;/p&gt;
&lt;p&gt;PersistentVolumeClaim，简称PVC，从名字上看比较好理解，就是用来向Kubernetes申请存储资源的。PVC是给Pod使用的对象，它相当于是Pod的代理，代表Pod向系统申请PV。一旦资源申请成功，Kubernetes就会把PV和PVC关联在一起，这个动作叫做“&lt;strong&gt;绑定&lt;/strong&gt;”（bind）。&lt;/p&gt;
&lt;p&gt;但是，系统里的存储资源非常多，如果要PVC去直接遍历查找合适的PV也很麻烦，所以就要用到StorageClass。&lt;/p&gt;
&lt;p&gt;StorageClass的作用有点像[第21讲]里的IngressClass，它抽象了特定类型的存储系统（比如Ceph、NFS），在PVC和PV之间充当“协调人”的角色，帮助PVC找到合适的PV。也就是说它可以简化Pod挂载“虚拟盘”的过程，让Pod看不到PV的实现细节。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/ad2fc8035a990bf08d7a.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;如果看到这里，你觉得还是差点理解也不要着急，我们找个生活中的例子来类比一下。毕竟和常用的CPU、内存比起来，我们对存储系统的认识还是比较少的，所以Kubernetes里，PV、PVC和StorageClass这三个新概念也不是特别好掌握。&lt;/p&gt;
&lt;p&gt;看例子，假设你在公司里想要10张纸打印资料，于是你给前台打电话讲清楚了需求。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“打电话”这个动作，就相当于PVC，向Kubernetes申请存储资源。&lt;/li&gt;
&lt;li&gt;前台里有各种牌子的办公用纸，大小、规格也不一样，这就相当于StorageClass。&lt;/li&gt;
&lt;li&gt;前台根据你的需要，挑选了一个品牌，再从库存里拿出一包A4纸，可能不止10张，但也能够满足要求，就在登记表上新添了一条记录，写上你在某天申领了办公用品。这个过程就是PVC到PV的绑定。&lt;/li&gt;
&lt;li&gt;而最后到你手里的A4纸包，就是PV存储对象。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;好，大概了解了这些API对象，我们接下来可以结合YAML描述和实际操作再慢慢体会。&lt;/p&gt;
&lt;h2 id="如何使用yaml描述persistentvolume"&gt;如何使用YAML描述PersistentVolume&lt;/h2&gt;
&lt;p&gt;Kubernetes里有很多种类型的PV，我们先看看最容易的本机存储“&lt;strong&gt;HostPath&lt;/strong&gt;”，它和Docker里挂载本地目录的 -v 参数非常类似，可以用它来初步认识一下PV的用法。&lt;/p&gt;
&lt;p&gt;因为Pod会在集群的任意节点上运行，所以首先，我们要作为系统管理员在每个节点上创建一个目录，它将会作为本地存储卷挂载到Pod里。&lt;/p&gt;
&lt;p&gt;为了省事，我就在 /tmp 里建立名字是 host-10m-pv 的目录，表示一个只有10MB容量的存储设备。&lt;/p&gt;
&lt;p&gt;有了存储，我们就可以使用YAML来描述这个PV对象了。&lt;/p&gt;
&lt;p&gt;不过很遗憾，你不能用 kubectl create 直接创建PV对象，&lt;strong&gt;只能用 kubectl api-resources、kubectl explain 查看PV的字段说明，手动编写PV 的YAML描述文件&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;下面我给出一个YAML示例，你可以把它作为样板，编辑出自己的PV：&lt;/p&gt;
&lt;p&gt;PV对象的文件头部分很简单，还是API对象的“老一套”，我就不再详细解释了，重点看它的 spec 部分，每个字段都很重要，描述了存储的详细信息。&lt;/p&gt;
&lt;p&gt;“&lt;strong&gt;storageClassName&lt;/strong&gt;”就是刚才说过的，对存储类型的抽象StorageClass。这个PV是我们手动管理的，名字可以任意起，这里我写的是 host-test，你也可以把它改成 manual、hand-work 之类的词汇。&lt;/p&gt;
&lt;p&gt;“&lt;strong&gt;accessModes&lt;/strong&gt;”定义了存储设备的访问模式，简单来说就是虚拟盘的读写权限，和Linux的文件访问模式差不多，目前Kubernetes里有3种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ReadWriteOnce：存储卷可读可写，但只能被一个节点上的Pod挂载。&lt;/li&gt;
&lt;li&gt;ReadOnlyMany：存储卷只读不可写，可以被任意节点上的Pod多次挂载。&lt;/li&gt;
&lt;li&gt;ReadWriteMany：存储卷可读可写，也可以被任意节点上的Pod多次挂载。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你要注意，这3种访问模式限制的对象是节点而不是Pod，因为存储是系统级别的概念，不属于Pod里的进程。&lt;/p&gt;</description></item><item><title>25 PersistentVolume + NFS：怎么使用网络共享存储？</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/027/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/027/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/25%20PersistentVolume%20%20%20NFS%ef%bc%9a%e6%80%8e%e4%b9%88%e4%bd%bf%e7%94%a8%e7%bd%91%e7%bb%9c%e5%85%b1%e4%ba%ab%e5%ad%98%e5%82%a8%ef%bc%9f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="25-persistentvolume-nfs-怎么使用网络共享存储"&gt;25 PersistentVolume + NFS：怎么使用网络共享存储？&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在上节课里我们看到了Kubernetes里的持久化存储对象PersistentVolume、PersistentVolumeClaim、StorageClass，把它们联合起来就可以为Pod挂载一块“虚拟盘”，让Pod在其中任意读写数据。&lt;/p&gt;
&lt;p&gt;不过当时我们使用的是HostPath，存储卷只能在本机使用，而Kubernetes里的Pod经常会在集群里“漂移”，所以这种方式不是特别实用。&lt;/p&gt;
&lt;p&gt;要想让存储卷真正能被Pod任意挂载，我们需要变更存储的方式，不能限定在本地磁盘，而是要改成&lt;strong&gt;网络存储&lt;/strong&gt;，这样Pod无论在哪里运行，只要知道IP地址或者域名，就可以通过网络通信访问存储设备。&lt;/p&gt;
&lt;p&gt;网络存储是一个非常热门的应用领域，有很多知名的产品，比如AWS、Azure、Ceph，Kubernetes还专门定义了CSI（Container Storage Interface）规范，不过这些存储类型的安装、使用都比较复杂，在我们的实验环境里部署难度比较高。&lt;/p&gt;
&lt;p&gt;所以今天的这次课里，我选择了相对来说比较简单的NFS系统（Network File System），以它为例讲解如何在Kubernetes里使用网络存储，以及静态存储卷和动态存储卷的概念。&lt;/p&gt;
&lt;h2 id="如何安装nfs服务器"&gt;如何安装NFS服务器&lt;/h2&gt;
&lt;p&gt;作为一个经典的网络存储系统，NFS有着近40年的发展历史，基本上已经成为了各种UNIX系统的标准配置，Linux自然也提供对它的支持。&lt;/p&gt;
&lt;p&gt;NFS采用的是Client/Server架构，需要选定一台主机作为Server，安装NFS服务端；其他要使用存储的主机作为Client，安装NFS客户端工具。&lt;/p&gt;
&lt;p&gt;所以接下来，我们在自己的Kubernetes集群里再增添一台名字叫Storage的服务器，在上面安装NFS，实现网络存储、共享网盘的功能。&lt;strong&gt;不过这台Storage也只是一个逻辑概念，我们在实际安装部署的时候完全可以把它合并到集群里的某台主机里&lt;/strong&gt;，比如这里我就复用了[第17讲]里的Console。&lt;/p&gt;
&lt;p&gt;新的网络架构如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/7083347b6b575c4baae1.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;在Ubuntu系统里安装NFS服务端很容易，使用apt即可：&lt;/p&gt;
&lt;p&gt;安装好之后，你需要给NFS指定一个存储位置，也就是网络共享目录。一般来说，应该建立一个专门的 /data 目录，这里为了简单起见，我就使用了&lt;strong&gt;临时目录 /tmp/nfs&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;接下来你需要配置NFS访问共享目录，修改 /etc/exports，指定目录名、允许访问的网段，还有权限等参数。这些规则比较琐碎，和我们的Kubernetes课程关联不大，我就不详细解释了，你只要把下面这行加上就行，注意目录名和IP地址要改成和自己的环境一致：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;改好之后，需要用 exportfs -ra 通知NFS，让配置生效&lt;/strong&gt;，再用 exportfs -v 验证效果：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/a4a7872dea72406807b3.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;现在，你就可以使用 systemctl 来启动NFS服务器了：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/ebf7784e7deba1f8076e.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;你还可以使用命令 showmount 来检查NFS的网络挂载情况：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/4cf14f90b913174e4717.webp" alt="图片"&gt;&lt;/p&gt;
&lt;h2 id="如何安装nfs客户端"&gt;如何安装NFS客户端&lt;/h2&gt;
&lt;p&gt;有了NFS服务器之后，为了让Kubernetes集群能够访问NFS存储服务，我们还需要在每个节点上都安装NFS客户端。&lt;/p&gt;
&lt;p&gt;这项工作只需要一条apt命令，不需要额外的配置：&lt;/p&gt;
&lt;p&gt;同样，在节点上可以用 showmount 检查NFS能否正常挂载，注意IP地址要写成NFS服务器的地址，我在这里就是“192.168.10.208”：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/871fb089e41d8f156de2.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;现在让我们尝试手动挂载一下NFS网络存储，先创建一个目录 /tmp/test 作为挂载点：&lt;/p&gt;
&lt;p&gt;然后用命令 mount 把NFS服务器的共享目录挂载到刚才创建的本地目录上：&lt;/p&gt;
&lt;p&gt;最后测试一下，我们在 /tmp/test 里随便创建一个文件，比如 x.yml：&lt;/p&gt;
&lt;p&gt;再回到NFS服务器，检查共享目录 /tmp/nfs，应该会看到也出现了一个同样的文件 x.yml，这就说明NFS安装成功了。之后集群里的任意节点，只要通过NFS客户端，就能把数据写入NFS服务器，实现网络存储。&lt;/p&gt;
&lt;h2 id="如何使用nfs存储卷"&gt;如何使用NFS存储卷&lt;/h2&gt;
&lt;p&gt;现在我们已经为Kubernetes配置好了NFS存储系统，就可以使用它来创建新的PV存储对象了。&lt;/p&gt;
&lt;p&gt;先来手工分配一个存储卷，需要指定 storageClassName 是 nfs，而 accessModes 可以设置成 ReadWriteMany，这是由NFS的特性决定的，它&lt;strong&gt;支持多个节点同时访问一个共享目录&lt;/strong&gt;。&lt;/p&gt;</description></item><item><title>26 StatefulSet：怎么管理有状态的应用？</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/028/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/028/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/26%20StatefulSet%ef%bc%9a%e6%80%8e%e4%b9%88%e7%ae%a1%e7%90%86%e6%9c%89%e7%8a%b6%e6%80%81%e7%9a%84%e5%ba%94%e7%94%a8%ef%bc%9f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="26-statefulset-怎么管理有状态的应用"&gt;26 StatefulSet：怎么管理有状态的应用？&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在中级篇里，我们学习了Deployment和DaemonSet两种API对象，它们是在Kubernetes集群里部署应用的重要工具，不过它们也有一个缺点，只能管理“无状态应用”（Stateless Application），不能管理“有状态应用”（Stateful Application）。&lt;/p&gt;
&lt;p&gt;“有状态应用”的处理比较复杂，要考虑的事情很多，但是这些问题我们其实可以通过组合之前学过的Deployment、Service、PersistentVolume等对象来解决。&lt;/p&gt;
&lt;p&gt;今天我们就来研究一下什么是“有状态应用”，然后看看Kubernetes为什么会设计一个新对象——StatefulSet来专门管理“有状态应用”。&lt;/p&gt;
&lt;h2 id="什么是有状态的应用"&gt;什么是有状态的应用&lt;/h2&gt;
&lt;p&gt;我们先从PersistentVolume谈起，它为Kubernetes带来了持久化存储的功能，能够让应用把数据存放在本地或者远程的磁盘上。&lt;/p&gt;
&lt;p&gt;那么你有没有想过，持久化存储，对应用来说，究竟意味着什么呢？&lt;/p&gt;
&lt;p&gt;有了持久化存储，应用就可以把一些运行时的关键数据落盘，相当于有了一份“保险”，如果Pod发生意外崩溃，也只不过像是按下了暂停键，等重启后挂载Volume，再加载原数据就能够满血复活，恢复之前的“状态”继续运行。&lt;/p&gt;
&lt;p&gt;注意到了吗？这里有一个关键词——“&lt;strong&gt;状态&lt;/strong&gt;”，应用保存的数据，实际上就是它某个时刻的“运行状态”。&lt;/p&gt;
&lt;p&gt;所以从这个角度来说，理论上任何应用都是有状态的。&lt;/p&gt;
&lt;p&gt;只是有的应用的状态信息不是很重要，即使不恢复状态也能够正常运行，这就是我们常说的“&lt;strong&gt;无状态应用&lt;/strong&gt;”。“无状态应用”典型的例子就是Nginx这样的Web服务器，它只是处理HTTP请求，本身不生产数据（日志除外），不需要特意保存状态，无论以什么状态重启都能很好地对外提供服务。&lt;/p&gt;
&lt;p&gt;还有一些应用，运行状态信息就很重要了，如果因为重启而丢失了状态是绝对无法接受的，这样的应用就是“&lt;strong&gt;有状态应用&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;“有状态应用”的例子也有很多，比如Redis、MySQL这样的数据库，它们的“状态”就是在内存或者磁盘上产生的数据，是应用的核心价值所在，如果不能够把这些数据及时保存再恢复，那绝对会是灾难性的后果。&lt;/p&gt;
&lt;p&gt;理解了这一点，我们结合目前学到的知识思考一下：&lt;strong&gt;Deployment加上PersistentVolume，在Kubernetes里是不是可以轻松管理有状态的应用了呢？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;的确，用Deployment来保证高可用，用PersistentVolume来存储数据，确实可以部分达到管理“有状态应用”的目的（你可以自己试着编写这样的YAML）。&lt;/p&gt;
&lt;p&gt;但是Kubernetes的眼光则更加全面和长远，它认为“状态”不仅仅是数据持久化，在集群化、分布式的场景里，还有多实例的依赖关系、启动顺序和网络标识等问题需要解决，而这些问题恰恰是Deployment力所不及的。&lt;/p&gt;
&lt;p&gt;因为只使用Deployment，多个实例之间是无关的，启动的顺序不固定，Pod的名字、IP地址、域名也都是完全随机的，这正是“无状态应用”的特点。&lt;/p&gt;
&lt;p&gt;但对于“有状态应用”，多个实例之间可能存在依赖关系，比如master/slave、active/passive，需要依次启动才能保证应用正常运行，外界的客户端也可能要使用固定的网络标识来访问实例，而且这些信息还必须要保证在Pod重启后不变。&lt;/p&gt;
&lt;p&gt;所以，Kubernetes就在Deployment的基础之上定义了一个新的API对象，名字也很好理解，就叫StatefulSet，专门用来管理有状态的应用。&lt;/p&gt;
&lt;h2 id="如何使用yaml描述statefulset"&gt;如何使用YAML描述StatefulSet&lt;/h2&gt;
&lt;p&gt;首先我们还是用命令 kubectl api-resources 来查看StatefulSet的基本信息，可以知道它的简称是 sts，YAML文件头信息是：&lt;/p&gt;
&lt;p&gt;和DaemonSet类似，StatefulSet也可以看做是Deployment的一个特例，它也不能直接用 kubectl create 创建样板文件，但它的对象描述和Deployment差不多，你同样可以把Deployment适当修改一下，就变成了StatefulSet对象。&lt;/p&gt;
&lt;p&gt;这里我给出了一个使用Redis的StatefulSet，你来看看它与Deployment有什么差异：&lt;/p&gt;
&lt;p&gt;我们会发现，YAML文件里除了 kind 必须是“&lt;strong&gt;StatefulSet&lt;/strong&gt;”，在 spec 里还多出了一个“&lt;strong&gt;serviceName&lt;/strong&gt;”字段，其余的部分和Deployment是一模一样的，比如 replicas、selector、template 等等。&lt;/p&gt;
&lt;p&gt;这两个不同之处其实就是StatefulSet与Deployment的关键区别。想要真正理解这一点，我们得结合StatefulSet在Kubernetes里的使用方法来分析。&lt;/p&gt;
&lt;h2 id="如何在kubernetes里使用statefulset"&gt;如何在Kubernetes里使用StatefulSet&lt;/h2&gt;
&lt;p&gt;让我们用 kubectl apply 创建StatefulSet对象，用 kubectl get 先看看它是什么样的：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/7ac829b9dc9a7a303407.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;从截图里，你应该能够看到，StatefulSet所管理的Pod不再是随机的名字了，而是有了顺序编号，从0开始分别被命名为 redis-sts-0、redis-sts-1，Kubernetes也会按照这个顺序依次创建（0号比1号的AGE要长一点），这就解决了&lt;strong&gt;“有状态应用”的第一个问题：启动顺序&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;有了启动的先后顺序，应用该怎么知道自己的身份，进而确定互相之间的依赖关系呢？&lt;/p&gt;
&lt;p&gt;Kubernetes给出的方法是&lt;strong&gt;使用hostname&lt;/strong&gt;，也就是每个Pod里的主机名，让我们再用 kubectl exec 登录Pod内部看看：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/d55262952ec8a23e4458.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;在Pod里查看环境变量 $HOSTNAME 或者是执行命令 hostname，都可以得到这个Pod的名字 redis-sts-0。&lt;/p&gt;
&lt;p&gt;有了这个唯一的名字，应用就可以自行决定依赖关系了，比如在这个Redis例子里，就可以让先启动的0号Pod是主实例，后启动的1号Pod是从实例。&lt;/p&gt;
&lt;p&gt;解决了启动顺序和依赖关系，还剩下&lt;strong&gt;第三个问题：网络标识，这就需要用到Service对象&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;不过这里又有一点奇怪的地方，我们不能用命令 kubectl expose 直接为StatefulSet生成Service，只能手动编写YAML。但是这肯定难不倒你，经过了这么多练习，现在你应该能很轻松地写出一个Service对象。&lt;/p&gt;</description></item><item><title>27 滚动更新：如何做到平滑的应用升级降级？</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/029/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/029/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/27%20%e6%bb%9a%e5%8a%a8%e6%9b%b4%e6%96%b0%ef%bc%9a%e5%a6%82%e4%bd%95%e5%81%9a%e5%88%b0%e5%b9%b3%e6%bb%91%e7%9a%84%e5%ba%94%e7%94%a8%e5%8d%87%e7%ba%a7%e9%99%8d%e7%ba%a7%ef%bc%9f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="27-滚动更新-如何做到平滑的应用升级降级"&gt;27 滚动更新：如何做到平滑的应用升级降级？&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;上次课里我们学习了管理有状态应用的对象StatefulSet，再加上管理无状态应用的Deployment和DaemonSet，我们就能在Kubernetes里部署任意形式的应用了。&lt;/p&gt;
&lt;p&gt;不过，只是把应用发布到集群里是远远不够的，要让应用稳定可靠地运行，还需要有持续的运维工作。&lt;/p&gt;
&lt;p&gt;如果你还记得在[第18节课]里，我们学过Deployment的“应用伸缩”功能就是一种常见的运维操作，在Kubernetes里，使用命令 kubectl scale，我们就可以轻松调整Deployment下属的Pod数量，因为StatefulSet是Deployment的一种特例，所以它也可以使用 kubectl scale 来实现“应用伸缩”。&lt;/p&gt;
&lt;p&gt;除了“应用伸缩”，其他的运维操作比如应用更新、版本回退等工作，该怎么做呢？这些也是我们日常运维中经常会遇到的问题。&lt;/p&gt;
&lt;p&gt;今天我就以Deployment为例，来讲讲Kubernetes在应用管理方面的高级操作：滚动更新，使用 kubectl rollout 实现用户无感知的应用升级和降级。&lt;/p&gt;
&lt;h2 id="kubernetes如何定义应用版本"&gt;Kubernetes如何定义应用版本&lt;/h2&gt;
&lt;p&gt;应用的版本更新，大家都知道是怎么回事，比如我们发布了V1版，过了几天加了新功能，要发布V2版。&lt;/p&gt;
&lt;p&gt;不过说起来简单，版本更新实际做起来是一个相当棘手的事。因为系统已经上线运行，必须要保证不间断地对外提供服务，通俗地说就是“给空中的飞机换引擎”。尤其在以前，需要开发、测试、运维、监控、网络等各个部门的一大堆人来协同工作，费时又费力。&lt;/p&gt;
&lt;p&gt;但是，应用的版本更新其实是有章可循的，现在我们有了Kubernetes这个强大的自动化运维管理系统，就可以把它的过程抽象出来，让计算机去完成那些复杂繁琐的人工操作。&lt;/p&gt;
&lt;p&gt;在Kubernetes里，版本更新使用的不是API对象，而是两个命令：kubectl apply 和 kubectl rollout，当然它们也要搭配部署应用所需要的Deployment、DaemonSet等YAML文件。&lt;/p&gt;
&lt;p&gt;不过在我们信心满满开始操作之前，首先要理解在Kubernetes里，所谓的“版本”到底是什么？&lt;/p&gt;
&lt;p&gt;我们常常会简单地认为“版本”就是应用程序的“版本号”，或者是容器镜像的“标签”，但不要忘了，在Kubernetes里应用都是以Pod的形式运行的，而Pod通常又会被Deployment等对象来管理，&lt;strong&gt;所以应用的“版本更新”实际上更新的是整个Pod&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;那Pod又是由什么来决定的呢？&lt;/p&gt;
&lt;p&gt;仔细回忆一下之前我们创建的那么多个对象，你就会发现，Pod是由YAML描述文件来确定的，更准确地说，是Deployment等对象里的字段 template。&lt;/p&gt;
&lt;p&gt;所以，&lt;strong&gt;在Kubernetes里应用的版本变化就是 template 里Pod的变化&lt;/strong&gt;，哪怕 template 里只变动了一个字段，那也会形成一个新的版本，也算是版本变化。&lt;/p&gt;
&lt;p&gt;但 template 里的内容太多了，拿这么长的字符串来当做“版本号”不太现实，所以Kubernetes就使用了“摘要”功能，用摘要算法计算 template 的Hash值作为“版本号”，虽然不太方便识别，但是很实用。&lt;/p&gt;
&lt;p&gt;我们就拿[第18讲]里的Nginx Deployment作为例子吧，创建对象之后，使用 kubectl get 来查看Pod的状态：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/a3d682ec25bd4636e76b.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;Pod名字里的那串随机数“6796……”就是Pod模板的Hash值，也就是Pod的“版本号”。&lt;/p&gt;
&lt;p&gt;如果你变动了Pod YAML描述，比如把镜像改成 nginx:stable-alpine，或者把容器名字改成 nginx-test，都会生成一个新的应用版本，kubectl apply 后就会重新创建Pod：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/287180211831de024631.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;你可以看到，Pod名字里的Hash值变成了“7c6c……”，这就表示Pod的版本更新了。&lt;/p&gt;
&lt;h2 id="kubernetes如何实现应用更新"&gt;Kubernetes如何实现应用更新&lt;/h2&gt;
&lt;p&gt;为了更仔细地研究Kubernetes的应用更新过程，让我们来略微改造一下Nginx Deployment对象，看看Kubernetes到底是怎么实现版本更新的。&lt;/p&gt;
&lt;p&gt;首先修改ConfigMap，让它输出Nginx的版本号，方便我们用curl查看版本：&lt;/p&gt;
&lt;p&gt;然后我们修改Pod镜像，明确地指定版本号是 1.21-alpine，实例数设置为4个：&lt;/p&gt;
&lt;p&gt;把它命名为 ngx-v1.yml，然后执行命令 kubectl apply 部署这个应用：&lt;/p&gt;
&lt;p&gt;我们还可以为它创建Service对象，再用 kubectl port-forward 转发请求来查看状态：&lt;/p&gt;</description></item><item><title>28 应用保障：如何让Pod运行得更健康？</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/030/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/030/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/28%20%e5%ba%94%e7%94%a8%e4%bf%9d%e9%9a%9c%ef%bc%9a%e5%a6%82%e4%bd%95%e8%ae%a9Pod%e8%bf%90%e8%a1%8c%e5%be%97%e6%9b%b4%e5%81%a5%e5%ba%b7%ef%bc%9f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="28-应用保障-如何让pod运行得更健康"&gt;28 应用保障：如何让Pod运行得更健康？&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在前面这么多节的课程中，我们都是在研究如何使用各种API对象来管理、操作Pod，而对Pod本身的关注却不是太多。&lt;/p&gt;
&lt;p&gt;作为Kubernetes里的核心概念和原子调度单位，Pod的主要职责是管理容器，以逻辑主机、容器集合、进程组的形式来代表应用，它的重要性是不言而喻的。&lt;/p&gt;
&lt;p&gt;那么今天我们回过头来，在之前那些上层API对象的基础上，一起来看看在Kubernetes里配置Pod的两种方法：资源配额Resources、检查探针Probe，它们能够给Pod添加各种运行保障，让应用运行得更健康。&lt;/p&gt;
&lt;h2 id="容器资源配额"&gt;容器资源配额&lt;/h2&gt;
&lt;p&gt;早在[第2讲]的时候我们就说过，创建容器有三大隔离技术：namespace、cgroup、chroot。其中的namespace实现了独立的进程空间，chroot实现了独立的文件系统，但唯独没有看到cgroup的具体应用。&lt;/p&gt;
&lt;p&gt;cgroup的作用是管控CPU、内存，保证容器不会无节制地占用基础资源，进而影响到系统里的其他应用。&lt;/p&gt;
&lt;p&gt;不过，容器总是要使用CPU和内存的，该怎么处理好需求与限制这两者之间的关系呢？&lt;/p&gt;
&lt;p&gt;Kubernetes的做法与我们在[第24讲]里提到的PersistentVolumeClaim用法有些类似，就是容器需要先提出一个“书面申请”，Kubernetes再依据这个“申请”决定资源是否分配和如何分配。&lt;/p&gt;
&lt;p&gt;但是CPU、内存与存储卷有明显的不同，因为它是直接“内置”在系统里的，不像硬盘那样需要“外挂”，所以申请和管理的过程也就会简单很多。&lt;/p&gt;
&lt;p&gt;具体的申请方法很简单，&lt;strong&gt;只要在Pod容器的描述部分添加一个新字段 resources 就可以了&lt;/strong&gt;，它就相当于申请资源的 Claim。&lt;/p&gt;
&lt;p&gt;来看一个YAML示例：&lt;/p&gt;
&lt;p&gt;这个YAML文件定义了一个Nginx Pod，我们需要重点学习的是 containers.resources，它下面有两个字段：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“&lt;strong&gt;requests&lt;/strong&gt;”，意思是容器要申请的资源，也就是说要求Kubernetes在创建Pod的时候必须分配这里列出的资源，否则容器就无法运行。&lt;/li&gt;
&lt;li&gt;“&lt;strong&gt;limits&lt;/strong&gt;”，意思是容器使用资源的上限，不能超过设定值，否则就有可能被强制停止运行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在请求 cpu 和 memory 这两种资源的时候，你需要特别注意它们的表示方式。&lt;/p&gt;
&lt;p&gt;内存的写法和磁盘容量一样，使用 Ki、Mi、Gi 来表示 KB、MB、GB，比如 512Ki、100Mi、0.5Gi 等。&lt;/p&gt;
&lt;p&gt;而CPU因为在计算机中数量有限，非常宝贵，所以Kubernetes允许容器精细分割CPU，即可以1个、2个地完整使用CPU，也可以用小数0.1、0.2的方式来部分使用CPU。这其实是效仿了UNIX“时间片”的用法，意思是进程最多可以占用多少CPU时间。&lt;/p&gt;
&lt;p&gt;不过CPU时间也不能无限分割，&lt;strong&gt;Kubernetes里CPU的最小使用单位是0.001，为了方便表示用了一个特别的单位 m&lt;/strong&gt;，也就是“milli”“毫”的意思，比如说500m就相当于0.5。&lt;/p&gt;
&lt;p&gt;现在我们再来看这个YAML，你就应该明白了，它向系统申请的是1%的CPU时间和100MB的内存，运行时的资源上限是2%CPU时间和200MB内存。有了这个申请，Kubernetes就会在集群中查找最符合这个资源要求的节点去运行Pod。&lt;/p&gt;
&lt;p&gt;下面是我在&lt;a href="https://www.freecodecamp.org/news/how-to-leverage-the-power-of-kubernetes-to-optimise-your-hosting-costs-c2e168a232a2/" rel="nofollow noreferrer noopener"&gt;网上&lt;/a&gt;找的一张动图，Kubernetes会根据每个Pod声明的需求，像搭积木或者玩俄罗斯方块一样，把节点尽量“塞满”，充分利用每个节点的资源，让集群的效益最大化。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/be12161284f2213ba523.gif" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;你可能会有疑问：如果Pod不写 resources 字段，Kubernetes会如何处理呢？&lt;/p&gt;
&lt;p&gt;这就意味着Pod对运行的资源要求“既没有下限，也没有上限”，Kubernetes不用管CPU和内存是否足够，可以把Pod调度到任意的节点上，而且后续Pod运行时也可以无限制地使用CPU和内存。&lt;/p&gt;
&lt;p&gt;我们课程里是实验环境，这样做是当然是没有问题的，但如果是生产环境就很危险了，Pod可能会因为资源不足而运行缓慢，或者是占用太多资源而影响其他应用，所以我们应当合理评估Pod的资源使用情况，尽量为Pod加上限制。&lt;/p&gt;
&lt;p&gt;看到这里估计你会继续追问：如果预估错误，Pod申请的资源太多，系统无法满足会怎么样呢？&lt;/p&gt;
&lt;p&gt;让我们来试一下吧，先删除Pod的资源限制 resources.limits，把 resources.request.cpu 改成比较极端的“10”，也就是要求10个CPU：&lt;/p&gt;
&lt;p&gt;然后使用 kubectl apply 创建这个Pod，你可能会惊奇地发现，虽然我们的Kubernetes集群里只有3个CPU，但Pod也能创建成功。&lt;/p&gt;
&lt;p&gt;不过我们再用 kubectl get pod 去查看的话，就会发现它处于“Pending”状态，实际上并没有真正被调度运行：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/3bc77b67b0d821603175.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;使用命令 kubectl describe 来查看具体原因，会发现有这么一句提示：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/22cb8b75a49cdb0458fe.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;这就很明确地告诉我们Kubernetes调度失败，当前集群里的所有节点都无法运行这个Pod，因为它要求的CPU实在是太多了。&lt;/p&gt;
&lt;h2 id="什么是容器状态探针"&gt;什么是容器状态探针&lt;/h2&gt;
&lt;p&gt;现在，我们使用 resources 字段加上资源配额之后，Pod在Kubernetes里的运行就有了初步保障，Kubernetes会监控Pod的资源使用情况，让它既不会“饿死”也不会“撑死”。&lt;/p&gt;</description></item><item><title>29 集群管理：如何用名字空间分隔系统资源？</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/031/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/031/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/29%20%e9%9b%86%e7%be%a4%e7%ae%a1%e7%90%86%ef%bc%9a%e5%a6%82%e4%bd%95%e7%94%a8%e5%90%8d%e5%ad%97%e7%a9%ba%e9%97%b4%e5%88%86%e9%9a%94%e7%b3%bb%e7%bb%9f%e8%b5%84%e6%ba%90%ef%bc%9f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="29-集群管理-如何用名字空间分隔系统资源"&gt;29 集群管理：如何用名字空间分隔系统资源？&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在上一节课里我们学习了资源配额和检查探针，它们可以保障Pod这个微观单位很好地运行。那么很自然地，我们就会想：在集群的宏观层次，会不会也有类似的方法来为Kubernetes提供运行保障呢？&lt;/p&gt;
&lt;p&gt;这是毫无疑问的，因为Kubernetes在各个方面都考虑得非常周密，也有很多的手段来管理、控制集群的资源。&lt;/p&gt;
&lt;p&gt;今天我们就来看看名字空间（namespace）的一些高级用法。&lt;/p&gt;
&lt;h2 id="为什么要有名字空间"&gt;为什么要有名字空间&lt;/h2&gt;
&lt;p&gt;其实我们很早就接触过Kubernetes的名字空间，比如[第10讲]中查看apiserver等组件要用到 kube-system 名字空间，还有在[第20讲]里的Service对象，DNS的完整域名里也会用到名字空间。&lt;/p&gt;
&lt;p&gt;不过之前学习的重点是Kubernetes架构和API对象，对名字空间没有特别关注，而且也过去比较久了，所以现在我们来重新认识一下名字空间。&lt;/p&gt;
&lt;p&gt;首先要明白，&lt;strong&gt;Kubernetes的名字空间并不是一个实体对象，只是一个逻辑上的概念&lt;/strong&gt;。它可以把集群切分成一个个彼此独立的区域，然后我们把对象放到这些区域里，就实现了类似容器技术里namespace的隔离效果，应用只能在自己的名字空间里分配资源和运行，不会干扰到其他名字空间里的应用。&lt;/p&gt;
&lt;p&gt;你可能要问了：Kubernetes的Master/Node架构已经能很好地管理集群，为什么还要引入名字空间这个东西呢？它的实际意义是什么呢？&lt;/p&gt;
&lt;p&gt;我觉得，这恰恰是Kubernetes&lt;strong&gt;面对大规模集群、海量节点时的一种现实考虑&lt;/strong&gt;。因为集群很大、计算资源充足，会有非常多的用户在Kubernetes里创建各式各样的应用，可能会有百万数量级别的Pod，这就使得资源争抢和命名冲突的概率大大增加了，情形和单机Linux系统里是非常相似的。&lt;/p&gt;
&lt;p&gt;比如说，现在有一个Kubernetes集群，前端组、后端组、测试组都在使用它。这个时候就很容易命名冲突，比如后端组先创建了一个Pod叫“Web”，这个名字就被“占用”了，之后前端组和测试组就只能绞尽脑汁再新起一个不冲突的名字。接着资源争抢也容易出现，比如某一天，测试组不小心部署了有Bug的应用，在节点上把资源都给“吃”完了，就会导致其他组的同事根本无法工作。&lt;/p&gt;
&lt;p&gt;所以，当多团队、多项目共用Kubernetes的时候，为了避免这些问题的出现，我们就需要&lt;strong&gt;把集群给适当地“局部化”，为每一类用户创建出只属于它自己的“工作空间”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;如果把Kubernetes比做一个大牧场的话，API对象就是里面的鸡鸭牛羊，而名字空间就是圈养它们的围栏，有了各自合适的活动区域，就能更有效、更安全地利用Kubernetes。&lt;/p&gt;
&lt;h2 id="如何使用名字空间"&gt;如何使用名字空间&lt;/h2&gt;
&lt;p&gt;名字空间也是一种API对象，使用命令 kubectl api-resources 可以看到它的简称是“ns”，命令 kubectl create 不需要额外的参数，可以很容易地创建一个名字空间，比如：&lt;/p&gt;
&lt;p&gt;Kubernetes初始化集群的时候也会预设4个名字空间：default、kube-system、kube-public、kube-node-lease。我们常用的是前两个，default 是用户对象默认的名字空间，kube-system 是系统组件所在的名字空间，相信你对它们已经很熟悉了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;想要把一个对象放入特定的名字空间，需要在它的 metadata 里添加一个 namespace 字段&lt;/strong&gt;，比如我们要在“test-ns”里创建一个简单的Nginx Pod，就要这样写：&lt;/p&gt;
&lt;p&gt;kubectl apply 创建这个对象之后，我们直接用 kubectl get 是看不到它的，因为默认查看的是“default”名字空间，&lt;strong&gt;想要操作其他名字空间的对象必须要用 -n 参数明确指定&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/28ebee907e7f938d57ed.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;因为名字空间里的对象都从属于名字空间，所以在删除名字空间的时候一定要小心，一旦名字空间被删除，它里面的所有对象也都会消失。&lt;/p&gt;
&lt;p&gt;你可以执行一下 kubectl delete，试着删除刚才创建的名字空间“test-ns”：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/aae1d40f0c989a046f84.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;就会发现删除名字空间后，它里面的Pod也会无影无踪了。&lt;/p&gt;
&lt;h2 id="什么是资源配额"&gt;什么是资源配额&lt;/h2&gt;
&lt;p&gt;有了名字空间，我们就可以像管理容器一样，给名字空间设定配额，把整个集群的计算资源分割成不同的大小，按需分配给团队或项目使用。&lt;/p&gt;
&lt;p&gt;不过集群和单机不一样，除了限制最基本的CPU和内存，还必须限制各种对象的数量，否则对象之间也会互相挤占资源。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;名字空间的资源配额需要使用一个专门的API对象，叫做 ResourceQuota，简称是 quota&lt;/strong&gt;，我们可以使用命令 kubectl create 创建一个它的样板文件：&lt;/p&gt;
&lt;p&gt;因为资源配额对象必须依附在某个名字空间上，所以在它的 metadata 字段里必须明确写出 namespace（否则就会应用到default名字空间）。&lt;/p&gt;
&lt;p&gt;下面我们先创建一个名字空间“dev-ns”，再创建一个资源配额对象“dev-qt”：&lt;/p&gt;
&lt;p&gt;ResourceQuota对象的使用方式比较灵活，既可以限制整个名字空间的配额，也可以只限制某些类型的对象（使用scopeSelector），今天我们看第一种，它需要在 spec 里使用 hard 字段，意思就是“&lt;strong&gt;硬性全局限制&lt;/strong&gt;”。&lt;/p&gt;</description></item><item><title>30 系统监控：如何使用Metrics Server和Prometheus？</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/032/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/032/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/30%20%e7%b3%bb%e7%bb%9f%e7%9b%91%e6%8e%a7%ef%bc%9a%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8Metrics%20Server%e5%92%8cPrometheus%ef%bc%9f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="30-系统监控-如何使用metrics-server和prometheus"&gt;30 系统监控：如何使用Metrics Server和Prometheus？&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在前面的两节课里，我们学习了对Pod和对集群的一些管理方法，其中的要点就是设置资源配额，让Kubernetes用户能公平合理地利用系统资源。&lt;/p&gt;
&lt;p&gt;虽然有了这些方法，但距离我们把Pod和集群管好用好还缺少一个很重要的方面——集群的可观测性。也就是说，我们希望给集群也安装上“检查探针”，观察到集群的资源利用率和其他指标，让集群的整体运行状况对我们“透明可见”，这样才能更准确更方便地做好集群的运维工作。&lt;/p&gt;
&lt;p&gt;但是观测集群是不能用“探针”这种简单的方式的，所以今天我就带你一起来看看Kubernetes为集群提供的两种系统级别的监控项目：Metrics Server和Prometheus，以及基于它们的水平自动伸缩对象HorizontalPodAutoscaler。&lt;/p&gt;
&lt;h2 id="metrics-server"&gt;Metrics Server&lt;/h2&gt;
&lt;p&gt;如果你对Linux系统有所了解的话，也许知道有一个命令 top 能够实时显示当前系统的CPU和内存利用率，它是性能分析和调优的基本工具，非常有用。&lt;strong&gt;Kubernetes也提供了类似的命令，就是 kubectl top，不过默认情况下这个命令不会生效，必须要安装一个插件Metrics Server才可以。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Metrics Server是一个专门用来收集Kubernetes核心资源指标（metrics）的工具，它定时从所有节点的kubelet里采集信息，但是对集群的整体性能影响极小，每个节点只大约会占用1m的CPU和2MB的内存，所以性价比非常高。&lt;/p&gt;
&lt;p&gt;下面的&lt;a href="https://kubernetes.io/zh-cn/docs/tasks/debug/debug-cluster/resource-metrics-pipeline/#metrics-server" rel="nofollow noreferrer noopener"&gt;这张图&lt;/a&gt;来自Kubernetes官网，你可以对Metrics Server的工作方式有个大概了解：它调用kubelet的API拿到节点和Pod的指标，再把这些信息交给apiserver，这样kubectl、HPA就可以利用apiserver来读取指标了：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/972ce10cd5770a2881c3.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;在Metrics Server的项目网址（&lt;a href="https://github.com/kubernetes-sigs/metrics-server" rel="nofollow noreferrer noopener"&gt;https://github.com/kubernetes-sigs/metrics-server&lt;/a&gt;）可以看到它的说明文档和安装步骤，不过如果你已经按照[第17讲]用kubeadm搭建了Kubernetes集群，就已经具备了全部前提条件，接下来只需要几个简单的操作就可以完成安装。&lt;/p&gt;
&lt;p&gt;Metrics Server的所有依赖都放在了一个YAML描述文件里，你可以使用wget或者curl下载：&lt;/p&gt;
&lt;p&gt;但是在 kubectl apply 创建对象之前，我们还有两个准备工作要做。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一个工作，是修改YAML文件&lt;/strong&gt;。你需要在Metrics Server的Deployment对象里，加上一个额外的运行参数 –kubelet-insecure-tls，也就是这样：&lt;/p&gt;
&lt;p&gt;这是因为Metrics Server默认使用TLS协议，要验证证书才能与kubelet实现安全通信，而我们的实验环境里没有这个必要，加上这个参数可以让我们的部署工作简单很多（生产环境里就要慎用）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二个工作，是预先下载Metrics Server的镜像。&lt;/strong&gt;看这个YAML文件，你会发现Metrics Server的镜像仓库用的是gcr.io，下载很困难。好在它也有国内的镜像网站，你可以用[第17讲]里的办法，下载后再改名，然后把镜像加载到集群里的节点上。&lt;/p&gt;
&lt;p&gt;这里我给出一段Shell脚本代码，供你参考：&lt;/p&gt;
&lt;p&gt;两个准备工作都完成之后，我们就可以使用YAML部署Metrics Server了：&lt;/p&gt;
&lt;p&gt;Metrics Server属于名字空间“kube-system”，可以用 kubectl get pod 加上 -n 参数查看它是否正常运行：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/50fdf62115309af68e32.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;现在有了Metrics Server插件，我们就可以使用命令 kubectl top 来查看Kubernetes集群当前的资源状态了。它有&lt;strong&gt;两个子命令，node 查看节点的资源使用率，pod 查看Pod的资源使用率&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;由于Metrics Server收集信息需要时间，我们必须等一小会儿才能执行命令，查看集群里节点和Pod状态：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/559ee8944087393ffab9.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;从这个截图里你可以看到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集群里两个节点CPU使用率都不高，分别是8%和4%，但内存用的很多，master节点用了差不多一半（48%），而worker节点几乎用满了（89%）。&lt;/li&gt;
&lt;li&gt;名字空间“kube-system”里有很多Pod，其中apiserver最消耗资源，使用了75m的CPU和363MB的内存。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="horizontalpodautoscaler"&gt;HorizontalPodAutoscaler&lt;/h2&gt;
&lt;p&gt;有了Metrics Server，我们就可以轻松地查看集群的资源使用状况了，不过它另外一个更重要的功能是辅助实现应用的“&lt;strong&gt;水平自动伸缩&lt;/strong&gt;”。&lt;/p&gt;</description></item><item><title>31 网络通信：CNI是怎么回事？又是怎么工作的？</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/033/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/033/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/31%20%e7%bd%91%e7%bb%9c%e9%80%9a%e4%bf%a1%ef%bc%9aCNI%e6%98%af%e6%80%8e%e4%b9%88%e5%9b%9e%e4%ba%8b%ef%bc%9f%e5%8f%88%e6%98%af%e6%80%8e%e4%b9%88%e5%b7%a5%e4%bd%9c%e7%9a%84%ef%bc%9f.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="31-网络通信-cni是怎么回事-又是怎么工作的"&gt;31 网络通信：CNI是怎么回事？又是怎么工作的？&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;到现在，我们对Kubernetes已经非常熟悉了，它是一个集群操作系统，能够管理大量计算节点和运行在里面的应用。不过，还有一个很重要的基础知识我们还没有学习，那就是“网络通信”。&lt;/p&gt;
&lt;p&gt;早在“入门篇”的[第6讲]里，我们就简单介绍过Docker的网络模式，然后在“中级篇”的[第17讲]，我们又为Kubernetes安装了一个网络插件Flannel。这些都与网络相关，但也只是浅尝辄止，并没有太多深究。&lt;/p&gt;
&lt;p&gt;如果你是一个喜欢刨根问底的人，会不会很好奇：Flannel到底是如何工作的呢？它为什么能够让Kubernetes集群正常通信呢？还有没有其他网络插件呢？&lt;/p&gt;
&lt;p&gt;今天我们就来聊一下这个话题，讲讲Kubernetes的网络接口标准CNI，以及Calico、Cilium等性能更好的网络插件。&lt;/p&gt;
&lt;h2 id="kubernetes的网络模型"&gt;Kubernetes的网络模型&lt;/h2&gt;
&lt;p&gt;在学习Kubernetes的网络之前，我们还是要先简单回顾一下Docker的网络知识。&lt;/p&gt;
&lt;p&gt;你对Docker的null、host和bridge三种网络模式还有印象吗？这里我重新画了一张图，描述了Docker里最常用的bridge网络模式：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/50c43f9ca9647c5b1de5.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;Docker会创建一个名字叫“docker0”的网桥，默认是私有网段“172.17.0.0/16”。每个容器都会创建一个虚拟网卡对（veth pair），两个虚拟网卡分别“插”在容器和网桥上，这样容器之间就可以互联互通了。&lt;/p&gt;
&lt;p&gt;Docker的网络方案简单有效，但问题是它只局限在单机环境里工作，跨主机通信非常困难（需要做端口映射和网络地址转换）。&lt;/p&gt;
&lt;p&gt;针对Docker的网络缺陷，Kubernetes提出了一个自己的网络模型“&lt;strong&gt;IP-per-pod&lt;/strong&gt;”，能够很好地适应集群系统的网络需求，它有下面的这4点基本假设：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集群里的每个Pod都会有唯一的一个IP地址。&lt;/li&gt;
&lt;li&gt;Pod里的所有容器共享这个IP地址。&lt;/li&gt;
&lt;li&gt;集群里的所有Pod都属于同一个网段。&lt;/li&gt;
&lt;li&gt;Pod直接可以基于IP地址直接访问另一个Pod，不需要做麻烦的网络地址转换（NAT）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我画了一张Kubernetes网络模型的示意图，你可以看一下：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/4d39ab22ca2aaf75ae32.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;这个网络让Pod摆脱了主机的硬限制，是一个“平坦”的网络模型，很好理解，通信自然也非常简单。&lt;/p&gt;
&lt;p&gt;因为Pod都具有独立的IP地址，相当于一台虚拟机，而且直连互通，也就可以很容易地实施域名解析、负载均衡、服务发现等工作，以前的运维经验都能够直接使用，对应用的管理和迁移都非常友好。&lt;/p&gt;
&lt;h2 id="什么是cni"&gt;什么是CNI&lt;/h2&gt;
&lt;p&gt;Kubernetes定义的这个网络模型很完美，但要把这个模型落地实现就不那么容易了。所以Kubernetes就专门制定了一个标准：&lt;strong&gt;CNI&lt;/strong&gt;（Container Networking Interface）。&lt;/p&gt;
&lt;p&gt;CNI为网络插件定义了一系列通用接口，开发者只要遵循这个规范就可以接入Kubernetes，为Pod创建虚拟网卡、分配IP地址、设置路由规则，最后就能够实现“IP-per-pod”网络模型。&lt;/p&gt;
&lt;p&gt;依据实现技术的不同，CNI插件可以大致上分成“&lt;strong&gt;Overlay&lt;/strong&gt;”“&lt;strong&gt;Route&lt;/strong&gt;”和“&lt;strong&gt;Underlay&lt;/strong&gt;”三种。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Overlay&lt;/strong&gt;的原意是“覆盖”，是指它构建了一个工作在真实底层网络之上的“逻辑网络”，把原始的Pod网络数据封包，再通过下层网络发送出去，到了目的地再拆包。因为这个特点，它对底层网络的要求低，适应性强，缺点就是有额外的传输成本，性能较低。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Route&lt;/strong&gt;也是在底层网络之上工作，但它没有封包和拆包，而是使用系统内置的路由功能来实现Pod跨主机通信。它的好处是性能高，不过对底层网络的依赖性比较强，如果底层不支持就没办法工作了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Underlay&lt;/strong&gt;就是直接用底层网络来实现CNI，也就是说Pod和宿主机都在一个网络里，Pod和宿主机是平等的。它对底层的硬件和网络的依赖性是最强的，因而不够灵活，但性能最高。&lt;/p&gt;
&lt;p&gt;自从2015年CNI发布以来，由于它的接口定义宽松，有很大的自由发挥空间，所以社区里就涌现出了非常多的网络插件，我们之前在[第17讲]里提到的Flannel就是其中之一。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flannel&lt;/strong&gt;（&lt;a href="https://github.com/flannel-io/flannel/" rel="nofollow noreferrer noopener"&gt;https://github.com/flannel-io/flannel/&lt;/a&gt;）由CoreOS公司（已被Redhat收购）开发，最早是一种Overlay模式的网络插件，使用UDP和VXLAN技术，后来又用Host-Gateway技术支持了Route模式。Flannel简单易用，是Kubernetes里最流行的CNI插件，但它在性能方面表现不是太好，所以一般不建议在生产环境里使用。&lt;/p&gt;
&lt;p&gt;现在还有两个常用CNI插件：Calico、Cilium，我们做个简略的介绍。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/1591283919d9c917cf3c.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Calico&lt;/strong&gt;（&lt;a href="https://github.com/projectcalico/calico" rel="nofollow noreferrer noopener"&gt;https://github.com/projectcalico/calico&lt;/a&gt;）是一种Route模式的网络插件，使用BGP协议（Border Gateway Protocol）来维护路由信息，性能要比Flannel好，而且支持多种网络策略，具备数据加密、安全隔离、流量整形等功能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cilium&lt;/strong&gt;（&lt;a href="https://github.com/cilium/cilium" rel="nofollow noreferrer noopener"&gt;https://github.com/cilium/cilium&lt;/a&gt;）是一个比较新的网络插件，同时支持Overlay模式和Route模式，它的特点是深度使用了Linux eBPF技术，在内核层次操作网络数据，所以性能很高，可以灵活实现各种功能。在2021年它加入了CNCF，成为了孵化项目，是非常有前途的CNI插件。&lt;/p&gt;
&lt;h2 id="cni插件是怎么工作的"&gt;CNI插件是怎么工作的&lt;/h2&gt;
&lt;p&gt;Flannel比较简单，我们先以它为例看看CNI在Kubernetes里的工作方式。&lt;/p&gt;
&lt;p&gt;这里必须要说明一点，计算机网络很复杂，有IP地址、MAC地址、网段、网卡、网桥、路由等许许多多的概念，而且数据会流经多个设备，理清楚脉络比较麻烦，今天我们会做一个大概的描述，不会讲那些太底层的细节。&lt;/p&gt;
&lt;p&gt;我们先来在实验环境里用Deployment创建3个Nginx Pod，作为研究对象：&lt;/p&gt;
&lt;p&gt;使用命令 kubectl get pod 可以看到，有两个Pod运行在master节点上，IP地址分别是“10.10.0.3”“10.10.0.4”，另一个Pod运行在worker节点上，IP地址是“10.10.1.77”：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/6c1d639f5b89dba7eca2.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;Flannel默认使用的是基于VXLAN的Overlay模式，整个集群的网络结构我画了一张示意图，你可以对比一下Docker的网络结构：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/cae2d12c4f07ed0e2a3f.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;从单机的角度来看的话，Flannel的网络结构和Docker几乎是一模一样的，只不过网桥换成了“cni0”，而不是“docker0”。&lt;/p&gt;
&lt;p&gt;接下来我们来操作一下，看看Pod里的虚拟网卡是如何接入cni0网桥的。&lt;/p&gt;
&lt;p&gt;在Pod里执行命令 ip addr 就可以看到它里面的虚拟网卡“eth0”：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/bd0b94da60167550369f.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;你需要注意它的形式，第一个数字“3”是序号，意思是第3号设备，“@if45”就是它另一端连接的虚拟网卡，序号是45。&lt;/p&gt;</description></item><item><title>32 实战演练：玩转Kubernetes（3）</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/034/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/034/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/32%20%e5%ae%9e%e6%88%98%e6%bc%94%e7%bb%83%ef%bc%9a%e7%8e%a9%e8%bd%acKubernetes%ef%bc%883%ef%bc%89.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="32-实战演练-玩转kubernetes-3"&gt;32 实战演练：玩转Kubernetes（3）&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;到今天，我们的“高级篇”课程也要结束了。比起前面的“初级篇”“中级篇”来说，这里的知识点比较多，难度也要高一些。如果你能够一篇不漏地学习下来，相信一定对Kubernetes有更深层次的认识和理解。&lt;/p&gt;
&lt;p&gt;今天的这节课还是来对前面的知识做回顾与总结，提炼出文章里的学习要点和重点，你也可以顺便检验一下自己的掌握程度，试试在不回看课程的情况下，自己能不能流畅说出关联的操作细节。&lt;/p&gt;
&lt;p&gt;复习之后，我们就来进行最后一次实战演练了。首先会继续改进贯穿课程始终的WordPress网站，把MariaDB改成StatefulSet，加上NFS持久化存储；然后我们会在Kubernetes集群里安装Dashboard，综合实践Ingress、namespace的用法。&lt;/p&gt;
&lt;h2 id="要点回顾一-api对象"&gt;要点回顾一：API对象&lt;/h2&gt;
&lt;p&gt;“高级篇”可以分成三个部分，第一部分讲的是PersistentVolume、StatefulSet等API对象。&lt;/p&gt;
&lt;p&gt;（[24讲]）&lt;strong&gt;PersistentVolume简称PV，是Kubernetes对持久化存储的抽象&lt;/strong&gt;，代表了LocalDisk、NFS、Ceph等存储设备，和CPU、内存一样，属于集群的公共资源。&lt;/p&gt;
&lt;p&gt;因为不同存储设备之间的差异很大，为了更好地描述PV特征，就出现了StorageClass，它的作用是分类存储设备，让我们更容易去选择PV对象。&lt;/p&gt;
&lt;p&gt;PV一般由系统管理员来创建，我们如果要使用PV就要用PVC（PersistentVolumeClaim）去申请，说清楚需求的容量、访问模式等参数，然后Kubernetes就会查找最合适的PV分配给我们使用。&lt;/p&gt;
&lt;p&gt;（[25讲]）手动创建PV的工作量很大，麻烦而且容易出错，所以就有了“动态存储卷”的概念，需要&lt;strong&gt;在StorageClass里绑定一个Provisioner对象，由它来代替人工，根据PVC自动创建出符合要求的PV&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;有了PV和PVC，我们就可以在Pod里用“persistentVolumeClaim”来引用PVC，创建出可供容器使用的Volume，然后在容器里用“volumeMounts”把它挂载到某个路径上，这样容器就可以读写PV，实现数据的持久化存储了。&lt;/p&gt;
&lt;p&gt;（[26讲]）持久化存储的一个重要应用领域就是保存应用的状态数据，&lt;strong&gt;管理有状态的应用，就要使用新的对象StatefulSet&lt;/strong&gt;，可以认为它是管理无状态应用对象Deployment的一个特例。&lt;/p&gt;
&lt;p&gt;StatefulSet对象的YAML描述和Deployment非常像，“spec”里只是多了一个“serviceName”字段，但它部署应用的方式却与Deployment差距很大。&lt;/p&gt;
&lt;p&gt;Deployment创建的Pod是随机的名字，而StatefulSet会对Pod顺序编号、顺序创建，保证应用有一个确定的启动先后次序，这样就可以实现主从、主备等关系。&lt;/p&gt;
&lt;p&gt;在使用Service为StatefulSet创建服务的时候，它也会为每个Pod单独创建域名，同样也是顺序编号，保证Pod有稳定的网络标识，外部用户就可以用这个域名来准确地访问到某个具体的Pod。&lt;/p&gt;
&lt;p&gt;StatefulSet还使用“volumeClaimTemplates”字段来定义持久化存储，里面其实就是一个PVC，每个Pod可以用这个模板来生成自己的PVC去申请PV，实现存储卷与Pod的独立绑定。&lt;/p&gt;
&lt;p&gt;通过&lt;strong&gt;启动顺序、稳定域名和存储模板&lt;/strong&gt;这三个关键能力，StatefulSet就可以很好地处理Redis、MySQL等有状态应用了。&lt;/p&gt;
&lt;h2 id="要点回顾二-应用管理"&gt;要点回顾二：应用管理&lt;/h2&gt;
&lt;p&gt;“高级篇”第二部分讲的是应用管理，包括滚动更新、资源配额和健康检查等内容。&lt;/p&gt;
&lt;p&gt;（[27讲]）在Kubernetes里部署好应用后，我们还需要对它做持续的运维管理，其中一项任务是版本的更新和回退。&lt;/p&gt;
&lt;p&gt;版本更新很简单，只要编写一个新的YAML（Deployment、DaemonSet、StatefulSet），再用 kubectl apply 应用就可以了。Kubernetes采用的是&lt;strong&gt;“滚动更新”策略，实际上是两个同步进行的“扩容”和“缩容”动作&lt;/strong&gt;，这样在更新的过程中始终会有Pod处于可用状态，能够平稳地对外提供服务。&lt;/p&gt;
&lt;p&gt;应用的更新历史可以用命令 kubectl rollout history 查看，如果有什么意外，就可以用 kubectl rollout undo 来回退。这两个命令相当于给我们的更新流程上了一个保险，可以放心大胆操作，失败就用“S/L大法”。&lt;/p&gt;
&lt;p&gt;（[28讲]）为了让Pod里的容器能够稳定运行，我们可以采用&lt;strong&gt;资源配额&lt;/strong&gt;和&lt;strong&gt;检查探针&lt;/strong&gt;这两种手段。&lt;/p&gt;
&lt;p&gt;资源配额能够限制容器申请的CPU和内存数量，不至于过多或者过少，保持在一个合理的程度，更有利于Kubernetes调度。&lt;/p&gt;
&lt;p&gt;检查探针是Kubernetes内置的应用监控工具，有Startup、Liveness、Readiness三种，分别探测启动、存活、就绪状态，探测的方式也有exec、tcpSocket、httpGet三种。组合运用这些就可以灵活地检查容器的状态，Kubernetes发现不可用就会重启容器，让应用在总体上处于健康水平。&lt;/p&gt;
&lt;h2 id="要点回顾三-集群管理"&gt;要点回顾三：集群管理&lt;/h2&gt;
&lt;p&gt;“高级篇”第三部分讲的是集群管理，有名字空间、系统监控和网络通信等知识点。&lt;/p&gt;
&lt;p&gt;（[29讲]）Kubernetes的集群里虽然有很多计算资源，但毕竟是有限的，除了要给Pod加上资源配额，我们也要为集群加上资源配额，方法就是用名字空间，把整体的资源池切分成多个小块，按需分配给不同的用户使用。&lt;/p&gt;
&lt;p&gt;名字空间的资源配额使用的是“ResourceQuota”，除了基本的CPU和内存，它还能够限制存储容量和各种API对象的数量，这样就可以避免多用户互相挤占，更高效地利用集群资源。&lt;/p&gt;
&lt;p&gt;（[30讲]）系统监控是集群管理的另一个重要方面，Kubernetes提供了Metrics Server和Prometheus两个工具：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Metrics Server&lt;/strong&gt;专门用来收集Kubernetes核心资源指标，可以用 kubectl top 来查看集群的状态，它也是水平自动伸缩对象HorizontalPodAutoscaler的前提条件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prometheus&lt;/strong&gt;，继Kubernetes之后的第二个CNCF毕业项目，是云原生监控领域的“事实标准”，在集群里部署之后就可以用Grafana可视化监控各种指标，还可以集成自动报警等功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（[31讲]）对于底层的基础网络设施，Kubernetes定义了平坦的网络模型“IP-per-pod”，实现它就要符合CNI标准。常用的网络插件有Flannel、Calico、Cilium等，Flannel使用Overlay模式，性能较低，Calico使用Route模式，性能较高。&lt;/p&gt;
&lt;p&gt;现在，“高级篇”的众多知识要点我们都完整地过了一遍，你是否已经都理解、掌握了它们呢？&lt;/p&gt;
&lt;h2 id="搭建wordpress网站"&gt;搭建WordPress网站&lt;/h2&gt;
&lt;p&gt;接下来我们就来在[第22讲]的基础上继续优化WordPress网站，其中的关键是让数据库MariaDB实现数据持久化。&lt;/p&gt;
&lt;p&gt;网站的整体架构图变化不大，前面的Nginx、WordPress还是原样，只需要修改MariaDB：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/cdcf35628bbb08bde8a6.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;因为MariaDB由Deployment改成了StatefulSet，所以我们要修改YAML，添加“serviceName”“volumeClaimTemplates”这两个字段，定义网络标识和NFS动态存储卷，然后在容器部分用“volumeMounts”挂载到容器里的数据目录“/var/lib/mysql”。&lt;/p&gt;
&lt;p&gt;修改后的YAML就是这个样子：&lt;/p&gt;
&lt;p&gt;改完MariaDB，我们还要再对WordPress做一点小修改。&lt;/p&gt;
&lt;p&gt;还记得吗？StatefulSet管理的每个Pod都有自己的域名，所以要把WordPress的环境变量改成MariaDB的新名字，也就是“&lt;strong&gt;maria-sts-0.maria-svc&lt;/strong&gt;”：&lt;/p&gt;
&lt;p&gt;改完这两个YAML，我们就可以逐个创建MariaDB、WordPress、Ingress等对象了。&lt;/p&gt;
&lt;p&gt;和之前一样，访问NodePort的“30088”端口，或者是用Ingress Controller的“wp.test”域名，都可以进入WordPress网站：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/70d91ce16c4fe5710c47.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;StatefulSet的持久化存储是否生效了呢？&lt;/p&gt;
&lt;p&gt;你可以把这些对象都删除后重新创建，再进入网站，看看是否原来的数据依然存在。或者更简单一点，直接查看NFS的存储目录，应该可以看到MariaDB生成的一些数据库文件：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/2ca1b3911c4d62dfbead.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;这两种方式都能够证明，我们的MariaDB使用StatefulSet部署后数据已经保存在了磁盘上，不会因为对象的销毁而丢失。&lt;/p&gt;</description></item><item><title>33 视频：高级篇实操总结</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/035/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/035/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/33%20%e8%a7%86%e9%a2%91%ef%bc%9a%e9%ab%98%e7%ba%a7%e7%af%87%e5%ae%9e%e6%93%8d%e6%80%bb%e7%bb%93.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="33-视频-高级篇实操总结"&gt;33 视频：高级篇实操总结&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;在“高级篇”的这段时间里，我们学习了PersistentVolume、PersistentVolumeClaim、StatefulSet等API对象，具备了部署有状态应用的能力，然后还学习了管理运维应用和集群的多种方式，包括滚动更新、资源配额、检查探针、名字空间、系统监控等等。&lt;/p&gt;
&lt;p&gt;掌握了这些知识，现在的你再回想一下三个月前学习第一节课的时候，有没有发现其实Kubernetes也没有当初自己想象得那么高深莫测呢？&lt;/p&gt;
&lt;p&gt;今天也是我们课程的最后一节正课，还是会用视频的形式，把“高级篇”里的一些重要的部分都实际演示出来，结合前面的文字和图片，你可以再次加深对Kubernetes的印象。&lt;/p&gt;
&lt;p&gt;接下来就一起开始我们的学习吧。&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;PV和PVC———-
我们先来创建一个本地存储卷，也就是PV。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在Master和Worker节点的“/tmp”目录里，先建立一个“host-10m-pv”的目录，表示一个只有10MB容量的存储设备：&lt;/p&gt;
&lt;p&gt;然后我们使用YAML来定义这个PV对象：&lt;/p&gt;
&lt;p&gt;它的kind是PersistentVolume，名字是“host-10m-pv”，后面“spec”里的字段都很重要，描述了PV的基本信息。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;因为这个PV是我们手动管理的，“storageClassName”的名字你可以任意起，这里我写的是“host-test”。&lt;/li&gt;
&lt;li&gt;“accessModes”定义了存储设备的访问模式，用的是最简单的“ReadWriteOnce”，可读可写，但只能被这个节点的Pod挂载。&lt;/li&gt;
&lt;li&gt;“capacity”定义了存储的容量，因为是测试，就设置成了10MB。&lt;strong&gt;注意，定义存储容量使用的是国际标准单位，必须要写成Ki/Mi/Gi的形式，否则就会出错。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;最后一个字段“hostPath”指定了存储卷的本地路径，也就是刚才节点上创建的目录“/tmp/host-10m-pv/”。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;现在我们用 kubectl apply 创建PV对象：&lt;/p&gt;
&lt;p&gt;再用 kubectl get 查看状态：&lt;/p&gt;
&lt;p&gt;就可以看到Kubernetes里已经有这个存储卷了。它的容量是10MB，访问模式是RWO，状态显示的是“Available”，StorageClass是我们自己定义的“host-test”。&lt;/p&gt;
&lt;p&gt;接下来我们再来定义PVC对象：&lt;/p&gt;
&lt;p&gt;它的名字是“host-5m-pvc”，“storageClassName”名字是“host-test”，访问模式是“ReadWriteOnce”，在“resources”字段里向Kubernetes申请使用5MB的存储。&lt;/p&gt;
&lt;p&gt;PVC比较简单，不像PV那样包含磁盘路径等存储细节。我们还是用 kubectl apply 创建对象：&lt;/p&gt;
&lt;p&gt;再用 kubectl get 查看PV和PVC的状态：&lt;/p&gt;
&lt;p&gt;就会发现已经成功分配了存储卷，状态是“Bound”。虽然PVC申请的是5MB，但系统里只有一个10MB的PV可以用，没有更合适的，所以Kubernetes也只能把它俩绑定在一起。&lt;/p&gt;
&lt;p&gt;下面要做的就是把这个PVC挂载进Pod里了，来看这个YAML文件：&lt;/p&gt;
&lt;p&gt;它在“volumes”里&lt;strong&gt;用“persistentVolumeClaim”声明了PVC的名字“host-5m-pvc”，这样就把PVC和Pod联系起来了&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;后面的“volumeMounts”就是挂载存储卷，这个你应该比较熟悉了吧，用name指定volume的名字，用path指定路径，这里就是挂载到了容器里的“/tmp”目录。&lt;/p&gt;
&lt;p&gt;现在我们创建这个Pod，再查看它的状态：&lt;/p&gt;
&lt;p&gt;可以看到它被Kubernetes调到到了worker节点上，让我们用 kubectl exec 进入容器，执行Shell命令，生成一个文本文件：&lt;/p&gt;
&lt;p&gt;然后我们登录worker节点，看一下PV对应的目录“/tmp/host-10m-pv”：&lt;/p&gt;
&lt;p&gt;输出的内容刚好就是我们在容器里生成的数据，这就说明Pod的数据确实已经持久化到了PV定义的存储设备上。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NFS网络存储卷————
下面我们来看看在Kubernetes里使用NFS网络存储卷的用法。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NFS服务器和客户端、还有NFS Provisioner的安装过程我就略过了，你可以对照着[第25讲]一步步来。&lt;/p&gt;
&lt;p&gt;安装完成之后，可以看一下Provisioner的运行状态：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意一定要配置好NFS Provisioner里的IP地址和目录&lt;/strong&gt;，如果地址错误或者目录不存在，那么Pod就无法正常启动，需要用 kubectl logs 来查看错误原因。&lt;/p&gt;
&lt;p&gt;来看一下NFS默认的StorageClass定义：&lt;/p&gt;
&lt;p&gt;它的名字是“nfs-client”，这个很关键，我们必须在PVC里写上它才能够找到NFS Provisioner。&lt;/p&gt;
&lt;p&gt;现在我们来看PVC和Pod的定义：&lt;/p&gt;
&lt;p&gt;这个PVC申请的是10MB，使用的访问模式是“ReadWriteMany”，这是因为NFS是网络共享存储，支持多个Pod同时挂载。&lt;/p&gt;
&lt;p&gt;在Pod里我们还是用“persistentVolumeClaim”声明PVC，“volumeMounts”挂载存储卷，目标还是容器里的“/tmp”目录。&lt;/p&gt;
&lt;p&gt;使用 kubectl apply 应用这个YAML，就会创建好PVC和Pod，用 kubectl get 查看一下集群里的PV和PVC：&lt;/p&gt;</description></item><item><title>加餐 docker-compose：单机环境下的容器编排工具</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/036/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/036/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/%e5%8a%a0%e9%a4%90%20docker-compose%ef%bc%9a%e5%8d%95%e6%9c%ba%e7%8e%af%e5%a2%83%e4%b8%8b%e7%9a%84%e5%ae%b9%e5%99%a8%e7%bc%96%e6%8e%92%e5%b7%a5%e5%85%b7.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="加餐-docker-compose-单机环境下的容器编排工具"&gt;加餐 docker-compose：单机环境下的容器编排工具&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;我们的课程学到了这里，你已经对Kubernetes有相当程度的了解了吧。&lt;/p&gt;
&lt;p&gt;作为云原生时代的操作系统，Kubernetes源自Docker又超越了Docker，依靠着它的master/node架构，掌控成百上千台的计算节点，然后使用YAML语言定义各种API对象来编排调度容器，实现了对现代应用的管理。&lt;/p&gt;
&lt;p&gt;不过，你有没有觉得，在Docker和Kubernetes之间，是否还缺了一点什么东西呢？&lt;/p&gt;
&lt;p&gt;Kubernetes的确是非常强大的容器编排平台，但强大的功能也伴随着复杂度和成本的提升，不说那几十个用途各异的API对象，单单说把Kubernetes运行起来搭建一个小型的集群，就需要耗费不少精力。但是，有的时候，我们只是想快速启动一组容器来执行简单的开发、测试工作，并不想承担Kubernetes里apiserver、scheduler、etcd这些组件的运行成本。&lt;/p&gt;
&lt;p&gt;显然，在这种简易任务的应用场景里，Kubernetes就显得有些“笨重”了。即使是“玩具”性质的minikube、kind，对电脑也有比较高的要求，会“吃”掉不少的计算资源，属于“大材小用”。&lt;/p&gt;
&lt;p&gt;那到底有没有这样的工具，既像Docker一样轻巧易用，又像Kubernetes一样具备容器编排能力呢？&lt;/p&gt;
&lt;p&gt;今天我就来介绍docker-compose，它恰好满足了刚才的需求，是一个在单机环境里轻量级的容器编排工具，填补了Docker和Kubernetes之间的空白位置。&lt;/p&gt;
&lt;h2 id="什么是docker-compose"&gt;什么是docker-compose&lt;/h2&gt;
&lt;p&gt;还是让我们从Docker诞生那会讲起。&lt;/p&gt;
&lt;p&gt;在Docker把容器技术大众化之后，Docker周边涌现出了数不胜数的扩展、增强产品，其中有一个名字叫“Fig”的小项目格外令人瞩目。&lt;/p&gt;
&lt;p&gt;Fig为Docker引入了“容器编排”的概念，使用YAML来定义容器的启动参数、先后顺序和依赖关系，让用户不再有Docker冗长命令行的烦恼，第一次见识到了“声明式”的威力。&lt;/p&gt;
&lt;p&gt;Docker公司也很快意识到了Fig这个小工具的价值，于是就在2014年7月把它买了下来，集成进Docker内部，然后改名成了“docker-compose”。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/3755760d9fcbe39cb059.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;从这段简短的历史中你可以看到，虽然docker-compose也是容器编排技术，也使用YAML，但它的基因与Kubernetes完全不同，走的是Docker的技术路线，所以在设计理念和使用方法上有差异就不足为怪了。&lt;/p&gt;
&lt;p&gt;docker-compose自身的定位是管理和运行多个Docker容器的工具，很显然，它没有Kubernetes那么“宏伟”的目标，只是用来方便用户使用Docker而已，所以学习难度比较低，上手容易，很多概念都是与Docker命令一一对应的。&lt;/p&gt;
&lt;p&gt;但这有时候也会给我们带来困扰，毕竟docker-compose和Kubernetes同属容器编排领域，用法不一致就容易导致认知冲突、混乱。考虑到这一点，我们在学习docker-compose的时候就要把握一个“度”，够用就行，不要太过深究，否则会对Kubernetes的学习造成一些不良影响。&lt;/p&gt;
&lt;h2 id="如何使用docker-compose"&gt;如何使用docker-compose&lt;/h2&gt;
&lt;p&gt;docker-compose的安装非常简单，它在GitHub（&lt;a href="https://github.com/docker/compose" rel="nofollow noreferrer noopener"&gt;https://github.com/docker/compose&lt;/a&gt;）上提供了多种形式的二进制可执行文件，支持Windows、macOS、Linux等操作系统，也支持x86_64、arm64等硬件架构，可以直接下载。&lt;/p&gt;
&lt;p&gt;在Linux上安装的Shell命令我放在这里了，用的是最新的2.6.1版本：&lt;/p&gt;
&lt;p&gt;安装完成之后，来看一下它的版本号，命令是 docker-compose version，用法和 docker version 是一样的：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/bd88f12cd7e934fd44f3.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;接下来，我们就要编写YAML文件，来管理Docker容器了，先用[第7讲]里的私有镜像仓库作为示范吧。&lt;/p&gt;
&lt;p&gt;docker-compose里管理容器的核心概念是“&lt;strong&gt;service&lt;/strong&gt;”。注意，它与Kubernetes里的 Service 虽然名字很像，但却是完全不同的东西。docker-compose里的“service”就是一个容器化的应用程序，通常是一个后台服务，用YAML定义这些容器的参数和相互之间的关系。&lt;/p&gt;
&lt;p&gt;如果硬要和Kubernetes对比的话，和“service”最像的API对象应该算是Pod里的container了，同样是管理容器运行，但docker-compose的“service”又融合了一些Service、Deployment的特性。&lt;/p&gt;
&lt;p&gt;下面的这个就是私有镜像仓库Registry的YAML文件，关键字段就是“&lt;strong&gt;services&lt;/strong&gt;”，对应的Docker命令我也列了出来：&lt;/p&gt;
&lt;p&gt;把它和Kubernetes对比一下，你会发现它和Pod定义非常像，“services”相当于Pod，而里面的“service”就相当于“spec.containers”：&lt;/p&gt;
&lt;p&gt;比如用 image 声明镜像，用 ports 声明端口，很容易理解，只是在用法上有些不一样，像端口映射用的就还是Docker的语法。&lt;/p&gt;
&lt;p&gt;由于docker-compose的字段定义在官网（&lt;a href="https://docs.docker.com/compose/compose-file/" rel="nofollow noreferrer noopener"&gt;https://docs.docker.com/compose/compose-file/&lt;/a&gt;）上有详细的说明文档，我就不在这里费口舌解释了，你可以自行参考。&lt;/p&gt;
&lt;p&gt;需要提醒的是，在docker-compose里，每个“service”都有一个自己的名字，它同时也是这个容器的唯一网络标识，有点类似Kubernetes里 Service 域名的作用。&lt;/p&gt;
&lt;p&gt;好，现在我们就可以启动应用了，命令是 docker-compose up -d，同时还要用 -f 参数来指定YAML文件，和 kubectl apply 差不多：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/d71721bd8459076c4b16.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;因为docker-compose在底层还是调用的Docker，所以它启动的容器用 docker ps 也能够看到：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/1e9f6071220211045f77.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;不过，我们用 docker-compose ps 能够看到更多的信息：&lt;/p&gt;</description></item><item><title>加餐 谈谈Kong Ingress Controller</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/037/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/037/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/%e5%8a%a0%e9%a4%90%20%e8%b0%88%e8%b0%88Kong%20Ingress%20Controller.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="加餐-谈谈kong-ingress-controller"&gt;加餐 谈谈Kong Ingress Controller&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;课程已经完结三个多月了，还记得结课时我说的那句话吗：“是终点，更是起点”，课程的完结绝不意味着我们终止对Kubernetes的钻研，相反，不论你我，都会在这个学习的道路上持续地走下去。&lt;/p&gt;
&lt;p&gt;当初开课时，我计划了很多内容，不过Kubernetes的领域实在太广，加上我日常工作比较忙，时间和精力有限，所以一些原定的知识点没有来得及展现，比较可惜，我一直想找机会做个补偿。&lt;/p&gt;
&lt;p&gt;这几天开发任务略微空闲了些，我就又回到了专栏，准备使用另一个流行的工具：Kong Ingress Controller，再来讲讲对Kubernetes集群管理非常重要的Ingress。&lt;/p&gt;
&lt;h2 id="认识kong-ingress-controller"&gt;认识Kong Ingress Controller&lt;/h2&gt;
&lt;p&gt;我们先快速回顾一下Ingress的知识（[第21讲]）。&lt;/p&gt;
&lt;p&gt;Ingress类似Service，基于HTTP/HTTPS协议，是七层负载均衡规则的集合，但它自身没有管理能力，必须要借助Ingress Controller才能控制Kubernetes集群的进出口流量。&lt;/p&gt;
&lt;p&gt;所以，基于Ingress的定义，就出现了各式各样的Ingress Controller实现。&lt;/p&gt;
&lt;p&gt;我们已经见过了Nginx官方开发的Nginx Ingress Controller，但它局限于Nginx自身的能力，Ingress、Service等对象更新时必须要修改静态的配置文件，再重启进程（reload），在变动频繁的微服务系统里就会引发一些问题。&lt;/p&gt;
&lt;p&gt;而今天要说的&lt;strong&gt;Kong Ingress Controller&lt;/strong&gt;，则是站在了Nginx这个巨人的肩膀之上，基于OpenResty和内嵌的LuaJIT环境，实现了完全动态的路由变更，消除了reload的成本，运行更加平稳，而且还有很多额外的增强功能，非常适合那些对Kubernetes集群流量有更高、更细致管理需求的用户（&lt;a href="https://konghq.com/solutions/build-on-kubernetes" rel="nofollow noreferrer noopener"&gt;图片来源Kong官网&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/ca1ce10e7d8d15573873.webp" alt="图片"&gt;&lt;/p&gt;
&lt;h2 id="安装kong-ingress-controller"&gt;安装Kong Ingress Controller&lt;/h2&gt;
&lt;p&gt;接下来我们就来看看如何在Kubernetes集群里引入Kong Ingress Controller。&lt;/p&gt;
&lt;p&gt;简单起见，这次我选择了minikube环境，版本还是1.25.2，对应的Kubernetes也是之前的1.23.3：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/668abf65f173e2686497.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;目前Kong Ingress Controller的最新版本是2.7.0，你可以从GitHub上(&lt;a href="https://github.com/Kong/kubernetes-ingress-controller" rel="nofollow noreferrer noopener"&gt;https://github.com/Kong/kubernetes-ingress-controller&lt;/a&gt;)直接获取它的源码包：&lt;/p&gt;
&lt;p&gt;Kong Ingress Controller安装所需的YAML文件，都存放在解压缩后的“deploy”目录里，提供“有数据库”和“无数据库”两种部署方式，这里我选择了最简单的“无数据库”方式，只需要一个 all-in-one-dbless.yaml 就可以完成部署工作，也就是执行这条 kubectl apply 命令：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/486b5b7fa8e953e98cf2.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;我们可以再对比一下两种 Ingress Controller的安装方式。Nginx Ingress Controller是由多个分散的YAML文件组成的，需要顺序执行多次 kubectl apply 命令，有点麻烦；&lt;strong&gt;而Kong Ingress Controller则把Namespace、RBAC、Secret、CRD等对象都合并在了一个文件里，安装很方便，同时也不会发生遗忘创建资源的错误。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;安装之后，Kong Ingress Controller会创建一个新的名字空间“kong”，里面有一个默认的Ingress Controller，还有对应的Service，你可以用 kubectl get 来查看：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/5352dd6afe29e48189c9.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;看这里的截图，你可能会注意到，在 kubectl get pod 输出的“READY”列里显示的是“2/2”，意思是这个Pod里有两个容器。&lt;/p&gt;</description></item><item><title>结束语 是终点，更是起点</title><link>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/038/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://go.botsarena.tech/docs/resources/cloud-native-web3-fulltext/kubernetes-practical-intro-course/038/</guid><description>&lt;div class="goclub-source-note"&gt;来源：&lt;a href="https://study.disign.me/document/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/%e7%bb%93%e6%9d%9f%e8%af%ad%20%e6%98%af%e7%bb%88%e7%82%b9%ef%bc%8c%e6%9b%b4%e6%98%af%e8%b5%b7%e7%82%b9.md" target="_blank" rel="noopener noreferrer"&gt;技术书栈原文&lt;/a&gt;。本页按授权迁移至 GoClub。&lt;/div&gt;
&lt;h1 id="结束语-是终点-更是起点"&gt;结束语 是终点，更是起点&lt;/h1&gt;
&lt;p&gt;你好，我是Chrono。&lt;/p&gt;
&lt;p&gt;经过近三个月的刻苦努力，我们的Kubernetes学习之旅在今天就抵达“终点站”了。&lt;/p&gt;
&lt;p&gt;三个月的时间，说长不长，说短不短，所谓“一期一会”，我非常珍惜与你共同度过的这段时间，同时也感谢你对专栏的支持和热情留言，正是由于我们彼此的帮助和鼓励，才让这段光阴变得更有意义、更加精彩。&lt;/p&gt;
&lt;p&gt;分别之际，我再来聊聊我自己的学习经历和建议吧，就当做是一个简单的“复盘”。&lt;/p&gt;
&lt;h2 id="我的kubernetes学习经验"&gt;我的Kubernetes学习经验&lt;/h2&gt;
&lt;p&gt;其实在“开篇词”里已经说过了我学习Kubernetes的过程，不过作为“前车之鉴”，我觉得有必要再回顾一下，相信也有不少同学对我是如何学习Kubernetes会比较感兴趣，而且这段经历也有一点参考的价值。&lt;/p&gt;
&lt;p&gt;首先要说的是，我属于研发出身，技术背景是C/C++/Nginx，所以在学习的过程中就会下意识地从这个角度去筛选知识点，偏重应用的开发和部署，对系统的安装、运维、管理则关注的不是太多。&lt;/p&gt;
&lt;p&gt;这里我也给你提个醒，&lt;strong&gt;学Kubernetes最好是结合自己的实际情况，定个“小目标”&lt;/strong&gt;，比如“我要学会在Kubernetes里开发云原生应用”“我要运维Kubernetes的监控系统”“我要搭建出高可用的Kubernetes生产系统”等等，而我当初的目标就是“要搞明白Nginx Ingress Controller的用法”。&lt;/p&gt;
&lt;p&gt;有了这样比较明确的目标，你就会有方向、有重点地去研究Kubernetes，方便检查自己的学习进度，也更容易集中精力“钻进去”，否则漫无目的学习就很容易迷失在Kubernetes的知识海洋里（&lt;a href="https://www.itopstimes.com/contain/itops-times-news-digest-platform9s-kubernetes-managed-apps-kontena-pharos-2-4-and-arm-data-center-softwares-new-network-analytics-capabilities/" rel="nofollow noreferrer noopener"&gt;图片来源&lt;/a&gt;），也很难构建起完整的知识体系。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/a54a7b0e5825bf8dea6c.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;和大多数人一样，我一开始学Kubernetes也是困难重重，主要原因还是Kubernetes是一个全新的系统，上来就要面对一大堆“不知所云”的概念（Pod、ConfigMap、Deployment等等），没有任何以往的经验可以借鉴，完全要从零开始。&lt;/p&gt;
&lt;p&gt;初学者想要翻越这个“高山”必须要下苦功夫、花大力气，&lt;strong&gt;我个人感觉这个过程没有捷径可走，必须反复阅读思考，再通过做实验来加深印象&lt;/strong&gt;。只要度过了这个“危险期”，理解了底层知识，打好了基础，后面的学习就会变得轻松一些了。&lt;/p&gt;
&lt;p&gt;说到做实验，我认为这也是学习Kubernetes的一个非常重要的手段。毕竟Kubernetes是一个“操作系统”，如果仅仅是看文字资料“纸上谈兵”，不真正上手演练，是很难弄清楚它的工作原理和运行机制的。&lt;/p&gt;
&lt;p&gt;最初我用的就是minikube，简单方便，功能也很齐全，对于Kubernetes入门来说非常合适，可以快速上手。&lt;/p&gt;
&lt;p&gt;不过因为minikube是基于Docker虚拟的，在节点和网络等方面离真实环境还是有一些差距，所以我也建议你在对Kubernetes有了比较深入的了解之后还是应该改换成kubeadm，这样才能够更透彻地学习研究Kubernetes。&lt;/p&gt;
&lt;p&gt;在学习的过程中我还有两个“最佳实践”，一个是&lt;strong&gt;勤记笔记&lt;/strong&gt;，另一个是&lt;strong&gt;画思维导图&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;俗话说“好记性不如烂笔头”，资料看得太多，大脑不可能全记住，我们就要及时把阅读时的思考和体会写下来。&lt;/p&gt;
&lt;p&gt;不过你也不必强求笔记完整详细，短短一两句话、简单的几个链接都是有价值的，等到笔记积累到一定的数量，就可以再花一些时间做个归纳和浓缩，这个时候就会用到思维导图。&lt;/p&gt;
&lt;p&gt;我想你应该对思维导图很熟悉吧，它的树状发散的形式很好地符合了人的自然思维模式，可以想到哪儿就写到哪儿，不需要什么心理负担，而且我们还可以给条目加各种小标记，条目之间还可以互相引用，用视觉效果来强化学习。&lt;/p&gt;
&lt;p&gt;把碎片化的笔记和有结构的思维导图结合起来，我们就能更顺畅地整理思路、总结经验，把零散的想法、概念分类合并，逐渐就能掌握系统、全面的知识了。&lt;/p&gt;
&lt;p&gt;作为示范，我把刚才说的这些也画成了思维导图。你看，有了图形化、可视化，知识点学习吸收起来是不是就更容易一些呢。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/99d5403d9b589744967b.webp" alt="图片"&gt;&lt;/p&gt;
&lt;h2 id="今后要怎么学kubernetes"&gt;今后要怎么学Kubernetes&lt;/h2&gt;
&lt;p&gt;课程虽然马上要结束了，但在“终点站”之外，Kubernetes的世界才刚刚展现在你的面前。这个世界是如此的广阔宏大，我们的Kubernetes课程可以说是只走出了“万里长征的第一步”，今后的道路将要去向何处，就要由你自己来把握了。&lt;/p&gt;
&lt;p&gt;接下来我就来说说四个可能的方向吧，你可以把它们看成是学习Kubernetes的“攻略指引”，帮助你走出属于自己的路。&lt;/p&gt;
&lt;p&gt;第一个是阅读Kubernetes官网上的文档。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kubernetes官网&lt;/strong&gt;（&lt;a href="https://kubernetes.io/zh-cn/docs/home/" rel="nofollow noreferrer noopener"&gt;https://kubernetes.io/zh-cn/docs/home/&lt;/a&gt;）里的资料非常丰富详细，包括入门介绍、安装指导、基本概念、应用教程、运维任务、参考手册等等。&lt;/p&gt;
&lt;p&gt;当然了，官网文档不是完全面向初学者的，不像我们的课程那样“循序渐进”，写得也不都是那么通俗易懂，要有一定的基础才能够看得下去。但它的优势就是全面、权威，覆盖了Kubernetes的每一个特性，你对Kubernetes有任何的疑惑和不解，都能够在这些文档里找到答案。&lt;/p&gt;
&lt;p&gt;不过官网文档太多太杂也对我们的学习造成了困难，想要去按部就班地查找知识点会很麻烦，这个时候就要善用它的搜索功能了，用关键字来快速定位文章、页面，节约我们的时间和精力。&lt;/p&gt;
&lt;p&gt;第二个学习方向是看Kubernetes的博客。&lt;/p&gt;
&lt;p&gt;官网上的文档只是描述了Kubernetes的现状，而没有讲它的历史，想要知道Kubernetes里的这些API对象是怎么设计出来的，怎么一步步发展到今天的这个样子，就要去看它的技术博客文章了。&lt;/p&gt;
&lt;p&gt;这里我推荐你去阅读&lt;strong&gt;英文博客&lt;/strong&gt;（&lt;a href="https://kubernetes.io/blog/" rel="nofollow noreferrer noopener"&gt;https://kubernetes.io/blog/&lt;/a&gt;），虽然中文官网也有博客，但翻译的不全，比较少，而英文博客从2015年开始，每个重要特性的变更几乎都有文章来介绍。而且博客和文档不同，它更注重面对普通用户，阐述的是技术决策的思考过程，也就更容易理解一些。&lt;/p&gt;
&lt;p&gt;如果条件允许的话，我建议你从2015年的第一篇博客开始看起，最好每篇都简略地过一遍。把这些博客全看完，“以史为鉴”，你就能够理解Kubernetes的演变过程了，也会对Kubernetes的现状有更深刻的认识。&lt;/p&gt;
&lt;p&gt;第三个是上CNCF网站（&lt;a href="https://www.cncf.io/" rel="nofollow noreferrer noopener"&gt;https://www.cncf.io/&lt;/a&gt;），看它的全景图，在里面找自己感兴趣的项目，然后在Kubernetes环境里部署应用起来，在实践中学习Kubernetes。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CNCF全景图里的项目&lt;/strong&gt;非常多，其中由它托管的项目又分成毕业（Graduated）项目、孵化（Incubating）项目和沙盒（Sandbox）项目。&lt;/p&gt;
&lt;p&gt;其实这些项目只要进入了CNCF，质量都是比较高的，区别只在于成熟度的不同而已。毕业项目是最成熟的，已经被业界广泛承认和采用，可用于生产环境；孵化项目应用程度还不太广，贡献者也不是太多，只有少数生产实践；而沙盒项目则属于实验性质，还没有经过充分的测试验证。&lt;/p&gt;
&lt;p&gt;这里我们也可以来简单了解下毕业项目和孵化项目，课后你可以挑自己感兴趣的深入研究。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.cncf.io/projects/" rel="nofollow noreferrer noopener"&gt;这张图&lt;/a&gt;是目前CNCF里全部的16个毕业项目：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/a844a6892244b94c0f35.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;这里面我们已经全面学习了Kubernetes，简单介绍过containerd和Prometheus，其他我个人比较感兴趣的还有Harbor、Helm、Vitess。&lt;/p&gt;
&lt;p&gt;CNCF的孵化项目目前有39个，比起毕业项目它们的知名度要略差一些，这个截图列出了一部分，其中我比较感兴趣的有gRPC、SPIRE、NATS、OpenTelemetry：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://go.botsarena.tech/pictures/study-disign/2f53d9de733e4f96b5a5.webp" alt="图片"&gt;&lt;/p&gt;
&lt;p&gt;第四个学习方向要量力而行，是参加Kubernetes的培训并且通过认证（&lt;a href="https://kubernetes.io/zh-cn/training/" rel="nofollow noreferrer noopener"&gt;https://kubernetes.io/zh-cn/training/&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;和很多其他的计算机技术一样，Kubernetes也设立了官方的培训课程和资质认证，在国内大家都比较了解的应该就是&lt;strong&gt;CKA&lt;/strong&gt;（Certified Kubernetes Administrator）了，另外还有一个更高级的是&lt;strong&gt;CKS&lt;/strong&gt;（Certified Kubernetes Security Specialist）。&lt;/p&gt;
&lt;p&gt;CKA主要考查的是对Kubernetes的概念理解和集群管理维护能力，重点是动手操作，使用kubectl来解决各种实际环境里可能遇到的问题。它的难度并不太高，但考点覆盖面广，而且考试时间长达2个小时（以前是3个小时），对脑力和体力都有不小的挑战。&lt;/p&gt;
&lt;p&gt;由于Kubernetes在云原生领域“一统天下”，CKA认证近几年也就“火”了起来，相关的考试资料有很多，你可以轻易地在各大网站上找到，学完了我们的这个专栏课程，再适当地强化训练一下，拿到CKA证书应该不是什么太难的事情。&lt;/p&gt;
&lt;p&gt;不过要注意的是，因为Kubernetes版本更新很频繁，所以CKA是有时效期的，三年（以前是两年）过后失效就得重考，你需要评估一下考试对自己收益再慎重做决定。&lt;/p&gt;</description></item></channel></rss>