在k8s-1.23.17集群中使用ingress-nginx暴露服务

本人转载并修改于原文《ingress-nginx详解和部署方案》,出处:https://blog.csdn.net/weixin_44729138/article/details/105978555

在此先感谢原作者的奉献。

1、ingress介绍

K8s集群对外暴露服务的方式目前只有三种:
Loadblancer;Nodeport;ingress
前两种熟悉起来比较快,而且使用起来也比较方便,在此就不进行介绍了。

下面详细讲解下ingress这个服务,ingress由两部分组成:

ingress controller:将新加入的Ingress转化成Nginx的配置文件并使之生效
ingress服务:将Nginx的配置抽象成一个Ingress对象,每添加一个新的服务只需写一个新的Ingress的yaml文件即可
其中ingress controller目前主要有两种:基于nginx服务的ingress controller和基于traefik的ingress controller。
而其中traefik的ingress controller,目前支持http和https协议。由于对nginx比较熟悉,而且需要使用TCP负载,所以在此我们选择的是基于nginx服务的ingress controller。
但是基于nginx服务的ingress controller根据不同的开发公司,又分为k8s社区的ingres-nginx和nginx公司的nginx-ingress。
在此根据github上的活跃度和关注人数,我们选择的是k8s社区的ingres-nginx。

k8s社区提供的ingress,github地址如下:

https://github.com/kubernetes/ingress-nginx

nginx社区提供的ingress,github地址如下:

https://github.com/nginxinc/kubernetes-ingress

2、ingress的工作原理

ingress具体的工作原理如下:
step1:ingress contronler通过与k8s的api进行交互,动态的去感知k8s集群中ingress服务规则的变化,然后读取它,并按照定义的ingress规则,转发到k8s集群中对应的service。
step2:而这个ingress规则写明了哪个域名对应k8s集群中的哪个service,然后再根据ingress-controller中的nginx配置模板,生成一段对应的nginx配置。
step3:然后再把该配置动态的写到ingress-controller的pod里,该ingress-controller的pod里面运行着一个nginx服务,控制器会把生成的nginx配置写入到nginx的配置文件中,然后reload一下,使其配置生效,以此来达到域名分配置及动态更新的效果。

3、ingress可以解决的问题

1)动态配置服务
如果按照传统方式, 当新增加一个服务时, 我们可能需要在流量入口加一个反向代理指向我们新的k8s服务. 而如果用了Ingress, 只需要配置好这个服务, 当服务启动时, 会自动注册到Ingress的中, 不需要而外的操作。

2)减少不必要的端口暴露
配置过k8s的都清楚, 第一步是要关闭防火墙的, 主要原因是k8s的很多服务会以NodePort方式映射出去, 这样就相当于给宿主机打了很多孔, 既不安全也不优雅. 而Ingress可以避免这个问题, 除了Ingress自身服务可能需要映射出去, 其他服务都不要用NodePort方式。

4、部署ingress(deployment的方式)

查看当前版api版本:

kubectl explain Ingress

注:查看ingress和自己本地的k8s版本是否对应上,在GitHub上有表格参考。

下载配置文件:

mkdir -p /root/ingress && cd /root/ingress
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.3/deploy/static/provider/baremetal/deploy.yaml

查看源:

cat deploy.yaml | grep image:

替换镜像:

sed  -i 's#k8s.gcr.io/ingress-nginx/controller:v1.1.3@sha256:31f47c1e202b39fadecf822a9b76370bd4baed199a005b3e7d4d1455f4fd3fe2#registry.cn-hangzhou.aliyuncs.com/imges/controller:v1.1.3#' deploy.yaml
sed  -i 's#k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1@sha256:64d8c73dca984af206adf9d6d7e46aa550362b1d7a01f3a0a91b20cc67868660#registry.cn-hangzhou.aliyuncs.com/chenby/kube-webhook-certgen:v1.1.1#' deploy.yaml

注意:有些版本的ingress-nginx的配置配置文件的type默认为LoadBalaner,需要暴露的服务其后端svc访问类型需要改成NodePort:

另外,ingress-controller的官方yaml默认注释了hostNetwork 工作方式,以防止端口的在宿主机的冲突,没有绑定到宿主机 80 端口,因此还要在名为ingress-nginx-controller的Deployment配置的spec下增加“hostNetwork: true”的属性:

启动POD:

kubectl apply -f deploy.yaml

查看pod运行情况:

kubectl get pod -n ingress-nginx

启动时间有点长,最终的运行结果应该是这样的:

说明:status为completed的两个pod为job类型资源,completed表示job已经成功执行无需管它。

查看svc运行情况:

kubectl get svc -n ingress-nginx

查看controller所在的工作节点:

kubectl get pods -n ingress-nginx -o wide
进入对应的节点环境中,查看是否80端口已经开启:
[root@k8s-node03 ~]# netstat -lnp|grep 80

至此,ingress-nginx部署成功。

5、启用默认后端

mkdir -p /root/ingress ; cd /root/ingress
cat > backend.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: default-http-backend
  labels:
    app.kubernetes.io/name: default-http-backend
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: default-http-backend
  template:
    metadata:
      labels:
        app.kubernetes.io/name: default-http-backend
    spec:
      terminationGracePeriodSeconds: 60
      containers:
      - name: default-http-backend
        image: registry.cn-hangzhou.aliyuncs.com/imges/defaultbackend-amd64:1.5 
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
            scheme: HTTP
          initialDelaySeconds: 30
          timeoutSeconds: 5
        ports:
        - containerPort: 8080
        resources:
          limits:
            cpu: 10m
            memory: 20Mi
          requests:
            cpu: 10m
            memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
  name: default-http-backend
  namespace: kube-system
  labels:
    app.kubernetes.io/name: default-http-backend
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app.kubernetes.io/name: default-http-backend
EOF

部署:

kubectl apply -f backend.yaml

查看默认后端运行情况:

kubectl get pods -n kube-system

访问ingress-nginx-controller节点所在的node的80端口,看是否正确返回backend的404提示页面:

curl 192.168.239.23

6、测试

创建svc和pod

我们使用默认的nginx镜像创建svc和pod,用于测试:

vim ingress-dev.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-dm
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      name: nginx
  template:
    metadata:
      labels:
        name: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.16
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: default
spec:
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
  selector:
    name: nginx
kubectl apply -f ingress-dev.yaml
kubectl get pod -n default -o wide

查看对应的ClusterIP:

kubectl get pods,svc -o wide
测试访问该IP地址:
curl 192.168.163.56

配置ingress

vim ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-test
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: test.sway.com.cn
    http:
      paths:
      - path: /
        pathType: Prefix    # 前缀匹配
        backend:
          service:
            name: nginx-svc
            port:
              number: 80
kubectl get ingress -n default

测试服务的可访问情况:

curl test.sway.com.cn

至此,所有配置完成。

DRBD脑裂”Split-Brain detected but unresolved, dropping connection!“

主节点宕机后,备节点写入了数据,当主节点再次启动时出现脑裂提示:

主节点状态:

状态为StandAlone或者WFonnection

备节点状态:

状态为StandAlone或者WFonnection

解决方法:

情况一:以primary的数据为准:

在从节点上操作:

#设为从盘
drbdadm secondary r0
#丢弃修改
drbdadm -- --discard-my-data connect r0

查看执行结果:

cat /proc/drbd
恢复正常。

情况二:以secondary的数据为准:

在主节点上操作:

#设为从盘
drbdadm secondary r0
#丢弃修改
drbdadm -- --discard-my-data connect r0

在从节点上手动连接资源:

drbdadm connect r0

查看执行结果:

cat /proc/drbd

其他

若主备不正确,使用以下指令即可

drbdadm primary r0

如果“drbdadm secondary r0”时出现以下提示:

则可能数据已被挂在,请先umount。

如果卸载失败,可以重启服务器后再尝试。

keepalived报“Can’t open PID file /var/run/keepalived.pid (yet?) after start: No such file or directory”

情况一:权限不足

解决方法,在keepalived.conf中增加:

   script_user root
   enable_script_security

情况二:同一局域网存在相同的路由ID

如果同一局域网内存在多组不同的keepalived,请确保keepalived.conf中的virtual_router_id与其他组的不一样,否出会存在交叉调用。

当然也有可能时这样的报错:

注意:同一组keepalived内需要相同。

k8s创建、删除pod

创建

使用yaml配置文件进行创建:

kubectl apply -f xxx.yaml

删除

使用yaml配置文件进行删除:

kubectl delete -f xxx.yaml

普通删除指定pod:

kubectl delete pod [pod名称] -n [namespace名称]

如果一直处于Terminating的状态且删除卡死,可增加参数 –force –grace-period=0 强制删除:

kubectl delete pod [pod名称] -n [namespace名称] --force --grace-period=0

启动keepalived时报“Cant find interface xxx for vrrp_instance VI_1”

使用 systemctl status keepalived 名称查看运行情况:

这种情况一般时keepalived配置中所指定的网卡不存在引起的。大家使用网上教程的时候要注意记得更改网卡名称,根据实际的网卡名称来进行配置。不指导网卡名称时什么的,可以使用“ip address”命令进行查看:

修改keepalived的配置文件:/etc/keepalived/keepalived.conf
修改后使用以下命令重启keepalived即可:
systemctl restart keepalived

k8s初始化int时出现Initial timeout of 40s passed

可以先使用以下命令查看k8s的启动日志:

systemctl status kubelet

或使用以下命令查看最近的k8s日志:

journalctl -xeu kubelet

可能性一:

pause容器还是用的k8s.gcr.io/pause:3.6,该镜像在国内服务器下载不成功。

所以在初始化前修改/etc/containerd/config.toml的镜像地址参数:

[plugins."io.containerd.grpc.v1.cri"]
  sandbox_image = "registry.aliyuncs.com/k8sxio/pause:3.6"

然后重启containerd:

systemctl restart containerd

可能性二:

查看/var/log/messages的最后日志:

May  7 14:34:26 k8s-master01 kubelet: E0507 14:34:26.071077   16320 eviction_manager.go:254] "Eviction manager: failed to get summary stats" err="failed to get node info: node \"k8s-master01\" not found"
May  7 14:34:26 k8s-master01 kubelet: E0507 14:34:26.096124   16320 kubelet.go:2394] "Container runtime network not ready" networkReady="NetworkReady=false reason:NetworkPluginNotReady message:Network plugin returns error: cni plugin not initialized"
May  7 14:34:26 k8s-master01 kubelet: E0507 14:34:26.160241   16320 kubelet.go:2469] "Error getting node" err="node \"k8s-master01\" not found"
May  7 14:34:26 k8s-master01 kubelet: E0507 14:34:26.262056   16320 kubelet.go:2469] "Error getting node" err="node \"k8s-master01\" not found"
May  7 14:34:26 k8s-master01 kubelet: E0507 14:34:26.362486   16320 kubelet.go:2469] "Error getting node" err="node \"k8s-master01\" not found"
May  7 14:34:26 k8s-master01 kubelet: E0507 14:34:26.463165   16320 kubelet.go:2469] "Error getting node" err="node \"k8s-master01\" not found"

这是由于还没有安装网络插件flannel引起的。

可能性三:kubernates与docker的cgroup驱动不一致

查看/var/log/messages的最后日志:

Aug 23 18:17:21 localhost kubelet: E0823 18:17:21.059626    9490 server.go:294] "Failed to run kubelet" err="failed to run Kubelet: misconfiguration: kubelet cgroup driver: \"systemd\" is different from docker cgroup driver: \"cgroupfs\""
Aug 23 18:17:21 localhost systemd: kubelet.service: main process exited, code=exited, status=1/FAILURE
Aug 23 18:17:21 localhost systemd: Unit kubelet.service entered failed state.
Aug 23 18:17:21 localhost systemd: kubelet.service failed.

从日志看出来,是因为hubernates与docker的cgroup驱动不一致引起的,那么我们只需要将两者的cgroup驱动设置为一直即可。(官方推荐systemd)

我们可以用以下指令确认一下docker的启动方式:

docker system info|grep -i driver

修改docker的cgroup驱动的方法

一、找到docker的配置文件/etc/docker/daemon.json并修改添加如下参数(没有找到则新建一个):

"exec-opts": ["native.cgroupdriver=systemd"]

二、重启docker,并再次检查docker的驱动是否已经变更:

systemctl restart docker
docker system info|grep -i driver

修改kubernates的cgroup驱动为systemd

有两个文件:

一、/etc/sysconfig/kubelet

增加参数–cgroup-driver=systemd:

二、修改10-kubeadm.conf文件(使用该指令查找:find / -name ’10-kubeadm.conf’)

增加参数–cgroup-driver=systemd:

然后重启kubernates服务即可:

systemctl restart kubelet

最后,重置kubeamdm并再次执行k8s初始化指令即可:

kubeadm reset

close