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 <none> 3d16h v1.14.3
k8s-node2 NotReady <none> 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 <none> 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 <none> 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 <none> <none>
nginx-8448bb446d-h2km5 1/1 Running 0 8m30s 172.17.51.3 k8s-node1 <none> <none>
nginx-8448bb446d-vwkg8 1/1 Running 0 8m30s 172.17.51.4 k8s-node1 <none> <none>
[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
<!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>
我们这里借助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
<!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>
说明各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
所有评论(0)