はじめに
この記事では、Argo Rolloutsのソースコードを通して、ArgoがKubernetesをどのように操作するのか、その仕組みの概要を理解できる内容となってます。
ドキュメントを読んでもいまいち分からなかった方にとって、Argoの動作メカニズムを少しでも理解する手助けになれば幸いです。
前提としてそれぞれの機能を深掘りはせず、あくまでKubernetesをどのように操作するのかをまとめたものになります。
そのため、それぞれの詳しい機能に関しては公式ドキュメント1や他の記事を参考にしていただくようお願いいたします。
Argo Rolloutsとは
Argo Rollouts(アルゴロールアウト) は、Kubernetes環境にデプロイメントのための機能を提供するGo言語で書かれたオープンソースのツールセットです。2
主な特徴としては、以下のような高度なデプロイメント機能が提供されていることです。
- ブルー/グリーンデプロイメント
- カナリアデプロイメント
- 自動ロールバックとプロモーション
Argo Rolloutsは、KubernetesコントローラーとCRDのセットを利用して、これらの高度なデプロイ機能を提供しています。1
オープンソースであることから、Githubにソースコードが公開されています。
Argo Projectには、Argo Rolloutsの他にも以下のプロジェクトがあります。
Argo Rolloutsが提供する機能
Argo Rolloutsが提供する主な機能は以下の2つです。
- kubectl-argo-rollouts
- rollouts-controller
kubectl-argo-rollouts
は、Kubernetesを操作するときに利用するコマンドkubectl
の拡張版コマンドのようなものです。
rollouts-controller
について、公式ドキュメントには以下のような記載がありました。3
Similar to the deployment object, the Argo Rollouts controller will manage the creation, scaling, and deletion of ReplicaSets. These ReplicaSets are defined by the spec.template field inside the Rollout resource, which uses the same pod template as the deployment object.
rollouts-controller
はKubernetesクラスター内で、ReplicaSetsの作成、スケーリング、削除を管理するものです。
Kubernetesリソースの操作について
kubectl-argo-rollouts
まず、前述した機能の内kubectl-argo-rollouts
を確認します。
やっていることは、NewCmdArgoRollouts
を実行なので、次はそのソースコードを確認します。
func main() {
klog.InitFlags(nil)
logutil.SetKLogLogger(log.New())
streams := genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
o := options.NewArgoRolloutsOptions(streams)
root := cmd.NewCmdArgoRollouts(o)
if err := root.Execute(); err != nil {
os.Exit(1)
}
}
NewCmdArgoRollouts
NewCmdArgoRollouts
には、色々なコマンドをセットして実行していることが分かります。
なかには以下のようなArgo CDのダッシュボードで見慣れた機能があります。
今回は「Abort」=NewCmdAbort
のソースコードを確認します。
// 抜粋
o.AddKubectlFlags(cmd)
cmd.AddCommand(create.NewCmdCreate(o))
cmd.AddCommand(get.NewCmdGet(o))
cmd.AddCommand(lint.NewCmdLint(o))
cmd.AddCommand(list.NewCmdList(o))
cmd.AddCommand(pause.NewCmdPause(o))
cmd.AddCommand(promote.NewCmdPromote(o))
cmd.AddCommand(restart.NewCmdRestart(o))
cmd.AddCommand(version.NewCmdVersion(o))
cmd.AddCommand(abort.NewCmdAbort(o))
cmd.AddCommand(retry.NewCmdRetry(o))
NewCmdAbort
rolloutIf.Patch
を実行しています。
Patch
とみて、まず思いつくのが、REST APIにおけるPATCH
です。(一般的には、リソースへの部分的な変更を適用に利用する)
次はRolloutInterface
を確認します。
const (
abortPatch = `{"status":{"abort":true}}`
)
// 省略
func AbortRollout(rolloutIf clientset.RolloutInterface, name string) (*v1alpha1.Rollout, error) {
ctx := context.TODO()
ro, err := rolloutIf.Patch(ctx, name, types.MergePatchType, []byte(abortPatch), metav1.PatchOptions{}, "status")
if err != nil && k8serrors.IsNotFound(err) {
ro, err = rolloutIf.Patch(ctx, name, types.MergePatchType, []byte(abortPatch), metav1.PatchOptions{})
}
return ro, err
}
RolloutInterface
c.client.Patch
は一般的なREST APIを叩くときのソースコードに見えます。
client
の正体はk8s.io/client-goです。
// 抜粋
import (
rest "k8s.io/client-go/rest"
)
type RolloutInterface interface {
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Rollout, err error)
}
// rollouts implements RolloutInterface
type rollouts struct {
client rest.Interface
ns string
}
// Patch applies the patch and returns the patched rollout.
func (c *rollouts) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Rollout, err error) {
result = &v1alpha1.Rollout{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("rollouts").
Name(name).
SubResource(subresources...).
VersionedParams(&opts, scheme.ParameterCodec).
Body(data).
Do(ctx).
Into(result)
return
}
k8s.io/client-go
client-go
とは、Kubernetesクラスターをgoアプリから操作するためのライブラリです。
READMEには、以下の記載があります。
The kubernetes package contains the clientset to access Kubernetes API.
client-go
を使うことで、Kubernetes APIにアクセスできます。
以下のKubernetesクラスターを構成するコンポーネントから分かる通り、Kubernetes APIは他コンポーネントからリクエストを処理し、同じクラスター内のリソースを操作できます。4 5
Argo Rolloutsは内部でKubernetes APIを叩くことで、Kubernetesのリソースを操作していたことが分かりました。
なお、前述の「Abort」については、Kubernetes APIに対してPATCH
でリクエストを送信し、クラスター内で以下のコマンドを実行しているイメージになります。
kubectl patch rollout --type merge -p {"status":{"abort":true}}
(おまけ)自動生成ツール
Argo Rolloutsのソースコードを読む中でマークコメントが多かったです。
そのため、ここからはおまけとして、筆者が気になった自動生成ツールを記載します。
前提として、ArgoはCustom Resource Definitions(CRD)という、Kubernetes APIを拡張した独自のリソース(Custom Resource=CR)を定義しています。
CRDは2つの構成要素があり、概要は以下になります。6
- カスタムリソース:特定のクラスター環境に応じたリソースをカスタムリソースを定義する
- カスタムコントローラー:指定したリソースの状態を監視しそれを維持するように動作する
CRについては以下のディレクトリにあります。
CRDの一例は以下にあります。
CRDには以下のマーカーコメントがあります。
// 抜粋
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:resource:path=rollouts,shortName=ro
//+kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.HPAReplicas,selectorpath=.status.selector
// +kubebuilder:subresource:status
-
+genclient
- client-genというCRDに対するクライアントコードを自動生成するツールに関するマーカーコメント
- 前述の
RolloutInterface
を定義しているファイルには、client-gen
によって生成されたコードであることを示す記載がありました
// Code generated by client-gen. DO NOT EDIT.
-
+k8s:deepcopy-gen
- deepcopy-genというdeepcopyを自動生成するツールに関するマーカーコメント
-
zz_generated.deepcopy.goには、
deepcopy-gen
によって生成されたコードであることを示す記載がありました
// Code generated by deepcopy-gen. DO NOT EDIT.
-
+kubebuilder
- KubebuilderというKubernetesを拡張するためのカスタムコントローラー/オペレーターを開発するためのフレームワーク
前述の自動生成ツールを利用することで、Kubernetesを操作する実装を簡易化していることが分かります。
さいごに
Argo Rolloutsは、Kubernetes APIにリクエストを投げることで、リソースを操作していることが分かりました。
しかし、詳細までは確認できていないため、今後調べていこうと思います!
おまけでKubernetesに関する自動生成ツールについても簡単に調べましたがすごく充実している印象を受けました。
やはりKubernetesとgoの親和性が高いですね。