翻看了不少的kubernetes的安装教程,也反复作了一些实验,深感教程之复杂,因此决定写一个极简版本的安装教程,目标在于用尽量少的参数启动服务,而且剖析各组件关系,而后再在此基础上逐步添加参数,实现功能完备;html
说明:node
先大概知道一下架构,借用一张图,来源:https://www.kubernetes.org.cn/4047.htmllinux
etcd | 3.3.8 |
docker | 18.03.1-ce |
flannel | 0.10.0 |
kubernetes | 1.10.5 |
etcd是一个基础组件,没有太复杂的依赖关系,没什么好说的;nginx
docker见以前的docker安装流程;git
flannel见以前的flannel安装流程;github
到官方changelog里找downloads:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.10.mdweb
只下载server和node包就够了,client包只有一个kubectl工具,在前两个里都有带;docker
server有三个组件,apiserver是接口服务,对接etcd,作存储逻辑的封装,后面跟着controller-manager和scheduler,作一些后台控制逻辑;json
整个server作的事情全都围绕etcd转,因此根本不须要系统的root权限,普通用户足够;bootstrap
解压server包,程序都解压到了kubernetes/server/bin路径:
$ tar vfxz kubernetes-server-linux-amd64.tar.gz # 解压 $ ls kubernetes/server/bin #看一下
$ cd kubernetes/server # 就在这个路径下作server服务的管理
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
参数说明:
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,来看一下后面这些东西:
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-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;
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参数;
bin/kube-scheduler \ --master=127.0.0.1:18080
查看etcd,多出现一些节点:
/registry/events/kube-system/kube-scheduler.153c41b5b3052d28
/registry/services/endpoints/kube-system/kube-scheduler
再看一下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的两个服务组件也用非root用户的,但试下来发现不行,kubelet须要与docker交互,而kube-proxy则要改iptables,都须要提供root权限;
解压node包,程序都解压到了kubernetes/node/bin路径:
# tar vfxz kubernetes-node-linux-amd64.tar.gz #解压 # ls kubernetes/node/bin/ # 看一下 # cd kubernetes/node # 就在这个路径下进行node服务的管理
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
参数说明:
在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>
$ bin/kubectl -s 127.0.0.1:18080 get node NAME STATUS ROLES AGE VERSION <node> Ready <none> 38m v1.10.5
bin/kube-proxy \ --master=<apiserver>:18080 \ --proxy-mode=iptables \ --logtostderr=true
参数说明:
至此,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 ps CONTAINER 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地址是同一个;
总结一下启动应用时这些资源的关系:
若是启动的应用自己是个服务的话,还须要将服务地址暴露出来,在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.97 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 30152/TCP Endpoints: 172.10.63.2:80 Session Affinity: None External Traffic Policy: Cluster Events: <none>
从后面的服务描述中获得如下信息:
在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作的事情了;
重点参考如下三篇: