53

Rook, 用还是不用,这就是Kubernetes

 4 years ago
source link: https://www.tuicool.com/articles/iUFzAv6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【编者的话】本文作者介绍了Rook项目并分享了他们学到的一些生产环境的使用经验和见解。

就在今年的五月份,Rook官方宣告 Rook 1.0.0 主版本发布了,”可用于Kubernetes,已经生产就绪的云原生存储”。大约在一年前,这个解决方案首次出现在我们的视野里,但是距离我们实际使用它又过去了一段时间。最后,我们很高兴在这里分享我们学到的一些经验教训。

q6nim2y.png!web

简而言之,Rook就是一组Kubernetes的Operator,它可以完全控制多种数据存储解决方案(例如Ceph, EdgeFS, Minio, Cassandra)的部署,管理以及自动恢复。

到目前为止, rook-ceph-operator 仍然是最先进(也是唯一一个 稳定 的)解决方案。

注意:Rook 1.0.0这次发布了一些和Ceph有关的显著特性,包括对Ceph Nautilus的支持,以及启动NFS守护进程用于导出CephFS卷或者RGW桶的CephNFS CRD。此外,新版本还加入了期待已久的对EdgeFS的beta级支持。

在本文中,我们将会:

  • 解答在Kubernetes集群里使用Rook来部署Ceph有什么好处的问题;
  • 分享我们在生产环境使用Rook的一些经验和见解;
  • 解释我们为什么对Rook说"Yes"的原因并分享我们的未来规划;

但是现在,让我们先从一些通俗的概念和理论讲起吧。

“Rook是我的强项!” (来自一位不知名的棋手)

yiArEbF.png!web

使用Rook的其中一个主要好处在于它是通过原生的Kubernetes机制和数据存储交互。这就意味着你不再需要通过命令行手动配置Ceph。

  • 你想要在一个集群里部署CephFS吗?只需要创建一个YAML文件就行了!
  • 什么?你还想要部署一个支持S3 API的对象存储?行,另外再建一个YAML文件就行!

Rook具备了一个典型的Kubernetes Operator的所有功能。与它的交互依赖于 自定义资源定义 (CRD)来描述我们需要的Ceph实例的一些属性(除非另有明确说明,否则在本文的其余部分中,我们将会隐去Ceph的字眼,因为它是Rook目前唯一稳定的存储解决方案)。通过使用给定的参数,一个Operator将会自动执行设置所需的命令。

我们不妨看一个创建对象存储的具体示例,以 CephObjectStoreUser 为例:

apiVersion: ceph.rook.io/v1

kind: CephObjectStore

metadata:

  name: {{ .Values.s3.storeName }}

  namespace: kube-rook

spec:

  metadataPool:

    failureDomain: host

    replicated:

      size: 3

  dataPool:

    failureDomain: host

    erasureCoded:

      dataChunks: 2

      codingChunks: 1

  gateway:

    type: s3

    sslCertificateRef:

    port: 80

    securePort:

    instances: 1

allNodes: false

apiVersion: ceph.rook.io/v1

kind: CephObjectStoreUser

metadata:

name: {{ .Values.s3.username }}

namespace: kube-rook

spec:

store: {{ .Values.s3.storeName }}

displayName: {{ .Values.s3.username }}

上述清单里列出的参数都是一些常见配置,无需进一步说明。不过这里要重点关注一下参数值指定为模板变量的部分。

一般流程的结构如下:我们会通过一个YAML文件请求资源,然后一个operator会去执行所有必要的命令并返回一个"不那么真实"的secret,我们可以据此进行接下来的工作( 参见下文 )。然后,基于上述提供的变量,Rook会帮助用户生成要执行的命令以及secret的名称。

什么命令呢?当Rook Operator为对象存储创建一个用户时,它会在Pod里执行这条命令:

radosgw-admin user create --uid={{ .Values.s3.username }} --display-name={{ .Values.s3.username }} --rgw-realm={{ .Values.s3.storeName }} --rgw-zonegroup={{ .Values.s3.storeName }}

它会生成如下结构的JSON数据:

{

    "user_id": "{{ .Values.s3.username }}",

    "display_name": "{{ .Values.s3.username }}",

    "keys": [

        {

           "user": "{{ .Values.s3.username }}",

           "access_key": "NRWGT19TWMYOB1YDBV1Y",

           "secret_key": "gr1VEGIV7rxcP3xvXDFCo4UDwwl2YoNrmtRlIAty"

        }

    ],

    ...

}

这里的 keys 在将来用于通过S3 API为应用程序提供访问对象存储的权限。Rook Operator将它们照单全收,然后以 rook-ceph-object-user-{{ .Values.s3.crdName }}-{{ .Values.s3.username }} 的格式作为secret存储到它的namespace里。

为了使用保存到secret里的数据,所有你要做的就是通过环境变量把它们传入到容器里。这里有一个Job模板的示例,它会自动给每个用户环境创建对应的bucket:

{{- range $bucket := $.Values.s3.bucketNames }}

apiVersion: batch/v1

kind: Job

metadata:

  name: create-{{ $bucket }}-bucket-job

  namespace: kube-rook

  annotations:

    "helm.sh/hook": post-install, post-upgrade

    "helm.sh/hook-weight": "2"

spec:

  template:

    metadata:

      name: create-{{ $bucket }}-bucket-job

    spec:

      restartPolicy: Never

      initContainers:

      - name: waitdns

        image: alpine:3.6

        command: ["/bin/sh", "-c"]

        args:

        - "while ! getent ahostsv4 rook-ceph-rgw-{{ $.Values.s3.storeName }}; do sleep 1; done"

      - name: config

        image: rook/toolbox:v0.7.1

        command: ["/bin/sh", "-c"]

        args:

        - >-

          s3cmd --configure --access_key=$(ACCESS-KEY) --secret_key=$(SECRET-KEY)

          --no-ssl --dump-config

          --host=rook-ceph-rgw-{{ $.Values.s3.storeName }}

          --host-bucket=rook-ceph-rgw-{{ $.Values.s3.storeName }}

          | tee /config/.s3cfg

        volumeMounts:

          - name: config

            mountPath: /config

        env:

        - name: ACCESS-KEY

          valueFrom:

            secretKeyRef:

              name: rook-ceph-object-user-{{ $.Values.s3.storeName }}-{{ $.Values.s3.username }}

              key: AccessKey

        - name: SECRET-KEY

          valueFrom:

            secretKeyRef:

              name: rook-ceph-object-user-{{ $.Values.s3.storeName }}-{{ $.Values.s3.username }}

              key: SecretKey

      containers:

      - name: create-bucket

        image: rook/toolbox:v0.7.1

        command: ["s3cmd", "mb", "s3://{{ $bucket }}"]

        ports:

        - name: s3-no-sll

          containerPort: 80

        volumeMounts:

        - name: config

          mountPath: /root

      volumes:

      - name: config

emptyDir: {}

{{- end }}

这个Job的所有操作仅在Kubernetes内部执行。YAML文件里描述的数据结构已经被添加到了Git仓库里以便复用。对于DevOps工程师和整体的CI/CD流程而言,这个功能真是棒极了。目前,Rook团队已经 计划 使用 Bucket置备库 ,这将会使你和S3 bucket的交互更加方便。

通过Rook处理RADOS

传统的Ceph + RBD组合在为Pod挂载卷时施加了特定的限制。

换句话说,namespace必须包含访问Ceph的secret这样有状态应用才可以正常工作。如果你有,比方说,namespace里有2-3个环境的话,这很容易做到:你可以手动复制secret。但是如果针对每个功能都创建一个具有自己的namespace的单独环境的话该怎么办呢?我们已经通过 shell-operator 解决了这个问题,它会自动将secret复制到新的namespace里(在 这篇文章 中你可以找到一个这样的hook示例)

!/bin/bash

if [[ $1 == "--config" ]]; then

cat <<EOF

{"onKubernetesEvent":[

{"name": "OnNewNamespace",

"kind": "namespace",

"event": ["add"]

}

]}

EOF

else

NAMESPACE=$(kubectl get namespace -o json | jq '.items | max_by( .metadata.creationTimestamp ) | .metadata.name')

kubectl -n ${CEPH_SECRET_NAMESPACE} get secret ${CEPH_SECRET_NAME} -o json | jq ".metadata.namespace=\"${NAMESPACE}\"" | kubectl apply -f -

fi

但是,如果使用Rook的话就不存在这个问题了。卷的挂载流程是基于 Flexvolume 或者 CSI (目前还是beta阶段)驱动,不需要任何secret。

Rook通过一些手段自动化地自行解决了这些问题,因此我们倾向于将它用于我们的新项目。

上手Rook

让我们通过安装Rook和Ceph来结束实战部分,这样你便可以实际上手体验。为了简化安装过程,开发人员提供了一个Helm包。让我们下载它:

$ helm fetch rook-master/rook-ceph --untar --version 1.0.0

你可以在 rook-ceph/values.yaml 文件里找到许多不同的设置。这里最重要的是为discover和agent指定toleration。这里我们不打算过多涉及Kubernetes里的 taints和tolerations 的细节,你只需要知道我们不想让应用的pod调度到带有数据存储盘的节点上即可。理由也是显而易见的:这样做的话,我们可以避免Rook的agent影响应用程序本身。

如今是时候在你喜欢的文本编辑器里打开 rook-ceph/values.yaml 然后将下列部分追加到文件的末尾:

discover:

  toleration: NoExecute

  tolerationKey: node-role/storage

agent:

  toleration: NoExecute

  tolerationKey: node-role/storage

  mountSecurityMode: Any

我们还为每个保留用于数据存储的节点打上了相应的污点(taint):

$ kubectl taint node ${NODE_NAME} node-role/storage="":NoExecute

然后通过下列命令安装Helm chart:

$ helm install --namespace ${ROOK_NAMESPACE} ./rook-ceph

现在我们可以去创建集群,然后指定OSD的路径:

apiVersion: ceph.rook.io/v1

kind: CephCluster

metadata:

  clusterName: "ceph"

  finalizers:

  - cephcluster.ceph.rook.io

  generation: 1

  name: rook-ceph

spec:

  cephVersion:

    image: ceph/ceph:v13

  dashboard:

    enabled: true

  dataDirHostPath: /var/lib/rook/osd

  mon:

    allowMultiplePerNode: false

    count: 3

  network:

    hostNetwork: true

  rbdMirroring:

    workers: 1

  placement:

    all:

      tolerations:

      - key: node-role/storage

        operator: Exists

  storage:

    useAllNodes: false

    useAllDevices: false

    config:

      osdsPerDevice: "1"

      storeType: bluestore

    nodes:

    - name: ceph01

      deviceFilter: ^sd[b-i]

    - name: ceph02

      deviceFilter: ^sd[b-i]

    - name: ceph03

      deviceFilter: ^sd[b-i]

Ceph的状态应当是 HEALTH_OK :

$ kubectl -n ${ROOK_NAMESPACE} exec $(kubectl -n ${ROOK_NAMESPACE} get pod -l app=rook-ceph-operator -o name -o jsonpath='{.items[0].metadata.name}') -- ceph -s

我们还要确保应用程序的pod不会调度到那些为Ceph保留的节点上去:

$ kubectl -n ${APPLICATION_NAMESPACE} get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName

现在你可以定制其他额外的组件了。相关的信息可以在 文档 里找到。为了能够让我们在集群里的后续操作变得更加顺滑,我们强烈推荐开启dashboard并且部署 toolbox

人无完人:Rook的不足之处是什么?

如你所见,Rook的开发进展顺利。但是存在一些问题,使得一些Ceph的手动配置仍然无法避免。

  • 目前 没有Rook驱动 可以导出展示已挂载的区块的使用情况的指标数据,因此我们无法监控它们的状态(Rook v1.0.3在Flexvolume卷上 加入了 这项功能)。
  • Flexvolume和CSI(与RBD不同) 无法调整 卷的大小,因此Rook缺乏这项有用的(有时甚至非常关键)工具。(在写完这篇文章后不久,业内 已经出现了 一些实现方案但是也仅仅只是针对Flexvolume;不过也有 讨论到 CSI)
  • Rook仍然不如Ceph灵活。举个例子,要将CephFS元数据存储到SSD上,然后将相关数据存储到HDD时,你必须得在 CRUSH映射 里手动定义每组设备。
  • 尽管rook-ceph-operator被视为已经稳定了,但是从Ceph 13升级到14版本的过程中仍然会遇到一些问题。

小结

“今天Rook是通过小兵们来抵御外面的世界,但是终有一天它会在游戏里扮演一个至关重要的角色!”(这句话简直像是为本文量身定制的。)

我们无疑非常喜欢Rook项目,尽管有许多利弊,但是我们相信它绝对值得你的关注。

我们未来的计划归结为将rook-ceph变成我们 addon-operator 的一个模块。这样一来,我们可以很轻松方便地在我们维护的大量Kubernetes集群里使用它。

本文最初是由 Flant 的工程师 Maksim Nabokikh 用俄语撰写和发布的。我们工程师的更多技术资料即将推出-请不要忘记关注 我们的博客

原文链接: to-rook-in-kubernetes (译者:吴佳兴)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK