kubernetes Node节点包含如下组件
- flannel
- docker
- kubelet
- kube-proxy
flannel,docker 根据前面的文章自行部署,这里重要介绍kubelet ,kube-proxy 组件
部署kubelet组件
Master apiserver启用TLS认证后,Node节点kubelet组件想要加入集群,必须使用CA签发的有效证书才能与apiserver通信,当Node节点很多时,签署证书是一件很繁琐的事情,因此有了TLS Bootstrapping机制,kubelet会以一个低权限用户自动向apiserver申请证书,kubelet的证书由apiserver动态签署。
相关文章:
https://mritd.me/2018/01/07/kubernetes-tls-bootstrapping-note/
在 apiserver 配置中指定了一个 token.csv
文件,该文件中是一个预设的用户配置;同时该用户的 Token 和 apiserver 的 CA 证书被写入了 kubelet 所使用的 bootstrap.kubeconfig
配置文件中;这样在首次请求时,kubelet 使用 bootstrap.kubeconfig
中的 apiserver CA 证书来与 apiserver 建立 TLS 通讯,使用 bootstrap.kubeconfig
中的用户 Token 来向 apiserver 声明自己的 RBAC 授权身份,所以,我们需要生成一个kubeconfig 的文件
1.生成kubeconfig文件
BOOTSTRAP_TOKEN=4274d4ed9dd65b7bddc521916a218f0a #这个内容可以到api指定的token文件中查看
KUBE_APISERVER="https://10.211.55.8:8443" #对外提供的api安全连接端口
# 设置集群参数
cd /etc/ssl/lkubernetes/
kubectl config set-cluster kubernetes \
--certificate-authority=./ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=bootstrap.kubeconfig
# 设置客户端认证参数
kubectl config set-credentials kubelet-bootstrap \
--token=${BOOTSTRAP_TOKEN} \
--kubeconfig=bootstrap.kubeconfig
# 设置上下文参数
kubectl config set-context default \
--cluster=kubernetes \
--user=kubelet-bootstrap \
--kubeconfig=bootstrap.kubeconfig
# 设置默认上下文
kubectl config use-context default --kubeconfig=bootstrap.kubeconfig
这里面授权用到的用户是kubelet-bootstap,所以还需要授权因为在有些用户首次启动时,可能与遇到 kubelet 报 401 无权访问 apiserver 的错误;
这是因为在默认情况下,kubelet 通过 bootstrap.kubeconfig 中的预设用户 Token 声明了自己的身份,然后创建 CSR 请求;
但是不要忘记这个用户在我们不处理的情况下他没任何权限的,包括创建 CSR 请求;所以需要如下命令创建一个 ClusterRoleBinding,
将预设用户 kubelet-bootstrap 与内置的 ClusterRole system:node-bootstrapper 绑定到一起,使其能够发起 CSR 请求
kubectl create clusterrolebinding kubelet-bootstrap \
--clusterrole=system:node-bootstrapper \
--user=kubelet-bootstrap
这样基本就完成了认证和授权相关的配置,只需要把生成的 bootstrap.kubeconfig 分发到个node节点上
2.增加配置文件
vim /etc/kubernetes/kubelet
KUBELET_OPTS="--logtostderr=true \
--v=2 \
--hostname-override=k8s-node1 \
--bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig \
--kubeconfig=/etc/kubernetes/kubelet.kubeconfig \
--cert-dir=/etc/kubernetes/ssl \
--pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.1 \
--address=10.211.55.12 \
--hostname-override=k8s-node1 \
--rotate-certificates \
--cluster-dns=10.0.0.2 \
--cluster-domain=cluster.local \
--allow-privileged=true \
--fail-swap-on=false"
kubelet --help 可以查看相关参数
参考文章:
https://www.jianshu.com/p/087895ba7d87
https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/
https://blog.frognew.com/2017/07/kubelet-production-config.html
最好按最新的官网去参考相应的参数
几个重要参数说明:
ddress:API
#监听地址,不能为 127.0.0.1,否则 kube-apiserver、heapster 等不能调用 kubelet 的 API;
readOnlyPort=0:
#关闭只读端口(默认 10255)
authentication.anonymous.enabled:
#设置为 false,不允许匿名访问 10250 端口;
authentication.x509.clientCAFile:
#指定签名客户端证书的 CA 证书,开启 HTTP 证书认证;
authentication.webhook.enabled=true:开启 HTTPs bearer token 认证;
#对于未通过 x509 证书和 webhook 认证的请求(kube-apiserver 或其他客户端),将被拒绝,提示 Unauthorized;
authroization.mode=Webhook:
#kubelet 使用 SubjectAccessReview API 查询 kube-apiserver 某 user、group 是否具有操作资源的权限(RBAC);
featureGates.RotateKubeletClientCertificate、featureGates.RotateKubeletServerCertificate:
#自动 rotate 证书,证书的有效期取决于 kube-controller-manager 的 --experimental-cluster-signing-duration 参数;
–cluster-dns
#指定kubedns的Service IP(可以先分配,后续创建kubedns 服务时指定该IP)
–cluster-domain
#指定域名后缀,与上面的参数同时指定后才会生效;
–hostname-override
#如果设置了kube-proxy也需要设置该选项,否则会出现找不到Node的情况;
--rotate-certificates
# kubelet 能够自动重载新证书
--cert-dir
#管理员通过了CSR请求后,kubelet自动在–cert-dir目录创建证书和私钥文件(kubelet-client.crt和kubelet-client.key),然后写入–kubeconfig文件(自动创建 –kubeconfig指定的文件);
----hairpin-mode
Kubelet 公开了一个 hairpin-mode 标志,如果 pod 试图访问它们自己的 Service VIP,
就可以让 Service 的 endpoints 重新负载到他们自己身上。hairpin-mode 标志必须设置为 hairpin-veth 或者 promiscuous-bridge。默认是promiscuous-bridge
参考文档:http://docs.kubernetes.org.cn/819.html
--pod-infra-container-image
Pod的pause镜像
3.启动脚本的配置
vim /usr/lib/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
After=docker.service
Requires=docker.service
[Service]
EnvironmentFile=/etc/kubernetes/kubelet
ExecStart=/usr/bin/kubelet $KUBELET_OPTS
Restart=on-failure
KillMode=process
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable kubelet
systemctl restart kubelet
查看启动状态和日志,确保正常
4.在Master审批Node加入集群
kubelet 启动后使用 --bootstrap-kubeconfig 向 kube-apiserver 发送 CSR 请求,当这个 CSR 被 approve 后,kube-controller-manager 为 kubelet 创建 TLS 客户端证书、私钥和 --kubeletconfig 文件。
注意:kube-controller-manager 需要配置 --cluster-signing-cert-file 和 --cluster-signing-key-file 参数,才会为 TLS Bootstrap 创建证书和私钥。
节点的 csr 均处于 pending 状态;
此时kubelet的进程有,但是监听端口还未启动,需要进行下面步骤!
在Master节点查看请求签名的Node:
[root@k8s-master1 ssl]# kubectl get csr
NAME AGE REQUESTOR CONDITION
node-csr-TvW12euzSdw9mNF4ZSoXrw2tzuEvmuyvErpQX3LbKcA 8m26s kubelet-bootstrap Pending
node-csr-AxONIPr10EUclMcM2Ix0MPmjc_nLrlUoxxY6xL-S-ik 23s kubelet-bootstrap Pending
可以手动或自动 approve CSR 请求。推荐使用自动的方式,因为从 v1.8 版本开始,可以自动轮转approve csr 后生成的证书
1.手动approve csr请求
[root@k8s-master1 ~]# kubectl certificate approve node-csr-TvW12euzSdw9mNF4ZSoXrw2tzuEvmuyvErpQX3LbKcA
certificatesigningrequest.certificates.k8s.io "node-csr-TvW12euzSdw9mNF4ZSoXrw2tzuEvmuyvErpQX3LbKcA" approved
查看 Approve 结果:
[root@k8s-master1 ~]# kubectl describe csr node-csr-TvW12euzSdw9mNF4ZSoXrw2tzuEvmuyvErpQX3LbKcA
[root@k8s-master1 ssl]# kubectl get csr
NAME AGE REQUESTOR CONDITION
node-csr-AxONIPr10EUclMcM2Ix0MPmjc_nLrlUoxxY6xL-S-ik 23s kubelet-bootstrap Pending
node-csr-TvW12euzSdw9mNF4ZSoXrw2tzuEvmuyvErpQX3LbKcA 12m kubelet-bootstrap Approved,Issued
[root@k8s-master1 ssl]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-node1 Ready 3d16h v1.14.3
k8s-node2 NotReady 3d16h v1.14.3
同理,可以把另一个节点加入
自动方式的配置可以参考下面的文章,这里不在演示了
https://www.orchome.com/1199
https://mritd.me/2018/01/07/kubernetes-tls-bootstrapping-note/ (这个链接没少发了,希望能认真看一次)
在查看服务相关的端口已经启动了
[root@k8s-master2 kubernetes]# netstat -lnpt|grep kubelet
tcp 0 0 127.0.0.1:10248 0.0.0.0:* LISTEN 27615/kubelet
tcp 0 0 127.0.0.1:34429 0.0.0.0:* LISTEN 27615/kubelet
tcp6 0 0 :::10250 :::* LISTEN 27615/kubelet
tcp6 0 0 :::10255 :::* LISTEN 27615/kubelet
部署kube-proxy组件
kube-proxy 运行在所有node节点上,,它监听 apiserver 中 service 和 Endpoint 的变化情况,创建路由规则来进行服务负载均衡。
本文档讲解部署 kube-proxy 的部署,使用 ipvs 模式。
1.创建kubeconfig文件
cd /etc/ssl/kubernetes/
kubectl config set-cluster kubernetes \
--certificate-authority=./ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=kube-proxy.kubeconfig
kubectl config set-credentials kube-proxy \
--client-certificate=./kube-proxy.pem \
--client-key=./kube-proxy-key.pem \
--embed-certs=true \
--kubeconfig=kube-proxy.kubeconfig
kubectl config set-context default \
--cluster=kubernetes \
--user=kube-proxy \
--kubeconfig=kube-proxy.kubeconfig
kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
说明:
指定该证书的 User 为 system:kube-proxy
;
预定义的clusterrolebinding system:node-proxier 将User system:kube-proxy 与 clusterrole system:node-proxier 绑定,
授予了调用 kube-apiserver Proxy 相关 API 的权限;
2.增加配置文件
vim /etc/kube-prxoy
KUBE_PROXY_OPTS="--logtostderr=true \
--v=2 \
--hostname-override=k8s-node1 \
--cluster-cidr=10.0.0.0/24 \
--proxy-mode=ipvs \
--kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig"
参考文档:
https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/
参数说明:
--hostname-override 使用该名字作为标识而不是实际的主机名需要和 kubelet 保持一致
--cluster-cidr 与api-server 保持一样 定义集群IP 范围
--proxy-mode 代理模式,这里用的是ipvs
--kubeconfig 指定认证和授权的kubeconfig 文件
--healthz-port 配置健康检查服务的端口,0表示禁止 (default 10256)
因为上面的代理模式用到ipvs 所以,我需要确保系统安装了ipvs 相关的服务和模块
yum install ipvsadm ipset -y
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr modprobe -- ip_vs_sh modprobe -- nf_conntrack_ipv4
yum install ipvsadm -y
cat > /etc/sysconfig/modules/ipvs.modules <<EOF #!/bin/bash modprobe -- ip_vs modprobe -- ip_vs_rr modprobe -- ip_vs_wrr modprobe -- ip_vs_sh modprobe -- nf_conntrack_ipv4 EOF
chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv4
[root@k8s-master2 kubernetes]# lsmod |egrep ip_vs
ip_vs_sh 12688 0
ip_vs_wrr 12697 0
ip_vs_rr 12600 4
ip_vs 145497 10 ip_vs_rr,ip_vs_sh,ip_vs_wrr
nf_conntrack 137239 7 ip_vs,nf_nat,nf_nat_ipv4,xt_conntrack,nf_nat_masquerade_ipv4,nf_conntrack_netlink,nf_conntrack_ipv4
libcrc32c 12644 4 xfs,ip_vs,nf_nat,nf_conntrack
3.配置服务脚本
vim /usr/lib/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Proxy
After=network.target
[Service]
EnvironmentFile=-/etc/kubernetes/kube-proxy
ExecStart=/usr/bin/kube-proxy $KUBE_PROXY_OPTS
Restart=on-failure
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable kube-proxy
systemctl restart kube-proxy
查看服务状态和日志
journalctl -u kube-proxy
[root@k8s-master2 kubernetes]# netstat -nulpt |egrep kube-proxy
tcp 0 0 127.0.0.1:10249 0.0.0.0:* LISTEN 17047/kube-proxy
tcp6 0 0 :::10256 :::* LISTEN 17047/kube-proxy
4. 查看ipvs 规则是否创建
ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.0.0.1:443 rr
-> 10.211.55.11:6443 Masq 1 1 0
-> 10.211.55.12:6443 Masq 1 0 0
-> 10.211.55.13:6443 Masq 1 0 0
可见将所有到 kubernetes cluster ip 443 端口的请求都转发到 kube-apiserver 的 6443 端口。
恭喜!至此node节点部署完成。
这样可以测试一下,因为我们部署完集群,又少一个DNS 来发现服务,所以这里部署一个coreDNS
按官网推荐方式安装,其他组件或者插件都在这个
https://github.com/kubernetes/kubernetes/tree/master/cluster/addons
coredns
https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns/coredns
wget https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/coredns/coredns.yaml.base
说明一下
镜像可以通过国内阿里云下载,修改标签,或者更换镜像地址都可以,我这里推荐一个下载的地方
docker pull registry.cn-hangzhou.aliyuncs.com/openthings/k8s-gcr-io-coredns:1.3.1
docker tag registry.cn-hangzhou.aliyuncs.com/openthings/k8s-gcr-io-coredns:1.3.1 k8s.gcr.io/coredns:1.3.1
我这里从dockerhup 下载一个最新的镜像来使用
containers:
- name: coredns
image: coredns/coredns
imagePullPolicy: IfNotPresent
接下来是需要 修改里面的变量,换成实际的值
kubernetes __PILLAR__DNS__DOMAIN__ in-addr.arpa ip6.arpa
修改成
kubernetes cluster.local in-addr.arpa ip6.arpa
memory: __PILLAR__DNS__MEMORY__LIMIT__
按实际情况修改
memory:500M
clusterIP: __PILLAR__DNS__SERVER__
修改成你在kubelet里面定义的DNS
clusterIP: 10.0.0.2
是deploy 部署,默认是一个POD,所以根据实际情况修改
# replicas: not specified here:
# 1. In order to make Addon Manager do not reconcile this replicas parameter.
# 2. Default is 1.
# 3. Will be tuned in real time if DNS horizontal auto-scaling is turned on.
replicas: 2
kubectl apply -f coredns.yaml
默认所以的资源都在kube-system名称空间里面,通过yaml 文件也可以看到
[root@k8s-master1 k8s]# kubectl get all -n kube-system
NAME READY STATUS RESTARTS AGE
pod/coredns-5b8d7c984b-5bvbd 1/1 Running 1 3m
pod/coredns-5b8d7c984b-hm5q7 1/1 Running 1 3m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kube-dns ClusterIP 10.0.0.2 53/UDP,53/TCP,9153/TCP 3m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coredns 2/2 2 2 5h53m
NAME DESIRED CURRENT READY AGE
replicaset.apps/coredns-5b8d7c984b 2 2 2 3m
可以看到cluster-ip 是10.0.0.2 满足之前的需求
测试一下:
下面编写了一个nginx web 测试案例
vim nginx-web.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-web
labels:
tier: frontend
spec:
type: NodePort
selector:
tier: frontend
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: nginx
image: nginx
ports:
- name: http
containerPort: 80
[root@k8s-master1 k8s]# kubectl get svc |egrep nginx
nginx-web NodePort 10.0.0.15
80:18401/TCP 11m
[root@k8s-master1 k8s]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-8448bb446d-2q55w 1/1 Running 0 8m30s 172.17.69.3 k8s-node2
nginx-8448bb446d-h2km5 1/1 Running 0 8m30s 172.17.51.3 k8s-node1
nginx-8448bb446d-vwkg8 1/1 Running 0 8m30s 172.17.51.4 k8s-node1
[root@k8s-master1 k8s]# kubectl get ep nginx-web
NAME ENDPOINTS AGE
nginx-web 172.17.51.3:80,172.17.51.4:80,172.17.69.3:80 12m
测试
[root@k8s-master1 ~]# curl 10.0.0.15
Welcome to nginx!
Welcome to nginx!
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.
For online documentation and support please refer to
Commercial support is available at
Thank you for using nginx.
我们这里借助DNS 来实现名称访问
启动一个测试pod
[root@k8s-master1 k8s]# kubectl run cirror-$RANDOM --rm -it --image=cirros -- /bin/sh
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # cat /etc/resolv.conf
nameserver 10.0.0.2
search default.svc.cluster.local. svc.cluster.local. cluster.local. localdomain
options ndots:5
/ # nslookup nginx-web
Server: 10.0.0.2
Address 1: 10.0.0.2 kube-dns.kube-system.svc.cluster.local
Name: nginx-web
Address 1: 10.0.0.15 nginx-web.default.svc.cluster.local
成功解析出IP
/ # curl nginx-web
Welcome to nginx!
Welcome to nginx!
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.
For online documentation and support please refer to
Commercial support is available at
Thank you for using nginx.
说明各Pod可以通过DNS 来解析服务名称了
总结一下:
如果在kubelet 里面配置 --anonymous-auth=false然后在master 完成csr以后报错
failed to run Kubelet: No authentication method configured
具体需要理解kubelet 的认证和授权:
kubelet 与 kube-apiserver 之间的通信是双向的, kubelet 既需要访问 kube-apiserver 获取分配到自己节点上的 pod 信息, kube-apiserver 也需要主动访问 kubelet 拉取日志, 状态, 监控数据等信息, 所以对两个组件来说, 认证是双向的, kube-apiserver 需要持有 kubelet 的客户端证书, 以完成 kubelet 对自己身份的校验; kubelet 也需要持有 kube-apiserver 的客户端证书, 完成 kube-apiserver 对自己身份的认证.
默认情况下, 对 kubelet 的 https 请求, 如果没有被配置的其他身份验证拒绝的话, 则被视为匿名请求, 并为这个请求赋予system:anonymous
用户名和system:unauthenticated
用户组
如需要禁用匿名访问, 可以在启动 kubelet Daemon 时加入--anonymous-auth=false
配置, 当有匿名访问时, 将回复401 Unauthorized
响应未认证的请求
- kubelet 启动时添加
--client-ca-file
参数, 并指定签发客户端证书的 ca 根证书所在路径
- kube-apiserver 启动时添加
--kubelet-client-certificate
和--kubelet-client-key
参数, 并分别为其指定由 kubelet ca 根证书签发的客户端证书和秘钥
任何被成功认证的请求(包括匿名请求)都将被授权. 默认的授权模式为AlwaysAllow
, 即允许所有类型的请求
- 匿名访问启用时, 应限制其调用 kubelet API 的能力
- 客户端证书身份认证启用时, 只允许配置 CA 签名的客户端证书使用 kubelet API
- 确保
authorization.k8s.io/v1beta1
该 API Group 在 kube-apiserver 中是被启动的状态
- 在 kubelet Daemon 启动参数中, 确保配置了
--authorization-mode=Webhook
和--kubeconfig
两个参数
kubelet 在接收到每个请求后, 会向指定的 kube-apiserver 发起 SubjectAccessReview
API 的请求, 来确定该请求是否被允许
相关文档:
https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-authentication-authorization/
https://jimmysong.io/kubernetes-handbook/guide/kubelet-authentication-authorization.html
https://www.orchome.com/1199