kubectl exec рдХрд╕реЗ рдХрд╛рд░реНрдп рдХрд░рддреЗ?

рдиреЛрдВрдж. рдЕрдиреБрд╡рд╛рдж: рд▓реЗрдЦрд╛рдЪреЗ рд▓реЗрдЦрдХ - рдПрд░реНрдХрди рдПрд░реЛрд▓, рдПрд╕рдПрдкреА рдордзреАрд▓ рдЕрднрд┐рдпрдВрддрд╛ - рд╕рдВрдШрд╛рдЪреНрдпрд╛ рдХрд╛рд░реНрдпрдкреНрд░рдгрд╛рд▓реАрдЪрд╛ рддреНрдпрд╛рдВрдЪрд╛ рдЕрднреНрдпрд╛рд╕ рд╕рд╛рдорд╛рдпрд┐рдХ рдХрд░рддрд╛рдд kubectl exec, Kubernetes рд╕рд╣ рдХрд╛рдо рдХрд░рдгрд╛рд░реНтАНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХрд╛рд╕рд╛рдареА рдкрд░рд┐рдЪрд┐рдд рдЖрд╣реЗ. рддреЛ рдХреБрдмрд░реНрдиреЗрдЯреНрд╕ рд╕реЛрд░реНрд╕ рдХреЛрдб (рдЖрдгрд┐ рд╕рдВрдмрдВрдзрд┐рдд рдкреНрд░рдХрд▓реНрдк) рдЪреНрдпрд╛ рд╕реВрдЪреАрд╕рд╣ рд╕рдВрдкреВрд░реНрдг рдЕрд▓реНрдЧреЛрд░рд┐рджрдо рд╕реЛрдмрдд рджреЗрддреЛ, рдЬреЗ рддреБрдореНрд╣рд╛рд▓рд╛ рдЖрд╡рд╢реНрдпрдХрддреЗрдиреБрд╕рд╛рд░ рд╡рд┐рд╖рдп рд╕рдордЬреВрди рдШреЗрдгреНрдпрд╛рд╕ рдЕрдиреБрдорддреА рджреЗрддреЗ.

kubectl exec рдХрд╕реЗ рдХрд╛рд░реНрдп рдХрд░рддреЗ?

рдПрдХрд╛ рд╢реБрдХреНрд░рд╡рд╛рд░реА, рдПрдХ рд╕рд╣рдХрд╛рд░реА рдорд╛рдЭреНрдпрд╛рдХрдбреЗ рдЖрд▓рд╛ рдЖрдгрд┐ рдкреЙрдб рд╡рд╛рдкрд░реВрди рдХрдорд╛рдВрдб рдХрд╢реА рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рд╛рдпрдЪреА рддреЗ рд╡рд┐рдЪрд╛рд░рд▓реЗ рдЧреНрд░рд╛рд╣рдХ-рдЬрд╛. рдореА рддреНрдпрд╛рд▓рд╛ рдЙрддреНрддрд░ рджреЗрдК рд╢рдХрд▓реЛ рдирд╛рд╣реА рдЖрдгрд┐ рдЕрдЪрд╛рдирдХ рд▓рдХреНрд╖рд╛рдд рдЖрд▓реЗ рдХреА рдорд▓рд╛ рдСрдкрд░реЗрд╢рдирдЪреНрдпрд╛ рдпрдВрддреНрд░рдгреЗрдмрджреНрджрд▓ рдХрд╛рд╣реАрд╣реА рдорд╛рд╣рд┐рдд рдирд╛рд╣реА kubectl exec. рд╣реЛрдп, рдорд▓рд╛ рддреНрдпрд╛рдЪреНрдпрд╛ рд╕рдВрд░рдЪрдиреЗрдмрджреНрджрд▓ рдХрд╛рд╣реА рдХрд▓реНрдкрдирд╛ рд╣реЛрддреНрдпрд╛, рдкрд░рдВрддреБ рдорд▓рд╛ рддреНрдпрд╛рдВрдЪреНрдпрд╛ рдЕрдЪреВрдХрддреЗрдмрджреНрджрд▓ 100% рдЦрд╛рддреНрд░реА рдирд╡реНрд╣рддреА рдЖрдгрд┐ рдореНрд╣рдгреВрди рдореА рдпрд╛ рд╕рдорд╕реНрдпреЗрдЪреЗ рдирд┐рд░рд╛рдХрд░рдг рдХрд░рдгреНрдпрд╛рдЪрд╛ рдирд┐рд░реНрдгрдп рдШреЗрддрд▓рд╛. рдмреНрд▓реЙрдЧ, рдбреЙрдХреНрдпреБрдореЗрдВрдЯреЗрд╢рди рдЖрдгрд┐ рд╕реЛрд░реНрд╕ рдХреЛрдбрдЪрд╛ рдЕрднреНрдпрд╛рд╕ рдХреЗрд▓реНрдпрд╛рд╡рд░, рдореА рдмрд░реНтАНрдпрд╛рдЪ рдирд╡реАрди рдЧреЛрд╖реНрдЯреА рд╢рд┐рдХрд▓реЛ рдЖрдгрд┐ рдпрд╛ рд▓реЗрдЦрд╛рдд рдорд▓рд╛ рдорд╛рдЭреЗ рд╢реЛрдз рдЖрдгрд┐ рд╕рдордЬ рд╕рд╛рдорд╛рдпрд┐рдХ рдХрд░рд╛рдпрдЪреЗ рдЖрд╣реЗ. рдХрд╛рд╣реА рдЪреВрдХ рдЕрд╕рд▓реНрдпрд╛рд╕, рдХреГрдкрдпрд╛ рдорд╛рдЭреНрдпрд╛рд╢реА рдпреЗрдереЗ рд╕рдВрдкрд░реНрдХ рд╕рд╛рдзрд╛ Twitter.

рдкреНрд░рд╢рд┐рдХреНрд╖рдг

MacBook рд╡рд░ рдХреНрд▓рд╕реНрдЯрд░ рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдореА рдХреНрд▓реЛрди рдХреЗрд▓реЗ ecomm-integration-ballerina/kubernetes-cluster. рдордЧ рдореА рдХреБрдмреЗрд▓реЗрдЯ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рдирдордзреАрд▓ рдиреЛрдбреНрд╕рдЪреЗ рдЖрдпрдкреА рдкрддреНрддреЗ рджреБрд░реБрд╕реНрдд рдХреЗрд▓реЗ, рдХрд╛рд░рдг рдбреАрдлреЙрд▓реНрдЯ рд╕реЗрдЯрд┐рдВрдЧреНрдЬрдиреЗ рдкрд░рд╡рд╛рдирдЧреА рджрд┐рд▓реА рдирд╛рд╣реА kubectl exec. рдЖрдкрдг рдпрд╛ рдореБрдЦреНрдп рдХрд╛рд░рдгрд╛рдмрджреНрджрд▓ рдЕрдзрд┐рдХ рд╡рд╛рдЪреВ рд╢рдХрддрд╛ рдпреЗрдереЗ.

  • рдХреЛрдгрддреАрд╣реА рдХрд╛рд░ = рдорд╛рдЭреЗ рдореЕрдХрдмреБрдХ
  • рдорд╛рд╕реНрдЯрд░ рдиреЛрдб IP = 192.168.205.10
  • рд╡рд░реНрдХрд░ рдиреЛрдб IP = 192.168.205.11
  • API рд╕рд░реНрд╡реНрд╣рд░ рдкреЛрд░реНрдЯ = 6443

рдШрдЯрдХ

kubectl exec рдХрд╕реЗ рдХрд╛рд░реНрдп рдХрд░рддреЗ?

  • kubectl exec рдкреНрд░рдХреНрд░рд┐рдпрд╛: рдЬреЗрд╡реНрд╣рд╛ рдЖрдкрдг "kubectl exec..." рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рддреЛ рддреЗрд╡реНрд╣рд╛ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╕реБрд░реВ рд╣реЛрддреЗ. рд╣реЗ K8s API рд╕рд░реНрд╡реНрд╣рд░рдордзреНрдпреЗ рдкреНрд░рд╡реЗрд╢ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдХреЛрдгрддреНрдпрд╛рд╣реА рдорд╢реАрдирд╡рд░ рдХреЗрд▓реЗ рдЬрд╛рдК рд╢рдХрддреЗ. рдиреЛрдВрдж рднрд╛рд╖рд╛рдВрддрд░.: рдкреБрдвреЗ рдХрдиреНрд╕реЛрд▓ рд╕реВрдЪреАрдордзреНрдпреЗ, рд▓реЗрдЦрдХ "рдХреЛрдгрддреНрдпрд╛рд╣реА рдорд╢реАрди" рдЯрд┐рдкреНрдкрдгреАрдЪрд╛ рд╡рд╛рдкрд░ рдХрд░рддрд╛рдд, рдпрд╛рдЪрд╛ рдЕрд░реНрде рдЕрд╕рд╛ рдЖрд╣реЗ рдХреА рдХреБрдмрд░реНрдиреЗрдЯреНрд╕рдордзреНрдпреЗ рдкреНрд░рд╡реЗрд╢ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдЕрд╢рд╛ рдХреЛрдгрддреНрдпрд╛рд╣реА рдорд╢реАрдирд╡рд░ рддреНрдпрд╛рдирдВрддрд░рдЪреНрдпрд╛ рдЖрджреЗрд╢рд╛рдВрдЪреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдХреЗрд▓реА рдЬрд╛рдК рд╢рдХрддреЗ.
  • api рд╕рд░реНрд╡реНрд╣рд░: рдорд╛рд╕реНрдЯрд░ рдиреЛрдбрд╡рд░реАрд▓ рдПрдХ рдШрдЯрдХ рдЬреЛ Kubernetes API рдордзреНрдпреЗ рдкреНрд░рд╡реЗрд╢ рдкреНрд░рджрд╛рди рдХрд░рддреЛ. рдХреБрдмрд░реНрдиреЗрдЯреНрд╕рдордзреАрд▓ рдХрдВрдЯреНрд░реЛрд▓ рдкреНрд▓реЗрдирд╕рд╛рдареА рд╣реЗ рдлреНрд░рдВрдЯрдПрдВрдб рдЖрд╣реЗ.
  • рдХреБрдмреЗрд▓реЗрдЯ: рдХреНрд▓рд╕реНрдЯрд░рдордзреАрд▓ рдкреНрд░рддреНрдпреЗрдХ рдиреЛрдбрд╡рд░ рдЪрд╛рд▓рдгрд╛рд░рд╛ рдПрдЬрдВрдЯ. рд╣реЗ рдкреЙрдбрдордзреАрд▓ рдХрдВрдЯреЗрдирд░рдЪреЗ рдСрдкрд░реЗрд╢рди рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рддреЗ.
  • рдХрдВрдЯреЗрдирд░ рд░рдирдЯрд╛рдЗрдо (рдХрдВрдЯреЗрдирд░ рд░рдирдЯрд╛рдЗрдо): рдХрдВрдЯреЗрдирд░ рдЪрд╛рд▓рд╡рдгреНрдпрд╛рд╕рд╛рдареА рдЬрдмрд╛рдмрджрд╛рд░ рд╕реЙрдлреНрдЯрд╡реЗрдЕрд░. рдЙрджрд╛рд╣рд░рдгреЗ: рдбреЙрдХрд░, CRI-O, рдХрдВрдЯреЗрдирд░...
  • рдХрд░реНрдирд▓: рд╡рд░реНрдХрд░ рдиреЛрдбрд╡рд░ рдУрдПрд╕ рдХрд░реНрдирд▓; рдкреНрд░рдХреНрд░рд┐рдпрд╛ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдирд╛рд╕рд╛рдареА рдЬрдмрд╛рдмрджрд╛рд░ рдЖрд╣реЗ.
  • рд▓рдХреНрд╖реНрдп (рд▓рдХреНрд╖реНрдп) рдХрдВрдЯреЗрдирд░: рдПрдХ рдХрдВрдЯреЗрдирд░ рдЬреЛ рдкреЙрдбрдЪрд╛ рднрд╛рдЧ рдЖрд╣реЗ рдЖрдгрд┐ рдХрд╛рдордЧрд╛рд░ рдиреЛрдбреНрд╕рдкреИрдХреА рдПрдХрд╛рд╡рд░ рдЪрд╛рд▓рддреЛ.

рдореА рдХрд╛рдп рд╢реЛрдзрд▓реЗ

1. рдХреНрд▓рд╛рдпрдВрдЯ рд╕рд╛рдЗрдб рдХреНрд░рд┐рдпрд╛рдХрд▓рд╛рдк

рдиреЗрдорд╕реНрдкреЗрд╕рдордзреНрдпреЗ рдкреЙрдб рддрдпрд╛рд░ рдХрд░рд╛ default:

// any machine
$ kubectl run exec-test-nginx --image=nginx

рдордЧ рдЖрдореНрд╣реА exec рдХрдорд╛рдВрдб рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рддреЛ рдЖрдгрд┐ рдкреБрдвреАрд▓ рдирд┐рд░реАрдХреНрд╖рдгрд╛рдВрд╕рд╛рдареА 5000 рд╕реЗрдХрдВрдж рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рддреЛ:

// any machine
$ kubectl exec -it exec-test-nginx-6558988d5-fgxgg -- sh
# sleep 5000

kubectl рдкреНрд░рдХреНрд░рд┐рдпрд╛ рджрд┐рд╕рддреЗ (рдЖрдордЪреНрдпрд╛ рдмрд╛рдмрддреАрдд pid=8507 рд╕рд╣):

// any machine
$ ps -ef |grep kubectl
501  8507  8409   0  7:19PM ttys000    0:00.13 kubectl exec -it exec-test-nginx-6558988d5-fgxgg -- sh

рдЖрдореНрд╣реА рдкреНрд░рдХреНрд░рд┐рдпреЗрдЪреА рдиреЗрдЯрд╡рд░реНрдХ рдХреНрд░рд┐рдпрд╛рдХрд▓рд╛рдк рддрдкрд╛рд╕рд▓реНрдпрд╛рд╕, рдЖрдореНрд╣рд╛рд▓рд╛ рдЖрдврд│реЗрд▓ рдХреА рддреЗ рдПрдкреАрдЖрдп-рд╕рд░реНрд╡реНрд╣рд░ (192.168.205.10.6443) рд╢реА рдХрдиреЗрдХреНрд╢рди рдЖрд╣реЗрдд:

// any machine
$ netstat -atnv |grep 8507
tcp4       0      0  192.168.205.1.51673    192.168.205.10.6443    ESTABLISHED 131072 131768   8507      0 0x0102 0x00000020
tcp4       0      0  192.168.205.1.51672    192.168.205.10.6443    ESTABLISHED 131072 131768   8507      0 0x0102 0x00000028

рдЪрд▓рд╛ рдХреЛрдб рдкрд╛рд╣реВ. Kubectl exec subresource рд╕рд╣ POST рд╡рд┐рдирдВрддреА рддрдпрд╛рд░ рдХрд░рддреЗ рдЖрдгрд┐ REST рд╡рд┐рдирдВрддреА рдкрд╛рдард╡рддреЗ:

              req := restClient.Post().
                        Resource("pods").
                        Name(pod.Name).
                        Namespace(pod.Namespace).
                        SubResource("exec")
                req.VersionedParams(&corev1.PodExecOptions{
                        Container: containerName,
                        Command:   p.Command,
                        Stdin:     p.Stdin,
                        Stdout:    p.Out != nil,
                        Stderr:    p.ErrOut != nil,
                        TTY:       t.Raw,
                }, scheme.ParameterCodec)

                return p.Executor.Execute("POST", req.URL(), p.Config, p.In, p.Out, p.ErrOut, t.Raw, sizeQueue)

(kubectl/pkg/cmd/exec/exec.go)

kubectl exec рдХрд╕реЗ рдХрд╛рд░реНрдп рдХрд░рддреЗ?

2. рдорд╛рд╕реНрдЯрд░ рдиреЛрдб рдмрд╛рдЬреВрд▓рд╛ рдХреНрд░рд┐рдпрд╛рдХрд▓рд╛рдк

рдЖрдореНрд╣реА рдПрдкреАрдЖрдп-рд╕рд░реНрд╡реНрд╣рд░ рдмрд╛рдЬреВрд▓рд╛ рд╡рд┐рдирдВрддреА рджреЗрдЦреАрд▓ рдкрд╛рд╣реВ рд╢рдХрддреЛ:

handler.go:143] kube-apiserver: POST "/api/v1/namespaces/default/pods/exec-test-nginx-6558988d5-fgxgg/exec" satisfied by gorestful with webservice /api/v1
upgradeaware.go:261] Connecting to backend proxy (intercepting redirects) https://192.168.205.11:10250/exec/default/exec-test-nginx-6558988d5-fgxgg/exec-test-nginx?command=sh&input=1&output=1&tty=1
Headers: map[Connection:[Upgrade] Content-Length:[0] Upgrade:[SPDY/3.1] User-Agent:[kubectl/v1.12.10 (darwin/amd64) kubernetes/e3c1340] X-Forwarded-For:[192.168.205.1] X-Stream-Protocol-Version:[v4.channel.k8s.io v3.channel.k8s.io v2.channel.k8s.io channel.k8s.io]]

рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд╛ рдХреА HTTP рд╡рд┐рдирдВрддреАрдордзреНрдпреЗ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ рдмрджрд▓рдгреНрдпрд╛рдЪреА рд╡рд┐рдирдВрддреА рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдЖрд╣реЗ. рдПрд╕рдкреАрдбреАрд╡рд╛рдп рддреБрдореНрд╣рд╛рд▓рд╛ рдПрдХрд╛ TCP рдХрдиреЗрдХреНрд╢рдирд╡рд░ рд╡реИрдпрдХреНрддрд┐рдХ stdin/stdout/stderr/spdy-рддреНрд░реБрдЯреА "рд╕реНрдЯреНрд░реАрдо" рдорд▓реНрдЯреАрдкреНрд▓реЗрдХреНрд╕ рдХрд░рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЗ.

API рд╕рд░реНрд╡реНрд╣рд░рд▓рд╛ рд╡рд┐рдирдВрддреА рдкреНрд░рд╛рдкреНрдд рд╣реЛрддреЗ рдЖрдгрд┐ рддреНрдпрд╛рдд рд░реВрдкрд╛рдВрддрд░рд┐рдд рд╣реЛрддреЗ PodExecOptions:

// PodExecOptions is the query options to a Pod's remote exec call
type PodExecOptions struct {
        metav1.TypeMeta

        // Stdin if true indicates that stdin is to be redirected for the exec call
        Stdin bool

        // Stdout if true indicates that stdout is to be redirected for the exec call
        Stdout bool

        // Stderr if true indicates that stderr is to be redirected for the exec call
        Stderr bool

        // TTY if true indicates that a tty will be allocated for the exec call
        TTY bool

        // Container in which to execute the command.
        Container string

        // Command is the remote command to execute; argv array; not executed within a shell.
        Command []string
}

(pkg/apis/core/types.go)

рдЖрд╡рд╢реНрдпрдХ рдХреНрд░рд┐рдпрд╛ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдПрдкреАрдЖрдп-рд╕рд░реНрд╡реНрд╣рд░рд▓рд╛ рдорд╛рд╣рд┐рдд рдЕрд╕рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ рдХреА рддреНрдпрд╛рд▓рд╛ рдХреЛрдгрддреНрдпрд╛ рдкреЙрдбрд╢реА рд╕рдВрдкрд░реНрдХ рд╕рд╛рдзрдгреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЖрд╣реЗ:

// ExecLocation returns the exec URL for a pod container. If opts.Container is blank
// and only one container is present in the pod, that container is used.
func ExecLocation(
        getter ResourceGetter,
        connInfo client.ConnectionInfoGetter,
        ctx context.Context,
        name string,
        opts *api.PodExecOptions,
) (*url.URL, http.RoundTripper, error) {
        return streamLocation(getter, connInfo, ctx, name, opts, opts.Container, "exec")
}

(pkg/registry/core/pod/strategy.go)

рдЕрд░реНрдерд╛рдд, рдПрдВрдбрдкреЙрдЗрдВрдЯрдмрджреНрджрд▓рдЪрд╛ рдбреЗрдЯрд╛ рдиреЛрдбрдмрджреНрджрд▓рдЪреНрдпрд╛ рдорд╛рд╣рд┐рддреАрд╡рд░реВрди рдШреЗрддрд▓рд╛ рдЬрд╛рддреЛ:

        nodeName := types.NodeName(pod.Spec.NodeName)
        if len(nodeName) == 0 {
                // If pod has not been assigned a host, return an empty location
                return nil, nil, errors.NewBadRequest(fmt.Sprintf("pod %s does not have a host assigned", name))
        }
        nodeInfo, err := connInfo.GetConnectionInfo(ctx, nodeName)

(pkg/registry/core/pod/strategy.go)

рд╣реБрд░реНрд░реЗ! рдХреБрдмреЗрд▓реЗрдЯрдордзреНрдпреЗ рдЖрддрд╛ рдПрдХ рдмрдВрджрд░ рдЖрд╣реЗ (node.Status.DaemonEndpoints.KubeletEndpoint.Port), рдЬреНрдпрд╛рд╡рд░ API рд╕рд░реНрд╡реНрд╣рд░ рдХрдиреЗрдХреНрдЯ рдХрд░реВ рд╢рдХрддреЛ:

// GetConnectionInfo retrieves connection info from the status of a Node API object.
func (k *NodeConnectionInfoGetter) GetConnectionInfo(ctx context.Context, nodeName types.NodeName) (*ConnectionInfo, error) {
        node, err := k.nodes.Get(ctx, string(nodeName), metav1.GetOptions{})
        if err != nil {
                return nil, err
        }

        // Find a kubelet-reported address, using preferred address type
        host, err := nodeutil.GetPreferredNodeAddress(node, k.preferredAddressTypes)
        if err != nil {
                return nil, err
        }

        // Use the kubelet-reported port, if present
        port := int(node.Status.DaemonEndpoints.KubeletEndpoint.Port)
        if port <= 0 {
                port = k.defaultPort
        }

        return &ConnectionInfo{
                Scheme:    k.scheme,
                Hostname:  host,
                Port:      strconv.Itoa(port),
                Transport: k.transport,
        }, nil
}

(pkg/kubelet/client/kubelet_client.go)

рджрд╕реНрддрдРрд╡рдЬреАрдХрд░рдг рдкрд╛рд╕реВрди рдорд╛рд╕реНрдЯрд░-рдиреЛрдб рдХрдореНрдпреБрдирд┐рдХреЗрд╢рди > рдорд╛рд╕реНрдЯрд░ рддреЗ рдХреНрд▓рд╕реНрдЯрд░ > рдПрдкрд┐рд╕рд░реНрд╡реНрд╣рд░ рддреЗ рдХреБрдмреЗрд▓реЗрдЯ:

рд╣реЗ рдХрдиреЗрдХреНрд╢рди рдХреБрдмреЗрд▓реЗрдЯрдЪреНрдпрд╛ HTTPS рдПрдВрдбрдкреЙрдЗрдВрдЯрд╢реА рдХреЗрд▓реЗ рдЬрд╛рддрд╛рдд. рдбреАрдлреЙрд▓реНрдЯрдиреБрд╕рд╛рд░, apiserver рдХреБрдмреЗрд▓реЗрдЯрдЪреНрдпрд╛ рдкреНрд░рдорд╛рдгрдкрддреНрд░рд╛рдЪреА рдкрдбрддрд╛рд│рдгреА рдХрд░рдд рдирд╛рд╣реА, рдЬреНрдпрд╛рдореБрд│реЗ рдХрдиреЗрдХреНрд╢рди рдореЕрди-рдЗрди-рдж-рдорд┐рдбрд▓ (MITM) рд╣рд▓реНрд▓реНрдпрд╛рдВрд╕рд╛рдареА рдЕрд╕реБрд░рдХреНрд╖рд┐рдд рд╣реЛрддреЗ рдЖрдгрд┐ рдЕрд╕реБрд░рдХреНрд╖рд┐рдд рдЕрд╡рд┐рд╢реНрд╡рд╕рдиреАрдп рдЖрдгрд┐/рдХрд┐рдВрд╡рд╛ рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рдиреЗрдЯрд╡рд░реНрдХрдордзреНрдпреЗ рдХрд╛рдо рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА.

рдЖрддрд╛ API рд╕рд░реНрд╡реНрд╣рд░рд▓рд╛ рдПрдВрдбрдкреЙрдЗрдВрдЯ рдорд╛рд╣рд┐рдд рдЖрд╣реЗ рдЖрдгрд┐ рдХрдиреЗрдХреНрд╢рди рд╕реНрдерд╛рдкрд┐рдд рдХрд░рддреЗ:

// Connect returns a handler for the pod exec proxy
func (r *ExecREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
        execOpts, ok := opts.(*api.PodExecOptions)
        if !ok {
                return nil, fmt.Errorf("invalid options object: %#v", opts)
        }
        location, transport, err := pod.ExecLocation(r.Store, r.KubeletConn, ctx, name, execOpts)
        if err != nil {
                return nil, err
        }
        return newThrottledUpgradeAwareProxyHandler(location, transport, false, true, true, responder), nil
}

(pkg/registry/core/pod/rest/subresources.go)

рдЪрд▓рд╛ рдорд╛рд╕реНрдЯрд░ рдиреЛрдбрд╡рд░ рдХрд╛рдп рд╣реЛрдд рдЖрд╣реЗ рддреЗ рдкрд╛рд╣реВрдпрд╛.

рдкреНрд░рдердо, рдЖрдореНрд╣реА рд╡рд░реНрдХрд░ рдиреЛрдбрдЪрд╛ рдЖрдпрдкреА рд╢реЛрдзрддреЛ. рдЖрдордЪреНрдпрд╛ рдмрд╛рдмрддреАрдд рддреЗ 192.168.205.11 рдЖрд╣реЗ:

// any machine
$ kubectl get nodes k8s-node-1 -o wide
NAME         STATUS   ROLES    AGE   VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
k8s-node-1   Ready    <none>   9h    v1.15.3   192.168.205.11   <none>        Ubuntu 16.04.6 LTS   4.4.0-159-generic   docker://17.3.3

рдирдВрддрд░ рдХреБрдмреЗрд▓реЗрдЯ рдкреЛрд░реНрдЯ рд╕реЗрдЯ рдХрд░рд╛ (рдЖрдордЪреНрдпрд╛ рдмрд╛рдмрддреАрдд 10250):

// any machine
$ kubectl get nodes k8s-node-1 -o jsonpath='{.status.daemonEndpoints.kubeletEndpoint}'
map[Port:10250]

рдЖрддрд╛ рдиреЗрдЯрд╡рд░реНрдХ рддрдкрд╛рд╕рдгреНрдпрд╛рдЪреА рд╡реЗрд│ рдЖрд▓реА рдЖрд╣реЗ. рд╡рд░реНрдХрд░ рдиреЛрдбрд╢реА (192.168.205.11) рдХрдиреЗрдХреНрд╢рди рдЖрд╣реЗ рдХрд╛? рд╣реЗ рдЖрд╣реЗ! рдЖрдкрдг рдПрдХ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдорд╛рд░рд▓реНрдпрд╛рд╕ exec, рддреЗ рдЕрджреГрд╢реНрдп рд╣реЛрдИрд▓, рдореНрд╣рдгреВрди рдорд▓рд╛ рдорд╛рд╣рд┐рдд рдЖрд╣реЗ рдХреА exec рдХрдорд╛рдВрдб рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХреЗрд▓реНрдпрд╛рдЪреНрдпрд╛ рдкрд░рд┐рдгрд╛рдореА рдХрдиреЗрдХреНрд╢рди api-server рджреНрд╡рд╛рд░реЗ рд╕реНрдерд╛рдкрд┐рдд рдХреЗрд▓реЗ рдЧреЗрд▓реЗ рдЖрд╣реЗ.

// master node
$ netstat -atn |grep 192.168.205.11
tcp        0      0 192.168.205.10:37870    192.168.205.11:10250    ESTABLISHED
тАж

kubectl exec рдХрд╕реЗ рдХрд╛рд░реНрдп рдХрд░рддреЗ?

kubectl рдЖрдгрд┐ api-server рдордзреАрд▓ рдХрдиреЗрдХреНрд╢рди рдЕрдЬреВрдирд╣реА рдЦреБрд▓реЗ рдЖрд╣реЗ. рдпрд╛рд╡реНрдпрддрд┐рд░рд┐рдХреНрдд, рдПрдкреАрдЖрдп-рд╕рд░реНрд╡реНрд╣рд░ рдЖрдгрд┐ рдХреБрдмреЗрд▓реЗрдЯ рджрд░рдореНрдпрд╛рди рдЖрдгрдЦреА рдПрдХ рдХрдиреЗрдХреНрд╢рди рдЖрд╣реЗ.

3. рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдиреЛрдбрд╡рд░реАрд▓ рдХреНрд░рд┐рдпрд╛рдХрд▓рд╛рдк

рдЖрддрд╛ рд╡рд░реНрдХрд░ рдиреЛрдбрд╢реА рдХрдиреЗрдХреНрдЯ рдХрд░реВ рдЖрдгрд┐ рддреНрдпрд╛рд╡рд░ рдХрд╛рдп рд╣реЛрдд рдЖрд╣реЗ рддреЗ рдкрд╛рд╣реВ.

рд╕рд░реНрд╡ рдкреНрд░рдердо, рдЖрдореНрд╣реА рдкрд╛рд╣рддреЛ рдХреА рддреНрдпрд╛рдЪреНрдпрд╛рд╢реА рдХрдиреЗрдХреНрд╢рди рджреЗрдЦреАрд▓ рд╕реНрдерд╛рдкрд┐рдд рдХреЗрд▓реЗ рдЖрд╣реЗ (рджреБрд╕рд░реА рдУрд│); 192.168.205.10 рд╣рд╛ рдорд╛рд╕реНрдЯрд░ рдиреЛрдбрдЪрд╛ IP рдЖрд╣реЗ:

 // worker node
  $ netstat -atn |grep 10250
  tcp6       0      0 :::10250                :::*                    LISTEN
  tcp6       0      0 192.168.205.11:10250    192.168.205.10:37870    ESTABLISHED

рдЖрдордЪреНрдпрд╛ рд╕рдВрдШрд╛рдЪреЗ рдХрд╛рдп sleep? рд╣реБрд░реНрд░реЗ, рддреА рдкрдг рддрд┐рдереЗ рдЖрд╣реЗ!

 // worker node
  $ ps -afx
  ...
  31463 ?        Sl     0:00      _ docker-containerd-shim 7d974065bbb3107074ce31c51f5ef40aea8dcd535ae11a7b8f2dd180b8ed583a /var/run/docker/libcontainerd/7d974065bbb3107074ce31c51
  31478 pts/0    Ss     0:00          _ sh
  31485 pts/0    S+     0:00              _ sleep 5000
  тАж

рдкрдг рдерд╛рдВрдмрд╛: рдХреБрдмреЗрд▓реЗрдЯрдиреЗ рд╣реЗ рдХрд╕реЗ рдХрд╛рдврд▓реЗ? рдХреБрдмреЗрд▓реЗрдЯрдордзреНрдпреЗ рдПрдХ рдбрд┐рдорди рдЖрд╣реЗ рдЬреЛ рдПрдкреАрдЖрдп-рд╕рд░реНрд╡реНрд╣рд░ рд╡рд┐рдирдВрддреНрдпрд╛рдВрд╕рд╛рдареА рдкреЛрд░реНрдЯрджреНрд╡рд╛рд░реЗ API рдордзреНрдпреЗ рдкреНрд░рд╡реЗрд╢ рдкреНрд░рджрд╛рди рдХрд░рддреЛ:

// Server is the library interface to serve the stream requests.
type Server interface {
        http.Handler

        // Get the serving URL for the requests.
        // Requests must not be nil. Responses may be nil iff an error is returned.
        GetExec(*runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error)
        GetAttach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error)
        GetPortForward(*runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error)

        // Start the server.
        // addr is the address to serve on (address:port) stayUp indicates whether the server should
        // listen until Stop() is called, or automatically stop after all expected connections are
        // closed. Calling Get{Exec,Attach,PortForward} increments the expected connection count.
        // Function does not return until the server is stopped.
        Start(stayUp bool) error
        // Stop the server, and terminate any open connections.
        Stop() error
}

(pkg/kubelet/server/streaming/server.go)

Kubelet exec рд╡рд┐рдирдВрддреНрдпрд╛рдВрд╕рд╛рдареА рдкреНрд░рддрд┐рд╕рд╛рдж рдПрдВрдбрдкреЙрдЗрдВрдЯрдЪреА рдЧрдгрдирд╛ рдХрд░рддреЗ:

func (s *server) GetExec(req *runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error) {
        if err := validateExecRequest(req); err != nil {
                return nil, err
        }
        token, err := s.cache.Insert(req)
        if err != nil {
                return nil, err
        }
        return &runtimeapi.ExecResponse{
                Url: s.buildURL("exec", token),
        }, nil
}

(pkg/kubelet/server/streaming/server.go)

рдЧреЛрдВрдзрд│реВрди рдЬрд╛рдК рдирдХрд╛. рд╣реЗ рдЖрджреЗрд╢рд╛рдЪрд╛ рдкрд░рд┐рдгрд╛рдо рджреЗрдд рдирд╛рд╣реА, рдкрд░рдВрддреБ рд╕рдВрдкреНрд░реЗрд╖рдгрд╛рд╕рд╛рдареА рдЕрдВрддрд┐рдо рдмрд┐рдВрджреВ:

type ExecResponse struct {
        // Fully qualified URL of the exec streaming server.
        Url                  string   `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
        XXX_NoUnkeyedLiteral struct{} `json:"-"`
        XXX_sizecache        int32    `json:"-"`
}

(cri-api/pkg/apis/runtime/v1alpha2/api.pb.go)

рдХреБрдмреЗрд▓реЗрдЯ рдЗрдВрдЯрд░рдлреЗрд╕ рд▓рд╛рдЧреВ рдХрд░рддреЗ RuntimeServiceClient, рдЬреЗ рдХрдВрдЯреЗрдирд░ рд░рдирдЯрд╛рдЗрдо рдЗрдВрдЯрд░рдлреЗрд╕рдЪрд╛ рднрд╛рдЧ рдЖрд╣реЗ (рдЖрдореНрд╣реА рдпрд╛рдмрджреНрджрд▓ рдЕрдзрд┐рдХ рд▓рд┐рд╣рд┐рд▓реЗ, рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рдпреЗрдереЗ - рдЕрдВрджрд╛рдЬреЗ рдЕрдиреБрд╡рд╛рдж.):

kubernetes/kubernetes рдордзреНрдпреЗ cri-api рдХрдбреВрди рджреАрд░реНрдШ рд╕реВрдЪреА

// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type RuntimeServiceClient interface {
        // Version returns the runtime name, runtime version, and runtime API version.
        Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error)
        // RunPodSandbox creates and starts a pod-level sandbox. Runtimes must ensure
        // the sandbox is in the ready state on success.
        RunPodSandbox(ctx context.Context, in *RunPodSandboxRequest, opts ...grpc.CallOption) (*RunPodSandboxResponse, error)
        // StopPodSandbox stops any running process that is part of the sandbox and
        // reclaims network resources (e.g., IP addresses) allocated to the sandbox.
        // If there are any running containers in the sandbox, they must be forcibly
        // terminated.
        // This call is idempotent, and must not return an error if all relevant
        // resources have already been reclaimed. kubelet will call StopPodSandbox
        // at least once before calling RemovePodSandbox. It will also attempt to
        // reclaim resources eagerly, as soon as a sandbox is not needed. Hence,
        // multiple StopPodSandbox calls are expected.
        StopPodSandbox(ctx context.Context, in *StopPodSandboxRequest, opts ...grpc.CallOption) (*StopPodSandboxResponse, error)
        // RemovePodSandbox removes the sandbox. If there are any running containers
        // in the sandbox, they must be forcibly terminated and removed.
        // This call is idempotent, and must not return an error if the sandbox has
        // already been removed.
        RemovePodSandbox(ctx context.Context, in *RemovePodSandboxRequest, opts ...grpc.CallOption) (*RemovePodSandboxResponse, error)
        // PodSandboxStatus returns the status of the PodSandbox. If the PodSandbox is not
        // present, returns an error.
        PodSandboxStatus(ctx context.Context, in *PodSandboxStatusRequest, opts ...grpc.CallOption) (*PodSandboxStatusResponse, error)
        // ListPodSandbox returns a list of PodSandboxes.
        ListPodSandbox(ctx context.Context, in *ListPodSandboxRequest, opts ...grpc.CallOption) (*ListPodSandboxResponse, error)
        // CreateContainer creates a new container in specified PodSandbox
        CreateContainer(ctx context.Context, in *CreateContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error)
        // StartContainer starts the container.
        StartContainer(ctx context.Context, in *StartContainerRequest, opts ...grpc.CallOption) (*StartContainerResponse, error)
        // StopContainer stops a running container with a grace period (i.e., timeout).
        // This call is idempotent, and must not return an error if the container has
        // already been stopped.
        // TODO: what must the runtime do after the grace period is reached?
        StopContainer(ctx context.Context, in *StopContainerRequest, opts ...grpc.CallOption) (*StopContainerResponse, error)
        // RemoveContainer removes the container. If the container is running, the
        // container must be forcibly removed.
        // This call is idempotent, and must not return an error if the container has
        // already been removed.
        RemoveContainer(ctx context.Context, in *RemoveContainerRequest, opts ...grpc.CallOption) (*RemoveContainerResponse, error)
        // ListContainers lists all containers by filters.
        ListContainers(ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption) (*ListContainersResponse, error)
        // ContainerStatus returns status of the container. If the container is not
        // present, returns an error.
        ContainerStatus(ctx context.Context, in *ContainerStatusRequest, opts ...grpc.CallOption) (*ContainerStatusResponse, error)
        // UpdateContainerResources updates ContainerConfig of the container.
        UpdateContainerResources(ctx context.Context, in *UpdateContainerResourcesRequest, opts ...grpc.CallOption) (*UpdateContainerResourcesResponse, error)
        // ReopenContainerLog asks runtime to reopen the stdout/stderr log file
        // for the container. This is often called after the log file has been
        // rotated. If the container is not running, container runtime can choose
        // to either create a new log file and return nil, or return an error.
        // Once it returns error, new container log file MUST NOT be created.
        ReopenContainerLog(ctx context.Context, in *ReopenContainerLogRequest, opts ...grpc.CallOption) (*ReopenContainerLogResponse, error)
        // ExecSync runs a command in a container synchronously.
        ExecSync(ctx context.Context, in *ExecSyncRequest, opts ...grpc.CallOption) (*ExecSyncResponse, error)
        // Exec prepares a streaming endpoint to execute a command in the container.
        Exec(ctx context.Context, in *ExecRequest, opts ...grpc.CallOption) (*ExecResponse, error)
        // Attach prepares a streaming endpoint to attach to a running container.
        Attach(ctx context.Context, in *AttachRequest, opts ...grpc.CallOption) (*AttachResponse, error)
        // PortForward prepares a streaming endpoint to forward ports from a PodSandbox.
        PortForward(ctx context.Context, in *PortForwardRequest, opts ...grpc.CallOption) (*PortForwardResponse, error)
        // ContainerStats returns stats of the container. If the container does not
        // exist, the call returns an error.
        ContainerStats(ctx context.Context, in *ContainerStatsRequest, opts ...grpc.CallOption) (*ContainerStatsResponse, error)
        // ListContainerStats returns stats of all running containers.
        ListContainerStats(ctx context.Context, in *ListContainerStatsRequest, opts ...grpc.CallOption) (*ListContainerStatsResponse, error)
        // UpdateRuntimeConfig updates the runtime configuration based on the given request.
        UpdateRuntimeConfig(ctx context.Context, in *UpdateRuntimeConfigRequest, opts ...grpc.CallOption) (*UpdateRuntimeConfigResponse, error)
        // Status returns the status of the runtime.
        Status(ctx context.Context, in *StatusRequest, opts ...grpc.CallOption) (*StatusResponse, error)
}

(cri-api/pkg/apis/runtime/v1alpha2/api.pb.go)
рдХрдВрдЯреЗрдирд░ рд░рдирдЯрд╛рдЗрдо рдЗрдВрдЯрд░рдлреЗрд╕рджреНрд╡рд╛рд░реЗ рдкрджреНрдзрдд рдХреЙрд▓ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреЗ рдлрдХреНрдд gRPC рд╡рд╛рдкрд░рддреЗ:

type runtimeServiceClient struct {
        cc *grpc.ClientConn
}

(cri-api/pkg/apis/runtime/v1alpha2/api.pb.go)

func (c *runtimeServiceClient) Exec(ctx context.Context, in *ExecRequest, opts ...grpc.CallOption) (*ExecResponse, error) {
        out := new(ExecResponse)
        err := c.cc.Invoke(ctx, "/runtime.v1alpha2.RuntimeService/Exec", in, out, opts...)
        if err != nil {
                return nil, err
        }
        return out, nil
}

(cri-api/pkg/apis/runtime/v1alpha2/api.pb.go)

рдХрдВрдЯреЗрдирд░ рд░рдирдЯрд╛рдЗрдо рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрд╕рд╛рдареА рдЬрдмрд╛рдмрджрд╛рд░ рдЖрд╣реЗ RuntimeServiceServer:

kubernetes/kubernetes рдордзреНрдпреЗ cri-api рдХрдбреВрди рджреАрд░реНрдШ рд╕реВрдЪреА

// RuntimeServiceServer is the server API for RuntimeService service.
type RuntimeServiceServer interface {
        // Version returns the runtime name, runtime version, and runtime API version.
        Version(context.Context, *VersionRequest) (*VersionResponse, error)
        // RunPodSandbox creates and starts a pod-level sandbox. Runtimes must ensure
        // the sandbox is in the ready state on success.
        RunPodSandbox(context.Context, *RunPodSandboxRequest) (*RunPodSandboxResponse, error)
        // StopPodSandbox stops any running process that is part of the sandbox and
        // reclaims network resources (e.g., IP addresses) allocated to the sandbox.
        // If there are any running containers in the sandbox, they must be forcibly
        // terminated.
        // This call is idempotent, and must not return an error if all relevant
        // resources have already been reclaimed. kubelet will call StopPodSandbox
        // at least once before calling RemovePodSandbox. It will also attempt to
        // reclaim resources eagerly, as soon as a sandbox is not needed. Hence,
        // multiple StopPodSandbox calls are expected.
        StopPodSandbox(context.Context, *StopPodSandboxRequest) (*StopPodSandboxResponse, error)
        // RemovePodSandbox removes the sandbox. If there are any running containers
        // in the sandbox, they must be forcibly terminated and removed.
        // This call is idempotent, and must not return an error if the sandbox has
        // already been removed.
        RemovePodSandbox(context.Context, *RemovePodSandboxRequest) (*RemovePodSandboxResponse, error)
        // PodSandboxStatus returns the status of the PodSandbox. If the PodSandbox is not
        // present, returns an error.
        PodSandboxStatus(context.Context, *PodSandboxStatusRequest) (*PodSandboxStatusResponse, error)
        // ListPodSandbox returns a list of PodSandboxes.
        ListPodSandbox(context.Context, *ListPodSandboxRequest) (*ListPodSandboxResponse, error)
        // CreateContainer creates a new container in specified PodSandbox
        CreateContainer(context.Context, *CreateContainerRequest) (*CreateContainerResponse, error)
        // StartContainer starts the container.
        StartContainer(context.Context, *StartContainerRequest) (*StartContainerResponse, error)
        // StopContainer stops a running container with a grace period (i.e., timeout).
        // This call is idempotent, and must not return an error if the container has
        // already been stopped.
        // TODO: what must the runtime do after the grace period is reached?
        StopContainer(context.Context, *StopContainerRequest) (*StopContainerResponse, error)
        // RemoveContainer removes the container. If the container is running, the
        // container must be forcibly removed.
        // This call is idempotent, and must not return an error if the container has
        // already been removed.
        RemoveContainer(context.Context, *RemoveContainerRequest) (*RemoveContainerResponse, error)
        // ListContainers lists all containers by filters.
        ListContainers(context.Context, *ListContainersRequest) (*ListContainersResponse, error)
        // ContainerStatus returns status of the container. If the container is not
        // present, returns an error.
        ContainerStatus(context.Context, *ContainerStatusRequest) (*ContainerStatusResponse, error)
        // UpdateContainerResources updates ContainerConfig of the container.
        UpdateContainerResources(context.Context, *UpdateContainerResourcesRequest) (*UpdateContainerResourcesResponse, error)
        // ReopenContainerLog asks runtime to reopen the stdout/stderr log file
        // for the container. This is often called after the log file has been
        // rotated. If the container is not running, container runtime can choose
        // to either create a new log file and return nil, or return an error.
        // Once it returns error, new container log file MUST NOT be created.
        ReopenContainerLog(context.Context, *ReopenContainerLogRequest) (*ReopenContainerLogResponse, error)
        // ExecSync runs a command in a container synchronously.
        ExecSync(context.Context, *ExecSyncRequest) (*ExecSyncResponse, error)
        // Exec prepares a streaming endpoint to execute a command in the container.
        Exec(context.Context, *ExecRequest) (*ExecResponse, error)
        // Attach prepares a streaming endpoint to attach to a running container.
        Attach(context.Context, *AttachRequest) (*AttachResponse, error)
        // PortForward prepares a streaming endpoint to forward ports from a PodSandbox.
        PortForward(context.Context, *PortForwardRequest) (*PortForwardResponse, error)
        // ContainerStats returns stats of the container. If the container does not
        // exist, the call returns an error.
        ContainerStats(context.Context, *ContainerStatsRequest) (*ContainerStatsResponse, error)
        // ListContainerStats returns stats of all running containers.
        ListContainerStats(context.Context, *ListContainerStatsRequest) (*ListContainerStatsResponse, error)
        // UpdateRuntimeConfig updates the runtime configuration based on the given request.
        UpdateRuntimeConfig(context.Context, *UpdateRuntimeConfigRequest) (*UpdateRuntimeConfigResponse, error)
        // Status returns the status of the runtime.
        Status(context.Context, *StatusRequest) (*StatusResponse, error)
}

(cri-api/pkg/apis/runtime/v1alpha2/api.pb.go)
kubectl exec рдХрд╕реЗ рдХрд╛рд░реНрдп рдХрд░рддреЗ?

рддрд╕реЗ рдЕрд╕рд▓реНрдпрд╛рд╕, рдЖрдкрдг рдХреБрдмреЗрд▓реЗрдЯ рдЖрдгрд┐ рдХрдВрдЯреЗрдирд░ рд░рдирдЯрд╛рдЗрдордордзреАрд▓ рдХрдиреЗрдХреНрд╢рди рдкрд╣рд╛рд╡реЗ, рдмрд░реЛрдмрд░? рдЪрд▓рд╛ рддрдкрд╛рд╕реВрдпрд╛.

exec рдХрдорд╛рдВрдбрдЪреНрдпрд╛ рдЖрдзреА рдЖрдгрд┐ рдирдВрддрд░ рд╣реА рдХрдорд╛рдВрдб рдЪрд╛рд▓рд╡рд╛ рдЖрдгрд┐ рдлрд░рдХ рдкрд╣рд╛. рдорд╛рдЭреНрдпрд╛ рдмрд╛рдмрддреАрдд рдлрд░рдХ рдЖрд╣реЗ:

// worker node
$ ss -a -p |grep kubelet
...
u_str  ESTAB      0      0       * 157937                * 157387                users:(("kubelet",pid=5714,fd=33))
...

рд╣рдореНрдореНрдо... рдХреБрдмреЗрд▓реЗрдЯ (pid=5714) рдЖрдгрд┐ рдХрд╛рд╣реАрддрд░реА рдЕрдЬреНрдЮрд╛рдд рджрд░рдореНрдпрд╛рди рдпреБрдирд┐рдХреНрд╕ рд╕реЙрдХреЗрдЯрджреНрд╡рд╛рд░реЗ рдирд╡реАрди рдХрдиреЗрдХреНрд╢рди. рддреЗ рдХрд╛рдп рдЕрд╕реВ рд╢рдХрддреЗ? рдмрд░реЛрдмрд░ рдЖрд╣реЗ, рддреЛ рдбреЙрдХрд░ рдЖрд╣реЗ (pid=1186)!

// worker node
$ ss -a -p |grep 157387
...
u_str  ESTAB      0      0       * 157937                * 157387                users:(("kubelet",pid=5714,fd=33))
u_str  ESTAB      0      0      /var/run/docker.sock 157387                * 157937                users:(("dockerd",pid=1186,fd=14))
...

рддреБрдореНрд╣рд╛рд▓рд╛ рдЖрдард╡рдд рдЕрд╕реЗрд▓, рд╣реА рдбреЙрдХрд░ рдбрд┐рдорди рдкреНрд░рдХреНрд░рд┐рдпрд╛ (pid=1186) рдЖрд╣реЗ рдЬреА рдЖрдордЪреА рдЖрдЬреНрдЮрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рддреЗ:

// worker node
$ ps -afx
...
 1186 ?        Ssl    0:55 /usr/bin/dockerd -H fd://
17784 ?        Sl     0:00      _ docker-containerd-shim 53a0a08547b2f95986402d7f3b3e78702516244df049ba6c5aa012e81264aa3c /var/run/docker/libcontainerd/53a0a08547b2f95986402d7f3
17801 pts/2    Ss     0:00          _ sh
17827 pts/2    S+     0:00              _ sleep 5000
...

4. рдХрдВрдЯреЗрдирд░ рд░рдирдЯрд╛рдЗрдордордзреАрд▓ рдХреНрд░рд┐рдпрд╛рдХрд▓рд╛рдк

рдХрд╛рдп рдЪрд╛рд▓рд▓реЗ рдЖрд╣реЗ рд╣реЗ рд╕рдордЬреВрди рдШреЗрдгреНрдпрд╛рд╕рд╛рдареА CRI-O рд╕реНрддреНрд░реЛрдд рдХреЛрдбрдЪреЗ рдкрд░реАрдХреНрд╖рдг рдХрд░реВрдпрд╛. рдбреЙрдХрд░рдордзреНрдпреЗ рддрд░реНрдХрд╢рд╛рд╕реНрддреНрд░ рд╕рдорд╛рди рдЖрд╣реЗ.

рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрд╕рд╛рдареА рдЬрдмрд╛рдмрджрд╛рд░ рд╕рд░реНрд╡реНрд╣рд░ рдЖрд╣реЗ RuntimeServiceServer:

// Server implements the RuntimeService and ImageService
type Server struct {
        config          libconfig.Config
        seccompProfile  *seccomp.Seccomp
        stream          StreamService
        netPlugin       ocicni.CNIPlugin
        hostportManager hostport.HostPortManager

        appArmorProfile string
        hostIP          string
        bindAddress     string

        *lib.ContainerServer
        monitorsChan      chan struct{}
        defaultIDMappings *idtools.IDMappings
        systemContext     *types.SystemContext // Never nil

        updateLock sync.RWMutex

        seccompEnabled  bool
        appArmorEnabled bool
}

(cri-o/server/server.go)

// Exec prepares a streaming endpoint to execute a command in the container.
func (s *Server) Exec(ctx context.Context, req *pb.ExecRequest) (resp *pb.ExecResponse, err error) {
        const operation = "exec"
        defer func() {
                recordOperation(operation, time.Now())
                recordError(operation, err)
        }()

        resp, err = s.getExec(req)
        if err != nil {
                return nil, fmt.Errorf("unable to prepare exec endpoint: %v", err)
        }

        return resp, nil
}

(cri-o/erver/container_exec.go)

рд╕рд╛рдЦрд│реАрдЪреНрдпрд╛ рд╢реЗрд╡рдЯреА, рдХрдВрдЯреЗрдирд░ рд░рдирдЯрд╛рдЗрдо рд╡рд░реНрдХрд░ рдиреЛрдбрд╡рд░ рдХрдорд╛рдВрдб рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рддреЛ:

// ExecContainer prepares a streaming endpoint to execute a command in the container.
func (r *runtimeOCI) ExecContainer(c *Container, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error {
        processFile, err := prepareProcessExec(c, cmd, tty)
        if err != nil {
                return err
        }
        defer os.RemoveAll(processFile.Name())

        args := []string{rootFlag, r.root, "exec"}
        args = append(args, "--process", processFile.Name(), c.ID())
        execCmd := exec.Command(r.path, args...)
        if v, found := os.LookupEnv("XDG_RUNTIME_DIR"); found {
                execCmd.Env = append(execCmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", v))
        }
        var cmdErr, copyError error
        if tty {
                cmdErr = ttyCmd(execCmd, stdin, stdout, resize)
        } else {
                if stdin != nil {
                        // Use an os.Pipe here as it returns true *os.File objects.
                        // This way, if you run 'kubectl exec <pod> -i bash' (no tty) and type 'exit',
                        // the call below to execCmd.Run() can unblock because its Stdin is the read half
                        // of the pipe.
                        r, w, err := os.Pipe()
                        if err != nil {
                                return err
                        }
                        go func() { _, copyError = pools.Copy(w, stdin) }()

                        execCmd.Stdin = r
                }
                if stdout != nil {
                        execCmd.Stdout = stdout
                }
                if stderr != nil {
                        execCmd.Stderr = stderr
                }

                cmdErr = execCmd.Run()
        }

        if copyError != nil {
                return copyError
        }
        if exitErr, ok := cmdErr.(*exec.ExitError); ok {
                return &utilexec.ExitErrorWrapper{ExitError: exitErr}
        }
        return cmdErr
}

(cri-o/internal/oci/runtime_oci.go)

kubectl exec рдХрд╕реЗ рдХрд╛рд░реНрдп рдХрд░рддреЗ?

рд╢реЗрд╡рдЯреА, рдХрд░реНрдирд▓ рдЖрдЬреНрдЮрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рддреЗ:

kubectl exec рдХрд╕реЗ рдХрд╛рд░реНрдп рдХрд░рддреЗ?

рдиреЗрдкреЛрдорд┐рдирд╛рдирд┐рдпрд╛

  • рдПрдкреАрдЖрдп рд╕рд░реНрд╡реНрд╣рд░ рдХреБрдмреЗрд▓реЗрдЯрд╢реА рдХрдиреЗрдХреНрд╢рди рджреЗрдЦреАрд▓ рд╕реБрд░реВ рдХрд░реВ рд╢рдХрддреЛ.
  • рдкрд░рд╕реНрдкрд░ рдХреНрд░рд┐рдпрд╛рд╢реАрд▓ рд╕рддреНрд░ рд╕рдВрдкреЗрдкрд░реНрдпрдВрдд рдЦрд╛рд▓реАрд▓ рдХрдиреЗрдХреНрд╢рди рдЯрд┐рдХреВрди рд░рд╛рд╣рддрд╛рдд:
    • kubectl рдЖрдгрд┐ api-server рджрд░рдореНрдпрд╛рди;
    • api-server рдЖрдгрд┐ kubectl рджрд░рдореНрдпрд╛рди;
    • рдХреБрдмреЗрд▓реЗрдЯ рдЖрдгрд┐ рдХрдВрдЯреЗрдирд░ рд░рдирдЯрд╛рдЗрдо рджрд░рдореНрдпрд╛рди.
  • Kubectl рдХрд┐рдВрд╡рд╛ api-server рд╡рд░реНрдХрд░ рдиреЛрдбреНрд╕рд╡рд░ рдХрд╛рд╣реАрд╣реА рдЪрд╛рд▓рд╡реВ рд╢рдХрдд рдирд╛рд╣реАрдд. рдХреБрдмреЗрд▓реЗрдЯ рдЪрд╛рд▓реВ рд╢рдХрддреЗ, рдкрд░рдВрддреБ рддреЗ рддреНрдпрд╛ рдЧреЛрд╖реНрдЯреА рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдХрдВрдЯреЗрдирд░ рд░рдирдЯрд╛рдЗрдорд╢реА рджреЗрдЦреАрд▓ рд╕рдВрд╡рд╛рдж рд╕рд╛рдзрддреЗ.

рд╕рдВрд╕рд╛рдзрдиреЗ

рдЕрдиреБрд╡рд╛рджрдХрд╛рдХрдбреВрди рдкреБрдирд╢реНрдЪ

рдЖрдордЪреНрдпрд╛ рдмреНрд▓реЙрдЧрд╡рд░ рджреЗрдЦреАрд▓ рд╡рд╛рдЪрд╛:

рд╕реНрддреНрд░реЛрдд: www.habr.com

рдПрдХ рдЯрд┐рдкреНрдкрдгреА рдЬреЛрдбрд╛