k8s群集中自建etcd服务遇到的问题

由于近期换了工作,新公司这边主业务也是使用k8s来跑服务;由于使用go语言进行开发,故这边之前就选择采用etcd仅作选举使用。

最近开发的新的服务模块,需要使用到etcd的服务发现功能,再使用之前运行的etcd时就出现了问题。

问题主要有两个:

  • 原镜像启动参数问题,导致使用服务发现方式连接etcd节点出现问题;但仅使用选举功能不受影响
  • 开发代码配置问题,导致发现方连接不到服务方地址

第一个问题是这样,启用参数配置错误,导致程序连接不到 advertise-client 地址,启动YAML命令:

官方对于启动参数的解释:

知道原因了就可以解决问题,修改启动参数即可;但k8s中从环境变量env.status.podIP中提取到的POD运行时产生的随机IP是不能直接作用于与其同级的 command 中的,于是我就重新制作了运行镜像,和类型为Deployment的yaml文件。

制作镜像采用了官方CentOS的基础镜像和etcd 3.5正式版的二进程文件,使用自建脚本进行启动:

vim start-etcd.sh
#!/bin/bash
# Author:Chris                          __On 2021/08/22
# =====================Description=====================
# [Features]: This is for Start an Singel ETCD in Docker Container.
# =====================================================

# <----------------------------Configure Start--------------------------->
Base=$(cd `dirname ${BASH_SOURCE}` ; pwd)
Execute="/usr/local/bin/etcd"
IP=$(ip add | grep inet | grep -v 127 | awk '{print $2}' | cut -d / -f 1)
[ -z "${POD_IP}" ] && POD_IP=${IP}
Name=${INS_NAME}
Cluster_List=${cluster_list:-"${Name}=http://${POD_IP}:2380"}
Cluster_Tk=${cluster_token:-"etcd-singel"}
Start_Opts="--name=${Name} --listen-client-urls=http://0.0.0.0:2379 --listen-peer-urls=http://0.0.0.0:2380 --advertise-client-urls=http://${POD_IP}:2379 --initial-advertise-peer-urls=http://${POD_IP}:2380 --initial-cluster=${Cluster_List} --initial-cluster-token=${Cluster_Tk} --initial-cluster-state=new --data-dir=/var/lib/etcd"
Ext_Args="[email protected]"
# <----------------------------Configure  End---------------------------->
if [ ! -f "${Execute}" ];then
    echo "ERROR: Can not find execute file: ${Execute}."
    exit 1
fi

# 使用exec command, 会用 command 进程替换当前shell进程, 并且保持 PID 不变。
# 为保证Docker容器不会终止, 脚本需一直处于运行状态, 
# 故不要在最后添加 & 让java在后台另起进程运行, 而是直接替代当前脚本运行。
# Tips: docker logs显示容器内/dev/stdout, /dev/stderr的内容, 阿里云k8s也是收集此日志;
#       故运行时就不进行日志重定向($App_Log)及 2>&1 了
exec ${Execute} ${Start_Opts} ${Ext_Args}

Dockerfile文件:

vim Dockerfile
FROM centos as build
RUN sed -i "/^alias mv/a\alias ll='ls -lh --time-style=\"+%Y/%m/%d %H:%M\" --color'" ~/.bashrc
ADD etcd-v3.5.0-linux-amd64/* /usr/local/bin/
ADD start-etcd.sh /root/
RUN yum install -y net-tools && yum clean all
RUN mkdir -p /var/lib/etcd /etc/etcd
RUN rm -f /root/*.cfg /root/*.log
FROM scratch
COPY --from=build / .
LABEL maintainer="Chris"
EXPOSE 2379 2380
WORKDIR /root
ENTRYPOINT ["/root/start-etcd.sh"]

打包镜像:

docker build -t etcd:v3.5.0-c8 .

打包完成后重新 tag 下,然后上传到k8s群集可以访问到的镜像仓库即可。

程序yaml文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: myetcd
  name: myetcd
  namespace: myetcd
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  selector:
    matchLabels:
      app: myetcd
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: myetcd
    spec:
      containers:
        - env:
            - name: POD_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
            - name: INS_NAME
              value: s1
            - name: cluster_list
              value: ""
            - name: cluster_token
              value: etcd-singel
          args:
            - "--host-whitelist=*"
          image: 'registry-vpc.cn-xx.aliyuncs.com/xxx/etcd:v3.5.0-c8'
          imagePullPolicy: IfNotPresent
          name: myetcd
          ports:
            - containerPort: 2379
              name: p2379
              protocol: TCP
            - containerPort: 2380
              name: p2380
              protocol: TCP
          resources:
            requests:
              cpu: 250m
              memory: 512Mi
          volumeMounts:
            - mountPath: /etc/localtime
              name: volume-localtime
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      volumes:
        - hostPath:
            path: /etc/localtime
            type: ''
          name: volume-localtime

服务yaml文件:

apiVersion: v1
kind: Service
metadata:
  name: myetcd-svc
  namespace: myetcd
spec:
  ports:
    - name: p2379
      port: 2379
      protocol: TCP
      targetPort: 2379
    - name: p2380
      port: 2380
      protocol: TCP
      targetPort: 2380
  selector:
    app: myetcd
  type: ClusterIP

这样群集中便可使用 myetcd-svc.myetcd:2379 来访问到 etcd 程序,再通过它拿到advertise-client地址来连接。

Tips:我是根据之前样式并且也没有数据需要存储,就顺手制作成 Deployment 进行启动,你也可以制作成StatefulSet来持久化 /var/lib/etcd 下的数据。只是当Pod销毁后,重启启动的 Pod 又分重新分配到不同的 ip 地址,此时用来做服务发现的程序也需要重启进行重新连接,故若十分依赖 etcd 的话建议在 k8s 群集之外在有固定IP的主机上以群集方式运行。

第二个遇到的问题就是 go 程序配置的问题了,服务被发现端在etcd提供的值地址是服务发现端所无法连接的。

由于配置里固定了地址为 127.0.0.1:8080 ,导致发现端pod是连不上被发现端的pod的。

和开发说明后,改为 0.0.0.0 即可正常:

此次遇到的问题主要还是配置,无论是基础服务配置还是程序服务配置,故大家也多留意下配置。

发表评论

error: Content is protected !!