kubernetes用于大型集群管理,而k3s属于kubernetes的一个轻量级版本,常用于嵌入式设备使用。现把它安装到树莓派上使用。
1 系统准备
这里用到树莓派的系统是:CentOS-Userland-7-armv7hl-RaspberryPI-Minimal-4-2009-sda.raw,型号是4B+,8g内存。
树莓派初次启动需要扩容,并且做一些基本调整:
扩容:
fdisk /dev/mmcblk0 //使用fdisk工具对磁盘进行分区
执行命令:p(查看分区)
执行命令:d(删除分区3)
执行命令:p(查看分区)
执行命令:n(添加分区)
输入:P(主分区)
分区号:默认3
起始扇区:填第2个分区的最后一个扇区+1
结束扇区:默认就可以了,默认为整个磁盘的最后一个扇区。
执行命令:p
执行命令:w
reboot重启设备
执行命令:resize2fs /dev/mmcblk0p3
使用df -lh查看磁盘已经扩容完成了
配置cgroup
cgroup是linux用来对进程分配cpu、内存资源的工具,需要在启动系统时开启他,k3s会用到。
在/boot/cmdline.txt后加入这个,然后reboot
cgroup_memory=1 cgroup_enable=memory
按个人习惯装几个常用的
yum install vim screen ntpdate wget
yum install docker git
关闭swap
swapoff -a # 临时
sed -ri 's/.*swap.*/#&/' /etc/fstab # 永久
将桥接的IPv4流量传递到iptables的链
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system # 生效
# 使用过程中经常需要清理一下
iptables --flush
iptables -tnat --flush
时间同步
ntpdate -u cn.ntp.org.cn
2 部署k3s
2.1 安装 部署Server(Master)结点
k3s是一个轻量级的k8s,适用于树莓派这种嵌入式设备。
# 方法1:官方做法
curl -sfL https://get.k3s.io | sh -
# 方法2:下载后执行
curl -o k3s_installer.sh https://get.k3s.io
bash k3s_installer.sh
这个脚本跑完的时候,会把k3s添加到systemd里面,可以通过systemctl status k3s来查看运作状态。启动成功就可以使用啦
image.png
另外,也可以通过手动启动server的方式
官方参考:https://rancher.com/docs/k3s/latest/en/installation/ha-embedded/
- 先启动一个主结点A,关掉systemd方式启动的k3s。然后手动开启server服务。
systemctl stop k3s
reboot # 重启为了关闭一些关不了的service
# 方法1:第一个结点启动,使用默认命名
K3S_TOKEN=自己写一个密钥S k3s server --cluster-init
# 方法2:自定义结点唯一名称
K3S_TOKEN=自己写一个密钥S K3S_NODE_NAME=自定义唯一节点名称 k3s server --cluster-init
等它重启个好几次之后,基本就成功了。
ku-system的pod都正常启动了
如果一直失败,可以输入命令刷一下iptable缓冲
iptables --flush
iptables -tnat --flush
- 接着到第二 第三个结点启动Server(加上--with-node-id,集群会给新结点一个随机结点名称。如果没修改hostname,也不带这个参数,新结点会用默认hostname,非常容易和其他结点发生ID冲突;或者也可以通过K3S_NODE_NAME环境变量来命名)
# 方法1:随机结点名称启动
K3S_TOKEN=自己写一个密钥S k3s server --server https://第一个结点IP:6443 --with-node-id
# 方法2:自定义结点名称的方式启动
K3S_TOKEN=自己写一个密钥S K3S_NODE_NAME=自定义唯一节点名称 k3s server --server https://第一个结点IP:6443
等第二个结点加入后,在任意结点执行命令,都能查看到已有的2个Server(Master)结点了
二人世界
当Server结点数大于等于3个且为奇数时,集群才可以实现高可用。
大于等于3是因为k3s使用了Raft算法来实现一致性,而Raft算法的容崩率为1/3,也就是只要集群中有2/3台机器正常运作,集群就能正常运作,所以3台机器是最低要求;要奇数个结点是因为Raft算法过程中有一个很重要的随机投票选Leader的流程,结点们通过定期投票选举出一个Leader角色,然后其他结点在它的任期内就向他同步数据,这个时候如果结点数是偶数,那么容易出现平票问题,选不出leader,并且,崩溃后集群进行数据恢复过程中,实现一致的方法是多数服从少数,如果是偶数Master结点,且刚好被分割成2个结点规模一样的集团,就没办法恢复数据了[裂开],所以需要奇数个结点以避免权力平分问题。
以上为个人理解。
一家三口
有兴趣的同学可以一起探讨这类共识算法,与此类似的还有联盟链的PBFT类算法,比特币PoW算法等等。
2.2 Worker结点加入网络
从Server(Master)结点上获取token
因为集群并非开放式集群,加入集群需要获取一个token作为校验。这个token可以从Master服务器上获取。(手动加入的话,仅需要使用相同的K3S_TOKEN参数启动即可。)
cat /var/lib/rancher/k3s/server/node-token
方案1:Worker结点利用token加入集群(官方):
# IP需要修改为自己Master服务器的IP
curl -sfL http://get.k3s.io | K3S_URL=https://任意Server结点IP:6443 K3S_TOKEN=刚才保存下来的连接令牌 sh -
方案2:利用K3S_TOKEN参数手动加入(推荐)
# 方法一:像上面Master一样 通过with-node-id来随机生成结点ID
K3S_TOKEN=自己写一个密钥S k3s agent --server https://任意Server结点IP:6443 --with-node-id
# 方法二:或者给自己一个node name
K3S_TOKEN=自己写一个密钥S K3S_NODE_NAME=结点唯一命名 k3s agent --server https://任意Server结点IP:6443
这样,结点就正常连接上啦:
还来了个佣人
2.3 关闭k3s服务
关闭k3s进程后,后台还留存一些服务占用着端口,需要用官方脚本关闭他们
/usr/local/bin/k3s-killall.sh
2.4故障排除:
server中,一些kube-system pod启动notReady,甚至Error
可以flush一下iptables,等他自己重启就行了。
iptables --flush
iptables -tnat --flush
worker加入密码不正确
有可能发生了一些冲突,可以试下重装k3s-agent
/usr/local/bin/k3s-agent-uninstall.sh
rm -f /etc/rancher/node/password
实在不行,可以把配置数据全部删除。所有结点删除这个文件之后,再重新 --cluster-init 即可
rm -rf /var/lib/rancher/k3s
3 部署一个普通的Nodejs应用
目前系统已经伴随k3s安装的一些软件:
crictl:类似与docker的命令行工具,比如:
crictl ps
crictl images
k3s:封装了kubeneters基本工具在里面的集成,如使用kubectl:
k3s kubectl get nodes
k3s kubectl get pods
3.1 编写node程序和制作一个docker镜像
这里示范部署一个最简单的web应用
找一个空地方,先写一个简单的Node web程序,server.js
const http = require("http")
var server = http.createServer(function(req, res) {
console.log("request url: " + req.url);
res.writeHead(200);
res.end("hello ");
})
server.listen(8080);
然后写个DockerFile:
FROM node:10
EXPOST 8080
COPY server.js .
CMD node server.js
接下来build一个docker镜像,记得加上最后的路径
docker build -t 镜像名称 .
构建完成后跑起来:
--net host 代表与本机享受同一个网络命名空间
docker run -it -p 8080:8080 --net host samply-node
# 换个screen检查情况:
curl http://localhost:8080 # 输出hello代表一切正常
这里可以在docker容器内开启ssh服务:https://blog.csdn.net/Leo_csdn_/article/details/96150534
然后提交镜像到自己的远程仓库
# 获取正在跑的容器ID
docker container ls
# 然后提交到远程容器
docker login
docker commit -m "COMMIT_MESSAGE" -a "AUTHER_NAME" CONTAINER_ID 远程仓库/容器名称:版本号
docker push 远程仓库/容器名称:版本号
3.2 使用kubectl 部署镜像
做好docker镜像后,就可以部署到集群上了。
首先使用kubectl run命令直接创建一个带镜像信息的POD,POD是节点上容器集合的概念。
k3s kubectl run hello-node --image=远程仓库/容器名称:版本号 --port=8080
等一会儿就能在pods列表里面看到了:
image.png
但这时候,这个pod并没有对外开放,只能在集群内部相互访问,通过get services命令查看集群的服务,发现并没有我们的hello-node服务。
image.png
所以你需要通过expose命令,让他开放出来。
k3s kubectl expose pod hello-node --type="NodePort"
expose命令其实是创建了一个service,用于给这个pod提供访问入口。
(如果使用--type=LoadBalancer,则代表一个deployment上管理的所有POD进行均衡负载,但这里还没用上deployment,第四章节会使用到)
等一会儿,pod上就有一个结点IP的对外端口,供外部访问了。
本地访问 局域网内其他设备访问
最后是关闭服务的操作:
k3s kubectl delete pods,services hello-node
输入删除指令
另一个screen查看状态,发现POD正在被关闭中
运行结束后,刚启动过的pod和service就不见了,服务也停止了。
4 使用Deployment部署一个均衡负载的简单node应用
docker容器,其实就是一个运行的轻量级系统,里面可以跑我们的业务应用。
而POD则是代表容器的集合,一个POD可以运行多个容器,一台机器上可以运行多个POD。
POD未必是一个对外开放的服务,他可能只是内部计算的程序,默认只能集群内部通信,所以还有Service的概念,用于让POD对外开放端口,供外部访问。这里的service本质上是个集群内部的负载均衡器,用来给同一个Deployment分流;对应的还有Ingress,外部负载均衡器,用于给多个Deployment分流。
而Deployment顾名思义,就是一次部署的抽象实例,比如说,现在需要部署一个3台机器均衡负载的nodejs业务应用,那么这个部署任务则代表一个deployment实例。
这个实例通过一个 hello-node-deployment.yaml 文件表示出来:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-node-deployment # 部署实例的名字
spec:
selector:
matchLabels:
app: hello-node-app
replicas: 3
template:
metadata:
# 实例的查询标签
# 因为一个实例部署后可能会有多个POD,我们可能要批量获取状态、日志或者删除他们,所以有一个标签字段给我们设置。
# 部署后查询实例内 pod和service时,可以通过添加参数 -l app=hello-node-app 来查询
labels:
app: hello-node-app
spec:
containers:
- name: hello-node-container
image: deadfish5/sample-node:1.0 # 业务镜像,这是我的一个公开镜像,建议换成自己的
ports:
- containerPort: 8080 # 业务开放的端口,仅容器内部防火墙设置。
部署之前必须启动docker
systemctl start docker
systemctl enable docker
然后执行部署:
k3s kubectl apply -f ./hello-node-deployment.yaml
很快,我们可以看到POD和deployment的部署情况,都已经正常运作。
部署状态查询
我们也可以像docker一样进入容器操作
k3s kubectl exec -it POD_NAME -- sh # --后面跟的是进入容器后执行的命令
进入容器后可以使用基本linux命令,也可见8080端口已经被我们的node应用占用了。
进入容器并使用sh
但是此时service还没有他们,也就是正处于无法提供外部服务的状态。
所以我们还是要通过expose命令来起一下service:(第三节部署是针对单个POD来expose,这里是直接针对deployment来expose了,相当于批量操作了3个POD,并让他们均衡负载)
k3s kubectl expose deployment hello-node-deployment --type="LoadBalancer"
这里对一个deployment里面的3个pod启动了个默认均衡负载服务,暴露出来的一个端口是30057,访问可通。
也能够通过logs命令查看控制台输出的日志。
服务启动后的状态
完事就关了它:
k3s kubectl delete deployment,service hello-node-deployment
正在删除
因为deployment实例中包含了pod的部署配置,所以删除deployment时,k3s就会直接把pod也删除掉。
但service并不在deployment部署的范围内,所以需要同步删除它,在删除命令中通过","与deployment分割开来即可。
至此已经把刚起来的服务全部关闭掉了。
关闭后状态
综合部署
这里是给3台Master+1台Agent,使用deployment启动了1个pod的9个副本:
部署情况然后给他们上均衡负载服务:
service情况看看请求效果
请求测试查询状态
集群状态这里我们看到3个Server(Master)结点由于需要维护集群高可用,对CPU持续20%左右的消耗,内存也需要一个G左右。而Agent(Wroker)结点只需要执行部署任务,所以对内存与CPU的需求都相对低一些,仅维持在10%左右的CPU和半个G左右的内存消耗。
参考:https://zhuanlan.zhihu.com/p/120171512
参考:http://kubernetes.kansea.com/docs/hellonode/
网友评论