k8s 行记: 高可用、helm 和 istio

Table of Contents

这是我第一次使用 kubernetes,它是个使用容器技术,帮助我们管理多个主机或虚拟机上的计算、存储、网络资源,让服务集群的部署、升级、扩展变得更容易的软件工具。 部署过程遇到许多问题,这里把经验记录下来,希望将来对他人有所帮助。

1 部署高可用 k8s 集群

看到 k8s的高可用方案文档 里面的这个图,一下子就决定用它了。

Sorry, your browser does not support SVG.

1.1 服务器准备

根据 kubeadm 的部署文档,我需要一些 Linux 服务器,每个服务器至少有 2GB 内存,至少 2 个 CPU,服务器内部网络全通, hostname、mac地址唯一,6443 端口可用,swap 关闭。

我去云服务商那里买了 3 个满足条件的 Ubuntu 虚拟机:

hostname ip 地址 别名
k8s3 172.21.0.11 node3
k8s4 172.21.0.13 node4
k8s5 172.21.0.2 node5

在所有节点上, /etc/hosts 文件最后加入下列内容:

172.21.0.11 node3 k8s3
172.21.0.13 node4 k8s4
172.21.0.2  node5 k8s5

在 k8s3 节点,生成 ssh-key,设置无密码登陆,复制到所有其它节点:

ssh-keygen
# 连续回车直到结束

cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 400 ~/.ssh/authorized_keys

scp -r ~/.ssh k8s4:~/
scp -r ~/.ssh k8s5:~/

之后多节点的操作就可以用 ansible 工具进行了,为了文档简洁,这里不再说明 ansible 相关的操作。

为了让服务器有更好的性能,对每个节点,按照 Linux 服务器初始化说明 设置内核参数、文件描述符限制等参数。

1.2 安装 docker

按照 docker 文档 部署在服务器上安装 docker。 在所有节点操作:

sudo apt-get update
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

systemctl start docker
systemctl enable docker

需要下面配置才能在让 k8s 使用 docker,否则 k8s 会报 systemd 相关的错误。

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

1.3 安装 kubeadm, kubectl

因为网络的原因,按照 kubernets 文档 安装总是会失败,需要切换一些国内镜像。

在所有节点上操作:

# 添加并信任APT证书
curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -

# 添加源地址
add-apt-repository "deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main"

# 更新源并安装最新版 kubenetes
sudo apt update && apt install -y kubelet kubeadm kubectl
systemctl enable kubelet

# 添加 completion,最好放入 .bashrc 中
source <(kubectl completion bash)
source <(kubeadm completion bash)

1.4 启动 k8s 集群

我想要部署 3 个 master 节点,就跟上面集群拓扑图里的一样。根据 k8s 的高可用部署文档 建议,需要一个 load balancer ,作为三个 maseter 节点上的 apiserver 的负载均衡。本想使用云服务商提供的负载均衡器,却遇到了自己不能访问自己的问题:

clb.png

于是打算用 nginx 部署在每个节点,作为负载均衡器,nginx 配置如下:

stream {
        upstream stream_backend {
                server node3:6443;
                server node4:6443;
                                server node5:6443;
        }
        server {
                listen 7443;
                proxy_pass stream_backend;
        }
}

网络结构如下图所示:

ngx.png

在 node3 启动 kubeadm 初始化程序,这里 gcr 连不上所以使用 --image-repository 指定三方镜像,这个地址里面的镜像有些残缺,他们似乎也做过一些修改,实际环境慎重考虑再使用吧:

kubeadm init --image-repository='registry.cn-hangzhou.aliyuncs.com/google_containers' --control-plane-endpoint "node3:7443" --upload-certs

如果失败,就 kubeadm reset 之后再运行上面的命令。

有时候会报某个镜像下载不了,大概是因为这个下载地址的内容还没有从 gcr 同步,需要自己想办法去别的地方下一个,或者直接下载个旧版本的,在 docker iamge tag 到它需要的版本 :b,最后 kubeadm resetinit

这里使用的是 registry.cn-hangzhou.aliyuncs.com/google_containers 的镜像地址,这个镜像不是很全,而且拉下来发现有的文件 sha256gcr 不一样,有的镜像命名有问题,明明写着 x86 实际却是 arm,实际环境可以考虑下面的方式:

kubeadm config image list 列举镜像列表,在本地架好梯子 docker pull 再传到服务器上面。

成功后会提示如下,注意这个提示中的内容 千万别泄露

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

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

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/

You can now join any number of the control-plane node running the following command on each as root:

  kubeadm join node3:7443 --token lavala.h9pvy1yj2d353p43 \
	--discovery-token-ca-cert-hash sha256:baa6264eeef2f235e1f73ddf39b24f889bf51d57cec9d976561132dae73257dd \
	--control-plane --certificate-key e9a0ee9426fe42dcbe9fda117584c7c61de28c745868bf62b5cc17958d1b1974

Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join node3:7443 --token lavala.h9pvy1yj2d353p43 \
	--discovery-token-ca-cert-hash sha256:baa6264eeef2f235e1f73ddf39b24f889bf51d57cec9d976561132dae73257dd

按照提示照做即可,在 node4, 和 node5 节点,运行命令提示里面提到的 kubeadm join xxxx 命令,注意 node3:7443 是负载均衡器的地址,可以改为本地 nginx 节点,让它负责转发。如果要加入 worker 节点,按照提示最后一个命令照做即可。

注意在今后需要执行 kubectl 命令的节点,设置 export KUBECONFIG=/etc/kubernetes/admin.conf 。可以写到 ~/.bashrc 里。

安装网络。 去 kubernetes 的集群网络文档 选择一个。这里选用 weave 因为简单。

kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

实际使用时,我在 CentOS 7 上使用 weave 有些问题,因为对这个网络不熟,直接改用 flannel 就可以了。 梯子下载 https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml 再上传到服务器上 kubectl apply -f 就可以了。需要注意的是使用 kubeadm 时,需要指定参数 --pod-network-cidr=10.244.0.0/16kubeadm init 命令。

修改 nodePort 可选范围。在所有 master 节点,打开文件 /etc/kubernetes/manifests/kube-apiserver.yaml ,在 -command: 最后添加一项 - --service-node-port-range=1-65535 ,如下所示:

spec:
  containers:
  - command:
    - kube-apiserver
    # ... ignore contents
    - --service-node-port-range=1-65535
    # ... 

然后等待配置被应用,过程大约几分钟。 可以使用 kubectl get nodes 查看是否 Ready 验证是否已经应用。 如果一直没有 Ready ,可以查看 kube-system 命名空间看看它在干嘛,针对性解决即可: kubectl -n kube-system get all

k8s默认不调度容器到主节点运行,如果服务器数量太少(小于10),也可设置主节点运行容器。如果条件允许,不建议这么做,据说有安全方面的原因。但我只有3个服务器,都是master 节点,只能如此了:

kubectl taint nodes --all node-role.kubernetes.io/master-

至此,一个高可用 k8s 集群设置完成。

2 验证 k8s 集群

部署一个 nginx 应用试试。编辑文件 nginx.yaml 如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2 # tells deployment to run 2 pods matching the template
  template:
    metadata:
      labels:
	app: nginx
    spec:
      containers:
      - name: nginx
	image: nginx:1.14.2
	ports:
	- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-srv
spec:
  type: NodePort
  ports:
  - port: 80
    nodePort: 80
    targetPort: 80
  selector:
    app: nginx

执行命令:

kubectl apply -f nginx.yaml

这样就在 80 端口启动了 nginx 服务,打开浏览器随便访问某个节点的IP地址,应该可以访问。

3 Helm

3.1 安装 helm

helm 是安装 k8s 上应用的一个包管理工具,可以很方便地部署 consul, elasticsearch, grafana 等常用服务。

使用下列命令安装 helm:

curl https://baltocdn.com/helm/signing.asc | sudo apt-key add -
sudo apt-get install apt-transport-https --yes
echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm

初始化 chart 仓库,可以去 Artifact Hub 查找可用的 chart 仓库。

helm repo add bitnami https://charts.bitnami.com/bitnami

尝试安装一个应用,这里演示 mysql:

helm repo update
helm install my-release bitnami/mysql

安装后按照提示操作,获得 mysql 用户和密码,连接验证即可。

如果等了一会没有完成,查看 Pod 状态 kubectl get podsPendingkubectl describe pod my-release-mysql-0 发现 PersistentVolumeClaims 没满足条件。 consis.png

查看 kubectl get pvc , kubectl get pvc -o yaml data-my-release-mysql-0 得到 pvc 定义。应该是创建一个满足条件的 pv 就可以了。

编辑 pv.yaml :

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv-volume
  labels:
    app.kubernetes.io/component: primary
    app.kubernetes.io/instance: my-release
    app.kubernetes.io/name: mysql
spec:
  volumeMode: Filesystem
  capacity:
    storage: 8Gi
  accessModes:
  - ReadWriteOnce
  hostPath:
    path: "/mnt/mysqldata"

应用 pv.yaml :

kubectl apply -f pv.yaml

注意给 /mnt/mysqldata 路径权限,我给了 chmod 777 -R /mnt :p, 等待完成即可。

3.2 使用 helm 安装 prometheus 套件

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install my-prometheus prometheus-community/kube-prometheus-stack

要通往 k8s.gcr.io 的网络环境的,很尴尬。

4 istio

istio release 下载解压,将 bin 目录添加到环境变量 PATH 中。

安装 demo 版

istioctl install --set profile=demo -y

Sorry, your browser does not support SVG.

安装示例项目。 进入 istio 安装包解压的目录, samples 在这个目录里面。

kubectl label namespace default istio-injection=enabled
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml

查看 EXTERNAL-IP:

kubectl get svc istio-ingressgateway -n istio-system

如果 EXTERNAL-IP 已经设置,那么已经有一个外部负载均衡器作为 ingress 网关了。 如果是 <none><pending> , 可以通过服务的 node port 访问 gateway。我是 node port 的情况。

export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
export TCP_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="tcp")].nodePort}')
#!!! 设置 IP 地址
export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT

尝试访问

curl -s "http://${GATEWAY_URL}/productpage" | grep -o "<title>.*</title>"

打开浏览器尝试访问,IP 地址改为对应节点的外网IP。

5 operator

5.1 TODO


By .