PR
この連載が書籍になりました!『RancherによるKubernetes活用完全ガイド

Rancherコードリーディング入門(2/3)

2019年12月25日(水)
西脇 雄基
前回に続き、紙面の都合で「RancherによるKubernetes活用完全ガイド」に掲載されなかったパートをご紹介します。

Rancherコードリーディング入門(2/3)

前回の記事では、rancher/rancherレポジトリ直下のディレクトリやファイルについて、ざっくりどのような役割を持っているのか紹介しました。

pkgディレクトリ(https://github.com/rancher/rancher/tree/v2.1.5/pkg)にRancherのほとんどのロジックが含まれていることは前回の記事でおわかりいただけたと思います。

今回と次回の2回で、その中でも特に重要なpackage、Rancher Agentのロジックを含むpkg/agentや、Rancher APIのロジックを含むpkg/apiなどを紹介していきます。まず今回は、コードリーディング入門の連載2回目として、Rancher Agent(pkg/agent)、Rancher API(pkg/api)について詳しく紹介していきます。

2回目:Rancher Agent、Rancher APIを中心に紹介します
  • rancher/rancherレポジトリの歩き方(2/3)
    • Rancherのアプリケーションコード
      • pkg/agent
      • pkg/api
3回目:Rancher Controller、Rancherの周辺レポジトリについて紹介します
  • rancher/rancherレポジトリの歩き方(3/3)
    • Rancherのアプリケーションコード
      • pkg/controllers
  • rancherレポジトリ以外の重要なレポジトリについて
    • rancher/types
    • rancher/rke
    • rancher/kontainer-engine
    • rancher/norman

rancher/rancherレポジトリの歩き方(2/3)

pkgディレクトリ内のpackageを詳細に見ていく1つ目のトピックとして、Rancher Agentに関するロジックが含まれるpkg/agentから紹介します

pkg/agent

リスト1:pkg/agentのファイルとディレクトリ

pkg/agent/
├── clean/
├── cluster/
├── main.go
├── main_windows.go
└── node/

pkg/agentの説明

ディレクトリ/ファイル説明
clean/Rancher Agentのエントリポイントとなるmain.goは、単発で実行されるcleanupにも利用されるため、そのcleanupロジックが含まれる
cluster/Cluster Agent 特有の関数が含まれる
main.goRancher Agentのエントリポイントとなる
main_windows.goWindowsをWorkerとする場合のエントリポイント
node/Node Agent 特有の関数が含まれる

RancherのNode AgentとCluster Agentのエントリポイントになります。実は、Node AgentとCluster Agentは同じmain.goを利用しており、環境変数で次のように処理を分岐しています

リスト2:pkg/agent/main.go(の一部)

...
57
58 func isCluster() bool {
59         return os.Getenv("CATTLE_CLUSTER") == "true"
60 }
61
62 func getParams() (map[string]interface{}, error) {
63         if isCluster() {
64                 return cluster.Params()
65         }
66         return node.Params(), nil
67 }
...

Agentの動作は、Cluster Agent、Node Agent問わずとてもシンプルで、大きく分けて2つのロジックが実装されていると理解できます。

以下にコードの一部を掲載しました。run関数は、main関数から呼ばれており、Rancher Agentのメインとなるロジックが定義されています。このロジックは、166~197行と199~215行の2つのロジックに分けられます。

166~197行目のロジックは、WebSocket Sessionが確立された後に呼び出され、定期的にrkenodeconfigclient.ConfigClientを呼び出しています。rkenodeconfigclient.ConfigClientは、/v3/connect/config APIを呼び出し、Nodeで動かすべきシステムコンテナのリストと作成すべきファイルを取得し、コンテナの実行、ファイルの作成を実施しています。173行目を見てわかるとおり、このロジックは、Rancher Node Agentのみで実行され、Cluster Agentの場合このロジックを実行しません。

199~215行目のロジックは、WebSocket Sessionを確立し、Proxy Serverとしてふるまうためのロジックが含まれています。remotedialer.ClientConnectにRancher Serverのエンドポイントと無名関数(func(proto, address string))を1つ渡しています。このremotedialer.ClientConnectは、WebSocket Sessionを確立した後に、Proxy Serverとしてふるまいますが、Proxy ServerでUnix SocketへのProxyをする際には、「/var/run/docker.sock」以外には、Proxyを許容しないように無名関数を指定し、Proxy要求の際のProtocolとAddressをチェックしています。Sessionが閉じられてしまった場合は、5秒ごとにリトライをするように設定されています。このロジックは、Rancher Node Agent、Cluster Agent両方で利用されています。

リスト3:pkg/agent/main.go(の一部)

139 func run() error {
... <省略>
165
166     onConnect := func(ctx context.Context) error {
167         connected()
168         connectConfig := fmt.Sprintf("https://%s/v3/connect/config", serverURL.Host)
169         if err := rkenodeconfigclient.ConfigClient(ctx, connectConfig, headers, writeCertsOnly); err != nil {
170             return err
171         }
172
173         if isCluster() {
174             return nil
175         }
176
177         if err := cleanup(context.Background()); err != nil {
178             return err
179         }
180
181         go func() {
182             logrus.Infof("Starting plan monitor")
183             for {
184                 select {
185                 case <-time.After(2 * time.Minute):
186                     err := rkenodeconfigclient.ConfigClient(ctx, connectConfig, headers, writeCertsOnly)
187                     if err != nil {
188                         logrus.Errorf("failed to check plan: %v", err)
189                     }
190                 case <-ctx.Done():
191                     return
192                 }
193             }
194         }()
195
196         return nil
197     }
198
199     for {
200         wsURL := fmt.Sprintf("wss://%s/v3/connect", serverURL.Host)
201         if !isConnect() {
202             wsURL += "/register"
203         }
204         logrus.Infof("Connecting to %s with token %s", wsURL, token)
205         remotedialer.ClientConnect(wsURL, http.Header(headers), nil, func(proto, address string) bool {
206             switch proto {
207             case "tcp":
208                 return true
209             case "unix":
210                 return address == "/var/run/docker.sock"
211             }
212             return false
213         }, onConnect)
214         time.Sleep(5 * time.Second)
215     }
216 }

Rancher Agentに関するロジックは、これのみになります。ここを見てもわかりますが、Rancher Agentは基本的には、Proxy Serverの起動のみを実施し、それ以外のイベントはすべてRancher ServerのManagement ControllerかUser Controllerがハンドリングの責任を持ちます。

pkg/api

続いて紹介するのは、Rancher API関連のロジックが含まれるpkg/apiです。

リスト4:pkg/apiのファイルとディレクトリ

pkg/api/
├── controllers/
├── customization/
├── server/
├── server.go
└── store/

pkg/apiの説明

ディレクトリとファイル説明
controllers/API Controllerの実装が含まれる
customization/Kubernetesリソースを保存する以外のAPIをManagement APIとして提供する際のロジック(action)が含まれる
server/Customer Resource Definitionの作成やRancher API Controllerの初期化など、Rancher APIに関わるすべての初期化を実施するロジックが含まれる
server.goserver/server.goに利用されるRancher APIに関する関数が含まれる
store/Rancherで扱う各リソースの保存方法を定義している。基本的にはKubernetesにリソースを保存するが、リソースの値によって保存先を切り替えたり、特定のフィールドを削除したりといった処理が定義されている

/pkg/apiに含まれるロジックは、以下の4つに大別されます。

  • Rancher API Controllerの実装(/pkg/api/controllers)
  • Rancher APIの拡張(/pkg/api/customization)
  • Rancher APIのバックエンドとなる保存先の設定(/pkg/api/store)
  • Rancher APIの初期化(/pkg/api/server)

1つずつ見ていきましょう。

/pkg/api/controllers

Rancher API Controllerは、Rancher API Serverの設定/挙動を管理するためのRancherのControllerです。次のように6つのディレクトリが/pkg/api/controllersの配下に存在しており、1ディレクトリが1つのControllerにマッチします。

リスト5:pkg/api/controllers

pkg/api/controllers/
├── dynamiclistener
├── dynamicschema
├── samlconfig
├── settings
├── usercontrollers
└── whitelistproxy

6つのControllerがどのような役割を持っているのか1つずつ紹介します。

dynamiclistener

dynamiclistenerコントローラは、Rancher API ServerがどのProtocolでListeningするかを設定する役割を持っています。現在は、httpsとacmeをサポートしています。

dynamicschema

dynamicschemaコントローラは、API Serverが提供しているAPI Schemaを拡張、更新する役割を持っています。後ほど紹介しますが、Rancher APIは、Norman Frameworkをベースに実装されています。

Norman Frameworkでは、API Serverに対してAPI Schemaオブジェクトを登録することで、APIを拡張することできます。Rancherが利用しているCustomer Resource Definition(以下、CRD)の1つに、dynamicschemaリソースがあります。

このdynamicschemaリソースは、このAPI Schemaオブジェクトがシリアライズされたものになっており、このdynamicschemaコントローラが新しいdynamicschemaリソースを検知すると、該当のリソースからAPI Schemaオブジェクトを生成し、API Serverに設定します。

このようにdynamicschemaコントローラは、Rancher APIを拡張する役割を持っています。

samlconfig

samlconfigコントローラは、Rancher GUIの認証プロバイダの設定であるauthconfigリソースを監視し、SAMLを利用した認証プロバイダがEnableにされたら、SAML認証(Security Assertion Markup Language)で利用するエンドポイントの初期化を実施する役割を持ちます。

settings

settingsコントローラは、Rancherの設定を管理しています。これはコントローラのディレクトリ配下に配置されているものの、特定のリソースを監視し、何かアクションを起こすということはしません。

settingsコントローラは、Rancher Server起動時に、Rancher Serverコンテナの「CATTLE_<Setting項目名>」の環境変数を利用して(指定されていなかった場合はコード上で定義されているデフォルト値を利用します)Settingリソースを作成し、Rancher Serverの設定を管理しています。

起動後は、Rancher Serverの他のモジュール、コントローラが設定を参照する際に、このコントローラを経由して、Settingリソースの値を取得し、それを設定値として解釈します

usercontrollers

userscontrollersコントローラは、起動されると2つのgoroutineとclusterリソースの監視を開始し、常に正しいRancher Serverがclusterownerになることを保証する責任を持ちます。

whitelistproxy

whitelistproxyコントローラは、docker-machineのNode Providerを示すnodedriverリソースを監視し、Rancher APIの/meta/proxy機能のProxy先のWhitelistをメンテナンスする責任を持ちます。

/pkg/api/customization

/pkg/api/customizationは、Rancher APIの拡張のためのロジックが含まれています。

/pkg/api/customizationディレクトリの下は、リソース名ごとのディレクトリになっており、各ディレクトリは、Kubernetesからリソースを取得した後にAPIで表示するためのリソースに変換するためのロジック(formatter.go)や、APIの拡張を実装したAction Handler(actionhandler.go)が含まれます。

リスト6:pkg/api/customization

pkg/api/customization/
├── alert/
├── app/
├── authn/
├── catalog/
├── cluster/
       ├── actionhandler.go
       ├── formatter.go
       └── shell.go
├── clusterregistrationtokens/
...

Rancher APIの1種であるRancherのManagement APIは、Kubernetes APIのProxyのようにふるまい、Management API経由で作成したリソースは、Kubernetes APIを通してKubernetesのリソースとして作成されます。また、逆にManagement API経由で取得するリソースは、Kubernetes APIを通してリソースが取得されます。

しかし、リソースによっては、特定のフィールドを動的に変換する必要があります。基本的な、フィールドの変換ロジックは、vendor/github.com/rancher/types/apis/management.cattle.io/v3/schema/schema.goで定義されていますが、そこでカバーできないものなどは、pkg/api/customization/<リソース名>でカバーしています。

またAction Handlerについては、リソースに対して、リソースを更新する以外のアクションを実装しています。Management APIはKubernetesのProxyとしてふるまうように設計されているため、リソースに対して作成、更新、削除以外のアクションを実施したい時に、APIが表現しづらいという課題があります。その課題に対してRancherは、各リソースの作成、削除、更新のAPI以外にActionという考え方を用意しています。

例えば、Cluster APIのActionの例として下記のAPIがあります

リスト7:Cluster APIのActionの例

https://<rancher-server>/v3/clusters/c-2t7gd?action=generateKubeconfig

これは、Clusterリソースのkubeconfigを取得するためのAPIです。「Clusterリソースのkubeconfigを取得する」という動作は、リソースの作成、削除、更新には含めづらいので、Actionとして実装されています。このように、リソースの作成、削除、更新ではないリソースに関わる動作をAPIとして提供したい際に、このpkg/api/customization/配下にactionhandlerという形で実装しています。

/pkg/api/store

続いて、紹介するのは、/pkg/api/storeになります。このディレクトリの配下も、リソース名のディレクトリになっています。各ディレクトリは、基本的にはstore.goを1つ持ち、Rancher APIよりリソースの作成、削除、更新をする際に、最終的に呼ばれるAPIのストレージレイヤーを定義しています。

基本的には、Kubernetes APIを用いてリソースを作成することになりますが、このstoreパッケージ内では、そのための前処理や後処理、またParent Kubernetesに保存するのかChild Kubernetesに保存するのかという部分をコントロールしています。

リスト8:pkg/api/store

pkg/api/store/
├── cert/
├── cluster/
├── ingress/
├── namespace/
├── node/
       └── store.go
├── nodetemplate/
...

/pkg/api/server

最後に、Rancher APIに関する初期化(/pkg/api/server)に関するロジックを紹介します。このディレクトリは、次のように2つのディレクトリと1つのファイルを持ちます。

リスト9:pkg/api/server

pkg/api/server/
├── managementstored/
├── server.go
└── userstored/

このserver.goは、Rancherのserver/server.goから呼ばれ、Rancher APIの初期化のためのエントリポイントになります。このpkg/api/server/server.goからmanagementstoredとuserstoredのロジックを評価し、CRDの作成を実施しています。また、このserver.goは、API Controllerの初期化も実施しています。

LINE株式会社

Verda室 Verdaプラットフォーム開発室 マネージャ兼Senior Software Engineer

LINE株式会社にてOpenStackとManaged Kubernetes Clusterの開発/運営を担当しているVerdaプラットフォーム開発チームリード。大規模なクラスターの開発者およびオペレーターとして3年以上OpenStackに取り組んでいる。2018年からはRancherを利用したManaged Kubernetes Serviceの開発/運用も開始。

現在は両プロジェクトを担当し、安定したプライベートクラウドプラットフォームの提供に務める。

連載バックナンバー

クラウド技術解説
第11回

Rancherコードリーディング入門(3/3)

2020/2/19
前回に続き、紙面の都合で「RancherによるKubernetes活用完全ガイド」に掲載されなかったパートをご紹介します。
クラウド技術解説
第10回

Rancherコードリーディング入門(2/3)

2019/12/25
前回に続き、紙面の都合で「RancherによるKubernetes活用完全ガイド」に掲載されなかったパートをご紹介します。
クラウド技術解説
第9回

Rancherコードリーディング入門(1/3)

2019/11/5
今回からは、紙面の都合で「RancherによるKubernetes活用完全ガイド」に掲載されなかったパートをご紹介します。

Think IT会員サービス無料登録受付中

Think ITでは、より付加価値の高いコンテンツを会員サービスとして提供しています。会員登録を済ませてThink ITのWebサイトにログインすることでさまざまな限定特典を入手できるようになります。

Think IT会員サービスの概要とメリットをチェック

他にもこの記事が読まれています