[编者的话] 这篇博客的本意是带大家从零开始搭建 Kubernetes 集群的. 但是我后面一想, 如果是我看了这篇文章, 会收获什么? 就是跟着步骤一步一走吗? 是我的话我会选择拒绝, 所以我加了关于 Kubernetes 的简单介绍, 每一步的步骤都添加了解释. 由于篇幅和时间原因, 我只介绍了 Kubernetes 中较为核心的 Pod 和 Service.
文章前半段会简单的介绍一下 Kubernetes, 后半段会介绍如何从零开始慢慢的搭建集群. 如果想直接开始着手搭建集群, 则可以直接从第三章开始看.
Kubernetes 是什么
Kubernetes 简称 K8S, 是由 Google 在 2014 年开源的生产级别的容器编排系统, 或者说是微服务和云原生平台. 虽说 14 年才开源, 但实际上 Kubernetes 是 Google 内部的容器编排系统 Borg 的开源版本, 在 Google 内部已经用了十多年了. 下面是一个关于 Kubernetes 的 Logo 来源的小插曲.
Kubernetes 由谷歌在 2014 年首次对外宣布 . 它的开发和设计都深受谷歌的 Borg 系统的影响, 它的许多顶级贡献者之前也是 Borg 系统的开发者. 在谷歌内部, Kubernetes 的原始代号曾经是 Seven, 即星际迷航中友好的 Borg(博格人)角色. Kubernetes 标识中舵轮有七个轮辐就是对该项目代号的致意.
不过也有一个说法是, Docker 的 Logo 是一个驮着集装箱的鲸鱼, 也就是运输船, Kubernetes 的 Logo 是一个船舵, 旨在引领着 Docker(或者说容器技术)走向远方.
简单了解 Kubernetes
看了很多官方文章, 是真官方. 官方什么意思呢, 就是有可能看完了约等于没有看, 一样的啥都不知道.
所以我想写这样一篇文章, 给那些看完文档仍然不太理解或者说完全没了解过 Kubernetes 的老铁一点小帮助. 那么让我们回到最初对 Kubernetes 的定义, 它是一个微服务框架.
说到微服务框架, 我们就不得不提一下目前业界十分主流的微服务框架, 与这些你十分熟悉的框架进行对比, 你就会很清晰的知道 Kubernetes 能做什么了. 目前很主流的微服务框架和平台有 Spring Cloud,Dubbo 和 Kubernetes.
Spring Cloud 来自 Netflix,Dubbo 来自阿里, 而 Kubernetes 则来自 Google. 说的直观一点, 这三个框架都是针对微服务的解决方案. 可能有人会说, Kubernetes 不是一个容器编排系统吗? 怎么跟 Spring Cloud 这种软件层面上的微服务框架做起了对比呢?
老铁别慌, 等我们慢慢深入这个概念.
我们都知道, 如果我们需要使用微服务, 那么肯定少不了一些底层的基础设施的支撑, 例如服务注册与发现, 负载均衡, 日志监控, 配置管理, 集群自愈和容错, 弹性伸缩...... 等等. 我没有列举完, 如其实这些组件都可以统称为微服务的公共关注点. 那我们是不是可以说, 只要能够提供的这些功能, 它就算一个微服务框架呢?
以上的大多数功能, Kubernetes 都是内置的. 故我们可以说 Kubernetes 是一个与 Docker Swarm 相类似的容器编排系统, 但是由于 Kubernetes 内置了微服务的解决方案, 它同时也是一个功能完备的微服务框架.
Pod 的概念
在 Docker Swarm 中, 调度的最小单位是容器, 而在 Kubernetes 中, 调度的最小是 Pod, 那啥是 Pod 呢?
Pod 是 Kubernetes 设计的一个全新的概念, 在英文中的原意是表达一群鲸鱼或者是一个豌豆荚的意思. 换句话说, 一个 Pod 中可以运行一个或者多个容器.
在一个集群中, Kubernetes 会为每个 Pod 都分配一个集群内唯一的 IP 地址. 因为 Kubernetes 要求底层网络支持集群内的任意节点之间的两个 Pod 能够直接通信. 这些容器共享当前 Pod 的文件系统和网络. 而这些容器之所以能够共享, 是因为 Pod 中有一个叫 Pause 的根容器, 其余的用户业务容器都是共享这个根容器的 IP 和 Volume. 所以这些容器之间都可以通过 localhost 进行通信.
有人可能会问, 为什么要引入根容器这个概念? 那是因为如果没有根容器的话, 当一个 Pod 中引入了多个容器的时候, 我们应该用哪一个容器的状态来判断 Pod 的状态呢? 所以才要引入与业务无关且不容易挂掉的 Pause 容器作为根容器, 用根容器的状态来代表整个容器的状态.
熟悉 Spring Cloud 或者微服务的都知道, 微服务中最忌讳的就是出现单点的情况.
所以针对同一个服务我们一般会部署 2 个或者更多个实例. 在 Kubernetes 中, 则是会部署多个 Pod 副本, 组成一个 Pod 集群来对外提供服务.
而我们前面提过, Kubernetes 会为每一个 Pod 提供一个唯一的 IP 地址, 客户端就需要通过每个 Pod 的唯一 IP + 容器端口来访问到具体的 Pod, 这样一来, 如果客户端把调用地址写死, 服务器就没有办法做负载均衡, 而且, Pod 重启之后 IP 地址是会变的, 难道每次重启都要通知客户端 IP 变更吗?
为了解决这个问题, 就要引出 Service 的概念了.
Service
Service 是 Kubernetes 中最核心的资源对象之一, 就是用于解决上面提到的问题. 我个人认为与 Swarm 中的 Service 概念没有太大的区别.
一旦 Service 被创建, Kubernetes 会为其分配一个集群内唯一的 IP, 叫做 ClusterIP, 而且在 Service 的整个生命周期中, ClusterIP 不会发生变更, 这样一来, 就可以用与 Docker Swarm 类似的操作, 建立一个 ClusterIP 到服务名的 DNS 域名映射即可.
值得注意的是, ClusterIP 是一个虚拟的 IP 地址, 无法被 Ping, 仅仅只限于在 Kubernetes 的集群内使用.
而 Service 对客户端, 屏蔽了底层 Pod 的寻址的过程. 并且由 kube-proxy 进程将对 Service 的请求转发到具体的 Pod 上, 具体到哪一个, 由具体的调度算法决定. 这样以来, 就实现了负载均衡.
而 Service 是怎么找到 Pod 的呢? 这就需要继续引入另外一个核心概念 Label 了.
Label
Lable 本质上是一个键值对, 具体的值由用户决定. Lable 就是标签, 可以打在 Pod 上, 也可以打到 Service 上. 总结来说, Label 与被标记的资源是一个一对多的关系.
例如, 我们给上面所描述的 Pod 打上了 role=serviceA 的标签, 那么只需要在 Service 中的 Label Selector 中加入刚刚那个标签, 这样一来, Service 就可以通过 Label Selector 找到打了同一 Label 的 Pod 副本集了.
接下来, 再简单的介绍一下其他的 Kubernetes 核心概念.
Replica Set
上面提到过部署多个 Pod, 是怎么一回事呢? Kubernetes 最开始有一个概念叫 Replication Controller, 不过现在已经慢慢的被 Replica Set 所替代, RS 也叫下一代的 RC. 简单来说 Replica Set 定义了一种期望的场景, 即让任何时候集群内的 Pod 副本数量都符合预期的值.
一旦被创建, 集群就会定期的检测当前存活的 Pod 数量, 如果多了, 集群就会停掉一些 Pod. 相反, 如果少了就会创建一些 Pod. 这样一来可以避免什么问题呢? 假设某个服务有两个实例在运行, 其中一个意外挂掉了, 如果我们设置了副本数量是 2, 那么集群就会自动创建一个 Pod, 以保证集群内始终有两个 Pod 在运行.
Kubernetes 的东西就简单的介绍这么多, 接下来让我们进入集群的搭建环节.
搭建 Kubernetes 的准备工作
不知道从哪篇博客开始, 不是很愿意写这种纯 TODO 类的博文, 但是我自己躺坑之后发现, 我自己这个还真是我目前见过最简单的.
我看到的有些安装分了很多种情况, 但是当一个初学者来看的时候, 可能反而会让他看懵逼. 所以接下来的安装会有些硬核. 不分情况, 就只有一种情况, 一把梭安装就完事.
系统版本: Ubuntu 18.04
Kubernetes 版本: v1.16.3
Docker 版本: v19.03.5
Flannel 版本: v0.11.0
准备工作
我们先假设以下的情况成立.
机器: 有 2-3 台物理机或虚拟机
系统: Ubuntu 18.04 且已换好国内的源
如果以上基本不成立, 本篇文章到此结束, 谢谢观看......
安装 Docker
我也不需要介绍各种情况了, 直接登上机器, 创建一个 shell 脚本, 例如叫 install_docker.sh, 一把梭代码如下:
- sudo apt-get update
- sudo apt-get install -y apt-transport-https ca-certificates
- curl gnupg-agent software-properties-common
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
- sudo apt-key fingerprint 0EBFCD88
- sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
- sudo apt-get update
- sudo apt-get -y install docker-ce docker-ce-cli containerd.io
然后执行 sh install_docker.sh, 等待命令跑完, 验证 Docker 是否安装好即可. 直接敲 docker + 回车.
安装 Kubernetes
同理, 新建一个 shell 脚本, 例如 install_k8s.sh. 一把梭代码如下:
- sudo curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
- sudo apt-get update
- cat <<EOF>/etc/apt/sources.list.d/kubernetes.list
- deb https://apt.kubernetes.io/ kubernetes-xenial main
- EOF
- sudo apt-get install -y kubelet kubeadm kubectl -allow-unauthenticated
然后执行 sh install_k8s.sh, 等待命令跑完, 验证 Kubernetes 是否安装好即可. 直接敲 kubectl + 回车.
关闭 Swap
先给出一把梭, 不要耽误了正在安装的老铁. 为什么要关闭后面再说.
暂时关闭, 直接使用命令 sudo swapoff -a, 但是重启之后会生效. 会导致 Kubernetes 无法正常运行.
永久关闭, 建议一劳永逸, sudo VIM /etc/fstab 将有 swap.img 那行注释掉, 保存即可.
那么, swap 是啥呢? 它是系统的交换分区, 你可以理解为虚拟内存. 当系统内存不足的时候, 会将一部分硬盘空间虚拟成内存使用. 那为什么 Kubernetes 需要将其关掉呢? 可以从下图看看访问内存和访问硬盘速度上的差异就知道了.
总的来说是为了性能考虑, 所以就需要避免开启 swap 交换, Kubernetes 希望所有的服务都不应该超过集群或节点 CPU 和内存的限制.
初始化 Master 节点
到这, 准备工作就完成了, 可以开始安装 Kubernetes 的 Master 节点了, 登上要作为 Master 节点的机器.
设置 HostName
老规矩, 先上命令, 再说为什么要设置.
sudo hostnamectl set-hostname master-node
自定义修改了主机名, 在之后查看集群内节点时, 每个节点的名字就不会显示 Kubernetes 自动生成的名字, 便于查看和记忆. 例如, 在其他的 Node 节点你可以将 master-node 改为 slave-node-1 或 worker-node-2, 效果如下.
初始化集群
在机器上执行如下命令.
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
然后, 等待命令执行完.
这里需要特别注意一下. 这个命令执行完成之后, 会打印一个有 kubeadm join 的命令, 需要保存下来.
大概长这样:
kubeadm join 你的 IP 地址: 6443 --token 你的 TOKEN --discovery-token-ca-cert-hash sha256: 你的 CA 证书哈希
顾名思义, 这个命令用于其他节点加入到集群中, 而且 Token 是有时效性的, 过期时间一般是 86400000 毫秒.
如果失效, 就需要重新生成. 如果你真的又没有保存, 又失效了... 我还是给你准备了两个补救措施. 如果命令保存下来了, 那么请直接跳过这两个补救措施.
token. 通过命令 Kubeadm token list 找回
ca-cert. 执行命令 openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.*//'找回
普通用户可执行
把下面的指令一把梭即可.
- mkdir -p $HOME/.kube
- sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
- sudo chown $(id -u):$(id -g) $HOME/.kube/config
主要是, 为了不那么麻烦, 在控制节点上执行 kubectl 这类的命令时, 不用每次都 sudo.
安装网络通信插件
执行如下命令, 安装网络插件 Flannel.
sudo kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
可以看到, 如果不安装 Flannel, 我们刚刚 Init 好的 Master 节点会处于 NOT_READY 的状态. 安装好之后, 可以通过命令 kubectl get nodes 来查看所有的节点的状态. 也可以通过 kubectl get pods --all-namespaces 来查看当前集群中所有 Pod 的状态. 这里需要注意的是, 只有在 master 节点是 READY, 所有 Pod 的状态是 RUNNING 之后, 才可以进行下一步.
为什么要装网络插件呢?
那是因为 Kubernetes 要求集群内的所有节点之间的 Pod 网络是互通的. 换句话说, Flannel 可以让集群内不同节点上的容器都有一个在当前集群内唯一的虚拟 IP 地址. 这样以来, 就可以实现, 跨节点的 Pod 与 Pod 直接通信.
这样一来, 将复杂的网络通信, 简单的变成了两个 IP 地址之间的通信. 这主要是通过虚拟二层网络实现的. 看似是这个节点的 Pod 直接和另一个节点上的 Pod 进行了通信, 最终还是通过节点的物理网卡流出的.
Slave 节点加入集群
到此, 一个单点的集群就已经搭建好了. 现在我们要做的是, 登录准备好的另一台 (我只有两台, 如果你有 3 台或者 4 天, 把这个章节反复走几次就好了) 服务器.
设置 HostName
执行如下命令.
sudo hostnamectl set-hostname slave-node
因为当前节点不是 master 了, 所以主机名设置成了 slave-node.
加入集群
重点来了, 执行上一章节生成的 kubeadm join 命令即可. 等待执行完毕之后, 就可以在 master 节点上通过命令 kubectl get nodes 看到 slave-node 已经加入了集群.
对于 Slave 节点的操作就没了.
来源: http://www.tuicool.com/articles/a6FJ7ji