使用 kind 搭建 K8s 多节点集群

CSDN 2024-10-06 09:07:03 阅读 72

在这里插入图片描述

Kind(Kubernetes in Docker)简介

<code>Kind是一个使用Docker容器“节点”运行本地Kubernetes集群的工具。‌它允许用户快速创建、删除Kubernetes集群,非常适合本地开发和测试。Kind的名称是Kubernetes in Docker的缩写,意味着它将Kubernetes的所有组件部署在一个或多个Docker容器中,以此来模拟一个完整的Kubernetes集群。通过Kind,用户可以轻松地搭建单节点或多节点的Kubernetes集群,这对于开发和测试Kubernetes相关的应用非常有帮助。

使用Kind安装配置集群的步骤相对简单,首先需要确保已经安装了DockerGo环境。安装完成后,用户可以通过命令行工具Kind来创建和管理Kubernetes集群。Kind支持通过配置文件来定义集群的配置,包括节点数量、网络配置等,从而满足不同场景下的需求。此外,Kind还支持高可用集群的搭建,通过模拟多个节点来实现集群的高可用性。

Kind主要用途包括:

本地开发‌:开发者可以使用Kind在本地快速搭建一个Kubernetes环境,模拟生产环境的各种场景,从而加速应用的开发和测试过程。测试‌:由于Kind搭建的集群完全在本地运行,因此非常适合进行单元测试、集成测试等,确保应用在Kubernetes环境中的兼容性和稳定性。教育和学习‌:对于学习和教学来说,Kind提供了一个低成本、高效率的方式来理解和实践Kubernetes的工作原理和架构。

总之,Kind是一个功能强大且易于使用的工具,无论是开发者测试人员还是教育工作者,都可以通过它来更好地理解和使用Kubernetes‌

使用kind安装配置集群

kind主要用于在本地机器上快速启动一个Kubernetes集群,由K8s官方开发设计,用于日常开发和测试勿用于生产环境)。

本文参照kind官方文档,介绍如何使用kind安装配置Kubernetes集群。

笔者使用的机器是MacBookPro M1,所以演示的一些命令为macOS平台下的指令。

kind内部使用kubeadm来启动一个多节点集群,它使用自实现的kindnetd

作为容器网络接口(CNI)实现。更多设计细节参考kind设计文档。

1. 安装kind

支持多种方式安装,笔者是macOS,所以使用Homebrew安装:

brew install kind

其他系统参考二进制安装kind。

2. 创建一个集群

kind使用一个构建好的节点镜像以容器的形式来启动一个集群(一个K8s单节点集群运行在一个容器中),镜像中包含了Kubernetes的关键组件,比如kubelet等。

节点镜像托管在DockerHub

,查看它的更多介绍。

创建命令如下:

kind create cluster --image=kindest/node:v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cba8a463690e98317add2c9ba72

如果不使用--imageflag,则使用当前kind版本默认的节点镜像。但为了确定安装行为,请使用--imageflag。

在kind版本发布页查找当前kind版本的预构建的不同K8s版本的节点镜像tag。

如果没有你想要的K8s版本,参考这个页面自行构建节点镜像。

创建后使用下面的命令简单管理集群:

kind get clusters

kind delete cluster -n <name>

当然,以上只是以最简化的集群配置方式启动。kind支持通过yaml文件[kind-config.yaml]来详细配置集群的启动参数,以下是一个包含注释的1主2Worker集群的完整kind配置示例。使用配置文件启动集群:

# nonk8s

# 此配置文件的完整说明位于:https://kind.sigs.k8s.io/docs/user/configuration/#getting-started

kind: Cluster

apiVersion: kind.x-k8s.io/v1alpha4

name: test-1.27 # 集群名

featureGates: # 启动/禁用K8s的特性门

"AdmissionWebhookMatchConditions": true

# 配置API Server的--runtime-config

# https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/

runtimeConfig:

"api/alpha": "false"

# 配置集群的网络

#networking:

# ipFamily: ipv6

# apiServerAddress: 127.0.0.1 # 默认127.0.0.1

# apiServerPort: 6443 # 默认随机

# podSubnet: "10.244.0.0/16" # 默认值

# serviceSubnet: "10.96.0.0/12" # 默认值

# disableDefaultCNI: true # 默认false,禁用后需安装其他CNI,例如calico

# kubeProxyMode: "ipvs" # kube-proxy使用的组件,默认iptables,设置none则禁用 kube-proxy

# 配置节点,下面是一个一主多worker的配置

nodes:

- role: control-plane # 默认的主节点

# 可选的为每个节点配置使用的节点镜像,不指定则使用kind版本对应的默认值

image: kindest/node:v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cba8a463690e98317add2c9ba72

# 可选的节点路径映射配置,用于持久化数据

# 若使用mac或win上的docker,需要检查hostPath必须存在于Docker配置中的Preferences -> Resources -> File Sharing

extraMounts:

- hostPath: ~/node_volume # 需提前创建

containerPath: /node_volume

- hostPath: ~/node_volume/example_file

containerPath: /example_file

readOnly: true # 默认false

selinuxRelabel: false # 默认false。selinuxRelabel是一个linux内核安全模块,请自行查询此参数的用途

propagation: None # https://kubernetes.io/docs/concepts/storage/volumes/#mount-propagation

# 可选的端口映射配置,用于像节点容器传入流量

# 若使用Docker for Desktop,则必须配置

extraPortMappings:

- containerPort: 30080 # 作为Pod的hostPort

hostPort: 80

# listenAddress: "127.0.0.1" # 默认0.0.0.0

protocol: TCP # 支持TCP(默认),有UDP, SCTP

# 可选的节点标签配置

labels:

tier: frontend

# 因为使用kubeadm启动集群,所以支持kubeadm的配置文件(可选)

kubeadmConfigPatches:

- |

# 仅限第一个主节点使用InitConfiguration

kind: InitConfiguration # 还支持ClusterConfiguration,KubeProxyConfiguration,KubeletConfiguration,JoinConfiguration

nodeRegistration:

kubeletExtraArgs:

node-labels: "master=true"

- role: worker

kubeadmConfigPatches:

- |

kind: JoinConfiguration

nodeRegistration:

kubeletExtraArgs:

node-labels: "worker=true"

- role: worker

# 注意内存占用,主节点占用约500MB,Worker节点占用约200MB,总共占用约1GB。确保你的宿主机内存充足

kind create cluster --config=kind-config.yaml --retain --wait=1m

其中的--retainflag表示在kind命令执行结束后保留节点容器,否则集群会自动删除。保留的目的是方便在启动集群失败时进入容器查看错误日志:

journalctl -xeu kubectl

--wait=1m是等待控制节点Ready的最长等待时间。下文将使用这个集群进行演示。

1.1 安装kubectl

kind创建集群后,我们需要在本机上安装kubectl来连接并管理集群。

如果你的机器已存在kubectl但版本与安装的k8s版本不同,可通过以下方式卸载:

which kubectl

sudo rm <path>

安装kubectl v1.27.3版本:

curl -LO "https://dl.k8s.io/release/v1.27.3/bin/darwin/arm64/kubectl"

sudo mv kubectl /usr/local/bin

sudo chmod +x /usr/local/bin/kubectl

kubectl version

该命令安装arm64架构的kubectl,其他架构请参考kubectl安装文档。

1.2 连接集群

kind在创建集群后会自动在本机的$HOME/.kube/config处配置好kubeconfig文件。此时我们已经可以使用kubectl来连接并管理集群了。

kubectl cluster-info

# 查看宿主机上的节点容器列表

docker ps |grep test-1.27

# 查看使用的容器运行时(containerd)

docker exec -it test-1.27-control-plane crictl info|grep runtimeType

不过有个问题得注意一下,containerd默认的镜像仓库地址是docker.io,后续使用K8s拉取远端镜像会特别慢,

你可以参考 containerd.config.toml,来修改每个节点容器中的containerd配置文件(位于/etc/containerd/config.toml,搜索关键字registry.mirrors),修改后需要重启containerd(service containerd restart)。

containerd.config.toml配置:

disabled_plugins = []

imports = []

oom_score = 0

plugin_dir = ""

required_plugins = []

root = "/var/lib/containerd" # 容器存放路径,确保可用空间充足

state = "/run/containerd"

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_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"]

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

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

sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6"

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 = "/etc/cni/net.d/cni-default.conf"

max_conf_num = 1

[plugins."io.containerd.grpc.v1.cri".containerd]

default_runtime_name = "runc"

disable_snapshot_annotations = true

discard_unpacked_layers = false

no_pivot = false

snapshotter = "overlayfs"

[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]

base_runtime_spec = ""

container_annotations = []

pod_annotations = []

privileged_without_host_devices = false

runtime_engine = ""

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 = ""

container_annotations = []

pod_annotations = []

privileged_without_host_devices = false

runtime_engine = ""

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 = ""

SystemdCgroup = true

[plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime]

base_runtime_spec = ""

container_annotations = []

pod_annotations = []

privileged_without_host_devices = false

runtime_engine = ""

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://docker.mirrors.ustc.edu.cn", "http://hub-mirror.c.163.com"]

[plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"]

endpoint = ["https://gcr.mirrors.ustc.edu.cn"]

[plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]

endpoint = ["https://gcr.mirrors.ustc.edu.cn/google-containers/"]

[plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"]

endpoint = ["https://quay.mirrors.ustc.edu.cn"]

[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.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"]

[plugins."io.containerd.service.v1.diff-service"]

default = ["walking"]

[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 = ""

pool_name = ""

root_path = ""

[plugins."io.containerd.snapshotter.v1.native"]

root_path = ""

[plugins."io.containerd.snapshotter.v1.overlayfs"]

root_path = ""

[plugins."io.containerd.snapshotter.v1.zfs"]

root_path = ""

[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.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

当然还有另一种办法,那就是直接使用宿主机的docker镜像,后面的3.1节会介绍如何操作。

1.4 创建并管理多个集群

前面已经介绍了可以通过kind get clusters看到当前kind安装的K8s集群列表。这就告诉了我们kind可以同时安装多个集群。

安装多集群后,可以切换context来连接不同的集群。

# 安装两个集群

$ kind create cluster

$ kind create cluster --name kind-2

# 查看集群列表

$ kind get clusters

kind

kind-2

# 切换context(默认是kind-kind)

$ kubectl cluster-info --context kind-kind

$ kubectl cluster-info --context kind-kind-2

3. 部署应用

3.1 添加镜像到节点容器

如果我们想要将宿主机上已存在的docker镜像直接导入节点容器(免去重复拉取),参照下面的命令:

# 需要先将镜像拉取到宿主机

docker pull busybox

# 再进行load操作

kind load docker-image busybox -n test-1.27

# 也可以从tar包导入

kind load image-archive /my-image-archive.tar

# 导入镜像到特定节点容器(默认所有)

kind load docker-image <image1> --nodes <node-name>

Load之后,查看节点容器中的镜像:

$ docker exec -it test-1.27-control-plane crictl images

IMAGE TAG IMAGE ID SIZE

docker.io/kindest/kindnetd v20230511-dc714da8 b18bf71b941ba 25.3MB

docker.io/kindest/local-path-helper v20230510-486859a6 d022557af8b63 2.92MB

docker.io/kindest/local-path-provisioner v20230511-dc714da8 eec7db0a07d0d 17.3MB

docker.io/library/busybox latest 23466caa55cb7 4.27MB

registry.k8s.io/coredns/coredns v1.10.1 97e04611ad434 14.6MB

registry.k8s.io/etcd 3.5.7-0 24bc64e911039 80.7MB

registry.k8s.io/kube-apiserver v1.27.3 634c53edb5c14 79.8MB

registry.k8s.io/kube-controller-manager v1.27.3 aea4f169db16d 71.5MB

registry.k8s.io/kube-proxy v1.27.3 278dd40f83dfb 68.1MB

registry.k8s.io/kube-scheduler v1.27.3 6234a065dec4c 57.6MB

registry.k8s.io/pause 3.7 e5a475a038057 268kB

3.2 部署Pod

注意,我们前面已经在宿主机上安装了kubectl,所以现在可以直接在宿主机上管理集群,而不需要进入节点容器。

pod_busybox.yaml配置如下:

apiVersion: v1

kind: Pod

metadata:

name: busybox

labels:

app: busybox

spec:

containers:

- name: busybox-container

image: busybox

command: [ "sleep", "infinity" ]

下面以清单 [pod_busybox.yaml]为例进行部署演示。

$ kubectl apply -f pod_busybox.yaml

# 清单中使用的镜像是上一节中导入的镜像,所以Pod应该很快Running

$ kubectl get po

NAME READY STATUS RESTARTS AGE

busybox 1/1 Running 0 36s

再部署一个可通过宿主机访问的应用 [deployment_python_http_svc_nodeport.yaml]:

apiVersion: apps/v1

kind: Deployment

metadata:

name: python-http-serv

spec:

selector:

matchLabels:

app: http

template:

metadata:

labels:

app: http

spec:

containers:

- name: http-container

image: python:3.9-alpine

command: [ "python3", "-mhttp.server", "8080" ]

ports:

- containerPort: 8080

---

apiVersion: v1

kind: Service

metadata:

name: python-http-serv

spec:

type: NodePort

selector:

app: http

ports:

- port: 80

targetPort: 8080

nodePort: 30080

具体操作:

$ docker pull python:3.9-alpine

$ kind load docker-image python:3.9-alpine -n test-1.27

$ kubectl apply -f deployment_python_http_svc_nodeport.yaml

$ kubectl get po

NAME READY STATUS RESTARTS AGE

busybox 1/1 Running 1 (15m ago) 19m

python-http-serv-6b874b4bdf-wtfhl 1/1 Running 0 5s

# 访问宿主机80端口(映射到控制平面节点的30080端口),可以看到一个HTML输出,其中包含Pod内容器的根目录下的文件列表

# 推荐使用浏览器访问

$ curl http://localhost/

结尾

在查看kind官方文档的时候,发现kind缺少一个可能比较关键的功能,那就是在kind配置文件限制节点容器的CPU/Memory额度。遗憾的是,

笔者看到了kind仓库中的这个ISSUE #1422

,也就是说kind截止目前(2024-1-2)也没有支持这个功能。


以上就是使用kind在MacOS上安装一个多节点集群的过程,其他操作系统的安装过程也是大差不差,具体可以看kind官文。

如果你有遇到问题请提出ISSUE,但也希望你能够先看一下官方kind文档。

参考

kind官方文档



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。