K8S搭建笔记
一、搭建过程
1.1 总体说明
本文主要记录kubernetesV1.6版本的搭建、并在其中部署一个Nginx和监控台的过程。仅简单介绍kubernetes的部分概念,不作展开,详细内容自行下来了解。主要参考网络上“宋静超”搭建K8S,对其文章中可能混淆的地方加以注解。完整过程请参考他的博客。
在CentOS上部署kubernetes集群
https://jimmysong.io/kubernetes-handbook/practice/install-kubernetes-on-centos.html
1.2虚拟机准备
总共需要3台虚拟机,如下表所示
主机 | IP | 角色 | 需要安装的软件 |
---|---|---|---|
master | 192.168.0.241 | K8S master节点 | docker Flannel etcd apiserver controller-manager scheduler |
node1 | 192.168.0.242 | K8S node节点 | docker Flannel etcd kubelet kube-proxy |
node2 | 192.168.0.243 | K8S node节点 | docker Flannel etcd kubelet kube-proxy |
节点角色说明
- master
Master节点负责对外提供一系列管理集群的API接口,并且通过和 node 节点交互来实现对集群的操作管理 - node
node节点为运行 Docker容器的节点,负责和节点上运行的 Docker 进行交互,并且提供了代理功能
公共组件说明
- docker
运行在所有节点,容器基础服务 - flannel
运行在所有节点,一个专为kubernetes定制的三层网络解决方案,主要用于解决容器的跨主机通信问题 - etcd
集群方式运行在所有节点,key-value键值存储数据库,用来存储kubernetes的信息的 - apiserver
运行在master节点,用户和 kubernetes 交互的入口,封装了核心对象的增删改查操作,提供了 RESTFul 风格的 API 接口,通过etcd来实现持久化并维护对象的一致性 - controller-manager
运行在master节点,主要是用于保证 replication Controller 定义的复制数量和实际运行的 pod 数量一致,另外还保证了从 service 到 pod 的映射关系总是最新的 - scheduler
运行在master节点,负责集群资源的调度和管理,例如当有 pod 异常退出需要重新分配机器时,scheduler 通过一定的调度算法从而找到最合适的节点。 - kubelet
运行在 node节点,负责和节点上的Docker交互,例如启停容器,监控运行状态等。 - proxy
运行在 node节点,负责为 pod 提供代理功能,会定期从 etcd 获取 service 信息,并根据 service 信息通过修改 iptables 来实现流量转发(最初的版本是直接通过程序提供转发功能,效率较低。),将流量转发到要访问的 pod 所在的节点上去。 - kubectl kube命令行工具
这是一个运行kube的脚本程序,可以理解为空K8S的cmd脚本工具,是用户和k8s在命令级别微观沟通桥梁
1.3 常规组件安装
1.3.1组件安装顺序
1.3.2 Docker安装
每台主机都要安装Docker,不建议直接在Centos中使用yum install docker。否则安装的版本比较老
安装docker,下载链接如下所示。目前使用18.03版本。==建议下载rpm包,使用以下方法安装==
https://docs.docker.com/install/linux/docker-ce/centos/
yum install 绝对路径/docker-ce-1803.rpm
1.3.3-A 证书制作
kubernetes 系统的各组件需要使用 TLS 证书对通信进行加密,Jimmy的博客中使用 CloudFlare 的 PKI 工具集 cfssl 来生成 Certificate Authority (CA) 和其它证书;关于Kubernetes和证书的关系可以参考书目或者以下链接
《如何理解Kubernetes认证和授权》
这边文章是翻译的官方文档,解释的比较通透,不过只翻译了部分,关于认证和授权的详细信息没有翻译到,链接如下:
《Kubernetes 认证》
这是kubernetes中文社区的文档,介绍的比较全,但是刚看的时候可能有点懵,实际上对于认证和授权,如果没有认识,建议还是跟着jimmy的教程配置一次,再反过来看,可能更容易接受,链接如下:
安装方法:
- 建议直接跟随《5.2.1. 创建TLS证书和秘钥》所述方法配置。
- CFSSL建议就不用文中的go语言方式安装了,可直接用文中二进制方法安装。
- 创建 kubernetes 证书 一节中127.0.0.1和10.254.0.1保留,其余换成自己的主机地址
1.3.3-B kubectl工具
kubectl是k8s集群的命令行工具,其概念可以参考Windows的cmd、Linux上的shell。master节点因为要执行众多命令,肯定是需要安装的。node节点按需安装,即如果有需要在node节点上也要执行创建pod、service等操作,就需要在pod上安装此工具。
安装方法:
- 建议直接跟随《5.2.4. 安装kubectl命令行工具》所述方法配置
- KUBE_APISERVER配置说明
KUBE_APISERVER="https://172.20.0.113:6443"
KUBE_APISERVER即master所在主机的地址,无论是在node配置kubectl还是master都是安装KUBE_APISERVER的地址(这里暂时未考虑master节点的高可用)。
1.3.4 Etcd集群安装
K8S依赖Etcd作为服务发现等功能,所以需要搭建Etcd组件。Etcd可以搭建单机,也可以搭建集群。目前按照集群方式搭建。在每台主机上安装。
安装方法
- Jimmy博客《5.2.3创建高可用 etcd 集群》
- 直接按照博客方法安装即可
1.3.5 master节点部署
这部分主要是在master节点上部署APIserver、scheduler和controller
部署安装方法
- 可以直接按照《5.2.5. 部署master节点》 部分内容直接部署
- 针对博客的补充说明
- kubernetes-server-linux-amd64.tar.gz 这个文件较大,wget如果失败,建议直接下载手工上传到主机
- 每个组件安装完成后,建议验证一下功能再向前。
1.3.6 node节点部署
这部分主要是在node节点上部署kube和kube-proxy
部署安装方法
- 可以直接按照《5.2.7. 部署node节点》 部分内容直接部署
- 针对博客的补充说明
- Jimmy的博客在本节开篇讲解了flannel接手docker网络的配置,这部分的参照作者的配置,并未达到目的————也即在后续使用代理访问dashboard时候,提示没有到node-docker地址的路由,这部分解决方法,放到了二、问题集2.3代理方式无法访问dashboard说明,请移步该节查看解决方案。
- /etc/kubernetes/kubelet 关于配置文件的问题,请直接看注释(针对192.168.0.242)
##我的注释:KUBELET_ADDRESS这个配置,参看网络很多配置这里都写的是0.0.0.0 或者"",所以我这里也留了空没有写。不妨碍成功启动。
## The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)
##原文配置KUBELET_ADDRESS="--address=172.20.0.113"
KUBELET_ADDRESS=""
#
## The port for the info server to serve on
#KUBELET_PORT="--port=10250"
#
##我的注释:这里如果要覆盖,请直接写正在配置的node的地址,原文给人造成歧义,写的是master的地址。
## You may leave this blank to use the actual hostname
##原文配置KUBELET_HOSTNAME="--hostname-override=172.20.0.113"
KUBELET_HOSTNAME="--hostname-override=192.168.0.242"
#
## location of the api-server
## COMMENT THIS ON KUBERNETES 1.8+
KUBELET_API_SERVER="--api-servers=http://192.168.0.241:8080"
#
##我的注释:如果没有私有仓库,请直接保留为"""即可
## pod infrastructure container
##原文配置KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=harbor-001.jimmysong.io/library/pod-infrastructure:rhel7"
#
## Add your own!
KUBELET_ARGS="--cgroup-driver=systemd --cluster-dns=10.254.0.2 --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig --kubeconfig=/etc/kubernetes/kubelet.kubeconfig --require-kubeconfig --cert-dir=/etc/kubernetes/ssl --cluster-domain=cluster.local --hairpin-mode promiscuous-bridge --serialize-image-pulls=false"
- 代理配置文件问题(针对192.168.0.242)
###
# kubernetes proxy config
# default config should be adequate
# Add your own!
##我的配置,bind-address也是正在配置的node的地址,hostname-override和上面kubelet的配置需要一直
##原文配置KUBE_PROXY_ARGS="--bind-address=172.20.0.113 --hostname-override=172.20.0.113 --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig --cluster-cidr=10.254.0.0/16"
KUBE_PROXY_ARGS="--bind-address=192.168.0.242 --hostname-override=192.168.0.242 --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig --cluster-cidr=10.254.0.0/16"
1.3.7 Flannel网络插件安装
默认情况,docker安装后,使用的是bridge桥接在宿主机上,意味着在不同主机上的docker通信设置复杂。所以诞生了第三方插件,帮助docker内的容器通信。flannel就是这样的组件。他是一个专为kubernetes定制的三层网络解决方案,主要用于解决容器的跨主机通信问题。
安装方法
- 参照《5.2.6. 安装flannel网络插件》
- 对博客配置方法的说明
- flannel的配置文件:/etc/sysconfig/flanneld的==FLANNEL_ETCD_PREFIX==配置要和后面的==etcdctl命令中的键值==对应;
- 代码段【2】中的命令,其实很简单 即 etcdctl mkdir /kube-centos/network;相当于使用etcdctl命令行创建了一个目录(键值对);
- 上面这段命令,如果配置错误,是不能覆盖的,需要使用 etcdctl rmdir [目录]先删除,然后再重建即可。
- 而代码中 --ca-file等代码块主要是认证用的
代码段【1】
# etcd config key. This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_PREFIX="/kube-centos/network"
代码段【2】
etcdctl --endpoints=https://172.20.0.113:2379,https://172.20.0.114:2379,https://172.20.0.115:2379 \
--ca-file=/etc/kubernetes/ssl/ca.pem \
--cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
mkdir /kube-centos/network
etcdctl --endpoints=https://172.20.0.113:2379,https://172.20.0.114:2379,https://172.20.0.115:2379 \
--ca-file=/etc/kubernetes/ssl/ca.pem \
--cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
mk /kube-centos/network/config '{"Network":"172.30.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan"}}'
二、部署项目
2.1项目部署概论
在纯docker中,我们通过docker run直接启动一个应用。
那么,K8S中,我们通过YAML文件来告诉集群应该怎么创建应用,这些内容描述了启动应用的images(以及下载地址)、需要创建的应用的数、应用类型、应用意外终止后的处理方式等。典型的一个hello-world.yaml文件如下所示:
apiVersion: v1
kind: Pod
metadata:
name: hello-world
spec: # specification of the pod’s contents
restartPolicy: Never
containers:
- name: hello
image: "ubuntu:14.04"
command: ["/bin/echo","hello”,”world"]
以上内容是启动一个Ubuntu:14.04,容器名hello-world,然后启动后执行 /bin/echo hello world .
然后使用 以下命令创建 Pods:kubectl create -f ./hello-world.yaml
然后查看Pod状态
## 我的注释:初始化的时候,新创建的pod还未被调度,也就是还没有节点被选中去运行它。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-world 0/1 Pending 0 0s
## 我的注释:这个是已经成功运行的状态
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-world 1/1 Running 0 s
2.2部署nginx
2.1.1创建nginx-rc.yaml和nginx-svc.yaml
#nginx-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx-test
labels:
name: nginx-test
spec:
replicas: 2
selector:
name: nginx-test
template:
metadata:
labels:
name: nginx-test
spec:
containers:
- name: nginx-test
image: docker.io/nginx
ports:
- containerPort: 80
#nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-test
labels:
name: nginx-test
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
targetPort: 80
name: http
nodePort: 30088
selector:
name: nginx-test
以上命令是创建一组nginx服务器,创建2个副本,将80端口映射到nodePort:30088
然后运行下面两条命令创建pod
kubectl create -f ./nginx-rc.yaml
kubectl create -f ./nginx-svc.yaml
2.1.2创建过程中可能出现的问题
A、Kubernetes在创建Pod的时候,需要从gcr.io下载一个helper镜像,这个镜像在Google下面,国内无法拉取可参考3.1解决。gcr.io镜像的实际拉取实际上在node上,也就是需要在node机器上的docker要有这个镜像,所以不能只在master操作。
B、 过了gcr.io这个坎后,有可能要拉取的nginx镜像也拉不出而一直处于creating状态。这部分排查需要不断查看系统日志和 tail -f /var/log/messages 以及kubectl describe pod nginx-controller-tr5zr 查看pod状态。如果日志显示是拉不到镜像需要检查yaml里面的images是否是可拉取的,如果可以通过docker run [images]可拉取,那么yaml应该也是可以的。如果拉取时间过慢,可以通过添加加速器等方式加速;
2.2部署dashboard
2.2.1部署方法
可以参照博客直接部署K8S的dashboard,需要注意的是要自己去找可用的1.6版本的dashboard镜像,可通过hub.docker.com搜索,然后使用加速器拉下来。
2.2.2部署问题
如遇到dashboard不可访问,路由问题,可参照下文3.3解决。
三、问题集
3.1 kubectl pod 一致处于ContainerCreating状态
3.1.1故障现象
[root@localhost nginx]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-controller-tr5zr 1/1 ContainerCreating 0 47s
nginx-controller-z3cbw 1/1 ContainerCreating 0 47s
3.1.2排查:查看具体pod状态
kubectl describe pod nginx-controller-tr5zr
unable to pull sandbox image \"gcr.io/google_containers/pause-amd64:3.0\": Error response from daemon: {\"message\":\"Get https://gcr.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)\"}
3.1.3确认问题
无法拉取 gcr.io/google_containers/pause-amd64:3.0 镜像
3.1.4解决方案:手动拉取、然后本地打tag.在每个POD(node)都执行此操作
[root@localhost ~]# docker pull cloudnil/pause-amd64:3.0
[root@localhost ~]# docker tag cloudnil/pause-amd64:3.0 gcr.io/google_containers/pause-amd64:3.0
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
gcr.io/google_containers/pause-amd64 3.0 66c684b679d2 12 months ago 747kB
cloudnil/pause-amd64 3.0 66c684b679d2 12 months ago 747kB
3.2 可以查看到csr,但是查看node为no resource
3.2.1现象
fannel、etcd 工作正常,但是就是看不到nodes(即get nodes为空)
[root@localhost nginx]# kubectl get csr
NAME AGE REQUESTOR CONDITION
csr-j4gbr 15h kubelet-bootstrap Approved,Issued
csr-mm575 15h kubelet-bootstrap Approved,Issued
[root@localhost nginx]# kubectl get nodes
NAME STATUS AGE VERSION
192.168.0.242 Ready 14h v1.6.0
192.168.0.243 Ready 14h v1.6.0
3.2.2排查 :需要查看系统日志
[root@localhost nginx]# tail -300f /var/log/messages
3.2.3确认问题:
没有关联角色,kubelet 启动时向 kube-apiserver 发送 TLS bootstrapping 请求,需要先将 bootstrap token 文件中的 kubelet-bootstrap 用户赋予 system:node-bootstrapper 角色,然后 kubelet 才有权限创建认证请求(certificatesigningrequests):
3.2.4解决方案:在master执行以下语句 关联角色
kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --user=kubelet-bootstrap
3.3 代理方式无法访问dashboard
3.3.1 现象
dashboard部署成功后,开启kube-proxy代理访问控制台,显示路由不通
[root@localhost ~]# kubectl proxy --address='192.168.0.241' --port=8086 --accept-hosts='^*$'
Starting to serve on 192.168.0.241:8086
访问http://masterip:8086/ui后显示如下:
dial tcp 172.17.0.3:9090: getsockopt: no route to host'
然后可跟踪到pod去,发现某pod如果docker是172.17.0.3可以通过此主机访问
3.3.2 排查-确定网络问题
经查考虑是docker网络问题
根据以下两篇文章及fannel网络原理
https://www.cnblogs.com/kevingrace/p/6859114.html
https://blog.csdn.net/yelllowcong/article/details/78303626
可知,网络转发通过fannel进行,正确安装fannel并且配置Docker后。docker0和fannel.1会是同一个网段,数据包到达fannel所在的网络的之后,也即到了docker。从而完成了数据的转发。
而最开始,我的网络路由不对
[root@localhost ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.0.1 0.0.0.0 UG 0 0 0 ens33
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 ens33
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
172.30.0.0 0.0.0.0 255.255.0.0 U 0 0 0 flannel.1
192.168.0.0 0.0.0.0 255.255.248.0 U 0 0 0 ens33
192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0
注意docker0的网段和flannel的网段不在同一个上面,所以是docker和flanned没有对接起来。
3.3.3解决方案:配置docker
情况一:根据上面第二个文章,配置docker的网络跟随flannel配置,如下文最后一行所示;
EnvironmentFile=-/run/flannel/docker
ExecStart=/usr/bin/dockerd \
$DOCKER_NETWORK_OPTIONS
情况二:博客中说的配置docker网络的Environment要放在情况一那部分代码的前面,否则不生效
EnvironmentFile=-/run/flannel/docker
EnvironmentFile=-/run/docker_opts.env
EnvironmentFile=-/run/flannel/subnet.env
EnvironmentFile=-/etc/sysconfig/docker
EnvironmentFile=-/etc/sysconfig/docker-storage
EnvironmentFile=-/etc/sysconfig/docker-network
EnvironmentFile=-/run/docker_opts.env
即:这部分代码要放在
ExecStart=/usr/bin/dockerd $DOCKER_NETWORK_OPTIONS的前面
3.3.4最终确认
flannel.1和docker0 在同一网段了
[root@localhost ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.0.1 0.0.0.0 UG 0 0 0 ens33
169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 ens33
172.30.0.0 0.0.0.0 255.255.0.0 U 0 0 0 flannel.1
172.30.98.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
192.168.0.0 0.0.0.0 255.255.248.0 U 0 0 0 ens33
192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0