Kubernetes是谷歌开源的容器集群管理系统,是Google多年大规模容器管理技术Borg的开源版本,也是CNCF最重要的项目之一,主要功能包括:
基于容器的应用部署、维护和滚动升级
负载均衡和服务发现
跨机器和跨地区的集群调度
自动伸缩
无状态服务和有状态服务
广泛的Volumn支持
插件机制保证扩展性
Kubernetes架构
Kubernetes主要由以下几个核心组件组成:
etcd保存了整个集群的状态;
apiserver提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上;
kubelet负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理;
Container runtime负责镜像管理以及Pod和容器的真正运行(CRI);
kube-proxy负责为Service提供cluster内部的服务发现和负载均衡
Kubeadm 1.13 安装高可用 kubernetes v1.13.1 集群
先上图给个肯定信心。
部署
以CentOS7为基础,搭建一个Master主机和三个Node主机,各个Node主机的配置方式基本相同。
OS: CentOS 7.5 x86_64
Container runtime: Docker 18.06.ce
Kubernetes: 1.13
这里需要使用常规的域名格式,因为后面需要为集群配置Kubernetes Dashboard要求有SSL数字签名。
系统配置
配置host,
1 2 3 4 5 cat /etc/hosts 192.168.50.71 master master.kubernetes.io 192.168.50.72 node1 node01.kubernetes.io 192.168.50.73 node2 node02.kubernetes.io 192.168.50.74 node3 node03.kubernetes.io
关闭防火墙,选择iptable加入端口或禁用防火墙服务两种方式。这里简单起见,禁用防火墙:
1 2 sudo systemctl stop firewalld sudo systemctl disable firewalld
禁用SELINUX,
1 2 3 sudo setenfore 0 sudo vi /etc/selinux/config SELINUX=disabled
所有节点关闭交换分区,
1 2 sudo swapoff -a sudo vi /etc/fstab
将交换区注释掉,使用free -m
查看交换分区是否关闭。
创建/etc/sysctl.d/k8s.conf
文件,添加如下内容,
1 2 3 net.bridge.bridge-nf-call-ip6tables=1 net.bridge.bridge-nf-call-iptables=1 net.ipv4.ip_forward=1
执行命令使修改生效,
1 2 sudo modprobe br_netfilter sudo sysctl -p /etc/sysctl.d/k8s.conf
kube-proxy开启ipvs,Kubernetes 1.11之后的版本默认支持使用ipvs代理模式的Service资源,但它依赖ipvs相关的内核模块,这些模块默认不会自动载入。kube-proxy开启ipvs的前提需要加载以下模块:
1 2 3 4 5 ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack_ipv4
创建/etc/sysconfig/modules/ipvs.modules
文件,内容如下
1 2 3 4 5 6 7 8 # !/bin/bash ipvs_mods_dir="/usr/lib/modules/$(uname -r)/kernel/net/netfilter/ipvs" for i in $(ls $ipvs_mods_dir | grep -o "^[^.]*"); do /sbin/modinfo -F filename $i &> /dev/null if [ $? -eq 0 ]; then /sbin/modprobe $i fi done
修改文件权限,并加载内核模块,
1 2 sudo chmod +x /etc/sysconfig/modules/ipvs.modules sudo /etc/sysconfig/modules/ipvs.modules
注意该步骤不是必须的,因为ipvs仅负责负载均衡相关任务,它无法完成kube-proxy中的包过滤机SNAT等功能,这些仍需要由iptables实现。也就是说,如果条件不满足,即使kube-proxy开启了ipvs模式,也会回退到iptables模式。
安装Docker
Docker的安装可以参考阿里云的docker-ce的安装方法,目前Kubernetes推荐的最新支持的docker版本为18.06,注意不要使用不兼容的版本。
如果你有阿里云账号,可以参考镜像加速器 方法。
另外,如果想通过外网访问Docker,可以在Systemd加入启动参数配置sock进行访问
1 2 3 4 5 sudo vim /lib/systemd/system/docker.service [Service] ... ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375
其它细节可以参考阿里docker相关文档。
配置外网访问SS
首先,你得先有一个SS,因为大陆内目前访问不了k8s.gcr.io 。虽然有人做了镜像同步,对同步的镜像进行retag也是可行的方式,但我有SS!!
1 2 3 4 5 6 7 8 9 10 11 12 13 sudo yum install python-pip epel-release -y sudo pip install shadowsocks cat > /etc/shadowsocks.json <<EOF { "server":"xx.xx.xx.xx", "server_port": 443, "local_port": 1080, "password":"xxx", "timeout":600, "method":"aes-256-cfb" } EOF sudo sslocal -c /etc/shadowsocks.json -d start
SS主要是配给Docker用的,编辑/lib/systemd/system/docker.service
文件,
1 2 3 Environment="HTTP_PROXY=socks5://127.0.0.1:1080/" Environment="HTTPS_PROXY=socks5://127.0.0.1:1080/" Environment="NO_PROXY=localhost,127.0.0.0/8,guqcep47.mirror.aliyuncs.com"
kubelet,kubeadm,kubectl安装
首先要设定组件的仓储,编辑/etc/yum.repos.d/kubernetes.repo
,内容如下,
1 2 3 4 5 6 7 [kubernetes] name=Kubernetes baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled=1 gpgcheck=0 repo_gpgcheck=1 gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
执行如下命令安装程序包,
1 sudo yum install kubelet kubeadm kubectl
kubernetes自1.8版本开始,强制要求关闭系统swap,编辑kubelet配置文件/etc/sysconfig/kubelet
,忽略禁止使用Swap限制,
1 KUBELET_EXTRA_ARGS="--fail-swap-on=false"
加入启动服务,
1 2 sudo systemctl start kubelet sudo systemctl enable kubelet
集群初始化
集群初始化动作需要在Master进行,然后在其它Node节点使用join
加入,所以这里的命令行需要在各个主机单独敲命令了。
有两种初始化方式,一种是命令带参数方式;另一种是使用配置文件,两种方式是等效的,
1 sudo kubeadm init --kubernetes-version=v1.13.2 --pod-network-cidr=10.244.0.0/16 --service-cidr=10.96.0.0/12 --apiserver-advertise-address=0.0.0.0 --ignore-preflight-errors=Swap
简单说一下这几个重要的参数,
--kubernetes-version
:Kubernete的版本
--pod-network-cidr
:Pod网络地址范围,其值为CIDR格式的网络地址;使用flannel网络插件是,默认地址为10.244.0.0/16。
--service-cidr
:Service的网络地址范围,其值为CIDR格式的网络地址,默认地址为10.96.0.0/12。
--apiserver-advertise-address
:API server通告给其他组件的IP地址,一般应该为Master节点的IP地址,0.0.0.0表示节点上的所有可用地址。
--ignore-preflight-errors
:忽略哪些运行时的错误信息,其值为Swap时,表示忽略因swap未关闭而导致的错误。
一般情况下,都是使用配置文件的方式,可以通过下面的命令查看一份完整的kubeadm配置示例,
1 sudo kubeadm config print init-defaults --component-configs KubeProxyConfiguration
存储输出的内容为kubeadm-config.yaml
,根据自己需求修改,执行命令初始化
1 sudo kubeadm init --config kubeadm-config.yaml --ignore-preflight-errors=Swap
初始化如果成功,会打印两个重要信息,一是
1 2 3 mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOEM/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config
照着写便是,默认情况下kubectl会从当前用户主目录的.kube下的config读取配置信息,包括Kubernetes集群、证书或令牌等。集群初始化时,kubeadm会自动生成一个用于此类功能的配置文件/etc/kubernetes/admin.conf
,将它复制为$HOME/.kube/config
文件即可直接使用。
kubectl有非常多的子命令,其中“get compontsstatuses
”可显示集群组件当前的状态,简写为get cs
:
1 2 3 4 5 6 7 kubectl get cs NAME STATUS MESSAGE ERROR controller-manager Healthy ok scheduler Healthy ok etcd-0 Healthy {"health": "true"}
若命令结果的STATUS字段为“Healthy”,表示组件处于健康运行状态,否则需要检查其错误所在,必要时使用“kubeadm reset”命令重置重新进行初始化。
另外使用kubectl get nodes
,获取集群节点的相关状态信息,例如
1 2 3 4 kubectl get nodes NAME STATUS ROLES AGE VERSION kubernetes-master NotReady master 7d5h v1.13.2
为Kubernetes提供的Pod网络插件非常多,目前流行的有flannel和Calico。flannel运行为Kubernetes集群的附件,它以Pod的形式部署运行与每个集群节点上以接受Kubernetes集群管理。事实上,flannel也可以以守护进程方式运行在各个节点,即以非托管的方式运行。部署命令使kubectl apply
或kubectl crreate
,下面是使用在线的方式进行flannel部署:
1 kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
部署成功会出现created字样,配置flannel网络插件时,Master节点上的Docker首先会去获取flannel镜像文件,而后根据镜像文件启动相应的Pod对象。现在再次查看Master已经变为“Ready”状态:
1 2 3 4 kubectl get nodes NAME STATUS ROLES AGE VERSION kubernetes-master Ready master 7d5h v1.13.2
可通过,
1 kubectl get pods -n kube-system | grep flannel
显示网络插件flannel的Pod状态情况。
集群初始化时,另一个信息是产生一段token信息,这段信息用于Node节点加入Master。
1 sudo kubeadm join kubernetes-master:6443 --token 0qpyy8.iv5v2uhhrjy3wsri --discovery-token-ca-cert-hash sha256:c8ad1e333b6e2e1185ea2ab7beb97b90022f8285e79a1cc6a7e71ad772748f42 --ignore-preflight-errors=Swap
每个节点加入到Master之后,再次通过kubectl get nodes
查看节点信息,
1 2 3 4 5 6 7 kubectl get nodes NAME STATUS ROLES AGE VERSION kubernetes-master Ready master 7d5h v1.13.2 kubernetes-node1 Ready <none> 7d4h v1.13.2 kubernetes-node2 Ready <none> 7d4h v1.13.2 kubernetes-node3 Ready <none> 7d4h v1.13.2
至此,Kubernetes集群的部署已经完成,后续有更多节点加入时,均可使用此方式。
Kubernetes的命令非常多,在文章最前面的大图已经描述清楚。由于这里仅介绍集群安装内容,下面简单了解下关于集群方面的命令,
获取集群信息,
1 2 3 4 5 kubectl cluster-info Kubernetes master is running at https://192.168.50.71:6443 KubeDNS is running at https://192.168.50.71:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
版本信息,
1 2 3 kubectl version --short=true Client Version: v1.13.2 Server Version: v1.13.2
移除节点,
1 2 kubectl drain NODE_ID --delete-local-data --force --ignore-daemonsets kubectl delete node NODE_ID
重置节点,
部署高可用CoreDNS
默认安装的CoreDNS存在单点问题。在Master节点查看kubectl get pods -n kube-system -owide
分布如下,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 kubectl get pods -n kube-system -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES coredns-86c58d9df4-6j2m6 0/1 ContainerCreating 0 90m <none> master.kubernetes.io <none> <none> coredns-86c58d9df4-glvnp 0/1 ContainerCreating 0 90m <none> master.kubernetes.io <none> <none> etcd-master.kubernetes.io 1/1 Running 2 89m 192.168.50.71 master.kubernetes.io <none> <none> kube-apiserver-master.kubernetes.io 1/1 Running 2 89m 192.168.50.71 master.kubernetes.io <none> <none> kube-controller-manager-master.kubernetes.io 1/1 Running 2 89m 192.168.50.71 master.kubernetes.io <none> <none> kube-flannel-ds-amd64-czh7z 1/1 Running 0 80m 192.168.50.73 node02.kubernetes.io <none> <none> kube-flannel-ds-amd64-gcqkk 1/1 Running 0 80m 192.168.50.74 node03.kubernetes.io <none> <none> kube-flannel-ds-amd64-lk5dw 1/1 Running 0 81m 192.168.50.72 node01.kubernetes.io <none> <none> kube-flannel-ds-amd64-xp5xf 0/1 PodInitializing 0 83m 192.168.50.71 master.kubernetes.io <none> <none> kube-proxy-b82sn 1/1 Running 0 80m 192.168.50.73 node02.kubernetes.io <none> <none> kube-proxy-ql6hp 1/1 Running 0 81m 192.168.50.72 node01.kubernetes.io <none> <none> kube-proxy-sh87s 1/1 Running 0 80m 192.168.50.74 node03.kubernetes.io <none> <none> kube-proxy-w2kv4 0/1 ContainerCreating 0 90m 192.168.50.71 master.kubernetes.io <none> <none> kube-scheduler-master.kubernetes.io 1/1 Running 2 89m 192.168.50.71 master.kubernetes.io <none> <none>
删除原来的单点CoreDNS,
1 kubectl delete deploy coredns -n kube-system
创建一份多实例配置coredns-ha.yaml,内容如下,
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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 apiVersion: apps/v1 kind: Deployment metadata: labels: k8s-app: kube-dns name: coredns namespace: kube-system spec: replicas: 2 selector: matchLabels: k8s-app: kube-dns strategy: rollingUpdate: maxSurge: 25 % maxUnavailable: 1 type: RollingUpdate template: metadata: labels: k8s-app: kube-dns spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: k8s-app operator: In values: - kube-dns topologyKey: kubernetes.io/hostname containers: - args: - -conf - /etc/coredns/Corefile image: registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:1.2.6 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 5 httpGet: path: /health port: 8080 scheme: HTTP initialDelaySeconds: 60 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 5 name: coredns ports: - containerPort: 53 name: dns protocol: UDP - containerPort: 53 name: dns-tcp protocol: TCP - containerPort: 9153 name: metrics protocol: TCP resources: limits: memory: 170Mi requests: cpu: 100m memory: 70Mi securityContext: allowPrivilegeEscalation: false capabilities: add: - NET_BIND_SERVICE drop: - all readOnlyRootFilesystem: true terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /etc/coredns name: config-volume readOnly: true dnsPolicy: Default restartPolicy: Always schedulerName: default-scheduler securityContext: {} serviceAccount: coredns serviceAccountName: coredns terminationGracePeriodSeconds: 30 tolerations: - key: CriticalAddonsOnly operator: Exists - effect: NoSchedule key: node-role.kubernetes.io/master volumes: - configMap: defaultMode: 420 items: - key: Corefile path: Corefile name: coredns name: config-volume
执行,
1 kubectl apply -f coredns-ha.yaml
再次查看分布,
1 2 3 4 5 6 7 kubectl get pods -n kube-system -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES coredns-6c67f849c7-jhqhj 1/1 Running 0 3m6s 10.244.1.3 node01.kubernetes.io <none> <none> coredns-6c67f849c7-kxtkw 1/1 Running 0 3m6s 10.244.3.4 node03.kubernetes.io <none> <none> etcd-master.kubernetes.io 1/1 Running 2 102m 192.168.50.71 master.kubernetes.io <none> <none> kube-apiserver-master.kubernetes.io 1/1 Running 2 102m 192.168.50.71 master.kubernetes.io <none> <none> kube-controller-manager-master.kubernetes.io 1/1 Running 2 102m 192.168.50.71 master.kubernetes.io <none> <none>
CoreDNS的Pod落在节点node1和node3上了。
安装dashboard
Kubernetes dashboard的安装也是在pod上的,所以要在所有节点执行,
1 sudo docker pull k8s.gcr.io/kubernetes-dashboard-amd64:v1.10.1
下载官方推荐的部署文件,
1 wget https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml
因为前面我已经将镜像拉取下来了,所以不必要再拉取,修改这个kubernetes-dashboard.yaml
文件,
1 2 3 4 5 6 7 8 9 spec: containers: - image: k8s.gcr.io/kubernetes-dashboard-amd64:v1.10.1 imagePullPolicy: Never name kubernetes-dashboard ports: - containerPort: 8443 dnsPolicy: ClusterFirst restartPolicy: Always
另外,需要暴露端口以给集群外部访问,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 kind: Service apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kube-system spec: type: NodePort ports: - port: 443 targetPort: 8443 nodePort: 31234 selector: k8s-app: kubernetes-dashboard
OK,执行命令部署pod,
1 kubectl create -f kubernetes-dashboard.yaml
查看一下dashboard的pod是否正常启动,如果正常,说明安装成功,
1 kubectl get pods -n kube-system -owide
查看外网暴露的端口,
1 2 3 4 kubectl get services -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 7d5h kubernetes-dashboard NodePort 10.104.42.80 <none> 443:31234/TCP 138m
默认情况下,kubeadm创建集群时已经创建了admin角色,我们直接绑定即可,
创建一个admin-user-role-binding.yaml
文件,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: admin annotations: rbac.authorization.kubernetes.io/autoupdate: "true" roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io subjects: - kind: ServiceAccount name: admin namespace: kube-system --- apiVersion: v1 kind: ServiceAccount metadata: name: admin namespace: kube-system labels: kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile
执行,
1 kubectl create -f admin-user-role-binding.yaml
获取token,
1 kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')
把Token复制的登录界面,即可。
这里登录时有个问题,就是HTTPS访问没有证书,chrome直接不让访问!Firefox还好可以绕过。所以需要自己加一个证书,
首先生成私钥和证书签名,
1 2 grep 'client-key-data' ~/.kube/config | head -n 1 | awk '{print $2}' | base64 -d >> dashboard.key grep 'client-certificate-data' ~/.kube/config | head -n 1 | awk '{print $2}' | base64 -d >> dashboard.crt
生成证书,
1 openssl pkcs12 -export -clcerts -inkey dashboard.key -in dashboard.crt -out dashboard.p12 -name "kubernetes-client"
将生成的dashboard.key
和dashboard.crt
放置在路径/certs
下, 重新配置
1 2 kubectl create -f kubernetes-dashboard.yaml kubectl create -f admin-user-role-binding.yaml
虽然添加了证书,但也仅能通过firefox添加例外访问,chrome根本不信任你。所以生产环境上还是要买一个SSL证书,另外Pod内的service最好也不要直接暴露给外网访问,一般用nginx-ingress做个代理。