在本文中,我将详细介绍如何部署Kubernetes1.25版本。这篇文章汇集了我个人的部署经验和实践,内容既实用又具有一定的学习值。
最初,这篇文章是为了在我的MacOS M系列(arm64)电脑上部署Kubernetes而编写的。经过不断的测试和改进,这一方法同样适用于生产环境中的部署,例如在CentOSAMD64系统上。当然,这些方法也适用于其他Linux发行版,只需适当调整某些命令即可。
跟着本文章走绝对可以部署K8s;
为了方便快捷的部署,文末我将提供一个一键部署脚本,可以在大约5分钟内完成Master或Node节点的完整部署。
为了更好地学习和实践,我们将从本地部署开始。虽然您也可以选择在云平台上操作,但本文主要探讨如何在MacOS M系列(arm64)上通过虚拟机部署CentOS。我已经在Mac虚拟机上部署CentOS花费了不少时间并解决了众多问题。当然,本文的方法也适用于非Mac或非arm64架构的设备。
接下来,本文内容将分为以下几个部分进行详细介绍:
- 前期准备:MacOS M系列(arm64)安装虚拟机并运行CentOS 7系统
- 包括集群规划和虚拟机创建过程(上面教程不只仅限MacOS)。
- 服务器配置:Kubernetes 1.25 相关配置 - 系统篇
- 涵盖开启SSH服务、配置静态IP、编辑hosts文件、关闭防火墙和SELinux、禁用Swap、系统参数优化、配置Linux内核模块和参数(例如调整文件打开数等),IPVS设置,添加YUM源,以及设置时间同步(使用Chrony作为NTP服务器,可选)。
- 搭建集群:(本文)
- 介绍安装containerd,配置containerd,安装和配置crictl,以及安装Kubernetes 1.25版本的kubectl、kubelet和kubeadm,初始化master集群,生成kubeadm默认配置文件,设置.kube/config,安装Calico网络插件,添加Node节点,并切换Kubernetes的IPVS转发模式。
- 扩展功能:
- 包括部署Traefik,安装NFS存储,安装KubeSphere,安装Grafana,以及部署EFK(Elasticsearch + Filebeat + Kibana)日志收集系统。
- 相关笔记:
- 整理在部署过程中遇到的问题及其解决方案,以及如何部署Filebeat。
进阶教程
接下来的阶段,我会接着更新:
- 扩展功能:
- 包括部署Traefik,安装NFS存储,安装KubeSphere,安装Grafana,以及部署EFK(Elasticsearch + Filebeat + Kibana)日志收集系统。
- 相关笔记:
- 整理在部署过程中遇到的问题及其解决方案,以及如何部署Filebeat。
- 额外内容:
- 部署各种中间件,部署主从数据库,部署高可用kafka,部署高可用HA-Hadoop,redis等等…
- 自动化部署脚本:
- 五分钟内完成从0到1搭建;
文章目录
1. 安装containerd
yum install containerd -y
systemctl enable containerd.service
👇👇👇👇
2. 配置
此命令通常在初始化 containerd
配置或修改默认配置时使用。它可以快速创建一个标准的配置文件,可以在此基础上进行修改,以适应特定的需求或环境。
containerd config default | tee /etc/containerd/config.toml
2.1 配置config
- 下面注意,注意pause版本,我这里安装 k8s 1.25选择了 pause版本3.8
- sandbox_image = “registry.aliyuncs.com/google_containers/pause:3.8”
- 下面配置中plugins的源我已经切换国内了,如果有其他需求可以自行更改
cat > /etc/containerd/config.toml << EOF
disabled_plugins = []
imports = []
oom_score = 0
plugin_dir = ""
required_plugins = []
root = "/var/lib/containerd"
state = "/run/containerd"
temp = ""
version = 2
[cgroup]
path = ""
[debug]
address = ""
format = ""
gid = 0
level = ""
uid = 0
[grpc]
address = "/run/containerd/containerd.sock"
gid = 0
max_recv_message_size = 16777216
max_send_message_size = 16777216
tcp_address = ""
tcp_tls_ca = ""
tcp_tls_cert = ""
tcp_tls_key = ""
uid = 0
[metrics]
address = ""
grpc_histogram = false
[plugins]
[plugins."io.containerd.gc.v1.scheduler"]
deletion_threshold = 0
mutation_threshold = 100
pause_threshold = 0.02
schedule_delay = "0s"
startup_delay = "100ms"
[plugins."io.containerd.grpc.v1.cri"]
device_ownership_from_security_context = false
disable_apparmor = false
disable_cgroup = false
disable_hugetlb_controller = true
disable_proc_mount = false
disable_tcp_service = true
enable_selinux = false
enable_tls_streaming = false
enable_unprivileged_icmp = false
enable_unprivileged_ports = false
ignore_image_defined_volumes = false
max_concurrent_downloads = 3
max_container_log_line_size = 16384
netns_mounts_under_state_dir = false
restrict_oom_score_adj = false
# 注释上面那行,添加下面这行,注意pause版本
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.8"
selinux_category_range = 1024
stats_collect_period = 10
stream_idle_timeout = "4h0m0s"
stream_server_address = "127.0.0.1"
stream_server_port = "0"
systemd_cgroup = false
tolerate_missing_hugetlb_controller = true
unset_seccomp_profile = ""
[plugins."io.containerd.grpc.v1.cri".cni]
bin_dir = "/opt/cni/bin"
conf_dir = "/etc/cni/net.d"
conf_template = ""
ip_pref = ""
max_conf_num = 1
[plugins."io.containerd.grpc.v1.cri".containerd]
default_runtime_name = "runc"
disable_snapshot_annotations = true
discard_unpacked_layers = false
ignore_rdt_not_enabled_errors = false
no_pivot = false
snapshotter = "overlayfs"
[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]
base_runtime_spec = ""
cni_conf_dir = ""
cni_max_conf_num = 0
container_annotations = []
pod_annotations = []
privileged_without_host_devices = false
runtime_engine = ""
runtime_path = ""
runtime_root = ""
runtime_type = ""
[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime.options]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
base_runtime_spec = ""
cni_conf_dir = ""
cni_max_conf_num = 0
container_annotations = []
pod_annotations = []
privileged_without_host_devices = false
runtime_engine = ""
runtime_path = ""
runtime_root = ""
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
BinaryName = ""
CriuImagePath = ""
CriuPath = ""
CriuWorkPath = ""
IoGid = 0
IoUid = 0
NoNewKeyring = false
NoPivotRoot = false
Root = ""
ShimCgroup = ""
# 下面目的是指定使用 systemd 作为 cgroup 的管理器
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime]
base_runtime_spec = ""
cni_conf_dir = ""
cni_max_conf_num = 0
container_annotations = []
pod_annotations = []
privileged_without_host_devices = false
runtime_engine = ""
runtime_path = ""
runtime_root = ""
runtime_type = ""
[plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime.options]
[plugins."io.containerd.grpc.v1.cri".image_decryption]
key_model = "node"
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = ""
[plugins."io.containerd.grpc.v1.cri".registry.auths]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.headers]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
#endpoint = ["https://registry-1.docker.io"]
# 注释上面那行,添加下面三行
endpoint = ["https://docker.mirrors.ustc.edu.cn"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
endpoint = ["https://registry.aliyuncs.com/google_containers"]
[plugins."io.containerd.grpc.v1.cri".x509_key_pair_streaming]
tls_cert_file = ""
tls_key_file = ""
[plugins."io.containerd.internal.v1.opt"]
path = "/opt/containerd"
[plugins."io.containerd.internal.v1.restart"]
interval = "10s"
[plugins."io.containerd.internal.v1.tracing"]
sampling_ratio = 1.0
service_name = "containerd"
[plugins."io.containerd.metadata.v1.bolt"]
content_sharing_policy = "shared"
[plugins."io.containerd.monitor.v1.cgroups"]
no_prometheus = false
[plugins."io.containerd.runtime.v1.linux"]
no_shim = false
runtime = "runc"
runtime_root = ""
shim = "containerd-shim"
shim_debug = false
[plugins."io.containerd.runtime.v2.task"]
platforms = ["linux/amd64"]
sched_core = false
[plugins."io.containerd.service.v1.diff-service"]
default = ["walking"]
[plugins."io.containerd.service.v1.tasks-service"]
rdt_config_file = ""
[plugins."io.containerd.snapshotter.v1.aufs"]
root_path = ""
[plugins."io.containerd.snapshotter.v1.btrfs"]
root_path = ""
[plugins."io.containerd.snapshotter.v1.devmapper"]
async_remove = false
base_image_size = ""
discard_blocks = false
fs_options = ""
fs_type = ""
pool_name = ""
root_path = ""
[plugins."io.containerd.snapshotter.v1.native"]
root_path = ""
[plugins."io.containerd.snapshotter.v1.overlayfs"]
root_path = ""
upperdir_label = false
[plugins."io.containerd.snapshotter.v1.zfs"]
root_path = ""
[plugins."io.containerd.tracing.processor.v1.otlp"]
endpoint = ""
insecure = false
protocol = ""
[proxy_plugins]
[stream_processors]
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
path = "ctd-decoder"
returns = "application/vnd.oci.image.layer.v1.tar"
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]
accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]
args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
path = "ctd-decoder"
returns = "application/vnd.oci.image.layer.v1.tar+gzip"
[timeouts]
"io.containerd.timeout.bolt.open" = "0s"
"io.containerd.timeout.shim.cleanup" = "5s"
"io.containerd.timeout.shim.load" = "5s"
"io.containerd.timeout.shim.shutdown" = "3s"
"io.containerd.timeout.task.state" = "2s"
[ttrpc]
address = ""
gid = 0
uid = 0
EOF
确保容器运行时环境(containerd)的配置更新能够正确应用,并重新启动服务以使这些更改生效:
systemctl daemon-reload
systemctl restart containerd
3. 安装crictl
主要目的是为了提供一个命令行界面来与容器运行时的 CRI(容器运行时接口)进行交互。在 Kubernetes 集群管理中尤为重要;
因为我是arm64架构下载了arm版本。如果是amd64则执行amd64
相关的命令;
# arm64 版本
wget https://kubernetes-1251060623.cos.ap-shanghai.myqcloud.com/crictl/v1.24.2/crictl-v1.24.2-linux-arm64.tar.gz
tar -zxf crictl-v1.24.2-linux-arm64.tar.gz
mv crictl /usr/local/bin/
# amd64 版本
wget https://kubernetes-1251060623.cos.ap-shanghai.myqcloud.com/crictl/v1.24.2/crictl-v1.24.2-linux-amd64.tar.gz
tar -zxf crictl-v1.24.2-linux-amd64.tar.gz
mv crictl /usr/local/bin/
编写配置文件
cat > /etc/crictl.yaml <<EOF
runtime-endpoint: unix:///var/run/containerd/containerd.sock
image-endpoint: unix:///var/run/containerd/containerd.sock
timeout: 10
debug: false
pull-image-on-create: false
EOF
4. 安装1.25版本的kubectl kubelet kubeadm
sudo yum install -y kubelet-1.25.0 kubeadm-1.25.0 kubectl-1.25.0
systemctl start kubelet.service
systemctl enable kubelet.service
到这里,完成了系统配置和k8s配置了,现在又可以克隆系统了,从此后可以在新克隆的系统中选择master和node节点了
到这里,完成了系统配置和k8s配置了,现在又可以克隆系统了,从此后可以在新克隆的系统中选择master和node节点了
到这里,完成了系统配置和k8s配置了,现在又可以克隆系统了,从此后可以在新克隆的系统中选择master和node节点了
5. 初始化master集群
使用 kubeadm
工具生成默认的 Kubernetes 集群初始化配置文件
kubeadm config print init-defaults --kubeconfig ClusterConfiguration > kubeadm.yml
上面命令执行后,/root根目录下产生kubeadm.yml文件,修个这个文件修改配置文件
-
修改
imageRepository
,改为aliyun的镜像仓库地址 -
如果需要修改
nodeRegistration
的taints
(非必需,根据需求选择)- 我的配置中effect设置了NoSchedule这意味着带有这个
taint
的节点将不会调度那些没有相应toleration
的 Pod。具体来说,node-role.kubernetes.io/master
这个taint
通常用于 master(控制平面)节点,以防止一般的工作负载被调度到这些节点上,从而确保控制平面的资源不被一般的 Pod 使用。
- 我的配置中effect设置了NoSchedule这意味着带有这个
-
修改
networking
中的podSubnet
以及serviceSubnet
(没有则添加),根据的自己的环境进行设置podSubnet
定义了 Pod 网络的 IP 地址范围。这个范围被用于分配给集群中的每个 Pod IP 地址。serviceSubnet
定义了 Kubernetes Service 对象的虚拟 IP 地址范围。这个范围用于分配给 Service 的 IP 地址。
-
设置
cgroupDriver
为systemd
,非必要 -
修改
advertiseAddress
为正确的Master的IP地址或者0.0.0.0(不推荐0.0.0.0 意味着监听所有接口上的流量) -
修改
nodeRegistration
的name
为k8s-matser名称(master中就是设置的hostname,我当前k8s-master)
修改后的配置文件(下面是一个样例,根据我上面所说的和自己的实际需求去配置)
apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
- system:bootstrappers:kubeadm:default-node-token
token: abcdef.0123456789abcdef
ttl: 24h0m0s
usages:
- signing
- authentication
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 10.211.55.100
bindPort: 6443
nodeRegistration:
criSocket: unix:///var/run/containerd/containerd.sock
imagePullPolicy: IfNotPresent
name: k8s-master
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master
---
apiServer:
timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns: {}
etcd:
local:
dataDir: /var/lib/etcd
imageRepository: registry.aliyuncs.com/google_containers
kind: ClusterConfiguration
kubernetesVersion: 1.25.0
networking:
dnsDomain: cluster.local
podSubnet: 10.10.0.0/16
serviceSubnet: 10.96.0.0/12
scheduler: {}
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
6. 生成kubeadm默认配置文件
在master中执行,node跳过
kubeadm init --config=kubeadm.yml --upload-certs
如果配置错误可以使用 kubeadm reset -f
重置集群记得 node节点也要重置一下
日志中看到这行表示成功
👇👇👇
记着上面的集群令牌kubeadm join 10.211.55.100:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:ed8b37e76c33c9ffec98f89816d6c9f8a6c616b2f80aab30da2aacb1ec382b0a
,等会需要使用!!
7. 设置.kube/config
想要哪个用户使用k8s就在哪个用户上面配置一下(master或node,甚至宿主机)
下面是master的配置;
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
编辑vim /etc/profile
添加一行
export KUBECONFIG=/etc/kubernetes/admin.conf
保存后刷新profile
source /etc/profile
8. calico网络插件安装
安装 Calico 网络插件在 Kubernetes 集群中的主要目的是提供网络功能,确保 pod 间和外部世界的通信
master中设置
下面👇根据自己的配置自行选择下载链接;
# 官方部署
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
# 国内镜像版本(arm64 架构)
kubectl apply -f https://kubernetes-1251060623.cos.ap-shanghai.myqcloud.com/yaml/calico/v3.26.1/calico_arm.yaml
# 国内镜像版本(amd64 架构)
kubectl apply -f https://kubernetes-1251060623.cos.ap-shanghai.myqcloud.com/yaml/calico/v3.26.1/calico_amd.yaml
在master查看状态
kubectl get pods -A
👇👇👇
9. node加入节点
从master中copy两个系统镜像,我这先配置node1,最后从node1中copy一个node2配置hostname和ip就可以
进入node1的ssh
9.1 设置主机名称
sudo hostnamectl set-hostname k8s-node1
9.2 修改静态ip
查找连接名称 nmcli con show
查询出当前运行中的静态网卡配置为:static-enp0s5
修改 IP 地址: nmcli con mod static-enp0s5 ipv4.addresses 10.211.55.101/24
这里的ip根据自己的实际情况来定
重新启动网络连接
nmcli con up static-enp0s5
👇👇👇
上面配置完成后,ssh会断开链接,根据刚刚设置的新的静态Ip地址来重新登陆Node节点;
9.3 节点加入
使用刚刚的集群令牌在node节点上执行加入集群,node1、node2一样
kubeadm join 10.211.55.100:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:ed8b37e76c33c9ffec98f89816d6c9f8a6c616b2f80aab30da2aacb1ec382b0a
👇👇👇
加入后在master节点上查看状态
kubectl get nodes
👇👇👇
node节点加入集群的令牌有效期为24小时,可以在master节点生产新的令牌
kubeadm token create --print-join-command
9.2.1 设置token用不过期(可选)
当前token 默认24小时过期,因此测试环境下可以设置用不过期
在master主机上,先查看存在的token
kubeadm token list
生成用不过期的token
kubeadm token create --ttl 0
获取token,然后生成哈希值
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
最后在节点上(token your-token
替换新生成的token;your-ca-cert-hash
替换上面命令生成的hash值)
kubeadm join 192.168.1.101:6443 --token your-token --discovery-token-ca-cert-hash sha256:your-ca-cert-hash
删除token的命令(如果需要)
kubeadm token delete [token]
10. 将k8s切换为ipvs转发模式
主要是为了提高服务转发的性能和扩展性。IPVS 是基于 Netfilter(Linux 内核的网络包过滤框架)的传输层负载均衡技术,适用于处理大量并发连接,特别是在大规模集群环境中
在master节点上修改配置
kubectl -n kube-system edit cm kube-proxy
找到mode:更改为mode: “ipvs”
找到metricsBindAddress: 更改为 metricsBindAddress: “127.0.0.1:10249”
- IP 地址: 这应该是节点上的一个有效的、可访问的 IP 地址。这可以是本地环回地址(127.0.0.1)用于仅在本地访问性能指标,也可以是节点的公共或私有网络地址,以便在网络中的其他位置访问性能指标。
- 端口号: 需要指定一个未被占用的端口号。通常,默认的端口号是 10249,但可以根据需要选择不同的端口。
👇👇👇
将原来的kube-proxy删除,重启
kubectl -n kube-system get pod -l k8s-app=kube-proxy | grep -v 'NAME' | awk '{print $1}' | xargs kubectl -n kube-system delete pod
11. 配置kubectl TAB补全功能
这是一个可选项,目的是为了终端方便操作kubectl相关命令
yum install bash-completion -y
source /usr/share/bash-completion/bash_completion
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc
到这里整个k8s集群环境就基本搭建完成了!!!
注意
- 安装时注意自己安装的程序版本
- 一般安装不会在胜利,会出现不少问题,用tail -f /var/log/messages跟踪下日志
- 最好把几台机器系统时间同步下,节点通讯中的token与时间也有关