手记

CentOS7上手动部署入门级kubernetes

前言

翻看了很多的kubernetes的安装教程,也反复做了一些实验,深感教程之复杂,所以决定写一个极简版本的安装教程,目标在于用尽可能少的参数启动服务,并且剖析各组件关系,然后再在此基础上逐步添加参数,实现功能完备;

干什么

  • 在server节点上启动三个主要服务:apiserver、controller-manager、scheduler;

  • 在node节点上启动两个主要服务:kubelet、kube-proxy;

  • 试验启动容器,且测试容器内服务的功能;

不干什么

  • 不通过软件源安装;

  • 不添加systemd服务;

  • 尽量不做安全配置;不做bootstrap流程,不用kubeadm创建集群;

  • 不做高可用配置;

  • 不用ipvs做负载均衡;

  • 不装任何插件(dns, dashboard, etc);

说明:

  1. 如果通过软件源安装,看似会简化部署流程,但事实上会引入一些别的问题,因为一方面解决版本配套要添加合适的源比较麻烦,另一方面发布包在操作系统上做的一些配置也很复杂,为迎合它也需要做一些复杂的工作;(其实就是源里的东西版本低了不想用这种大实话我是绝对不会说的)

  2. 个人感觉安全配置是提高了kubernetes入门门槛的关键原因,本身安全认证授权就是非常复杂的机制,与业务独立,一般人较少接触,任何一个新入门kubernetes的人在瞎子摸象之前要先搞明白这些东西就要花很多功夫;
    但是,非安全的服务端口正在被deperecate,看一下今年4月社区contributer的表态,自己感受一下吧,https://github.com/kubernetes/kubernetes/pull/59018#issuecomment-381583629
    未来这一部分可能是造成本文失效的最主要原因; 

准备

架构概述

先大概知道一下架构,借用一张图,来源:https://www.kubernetes.org.cn/4047.html

版本及依赖

etcd3.3.8
docker18.03.1-ce
flannel0.10.0
kubernetes1.10.5

etcd是一个基础组件,没有太复杂的依赖关系,没什么好说的;

docker见之前的docker安装流程

flannel见之前的flannel安装流程

下载程序包

到官方changelog里找downloads:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.10.md

只下载server和node包就够了,client包只有一个kubectl工具,在前两个里都有带;

Server部署

server有三个组件,apiserver是接口服务,对接etcd,做存储逻辑的封装,后面跟着controller-manager和scheduler,做一些后台控制逻辑;

整个server做的事情全都围绕etcd转,所以根本不需要系统的root权限,普通用户足够;

安装server

解压server包,程序都解压到了kubernetes/server/bin路径:

$ tar vfxz kubernetes-server-linux-amd64.tar.gz # 解压
$ ls kubernetes/server/bin #看一下
$ cd kubernetes/server # 就在这个路径下做server服务的管理

启动apiserver

bin/kube-apiserver \
        --cert-dir=etc/kubernetes/cert \        --insecure-bind-address=0.0.0.0 \        --insecure-port=18080 \ 
        --service-cluster-ip-range=10.0.0.0/16 \        --etcd-servers=http://<etcd>:2379 \
        --logtostderr=true

参数说明:

  1. --cert-dir:虽然我不想做安全配置,但奈何kubernetes的https server是必选项,一定要有TLS证书和密钥才行,如果没有提供自定义的证书密钥,就会自动创建一个;如果不设这个参数,证书和密钥会创建到/var/run/kubernetes下;

  2. --insecure-bind-address,--insecure-port:非安全服务地址和端口,以后会废弃;

  3. --service-cluster-ip-range:service cluster ip是提供给service的虚拟ip,因而这里应当使用一个虚拟段,以免与物理ip段混用造成路由混乱;

  4. --etcd-servers:kubernetes强依赖etcd服务; 

生成的文件及数据

apiserver启动后,在cert-dir下出现了自动创建的证书和密钥:

$ file etc/kubernetes/cert/*etc/kubernetes/cert/apiserver.crt: PEM certificate
etc/kubernetes/cert/apiserver.key: PEM RSA private key

在etcd上出现了一些数据:

# ETCDCTL_API=3 etcdctl get / --prefix --keys-only/registry/apiregistration.k8s.io/apiservices/v1./registry/apiregistration.k8s.io/apiservices/v1.apps/registry/apiregistration.k8s.io/apiservices/v1.authentication.k8s.io/registry/apiregistration.k8s.io/apiservices/v1.authorization.k8s.io/registry/apiregistration.k8s.io/apiservices/v1.autoscaling/registry/apiregistration.k8s.io/apiservices/v1.batch/registry/apiregistration.k8s.io/apiservices/v1.networking.k8s.io/registry/apiregistration.k8s.io/apiservices/v1.rbac.authorization.k8s.io/registry/apiregistration.k8s.io/apiservices/v1.storage.k8s.io/registry/apiregistration.k8s.io/apiservices/v1beta1.admissionregistration.k8s.io/registry/apiregistration.k8s.io/apiservices/v1beta1.apiextensions.k8s.io/registry/apiregistration.k8s.io/apiservices/v1beta1.apps/registry/apiregistration.k8s.io/apiservices/v1beta1.authentication.k8s.io/registry/apiregistration.k8s.io/apiservices/v1beta1.authorization.k8s.io/registry/apiregistration.k8s.io/apiservices/v1beta1.batch/registry/apiregistration.k8s.io/apiservices/v1beta1.certificates.k8s.io/registry/apiregistration.k8s.io/apiservices/v1beta1.events.k8s.io/registry/apiregistration.k8s.io/apiservices/v1beta1.extensions/registry/apiregistration.k8s.io/apiservices/v1beta1.policy/registry/apiregistration.k8s.io/apiservices/v1beta1.rbac.authorization.k8s.io/registry/apiregistration.k8s.io/apiservices/v1beta1.storage.k8s.io/registry/apiregistration.k8s.io/apiservices/v1beta2.apps/registry/apiregistration.k8s.io/apiservices/v2beta1.autoscaling/registry/namespaces/default/registry/namespaces/kube-public/registry/namespaces/kube-system/registry/ranges/serviceips/registry/ranges/servicenodeports/registry/services/endpoints/default/kubernetes/registry/services/specs/default/kubernetes

忽略掉前面的/register/apixxx,来看一下后面这些东西:

  • 三个namespace:default、kube-public、kube-system;

  • /register/ranges/serviceips:内容包含10.0.0.0/16,与--service-cluster-ip-range一致;

  • /registry/ranges/servicenodeports:内容包含30000-32767,查了一下代码,这个值是--service-node-port-range参数的默认值,见https://github.com/kubernetes/kubernetes/blob/release-1.10/pkg/kubeapiserver/options/options.go

  • /registry/services/<endpoints,specs>/default/kubernetes:描述了同一个service(kubernetes)的endpoints信息及specs信息,endpoints内容可见server的外网ip,specs内容可见10.0.0.1,这是service-cluster-ip-range的第一个ip地址;

使用kubectl查看信息

apiserver提供了restapi以获取和管理在etcd上的集群状态信息;kubectl就是这个restapi的客户端;

因为我们的服务端口不在默认的8080上,所以使用时要加一个-s参数:

查看namespace:

$ bin/kubectl -s 127.0.0.1:18080 get ns # 查看三个namespace,与etcd的/registry/namespaces对应
NAME          STATUS    AGE
default       Active    1h
kube-public   Active    1h
kube-system   Active    1h

查看service:

$ bin/kubectl -s 127.0.0.1:18080 get svc # 查看service,与/registry/services/specs/default/kubernetes对应
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.0.0.1     <none>        443/TCP   1h

查看endpionts:

$ bin/kubectl -s 127.0.0.1:18080 get ep #查看endpoints,与/registry/services/endpoints/default/kubernetes对应
NAME         ENDPOINTS            AGE
kubernetes   xxxxx:6443   1h

启动controller-manager

注:这个命令虽然可以启动controller-manager,但后续会出现一些问题,需要再添加参数,见“测试”部分对controller-manaer的调整

bin/kube-controller-manager \        --master=127.0.0.1:18080 \        --logtostderr=true

生成的数据

查看etcd,多出现一些节点:

/registry/events/kube-system/kube-controller-manager.153c40f68e70e209/registry/serviceaccounts/default/default/registry/serviceaccounts/kube-public/default/registry/serviceaccounts/kube-system/default/registry/services/endpoints/kube-system/kube-controller-manager

多出一个event,三个serviceaccounts(三个namespace下的default),以及一个endpoints;

使用kubectl查看信息

event就不看了,来看一下service account:

$ bin/kubectl -s 127.0.0.1:18080 get sa
NAME      SECRETS   AGE
default   0         3m

这里的SECRETS为0,表明没有为这个service account生成secret,这个也是与安全相关的东西,先忽略,后面会遇到问题,然后我们再回来处理它;

再看一下endpoint:

$ bin/kubectl -s 127.0.0.1:18080 get ep --namespace kube-system
NAME                      ENDPOINTS   AGE
kube-controller-manager   <none>      7m

因为kube-controller-manager在kube-system下,所以需要多加一个namespace参数;

启动scheduler

bin/kube-scheduler \        --master=127.0.0.1:18080

生成的数据

查看etcd,多出现一些节点:

/registry/events/kube-system/kube-scheduler.153c41b5b3052d28/registry/services/endpoints/kube-system/kube-scheduler

使用kubectl查看信息

再看一下endpoints

$ bin/kubectl -s 127.0.0.1:18080 get ep --namespkube-system
NAME                      ENDPOINTS   AGE
kube-controller-manager   <none>      15m
kube-scheduler            <none>      1m

查看集群状态

$ bin/kubectl -s 127.0.0.1:18080 get cs
NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok                  
scheduler            Healthy   ok                  
etcd-0               Healthy   {"health":"true"}

至此,server就算部署好了

node部署

本想node的两个服务组件也用非root用户的,但试下来发现不行,kubelet需要与docker交互,而kube-proxy则要改iptables,都需要提供root权限;

安装node

解压node包,程序都解压到了kubernetes/node/bin路径:

# tar vfxz kubernetes-node-linux-amd64.tar.gz #解压
# ls kubernetes/node/bin/ # 看一下
# cd kubernetes/node # 就在这个路径下进行node服务的管理

启动kubelet

kubelet的参数太多了,可能为了简化kubelet的启动脚本吧,引入了两个配置文件,两个,两个......而且除这两个文件外还要设置其它参数,设置其它参数,其它参数......faint

先生成访问apiserver需要用的kubeconfig文件:

# KUBE_APISERVER="http://<apiserver>:18080"#
# bin/kubectl config set-cluster kubernetes \  --server=$KUBE_APISERVER \  --kubeconfig=etc/kubernetes/kubelet.kubeconfig
#
# bin/kubectl config set-context default \  --cluster=kubernetes \  --user=default-noauth \  --kubeconfig=etc/kubernetes/kubelet.kubeconfig
#
# bin/kubectl config use-context default --kubeconfig=etc/kubernetes/kubelet.kubeconfig
#
# bin/kubectl config view --kubeconfig=etc/kubernetes/kubelet.kubeconfig

apiVersion: v1
clusters:- cluster:
    server: http://<apiserver>:18080  name: kubernetes
contexts:- context:
    cluster: kubernetes
    user: ""
  name: default
current-context: default
kind: Config
preferences: {}
users: []

这个配置文件就声明了一个以http://<apiserver>:18080为入口的名为“kubernetes”的集群,以及一个匿名访问“kubernetes”集群的名为“default”的上下文,并声明使用这个"default"上下文;

写kubelet自身需要的配置文件(这个文件可以是yaml或json格式,因为很多教程用了json格式,所以这里我用一下yaml格式):

# cat etc/kubernetes/kubelet.config.yaml 
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
cgroupDriver: cgroupfs

kind和apiVersion都是定死的,可见https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/,以及代码的这里:https://github.com/kubernetes/kubernetes/blob/release-1.10/pkg/kubelet/apis/kubeletconfig/v1beta1/register.go

cgroupDriver需要与docker的真实情况相符,通过以下命令查看:

# docker info|grep 'Cgroup Driver'Cgroup Driver: cgroupfs

启动kubelet:

bin/kubelet \  --cert-dir=etc/kubernetes/cert \  --kubeconfig=etc/kubernetes/kubelet.kubeconfig \  --config=etc/kubernetes/kubelet.config.yaml \  --pod-infra-container-image=<registry>/rhel7/pod-infrastructure:latest \  --runtime-cgroups=/systemd/system.slice --kubelet-cgroups=/systemd/system.slice \  --logtostderr=true

参数说明:

  1. --cert-dir:与apiserver的参数道理相同;kubernete强制生成;

  2. --kubeconfig:访问apiserver需要的config;

  3. --config:kubelet自己需要的config;

  4. --pod-infra-container-image:一个基础容器镜像,做为pod的第一个容器启动,为每个pod提供network namespace;没啥业务逻辑,理论上说就是启动之后保持不退出即可,以后我可以自己写一个试试。默认镜像地址可能被墙,所以想办法下载一个下来(docker官方registry上有),然后推到私有的registry比较好;

  5. --runtime-cgroups,--kubelet-cgroups:可能是因为我手动启动kubelet,而不是做为systemd的service启动的缘故吧,启动之后会时而出现这个错误:Failed to get system container stats for "/user.slice/user-0.slice/,加上这两个参数就好了;

生成的文件及数据

在cert-dir下出现了自动创建的证书和密钥:

# file etc/kubernetes/cert/*etc/kubernetes/cert/kubelet.crt: PEM certificate
etc/kubernetes/cert/kubelet.key: PEM RSA private key

多说一句,这个证书是node自己签发的,肯定得不到apiserver的认可;当然因为我在这里不做安全集群,这就无所谓;但在安全集群里,kubelet的证书会由controller-manager签发;

在etcd上出现了一些新的数据,除去events外,就一个最重要的minions数据:

/registry/minions/<node>

使用kubectl查看信息

$ bin/kubectl -s 127.0.0.1:18080 get node
NAME         STATUS    ROLES     AGE       VERSION<node>   Ready     <none>    38m       v1.10.5

启动kube-proxy

bin/kube-proxy \        --master=<apiserver>:18080 \        --proxy-mode=iptables \        --logtostderr=true

参数说明:

  1. proxy-mode:当前默认使用的就是iptables模式,ipvs还是experiment功能;如果iptables不适用,则回退到userspace模式下;

至此,node部署完成;

测试

启动应用

到apiserver上,执行以下命令,运行一个标准的nginx容器,为了省时间,我也把它拉下来,push到私有registry上了:

$ bin/kubectl -s 127.0.0.1:18080 run nginx --image=<registry>/nginx/nginx --port=80

pod启动失败,报错:No API token found for service account "default"

之前遗留了一个问题,见service_account_without_secrets

于是调整一下controller-manager的参数,加上--service-account-private-key-file和--root-ca-file参数,重启controller-manager:

$ bin/kube-controller-manager \        --master=127.0.0.1:18080 \        --service-account-private-key-file=etc/kubernetes/cert/apiserver.key \        --root-ca-file=etc/kubernetes/cert/apiserver.crt \        --logtostderr=true

再看一下service account的情况,secrets已经不是0了:

$ bin/kubectl -s 127.0.0.1:18080 get sa
NAME      SECRETS   AGE
default   1         2h

再重新试一下启动nginx,并查看状态;

$ bin/kubectl -s 127.0.0.1:18080 run nginx --image=<registry>/nginx/nginx --port=80  # 启动
$
$ bin/kubectl -s 127.0.0.1:18080 get deploy    # 查看deploy
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx     1         1         1            1           1m
$
$ bin/kubectl -s 127.0.0.1:18080 describe deploy nginx|grep NewReplicaSet  # 查看deploy详情
  Progressing    True    NewReplicaSetAvailable
NewReplicaSet:   nginx-55cc995fdb (1/1 replicas created)
$
$ bin/kubectl -s 127.0.0.1:18080 describe replicasets nginx-55cc995fdb |tail -1  # 查看replicaset详情
  Normal  SuccessfulCreate  10m   replicaset-controller  Created pod: nginx-55cc995fdb-27t7z
$
$ bin/kubectl -s 127.0.0.1:18080 get pod nginx-55cc995fdb-27t7z -o wide   # 查看pod
NAME                     READY     STATUS    RESTARTS   AGE       IP            NODE
nginx-55cc995fdb-27t7z   1/1       Running   0          11m       172.10.63.2   <node>

再到node上看一下容器状态:

# docker psCONTAINER ID        IMAGE                                           COMMAND                  CREATED             STATUS              PORTS               NAMES
3e5e69a69950        <registry>/nginx/nginx                       "nginx -g 'daemon of…"   28 seconds ago      Up 27 seconds                           k8s_nginx_nginx-55cc995fdb-27t7z_default_1ca63ddd-7ab5-11e8-be61-3440b59f0098_0
ec311fee295d        <registry>/rhel7/pod-infrastructure:latest   "/pod"                   28 seconds ago      Up 27 seconds                           k8s_POD_nginx-55cc995fdb-27t7z_default_1ca63ddd-7ab5-11e8-be61-3440b59f0098_0

看以看到,同时启动了两个容器,一个是pod-infrastructure,另一个是nginx;如果进入这两个容器内看一下的话,会发现它们的ip地址是同一个;

总结一下启动应用时这些资源的关系:

  • 一个应用会对应一个deploy资源; 

  • 一个deploy会对应一个replicaset资源;

  • 一个replicaset会对应多个(默认为一个)pod资源;

  • 一个pod会在某个node上启动至少两个container,其中一个是pod-infrastructure运行容器,另一个是应用所在的运行容器;它们共享ip地址;

暴露服务

如果启动的应用本身是个服务的话,还需要将服务地址暴露出来,在server(master)上运行:

$ bin/kubectl -s 127.0.0.1:18080  expose deployment nginx --type=NodePort --name=example-service
service "example-service" exposed$
$ bin/kubectl -s 127.0.0.1:18080 describe services example-service
Name:                     example-service
Namespace:                default
Labels:                   run=nginx
Annotations:              <none>Selector:                 run=nginx
Type:                     NodePort
IP:                       10.0.158.97Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30152/TCP
Endpoints:                172.10.63.2:80Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

从后面的服务描述中得到以下信息:

  • service的cluster_ip是10.0.158.97,这个地址在apiserver启动时的--service-cluster-ip-range参数范围内,也即etcd上/register/ranges/serviceips节点内配置的信息;

  • service的nodeport是30152,这个端口在apiserver启动时的--service-node-port-range参数范围内,也即etcd上/registry/ranges/servicenodeports节点内配置的信息;

  • service的endpoints是172.10.63.2:80,这是ip地址是容器内的地址;

测试服务

在server(master)上访问node的30152端口,nginx服务正常:

$ curl <node>:30152<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to<a href="http://nginx.org/">nginx.org</a>.<br/>Commercial support is available at<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

在node上访问10.0.158.97:80,nginx也服务正常;查看一下node的iptables,会发现有一条规则将target为10.0.158.97:80的流量转发到了本机的30152端口;这是kueb-proxy做的事情;

在另一个节点(可以不是kubernetes node,只要求启用了与node相同的flanneld)或容器(可以不是kubernetes pod,只要求容器所在节点启用了与node相同的flanneld)内访问172.10.63.2:80,nginx也服务正常;这就是flanneld做的事情了;

 

重点参考以下三篇:

  • https://www.jianshu.com/p/8358117a23bb

  • https://github.com/opsnull/follow-me-install-kubernetes-cluster

  • https://jimmysong.io/kubernetes-handbook/practice/install-kubernetes-on-centos.html

原文出处

1人推荐
随时随地看视频
慕课网APP