1、安装docker
1.1、增加docker 源
1 | yum-config-manager \ |
官方的源比较慢,可以增加阿里的源
1 | # yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo |
1.1.1 node节点安装插件
1 | # yum install -y epel-release |
1.2、安装docker
1 | # yum -y install docker-ce |
1.3、修改docker systemd unit 文件
1 | # cat /usr/lib/systemd/system/docker.service |egrep -Ev "^$|^#" |
- dockerd 运行时会调用其它 docker 命令,如 docker-proxy,所以需要将 docker 命令所在的目录加到 PATH 环境变量中;
- flanneld 启动时将网络配置写入 /run/flannel/docker 文件中,dockerd 启动前读取该文件中的环境变量 DOCKER_NETWORK_OPTIONS ,然后设置 docker0 网桥网段;
- 如果指定了多个 EnvironmentFile 选项,则必须将 /run/flannel/docker 放在最后(确保 docker0 使用 flanneld 生成的 bip 参数);
- docker 需要以 root 用于运行;
1.4、启动 docker 服务
1 | # systemctl daemon-reload && systemctl enable docker && systemctl restart docker && systemctl status docker |
1.5、检查 docker0 网桥
1 | # /usr/sbin/ip addr show flannel.1 && /usr/sbin/ip addr show docker0 |
1.5.1、查看 docker 的状态信息
1 | # ps -elfH|grep docker |
2、部署 kubelet 组件
kubelet 运行在每个 worker 节点上,接收 kube-apiserver 发送的请求,管理 Pod 容器,执行交互式命令,如 exec、run、logs 等。
kubelet 启动时自动向 kube-apiserver 注册节点信息,内置的 cadvisor 统计和监控节点的资源使用情况。
为确保安全,部署时关闭了 kubelet 的非安全 http 端口,对请求进行认证和授权,拒绝未授权的访问(如 apiserver、heapster 的请求)。
2.1、创建 kubelet bootstrap kubeconfig 文件
NODE_NAMES 里面的值是node的主机名
1 | # NODE_NAMES=(node-01 node-02 node-03) |
向 kubeconfig 写入的是 token,bootstrap 结束后 kube-controller-manager 为 kubelet 创建 client 和 server 证书;
查看 kubeadm 为各节点创建的 token:
master 节点查看1
2
3
4
5# kubeadm token list --kubeconfig ~/.kube/config
TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS
016e9x.306t91l832suzg8i 19h 2019-09-17T11:29:43+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:node-03
4l4tcx.juy6qs9rmrnfpbig 19h 2019-09-17T11:29:43+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:node-01
64pk36.vbhvbmtojpskyclt 19h 2019-09-17T11:29:43+08:00 authentication,signing kubelet-bootstrap-token system:bootstrappers:node-02- token 有效期为 1 天,超期后将不能再被用来 boostrap kubelet,且会被 kube-controller-manager 的 tokencleaner 清理;
- kube-apiserver 接收 kubelet 的 bootstrap token 后,将请求的 user 设置为 system:bootstrap:
,group 设置为 system:bootstrappers,后续将为这个 group 设置 ClusterRoleBinding;
查看各 token 关联的 Secret:
1
2
3
4# kubectl get secrets -n kube-system|grep bootstrap-token
bootstrap-token-016e9x bootstrap.kubernetes.io/token 7 4h25m
bootstrap-token-4l4tcx bootstrap.kubernetes.io/token 7 4h25m
bootstrap-token-64pk36 bootstrap.kubernetes.io/token 7 4h25m
2.2、创建和分发 kubelet 参数配置文件
从 v1.10 开始,部分 kubelet 参数需在配置文件中配置,kubelet –help 会提示:
1 | DEPRECATED: This parameter should be set via the config file specified by the Kubelet's --config flag |
创建 kubelet 参数配置文件模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69# cat /etc/kubernetes/kubelet-config.yaml
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
address: "##node_ip##"
staticPodPath: ""
syncFrequency: 1m
fileCheckFrequency: 20s
httpCheckFrequency: 20s
staticPodURL: ""
port: 10250
readOnlyPort: 0
rotateCertificates: true
serverTLSBootstrap: true
authentication:
anonymous:
enabled: false
webhook:
enabled: true
x509:
clientCAFile: "/etc/kubernetes/ssl/ca.pem"
authorization:
mode: Webhook
registryPullQPS: 0
registryBurst: 20
eventRecordQPS: 0
eventBurst: 20
enableDebuggingHandlers: true
enableContentionProfiling: true
healthzPort: 10248
healthzBindAddress: "##node_ip##"
clusterDomain: "cluster.local"
clusterDNS:
- "10.254.0.2"
nodeStatusUpdateFrequency: 10s
nodeStatusReportFrequency: 1m
imageMinimumGCAge: 2m
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80
volumeStatsAggPeriod: 1m
kubeletCgroups: ""
systemCgroups: ""
cgroupRoot: ""
cgroupsPerQOS: true
cgroupDriver: cgroupfs
runtimeRequestTimeout: 10m
hairpinMode: promiscuous-bridge
maxPods: 100
# podCIDR: "172.30.0.0/16"
podPidsLimit: -1
resolvConf: /etc/resolv.conf
maxOpenFiles: 1000000
kubeAPIQPS: 1000
kubeAPIBurst: 2000
serializeImagePulls: false
evictionHard:
memory.available: "100Mi"
nodefs.available: "10%"
nodefs.inodesFree: "5%"
imagefs.available: "15%"
evictionSoft: {}
enableControllerAttachDetach: true
failSwapOn: true
containerLogMaxSize: 20Mi
containerLogMaxFiles: 10
systemReserved: {}
kubeReserved: {}
systemReservedCgroup: ""
kubeReservedCgroup: ""
enforceNodeAllocatable: ["pods"]- address:kubelet 安全端口(https,10250)监听的地址,不能为 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 参数;
- 需要 root 账户运行;
2.3、创建kubelet systemd unit 文件
1 | # cat /usr/lib/systemd/system/kubelet.service |
- 如果设置了 –hostname-override 选项,则 kube-proxy 也需要设置该选项,否则会出现找不到 Node 的情况;
- –bootstrap-kubeconfig:指向 bootstrap kubeconfig 文件,kubelet 使用该文件中的用户名和 token 向 kube-apiserver 发送 TLS Bootstrapping 请求;
- K8S approve kubelet 的 csr 请求后,在 –cert-dir 目录创建证书和私钥文件,然后写入 –kubeconfig 文件;
- –pod-infra-container-image 不使用 redhat 的 pod-infrastructure:latest 镜像,它不能回收容器的僵尸;
2.4、Bootstrap Token Auth 和授予权限
kubelet 启动时查找 –kubeletconfig 参数对应的文件是否存在,如果不存在则使用 –bootstrap-kubeconfig 指定的 kubeconfig 文件向 kube-apiserver 发送证书签名请求 (CSR)。
kube-apiserver 收到 CSR 请求后,对其中的 Token 进行认证,认证通过后将请求的 user 设置为 system:bootstrap:
默认情况下,这个 user 和 group 没有创建 CSR 的权限,kubelet 启动失败,错误日志如下:
1 | Sep 16 11:39:43 node-02 kubelet[20385]: E0916 11:39:43.858672 20385 reflector.go:126] k8s.io/client-go/informers/factory.go:133: Failed to list *v1beta1.RuntimeClass: Unauthorized |
解决办法是:创建一个 clusterrolebinding,将 group system:bootstrappers 和 clusterrole system:node-bootstrapper 绑定:
1 | # kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --group=system:bootstrappers |
2.5、启动 kubelet 服务
1 | # mkdir -p /var/log/k8s/kubelet |
- 启动服务前必须先创建工作目录;
- 关闭 swap 分区,否则 kubelet 会启动失败;
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 创建证书和私钥。
1 | # kubectl get csr |
2.6、自动 approve CSR 请求
1 | # cat /etc/kubernetes/csr-crb.yaml |
- auto-approve-csrs-for-group:自动 approve node 的第一次 CSR; 注意第一次 CSR 时,请求的 Group 为 system:bootstrappers;
- node-client-cert-renewal:自动 approve node 后续过期的 client 证书,自动生成的证书 Group 为 system:nodes;
- node-server-cert-renewal:自动 approve node 后续过期的 server 证书,自动生成的证书 Group 为 system:nodes;
2.6、等查看 kubelet 的情况
待一段时间(1-10 分钟),三个节点的 CSR 都被自动 approved:
1 | # kubectl get csr |
- 所有节点均 ready:
1
2
3
4
5# kubectl get nodes
NAME STATUS ROLES AGE VERSION
172.21.16.204 Ready <none> 4m17s v1.14.6
172.21.16.240 Ready <none> 3m2s v1.14.6
172.21.16.231 Ready <none> 3s v1.14.6
kube-controller-manager 为各 node 生成了 kubeconfig 文件和公私钥:
1 | # ls -l /etc/kubernetes/kubelet.kubeconfig |
没有自动生成 kubelet server 证书;
2.8、手动 approve server cert csr
基于安全性考虑,CSR approving controllers 不会自动 approve kubelet server 证书签名请求,需要手动 approve:
1 | # kubectl get csr |
2.9、kubelet 提供的 API 接口
kubelet 启动后监听多个端口,用于接收 kube-apiserver 或其它客户端发送的请求:
1 | # netstat -lnpt|grep kubelet |
- 10248: healthz http 服务;
- 10250: https 服务,访问该端口时需要认证和授权(即使访问 /healthz 也需要);
- 未开启只读端口 10255;
- 从 K8S v1.10 开始,去除了 –cadvisor-port 参数(默认 4194 端口),不支持访问 cAdvisor UI & API。
由于关闭了匿名认证,同时开启了 webhook 授权,所有访问 10250 端口 https API 的请求都需要被认证和授权。
预定义的 ClusterRole system:kubelet-api-admin 授予访问 kubelet 所有 API 的权限(kube-apiserver 使用的 kubernetes 证书 User 授予了该权限):
1 | # kubectl describe clusterrole system:kubelet-api-admin |
2.10、kubelet api 认证和授权
kubelet 配置了如下认证参数:
- authentication.anonymous.enabled:设置为 false,不允许匿名访问 10250 端口;
- authentication.x509.clientCAFile:指定签名客户端证书的 CA 证书,开启 HTTPs 证书认证;
- authentication.webhook.enabled=true:开启 HTTPs bearer token 认证;
同时配置了如下授权参数:
- authroization.mode=Webhook:开启 RBAC 授权
kubelet 收到请求后,使用 clientCAFile 对证书签名进行认证,或者查询 bearer token 是否有效。如果两者都没通过,则拒绝请求,提示 Unauthorized:
1 | # curl -s --cacert /etc/kubernetes/ssl/ca.pem https://172.21.16.231:10250/metrics |
通过认证后,kubelet 使用 SubjectAccessReview API 向 kube-apiserver 发送请求,查询证书或 token 对应的 user、group 是否有操作资源的权限(RBAC);
2.11、证书认证和授权
1 | # 权限不足的证书; |
- –cacert、–cert、–key 的参数值必须是文件路径,如上面的 ./admin.pem 不能省略 ./,否则返回 401 Unauthorized;
2.12、bear token 认证和授权
创建一个 ServiceAccount,将它和 ClusterRole system:kubelet-api-admin 绑定,从而具有调用 kubelet API 的权限:
1 | # kubectl create sa kubelet-api-test |
3、cadvisor 和 metrics
cadvisor 是内嵌在 kubelet 二进制中的,统计所在节点各容器的资源(CPU、内存、磁盘、网卡)使用情况的服务。
浏览器访问 https://172.21.16.231:10250/metrics 和 https://172.21.16.231:10250/metrics/cadvisor 分别返回 kubelet 和 cadvisor 的 metrics。
注意:
- kubelet.config.json 设置 authentication.anonymous.enabled 为 false,不允许匿名证书访问 10250 的 https 服务;
- 参考kubelet提供api请求接口,创建和导入相关证书,然后访问上面的 10250 端口;
3.1、获取 kubelet 的配置
从 kube-apiserver 获取各节点 kubelet 的配置:
1 | # 使用部署 kubectl 命令行工具时创建的、具有最高权限的 admin 证书; |
4、部署 kube-proxy 组件
kube-proxy 运行在所有 worker 节点上,它监听 apiserver 中 service 和 endpoint 的变化情况,创建路由规则以提供服务 IP 和负载均衡功能。
4.1、创建 kube-proxy 证书
1 | cat > kube-proxy-csr.json <<EOF |
CN:指定该证书的 User 为 system:kube-proxy;
预定义的 RoleBinding system:node-proxier 将User system:kube-proxy 与 Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限;
该证书只会被 kube-proxy 当做 client 证书使用,所以 hosts 字段为空;
生成证书和私钥
1
2
3
4# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy
# ls kube-proxy*.pem
kube-proxy-key.pem kube-proxy.pem
4.2、创建和分发 kubeconfig 文件
1 | # kubectl config set-cluster kubernetes \ |
- –embed-certs=true:将 ca.pem 和 admin.pem 证书内容嵌入到生成的 kubectl-proxy.kubeconfig 文件中(不加时,写入的是证书文件路径)
4.3、创建 kube-proxy 配置文件
1 | # cat /etc/kubernetes/kube-proxy-config.yaml |
- bindAddress: 监听地址;
- clientConnection.kubeconfig: 连接 apiserver 的 kubeconfig 文件;
- clusterCIDR: kube-proxy 根据 –cluster-cidr 判断集群内部和外部流量,指定 –cluster-cidr 或 –masquerade-all 选项后 kube-proxy 才会对访问 Service IP 的请求做 SNAT;
- hostnameOverride: 参数值必须与 kubelet 的值一致,否则 kube-proxy 启动后会找不到该 Node,从而不会创建任何 ipvs 规则;
- mode: 使用 ipvs 模式;
4.4、创建kube-proxy systemd unit 文件
1 | # cat /usr/lib/systemd/system/kube-proxy.service |
4.5、启动 kube-proxy 服务
1 | # mkdir -p /var/log/k8s/kube-proxy |
4.5、检查
查看监听端口
1
2
3# netstat -lnpt|grep kube-prox
tcp 0 0 172.21.16.231:10256 0.0.0.0:* LISTEN 27423/kube-proxy
tcp 0 0 172.21.16.231:10249 0.0.0.0:* LISTEN 27423/kube-proxy查看 ipvs 路由规则
1
2
3
4
5
6
7
8# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.254.0.1:443 rr
-> 172.21.17.30:6443 Masq 1 0 0
-> 172.21.17.31:6443 Masq 1 0 0
-> 172.21.16.110:6443 Masq 1 0 0