ื•ื•ื™ ืึทื–ื•ื™ ืึทืจื‘ืขื˜ kubectl ืขืงืกืขืง?

ื ืื˜ื™ืฅ. ื˜ืจืึทื ืกืœ.: ื“ืขืจ ืžื—ื‘ืจ ืคื•ืŸ ื“ืขื ืึทืจื˜ื™ืงืœ - Erkan Erol, ืึทืŸ ื™ื ื–ืฉืขื ื™ืจ ืคื•ืŸ SAP - ืฉืึทืจืขืก ื–ื™ื™ืŸ ืœืขืจื ืขืŸ ืคื•ืŸ ื“ื™ ืžืขืงืึทื ื™ื–ืึทืžื– ืคื•ืŸ ื“ื™ ืคืึทื ื’ืงืฉืึทื ื™ื ื’ ืคื•ืŸ ื“ื™ ืžืึทื ืฉืึทืคึฟื˜ kubectl exec, ืึทื–ื•ื™ ื‘ืึทืงืึทื ื˜ ืฆื• ืึทืœืขืžืขืŸ ื•ื•ืืก ืึทืจื‘ืขื˜ ืžื™ื˜ Kubernetes. ืขืจ ืึทืงืึทืžืคึผืึทื ื™ื– ื“ื™ ื’ืื ืฆืข ืึทืœื’ืขืจื™ื“ืึทื ืžื™ื˜ ืœื™ืกื˜ื™ื ื’ืก ืคื•ืŸ ื“ื™ ืงื•ื‘ืขืจื ืขื˜ืขืก ืžืงื•ืจ ืงืึธื“ (ืื•ืŸ ืคึฟืึทืจื‘ื•ื ื“ืขื ืข ืคึผืจืึทื“ื–ืฉืขืงืก), ื•ื•ืึธืก ืœืึธื–ืŸ ืื™ืจ ืฆื• ืคึฟืึทืจืฉื˜ื™ื™ืŸ ื“ื™ ื˜ืขืžืข ื•ื•ื™ ื“ื™ืคึผืœื™ ื•ื•ื™ ื ื™ื™ื˜ื™ืง.

ื•ื•ื™ ืึทื–ื•ื™ ืึทืจื‘ืขื˜ kubectl ืขืงืกืขืง?

ืื™ื™ืŸ ืคืจื™ื™ื˜ืื’ ืื™ื– ืฆื• ืžื™ืจ ืฆื•ื’ืขืงื•ืžืขืŸ ื ืงืืœืขื’ืข ืื•ืŸ ื’ืขืคืจืขื’ื˜ ื•ื•ื™ ืื–ื•ื™ ืžืขืŸ ืงืขืŸ ืื•ื™ืกืคื™ืจืŸ ื ื‘ืืคืขืœ ืื™ืŸ ื ืคืื“ ื ื™ืฆืŸ ืงืœื™ืขื ื˜-ื’ื™ื™ืŸ. ืื™ืš ืงืขืŸ ืื™ื ื ื™ืฉื˜ ืขื ื˜ืคึฟืขืจืŸ ืื•ืŸ ืคึผืœื•ืฆืœื™ื ื’ ืื™ื™ื ื’ืขื–ืขืŸ ืึทื– ืื™ืš ื•ื•ื™ืกืŸ ื’ืึธืจื ื™ืฉื˜ ื•ื•ืขื’ืŸ ื“ื™ ืžืขืงืึทื ื™ื–ืึทื ืคื•ืŸ ืึทืจื‘ืขื˜ kubectl exec. ื™ืึธ, ืื™ืš ื’ืขื”ืื˜ ื–ื™ื›ืขืจ ื’ืขื“ืื ืงืขืŸ ื•ื•ืขื’ืŸ ื–ื™ื™ืŸ ืžื™ื˜ืœ, ืึธื‘ืขืจ ืื™ืš ืื™ื– ื ื™ืฉื˜ 100% ื–ื™ื›ืขืจ ืคื•ืŸ ื–ื™ื™ืขืจ ืจื™ื›ื˜ื™ืง ืื•ืŸ ื“ืขืจื™ื‘ืขืจ ื‘ืึทืฉืœืึธืกืŸ ืฆื• ืึทื“ืจืขืก ื“ืขื ืึทืจื•ื™ืกื’ืขื‘ืŸ. ืื™ืš ื”ืึธื‘ ื’ืขืœืขืจื ื˜ ื‘ืœืึธื’ืก, ื“ืึทืงื™ื•ืžืขื ื˜ื™ื™ืฉืึทืŸ ืื•ืŸ ืžืงื•ืจ ืงืึธื“, ืื™ืš ื’ืขืœืขืจื ื˜ ืึท ืคึผืœืึทืฅ, ืื•ืŸ ืื™ืŸ ื“ืขื ืึทืจื˜ื™ืงืœ ืื™ืš ื•ื•ื™ืœืŸ ืฆื• ื˜ื™ื™ืœืŸ ืžื™ื™ืŸ ื“ื™ืกืงืึทื•ื•ืขืจื™ื– ืื•ืŸ ืคืืจืฉื˜ืื ื“. ืื•ื™ื‘ ืขืคึผืขืก ืื™ื– ืคืึทืœืฉ, ื‘ื™ื˜ืข ืงืึธื ื˜ืึทืงื˜ ืžื™ืจ ื‘ื™ื™ึท ื˜ื•ื•ื™ื˜ื˜ืขืจ.

ื˜ืจืึทื™ื ื™ื ื’

ืฆื• ืฉืึทืคึฟืŸ ืึท ืงื ื•ื™ืœ ืื•ื™ืฃ ืึท ืžืึทืงื‘ืึธืึธืง, ืื™ืš ืงืœืึธื•ื ื“ ecomm-integration-ballerina/kubernetes-cluster. ื“ืขืจื ืึธืš ืื™ืš ืงืขืจืขืงื˜ืึทื“ ื“ื™ IP ืึทื“ืจืขืกืขืก ืคื•ืŸ ื“ื™ ื ืึธื•ื“ื– ืื™ืŸ ื“ื™ ืงื•ื‘ืขืœืขื˜ืึท ืงืึทื ืคื™ื’ื™ืขืจื™ื™ืฉืึทืŸ, ื•ื•ื™ื™ึทืœ ื“ื™ ืคืขืœื™ืงื™ื™ึทื˜ ืกืขื˜ื˜ื™ื ื’ืก ื”ืึธื‘ืŸ ื ื™ืฉื˜ ืขืจืœื•ื™ื‘ื˜ kubectl exec. ืื™ืจ ืงืขื ื˜ ืœื™ื™ืขื ืขืŸ ืžืขืจ ื•ื•ืขื’ืŸ ื“ื™ ื”ื•ื™ืคึผื˜ ืกื™ื‘ื” ืคึฟืึทืจ ื“ืขื ื“ืึธ.

  • ืงื™ื™ืŸ ืžืึทืฉื™ืŸ = ืžื™ื™ืŸ ืžืึทืงื‘ืึธืึธืง
  • ื‘ืขืœ ื ืึธื“ืข IP = 192.168.205.10
  • IP ืึทืจื‘ืขื˜ืขืจ ื ืึธื“ืข = 192.168.205.11
  • ืึทืคึผื™ ืกืขืจื•ื•ืขืจ ืคึผืึธืจื˜ = 6443

ืงืึทืžืคึผืึธื•ื ืึทื ืฅ

ื•ื•ื™ ืึทื–ื•ื™ ืึทืจื‘ืขื˜ kubectl ืขืงืกืขืง?

  • kubectl ืขืงืกืขืง ืคึผืจืึธืฆืขืก: ื•ื•ืขืŸ ืžื™ืจ ื˜ืึธืŸ "ืงื•ื‘ืขืงื˜ืœ ืขืงืกืขืง ..." ื“ืขืจ ืคึผืจืึธืฆืขืก ืื™ื– ืกื˜ืึทืจื˜ืขื“. ืื™ืจ ืงืขื ืขืŸ ื˜ืึธืŸ ื“ืึธืก ืื•ื™ืฃ ืงื™ื™ืŸ ืžืึทืฉื™ืŸ ืžื™ื˜ ืึทืงืกืขืก ืฆื• ื“ื™ K8s API ืกืขืจื•ื•ืขืจ. ื ืื˜ื™ืฅ. ืื™ื‘ืขืจื–ืขืฆื•ื ื’: ื•ื•ื™ื™ึทื˜ืขืจ ืื™ืŸ ื“ื™ ืงืึทื ืกืึธื•ืœ ืœื™ืกื˜ื™ื ื’ืก, ื“ืขืจ ืžื—ื‘ืจ ื ื™ืฆื˜ ื“ืขื ื‘ืึทืžืขืจืงื•ื ื’ "ืงื™ื™ืŸ ืžืึทืฉื™ืŸ", ื™ืžืคึผืœื™ื™ื– ืึทื– ื“ื™ ืคืืœื’ืขื ื“ืข ืงืึทืžืึทื ื“ื– ืงืขื ืขืŸ ื–ื™ื™ืŸ ืขืงืกืึทืงื™ื•ื˜ืึทื“ ืื•ื™ืฃ ืงื™ื™ืŸ ืึทื–ืึท ืžืืฉื™ื ืขืŸ ืžื™ื˜ ืึทืงืกืขืก ืฆื• Kubernetes.
  • ืึทืคึผื™ ืกืขืจื•ื•ืขืจ: ื ืงืึธืžืคึผืึธื ืขื ื˜ ืื•ื™ืฃ ื“ื™ ื‘ืขืœ ื ืึธื“ืข ื•ื•ืึธืก ื’ื™ื˜ ืึทืงืกืขืก ืฆื• ื“ื™ Kubernetes API. ื“ืึธืก ืื™ื– ื“ืขืจ ืคืจืึธื ื˜ ืกื•ืฃ ืคึฟืึทืจ ื“ื™ ืงืึธื ื˜ืจืึธืœ ืคืœืึทืš ืื™ืŸ Kubernetes.
  • ืงื•ื‘ืขืœืขื˜: ืึทื’ืขื ื˜ ื•ื•ืึธืก ืœื•ื™ืคื˜ ืื•ื™ืฃ ื™ืขื“ืขืจ ื ืึธื“ืข ืื™ืŸ ื“ืขื ืงื ื•ื™ืœ. ืขืก ื’ื™ื˜ ื“ื™ ืึทืจื‘ืขื˜ ืคื•ืŸ ืงืึทื ื˜ื™ื™ื ืขืจื– ืื™ืŸ ื“ื™ ืคึผืึทื“.
  • ืงืึทื ื˜ื™ื™ื ืขืจ ืจื•ื ื˜ื™ืžืข (container ืจื•ื ื˜ื™ืžืข): ื“ื™ ื•ื•ื™ื™ื›ื•ื•ืืจื’ ืคืึทืจืึทื ื˜ื•ื•ืึธืจื˜ืœืขืš ืคึฟืึทืจ ืคืœื™ืกื ื“ื™ืง ืงืึทื ื˜ื™ื™ื ืขืจื–. ื‘ื™ื™ืฉืคื™ืœืŸ: ื“ืึธืงืงืขืจ, CRI-O, ืงืึทื ื˜ื™ื™ื ื“ืขืจื– ...
  • ืงืขืจืŸ: ืึทืก ืงืขืจืŸ ืื•ื™ืฃ ื“ื™ ืึทืจื‘ืขื˜ืขืจ ื ืึธื“ืข; ืคืึทืจืึทื ื˜ื•ื•ืึธืจื˜ืœืขืš ืคึฟืึทืจ ืคึผืจืึธืฆืขืก ืคืึทืจื•ื•ืึทืœื˜ื•ื ื’.
  • ืฆื™ืœ (ืฆื™ืœ) ืงืึทื ื˜ื™ื™ื ืขืจ: ืึท ืงืึทื ื˜ื™ื™ื ืขืจ ื•ื•ืึธืก ืื™ื– ื˜ื™ื™ืœ ืคื•ืŸ ืึท ืคึผืึธื“ ืื•ืŸ ืœื•ื™ืคื˜ ืื•ื™ืฃ ืื™ื™ื ืขืจ ืคื•ืŸ ื“ื™ ืึทืจื‘ืขื˜ ื ืึธื•ื“ื–.

ื•ื•ืึธืก ื”ืึธื‘ ืื™ืš ื’ืขืคึฟื•ื ืขืŸ

1. ืึทืงื˜ื™ื•ื•ื™ื˜ืขื˜ ืื•ื™ืฃ ื“ืขืจ ืงืœื™ืขื ื˜ ื–ื™ื™ึทื˜

ืฉืึทืคึฟืŸ ืึท ืคึผืึธื“ ืื™ืŸ ืึท ื ืึทืžืขืกืคึผืึทืกืข default:

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

ื“ืขืจื ืึธืš ืžื™ืจ ื•ื™ืกืคื™ืจืŸ ื“ื™ ืขืงืกืขืง ื‘ืึทืคึฟืขืœ ืื•ืŸ ื•ื•ืึทืจื˜ืŸ 5000 ืกืขืงื•ื ื“ืขืก ืคึฟืึทืจ ื•ื•ื™ื™ึทื˜ืขืจ ืึทื‘ื–ืขืจื•ื•ื™ื™ืฉืึทื ื–:

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

ื“ืขืจ ืงื•ื‘ืขืงื˜ืœ ืคึผืจืึธืฆืขืก ืื™ื– ืืจื•ื™ืก (ืžื™ื˜ ืคึผื™ื“ = 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 ืงืจื™ื™ื™ืฅ ืึท 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 ืขืงืกืขืง?

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]]

ื‘ืึทืžืขืจืงื•ื ื’ ืึทื– ื“ื™ ื”ื˜ื˜ืคึผ ื‘ืขื˜ืŸ ื›ื•ืœืœ ืึท ืคึผืจืึธื˜ืึธืงืึธืœ ื˜ื•ื™ืฉืŸ ื‘ืขื˜ืŸ. SPDY ืึทืœืึทื•ื– ื‘ืึทื–ื•ื ื“ืขืจ "ืกื˜ืจื™ืžื–" ืคื•ืŸ ืกื˜ื“ื™ืŸ/ืกื˜ื“ืึธื•ื˜/ืกื˜ื“ืขืจืจ/ืกืคึผื“ื™-ื˜ืขื•ืช ืฆื• ื–ื™ื™ืŸ ืžื•ืœื˜ื™ืคึผืœืขืงืกืขื“ ืื™ื‘ืขืจ ืึท ืื™ื™ืŸ ื˜ืงืคึผ ืงืฉืจ.

ื“ืขืจ ืึทืคึผื™ ืกืขืจื•ื•ืขืจ ื ืขืžื˜ ื“ื™ ื‘ืงืฉื” ืื•ืŸ ืงืึทื ื•ื•ืขืจืฅ ืขืก ืฆื• 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)

ื”ืืจื™ื™! Kubelet ื”ืื˜ ืื™ืฆื˜ ืึท ืคึผืึธืจื˜ (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)

ืคึฟื•ืŸ ื“ืึทืงื™ื•ืžืขื ื˜ื™ื™ืฉืึทืŸ ื‘ืขืœ-ื ืึธื“ืข ืงืึธืžื•ื ื™ืงืึทืฆื™ืข> ื‘ืขืœ ืฆื• ืงื ื•ื™ืœ> ืึทืคึผื™ืกืขืจื•ื•ืขืจ ืฆื• ืงื•ื‘ืขืœืขื˜:

ื“ื™ ืงืึทื ืขืงืฉืึทื ื– ื–ืขื ืขืŸ ื˜ืขืจืžืึทื ื™ื™ื˜ื™ื“ ื‘ื™ื™ึท ื“ื™ ืงื•ื‘ืขืœืขื˜ ืก ื”ื˜ื˜ืคึผืก ืขื ื“ืคึผื•ื™ื ื˜. ื“ื•ืจืš ืคืขืœื™ืงื™ื™ึทื˜, ืึทืคึผื™ืกืขืจื•ื•ืขืจ ืงืขืŸ ื ื™ืฉื˜ ื‘ืึทืฉื˜ืขื˜ื™ืงืŸ ื“ื™ ืงื•ื‘ืขืœืขื˜ ืก ื‘ืึทื•ื•ื™ื™ึทื–ืŸ, ื•ื•ืึธืก ืžืื›ื˜ ื“ื™ ืงืฉืจ ืฉืคึผื™ืจืขื•ื•ื“ื™ืง ืคึฟืึทืจ "ืžืขื ื˜ืฉ-ืื™ืŸ-ื“ื™-ืžื™ื˜ืŸ ืื ืคืืœืŸ" (MITM) ืื•ืŸ ืื•ืžื–ื™ื›ืขืจ ืฆื• ืึทืจื‘ืขื˜ืŸ ืื™ืŸ ืึทื ื˜ืจืึทืกื˜ื™ื“ ืื•ืŸ / ืึธื“ืขืจ ืขืคื ื˜ืœืขืš ื ืขื˜ื•ื•ืึธืจืงืก.

ืื™ืฆื˜ ื“ืขืจ ืึทืคึผื™ ืกืขืจื•ื•ืขืจ ื•ื•ื™ื™ืกื˜ ื“ื™ ืขื ื“ืคึผื•ื™ื ื˜ ืื•ืŸ ื™ืกื˜ืึทื‘ืœื™ืฉื™ื– ืึท ืงืฉืจ:

// 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)

ื–ืืœ ืก ื–ืขืŸ ื•ื•ืึธืก ื›ืึทืคึผืึทื ื– ืื•ื™ืฃ ื“ื™ ื‘ืขืœ ื ืึธื“ืข.

ืขืจืฉื˜ืขืจ, ื’ืขืคึฟื™ื ืขืŸ ื“ื™ IP ืคื•ืŸ ื“ื™ ืึทืจื‘ืขื˜ ื ืึธื“ืข. ืื™ืŸ ืื•ื ื“ื–ืขืจ ืคืึทืœ, ื“ืึธืก ืื™ื– 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, ืขืก ื•ื•ืขื˜ ืคืึทืจืฉื•ื•ื™ื ื“ืŸ, ืึทื–ื•ื™ ืื™ืš ื•ื•ื™ืกืŸ ืึทื– ื“ื™ ืงืฉืจ ืื™ื– ื’ืขื’ืจื™ื ื“ืขื˜ ื“ื•ืจืš ื“ื™ ืึทืคึผื™-ืกืขืจื•ื•ืขืจ ื•ื•ื™ ืึท ืจืขื–ื•ืœื˜ืึทื˜ ืคื•ืŸ ื“ื™ ืขืงืกืึทืงื™ื•ื˜ืึทื“ ืขืงืกืขืง ื‘ืึทืคึฟืขืœ.

// master node
$ netstat -atn |grep 192.168.205.11
tcp        0      0 192.168.205.10:37870    192.168.205.11:10250    ESTABLISHED
โ€ฆ

ื•ื•ื™ ืึทื–ื•ื™ ืึทืจื‘ืขื˜ kubectl ืขืงืกืขืง?

ื“ืขืจ ืงืฉืจ ืฆื•ื•ื™ืฉืŸ 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 ืงืึทืœืงื™ืึทืœื™ื™ืฅ ื“ื™ ืขื ื˜ืคืขืจ ืขื ื“ืคึผื•ื™ื ื˜ ืคึฟืึทืจ ืขืงืกืขืง ืจื™ืงื•ื•ืขืก:

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)

Kubelet ื™ืžืคึผืœืึทืžืึทื ืฅ ืึท ืฆื•ื‘ื™ื ื“ RuntimeServiceClient, ื•ื•ืึธืก ืื™ื– ื˜ื™ื™ืœ ืคื•ืŸ ื“ื™ ืงืึทื ื˜ื™ื™ื ืขืจ ืจื•ื ื˜ื™ืžืข ืฆื•ื‘ื™ื ื“ (ืžื™ืจ ื’ืขืฉืจื™ื‘ืŸ ืžืขืจ ื•ื•ืขื’ืŸ ืื™ื, ืœืžืฉืœ, ื“ืึธ - ื‘ืขืขืจืขืš. ืื™ื‘ืขืจื–ืขืฆืŸ):

ืœืื ื’ ืœื™ืกื˜ื™ื ื’ ืคื•ืŸ ืงืจื™-ืึทืคึผื™ ืื™ืŸ ืงื•ื‘ืขืจื ืขื˜ืขืก / ืงื•ื‘ืขืจื ืขื˜ืขืก

// 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:

ืœืื ื’ ืœื™ืกื˜ื™ื ื’ ืคื•ืŸ ืงืจื™-ืึทืคึผื™ ืื™ืŸ ืงื•ื‘ืขืจื ืขื˜ืขืก / ืงื•ื‘ืขืจื ืขื˜ืขืก

// 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 ืขืงืกืขืง?

ืื•ื™ื‘ ืึทื–ื•ื™, ืžื™ืจ ื–ืึธืœ ื–ืขืŸ ืึท ืงืฉืจ ืฆื•ื•ื™ืฉืŸ ื“ื™ ืงื•ื‘ืขืœืขื˜ ืื•ืŸ ื“ื™ ืงืึทื ื˜ื™ื™ื ืขืจ ืจื•ื ื˜ื™ืžืข, ืจืขื›ื˜? ื–ืืœ ืก ื˜ืฉืขืง.

ืœื•ื™ืคืŸ ื“ืขื ื‘ืึทืคึฟืขืœ ืื™ื™ื“ืขืจ ืื•ืŸ ื ืึธืš ื“ื™ ืขืงืกืขืง ื‘ืึทืคึฟืขืœ ืื•ืŸ ื–ืขืŸ ื“ื™ ื—ื™ืœื•ืง. ืื™ืŸ ืžื™ื™ืŸ ืคืึทืœ, ื“ื™ ื—ื™ืœื•ืง ืื™ื–:

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

ื”ืžืžืž... ื ื™ื• ื™ื•ื ื™ืงืก ื›ืึธืœืขืœ ืคึฟืึทืจื‘ื™ื ื“ื•ื ื’ ืฆื•ื•ื™ืฉืŸ ืงื•ื‘ืขืœืขื˜ (ืคึผื™ื“ = 5714) ืื•ืŸ ืขืคึผืขืก ืื•ืžื‘ืึทืงืึทื ื˜. ื•ื•ืึธืก ืงืขืŸ ืขืก ื–ื™ื™ืŸ? ื“ืึธืก ืื™ื– ืจืขื›ื˜, ื“ืึธืก ืื™ื– ื“ืึธืงืขืจ (ืคึผื™ื“ = 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))
...

ื•ื•ื™ ืื™ืจ ื’ืขื“ืขื ืงืขืŸ, ื“ืึธืก ืื™ื– ื“ืขืจ ื“ืึธืงืงืขืจ ื“ื™ื™ืžืึทืŸ ืคึผืจืึธืฆืขืก (ืคึผื™ื“ = 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/server/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 ืขืงืกืขืง?

ืฆื•ื ืกื•ืฃ, ื“ืขืจ ืงืขืจืŸ ืขืงืกืึทืงื™ื•ืฅ ื“ื™ ืงืึทืžืึทื ื“ื–:

ื•ื•ื™ ืึทื–ื•ื™ ืึทืจื‘ืขื˜ kubectl ืขืงืกืขืง?

ื“ืขืจืžืึธื ื•ื ื’ืขืŸ

  • ื“ื™ ืึทืคึผื™ ืกืขืจื•ื•ื™ืจืขืจ ืงืขื ืขืŸ ืื•ื™ืš ื™ื ื™ืฉืึทืœื™ื™ื– ืึท ืงืฉืจ ืฆื• ืึท ืงื•ื‘ืขืœืขื˜.
  • ื“ื™ ืคืืœื’ืขื ื“ืข ืงืึทื ืขืงืฉืึทื ื– ืึธื ื”ืึทืœื˜ืŸ ื‘ื™ื– ื“ืขื ืกื•ืฃ ืคื•ืŸ ื“ื™ ื™ื ื˜ืขืจืึทืงื˜ื™ื•ื• ืขืงืกืขืงื•ื˜ื™ื•ื•ืข ืกืขืกื™ืข:
    • ืฆื•ื•ื™ืฉืŸ kubectl ืื•ืŸ api-server;
    • ืฆื•ื•ื™ืฉืŸ ืึทืคึผื™-ืกืขืจื•ื•ืขืจ ืื•ืŸ ืงื•ื‘ืขืงื˜ืœ;
    • ืฆื•ื•ื™ืฉืŸ ื“ื™ ืงื•ื‘ืขืœืขื˜ ืื•ืŸ ื“ื™ ืงืึทื ื˜ื™ื™ื ืขืจ ืจื•ื ื˜ื™ืžืข.
  • Kubectl ืึธื“ืขืจ ืึทืคึผื™-ืกืขืจื•ื•ืขืจ ืงืขื ืขืŸ ื ื™ืฉื˜ ืœื•ื™ืคืŸ ืขืคึผืขืก ืื•ื™ืฃ ืึทืจื‘ืขื˜ ื ืึธื•ื“ื–. ื ืงื•ื‘ืขืœืขื˜ ืงืขื ืขืŸ ืœื•ื™ืคืŸ, ืึธื‘ืขืจ ืขืก ืื•ื™ืš ื™ื ื˜ืขืจืึทืงืฅ ืžื™ื˜ ื“ื™ ืจื•ื ื˜ื™ืžืข ืคื•ืŸ โ€‹โ€‹ื“ืขื ืงืึทื ื˜ื™ื™ื ืขืจ ืคึฟืึทืจ ื“ื™ ืึทืงืฉืึทื ื–.

ืจืขืกื•ืจืกืŸ

ืคึผืก ืคื•ืŸ ืื™ื‘ืขืจื–ืขืฆืขืจ

ืœื™ื™ืขื ืขืŸ ืื•ื™ืš ืื•ื™ืฃ ืื•ื ื“ื–ืขืจ ื‘ืœืึธื’:

ืžืงื•ืจ: www.habr.com

ืœื™ื™ื’ืŸ ืึท ื‘ืึทืžืขืจืงื•ื ื’