最低可行的Kubernetes

本文的翻译是在“ DevOps实践和工具”课程开始之前准备的










如果您正在阅读本文,那么您可能已经听说过有关Kubernetes的内容(如果没有,您是如何结束的?)但是Kubernetes到底是什么呢?这是“工业级容器编排”吗?还是“云原生操作系统”?这到底是什么意思?



老实说,我不确定100%。但是我认为深入研究内部结构并查看其多层抽象下的Kubernetes实际发生的事情很有趣。因此,仅出于娱乐目的,让我们看看最小的“ Kubernetes集群”的外观。 (这将比Kubernetes The Hard Way容易得多。)



我假设您具有Kubernetes,Linux和容器的基本知识。我们在这里谈论的所有内容仅用于研究/研究,请勿在生产中运行任何内容!



总览



Kubernetes包含许多组件。根据Wikipedia的说法,该体系结构如下所示:







这里至少显示了八个组件,但是我们将忽略其中的大部分。我想指出,可以合理地称为Kubernetes的最小事物具有三个主要组成部分:



  • kubelet
  • kube-apiserver(取决于etcd-它的数据库)
  • 容器运行时(在本例中为Docker)


让我们看看文档中关于它们(俄语英语)的说法。首先是kubelet



在集群中每个节点上运行的代理。他确保容器在吊舱中运行。



听起来很简单。怎么样运行的容器(容器运行时)?



容器运行时是旨在运行容器的程序。



非常丰富。但是,如果您熟悉Docker,那么您应该对它的功能有基本的了解。 (的容器运行时和kubelet之间分离关注的细节其实很微妙,我不会去到这里。)



服务器API



API Server-Kubernetes仪表板组件,代表Kubernetes API。API服务器是Kubernetes仪表板的前端,



任何使用Kubernetes做过任何事情的人都必须直接或通过kubectl与API进行交互。这就是创建Kubernetes的核心。Kubernetes是将我们都知道并喜欢的YAML山脉变成一个正常工作的基础设施的大脑。显然,API应该以我们的最小配置出现。



前提条件



  • 扎根的Linux虚拟机或物理机(我在虚拟机中使用Ubuntu 18.04)。
  • 这就是全部!


无聊的安装



Docker需要安装在我们将要使用的机器上。(我不会详细介绍Docker和容器的工作方式;如果您有兴趣的话,这里有很多不错的文章)。让我们用安装它apt



$ sudo apt install docker.io
$ sudo 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
F0609 04:03:29.105194    4583 server.go:254] mkdir /var/lib/kubelet: permission denied


kubelet应该以root身份运行。这很合乎逻辑,因为他需要管理整个节点。让我们看一下它的参数:



$ ./kubelet -h
<  ,   >
$ ./kubelet -h | wc -l
284


哇,有很多选择!幸运的是,我们只需要其中的几个。这是我们感兴趣的参数之一:



--pod-manifest-path string


包含静态容器文件的目录路径,或描述静态容器文件的路径。以句点开头的文件将被忽略。 (不推荐使用:此参数应在通过--config选项传递给Kubelet的配置文件中设置。有关更多信息,请参见kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file。)



此参数使我们可以静态运行豆荚-未通过Kubernetes API管理吊舱。静态Pod很少使用,但是它们对于快速提升集群非常方便,这正是我们所需要的。我们将忽略这个响亮的警告(再次,不要在生产中运行此警告!),看看是否可以在下面运行。



首先,我们将为静态容器创建一个目录并运行kubelet



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


然后,在另一个终端/ tmux窗口/其他地方,我们将创建一个pod清单:



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


kubelet开始写一些警告,似乎什么都没有发生。但这种情况并非如此!让我们看一下Docker:



$ sudo docker ps -a
CONTAINER ID        IMAGE                  COMMAND                 CREATED             STATUS                      PORTS               NAMES
8c8a35e26663        busybox                "echo 'hello world!'"   36 seconds ago      Exited (0) 36 seconds ago                       k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
68f670c3c85f        k8s.gcr.io/pause:3.2   "/pause"                2 minutes ago       Up 2 minutes                                    k8s_POD_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_0
$ sudo docker logs k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
hello world!


kubelet阅读Pod清单并指示Docker根据我们的规范运行几个容器。(如果您对“暂停”容器感到好奇,这是Kubernetes的骇客-有关详细信息,请参见此博客。)Kubelet将busybox使用指定的命令启动我们的容器,并无限期地重新启动容器,直到移除静态容器为止。



恭喜你 我们只是想出了最复杂的方法之一来向终端输出文本!



运行etcd



我们的最终目标是运行Kubernetes API,但为此,我们首先需要运行etcd。通过将其设置放在pods目录(例如pods/etcd.yaml)中,启动一个最小的etcd集群



apiVersion: v1
kind: Pod
metadata:
  name: etcd
  namespace: kube-system
spec:
  containers:
  - name: etcd
    command:
    - etcd
    - --data-dir=/var/lib/etcd
    image: k8s.gcr.io/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


如果您曾经使用过Kubernetes,那么您应该熟悉这些YAML文件。这里只有两件事值得注意:



我们将主机文件夹安装/var/lib/etcd在Pod中,以便在重启后保存etcd数据(如果不这样做,则每次重启Pod都会删除集群状态,即使最小的Kubernetes安装也很糟糕)。



我们已经安装好了hostNetwork: true毫不奇怪,此选项将etcd配置为使用主机网络而不是Pod的内部网络(这将使API服务器更容易找到etcd集群)。



一个简单的检查表明etcd确实在localhost上运行并将数据保存到磁盘:



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


启动API服务器



启动Kubernetes API Server更加容易。需要传递的唯一参数--etcd-servers是您期望的:



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: k8s.gcr.io/kube-apiserver:v1.18.5
  hostNetwork: true


将此YAML文件放在目录中pods,API服务器将启动。通过帮助进行的验证curl表明,Kubernetes API正在完全开放的访问权限下监听8080端口-无需身份验证!



$ 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 get pod -n kube-system
No resources found in kube-system namespace.


我们创建的静态Pod消失了!实际上,我们的kubelet节点根本不显示:



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


怎么了?如果您还记得,在几段之前,我们使用一组非常简单的命令行参数启动了kubelet,因此kubelet不知道如何联系API服务器并通知其状态。在检查了文档之后,我们找到了相应的标志:



--kubeconfig string



文件的路径kubeconfig,它指示如何连接到API服务器。存在状态--kubeconfig启用API服务器模式,不存在状态--kubeconfig启用离线模式。



一直以来,我们都不知道,我们在“离线模式”下运行kubelet。(如果我们是书呆子,我们可以将kubelet独立模式视为“最小可行的Kubernetes”,但这将非常无聊)。为了使“实际”配置生效,我们需要将kubeconfig文件传递给kubelet,以便它知道如何与API服务器通信。幸运的是,这非常简单(因为我们在身份验证或证书方面没有问题):



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


将其另存为kubeconfig.yaml,终止进程kubelet并使用必需的参数重新启动:



$ sudo ./kubelet --pod-manifest-path=pods --kubeconfig=kubeconfig.yaml


(顺便说一下,如果您尝试在kubelet不工作时使用curl访问API,您会发现它仍然可以工作!Kubelet不是其pod的“父级”,就像Docker,它更像是一个“控制守护程序”。由kubelet管理的容器将一直运行,直到kubelet停止它们为止。)



几分钟后,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>        Ubuntu 18.04.4 LTS   4.15.0-109-generic   docker://19.3.6


这次让我们真正地祝贺自己(我知道我已经祝贺了)-我们使用功能齐全的API的最小Kubernetes“集群”!



跑下



现在,让我们看看API的功能。让我们从Nginx Pod开始:



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


在这里,我们得到一个非常有趣的错误:



$ ./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环境的残缺程度令人震惊—我们没有服务帐户。让我们通过手动创建服务帐户来再试一次,看看会发生什么:



$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
EOS
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


即使我们手动创建服务帐户,也不会生成身份验证令牌。当我们继续尝试极简的“集群”时,我们会发现通常自动发生的大多数有用的东西都将丢失。Kubernetes API服务器非常简单,大多数繁琐的自动调整发生在尚未运行的各种控制器和后台作业中。



我们可以通过automountServiceAccountToken为服务帐户设置一个选项解决此问题(因为无论如何我们都不必使用它):



$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
automountServiceAccountToken: false
EOS
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


终于,下出现了!但实际上,它不会启动,因为我们没有调度程序(调度程序)-Kubernetes的另一个重要组件。再一次,我们可以看到Kubernetes API令人惊讶地愚蠢-当您在API中创建pod时,它会注册它,但不会尝试找出要在哪个节点上运行它。



您实际上并不需要调度程序来运行pod。您可以将节点手动添加到清单中的参数中nodeName



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


(用mink8s主机名替换。)删除并应用后,我们看到nginx已启动并正在侦听内部IP地址:



$ ./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运行curl:



$ cat <<EOS | ./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
EOS
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>


深入研究这种环境,看看什么有效,什么无效,这非常有趣。我发现ConfigMap和Secret可以按预期工作,但Service and Deployment却无法正常工作。



成功!



这篇文章越来越大,所以我要宣布胜利,并宣布这是一个称为“ Kubernetes”的可行配置。总结:四个二进制文件,五个命令行参数和“仅” 45行YAML(按标准,不是很多) Kubernetes),我们还有很多工作要做:



  • 使用常规的Kubernetes API管理Pod(有一些技巧)
  • 您可以上传和管理公共容器图像
  • Pod保持活动并自动重启
  • 单个节点内的Pod之间的联网效果很好
  • ConfigMap,秘密和最简单的存储库安装按预期工作


但是,使Kubernetes真正有用的大多数东西仍然缺失,例如:



  • 吊舱规划师
  • 认证/授权
  • 多个节点
  • 服务网络
  • 群集内部DNS
  • 服务帐户,部署,云提供商集成以及Kubernetes带来的其他大多数优势的控制器


那么,我们实际上得到了什么?单独运行的Kubernetes API实际上只是一个容器自动化平台它并没有做什么用-它适用于使用API​​的各种控制器和操作员-但它提供了一致的自动化框架。



在免费的在线讲座中了解有关该课程的更多信息。






阅读更多:






All Articles