您当前的位置:首页 > 电脑百科 > 网络技术 > 网络技术

构建最小化的 Kubernetes 集群

时间:2020-07-07 16:11:54  来源:  作者:

Kubernetes 号称云原生操作系统,可想而知其复杂程度也是非常大的,由许多组件组成,我们很难去追踪到所有的组件信息。

构建最小化的 Kubernetes 集群

 

上图中至少列出了七八个组件,我们这里会忽略其中大部分组件,要运行一个最小级别的 Kubernetes 至少要包括如下三个基本组件:

  • kubelet:在集群中每个节点上运行的代理,负责容器真正运行的核心组件
  • kube-apiserver:Kubernetes 控制平面的组件,提供资源操作的唯一入口

这里我们来尝试配置一个最小级别的 Kubernetes,这对于我们加速对集群的理解也是非常有帮助的。

安装

首先需要在节点上安装 Docker 容器运行时,我们这里使用的是操作系统为 centos7 版本,在 root 用户下面执行相关操作。执行如下所示命令直接安装即可:

$ yum install -y yum-utils
$ yum-config-manager 
    --add-repo 
    https://download.docker.com/linux/centos/docker-ce.repo
$ yum install -y docker-ce docker-ce-cli containerd.io
$ systemctl enable docker
$ systemctl daemon-reload
$ systemctl start docker

接下来,我们需要获取 Kubernetes 二进制文件。实际上,我们只需要使用 kubelet 组件来引导我们的“集群”,因为我们可以使用 kubelet 来运行其他组件,一旦集群启动了,我们就可以使用 kubectl 来进行操作了。

$ curl -L https://dl.k8s.io/v1.18.5/kubernetes-server-linux-amd64.tar.gz > server.tar.gz
$ tar xzvf server.tar.gz
$ cp kubernetes/server/bin/kubelet .
$ cp kubernetes/server/bin/kubectl .
$ ./kubelet --version
Kubernetes v1.18.5

由于 kubelet 配置太多,但是这里我们只需要几个设置参数即可:

$ ./kubelet -h
<far too much output to copy here>
$ ./kubelet -h | wc -l
284

我们这里需要使用的是 --pod-manifest-path 这个参数,该参数用于指定要运行的静态 Pod 文件的目录,静态 Pod 不受 Kubernetes API 管理,虽然平时我们在使用 Kubernetes 的时候比较少使用静态 Pod,但是对于引导集群却是非常有用,对 Kubeadm 熟悉的应该知道,该方案就是利用静态 Pod 将 Kubernetes 控制面板容器化的。下面我们来尝试下是否可以使用 kubelet 来运行 Pod。

首先我们创建一个静态 Pod 目录来运行 kubelet:

$ mkdir pods
$ ./kubelet --pod-manifest-path=pods

然后重新打开一个终端,创建如下所示的 Pod 资源清单文件:

$ cat <<EOF > pods/hello.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello
spec:
  containers:
  - image: busybox
    name: hello
    command: ["echo", "hello world!"]
EOF

上面资源清单出现在 pods 目录后,就可以在 kubelet 日志中看到如下所示的错误信息:

......
E0707 10:25:58.489839   21311 pod_workers.go:191] Error syncing pod ab61ef0307c6e0dee2ab05dc1ff94812 ("hello-iz2ze8x2keg0a301rpa7cvz_default(ab61ef0307c6e0dee2ab05dc1ff94812)"), skipping: failed to "CreatePodSandbox" for "hello-iz2ze8x2keg0a301rpa7cvz_default(ab61ef0307c6e0dee2ab05dc1ff94812)" with CreatePodSandboxError: "CreatePodSandbox for pod "hello-iz2ze8x2keg0a301rpa7cvz_default(ab61ef0307c6e0dee2ab05dc1ff94812)" failed: rpc error: code = Unknown desc = failed pulling image "k8s.gcr.io/pause:3.2": Error response from daemon: Get https://k8s.gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)"
......

这是因为 Kubernetes 的 Pod 默认情况下会优先启动一个 k8s.gcr.io/pause:3.2的 pause 镜像,而该镜像由于某些原因获取不到,我们可以 --pod-infra-container-image 参数重新指定一个可以访问到的镜像:

$ ./kubelet --pod-manifest-path=pods --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.2

现在我们检查下 Docker 容器是否有新的容器启动:

$ docker ps -a
CONTAINER ID        IMAGE                  COMMAND                 CREATED             STATUS                      PORTS               NAMES
d6e822dbcebd        busybox                                                              "echo 'hello world!'"    27 seconds ago      Exited (0) 26 seconds ago                             k8s_hello_hello-iz2ze8x2keg0a301rpa7cvz_default_ab61ef0307c6e0dee2ab05dc1ff94812_3
102b999be2dd        registry.aliyuncs.com/google_containers/pause:3.2                    "/pause"                 2 minutes ago       Up 2 minutes                                          k8s_POD_hello-iz2ze8x2keg0a301rpa7cvz_default_ab61ef0307c6e0dee2ab05dc1ff94812_0
$ docker logs k8s_hello_hello-iz2ze8x2keg0a301rpa7cvz_default_ab61ef0307c6e0dee2ab05dc1ff94812_3
hello world!

kubelet 通过我们指定的静态 Pod 目录,读取其中的 YAML 文件来创建 Pod。由于我们这里执行的就是 echo 命令,所以会不断的重启,验证完成后删除该 YAML 文件即可。

当然这还不够,我们还需要运行 APIServer,要做到这一点,我们需要首先运行 etcd,同样的我们也可以使用静态 Pod 来运行 etcd,创建如下所示的 etcd 资源清单文件:

$ cat <<EOF > pods/etcd.yaml
apiVersion: v1
kind: Pod
metadata:
  name: etcd
  namespace: kube-system
spec:
  containers:
  - name: etcd
    command:
    - etcd
    - --data-dir=/var/lib/etcd
    image: registry.aliyuncs.com/google_containers/etcd:3.4.3-0
    volumeMounts:
    - mountPath: /var/lib/etcd
      name: etcd-data
  hostNetwork: true
  volumes:
  - hostPath:
      path: /var/lib/etcd
      type: DirectoryOrCreate
    name: etcd-data
EOF

这就是一个非常普通的 Pod 资源清单文件,大家应该都非常熟悉,不过还是需要注意两件事:

  • 我们将宿主机的 /var/lib/etcd 目录挂载到 Pod 容器中,这样可以保证 etcd 在重新启动以后数据依然存在。
  • 另外我们设置了 hostNetwork=true,这样可以使容器和宿主机共享网络命名空间,可以让 APIServer 更容易和 etcd 通信。

我们可以使用如下所示的命令来检查 etcd 是否启动成功:

$ curl localhost:2379/version
{"etcdserver":"3.4.3","etcdcluster":"3.4.0"}
$ tree /var/lib/etcd/
/var/lib/etcd/
└── member
    ├── snap
    │   └── db
    └── wal
        ├── 0000000000000000-0000000000000000.wal
        └── 0.tmp

3 directories, 3 files

现在 etcd 启动成功了,就可以来启动 APIServer 了,我们这里只需要通过参数 --etcd-servers 传递 etcd 地址即可,同样在静态 pods 目录下面创建如下所示的资源清单:

$ cat <<EOF > pods/apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - name: kube-apiserver
    command:
    - kube-apiserver
    - --etcd-servers=http://127.0.0.1:2379
    image: cnych/kube-apiserver:v1.18.5  # 阿里云镜像未同步
  hostNetwork: true
EOF

创建完成后正常 APIServer 就会正常启动,可以通过如下所示的命令来验证:

$ curl localhost:8080/healthz
ok
$ curl localhost:8080/api/v1/pods
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "selfLink": "/api/v1/pods",
    "resourceVersion": "59"
  },
  "items": []
}

而且 kubectl 也不需要额外的配置就可以直接使用了:

$ ./kubectl version
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:47:41Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:39:24Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
$ ./kubectl get pod
No resources found in default namespace.

这是因为 kubectl 默认是通过 localhost:8080 和 APIServer 进行通信的。

配置

但是当我们去获取刚刚创建的静态 Pod 的时候却发现没有对应的记录:

$ ./kubectl get pod -n kube-system
No resources found in kube-system namespace.

而且运行 kubelet 的节点也根本没有显示:

$ ./kubectl get nodes
No resources found in default namespace.

这其实是因为 kubelet 不知道如何与 APIServer 进行通信并更新状态造成的,我们可以通过 kubelet 的 --kubeconfig 参数来指定 KUBECONFIG 文件的路径,可以通过该文件来指定如何连接到 APIServer。由于我们这里就是启动一个最新的 Kubernetes,没有身份验证或者证书之类的麻烦事情,所以非常简单,创建名为 kubeconfig.yaml 的如下所示文件:

apiVersion: v1
kind: Config
clusters:
- cluster:
    server: http://127.0.0.1:8080
  name: mink8s
contexts:
- context:
    cluster: mink8s
  name: mink8s
current-context: mink8s

然后杀掉 kubelet 进程,添加上 --kubeconfig 参数重新运行:

$ ./kubelet --pod-manifest-path=pods --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.2 --kubeconfig=kubeconfig.yaml

隔一会儿后我们再次使用 kubectl 来查看上面我们运行的静态 Pod 就正常了:

$ ./kubectl get pods -A
NAMESPACE     NAME                    READY   STATUS             RESTARTS   AGE
default       hello-mink8s            0/1     CrashLoopBackOff   261        21h
kube-system   etcd-mink8s             1/1     Running            0          21h
kube-system   kube-apiserver-mink8s   1/1     Running            0          21h
$ ./kubectl get nodes -owide
NAME     STATUS   ROLES    AGE   VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION       CONTAINER-RUNTIME
mink8s   Ready    <none>   21h   v1.18.5   10.70.10.228   <none>        CentOS Linux 7 (Core)   4.15.0-109-generic   docker://19.3.6

这样我们就运行了一个最小功能集的 Kubernetes 集群了。下面我们来尝试运行一个普通的 Pod 看能否正常运行。

同样尝试来创建一个 Nginx 的 Pod:

$ cat <<EOF > nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
EOF

然后使用 kubectl 来创建上面的资源对象:

$ ./kubectl Apply -f nginx.yaml
Error from server (Forbidden): error when creating "nginx.yaml": pods "nginx" is
forbidden: error looking up service account default/default: serviceaccount
"default" not found
$ ./kubectl get serviceaccounts
No resources found in default namespace.

可以看到有错误信息,这是因为我们上面部署的最小级别的 Kubernetes 环境完整性还是不够,没有自动生成默认的 default 这个 ServiceAccount,我们来手动创建再来验证一次:

$ cat <<EOF | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
EOF
serviceaccount/default created
$ ./kubectl apply -f nginx.yaml
Error from server (ServerTimeout): error when creating "nginx.yaml": No API
token found for service account "default", retry after the token is
automatically created and added to the service account

我们手动创建了 ServiceAccount,但是却并没有创建对应的身份验证的 Token,我们可以看到以前很多自动完成的操作现在都没有了。

不过我们可以通过 automountServiceAccountToken 参数在 ServiceAccount 上来规避这个特定问题,因为实际上我们这里并不需要使用 ServiceAccount:

$ cat <<EOF | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
automountServiceAccountToken: false
EOF
serviceaccount/default configured
$ ./kubectl apply -f nginx.yaml
pod/nginx created
$ ./kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
nginx   0/1     Pending   0          13m

现在我们可以看到 Pod 出现了,但是处于 pending 状态,这是因为我们并没有部署 kube-scheduler 这个负责调度的组件,自然是不能被调度的,当然我们也可以不需要调度程序,直接使用 nodeName 属性将 Pod 手动固定到节点上即可:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
  nodeName: mink8s

现在将之前部署的 Pod 删除重新来部署,正常就可以运行了:

$ ./kubectl delete pod nginx
pod "nginx" deleted
$ ./kubectl apply -f nginx.yaml
pod/nginx created
$ ./kubectl get pods -owide
NAME    READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          30s   172.17.0.2   mink8s   <none>           <none>
$ curl -s 172.17.0.2 | head -4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

为了验证 Pod 与 Pod 之间是可以正常通信的,我们可以使用如下的 Pod 来验证:

$ cat <<EOF | ./kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: curl
spec:
  containers:
  - image: curlimages/curl
    name: curl
    command: ["curl", "172.17.0.2"]
  nodeName: mink8s
EOF
pod/curl created
$ ./kubectl logs curl | head -6
% Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

可以看到可以正常通信。这样我们就完成了一个最小的 Kubernetes 集群部署。当然这也仅仅是为了简化我们对 Kubernetes 的理解而已,在实际的生产环境是绝对不能这样去部署使用的。

参考

  • https://eevans.co/blog/minimum-viable-kubernetes/
  • https://commons.wikimedia.org/w/index.php?curid=53571935
  • https://kubernetes.io/docs/concepts/overview/components/
  • https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/


Tags:Kubernetes 集群   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
Kubernetes 号称云原生操作系统,可想而知其复杂程度也是非常大的,由许多组件组成,我们很难去追踪到所有的组件信息。 上图中至少列出了七八个组件,我们这里会忽略其中大部分组件...【详细内容】
2020-07-07  Tags: Kubernetes 集群  点击:(85)  评论:(0)  加入收藏
当我们使用 Kubernetes 部署应用后,会发现如果用户增长速度超过预期,以至于计算资源不够时,你会怎么做呢?Kubernetes 给出的解决方案就是:自动伸缩(auto-scaling),通过自动伸缩组件...【详细内容】
2020-06-21  Tags: Kubernetes 集群  点击:(81)  评论:(0)  加入收藏
▌简易百科推荐
写一个shell获取本机ip地址、网关地址以及dns信息。经常会遇到取本机ip、网关、dns地址,windows一个命令ipconfig /all全部获取到,但linux系统却并非如此。linux系统都自带ifc...【详细内容】
2021-12-27  K佬食古    Tags:shell   点击:(2)  评论:(0)  加入收藏
步骤1、配置 /etc/sysconfig/network-scripts/ifcfg-eth0 里的文件。it动力的CentOS下的ifcfg-eth0的配置详情:[root@localhost ~]# vim /etc/sysconfig/network-scripts/ifc...【详细内容】
2021-12-24  忆梦如风    Tags:网卡   点击:(10)  评论:(0)  加入收藏
1、查找当前目录下所有以.tar结尾的文件然后移动到指定目录find . -name “*.tar” -execmv {}./backup/ ;注解:find &ndash;name 主要用于查找某个文件名字,-exec 、xargs可...【详细内容】
2021-12-17  郭主任    Tags:运维   点击:(20)  评论:(0)  加入收藏
对于经常上网的朋友来说,除了手机购物上网,pc端玩网页游戏还是很多小伙伴首选的,但是有时候明明宽带链接上了,打开浏览器却出现上不了网的现象,下面小编要来跟大家说说电脑有网络...【详细内容】
2021-12-16  小白系统    Tags:网页无法打开   点击:(28)  评论:(0)  加入收藏
在访问像github、gitlab这样的外国网站时,很有可能会出现页面加载不出来或找不到页面的错误。这时候有的朋友就会以为是网络的问题,于是把Wifi断掉连上自己手机的热点,结果却还...【详细内容】
2021-12-15  启施技术IT狼叔    Tags:外网   点击:(16)  评论:(0)  加入收藏
网络地址来源:获取公网IP地址 https://ipip.yy.com/get_ip_info.phphttp://pv.sohu.com/cityjson?ie=utf-8http://www.ip168.com/json.do?view=myipaddress...【详细内容】
2021-12-15  韦廷华12    Tags:外网ip   点击:(15)  评论:(0)  加入收藏
准备好软件IPOP、用ENSP模拟一下华为交换机 启动交换机 <Huawei>sysEnter system view, return user view with Ctrl+Z.[Huawei]sysname FTPClient[FTPClient]interface vla...【详细内容】
2021-12-15  思源Edward    Tags:交换机   点击:(24)  评论:(0)  加入收藏
我们经常用到netstat命令查看主机连接状况,包括连接ip、端口、状态等,今天就练习下shell分析netsat结果。描述假设netstat命令运行的结果我们存储在nowcoder.txt里,格式如下:Pro...【详细内容】
2021-12-14  K佬食古    Tags:netstat   点击:(19)  评论:(0)  加入收藏
什么是滑动窗口?窗口是操作系统开辟的一块缓存空间,发送方在收到接收方ACK应答之前,必须在缓冲区保留已发送的数据,如果按期收到确认应答,数据就可以从缓冲区移除。什么是滑动窗...【详细内容】
2021-12-14  DifferentJava    Tags:TCP   点击:(30)  评论:(0)  加入收藏
概述日常管理华为路由设备过程中,难为会忘记设备登录密码,那么该如何重置设备登录密码吗?本期文章将全面向各位小伙伴总结分享。重置华为设备登录密码思路先行 采用console登录...【详细内容】
2021-12-10  onme0    Tags:   点击:(27)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条