前言

本人之前的实验环境一直用的是OCP/OKD集群,其搭建在PC Server服务器上,现需要在自己的windows工作站上搭建一个K8S集群,首先想到的是使用CodeReady Container(CRC)来搭建一个OKD集群,但其资源要求实在是太高(4C9G内存),且CRC所需的虚拟化软件hyper-vvirshbox有冲突,鉴于此问题,虽然有minikube可供选择,但本人决定手动搭建一个原生态K8S集群。

本人实验环境为virshbox安装的一台虚拟机,其操作系统版本为Centos Linux 8CPU 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网络,静态配置IP192.168.110.6

安装容器运行时

参考k8s官方文档cri-o官方文档我们先选择一容器运行时,如作者对docker容器运行时实在不敢兴趣1,故计划安装cri-o轻量级容器运行时,参考下图,作者准备安装k8s 1.18,故选择cri-o 1.18.x

image.png

对于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的管理工具crictl3kubeadm会使用其拉取镜像。

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.iok8s.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(网桥)在容器间传输流量,亦或者如dockercrio4默认的网络插件同样基于网桥传输流量,此时需加载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_uuidcat /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

接着,执行如下命令安装kubeletkubeadmkubectl,而后设置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.

我们配置的criocgroup驱动为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

注意etcdkube-apiserverkube-controller-managerkube-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.ioZonefile,然后于其中添加对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映射到集群外部,其中9000dashboard端口,8000http端口,8443https端口。

$ 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控制台,其是一个很简洁的控制台,可用于查看一些状态信息。

image.png

现在,我们为在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,若要在控制台上显示PodCpuMemory信息,则我们须安装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按其步骤实施即可,本文不再重复描述此过程。

image.png

如下所示,这是一个登陆成功后的概况图:

image.png


  1. crio: ocp/okd默认容器运行时为crio
  2. error: 本人安装好k8s后,对于此仓库后续更新遇到ERROR: The certificate of ‘download.opensuse.org’ is not trusted错误,估计系统ca有问题
  3. crictl: 此工具作用可用命令alias docker=crictl来表述
  4. crio:默认插件存储于/etc/cni/net.d/100-crio-bridge.conf目录
  5. metrics: 其是Heapster的轻量化版本,且Heapster早已被废弃

我是读书人
114 声望136 粉丝

随意记录,想着啥,写啥