学生の早期導入のようにいくつかの変更は、このような何かの前に、あるCSのコンテンツを取得kubectl実行最新kubernetes出力に気づくことがあります。
> kubectl get componentstatuses
NAME STATUS MESSAGE ERROR
controller-manager Healthy ok
scheduler Healthy ok
etcd-0 Healthy {"health":"true"}
今すぐ次のようになります。
> kubectl get componentstatuses
NAME Age
controller-manager <unknown>
scheduler <unknown>
etcd-0 <unknown>
それが最初のクラスタの展開の問題が見えるかもしれませんが、ステータス、メッセージおよびその他の情報がkubectl GET CS -o YAMLであるが、印刷されないことが判明。オリジナルはなぜそこには、この変更、私たちはよく見ている、CSの出力形式が変更され得るkubectlです。
ポジショニングの問題の原因
バージョン1.15は、通常発見された以前の試みは、デバッグコードがAddHandlers方法は様々なリソースの登録の印刷機能を担当してk8s.io/kubernetes/pkg/printers/internalversion/printers.goの印刷コードcomponentstatuses、上に見出さで来ます。
// AddHandlers adds print handlers for default Kubernetes types dealing with internal versions.
// TODO: handle errors from Handler
func AddHandlers(h printers.PrintHandler) {
......
componentStatusColumnDefinitions := []metav1beta1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
{Name: "Status", Type: "string", Description: "Status of the component conditions"},
{Name: "Message", Type: "string", Description: "Message of the component conditions"},
{Name: "Error", Type: "string", Description: "Error of the component conditions"},
}
h.TableHandler(componentStatusColumnDefinitions, printComponentStatus)
h.TableHandler(componentStatusColumnDefinitions, printComponentStatusList)
k8s.io/kubernetes/pkg/kubectl/cmd/get/humanreadable_flags.go位置AddHandlersへのコール(ステージングで見つけることができない場合はステージングへの移行を、見つけるために行ってきました)、以下の行32場所:
// ToPrinter receives an outputFormat and returns a printer capable of
// handling human-readable output.
func (f *HumanPrintFlags) ToPrinter(outputFormat string) (printers.ResourcePrinter, error) {
if len(outputFormat) > 0 && outputFormat != "wide" {
return nil, genericclioptions.NoCompatiblePrinterError{Options: f, AllowedFormats: f.AllowedFormats()}
}
showKind := false
if f.ShowKind != nil {
showKind = *f.ShowKind
}
showLabels := false
if f.ShowLabels != nil {
showLabels = *f.ShowLabels
}
columnLabels := []string{}
if f.ColumnLabels != nil {
columnLabels = *f.ColumnLabels
}
p := printers.NewTablePrinter(printers.PrintOptions{
Kind: f.Kind,
WithKind: showKind,
NoHeaders: f.NoHeaders,
Wide: outputFormat == "wide",
WithNamespace: f.WithNamespace,
ColumnLabels: columnLabels,
ShowLabels: showLabels,
})
printersinternal.AddHandlers(p)
// TODO(juanvallejo): handle sorting here
return p, nil
}
ビューhumanreadable_flags.goファイルの変更履歴、故意以降のバージョン1.16からの影響、オブジェクトの内側に印刷を削除2019.6 0.28で発見されました。
なぜ午前
私はここで、いくつかの個人的な憶測を行うプロセス全体を復元するために、公式の説明を発見していません。
当初kubectlで印刷されたフォームのAPIリソースを実現
だから、他のクライアントのために、apiserverで、あること、サービス側に印刷形成する必要があり、繰り返しのことを行う必要があり、かつ一貫性のない動作を実現することができます
クライアントが完全に除去されないように、緩やかなプロセスを介して、支持体のサーバ側印刷、それが同時にサポートされている、kubectl裁判官は、ダイレクト印刷にサーバテーブルから返された、または印刷特定は、特定のオブジェクトのクライアントとサーバーを使用してオブジェクトプリントはk8s.io/kubernetes/pkg/printers/internalversion/printers.goを呼び出している達成され、
1.11バージョン意志kubectlコマンドラインパラメータの
--server-print
デフォルト値はtrueです1.16バージョンに、コミュニティは、すべてのオブジェクトがサーバーに移動し、印刷クライアントkubectlが、その後に加えて行くされていると思うかもしれません
しかし、実際のcomponentstatusesにcomponentstatusesオブジェクトはポッドは結果の、etcdから取得されているような、他のオブジェクト、リアルタイムですべての時間を取得し、他のオブジェクトのような、いないキャッシュからのものではない、おそらく主な理由は、なぜ漏れ、不足していますk8s.io/kubernetes/pkg/registry/core/pod/storage/storage.goフォーマット定義は、次の行の15点の位置:
// NewStorage returns a RESTStorage object that will work against pods. func NewStorage(optsGetter generic.RESTOptionsGetter, k client.ConnectionInfoGetter, proxyTransport http.RoundTripper, podDisruptionBudgetClient policyclient.PodDisruptionBudgetsGetter) PodStorage { store := &genericregistry.Store{ NewFunc: func() runtime.Object { return &api.Pod{} }, NewListFunc: func() runtime.Object { return &api.PodList{} }, PredicateFunc: pod.MatchPod, DefaultQualifiedResource: api.Resource("pods"), CreateStrategy: pod.Strategy, UpdateStrategy: pod.Strategy, DeleteStrategy: pod.Strategy, ReturnDeletedObject: true, TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)}, }
componentstatusesは本当のStorgeを使用していないが、それが欠けていた、比較的控えめです。
回避策
あなたは、元の同様のコンテンツを印刷したい場合は、唯一のテンプレート:
kubectl get cs -o=go-template='{{printf "|NAME|STATUS|MESSAGE|\n"}}{{range .items}}{{$name := .metadata.name}}{{range .conditions}}{{printf "|%s|%s|%s|\n" $name .status .message}}{{end}}{{end}}'
出力:
|NAME|STATUS|MESSAGE|
|controller-manager|True|ok|
|scheduler|True|ok|
|etcd-0|True|{"health":"true"}|
深印刷処理の流れ
出力の形式は、YAML、JSON、テンプレート、およびフォームいくつかのスタイル、-oパラメータは、デフォルトの印刷形式でなくて、私たちは詳細に分析し、印刷された形である上記の問題は、次の印刷がそこに着くkubectl、kubectl -oパラメータによって制御され、出力処理。
kubectl
コード入力k8s.io/kubernetes/pkg/kubectl/cmd/get/get.goを取得kubectl、ファイル名を指定して実行フィールドには、コマンドの実行方法です。
func NewCmdGet(parent string, f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
......
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd))
cmdutil.CheckErr(o.Run(f, cmd, args))
},
......
}
完全な方法プリンタの初期化が完了し、それがk8s.io/kubernetes/pkg/kubectl/cmd/get/get_flags.goに位置しています:
func (f *PrintFlags) ToPrinter() (printers.ResourcePrinter, error) {
......
if p, err := f.HumanReadableFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
return p, err
}
......
}
-oパラメータを指定せずに使用する、上述の方法は、f.HumanReadableFlags.ToPrinter(OUTPUTFORMAT)を返し、最後にk8s.io/cli-runtime/pkg/printers/tableprinter.goに位置HumanReadablePrinterオブジェクトを返します。
// NewTablePrinter creates a printer suitable for calling PrintObj().
func NewTablePrinter(options PrintOptions) ResourcePrinter {
printer := &HumanReadablePrinter{
options: options,
}
return printer
}
そして、ヘッダプリントサーバー、プリントサーバーの追加、非常に重要なアクションがあり、HTTPリクエストを送信する前に、apiserverにHTTPリクエストを送信し終えた、主に完全なファイル名を指定して実行した後、メインコマンド実行処理に戻り、アクションの結果を印刷で--server-print
1.11デフォルトからtrueに、パラメータ制御なので、サーバーがサポートする変換はmetav1beta1.Tableタイプを返します場合、また無効プリントサーバーにfalseに設定することができます。
func (o *GetOptions) transformRequests(req *rest.Request) {
......
if !o.ServerPrint || !o.IsHumanReadablePrinter {
return
}
group := metav1beta1.GroupName
version := metav1beta1.SchemeGroupVersion.Version
tableParam := fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", version, group)
req.SetHeader("Accept", tableParam)
......
}
:それはmetav1.Statusが続いている場合は、最後の印刷も、プロセッサの特殊なタイプを持っているデフォルトのプロセッサで終わるだろう、サーバーを印刷する直接戻った場合は、最初の決定HumanReadablePrinter.PrintObjメソッド呼び出し、metav1beta1.Tableタイプです
func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) error {
......
// Parameter "obj" is a table from server; print it.
// display tables following the rules of options
if table, ok := obj.(*metav1beta1.Table); ok {
return printTable(table, output, localOptions)
}
// Could not find print handler for "obj"; use the default or status print handler.
// Print with the default or status handler, and use the columns from the last time
var handler *printHandler
if _, isStatus := obj.(*metav1.Status); isStatus {
handler = statusHandlerEntry
} else {
handler = defaultHandlerEntry
}
......
if err := printRowsForHandlerEntry(output, handler, eventType, obj, h.options, includeHeaders); err != nil {
return err
}
......
return nil
}
componetstatusesをリアルタイムに取得されるので、デフォルトのプロセッサは、年齢が不明でプリントアウトしていないetcdで、ストレージを作成するための時間がない、名前と年齢2を印刷します。
apiserver
apiserverサーバーの処理の流れで見てみましょう、k8s.io/apiserver/pkg/endpoints/handlersディレクトリ内の外部RESTインターフェースを提供し、kubectl CSはListResource方法でget.go入り、3つの主要なステップをリストに掲載するなど、次のされています。
func ListResource(r rest.Lister, rw rest.Watcher, scope *RequestScope, forceWatch bool, minRequestTimeout time.Duration) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
......
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
......
result, err := r.List(ctx, &opts)
......
transformResponseObject(ctx, scope, trace, req, w, http.StatusOK, outputMediaType, result)
......
}
}
NegotiateOutputMediaTypeプリントサーバかどうかを含めて、ヘッダに設けられたサーバを要求するクライアントの一部の挙動に応じて、k8s.io/kubernetes/pkg/registryで具現記憶層から取得r.Listリソースデータ、; transformResponseObjectは、クライアントに結果を返します。
私はtransformResponseObjectをお話しましょう、変換フィールドNegotiateOutputMediaTypeに従って変換かどうかを判断しますそれは表のタイプにリソースr.Listの変換表の特定のタイプを返します場合は、変更のoutputMediaTypeターゲット・タイプが返されました:
func doTransformObject(ctx context.Context, obj runtime.Object, opts interface{}, mediaType negotiation.MediaTypeOptions, scope *RequestScope, req *http.Request) (runtime.Object, error) {
......
switch target := mediaType.Convert; {
case target == nil:
return obj, nil
......
case target.Kind == "Table":
options, ok := opts.(*metav1beta1.TableOptions)
if !ok {
return nil, fmt.Errorf("unexpected TableOptions, got %T", opts)
}
return asTable(ctx, obj, options, scope, target.GroupVersion())
......g
}
}
非安定ファイナルコールscope.TableConvertor.ConvertToTableの上mediaType.Convertは、この変換をトリガすることなく空であるので、それは、NegotiateOutputMediaTypeで問題の嘘空である理由、そして、それは最終的に呼び出します、フォームの変換作業、この記事の問題を完了k8s.io \ apiserver \ PKG \エンドポイント\ハンドラ\ AllowsMediaTypeTransform方法rest.go scope.TableConvertorは、最終的に呼び出しても、表に変換するので、空であります:
func (scope *RequestScope) AllowsMediaTypeTransform(mimeType, mimeSubType string, gvk *schema.GroupVersionKind) bool {
......
if gvk.GroupVersion() == metav1beta1.SchemeGroupVersion || gvk.GroupVersion() == metav1.SchemeGroupVersion {
switch gvk.Kind {
case "Table":
return scope.TableConvertor != nil &&
mimeType == "application" &&
(mimeSubType == "json" || mimeSubType == "yaml")
......
}
例えばcomponentstatusesとしてapiserver初期化、リソースの各タイプは、グローバル、ポッドがある場合RequestScopeが作成され、さらにトラッキングは、以下のようにグローバル初期化プロセスであるがあります。
InstallAPIsによってk8s.io \ kubernetes \ PKG \マスター\ InstallLegacyAPIとInstallAPIs方法、componentStatusesが含まれているNewLegacyRESTStorage方法は、以下を参照してください特定の古いリソースのいくつかのために主に前者master.go、他のリソースでapiserver初期エントリ初期設定:
// InstallLegacyAPI will install the legacy APIs for the restStorageProviders if they are enabled.
func (m *Master) InstallLegacyAPI(c *completedConfig, restOptionsGetter generic.RESTOptionsGetter, legacyRESTStorageProvider corerest.LegacyRESTStorageProvider) error {
legacyRESTStorage, apiGroupInfo, err := legacyRESTStorageProvider.NewLegacyRESTStorage(restOptionsGetter)
......
}
初期化ストレージ:
func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter generic.RESTOptionsGetter) (LegacyRESTStorage, genericapiserver.APIGroupInfo, error) {
apiGroupInfo := genericapiserver.APIGroupInfo{
PrioritizedVersions: legacyscheme.Scheme.PrioritizedVersionsForGroup(""),
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{},
Scheme: legacyscheme.Scheme,
ParameterCodec: legacyscheme.ParameterCodec,
NegotiatedSerializer: legacyscheme.Codecs,
}
......
restStorageMap := map[string]rest.Storage{
"pods": podStorage.Pod,
"pods/attach": podStorage.Attach,
"pods/status": podStorage.Status,
"pods/log": podStorage.Log,
"pods/exec": podStorage.Exec,
"pods/portforward": podStorage.PortForward,
"pods/proxy": podStorage.Proxy,
"pods/binding": podStorage.Binding,
"bindings": podStorage.LegacyBinding,
"podTemplates": podTemplateStorage,
"replicationControllers": controllerStorage.Controller,
"replicationControllers/status": controllerStorage.Status,
"services": serviceRest,
"services/proxy": serviceRestProxy,
"services/status": serviceStatusStorage,
"endpoints": endpointsStorage,
"nodes": nodeStorage.Node,
"nodes/status": nodeStorage.Status,
"nodes/proxy": nodeStorage.Proxy,
"events": eventStorage,
"limitRanges": limitRangeStorage,
"resourceQuotas": resourceQuotaStorage,
"resourceQuotas/status": resourceQuotaStatusStorage,
"namespaces": namespaceStorage,
"namespaces/status": namespaceStatusStorage,
"namespaces/finalize": namespaceFinalizeStorage,
"secrets": secretStorage,
"serviceAccounts": serviceAccountStorage,
"persistentVolumes": persistentVolumeStorage,
"persistentVolumes/status": persistentVolumeStatusStorage,
"persistentVolumeClaims": persistentVolumeClaimStorage,
"persistentVolumeClaims/status": persistentVolumeClaimStatusStorage,
"configMaps": configMapStorage,
"componentStatuses": componentstatus.NewStorage(componentStatusStorage{c.StorageFactory}.serversToValidate),
}
......
apiGroupInfo.VersionedResourcesStorageMap["v1"] = restStorageMap
return restStorage, apiGroupInfo, nil
}
RESTインターフェースハンドラ登録、ハンドラはRequestScopeが含まれ、TableConvertor requestScopeフィールドは上記NewLegacyRESTStorageが作成に対応する記憶、すなわちストレージリソース、例えばcomponentStatusesから除去され、componentstatus.NewStorage(componentStatusStorage {c.StorageFactory} .serversToValidate)は、あります抽出方法はrest.TableConvertorストレージインターフェイスを達成するために、あるアサーション・ストレージ(rest.TableConvertor)のタイプである、または空を取り出します。
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws *restful.WebService) (*metav1.APIResource, error) {
......
tableProvider, _ := storage.(rest.TableConvertor)
var apiResource metav1.APIResource
......
reqScope := handlers.RequestScope{
......
// TODO: Check for the interface on storage
TableConvertor: tableProvider,
......
for _, action := range actions {
......
switch action.Verb {
......
case "LIST": // List all resources of a kind.
doc := "list objects of kind " + kind
if isSubresource {
doc = "list " + subresource + " of objects of kind " + kind
}
handler := metrics.InstrumentRouteFunc(action.Verb, group, version, resource, subresource, requestScope, metrics.APIServerComponent, restfulListResource(lister, watcher, reqScope, false, a.minRequestTimeout))
......
}
// Note: update GetAuthorizerAttributes() when adding a custom handler.
}
......
}
具体的にはストレージのcomponentStatusesを参照するに、k8s.io \ kubernetes \ PKG \レジストリ\コア\ componentstatus \ rest.go、それが実際に実装されていないrest.TableConvertorインタフェースので、その結果、空であるにcomponentStatusesのTableConvertorフィールドRequestScopeハンドラ問題:
type REST struct {
GetServersToValidate func() map[string]*Server
}
// NewStorage returns a new REST.
func NewStorage(serverRetriever func() map[string]*Server) *REST {
return &REST{
GetServersToValidate: serverRetriever,
}
}
コードの修正
根本原因を見つけた後、修理は、比較的単純であるrest.TableConvertorストレージ・インタフェースを達成する必要があり、インターフェイスは、中\ rest.goを休まk8s.io \ apiserver \ PKG \レジストリ\で定義されています。
type TableConvertor interface {
ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1beta1.Table, error)
}
他のリソース・ストレージ・コードを参照し、変更k8s.io \ kubernetes \ PKG \レジストリ\コア\ componentstatus \ rest.go 問題が解決され、場合csは、おなじみのフォームをプリントアウトし得るkubectl以下のコードkubectl get cs --server-print=false
はまだ唯一の名前を印刷し、年齢2コラム:
type REST struct {
GetServersToValidate func() map[string]*Server
tableConvertor printerstorage.TableConvertor
}
// NewStorage returns a new REST.
func NewStorage(serverRetriever func() map[string]*Server) *REST {
return &REST{
GetServersToValidate: serverRetriever,
tableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
}
}
func (r *REST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1beta1.Table, error) {
return r.tableConvertor.ConvertToTable(ctx, object, tableOptions)
}
遂に
PRのkubernetesを提出することを望んでいた、最初のステップは、すでにされていることが判明述べた、と私は正確に同じソリューションを持っていますが、tableConvertorフィールド上の大文字が、私は良い感じ小文字始めて、少しがっかり。2019年9月23日そして、この問題はすでにされてい提起されている、ちょうど非常に迅速に解決するために、1.16、9.24、誰かの言及のPRを、リリースしたときに、私たちは高熱の長所とオープンソースソフトウェアのK8Sを見ることができ、そのための無数の開発者があります貢献し、スポットライトの下の星のようK8Sは、目の無数のペアが前方に視線。しかし、このPRはまだ優先度がそれほど高くないので、このバグは、相対的に非常に深刻ではない、コードレビューの段階ではまだ、トランクに収まらない、あなたはまだ1097 PRがいることを知っています。最後に少し後悔がありますが、だけでなく、問題解決の過程でkubernetesの理解を深め、あるいは多くのことを学びました。それ以降は2017年からですが、コードを読み取る過程では、あなたは、A新星をK8Sコードが別の場所に移動し、明日、それは常に、今日ここに、コードをリファクタリングすることを見出し、TODOのすべての種類を見ることができます事実上の標準コンテナの配置、および広く運用環境で使用されているが、それ自体はまだ、絶えず改善する必要性が進化しています。
ようこそ忠パンさんのブログ