暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

如何读取Kubernetes存储在etcd上的数据

int32bit 2019-12-02
585

etcd是一个分布式KV存储系统,在分布式系统中被广泛使用,Kubernetes就是使用了etcd存储持久化数据,包括创建的所有Pod、Deployment、Service等资源。

接下来我们看下如何读取Kubernetes存的数据。

首先如果使用kubeadm部署Kubernetes,默认会把CA根证书和签发的Server证书放在 /etc/kubernetes/pki/etcd
目录下,并且etcd Pod使用的是host网络:

因此可以直接在Master节点使用etcdctl命令:

  1. alias etcdctl='etcdctl \

  2. --key=/etc/kubernetes/pki/etcd/server.key \

  3. --cert=/etc/kubernetes/pki/etcd/server.crt \

  4. --cacert=/etc/kubernetes/pki/etcd/ca.crt \

  5. --endpoints https://127.0.0.1:2379'


  6. # etcdctl endpoint status

  7. https://127.0.0.1:2379, 17057a8cf6d6cbb3, 3.3.15, 10 MB, true, 4, 523191

由于新版本Kubernetes默认使用了etcd v3 API,v3版本的数据存储没有目录层级关系了,而是采用平展(flat)模式,换句话说 /a
与 /a/b
并没有嵌套关系,而只是key的名称差别而已,这个和AWS S3以及OpenStack Swift对象存储一样,没有目录的概念,但是key名称支持 /
字符,从而实现看起来像目录的伪目录,但是存储结构上不存在层级关系。

也就是说etcdctl无法使用类似v2的 ls
命令。但是我还是习惯使用v2版本的 etcdctl ls
查看etcdctl存储的内容,于是写了个性能不怎么好但是可以用的shell脚本 etcd_ls.sh
:

  1. #!/bin/bash

  2. KEY_FILE=/etc/kubernetes/pki/etcd/server.key

  3. CERT_FILE=/etc/kubernetes/pki/etcd/server.crt

  4. CA_FILE=/etc/kubernetes/pki/etcd/ca.crt

  5. ENDPOINTS=https://127.0.0.1:2379

  6. PREFIX=${1:-/}

  7. ORIG_PREFIX="$PREFIX"


  8. LAST_CHAR=${PREFIX:${#PREFIX}-1:1}

  9. if [[ $LAST_CHAR != '/' ]]; then

  10. PREFIX="$PREFIX/" # Append '/' at the end if not exist

  11. fi


  12. for ITEM in $(etcdctl --key="$KEY_FILE" \

  13. --cert="$CERT_FILE" \

  14. --cacert="$CA_FILE" \

  15. --endpoints "$ENDPOINTS" \

  16. get "$PREFIX" --prefix=true --keys-only | grep "$PREFIX"); do

  17. PREFIX_LEN=${#PREFIX}

  18. CONTENT=${ITEM:$PREFIX_LEN}

  19. POS=$(expr index "$CONTENT" '/')

  20. if [[ $POS -le 0 ]]; then

  21. POS=${#CONTENT} # No '/', it's not dir, get whole str

  22. fi

  23. CONTENT=${CONTENT:0:$POS}

  24. LAST_CHAR=${CONTENT:${#CONTENT}-1:1}

  25. if [[ $LAST_CHAR == '/' ]]; then

  26. CONTENT=${CONTENT:0:-1}

  27. fi

  28. echo "${PREFIX}${CONTENT}"

  29. done | sort | uniq


  30. etcdctl --key="$KEY_FILE" \

  31. --cert="$CERT_FILE" \

  32. --cacert="$CA_FILE" \

  33. --endpoints "$ENDPOINTS" get "$ORIG_PREFIX"

由于Kubernetes的所有数据都以 /registry
为前缀,因此首先查看 /registry
:

  1. # ./etcd_ls.sh /registry

  2. /registry/apiregistration.k8s.io

  3. /registry/clusterrolebindings

  4. /registry/clusterroles

  5. /registry/configmaps

  6. /registry/controllerrevisions

  7. /registry/daemonsets

  8. /registry/deployments

  9. /registry/events

  10. /registry/leases

  11. /registry/masterleases

  12. /registry/minions

  13. /registry/namespaces

  14. /registry/persistentvolumeclaims

  15. /registry/persistentvolumes

  16. /registry/pods

  17. /registry/podsecuritypolicy

  18. /registry/priorityclasses

  19. /registry/ranges

  20. /registry/replicasets

  21. /registry/rolebindings

  22. /registry/roles

  23. /registry/secrets

  24. /registry/serviceaccounts

  25. /registry/services

  26. /registry/statefulsets

  27. /registry/storageclasses

我们发现除了 minions
、 range
等大多数资源都可以通过 kubectlgetxxx
获取,组织格式为 /registry/{resource_name}/{namespace}/{resource_instance}
,而 minions
其实就是node信息,Kubernetes之前节点叫 minion
,应该还没有改过来,因此还是使用的 /registry/minions

range
对应Service网段以及NodePort端口范围:

  1. # ./etcd_ls.sh /registry/ranges

  2. /registry/ranges/serviceips

  3. /registry/ranges/servicenodeports

  4. # ./etcd_ls.sh /registry/ranges/servicenodeports | strings

  5. /registry/ranges/servicenodeports

  6. RangeAllocation

  7. 30000-32767

  8. # ./etcd_ls.sh /registry/ranges/serviceips | strings

  9. /registry/ranges/serviceips

  10. RangeAllocation

  11. 10.96.0.0/12

如上为什么需要使用 strings
命令,那是因为除了 /registry/apiregistration.k8s.io
是直接存储JSON格式的,其他资源默认都不是使用JSON格式直接存储,而是通过protobuf格式存储,当然这么做的原因是为了性能,除非手动配置 --storage-media-type=application/json
,参考etcdctl v3: k8s changes its internal format to proto, and the etcdctl result is unreadable。

如果我们直接读会得到部分乱码:


使用proto提高了性能,但也导致有时排查问题时不方便直接使用etcdctl读取内容,可幸的是openshift项目已经开发了一个强大的辅助工具etcdhelper可以读取etcd内容并解码proto。

不过编译有坑,需要做如下修改:


通过如下命令进行编译安装:

  1. go build .

  2. cp etcdhelper /usr/local/bin

  3. alias etcdhelper='etcdhelper -cacert /etc/kubernetes/pki/etcd/ca.crt \

  4. -key /etc/kubernetes/pki/etcd/server.key \

  5. -cert /etc/kubernetes/pki/etcd/server.crt'

编译完后就可以读取etcd解码内容了,比如读取namespace default信息:

  1. # etcdhelper get /registry/namespaces/default && echo

  2. /v1, Kind=Namespace

  3. {

  4. "kind": "Namespace",

  5. "apiVersion": "v1",

  6. "metadata": {

  7. "name": "default",

  8. "uid": "6ee8cecc-37f3-4df5-a415-27d1e5023266",

  9. "creationTimestamp": "2019-11-28T09:00:35Z"

  10. },

  11. "spec": {

  12. "finalizers": [

  13. "kubernetes"

  14. ]

  15. },

  16. "status": {

  17. "phase": "Active"

  18. }

  19. }

值得注意的是存储在etcd的secret默认仅仅使用了base64编码而并没有加密:


可见 kubectlgetsecret
一样,secret是base64编码的,secret保存着私钥证书、Docker登录信息、密码等敏感数据,因此需要严格控制etcd的访问权限,避免其他人读取。

当然更安全起见,建议配置etcd数据存储加密,参考https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/.

Kubernetes系列文章:

文章转载自int32bit,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论