k8s 行记: 高可用、helm 和 istio
Table of Contents
这是我第一次使用 kubernetes,它是个使用容器技术,帮助我们管理多个主机或虚拟机上的计算、存储、网络资源,让服务集群的部署、升级、扩展变得更容易的软件工具。 部署过程遇到许多问题,这里把经验记录下来,希望将来对他人有所帮助。
1 部署高可用 k8s 集群
看到 k8s的高可用方案文档 里面的这个图,一下子就决定用它了。
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 的负载均衡。本想使用云服务商提供的负载均衡器,却遇到了自己不能访问自己的问题:
于是打算用 nginx 部署在每个节点,作为负载均衡器,nginx 配置如下:
stream { upstream stream_backend { server node3:6443; server node4:6443; server node5:6443; } server { listen 7443; proxy_pass stream_backend; } }
网络结构如下图所示:
在 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 reset
再 init
。
这里使用的是 registry.cn-hangzhou.aliyuncs.com/google_containers
的镜像地址,这个镜像不是很全,而且拉下来发现有的文件 sha256
与 gcr
不一样,有的镜像命名有问题,明明写着 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/16
给 kubeadm 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 pods
是 Pending
。
kubectl describe pod my-release-mysql-0
发现 PersistentVolumeClaims 没满足条件。
查看 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
中。
istioctl install --set profile=demo -y
安装示例项目。 进入 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。