k8s
原生Kubernetes云平台部署
1.部署架构
Kubernetes(简称K8S)是开源的容器集群管理系统,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。它既是一款容器编排工具,也是全新的基于容器技术的分布式架构领先方案。在Docker技术的基础上,为容器化的应用提供部署运行、资源调度、服务发现和动态伸缩等功能,提高了大规模容器集群管理的便捷性。
K8S集群中有管理节点与工作节点两种类型,部署架构如图7-5-1所示。管理节点主要负责K8S集群管理,集群中各节点间的信息交互、任务调度,还负责容器、Pod、NameSpaces、PV等生命周期的管理。工作节点主要为容器和Pod提供计算资源,Pod及容器全部运行在工作节点上,工作节点通过Kubelet服务与管理节点通信以管理容器的生命周期,并与集群其他节点进行通。
图7-5-1 Kubernetes部署架构
2.规划节点
Kubernetes集群各节点的规划见表7-5-1。
表7-5-1 规划节点
IP地址 |
主机名 |
节点 |
10.18.4.33 |
master |
Master节点 |
10.18.4..42 |
node |
Node节点 |
3.基础准备
所有节点安装CentOS_7.2.1511系统,配置网卡和主机名。
7.5.3 案例实施
1.基础环境配置
(1)配置YUM源
所有节点将提供的压缩包K8S.tar.gz上传至/root目录并解压。
# tar -zxvf K8S.tar.gz
所有节点配置本地YUM源。
# cat /etc/yum.repod.s/local.repo
[kubernetes]
name=kubernetes
baseurl=file:///root/Kubernetes
gpgcheck=0
enabled=1
(2)升级系统内核
所有节点升级系统内核。
# yum upgrade -y
(3)配置主机映射
所有节点,修改/etc/hosts文件。
# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
10.18.4.33 master
10.18.4.42 node
(4)配置防火墙及SELinux
所有节点配置防火墙及SELinux。
# systemctl stop firewalld && systemctl disable firewalld
# iptables -F
# iptables -X
# iptables -Z
# /usr/sbin/iptables-save
# sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
# reboot
(5)关闭Swap
Kubernetes的想法是将实例紧密包装到尽可能接近100%。所有的部署应该与CPU和内存限制固定在一起。所以如果调度程序发送一个Pod到一台机器,它不应该使用交换。设计者不想交换,因为它会减慢速度。所以关闭Swap主要是为了性能考虑。
所有节点关闭Swap。
# swapoff -a
# sed -i "s/\/dev\/mapper\/centos-swap/\#\/dev\/mapper\/centos-swap/g" /etc/fstab
(5)配置时间同步
所有节点安装chrony服务。
# yum install -y chrony
Master节点修改/etc/chrony.conf文件,注释默认NTP服务器,指定上游公共NTP服务器,并允许其他节点同步时间。
[root@master ~]# sed -i 's/^server/#&/' /etc/chrony.conf
[root@master ~]# cat >> /etc/chrony.conf << EOF
local stratum 10
server master iburst
allow all
EOF
Master节点重启chronyd服务并设为开机启动,开启网络时间同步功能。
[root@master ~]# systemctl enable chronyd && systemctl restart chronyd
[root@master ~]# timedatectl set-ntp true
Node节点修改/etc/chrony.conf文件,指定内网Master节点为上游NTP服务器,重启服务并设为开机启动。
[root@node ~]# sed -i 's/^server/#&/' /etc/chrony.conf
[root@node ~]# echo server 10.18.4.33 iburst >> /etc/chrony.conf //IP为master节点地址
[root@node ~]# systemctl enable chronyd && systemctl restart chronyd
所有节点执行chronyc sources命令,查询结果中如果存在以“^*”开头的行,即说明已经同步成功。
# chronyc sources
210 Number of sources = 1
MS Name/IP address Stratum Poll Reach LastRx Last sample
==================================================================
^* master 10 6 77 7 +13ns[-2644ns] +/- 13us
(6)配置路由转发
RHEL/CentOS7上的一些用户报告了由于iptables被绕过而导致流量路由不正确的问题,所以需要在各节点开启路由转发。
所有节点创建/etc/sysctl.d/K8S.conf文件,添加如下内容。
# cat << EOF | tee /etc/sysctl.d/K8S.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
# modprobe br_netfilter
# sysctl -p /etc/sysctl.d/K8S.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
(7)配置IPVS
由于IPVS已经加入到了内核主干,所以需要加载以下内核模块以便为kube-proxy开启IPVS功能。
在所有节点执行以下操作。
# 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
上面脚本创建了/etc/sysconfig/modules/ipvs.modules文件,保证在节点重启后能自动加载所需模块。使用lsmod | grep -e ip_vs -e nf_conntrack_ipv4命令查看是否已经正确加载所需的内核模块。
# lsmod | grep -e ip_vs -e nf_conntrack_ipv4
nf_conntrack_ipv4 15053 0
nf_defrag_ipv4 12729 1 nf_conntrack_ipv4
ip_vs_sh 12688 0
ip_vs_wrr 12697 0
ip_vs_rr 12600 0
ip_vs 145497 6 ip_vs_rr,ip_vs_sh,ip_vs_wrr
nf_conntrack 139224 2 ip_vs,nf_conntrack_ipv4
libcrc32c 12644 3 xfs,ip_vs,nf_conntrack
所有节点安装ipset软件包。
# yum install ipset ipvsadm -y
(8)安装Docker
Kubernetes默认的容器运行时仍然是Docker,使用的是Kubelet中内置dockershim CRI实现。需要注意的是,在Kubernetes1.14的版本中,支持的版本有1.13.1、17.03、17.06、17.0918.06和18.09,案例统一使用Docker 18.09版本。
所有节点安装Docker,启动Docker引擎并设置开机自启。
# yum install -y yum-utils device-mapper-persistent-data lvm2
# yum install docker-ce-18.09.6 docker-ce-cli-18.09.6 containerd.io -y
# mkdir -p /etc/docker
# tee /etc/docker/daemon.json <<-'EOF'
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
# systemctl daemon-reload
# systemctl restart docker
# systemctl enable docker
# docker info |grep Cgroup
Cgroup Driver: system
2.安装Kubernetes集群
(1)安装工具
Kubelet负责与其他节点集群通信,并进行本节点Pod和容器生命周期的管理。Kubeadm是Kubernetes的自动化部署工具,降低了部署难度,提高效率。Kubectl是Kubernetes集群命令行管理工具。
所有节点安装Kubernetes工具并启动Kubelet。
# yum install -y kubelet-1.14.1 kubeadm-1.14.1 kubectl-1.14.1
# systemctl enable kubelet && systemctl start kubelet
// 此时启动不成功正常,后面初始化的时候会变成功
(2)初始化Kubernetes集群
登录Master节点,初始化Kubernetes集群。
[root@master ~]# ./kubernetes_base.sh
[root@master ~]# kubeadm init --apiserver-advertise-address 10.18.4.33 --kubernetes-version="v1.14.1" --pod-network-cidr=10.16.0.0/16 --image-repository=registry.aliyuncs.com/google_containers
[init] Using Kubernetes version: v1.14.1
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Activating the kubelet service
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [master kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 10.18.4.33]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [master localhost] and IPs [10.18.4.33 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [master localhost] and IPs [10.18.4.33 127.0.0.1 ::1]
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 25.502670 seconds
[upload-config] storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.14" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --experimental-upload-certs
[mark-control-plane] Marking the node master as control-plane by adding the label "node-role.kubernetes.io/master=''"
[mark-control-plane] Marking the node master as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: i9k9ou.ujf3blolfnet221b
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 10.18.4.33:6443 --token i9k9ou.ujf3blolfnet221b \
--discovery-token-ca-cert-hash sha256:a0402e0899cf798b72adfe9d29ae2e9c20d5c62e06a6cc6e46c93371436919dc
初始化操作主要经历了下面15个步骤,每个阶段均输出均使用[步骤名称]作为开头:
①[init]:指定版本进行初始化操作。
②[preflight]:初始化前的检查和下载所需要的Docker镜像文件。
③[kubelet-start]:生成Kubelet的配置文件/var/lib/kubelet/config.yaml,没有这个文件Kubelet无法启动,所以初始化之前的Kubelet实际上启动失败。
④[certificates]:生成Kubernetes使用的证书,存放在/etc/kubernetes/pki目录中。
⑤[kubeconfig]:生成KubeConfig文件,存放在/etc/kubernetes目录中,组件之间通信需要使用对应文件。
⑥[control-plane]:使用/etc/kubernetes/manifest目录下的YAML文件,安装Master组件。
⑦[etcd]:使用/etc/kubernetes/manifest/etcd.yaml安装Etcd服务。
⑧[wait-control-plane]:等待control-plan部署的Master组件启动。
⑨[apiclient]:检查Master组件服务状态。
⑩[uploadconfig]:更新配置。
11[kubelet]:使用configMap配置Kubelet。
12[patchnode]:更新CNI信息到Node上,通过注释的方式记录。
13[mark-control-plane]:为当前节点打标签,打了角色Master和不可调度标签,这样默认就不会使用Master节点来运行Pod。
14[bootstrap-token]:生成的Token需要记录下来,后面使用kubeadm join命令往集群中添加节点时会用到。
15[addons]:安装附加组件CoreDNS和kube-proxy。
输出结果中的最后一行用于其它节点加入集群。
Kubectl默认会在执行的用户home目录下面的.kube目录中寻找config文件,配置kubectl工具。
[root@master ~]# mkdir -p $HOME/.kube
[root@master ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
检查集群状态。
[root@master ~]# kubectl get cs
NAME STATUS MESSAGE ERROR
scheduler Healthy ok
controller-manager Healthy ok
etcd-0 Healthy {"health":"true"}
(3)配置Kubernetes网络
登录Master节点,部署flannel网络。
[root@master ~]# kubectl apply -f yaml/kube-flannel.yml
[root@master ~]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-8686dcc4fd-v88br 0/1 Running 0 4m42s
coredns-8686dcc4fd-xf28r 0/1 Running 0 4m42s
etcd-master 1/1 Running 0 3m51s
kube-apiserver-master 1/1 Running 0 3m46s
kube-controller-manager-master 1/1 Running 0 3m48s
kube-flannel-ds-amd64-6hf4w 1/1 Running 0 24s
kube-proxy-r7njz 1/1 Running 0 4m42s
kube-scheduler-master 1/1 Running 0 3m37s
(4)Node节点加入集群
登录Node节点,使用kubeadm join命令将Node节点加入集群。
[root@master ~]# ./kubernetes_base.sh
[root@node ~]# kubeadm join 10.18.4.33:6443 --token qf4lef.d83xqvv00l1zces9 --discovery-token-ca-cert-hash sha256:ec7c7db41a13958891222b2605065564999d124b43c8b02a3b32a6b2ca1a1c6c
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.14" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Activating the kubelet service
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
登录Master节点,检查各节点状态。
[root@master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 4m53s v1.14.1
node Ready <none> 13s v1.14.1
(5)安装Dashboard
使用kubectl create命令安装Dashboard。
[root@master ~]# kubectl create -f yaml/kubernetes-dashboard.yaml
创建管理员。
[root@master ~]# kubectl create -f yaml/dashboard-adminuser.yaml
serviceaccount/kubernetes-dashboard-admin created
clusterrolebinding.rbac.authorization.K8S.io/kubernetes-dashboard-admin created
检查所有Pod状态。
[root@master ~]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-8686dcc4fd-8jqzh 1/1 Running 0 11m
coredns-8686dcc4fd-dkbhw 1/1 Running 0 11m
etcd-master 1/1 Running 0 11m
kube-apiserver-master 1/1 Running 0 11m
kube-controller-manager-master 1/1 Running 0 11m
kube-flannel-ds-amd64-49ssg 1/1 Running 0 7m56s
kube-flannel-ds-amd64-rt5j8 1/1 Running 0 7m56s
kube-proxy-frz2q 1/1 Running 0 11m
kube-proxy-xzq4t 1/1 Running 0 11m
kube-scheduler-master 1/1 Running 0 11m
kubernetes-dashboard-5f7b999d65-djgxj 1/1 Running 0 11m
查看Dashboard端口号。
[root@master ~]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 15m
kubernetes-dashboard NodePort 10.102.195.101 <none> 443:30000/TCP 4m43s
可以查看到kubernetes-dashboard对外暴露的端口号为30000,在Firefox浏览器中输入地址(https://10.18.4.33:30000),即可访问Kubernetes Dashboard,如图7-5-2所示。
图7-5-2 Kubernetes Dashboard首次登录
单击“接受风险并继续”按钮,即可进入Kubernetes Dasboard认证界面,如图7-5-3所示。
图7-5-3 Kubernetes Dashboard认证界面
登录Kubernetes Dasboard需要输入令牌,通过以下命令获取访问Dashboard的认证令牌。
[root@master ~]# kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep kubernetes-dashboard-admin-token | awk '{print $1}')
Name: kubernetes-dashboard-admin-token-j5dvd
Namespace: kube-system
Labels: <none>
Annotations: kubernetes.io/service-account.name: kubernetes-dashboard-admin
kubernetes.io/service-account.uid: 1671a1e1-cbb9-11e9-8009-ac1f6b169b00
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1025 bytes
namespace: 11 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZC1hZG1pbi10b2tlbi1qNWR2ZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZC1hZG1pbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjE2NzFhMWUxLWNiYjktMTFlOS04MDA5LWFjMWY2YjE2OWIwMCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTprdWJlcm5ldGVzLWRhc2hib2FyZC1hZG1pbiJ9.u6ZaVO-WR632jpFimnXTk5O376IrZCCReVnu2Brd8QqsM7qgZNTHD191Zdem46ummglbnDF9Mz4wQBaCUeMgG0DqCAh1qhwQfV6gVLVFDjHZ2tu5yn0bSmm83nttgwMlOoFeMLUKUBkNJLttz7-aDhydrbJtYU94iG75XmrOwcVglaW1qpxMtl6UMj4-bzdMLeOCGRQBSpGXmms4CP3LkRKXCknHhpv-pqzynZu1dzNKCuZIo_vv-kO7bpVvi5J8nTdGkGTq3FqG6oaQIO-BPM6lMWFeLEUkwe-EOVcg464L1i6HVsooCESNfTBHjjLXZ0WxXeOOslyoZE7pFzA0qg
将获取到的令牌输入浏览器,认证后即可进入Kubernetes控制台,如图7-5-4所示。
图7-5-4 Kubernetes控制台
(6)配置Kuboard
Kuboard是一款免费的Kubernetes图形化管理工具,其力图帮助用户快速在Kubernetes上落地微服务。登录Master节点,使用kuboard.yaml文件部署Kuboard。
[root@master ~]# kubectl create -f yaml/kuboard.yaml
deployment.apps/kuboard created
service/kuboard created
serviceaccount/kuboard-user created
clusterrolebinding.rbac.authorization.K8S.io/kuboard-user created
serviceaccount/kuboard-viewer created
clusterrolebinding.rbac.authorization.K8S.io/kuboard-viewer created
clusterrolebinding.rbac.authorization.K8S.io/kuboard-viewer-node created
clusterrolebinding.rbac.authorization.K8S.io/kuboard-viewer-pvp created
ingress.extensions/kuboard created
在浏览器中输入地址http://10.18.4.33:31000,即可进入Kuboard的认证界面,如图7-5-5所示,在Token文本框中输入令牌后可进入Kuboard控制台。
图7-5-5 Kuboard认证界面
图7-5-6 Kuboard控制台
如图7-5-6所示,在Kuboard控制台中可以查看到集群概览,至此Kubernetes容器云平台就部署完成了。
3.配置Kubernetes集群
(1)开启IPVS
IPVS是基于TCP四层(IP+端口)的负载均衡软件。
IPVS会从TCPSYNC包开始为一个TCP连接所有的数据包,建立状态跟踪机制,保证一个TCP连接中所有的数据包能到同一个后端。所以IPVS是基于TCP状态机进行控制管理,只感知TCP头而不对TCP的payload进行查看。因此,对IPVS后端服务器集群还有一个假定,那就是所有后端都具有同样的应用层的服务功能,但是由于IPVS可以给后端设定权重,所以每个后端的服务的能力可以不同。
一个合理的负载均衡软件,应该能够尽量提高服务接入能力(ReqPS,Request Per Second),而将服务处理能力(ResPS,Response Per second)通过合理分配后端来达到最佳。
根据处理请求和响应数据包的模式的不同,IPVS具有如下4种工作模式:
①NAT模式
②DR(Direct Routing)模式
③TUN(IP Tunneling)模式
④FULLNAT模式
而根据响应数据包返回路径的不同,可以分为如下2种模式:
①双臂模式:请求、转发和返回在同一路径上,client和IPVS director、IPVS director和后端real server都是由请求和返回2个路径连接。
②三角模式:请求、转发和返回3个路径连接client、IPVS director和后端real server成为一个三角形。
登录Master节点,修改ConfigMap的kube-system/kube-proxy中的config.conf文件,修改为mode: "ipvs"。
[root@master ~]# kubectl edit cm kube-proxy -n kube-system
ipvs:
excludeCIDRs: null
minSyncPeriod: 0s
scheduler: ""
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: "ipvs" //修改此处
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
resourceContainer: /kube-proxy
udpIdleTimeout: 250ms
(2)重启kube-proxy
[root@master ~]# kubectl get pod -n kube-system | grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}'
pod "kube-proxy-bd68w" deleted
pod "kube-proxy-qq54f" deleted
pod "kube-proxy-z9rp4" deleted
由于已经通过ConfigMap修改了kube-proxy的配置,所以后期增加的Node节点,会直接使用IPVS模式。查看日志。
[root@master ~]# kubectl logs kube-proxy-9zv5x -n kube-system
I1004 07:11:17.538141 1 server_others.go:177] Using ipvs Proxier. #正在使用ipvs
W1004 07:11:17.538589 1 proxier.go:381] IPVS scheduler not specified, use rr by default
I1004 07:11:17.540108 1 server.go:555] Version: v1.14.1
I1004 07:11:17.555484 1 conntrack.go:52] Setting nf_conntrack_max to 524288
I1004 07:11:17.555827 1 config.go:102] Starting endpoints config controller
I1004 07:11:17.555899 1 controller_utils.go:1027] Waiting for caches to sync for endpoints config controller
I1004 07:11:17.555927 1 config.go:202] Starting service config controller
I1004 07:11:17.555965 1 controller_utils.go:1027] Waiting for caches to sync for service config controller
I1004 07:11:17.656090 1 controller_utils.go:1034] Caches are synced for service config controller
I1004 07:11:17.656091 1 controller_utils.go:1034] Caches are synced for endpoints config controller
日志中打印出了“Using ipvs Proxier”字段,说明IPVS模式已经开启。
(3)测试IPVS
使用ipvsadm测试,可以查看之前创建的Service是否已经使用LVS创建了集群。
[root@master ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.17.0.1:30099 rr
TCP 172.17.0.1:30188 rr
TCP 172.17.0.1:30301 rr
TCP 172.17.0.1:31000 rr
(4)调度Master节点
出于安全考虑,默认配置下Kubernetes不会将Pod调度到Master节点。查看Master节点Taints字段默认配置:
[root@master ~]# kubectl describe node master
……
CreationTimestamp: Fri, 04 Oct 2019 06:16:45 +0000
Taints: node-role.kubernetes.io/master:NoSchedule //状态为NoSchedule
Unschedulable: false
……
如果希望将K8S-master也当作Node节点使用,可以执行如下命令:
[root@master ~]# kubectl taint node master node-role.kubernetes.io/master-
node/master untainted
[root@master ~]# kubectl describe node master
……
CreationTimestamp: Fri, 04 Oct 2019 06:16:45 +0000
Taints: <none> //状态已经改变
Unschedulable: false
……
可以看到,Master节点的调度状态已经发生改变。
7.6 实战案例——Kubernetes容器云平台的基础使用
7.6.1 案例目标
(1)掌握Kubernetes节点的调度方法。
(2)掌握Kubectl工具的基本使用。
(3)掌握Kubernetes部署并发布简单服务的方法。
7.6.2 案例分析
1.规划节点
Kubernetes集群各节点的规划见表7-6-1。
表7-6-1 规划节点
IP地址 |
主机名 |
节点 |
10.18.4.33 |
master |
Master节点 |
10.18.4.42 |
node |
Node节点 |
2.基础准备
确保Kubernetes集群已部署完成。
7.6.3 案例实施
Kubectl是Kubernetes集群的命令行工具,通过kubectl能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。运行kubectl命令的语法如下:
# kubectl [command] [TYPE] [NAME] [flags]
l comand:指定要对资源执行的操作,例如create、get、describe和delete。
l TYPE:指定资源类型,资源类型需注意大小写。
l NAME:指定资源的名称,名称也是大小写敏感的。如果省略名称,则会显示所有的资源。
l flags:指定可选的参数。
Kubectl常用的命令见表7-6-2。
表7-6-2 Kubectl主要命令
命令 |
类型 |
作用 |
get |
查 |
列出某个类型的下属资源 |
describe |
查 |
查看某个资源的详细信息 |
logs |
查 |
查看某个Pod的日志 |
create |
增 |
新建资源 |
explain |
查 |
查看某个资源的配置项 |
delete |
删 |
删除某个资源 |
edit |
改 |
修改某个资源的配置项 |
apply |
改 |
应用某个资源的配置项 |
1.Kubectl工具的使用
(1)kubectl get(列出资源)
K8S把所有的东西都抽象成了资源,而kubectl get命令就是用来查看这些资源的,最常见的资源就是Pod。
Pod的概念其实和Docker中的容器非常相似。Pod是K8S中的最小工作单位,可以把Pod理解成一个一个的小机器人,而K8S抽象出来的大资源池就是它们的工厂。
那么Pod和Docker容器是什么关系呢,简单来说,Pod将一个或多个Docker容器封装成一个统一的整体进行管理并对外提供服务。
不仅部署的服务要封装成Pod,就连K8S平台自身也是运行在一堆Pod上。
查看一下K8S的Pod的命令如下。
# kubectl get pod -n kube-system
参数“-n”指定了要查看哪个命名空间下的Pod。K8S平台自身所有的Pod都被放置在kube-system命名空间下。
命名空间namespace是K8S中“组”的概念,提供同一服务的Pod应该被放置同一命名空间下,而不是混杂在一起。K8S可以用命名空间来做权限控制和资源隔离。如果不指定的话,Pod将被放置在默认的命名空间default下。
执行了kubectl get pod -n kube-system命令后可以看到以下内容:
[root@master ~]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-8686dcc4fd-v88br 1/1 Running 0 63m
coredns-8686dcc4fd-xf28r 1/1 Running 0 63m
etcd-master 1/1 Running 0 62m
kube-apiserver-master 1/1 Running 0 62m
kube-controller-manager-master 1/1 Running 0 62m
kube-flannel-ds-amd64-6hf4w 1/1 Running 0 58m
kube-flannel-ds-amd64-t5j2k 1/1 Running 0 53m
kube-proxy-9kx9n 1/1 Running 0 53m
kube-proxy-r7njz 1/1 Running 0 63m
kube-scheduler-master 1/1 Running 0 62m
kubernetes-dashboard-5f7b999d65-77q4d 1/1 Running 0 52m
其中每一行就是一个资源,这里看到的资源是Pod,这个列表里包含了K8S在所有节点上运行的Pod,加入的节点越多,那么显示的Pod也就越多。
以下是查询结果中的参数说明:
l NAME:Pod的名字,K8S可以为Pod随机分配一个五位数的后缀。
l READY:Pod中已经就绪的Docker容器的数量,Pod封装了一个或多个Docker容器,此处“1/1”的含义为“就绪1个容器/共计1个容器”。
l STATUS:Pod的当前状态,常见的状态有Running、Error、Pending等。
l RESTART:Pod一共重启了多少次,K8S可以自动重启Pod。
l AGE:Pod启动的时间。
kubectl get可以列出K8S中所有资源,这里只介绍了如何用kubectl获取Pod的列表,还可以获取其它资源列表信息,如get svc(查看服务)、get rs(查看副本控制器)、get deploy(查看部署)等。
如果想要查看更多信息,指定-o wide参数即可,语法如下:
# kubectl get <资源> -n <命名空间> -o wide
加上这个参数之后就可以看到资源的IP和所在节点。
(2)kubectl describe(查看详情)
kubectl describe命令可以用来查看某一资源的具体信息,同样可以使用-n参数指定资源所在的命名空间。
例如,可以用如下命令来查看刚才Pod列表中的某个Pod的详细信息。
# kubectl describe pod kube-flannel-ds-amd64-6hf4w -n kube-system
查询结果中可以看到很多的信息,首先是基本属性,可以在详细信息的开头找到。
Name: kube-flannel-ds-amd64-6hf4w #Pod名称
Namespace: kube-system #所处命名空间
Priority: 0
PriorityClassName: <none>
Node: master/10.18.4.33 #所在节点
Start Time: Tue, 29 Oct 2019 07:19:12 +0000 #启动时间
Labels: app=flannel #标签
controller-revision-hash=8676477c4
pod-template-generation=1
tier=node
Annotations: <none> #注释
Status: Running #当前状态
IP: 10.18.4.33 #所在节点IP
Controlled By: DaemonSet/kube-flannel-ds-amd64 #由哪种资源控制
其中几个比较常用的,例如Node、Labels和Controlled By。通过Node可以快速定位到Pod所处的机器,从而检查该机器是否出现问题或宕机等。通过labels可以检索到该Pod的大致用途及定位。而通过Controlled By可以知道该Pod是由哪种K8S资源创建的,然后就可以使用“kubectl get<资源名>”命令继续查找问题。
在中间部分可以找到Containers段落。该段落详细的描述了Pod中每个Docker容器的信息,常用的比如Image字段,当Pod出现ImagePullBackOff错误的时候就可以查看该字段,确认拉取的是什么镜像。其他的字段名都很通俗,直接翻译即可。
Containers:
kube-flannel:
Container ID: docker://d41165b1f1a5261d813a9fb3c07caadffd0b224e095bb15f3eb1342da0d01c32
Image: quay.io/coreos/flannel:v0.11.0-amd64
Image ID: docker://sha256:ff281650a721f46bbe2169292c91031c66411554739c88c861ba78475c1df894
Port: <none>
Host Port: <none>
Command:
/opt/bin/flanneld
Args:
--ip-masq
--kube-subnet-mgr
State: Running
Started: Tue, 29 Oct 2019 07:19:18 +0000
Ready: True
Restart Count: 0
Limits:
cpu: 100m
memory: 50Mi
Requests:
cpu: 100m
memory: 50Mi
Environment:
POD_NAME: kube-flannel-ds-amd64-6hf4w (v1:metadata.name)
POD_NAMESPACE: kube-system (v1:metadata.namespace)
Mounts:
/etc/kube-flannel/ from flannel-cfg (rw)
/run/flannel from run (rw)
/var/run/secrets/kubernetes.io/serviceaccount from flannel-token-pmrss (ro)
在describe查看详情的时候,最常用的信息获取处就是Event段落,可以在介绍内容的末尾找到它,如下:
Events: <none>
Events字段为<none>就说明该Pod一切正常。当Pod的状态不是Running时,这里一定会有或多或少的问题,然后就可以通过其中的信息分析Pod出现问题的详细原因了。
(3)kubectl logs(查看日志)
如果要查看一个Pod的具体日志,就可以通过“kubectl logs <pod名>”命令来查看。kubectl logs只能查看Pod的日志。通过添加-f参数可以持续查看日志。例如,查看kube-system命名空间中某个flannel Pod的日志。
# kubectl logs -f kube-flannel-ds-amd64-6hf4w -n kube-system
(4)kubectl create(创建资源)
K8S中的所有东西都可以通过kubectl create命令创建,无论是创建一个Pod还是一个大型的滚动升级服务Deployment,create命令都可以做到。使用create生成一个资源主要有两种常用方法,从YAML配置文件创建和简易创建。
如果想让K8S生成一个和预期一模一样的资源,那就要充分而详细地描述这个资源,K8S就提供了这么一个方法,可以使用YAML文件按照K8S指定好的结构定义一个对象,然后使用如下方法将该文件传递给K8S。
# kubectl create -f <配置文件名.yaml>
例如,使用下面的配置文件就可以创建一个最简单的Pod。
[root@master ~]# cat kubia.yaml
apiVersion: v1
kind: Pod
metadata:
name: kubia-manual
spec:
containers:
- image: luksa/kubia
name: kubia
ports:
- containerPort: 8080
protocol: TCP
然后使用“kubectl create -f kubia.yaml”命令即可创建。
[root@master ~]# kubectl create -f kubia.yaml
pod/kubia-manual created
如果配置文件有问题,那么K8S就会报错,多数错误一般都是拼写导致的。使用YAML文件相对比较复杂,可以先来学习更简单的简易创建模式。
K8S为一些常用的资源提供了简易创建的方法,比如说Service、Namespace、Deployment等,这些方法可以使用“kubectl create <资源类型> <资源名>”的方式创建。例如,创建一个名叫hello-world的命名空间,直接使用下面命令即可:
[root@master ~]# kubectl create namespace hello-world
namespace/hello-world created
(5)kubectl explain(解释配置)
K8S可以通过配置文件来生成资源,而为了尽可能详细地描述资源的模样,K8S提供了数量庞大的配置项。使用explain命令可以快速地了解到某个配置项的作用,其语法如下:
# kubectl explain <配置名>
要了解创建Pod的基本属性都是干什么的,使用kubectl explain pod命令即可。
[root@master ~]# kubectl explain pod
KIND: Pod
VERSION: v1
DESCRIPTION:
Pod is a collection of containers that can run on a host. This resource is
created by clients and scheduled onto hosts.
FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.K8S.io/community/contributors/devel/api-conventions.md#resources
kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.K8S.io/community/contributors/devel/api-conventions.md#types-kinds
metadata <Object>
Standard object's metadata. More info:
https://git.K8S.io/community/contributors/devel/api-conventions.md#metadata
spec <Object>
Specification of the desired behavior of the pod. More info:
https://git.K8S.io/community/contributors/devel/api-conventions.md#spec-and-status
status <Object>
Most recently observed status of the pod. This data may not be up to date.
Populated by the system. Read-only. More info:
https://git.K8S.io/community/contributors/devel/api-conventions.md#spec-and-status
可以看到,输出结果中信息很详细,并且每个解释的最后都附带了一条链接,便于更加深入地进行了解。
(6)kubectl delete(删除资源)
kubectl delete命令用于删除资源,语法如下:
# kubectl delete <资源类型> <资源名>
例如,删除一个名为kubia-manual的Pod。
[root@master ~]# kubectl delete pod kubia-manual
pod "kubia-manual" deleted
如果要删除所有的Pod,命令如下:
# kubectl delete pod --all
如果要删除一切,命令如下:
# kubectl delete all --all
(7)kubectl edit(修改配置)
K8S的每个资源都是通过一个YAML配置文件生成的,哪怕是简易创建的资源,也是K8S从一个默认的配置文件创建而来的。可以使用“kubectl get <资源类型> <资源名> -o yaml”命令查看某个现有资源的配置项。例如,查看kube-proxy-9kx9n的配置项:
# kubectl get pod kube-proxy-9kx9n -n kube-system -o yaml
执行之后就可以看到一个很长的配置列表,使用kubectl edit命令就可以编辑刚才打开的配置列表。
# kubectl edit pod kube-proxy-9kx9n -n kube-system
注意:对于运行中的资源无法修改其名称或类型,可以修改其它属性,例如,将拉取镜像的标签指定为latest。
修改完成后输入“:wq”命令保存即可。
使用“kubectl edit <资源类型> <资源名>”命令可以编辑一个资源的具体配置项,edit命令在实际使用中更偏向于人工修改某个配置项来解决问题。例如,修改镜像地址解决拉取不到镜像的问题。
(8)kubectl apply(应用配置)
kubectl edit可以对某个资源进行简单快捷的修改,但是如果想对资源进行大范围的修改,就可以用到kubectl apply命令。其基本用法如下:
# kubectl apply -f <新配置文件名.yaml>
kubeclt apply可以理解成edit命令的升级版,其最大的区别就是,apply接受一个YAML配置文件,而不是打开一个编辑器去修改。K8S在接受到这个配置文件后,会根据metadata中的元数据来查找目标资源,如果没有的话则直接新建;如果有的话就依次比对配置文件之间有什么不同点,然后更新并应用不同的配置。
这么做的好处有很多,例如,通过kubectl apply -f https://some-network-site/resourse.yaml命令从一个网站上部署了用户的资源,这样当它的管理者更新了这个配置文件后,用户只需要再次执行这个命令,就可以应用更新后的内容了,而不用关心到底修改了哪些配置项。
2.部署并发布简单应用
一旦运行了K8S集群,就可以在其上部署容器化应用程序。为此,需要创建Deployment配置,Deployment指示K8S如何创建和更新应用程序的实例。创建Deployment后,K8S调度组件将应用程序实例提到集群中的各个节点上。
创建应用程序实例后,Kubernetes Deployment Controller会持续监控这些实例。如果托管实例的节点关闭或被删除,则Deployment控制器会替换它,K8S提供了一种自我修复机制来解决机器故障或维护问题,如图7-6-1所示。
图 7-6-1 K8S自我修复机制
(1)创建Deployment
使用kubectl create命令创建一次部署,该部署用于管理Pod。
[root@master ~]# kubectl create deployment my-first-nginx --image=nginx
deployment.apps/my-first-nginx created
命令中nginx为Deployment名称,--image为指定使用的镜像,默认从Docker Hub拉取。
(2)查看Deployment
创建完成后查看Deployment列表。
[root@master ~]# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
my-first-nginx 1/1 1 1 28s
(3)查看Pod
查看Pod运行状态。
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
my-first-nginx-6cbc56bdc4-m6kd9 1/1 Running 0 36s
(4)发布服务
K8S暴露服务有3种方式,分别为LoadBlancer Service、NodePort Service和Ingress。
Ingress的官方定义为管理对外服务到集群内服务之间规则的集合,可以理解为Ingress定义规则来允许进入集群的请求被转发到集群中对应服务上,从而实现服务暴露。Ingress能把集群内Service配置成外网能够访问的URL,流量负载均衡,终止SSL,提供基于域名访问的虚拟主机等等。
LoadBlancer Service是K8S结合云平台的组件,如国外GCE、AWS、国内阿里云等等,使用它向使用的底层云平台申请创建负载均衡器来实现,但它有局限性,对于使用云平台的集群比较方便。
NodePort Service是通过在节点上暴露端口,然后通过将端口映射到具体某个服务上来实现服务暴露,比较直观方便,但是对于集群来说,随着Service不断增加,需要的端口越来越多,很容易出现端口冲突,而且不容易管理。当然对于小规模的集群服务,还是比较不错的。
NodePort服务是引导外部流量服务的最原始方式。NodePort方式会在所有节点上开放一个特定端口,任何发送到该端口的流量都被转发到对应服务。此处案例采用NodePort的方式来暴露Nginx服务。
[root@master ~]# kubectl expose deployment my-first-nginx --port=80 --type=NodePort
service/my-first-nginx exposed
查看Nginx服务对外暴露的端口。
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 19h
my-first-nginx NodePort 10.99.186.60 <none> 80:30551/TCP 44s
暴露服务后,就可以通过“http://任意节点IP:30551”来访问Nginx服务了,如图7-6-2和图7-6-3所示。
图 7-6-2 通过Master节点IP访问Nginx服务
图 7-6-3 通过Node节点IP访问Nginx服务
(5)Pod动态伸缩
在实际生产系统中,经常会遇到某个服务需要扩容的场景,也可能会遇到由于资源紧张或者工作负载降低而需要减少服务实例数量的场景。此时,可以利用K8S的弹性伸缩功能来完成这些任务。
弹性伸缩是指适应负载变化,以弹性可伸缩方式提供资源。反映到K8S中,可根据负载的高低,动态地调整Pod的副本数。目前K8S提供了API接口实现Pod的弹性伸缩,Pod的副本数本来通过Replication Controller进行控制,所以Pod的弹性伸缩就是修改Replication Controller的Pod副本数,可以通过kubectl scale命令来完成。
通过执行如下命令将Nginx Deployment控制的Pod副本数量从初始的1更新为5。
[root@master ~]# kubectl scale deployment my-first-nginx --replicas=5
deployment.extensions/my-first-nginx scaled
执行kubectl get pods命令来验证Pod的副本数量是否增加到5。
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
my-first-nginx-6cbc56bdc4-6qnxj 1/1 Running 0 3m18s
my-first-nginx-6cbc56bdc4-bznzk 1/1 Running 0 3m18s
my-first-nginx-6cbc56bdc4-kghzw 1/1 Running 0 3m18s
my-first-nginx-6cbc56bdc4-m6kd9 1/1 Running 0 15m
my-first-nginx-6cbc56bdc4-tmcxz 1/1 Running 0 3m18s
将--replicas设置为比当前Pod副本数量更小的数字,系统将会“杀掉”一些运行中的Pod,即可实现应用集群缩容。
[root@master ~]# kubectl scale deployment my-first-nginx --replicas=2
deployment.extensions/my-first-nginx scaled
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
my-first-nginx-6cbc56bdc4-m6kd9 1/1 Running 0 17m
my-first-nginx-6cbc56bdc4-tmcxz 1/1 Running 0 5m30s
还可以使用autoscale自动设置在K8S集群中运行的pod数量(水平自动伸缩)。
指定Deployment、ReplicaSet或ReplicationController,并创建已经定义好资源的自动伸缩器。使用自动伸缩器可以根据需要自动增加或减少系统中部署的Pod数量。
[root@master ~]# kubectl autoscale deployment my-first-nginx --min=1 --max=10
horizontalpodautoscaler.autoscaling/my-first-nginx autoscaled
设置后,Pod的副本数就会根据负载在1-10之间自动伸缩。