摩尔投票
时间复杂度$O(n)$
空间复杂度$O(1)$
1 | class Solution { |
时间复杂度 $O(m+n)$
空间复杂度 $O(1)$
1 | class Solution { |
把新数组想象成一个栈,用一个指针维护栈顶位置
时间复杂度$O(n)$
空间复杂度$O(1)$
1 | class Solution { |
在k8s集群上利用nfs实现跨节点目录共享,部署ollama和nginx作为练习。
环境为openeuler22.03 sp3,k8s1.23.6,docker为容器引擎,flannel提供通信支持
一主(node0-192.168.195.40)二从(node1-192.168.195.41、node2-192.168.195.42)
所有数据利用nfs实现共享(所有节点均需安装nfs服务),数据存储在主节点/opt/k8s_store/目录。分别为/opt/k8s_store/ollama和/opt/k8s_store/nginx
安装NFS服务(所有节点):
1 | sudo dnf install nfs-utils |
编辑/etc/exports文件,添加共享目录配置(存储节点):
1 | /opt/k8s_store *(rw,sync,no_root_squash,no_subtree_check) |
这会使/opt/k8s_store目录对所有节点(*表示所有)可读写。
重启NFS服务:
1 | sudo systemctl restart nfs-server |
1 | [root@node0 ollama]# ls |
配置文件pv.yaml
1 | [root@node0 ollama]# cat pv.yaml |
配置文件pvc.yaml
1 | [root@node0 ollama]# cat pvc.yaml |
配置文件deployment.yaml
1 | [root@node0 ollama]# cat deployment.yaml |
配置文件service.yaml
1 | [root@node0 ollama]# cat service.yaml |
(实际上在本示例中并未进一步使用此处开放的端口),这里可以进一步与dify等工具联动,实现外部api访问服务。可以查阅
Private Deployment of Ollama + DeepSeek + Dify: Build Your Own AI Assistant | Dify
利如下命令进行部署
1 | kubectl apply -f pv.yaml |
最终结果为
1 | [root@node0 ollama]# kubectl get pods |
此处进入pod内部,下载deepseekr1:1.5b完整部署结果
1 | [root@node0 ollama]# kubectl exec -it ollama-deployment-777b9bccb7-sj7wd -- /bin/bash |
可以顺利使用,部署成功。
此处准备了wireshark的本地用户手册作为服务内容。
1 | WiresharkUser'sGuide.zip # 内含n多html文档 |
解压到/opt/k8s_store/nginx/index里面。
1 | unzip "WiresharkUser'sGuide.zip" -d /opt/k8s_store/nginx/index |
在共享目录写一个index.html放在/opt/k8s_store/nginx
1 | [root@node0 nginx]# nano index.html |
用到的配置文件
1 | [root@node0 nginx]# ls |
pv.yaml
1 | [root@node0 nginx]# cat pv.yaml |
pvc.yaml
1 | [root@node0 nginx]# cat pvc.yaml |
nginx-deployment.yaml
1 | [root@node0 nginx]# cat nginx-deployment.yaml |
nginx-service.yaml
1 | [root@node0 nginx]# cat nginx-service.yaml |
应用即可
所有人可以访问
1 | [root@node0 nginx]# sudo chmod -R 777 /opt/k8s_store |
如果之前部署文件有误要重新部署,可以使用删除原配置重新部署
1 | # 删除旧的pvc和pv创建新的 |
显示
1 | [root@node0 ~]# kubectl get pvc ollama-pvc |
当PVC显示为Terminating时,通常意味着Kubernetes正在执行删除操作,但未能完全完成。这通常是由于某些资源清理不完整或在删除过程中发生阻塞。
查看该PVC详细信息
1 | kubectl describe pvc ollama-pvc |
此处显示
1 | [root@node0 ~]# kubectl describe pvc ollama-pvc |
可以看到,它正处于Finalizers保护机制下。Finalizers: [kubernetes.io/pvc-protection]表示Kubernetes正在确保该PVC不会在被使用时被删除。
由于ollama-deployment-777b9bccb7-wq6lh这个Pod仍在使用该PVC,删除操作没有完成。
解决方案为删除Pod
1 | kubectl delete pod ollama-deployment-777b9bccb7-wq6lh |
这时候pvc即可删除
除此之外,可以选择强制删除pvc,不过不做推荐(此处未使用)
1 | kubectl delete pvc ollama-pvc --force --grace-period=0 |
刚才执行了
1 | kubectl delete pod ollama-deployment-777b9bccb7-wq6lh |
但由于之前部署由Deployment管理,Deployment会自动创建一个新的Pod来替代被删除的Pod。
如下所示
1 | [root@node0 ~]# kubectl get po |
阻止pod自动重启可以删除deployment
1 | kubectl delete deployment ollama-deployment |
重新查看,发现删除成功
1 | [root@node0 ollama]# kubectl get pods |
1 | kubectl scale deployment k8s-nginx --replicas=1 |
1 | PS C:\Users\dd\Desktop> scp '.\WiresharkUser''sGuide.zip' root@192.168.195.40:/opt/k8s_store/nginx |
1 | nano /opt/prometheus/config/prometheus.yml |
填入如下内容作为配置(监控自己)
1 | scrape_configs: |
运行
1 | docker run -d --name=prometheus -p 9090:9090 -v /opt/prometheus/config/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus |
运行成功后可以浏览器访问

1 | docker run -d --name=mynginx -p 10080:80 nginx |
此时可以在浏览器访问

使用stats查看某容器状态
1 | [root@node0 docker.service.d]# docker stats mynginx |
说明 –no-stream Disable streaming stats and only pull the first result\
1 | docker stats --no-stream mynginx | awk 'NR!=1{print $3}' |
解释
NR:表示当前处理的行号。NR != 1 代表跳过第一行(标题行)
print $3:表示输出每行的第三列(即 CPU 使用百分比)。
awk 是一种强大的文本处理工具,通常用于从文本中提取、处理和生成报告。它特别适用于处理结构化的文本数据,比如日志文件、CSV 文件、配置文件、表格数据等。它可以按行处理文本,并且可以通过分隔符(例如空格、逗号等)来处理数据。
awk 基本用途和功能
awk 可以轻松处理按空格或其他分隔符(例如逗号)分隔的文本。默认情况下,awk 使用空格或制表符(Tab)作为分隔符来划分每行数据,并通过 $1, $2, $3 等来访问每一列。
示例:从一个文件或命令输出中提取每行的第一列数据:
1 | echo -e "apple 10\nbanana 20" | awk '{print $1}' |
输出:
1 | apple |
在 awk 中使用条件语句、循环、数学运算等来处理文本中的数据。
示例:计算每行第一列和第二列的和:
1 | echo -e "10 20\n30 40" | awk '{print $1 + $2}' |
输出:
1 | 30 |
可以指定自定义的字段分隔符。例如,当数据是 CSV 格式时,分隔符是逗号。
示例:指定逗号作为分隔符来处理 CSV 数据:
1 | echo -e "apple,10\nbanana,20" | awk -F ',' '{print $1}' |
输出:
输出:
1 | apple |
awk 允许根据条件执行特定的操作,例如打印满足条件的行。
示例:打印包含“banana”这一单词的行:
1 | echo -e "apple 10\nbanana 20" | awk '$1 == "banana" {print $0}' |
输出:
1 | banana 20 |
awk 提供了一些内置变量,比如:
NR:当前处理的行号。
NF:当前行的字段数。
$0:整行文本。
$1, $2, $3:每行的第 1、2、3 列。
FS:字段分隔符(默认是空格或制表符)。
打印格式化输出
awk 可以自定义输出格式,比如指定列宽、对齐等。
示例:输出每个字段的固定宽度:
1 | echo -e "apple 10\nbanana 20" | awk '{printf "%-10s %10s\n", $1, $2}' |
输出:
1 | apple 10 |
安装
1 | docker run -d --volume=/:/rootfs:ro \ |
此时可以在8080访问

在prometheus绑定的配置文件添加监控源
1 | nano /opt/prometheus/config/prometheus.yml |

重启prometheus
1 | docker restart prometheus |
静待片刻即可收到返回的新数据

1 | docker run -d --name=grafana -p 3000:3000 grafana/grafana |
之后可以在端口3000访问
默认账号密码
1 | admin |
第一次会提示修改密码

先添加数据源



再导入监控模板



之后即可看到监控数据

先创建容器,使用 –net=”host”` 网络模式共享主机网络堆栈,不需要-p进行端口映射
1 | docker run -d --name=node-exporter --net="host" --pid="host" -v "/:/host:ro,rslave" quay.io/prometheus/node-exporter:latest --path.rootfs=/host |

1 | nano /opt/prometheus/config/prometheus.yml |

1 | docker restart prometheus |
静待一会即可

对于Grafana,依旧是带入库模板,编号9276


之后即可看到面板

有些显示没有图表,一般是三个原因

有结果的
1 | node_cpu_seconds_total{instance=~"$node",mode="idle"} |

加一个irate就没了
1 | irate(node_cpu_seconds_total{instance=~"$node",mode="idle"}[1m]) |

推测node_cpu_seconds_total 只被更新不频繁,增加时间范围,到2分钟,可以
1 | irate(node_cpu_seconds_total{instance=~"$node",mode="idle"}[2m]) |

于是问题解决
1 | 100 - (avg(irate(node_cpu_seconds_total{instance=~"$node",mode="idle"}[2m])) * 100) |

点进去发现具有同样的问题,时间片从1m改为2m解决,最后一个为实时io,无data,查询普罗米修斯,返回状态码0


与上面类似处理

类似处理,可以显示数据(实际上四条查询,前两个指标nodata,原因尚不清楚,后两个有)

类似处理,解决

比如下面这个是默认的网卡名称不对

1 | irate(node_network_receive_bytes_total{instance=~'$node',device=~'$nic'}[5m])*8 |
改为实际的网卡即可
1 | irate(node_network_receive_bytes_total{instance=~'$node',device=~'ens33'}[5m])*8 |

刷新解决

记录学习部署k8s过程中踩过的坑,下次部署有个参考
1 | k8s 1.20.2 + docker + flannel 0.20.2 |
修自定义主机名,即刻应用,以一个为例
1 | # hostnamectl set-hostname node1 |

整个顺手的文本编辑器,笔者这里选了nano
1 | # yum install nano |

配置主机名解析
/etc/hosts是一个本地的静态文件,用于将主机名映射到 IP 地址。当系统需要解析主机名时,首先查找该文件。如果找到匹配项,则使用该 IP 地址,而不通过 DNS 服务器
1 | # nano /etc/hosts |
添加如下内容

关闭swap,selinux,禁用防火墙
k8s运行需要禁用swap(交换空间)更好的进行容器内存管理。swapoff -a关闭swap。/etc/fstab 用于定义系统启动时自动挂载的文件系统、分区、交换空间(swap)等信息,sed -ri 's/.*swap.*/#&/' /etc/fstab注释掉其中关于swap的信息。setenforce 0禁用 SELinux 的强制安全策略避免k8s遇到权限问题。systemctl stop firewalld && systemctl disable firewalld关闭并禁用防火墙
1 | # swapoff -a |

启用内核模块
**modprobe overlay**:加载 Overlay 文件系统(用于容器运行时)内核模块,支持容器文件系统。
**modprobe br_netfilter**: 加载网络桥接过滤模块,用于容器和主机之间的流量控制。
1 | # modprobe overlay |
网络转发常用配置
net.bridge.bridge-nf-call-iptables = 1iptables 进行包过滤。避免容器间的流量被误拦截。net.ipv4.ip_forward = 1net.bridge.bridge-nf-call-ip6tables = 1vm.swappiness=0swappiness 控制内核决定什么时候将内存页面交换到硬盘上。swappiness=0 表示内核尽量避免交换,优先使用物理内存,避免频繁的内存交换。1 | cat > /etc/sysctl.d/kubernetes.conf <<EOF |
生效规则
1 | # sysctl -p /etc/sysctl.d/kubernetes.conf |
1 | # dnf install -y docker |

开机自启
1 | # systemctl enable docker && systemctl start docker |
查看版本,确定安装成功
1 | # docker --version |

Flannel 是 Kubernetes 的网络插件,用于为集群中的容器提供跨节点的网络通信,常用的还有calico,flannel简单易用,calico功能更强大
1 | # mkdir /opt/yaml/ |
内容如下,注意这里的镜像是国外的,可以换成国内的,也可以给docker配置代理
另外,如果使用其他版本的flannel,可以直接去其github仓库下载对应yml文件
1 | --- |
安装cni并配置到flannel的默认路径
CNI(Container Network Interface)是容器网络接口的缩写,为容器提供网络配置和管理功能,包括为容器分配 IP 地址、设置网络策略、路由流量等
1 | # dnf install -y containernetworking-plugins |

copy cni配置到flannel默认读取的目录
1 | # mkdir -p /opt/cni/bin |

依赖组件conntrack
conntrack是一种用户空间工具,用于操作和管理 netfilter跟踪连接的状态,此处用于处理容器间的网络连接
1 | # dnf install -y conntrack |

kubelet开启自启
1 | # systemctl enable kubelet.service && systemctl start kubelet.service |

1 | # dnf install -y kubernetes-kubeadm kubernetes-kubelet kubernetes-master |

1 | kubeadm init --apiserver-advertise-address=192.168.126.139 \ |
这里笔者配置了附录的docker代理,如果没有,可以考虑在这里直接指定国内源,比如下面这种方式
1 | kubeadm init --kubernetes-version=1.20.2 --apiserver-advertise-address=192.168.126.139 --cri-socket=/var/run/isulad.sock --image-repository=registry.aliyuncs.com/google_containers --service-cidr=10.96.0.0/12 --pod-network-cidr=10.244.0.0/16 --v=5 |
输出以下信息初始化成功

1 | kubeadm join 192.168.126.139:6443 --token dokaoz.sfrch50yf43iubsz \ |
/etc/profile 是一个系统级的 shell 配置文件,通常用于设置所有用户的环境变量和启动时执行的系统命令
1 | # nano /etc/profile |
最后面添加
1 | export KUBECONFIG=/etc/kubernetes/admin.conf |
应用改变
1 | # source /etc/profile |
1 | kubectl get nodes |
此时提示NotReady,是因为网络插件未配置(这里笔者已经在从节点执行了1. 2步骤加入了集群)

1 | kubectl apply -f /opt/yaml/kube-flannel.yaml |

1 | kubectl get nodes |
安装完成后状态如下所示

1 | [root@node0 ~]# dnf install -y kubernetes-kubeadm kubernetes-kubelet kubernetes-node |

1 | kubeadm join 192.168.126.139:6443 --token dokaoz.sfrch50yf43iubsz \ |
显示如下信息加入成功

此处选择nginx进行测试创建并暴露端口
【来自GPT】
kubectl create deployment nginx --image=nginx**kubectl create deployment**:创建一个新的部署(Deployment)。Deployment 是 Kubernetes 中管理应用生命周期的资源对象,它定义了应用的期望状态(例如 Pod 数量、副本、容器镜像等)。
**nginx**:指定部署的名称。这个名字会被用作创建的 Deployment 资源的标识。
**--image=nginx**:指定容器使用的镜像。这里指定的是 nginx 镜像,意味着创建的 Pod 中将运行 Nginx Web 服务器。这个镜像是从 Docker Hub 中拉取的官方 Nginx 镜像。
kubectl expose deployment nginx --port=80 --type=NodePort**kubectl expose**:该命令用于暴露一个服务,使其可以通过网络访问。通常,Kubernetes 使用 Service 资源来暴露应用,使其在集群内或集群外可访问。
**deployment nginx**:暴露的对象是之前创建的 nginx Deployment。意味着这个命令会创建一个 Service,将 nginx 部署暴露出去。
**--port=80**:指定暴露服务的端口号,80 是 Nginx 的默认 HTTP 服务端口。这个端口将用于访问 Nginx Web 服务器。
**--type=NodePort**:指定服务暴露的类型为 NodePort。NodePort 类型的服务会在每个节点上打开一个端口,使集群外部的用户可以通过节点的 IP 地址和指定的端口号来访问服务。Kubernetes 会自动为该服务分配一个端口,默认范围是 30000 到 32767。
1 | kubectl create deployment nginx --image=nginx |
查询当前集群中 Pod 和 Service 状态,查看是否运行成功
1 | kubectl get pod,svc |

查看pod运行的节点可以用
1 | kubectl get pods -o wide |
在从节点查看,可以发现这里自动部署在在192.168.126.141节点上

所有节点均可访问nginx服务器
1 | 192.168.126.140:32641 |
比如主节点

扩展副本
1 | kubectl scale deployment nginx --replicas=3 |
1 | # cd /etc/sysconfig/network-scripts |
笔者文件如下

对于要设置的静态ip所属子网,可以在宿主机使用ipconfig命令查询,笔者使用NAT链接虚拟机和宿主机,这里是VMnet 8网络,网关一般默认为xxx.xxx.xxx.2,DNS照搬即可。
更改如下选项即可,黄色由dhcp改为static,红色为新加的。

之后重启网卡即可生效设置
1 | # systemctl reload NetworkManager |
重启后已经可以访问网络

实际上有时候管用,有时候不管用,原因笔者还没弄明白,坑++,这里选择粗暴的重启节点
1 | # reboot |
1 | [root@node0 ~]# export http_proxy="http://192.168.1.104:7890" |
1 | [root@node0 ~]# sudo mkdir /etc/systemd/system/docker.service.d |
proxy.conf内容为
1 | [Service] |

初始化时警告,主节点socat缺失
1 | [WARNING FileExisting-socat]: socat not found in system path |
补一个即可
1 | yum install -y socat |
初始化时警告docker cgroups与推荐不一致,在
“/etc/docker/daemon.json”添加
1 | "exec-opts": ["native.cgroupdriver=systemd"] |
之后重启docker和守护进程即可,查看docker info可以看到当前的cgroups
1 | [root@lab2 docker]# nano /etc/docker/daemon.json |
完整文件示例
1 | “ |
如果想查看k8s的cgroups
1 | nano /var/lib/kubelet/config.yaml |
下面的命令可以在所有节点删除现有配置
1 | sudo kubeadm reset -f |
此命令不会删除docker,k8s组件等镜像,只会删除初始化的影响
主节点需要额外执行
1 | sudo rm -rf /etc/kubernetes |
问题:
1 | [preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml' |
方法(主节点)
1 | kubectl delete node lab2 |
然后从节点重新加入
1 | [root@lab2 ~]# kubectl delete -f /opt/yaml/kube-flannel.yaml |
删除 Deployment 和 Pod
1 | kubectl delete deployment nginx |
删除对应的 Service
1 | kubectl delete svc nginx |
确认资源已被删除
1 | kubectl get pod,svc |
kubelet:在 每个节点 上运行,负责管理节点上的容器和 Pod。
kubeadm:在 主节点 上用于初始化集群,在 从节点 上用于加入集群。
kubectl:在 任意节点 上运行,用于操作和管理集群。
上述过程中kubectl只部署在了主节点上,从节点调用相关指令有如下错误
1 | [root@lab2 ~]# kubectl apply -f /opt/yaml/kube-flannel.yaml |
如果想在从节点执行这些指令时,用不了,可以复制主节点的配置即可
1 | kubectl apply -f /opt/yaml/kube-flannel.yaml |
具体操作
查看从节点配置:
1 | [root@lab2 ~]# ls ~/.kube/config |
没有找到 ~/.kube/config 文件,说明 kubectl 配置文件还没有正确设置。
解决步骤:
在主节点上获取 kubeconfig 文件:
1 | sudo cat /etc/kubernetes/admin.conf |
复制配置文件到工作节点
将输出的配置文件内容复制到工作节点上的 ~/.kube/config 文件
没有则新建
1 | [root@lab2 ~]# mkdir -p ~/.kube |
然后就好了
1 | [root@lab2 ~]# kubectl get nodes |
K8s+iSulad环境部署 | openEuler文档 | openEuler社区 | v22.03_LTS_SP3
openEuler 部署Kubernetes(K8s)集群_《openeuler kubernetes 集群部署指南》-CSDN博客
K8s+iSulad环境部署 | openEuler文档 | openEuler社区 | v22.03_LTS_SP3
环境:Win11 VMware Workstation 16.02
选择自定义

保持不变

稍后安装操作系统

这里笔者选择的Linux Centos 7 64位

自定义名称和路径

自定义处理器信息。笔者指定了2个处理器,每个处理器2个内核

自定义内存,笔者指定4GB

自定义网络连接方式。笔者使用了NAT方式,可以理解为在宿主机新建一张虚拟网卡,然后把虚拟机分配到其子网中

保持不变

保持不变

保持不变

自定义磁盘大小,笔者分配了40GB,存储为单个文件

自定义虚拟磁盘路径

指定虚拟DVD



Enter/等待60s 开始安装,磁盘检查大概要几分钟,进入安装界面
安装过程使用中文

安装目的地选择,点进去退出来就可以

语言支持添加英语

网络链接,设置主机名

设置root用户

开始安装&重启系统,出现如下界面安装完成

在搭建k8s集群时遇到了需要给磁盘扩容的情况,写一篇文章记录自己的经历
背景知识:
LVM(Logical Volume Manager,逻辑卷管理器)是 Linux 操作系统中的一种灵活的磁盘管理工具,它通过将多个物理硬盘或分区组合成一个或多个逻辑卷,提供比传统分区管理更强大的存储管理功能。LVM 允许动态调整存储空间大小、跨多个物理磁盘创建逻辑卷,并支持快照、镜像等高级功能。
1 | sudo fdisk -l |
可以看到前面是若干回环设备(如loop0)的信息,这是linux一种虚拟设备,而/dev/sda是物理硬盘。/dev/sda1, /dev/sda2, /dev/sda3:表示该硬盘的不同分区。最后是由 LVM(逻辑卷管理器) 管理的分区/dev/mapper/ubuntu–vg-ubuntu–lv
1 | df -h |
运行 df -h 查看文件系统的当前使用情况,udev 是 Linux 内核提供的设备管理器,tmpfs 是基于内存的文件系统,通常用于存储临时文件。可以看到系统的根分区/目前为 98GB,使用了 6.8GB,剩余 87GB。根目录挂在/dev/mapper/ubuntu–vg-ubuntu–lv
1 | lsblk |
运行 lsblk 可以查看块设备的挂载点及分区。可以看到 /dev/sda3 被分配到了 ubuntu–vg-ubuntu–lv 逻辑卷,大小为 100GB
1 | sudo vgdisplay |
运行 vgdisplay 查看当前卷组的详细信息。可以看到 ubuntu-vg 卷组的总大小为 1.82TB,其中 100GB 已被分配给现有的逻辑卷,还剩 1.72TB 可用于扩展
1 | sudo lvextend -l +200G /dev/mapper/ubuntu--vg-ubuntu--lv |
将逻辑卷 ubuntu-lv 的大小增加 200 GB,-l:表示根据扩展单位 “PE(Physical Extents)” 来扩展空间。+200G:表示将逻辑卷增加 200 GB 的容量,/dev/mapper/ubuntu–vg-ubuntu–lv:是目标逻辑卷的路径,表示要扩展的逻辑卷在 ubuntu-vg 卷组中的 ubuntu-lv
1 | sudo resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv |
扩展逻辑卷后,需要调整文件系统的大小以使用新分配的空间。使用 LVM 扩展逻辑卷时,文件系统不会自动扩展,文件系统扩展是必需的
1 | df -h |
可以看到 /dev/mapper/ubuntu–vg-ubuntu–lv 的总大小已成功扩展