1. 设置系统参数 – 允许路由转发,不对bridge的数据进行处理
- net.bridge.bridge-nf-call-ip6tables=1
- net.bridge.bridge-nf-call-iptables=1
- net.ipv4.ip_forward=1
- vm.swappiness=0
2. 设置iptables的FORWARD链,Docker从1.13版本开始调整了默认的防火墙规则,禁用了iptables filter表中FOWARD链,这样会引起Kubernetes集群中跨Node的Pod无法通信。
- iptables -P FORWARD ACCEPT
- iptables -nvL #查看命令
3. Kubernetes 1.8开始要求关闭系统的Swap,如果不关闭,默认配置下kubelet将无法启动。可以通过kubelet的启动参数--fail-swap-on=false更改这个限制。
- swapoff -a
- free -mh #查看命令
4. 通过kubectl命令获取secret,以及token,用于登录dashboard UI,基于RBAC(基于角色的访问控制)见附录
- kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin | awk '{print $1}')
#关键字根据你创建的create来,比如admin,dashboard
原理:创建一个ServiceAccount,并将该SA绑定到cluster-admin这个ClusterRole角色上,就拥有了该角色的权限,而这个角色拥有整个集群的所有权限。
#这里还涉及一个https访问的问题,需要在master节点(这里要求dashboard部署在master节点上)生成自签名的证书,然后在kubernetes-dashboard.yaml文件中修改:
volumes:
- name: kubernetes-dashboard-certs
hostPath:
path: /usr/share/certs
type: Directory
自签名的步骤如下:
openssl genrsa -des3 -passout pass:x -out dashboard.pass.key 2048
openssl rsa -passin pass:x -in dashboard.pass.key -out dashboard.key
rm dashboard.pass.key
openssl req -new -key dashboard.key -out dashboard.csr
openssl x509 -req -sha256 -days 365 -in dashboard.csr -signkey dashboard.key -out dashboard.crt
#将dashboard.key dashboard.crt拷贝至/usr/share/certs目录即可。
5. 通过kubeadm init命令创建集群之后,会报错http://localhost:10255/healthz出错,发现是kubelet服务没有启动
需要创建kubelet.service.d目录,并在其下创建10-kubeadm.conf文件,内容如下:
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_SYSTEM_PODS_ARGS=--pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true"
Environment="KUBELET_NETWORK_ARGS=--network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin"
Environment="KUBELET_DNS_ARGS=--cluster-dns=10.96.0.10 --cluster-domain=cluster.local"
Environment="KUBELET_AUTHZ_ARGS=--authorization-mode=Webhook --client-ca-file=/etc/kubernetes/pki/ca.crt"
# Value should match Docker daemon settings.
# Defaults are "cgroupfs" for Debian/Ubuntu/OpenSUSE and "systemd" for Fedora/CentOS/RHEL
Environment="KUBELET_CGROUP_ARGS=--cgroup-driver=cgroupfs"
Environment="KUBELET_CADVISOR_ARGS=--cadvisor-port=0"
Environment="KUBELET_CERTIFICATE_ARGS=--rotate-certificates=true"
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_SYSTEM_PODS_ARGS $KUBELET_NETWORK_ARGS $KUBELET_DNS_ARGS $KUBELET_AUTHZ_ARGS $KUBELET_CGROUP_ARGS $KUBELET_CADVISOR_ARGS $KUBELET_CERTIFICATE_ARGS $KUBELET_EXTRA_ARGS
这里也可以直接通过命令从github上下载
curl -O "https://raw.githubusercontent.com/kubernetes/kubernetes/v1.10.0/build/debs/kubelet.service" #注意/usr/bin/kubelet路径问题
再创建kubelet.service.d目录,并从github上下载
curl -O "https://raw.githubusercontent.com/kubernetes/kubernetes/v1.10.0/build/debs/10-kubeadm.conf"
6. 集群部署成功之后,发送kubectl命令还是无法获取集群信息,这里需要export KUBECONFIG参数
为了简化,可以写在/etc/profile,并执行source /etc/profile使其生效,注意:master节点和node节点采用不一样的配置文件,权限不一样。
# master节点上
export KUBECONFIG=/etc/kubernetes/admin.conf
# node节点上
export KUBECONFIG=/etc/kubernetes/kubelet.conf
7. kubectl proxy命令目前来说,使用的场景是,自动化部署脚本中,需要远程部署Pod至Kubernetes集群,因为需要http访问,绕过https。
kubectl proxy --address='0.0.0.0' --port=8080 --accept-hosts='^*$'
这样在shell脚本中才可以执行: kubectl -s http://masterIp:8080 apply -f *.yaml
8. 通过kubeadm token命令生成一个forever的token,默认的24h,利于后续join新的节点到集群。
kubeadm token create --ttl=0
kubeadm join masterIp:6443 --token bhpmca.yz08394xcolahg0y --discovery-token-ca-cert-hash sha256:f2d058d7f6489dd71aba1c780adf4e7e24663a6db54e7e1fbc3ca079f616bd92
另外,如果忘记了sha256,这里也提供一个查询方法,如下:
#获取master上的ca.crt证书sha256编码hash值
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
9. 集群清理命令
# 删除节点
kubectl drain nodeName --delete-local-data --force --ignore-daemonsets
kubectl delete node nodeName
# 清理
kubeadm reset
ifconfig cni0 down
ip link delete cni0
ifconfig flannel.1 down
ip link delete flannel.1
rm -rf /var/lib/cni/
rm -rf /etc/kubernetes/
systemctl stop kubelet;
docker rm -f -v $(docker ps -q);
find /var/lib/kubelet | xargs -n 1 findmnt -n -t tmpfs -o TARGET -T | uniq | xargs -r umount -v;
rm -rf /var/lib/kubelet /var/lib/etcd;
# 记住要清理iptables
iptables -FXZ
10. 通过java -jar启动jar包项目时,出现服务发现失败的问题
比如在项目的rabbitmq.properties文件中定义了Kubernetes的serviceName来访问节点
但是如果是直接server.host=rabbitmq,程序中访问失败,但是查看dns,ping服务名ip显示都是正常
root@node:~/k8s# cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
root@node:~/k8s# ping rabbitmq
PING rabbitmq.default.svc.cluster.local (10.109.82.27): 56 data bytes
.default,则访问正常,见附录2。
11. 通过netstat -npt | grep 30039 | wc -l查看端口连接个数
- apt-get install -y net-tools
- netstat -anopt|grep 30039|wc -l
12. Node的隔离与恢复
- kubectl replace -f unschedule_node.yaml
apiVersion: v1
kind: Node
metadata:
name: masterNodename
labels:
xx.com/hostname: masterNodename
spec:
unschedulable: true
- kubectl patch node nodeName -p '{"spec":{"unschedulable":true}}'
- 将某个Node脱离调度范围时,在其上运行的Pod并不会自动停止,管理员需要手动停止在该Node上运行的Pod
13. Node设置标签,将Pod调度指定Node
- kubectl label nodes nodeName zone=north
- kubectl label nodes nodeName disktype=ssd
- kubectl get node --show-labels #查看节点label
- Pod/Deployment的spec.spec.nodeSelector #指定将此 Pod 部署到具有 label
key=value
的 Node 上 - 注意:如果指定Pod的nodeSelector条件,且集群中不存在包含相应标签的Node时,即使还有其他可供调度的Node,这个Pod也最终会调度失败
14. 应用的滚动升级
- Kubernetes提供了rolling-update(滚动升级),解决服务需要先全部停止然后逐步升级的方式导致的较长时间的服务不可用情况
- 滚动升级通过执行kubectl rolling-update命令一键完成,该命令创建了一个新的RC,然后自动控制旧的RC中的Pod副本数量逐渐减少到0,同时新的RC中的Pod副本数量从0逐步增加到目标值,最终实现了Pod的升级。需要注意的是,系统要求新的RC需要与旧的RC在相同的命名空间(Namespace)内,即不能把别人的资产偷偷转移到自家名下
- 以redis-master为例,假设当前运行的redis-master Pod是1.0版本,则现在需要升级到2.0版本
- RC的名字(name)不能与旧的RC的名字相同
- 在selector中应至少有一个Label与旧的RC的Label不同,以标识其为新的RC,例如:version: v2
- kubectl rolling-update redis-master -f redis-master-controller-v2.yaml
- 等所有新的Pod启动完成后,旧的Pod也被全部销毁,这样就完成了容器集群的更新。
- 直接用kubectl rolling-update命令,加上--image参数指定新版镜像名称来完成Pod的滚动升级
- 执行的结果是旧的RC被删除,新的RC仍将使用旧的RC的名字
- 如果在更新过程中发现配置有误,则用户可以中断更新操作,并通过执行Kubectl rolling-update –rollback完成Pod版本的回滚
kubectl rolling-update redis-master --image=kubeguide/redis-master:2.0
kubectl rolling-update redis-master --image=kubeguide/redis-master:2.0 --rollback
以上是RC的滚动升级方式,目前有更好的Deployment资源管理:
1. 更新deployment,我们只需更新deployment里面的template,当我们更新deployment时,当有合适的Pod产生,k8s才会删除老的Pod,当老的Pod被删除足够多时,才会继续产生新的Pod。
2. 定义deployment时,与rolling update相关项
.spec.minReadySeconds: 新建的Pod状态为ready持续时间至少为它,才认为Pod Available(Ready),默认30s
.spec.strategy.rollingUpdate.maxSurge: 默认是desired Pods数的25%,Scale Up新的ReplicaSet时,允许的maxSurge,可以是整数或者百分比,向上取整
.spec.strategy.rollingUpdate.maxUnavailable: 默认为desired Pods数的25%,Scale Down旧的ReplicaSet时,允许的maxUnavailable,可以是整数或者百分比,向下取整
因此,在Deployment rollout时,需要保证Available(Ready) Pods数不低于 desired pods number - maxUnavailable; 保证所有的Pods数不多于 desired pods number + maxSurge。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: frontend
spec:
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 3
maxUnavailable: 2
replicas: 25
template:
kubectl set image deployment xxx-dep myregistry:5000/xxx=myregistry:5000/xxx:v2 --namespace=default
kubectl rollout undo deployment xxx-dep --namespace=default
kubectl rollout undo deployment xxx-dep --revision=1 --namespace=default
kubectl rollout history deployment xxx-dep --namespace=default
kubectl rollout history deployment xxx-dep --revision=2 --namespace=default
15. 修改k8s集群的端口范围,默认是30000-32767,为了不合其他应用冲突,通过修改配置/etc/kubernetes/manifests/kube-apiserver.yaml实现
- --service-cluster-ip-range=10.96.0.0/12
- --service-node-port-range=1-65535
- 修改kube-apiserver.yaml后,kube-apiserver自动重启,这是因为kubelet在监听着/etc/kubernetes/manifests目录下的文件变化并处理
16. 目前采用的版本是Kubernetes v1.10.0,以下是对应的镜像版本
k8s.gcr.io/kube-apiserver-amd64:v1.10.0
k8s.gcr.io/kube-scheduler-amd64:v1.10.0
k8s.gcr.io/kube-controller-manager-amd64:v1.10.0
k8s.gcr.io/etcd-amd64:v1.10.0
k8s.gcr.io/kube-proxy-amd64:v1.10.0
k8s.gcr.io/pause-amd64:3.1
quay.io/coreos/flannel:v0.10.0-amd64
k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3
k8s.gcr.io/k8s-dns-dnsmasq-nanny-amd64:1.14.8
k8s.gcr.io/k8s-dns-sidecar-amd64:1.14.8
k8s.gcr.io/k8s-dns-kube-dns-amd64:1.14.8
k8s.gcr.io/heapster-influxdb-amd64:v1.3.3
k8s.gcr.io/heapster-grafana-amd64:v4.4.3
k8s.gcr.io/heapster-amd64:v1.4.2
17. kubelet指定本地镜像
kubelet 修改配置以使用本地自定义pause镜像
cat > /etc/systemd/system/kubelet.service.d/10-kubeadm.conf <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--pod-infra-container-image=local.repo/google_containers/pause-amd64:3.1"
EOF
systemctl daemon-reload
systemctl restart kubelet
附录1. RBAC(Role-Base-Access-Control),基于角色的权限控制策略。
涉及ServiceAccount、Role、RoleBinding,Kubernetes集群中也有一个默认的ClusterRole、ClusterRoleBinding拥有所有权利的角色。
所以简单的做法是:创建一个ServiceAccount,让它通过ClusterRoleBinding绑定到ClusterRole(cluster-admin),则新创建的用户获得了集群的所有权限。
当然,在kubernetes集群中,最佳的做法是,为每一个应用程序的服务账户授予角色,确保该账户在指定命名空间下控制应用程序的运行。
例如:在tiller-world的namespace下创建tiller用户,通过RoleBinding绑定到一个自定义的Role上。
$ kubectl create namespace tiller-world
namespace "tiller-world" created
$ kubectl create serviceaccount tiller --namespace tiller-world
serviceaccount "tiller" created
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: tiller-manager
namespace: tiller-world
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["*"]
verbs: ["*"]
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: tiller-binding
namespace: tiller-world
subjects:
- kind: ServiceAccount
name: tiller
namespace: tiller-world
roleRef:
kind: Role
name: tiller-manager
apiGroup: rbac.authorization.k8s.io
RBAC虽然很灵活,但是它的缺点是没有提供顺序操作的机制,导致它无法应用到那些有严格操作次序的系统。
附录2. 服务发现
k8s将Service的名称当做域名注册到kube-dns,这样在集群中就可以通过serverName访问服务了。
kube-dns通过nslookup指令检查DNS查询服务的健康状态,pod中的容器查询DNS时是通过serverName.namespace子域名查询的,所以最好在serviceName后面加上namespace,避免可能的错误,例如: nslookup kubernetes.default
kube-dns支持的域名格式,具体为:<service_name>.<namespace>.svc.<cluster_domain>
其中cluster_domain可以使用kubelet的--cluster-domain=SomeDomain参数进行设置,通常设置为:cluster.local
每个Pod中的/etc/resolv.conf文件中的参数:nameserver 10.96.0.10,对应就是kube-dns服务的ClusterIp,Pod的域名解析服务器,当然是通过kubelet的--cluster-dns参数设置的。