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 \
--feature-gates=RotateKubeletServerCertificate=true \
--feature-gates=RotateKubeletClientCertificate=true \
--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.cnblogs.com/centos-python/articles/13168559.html (后面補發的文章)
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
modprobe br_netfilter
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