前言
本人之前的实验环境一直用的是OCP/OKD集群,其搭建在PC Server服务器上,现需要在自己的windows工作站上搭建一个K8S集群,首先想到的是使用CodeReady Container(CRC)来搭建一个OKD集群,但其资源要求实在是太高(4C9G内存),且CRC所需的虚拟化软件hyper-v与virshbox有冲突,鉴于此问题,虽然有minikube可供选择,但本人决定手动搭建一个原生态K8S集群。
本人实验环境为virshbox安装的一台虚拟机,其操作系统版本为Centos Linux 8,CPU 4个,内存4G,磁盘35G,其拥有两块网卡,网卡1连接到NAT网络以便利用宿主机网络连接英特网,而网卡2连接到Host-Only网络,K8S API监听在在此网络上,主机名称为cts.zyl.io。
主机名 | 主机资源 | 网卡1 | 网卡2 |
---|---|---|---|
cts.zyl.io | 4C4G、Disk:35G | Nat网络,dhcp获取ip | Host-only网络,静态配置IP为192.168.110.6 |
安装容器运行时
参考k8s官方文档与cri-o官方文档我们先选择一容器运行时,如作者对docker容器运行时实在不敢兴趣1,故计划安装cri-o轻量级容器运行时,参考下图,作者准备安装k8s 1.18,故选择cri-o 1.18.x。
对于Centos Linux 8操作系统,执行如下命令安装CRI-O容器运行时2:
sudo dnf -y install 'dnf-command(copr)'
sudo dnf -y copr enable rhcontainerbot/container-selinux
sudo curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/CentOS_8/devel:kubic:libcontainers:stable.repo
sudo curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable:cri-o:1.18.repo https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:1.18/CentOS_8/devel:kubic:libcontainers:stable:cri-o:1.18.repo
sudo dnf -y install cri-o
注意:作者当前安装crio-o 1.18.1时遇到一个bug:文件/etc/crio/crio.conf中的conmon路径为 /usr/libexec/crio/conmon
,但实际位置在/usr/bin/conmon
,于是执行如下命令调整配置后重启crio进程。
sed 's|/usr/libexec/crio/conmon|/usr/bin/conmon|' -i /etc/crio/crio.conf
systemctl start crio.service
我们安装crio的管理工具crictl
3,kubeadm
会使用其拉取镜像。
wget -O crictl.tgz https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.18.0/crictl-v1.18.0-linux-amd64.tar.gz
tar -xf crictl.tgz
mv crictl /usr/local/bin
如下所示,我们为docker.io与k8s.gcr.io配置mirror镜像仓库。注意:为k8s.gcr.io配置镜像仓库很重要,虽然我们可设置--image-repository registry.aliyuncs.com/google_containers
来告知kubeinit
从阿里云镜像仓库下载镜像,但本人发现部署Pod时仍使用k8s.gcr.io/pause镜像,故为了避免因此产生报错,此处为其配置Mirror镜像仓库。
cat > /etc/containers/registries.conf <<EOF
unqualified-search-registries = ["docker.io", "quay.io"]
[[registry]]
location = "docker.io"
insecure = false
blocked = false
mirror-by-digest-only = false
prefix = ""
[[registry.mirror]]
location = "docker.mirrors.ustc.edu.cn"
insecure = false
[[registry]]
location = "k8s.gcr.io"
insecure = false
blocked = false
mirror-by-digest-only = false
prefix = ""
[[registry.mirror]]
location = "registry.aliyuncs.com/google_containers"
insecure = false
EOF
参考Network Plugins,当没有为kubelet配置cni网络插件时,其使用的noop插件依赖于Linux bridge(网桥)在容器间传输流量,亦或者如docker、crio4默认的网络插件同样基于网桥传输流量,此时需加载br_netfilter
模块并设置net.bridge.bridge-xxx=1
以便iptables能识别网桥传输的流量。注意:若后续我们选择的SDN网络不依赖Linux网桥传输流量,则此处实际上可忽略,但配置上也没任何问题。
# these persist across reboots.
cat > /etc/modules-load.d/k8s.conf <<EOF
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
# Set up required sysctl params, these persist across reboots.
cat > /etc/sysctl.d/99-kubernetes-cri.conf <<EOF
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sysctl --system
开始安装kubeadm
上节已安装了cri-o容器运行时,本节我们将安装kubeadm,但在此前,我们参考官方文档Installing kubeadm了解一些重要事项,并完善其他所需步骤。
- 虽然对于本文搭建的单节点集群来所此处没任何意义,但为了完整性考虑,我还是记录于此:检查集群各节点,其需具备独立的主机名称、MAC地址及product_uuid(
cat /sys/class/dmi/id/product_uuid
); - 主机需配置防火墙放行一些端口,为了简单起见,关闭主机防火墙:
systemctl stop firewalld
systemctl disable firewalld
- 禁用系统Swap,否则
kubeadm
会报错。
cat > /etc/sysctl.d/99-disable-swap.conf <<EOL
vm.swappiness=0
EOL
sysctl --system
vi /etc/fstab # 注释swap行
- 禁用selinux或设置为permissive,否则
kubeadm
会报错。
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
接着,执行如下命令安装kubelet
、kubeadm
、kubectl
,而后设置kubelet
开机自启动。
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF
yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
systemctl enable --now kubelet
注意:
- 因网络问题,此处yum源我们选择阿里云仓库,而又因当前无EL8仓库,故此处只能选择EL7;
- 此时kubelete会不断重启,但这是正常行为,其等待我们运行
kubeadm
初始化或加入现有k8s集群。
当前kubeadm仅能为docker容器运行时自动检测其cgroup driver,而对于我们配置的crio容器运行时,则需要为其手动配置cgroup驱动。
When using Docker, kubeadm will automatically detect the cgroup driver for the kubelet and set it in the/var/lib/kubelet/config.yaml
file during runtime....
The automatic detection of cgroup driver for other container runtimes like CRI-O and containerd is work in progress.
我们配置的crio的cgroup驱动为systemd而非cgroupfs,按照官方文档所示需配置/var/lib/kubelet/config.yaml文件,于其中设置cgroupDriver: systemd
。
$ cat /etc/crio/crio.conf |grep systemd
cgroup_manager = "systemd"
$ mkdir -p /var/lib/kubelet
$ cat > /var/lib/kubelet/config.yaml <<EOL
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
EOL
$ systemctl daemon-reload
$ systemctl restart kubelet
但是,这里有个问题,当执行kubeadm init
后不会保留cgroupDriver值,从而导致kubelet
调用crio
容器运行时异常,如下所示。
% journalctl -u kubelet -f
... RunPodSandbox from runtime service failed: rpc error: code = Unknown desc = cri-o configured with systemd cgroup manager, but did not receive slice as parent: /kubepods/besteffort/pod6407b05153e245d7313ea88bfb3be36a
鉴于此,虽然官方文档不建议将参数配置于/etc/sysconfig/kubelet、/etc/default/kubelet或/var/lib/kubelet/kubeadm-flags.env中,但我们仍然配置于此文件中:
cat > /etc/sysconfig/kubelet <<EOF
KUBELET_EXTRA_ARGS=--cgroup-driver=systemd
EOF
注意:“安装容器运行时”一节有提到过,即使我们在调用kubeadm init
时通过参数--image-repository
指定镜像仓库地址,但pause镜像仍从k8s.gcr.io获取从而导致异常,为解决此问题,我们在容器运行时为k8s.gcr.io配置了镜像仓库,但还有一种方式可供选择:调整kubelet配置指定pause镜像。
# /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS=--pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.2
创建单控制面板集群
参考官方文档Creating a single control-plane cluster with kubeadm,我们执行如下命令创建一个单节点k8s集群。注意:其中--pod-network-cidr
设置的pod网段不应与现有网段重叠,即网段是当前空闲的,因虚拟机有两块网卡,其默认路由在nat网段上,为了避免集群api server监控到nat所在的网卡上,此处通过--apiserver-advertise-address
指定apiserver使用host-only所在的网段。
kubeadm init \
--apiserver-advertise-address=192.168.110.6 \
--pod-network-cidr=10.254.0.0/16
若命令无报错,成功后将显示如下信息:
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 192.168.110.6:6443 --token nh1erl.d8eh61epm8s4y8oj \
--discovery-token-ca-cert-hash sha256:dce7e5ffc2d3d8662ab48bb1a3eae3fff8e0cbf65784295ac01cf631bbfe5ba1
我们执行如下命令为客户端工具kubectl
配置上下文,其中文件/etc/kubernetes/admin.conf具备整个集群管理员权限。
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
那么此时,我们可通过kubectl
检查集群状态了,如下所示:
# 默认创建了4个命名空间:
$ kubectl get namespaces
NAME STATUS AGE
default Active 54m
kube-node-lease Active 54m
kube-public Active 54m
kube-system Active 54m
# k8s系统组件全部至于kube-system命名空间中
$ kubectl get pod --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-66bff467f8-4hfws 1/1 Running 0 55m
kube-system coredns-66bff467f8-rm5lp 1/1 Running 0 55m
kube-system etcd-cts.zyl.io 1/1 Running 0 56m
kube-system kube-apiserver-cts.zyl.io 1/1 Running 0 56m
kube-system kube-controller-manager-cts.zyl.io 1/1 Running 0 56m
kube-system kube-proxy-zcbjj 1/1 Running 0 55m
kube-system kube-scheduler-cts.zyl.io 1/1 Running 0 56m
注意:etcd、kube-apiserver、kube-controller-manager、kube-scheduler组件为静态模式部署,其部署清单在主机的/etc/kubernetes/manifests目录里,kubelet会自动加载此目录并启动pod。
$ ls -l /etc/kubernetes/manifests/
-rw------- 1 root root 1858 Jun 8 20:33 etcd.yaml
-rw------- 1 root root 2709 Jun 8 20:33 kube-apiserver.yaml
-rw------- 1 root root 2565 Jun 8 20:33 kube-controller-manager.yaml
-rw------- 1 root root 1120 Jun 8 20:33 kube-scheduler.yaml
coredns使用deployment部署,而kube-proxy使用daemonset模式部署:
$ kubectl get ds,deploy -n kube-system
NAME DESIRED NODE SELECTOR AGE
daemonset.apps/kube-proxy 1 ... kubernetes.io/os=linux 60m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coredns 2/2 2 2 60m
coredns部署了两个pod,对于我们单节点实验环境来说没必要,将其设置为1:
kubectl scale deployment/coredns --replicas=1 -n kube-system
集群还缺少SDN网络,我们这里选择Calico,其不但具备高性能,且支持网络策略。参考文档Quickstart for Calico on Kubernetes,当系统配置NetworkManager管理网络时,为避免其影响Calico,配置如下文件告知NM不要管理Calico的网络接口。
cat > /etc/NetworkManager/conf.d/calico.conf <<'EOF'
[keyfile]
unmanaged-devices=interface-name:cali*;interface-name:tunl*
EOF
接着,我们执行如下命令部署Calico。注意:除了直接使用部署清单部署calico外,我们还可通过operator部署,项目地址为tigera/operator。
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
执行如下等待其Pod运行正常:
$ watch kubectl get pods -n kube-system
...
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-76d4774d89-bgt86 1/1 Running 0 15m
calico-node-7gjls 1/1 Running 0 17m
控制节点默认不允许调度Pod,但对于我们的单节点集群来说,为了测试我们必须要让控制节点可调度Pod,故执行如下命令放开此限制:
kubectl taint nodes --all node-role.kubernetes.io/master-
于K8S集群部署应用
我们已搭建了一个最基本的K8S集群,其当前仅含K8S核心控制组件与Calico SDN网络,虽然集群仅有一个单节点,但因我们配置Master可调度Pod,故此时我们已经可部署测试应用了。
当前我们位于default命名空间中,执行如下命令快速部署一个nginx:
kubectl create deployment nginx --image=nginx
此nginx部署仅含一个pod,如下所示:
$ kubectl get pod --show-labels -w
NAME READY STATUS RESTARTS AGE LABELS
nginx-f89759699-lrvzq 1/1 Running 1 15h app=nginx
获取其IP地址并访问:
$ kubectl describe pod -l app=nginx | grep ^IP:
IP: 10.254.40.9
$ curl 10.254.40.9
...
<title>Welcome to nginx!</title>
为deployment创建service:
$ kubectl expose deploy nginx --port=80 --target-port=80
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP 10.106.209.139 <none> 80/TCP 34m
配置proxy使用ipvs模式
参考这里,kube-proxy使用ipvs模式在k8s v1.11时已GA,其相对于iptables具备更高的性能,但并不是说iptables就没有用了,实际上ipvs会与iptables联合起效。
若我们未执行kubeadm init
初始化集群,则可通过如下方式设置kube-proxy使用ipvs模式:
cat > config.yml <<'EOF'
kubeProxy:
config:
featureGates:
SupportIPVSProxyMode: true
mode: ipvs
EOF
kube init --config config.yml
对于已经在用的k8s集群,我们在各节点上执行如下命令加载ipvs模块。注意:本人实验环境发现下述模块已被自动加载,若如此,则无需处理。
# 动态加载模块
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack
# 确保开机时能加载模块
cat > /etc/modules-load.d/ipvs.conf <<'EOF'
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
EOF
更新configmap,将mode调整为ipvs:
$ kubectl edit cm -n kube-system kube-proxy
...
mode: "ipvs" # 默认为""(空)则为iptables模式
...
而后重启kube-proxy,执行如下命令:
kubectl delete pod -n kube-system -l k8s-app=kube-proxy
最后,我们安装ipvsadm工具并可验证service已被ipvs所配置。
$ yum -y install ipvsadm
$ ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.96.0.1:443 rr
-> 192.168.110.6:6443 Masq 1 4 0
...
调整集群CoreDNS配置
对于生产环境,K8S集群节点主机名应可供DNS域名解析出来,否则可能会因主机名无法被解析而造成某些应用异常,如metrics-server,同样,对于创建的Ingress所分配的域名,也应被DNS域名系统正确解析,否则我们不得不手动为每个Ingress配置的主机名修改/etc/hosts文件已做静态配置。
对于本文搭建的测试环境,我们不在单独配置额外的DNS域名系统,而是调整K8S集群的CoreDNS域名系统,如下所示。
参考Custom DNS Entries For Kubernetes文章,我们调整configmap:coredns,于其中添加一个zyl.io的Zonefile,然后于其中添加对cts.zyl.io主机的域名解析以及指定.app.zyl.io的一个DNS Wildcard。
$ kubectl -n kube-system edit cm coredns
...
Corefile: |
.:53 {
...
file /etc/coredns/zone.zyl.io zyl.io
}
zone.zyl.io: |
zyl.io. IN SOA root.zyl.io. root.zyl.io. 2020061113 7200 3600 1W 1D
cts IN A 192.168.110.6
*.app.zyl.io. 300 IN A 192.168.110.6
而后,执行如下命令调整coredns部署,将zone.zyl.io挂载到容器中。
$ kubectl -n kube-system edit deployment coredns
...
volumes:
- configMap:
defaultMode: 420
items:
- key: Corefile
path: Corefile
- key: zone.zyl.io
path: zone.zyl.io
name: coredns
name: config-volume
接着,启动一个包含nslookup、host命令的容器进行测试:
$ kubectl run -it --rm --restart=Never --image-pull-policy='IfNotPresent' \
--image=infoblox/dnstools:latest dnstools
dnstools# host cts
cts.zyl.io has address 192.168.110.6
dnstools# host z.app.zyl.io
z.app.zyl.io has address 192.168.110.6
为了让集群外可使用此dns域名系统,我们可将其通过hostNetwork将端口映射出来:
$ kubectl -n kube-system edit deployment coredns
...
hostNetwork: true
...
$ netstat -an|grep 53|grep udp
udp6 0 0 :::53 :::*
安装附加组件(Add-on)
为了完善集群功能,本章介绍一些基本的附加组件,我们可按需配置,如dashboard对我们测试来说没啥用,而ingress、持久化存储比较有用。
通过Ingress使容器可供集群外访问
通过Ingress我们可将容器端口映射到集群外,参考官方文档Ingress Controllers此处我们选择Traefik控制器。
下面我们使用helm v3来安装traefik,故我们首先安装helm v3:
wget -O helm.tgz https://get.helm.sh/helm-v3.2.3-linux-amd64.tar.gz
tar -xf helm.tgz
mv linux-amd64/helm /usr/local/bin/
chmod 755 /usr/local/bin/helm
参考项目containous/traefik-helm-chart,执行如下命令将其安装到独立的traefik命名空间中。注意:这里我们设置hostNetwork=true
将端口以主机网络映射到集群外,而为了访问dashboard(为了安全原因默认未开放),此处我们设置--api.insecure
参数。
helm repo add traefik https://containous.github.io/traefik-helm-chart
cat > /tmp/values.yaml <<EOF
podDisruptionBudget:
enabled: true
hostNetwork: true
service:
type: ClusterIP
dashboard:
ingressRoute: true
ports:
traefik:
expose: true
additionalArguments:
- "--providers.kubernetesingress.ingressclass=traefik"
- "--log.level=DEBUG"
- "--api.insecure"
- "--serverstransport.insecureskipverify=true"
EOF
helm -n traefik install -f /tmp/values.yaml traefik traefik/traefik
上述命令将于traefik命名空间中安装如下对象,而端口通过hostNetwork映射到集群外部,其中9000为dashboard端口,8000为http端口,8443为https端口。
$ kubectl get pod,svc,ingressroute,deployment -n traefik
NAME READY STATUS RESTARTS AGE
pod/traefik-7474bbc877-m9c52 1/1 Running 0 2m35s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) ...
service/traefik ClusterIP 10.111.109.186 <none> 9000/TCP,80/TCP,443/TCP
NAME AGE
ingressroute.traefik.containo.us/traefik-dashboard 2m35s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/traefik 1/1 1 1 2m35s
通过主机的9000端口我们打开dashboard控制台,其是一个很简洁的控制台,可用于查看一些状态信息。
现在,我们为在default命名空间中部署的nginx创建一个ingress对象,我们通过注释kubernetes.io/ingress.class指定由traefik解析。
kubectl -n default apply -f - <<EOF
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginx
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: nginx.app.zyl.io
http:
paths:
- path: /
backend:
serviceName: nginx
servicePort: 80
EOF
此处我们设置ingress主机名称为nginx.app.zyl.io,那么我们在主机/etc/hosts为其配置一个dns条目指向主机ip地址。
$ cat >> /etc/hosts <<EOF
192.168.110.6 nginx.app.zyl.io
EOF
$ curl nginx.app.zyl.io:8000
...
<title>Welcome to nginx!</title>
...
$ curl 192.168.110.6:8000
404 page not found
快速切换集群上下文与命名空间
kubectl
每次切换命名空间均需附带-n <namespace>
参数,对于使用惯了oc
命令的用户来说,这样实在是太不方便了,幸好有牛人开发了一个ahmetb/kubectx工具,我们可用此来快速切换集群上下文与命名空间。
安装方法有两种,方法一:通过kubectl插件包管理工具krew安装;方法二:手动安装。
- 方法1:通过krew安装插件方法,参考这里我们先安装krew:
# 安装git
yum -y install git
# 安装krew
(
set -x; cd "$(mktemp -d)" &&
curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/krew.{tar.gz,yaml}" &&
tar zxvf krew.tar.gz &&
KREW=./krew-"$(uname | tr '[:upper:]' '[:lower:]')_amd64" &&
"$KREW" install --manifest=krew.yaml --archive=krew.tar.gz &&
"$KREW" update
)
# 配置环境变量:
cat > .bashrc <<'EOF'
export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
EOF
而后我们执行kubectl krew search
验证krew是否运行,并检查有哪些plugin可安装,如下所示:
$ kubectl krew search
NAME DESCRIPTION INSTALLED
access-matrix Show an RBAC access matrix for server resources no
advise-psp Suggests PodSecurityPolicies for cluster. no
apparmor-manager Manage AppArmor profiles for cluster. no
...
接着,我们执行如下命令安装ctx(切换context上下文)与ns(切换命名空间):
kubectl krew install ctx
kubectl krew install ns
安装完成后,我们则可以插件方式运行kubectl
了,如下所示,我们使用ns快速切换命名空间。
$ kubectl ns # 有哪些namespace
default
kube-node-lease
kube-public
kube-system
traefik
$ kubectl ns traefik # 切换到traefik命名空间
Context "kubernetes-admin@kubernetes" modified.
Active namespace is "traefik".
$ kubectl get pod # 此时已位于traefik命名空间
NAME READY STATUS RESTARTS AGE
nginx-f89759699-254tt 1/1 Running 0 130m
$ kubectl ns - # 切换回原命名空间
- 方法二:手动安装。
git clone https://github.com/ahmetb/kubectx.git ~/.kubectx
COMPDIR=$(pkg-config --variable=completionsdir bash-completion)
ln -sf ~/.kubectx/completion/kubens.bash $COMPDIR/kubens
ln -sf ~/.kubectx/completion/kubectx.bash $COMPDIR/kubectx
cat << FOE >> ~/.bashrc
#kubectx and kubens
export PATH=~/.kubectx:\$PATH
FOE
如果我们结合fzf则可以交互模式运行上述命令,执行如下命令安装fzf。
# 安装fzf
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
配置一个NFS持久化存储系统
很多场景,我们的应用需要持久化数据,为了简单考虑,我们选择使用nfs-server-provisioner搭建一个持久化的NFS存储系统,此存储系统一般没法用于生产,但对于我们测试来说足够了。
首先,我们需要安装helm v3而后执行如下命令添加helm仓库。
$ helm repo add stable https://kubernetes-charts.storage.googleapis.com
"stable" has been added to your repositories
对于作者的单节点K8S集群,我们需执行如下命令确保如下标签(labels)值,我们将用其设置部署应用的节点选择器(nodeSelector)。
% kubectl get node --show-labels
NAME STATUS ROLES AGE VERSION LABELS
cts.zyl.io Ready master 43h v1.18.3 kubernetes.io/hostname=cts.zyl.io,...
准备一个/app/nfs/0目录供本节搭建的nfs服务所用,如作者准备的目录大小仅10G左右。
mkdir -p /app/nfs/0
创建如下文件,其中,我们指定将pod调度到cts.zyl.io主机,指定pod副本数为1,为保证nfs提供的存储卷是持久化的,我们需为nfs server启用持久化卷(persistence.enabled:true),虽然上面准备的目录才10G大小,但此处仍可设置超过10G的大小(200Gi),指定创建的存储卷为默认存储卷(storageClass.defaultClass:true),这样若pvc未明确指定storageClass将使用此存储类。
cat > /tmp/values.yaml <<'EOF'
replicaCount: 1
persistence:
enabled: true
storageClass: "-"
size: 200Gi
storageClass:
defaultClass: true
nodeSelector:
kubernetes.io/hostname: cts.zyl.io
EOF
执行如下命令将nfs server部署到一个独立的命名空间nfs-server-provisioner中:
kubectl create namespace nfs-server-provisioner
helm -n nfs-server-provisioner install \
-f /tmp/values.yaml nfs-server-provisioner stable/nfs-server-provisioner
如下所示,部署清单中所需的持久化卷声明(pvc)此时处于Pending状态,此时我们为主机的/app/nfs/0创建一个持久化卷(pv),而后关联到此pvc上。
$ oc get pvc
NAME STATUS VOLUME CAPACITY ...
data-nfs-server-provisioner-0 Pending
$ kubectl -n nfs-server-provisioner create -f - <<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
name: data-nfs-server-provisioner-0
spec:
capacity:
storage: 200Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /app/nfs/0
claimRef:
kind: PersistentVolumeClaim
name: data-nfs-server-provisioner-0
namespace: nfs-server-provisioner
EOF
待pod启动完成后,我们可发现集群上创建了一名为nfs的默认存储类,主机目录/app/data/0存在一些文件。
$ oc get pod
NAME READY STATUS RESTARTS AGE
nfs-server-provisioner-0 1/1 Running 0 3s
$ oc get storageclass
NAME PROVISIONER RECLAIMPOLICY ...
nfs (default) cluster.local/nfs-server-provisioner Delete ...
$ ls /app/nfs/0/
ganesha.log nfs-provisioner.identity v4old v4recov vfs.conf
最后,让我们创建一pvc来验证通过此存储能为我们自动创建持久化卷pv,换句话说,此存储类是自动提供的,我们仅声明需要多大的存储,而底层存储卷由存储系统为我们自动创建好。
$ kubectl -n default create -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
EOF
$ kubectl -n default get pvc
NAME STATUS VOLUME CAPACITY ...
test-pvc Bound pvc-ae8f8a8b-9700-494f-ad9f-259345918ec4 10Gi ...
$ kubectl get pv
NAME CAPACITY ACCESS ... STORAGECLASS
pvc-ae8f8a8b-... 10Gi RWO ... nfs
接着,我们执行kubectl -n default delete pvc test-pvc
,而后再去观察pv可发现其被自动释放了。
安装K8S原始Dashboard控制台
本节安装k8s原生dashboard,这里使用helm安装,安装方法见这里,首先我们添加helm仓库:
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
helm repo add stable https://kubernetes-charts.storage.googleapis.com
参考Kubernetes monitoring architecture,若要在控制台上显示Pod的Cpu与Memory信息,则我们须安装Metrics Server5,但若我们选择使用coreos/kube-prometheus监控集群,则可禁用metrics-server,因其已包含了metrics-server的功能。
The kube-prometheus stack includes a resource metrics API server, so the metrics-server addon is not necessary. Ensure the metrics-server addon is disabled on minikube:
这里我们选择部署metrics-server,其仅收集集群核心数据,如资源使用情况,其功能虽没有coreos/kube-prometheus全,但收集的数据不但可供dashbord用于呈现资源状态,也可用于基于CPU与内存的应用水平扩展HPA与应用垂直扩展VPA。
如下所示,在安装dashboard前,我们先行将metrics-server安装到独立的kube-monitoring命名空间中,因我们的ca证书是自签名的,故需要使用--kubelet-insecure-tls
启动metrics-server。
kubectl create namespace kube-monitoring
cat > /tmp/values.yaml <<EOF
args:
- --kubelet-insecure-tls
EOF
helm -n kube-monitoring -f /tmp/values.yaml \
install metrics-server stable/metrics-server
等待pod启动成功后,执行如下命令验证检查是否可获取数据。
$ kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes"
$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
cts.zyl.io 273m 6% 1518Mi 41%
$ kubectl top pod
NAME CPU(cores) MEMORY(bytes)
metrics-server-5b6896f6d7-snnbv 3m 13Mi
开始安装dashboard,这里我们启用metricsScraper,这样将从metrics-server处获取性能数据以呈现到控制台上,而配置ingress使用https实在有点麻烦,为了方便起见这里使用NodePort将端口映射到集群外。
kubectl create namespace kube-dashboard
helm -n kube-dashboard install dash \
--set metricsScraper.enabled=true \
--set service.type=NodePort \
kubernetes-dashboard/kubernetes-dashboard
如下所示NodePort模式所分配的主机端口为30808,故我们可通过https://192.168.110.6:30808访问控制台。
$ oc get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
dash-kubernetes-dashboard NodePort 10.108.149.116 <none> 443:30808/TCP
如下所示,两种登陆控制台的方法,我们采用Token方式登陆控制台,获取Token的步骤参考官方文档Creating sample user按其步骤实施即可,本文不再重复描述此过程。
如下所示,这是一个登陆成功后的概况图:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。