没有CRD的超级简单的Kubernetes Operator (基于Shell脚本)

首先

Kubernetes Operator是一种设计模式,作为Kubernetes的扩展手段提供。通过自定义资源和控制器扩展Kubernetes API,并可以使用Kubernetes API和kubectl命令像其他资源一样进行控制。

我为什么写这篇稿件

操作者的便利性非常高,现在已经开发了许多能够无缝适配到Kubernetes的操作者。但另一方面,定义CRD和使用go语言进行开发都需要一定的难度,这也是事实。

如果按照operator-SDK的教程,尝试按照步骤操作memcached-operator,甚至进行了代码修改,但是仍然很难理解的人可能有很多,我也是其中之一。

いろいろ勉強するうちに、memcached-operator機能も、operator-SDKの痒い所に手が届く仕組みも理解が進みましたが、知識ゼロのところからmemcachd-operatorチュートリアルに橋渡しする何かが必要だと感じ、いくつか記事を書くことにしました。これはその1つです。

我认识到Operator的最大特点是CRD,但我也认为CRD在使用和理解Operator方面存在一些障碍。因此,在这里,我将尽量避免涉及自定义资源,并着重介绍控制器的功能。

Kubernetes Operatorについてのちょっとした不満
Kubernetes APIとしてシームレスに組み込むために、カスタムリソースは cluster resourceになります。クラスター管理者がCRDをインストールし、開発ユーザがそれをinstance化する役割分担なわけですが、開発ユーザによる自由なOperatorインストールを阻害していると思います。
また、Operatorを作成する際に必要となるCRDの定義もハードルが高い要素の1つです。operator-SDKでは、go言語の構造体からCRDを生成するツールを提供していますが、「やっぱりgo言語使わないといけない」点にイラッとしてしまいます。

达成目标

本稿と、これに続く一連の記事では以下を目標とします。取り消し線の項目は、本稿ではなく別記事で説明します。

    • Kubernetes Operatorをざっくり理解する

 

    • Python SDKを使用してOperatorのロジックを作成する

 

    Python Operator SDK (kopf)を使用してOperatorを作成する

また、タイトルにあるようにここではOperatorのコントローラが実現するロジックやコーディングを中心に説明し、CRDについては触れません。また作成したロジックをOperatorとして動作させるための周辺作業については必要最小限の説明とさせてください。

必须的东西 (bì xū de xi)

前提的知识

知識ゼロとは言え、「kubernetesって何?」からだと大変なので、kubernetesの操作はある程度できる、とさせてください。例題として扱う問題の性質上、クラスターを運用管理する駆け出しエンジニア程度の知識があれば、なお良いです。

需要的环境

此处要创建的是一个旨在“一眼看懂”的项目,因此并不需要运行环境(不考虑运行)。

实际上,拥有可自由使用的Kubernetes集群是很好的。考虑到故障处理的问题性质,minikube和OpenShift Local并不足够。

再考:Operator指的是什么?

OperatorとはKubernetesドキュメント「オペレーターパターン2」の冒頭にあるように、運用の知識をコード化するためのものです。

オペレーターパターンはサービス、またはサービス群を管理している運用担当者の主な目的をキャプチャすることが目標です。

そのための仕組みをカスタムリソースとコントローラで実装するのがKubernetes Operatorです。ここではカスタムリソースやコントローラから説明するアプローチではなく、「運用知識のコード化」を出発点に説明していきます。

そんなに難しい話ではありません。インフラエンジニアなら普通に行なっていることを、Operatorっぽく説明するだけです。

StatefulSet Pod的強制刪除操作器

应解决的问题

StatefulSetはPodの順序と一意性を保証するKubernetesリソースです。Podがユニークであることを保証するために、ノード障害が発生した場合、Deploymentのように別のノードでPodが自動的に起動することはありません。ノードがクラスターから見えなくなった原因が単なるネットワークの切断であり、どこかでPodが稼働し続けているのか、それともノードが停止してPodも消滅したのかが判断できないからです。Podはノードが復帰するまで、もしくは強制的に削除されるまでTerminatingのままとなります。このTerminating Podが存在し続ける限り、新たなStatefulSet Podは起動しません。

如果节点由于故障停止,希望像部署一样,自动将StatefulSet Pod迁移到另一个节点上。为简化问题,我们假设以下两点。

    • Podはいつでも問題なくdeleteできる

 

    ノードにNoExecuteテイントがついていたらノード障害と判断できる

一般的にStatefulSet Podは外部リソースの使用など難しい制約を持つ場合が少なくなく、停止には慎重な判断が必要です。これは放置しておいても修復されない問題の一例として取り上げたものであり、プロダクション環境でこのような自動化を行う際には十分な配慮が必要です。

StatefulPodの強制削除は、Force Delete StatefulSet Podsに書かれているように、–forceオプション付きのkubectl deleteコマンドを実行します。

常见的解决方法

普通、次のようなシェルスクリプトを書くのではないでしょうか。まず分かりやすくするために疑似コードで書いてみます。

無限ループ {
  if Terminating StateのPodがある {
    if そのPodが稼働しているノードが障害で停止している {
      そのPodを強制削除する
    }
  }
}

実際のシェルスクリプトではこんな感じになるでしょうか。

#!/bin/sh
NAMESPACE=application

while True
do
  kubectl get pod -o wide -n $NAMESPACE | grep Terminating | awk '{print $1, $7'} | while read terminating
  do
    appl=$(echo $terminating | awk '{print $1}')
    node=$(echo $terminating | awk '{print $2}')

    kubectl get node $node -n $NAMESPACE -o jsonpath="{.spec.taints[*].effect}" | grep NoExecute > /dev/null
    if [ $? -eq 0 ]; then
      echo $node is NoExecute, $appl must be deleted
      kubectl delete pod $appl --force -n $NAMESPACE
    fi
    
  done

  sleep 10
done

特殊なことは何もありません。運用作業として行われるであろう

    • Terminatingが継続しているPodがあるぞ

 

    • なんでTerminatingなんだ?

 

    ノード障害が原因か。Podを停止して別のノードに移動させるか

という手順をストレートにコード化しただけです。実際にはノード障害の判定をもう少し慎重にするだとか、いきなりdeleteするのではなくて、この状況がしばらく継続していたらとか、さまざまな配慮を行うでしょうが本質は変わりません。

将Kubernetes进行操作符化

在将其转化为Operator的基础上,基本上不需要任何额外的事情。这个shell脚本本身就是一个Operator(可能有点极端)。

もちろん、このシェルスクリプトをKubernetes Operatorとして稼働させるには、少なくとも

コンテナ化して、Deployment Podにする
Podのdeleteを行う権限を持つserviceaccountと、それを使用するkubeconfigを作成して、シェルスクリプトに同梱する(もしくはSecretとして供給する)

など、やるべきことは多々あるのですが、本稿はOperatorの理解に焦点を当てているので、そうした周辺作業は取り上げない(サボる)ことにします。

このシェルスクリプトを抽象化すると、次のような形になります。

無限ループ {
    状態を確認する
    if 不都合があり、希望する状態から乖離している {
        不都合を修正して、希望の状態にする
    }
}

この中で、不都合を修正する作業をreconcileと呼び、これを繰り返し行うループをreconcile-loopと呼びます。例題のシェルスクリプトでは、Podの削除を行いました。コントローラが繰り返し行うループなので、control-loopとも呼ばれます。

在许多情况下,状态通常由自定义资源进行管理,但并不一定必须使用自定义资源来表示。在这里,我们希望”在故障节点上不存在处于Terminating状态的Pod”,我们使用现有的Pod状态和节点的taint来实现。

同様に、例えばDeplymentリソースが存在した場合、そこに定義されたPodが稼働していなければPodを起動するのがDeplymentコントローラーです。そこではDeplymentが(カスタムではないけれども)リソースであり、DeploymentリソースとDeploymentコントローラがDeployment Operatorを構成することで、Kubernetesの一般的な処理をこのデザインパターンで説明できます。

结束时

通过将StatefulSet Pod在节点故障时保持终止状态的现象转化为Shell脚本Operator的操作表达来解决,以此来描述Operator的基本结构和作用。

CRDとか、reconcile-loopとか、desired-stateとか、小難しい説明がいろいろ出てきて初学者は面食らうKubernetes Operatorですが、基本的にはそれほど難しいものではありません。一般的にインフラ運用で(シェルスクリプトなどとして)作成するツールと考え方としては同じことを、本稿では示せたと思います。

operator-SDKを使用して作成するOperatorもこの構造を踏襲します。ツールで生成した雛形に対して、状態の確認と状態の修正を行うコードを追加するわけです。memcached-operatorチュートリアルに挫折した方は、この構造を理解した上で再度チャレンジすることをお勧めします3。

在实际的生产环境中使用的Operator可能无法处理像此处展示的kubectl命令输出这样的粗糙设计。作为继续,我想进一步说明使用Python和Python Kubernetes客户端4通过Kubernetes API操作集群的Operator。

有很多不同于Go语言的operator sdk可供选择。CRD生成这一部分可能比较难理解,但至少应该能理解Go语言代码修正的目的。

这不是Python的Operator SDK,那是kopf。它也会推出的。

广告
将在 10 秒后关闭
bannerAds