ΠΡΠΈΠΌ. ΠΏΠ΅ΡΠ΅Π².: Π°Π²ΡΠΎΡ ΡΡΠ°ΡΡΠΈ β Erkan Erol, ΠΈΠ½ΠΆΠ΅Π½Π΅Ρ ΠΈΠ· SAP β Π΄Π΅Π»ΠΈΡΡΡ ΡΠ²ΠΎΠΈΠΌ ΠΈΠ·ΡΡΠ΅Π½ΠΈΠ΅ΠΌ ΠΌΠ΅Ρ
Π°Π½ΠΈΠ·ΠΌΠΎΠ² ΡΡΠ½ΠΊΡΠΈΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ kubectl exec, ΡΡΠΎΠ»Ρ ΠΏΡΠΈΠ²ΡΡΠ½ΠΎΠΉ Π΄Π»Ρ Π²ΡΠ΅Ρ
, ΠΊΡΠΎ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ Ρ Kubernetes. ΠΠ΅ΡΡ Π°Π»Π³ΠΎΡΠΈΡΠΌ ΠΎΠ½ ΡΠΎΠΏΡΠΎΠ²ΠΎΠΆΠ΄Π°Π΅Ρ Π»ΠΈΡΡΠΈΠ½Π³Π°ΠΌΠΈ ΠΈΡΡ
ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π° Kubernetes (ΠΈ ΡΠ²ΡΠ·Π°Π½Π½ΡΡ
ΠΏΡΠΎΠ΅ΠΊΡΠΎΠ²), ΠΊΠΎΡΠΎΡΡΠ΅ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡΡ ΡΠ°Π·ΠΎΠ±ΡΠ°ΡΡΡΡ Π² ΡΠ΅ΠΌΠ΅ Π½Π°ΡΡΠΎΠ»ΡΠΊΠΎ Π³Π»ΡΠ±ΠΎΠΊΠΎ, Π½Π°ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΡΠΎ ΡΡΠ΅Π±ΡΠ΅ΡΡΡ.

Π ΠΎΠ΄Π½Ρ ΠΈΠ· ΠΏΡΡΠ½ΠΈΡ ΠΊΠΎ ΠΌΠ½Π΅ ΠΏΠΎΠ΄ΠΎΡΠ΅Π» ΠΊΠΎΠ»Π»Π΅Π³Π° ΠΈ ΠΏΠΎΠΈΠ½ΡΠ΅ΡΠ΅ΡΠΎΠ²Π°Π»ΡΡ, ΠΊΠ°ΠΊ Π²ΡΠΏΠΎΠ»Π½ΠΈΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ Π² podβΠ΅ Ρ ΠΏΠΎΠΌΠΎΡΡΡ . Π― Π½Π΅ ΡΠΌΠΎΠ³ Π΅ΠΌΡ ΠΎΡΠ²Π΅ΡΠΈΡΡ ΠΈ Π²Π½Π΅Π·Π°ΠΏΠ½ΠΎ ΠΎΡΠΎΠ·Π½Π°Π», ΡΡΠΎ Π½ΠΈΡΠ΅Π³ΠΎ Π½Π΅ Π·Π½Π°Ρ ΠΎ ΠΌΠ΅Ρ
Π°Π½ΠΈΠ·ΠΌΠ΅ ΡΠ°Π±ΠΎΡΡ kubectl exec. ΠΠ°, Ρ ΠΌΠ΅Π½Ρ Π±ΡΠ»ΠΈ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½Π½ΡΠ΅ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΡ ΠΎ Π΅Π³ΠΎ ΡΡΡΡΠΎΠΉΡΡΠ²Π΅, ΠΎΠ΄Π½Π°ΠΊΠΎ Ρ Π½Π΅ Π±ΡΠ» ΡΠ²Π΅ΡΠ΅Π½ Π½Π° 100% Π² ΠΈΡ
ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎΡΡΠΈ ΠΈ ΠΏΠΎΡΠΎΠΌΡ ΡΠ΅ΡΠΈΠ» Π·Π°Π½ΡΡΡΡΡ ΡΡΠΈΠΌ Π²ΠΎΠΏΡΠΎΡΠΎΠΌ. ΠΡΠΎΡΡΡΠ΄ΠΈΡΠΎΠ²Π°Π² Π±Π»ΠΎΠ³ΠΈ, Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ ΠΈ ΠΈΡΡ
ΠΎΠ΄Π½ΡΠΉ ΠΊΠΎΠ΄, ΡΠ·Π½Π°Π» ΠΌΠ½ΠΎΠ³ΠΎ Π½ΠΎΠ²ΠΎΠ³ΠΎ, ΠΈ Π² ΡΡΠΎΠΉ ΡΡΠ°ΡΡΠ΅ Ρ
ΠΎΡΡ ΠΏΠΎΠ΄Π΅Π»ΠΈΡΡΡΡ ΡΠ²ΠΎΠΈΠΌΠΈ ΠΎΡΠΊΡΡΡΠΈΡΠΌΠΈ ΠΈ ΠΏΠΎΠ½ΠΈΠΌΠ°Π½ΠΈΠ΅ΠΌ. ΠΡΠ»ΠΈ ΡΡΠΎ-ΡΠΎ Π½Π΅ ΡΠ°ΠΊ, ΠΏΠΎΠΆΠ°Π»ΡΠΉΡΡΠ°, ΡΠ²ΡΠΆΠΈΡΠ΅ΡΡ ΡΠΎ ΠΌΠ½ΠΎΠΉ Π² .
ΠΠΎΠ΄Π³ΠΎΡΠΎΠ²ΠΊΠ°
Π§ΡΠΎΠ±Ρ ΡΠΎΠ·Π΄Π°ΡΡ ΠΊΠ»Π°ΡΡΠ΅Ρ Π½Π° MacBookβΠ΅, Ρ ΡΠΊΠ»ΠΎΠ½ΠΈΡΠΎΠ²Π°Π» . ΠΠ°ΡΠ΅ΠΌ ΠΏΠΎΠΏΡΠ°Π²ΠΈΠ» IP-Π°Π΄ΡΠ΅ΡΠ° ΡΠ·Π»ΠΎΠ² Π² ΠΊΠΎΠ½ΡΠΈΠ³Π΅ kubeletβΠ°, ΠΏΠΎΡΠΊΠΎΠ»ΡΠΊΡ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ Π½Π΅ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ»ΠΈ Π²ΡΠΏΠΎΠ»Π½ΡΡΡ kubectl exec. ΠΠΎΠ΄ΡΠΎΠ±Π½Π΅Π΅ ΠΎΠ± ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΠΏΡΠΈΡΠΈΠ½Π΅ ΡΠΎΠΌΡ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡΠΎΡΠΈΡΠ°ΡΡ .
- ΠΡΠ±Π°Ρ ΠΌΠ°ΡΠΈΠ½Π° = ΠΌΠΎΠΉ MacBook
- IP master-ΡΠ·Π»Π° = 192.168.205.10
- IP worker-ΡΠ·Π»Π° = 192.168.205.11
- ΠΏΠΎΡΡ API-ΡΠ΅ΡΠ²Π΅ΡΠ° = 6443
ΠΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΡ

- kubectl exec process: ΠΊΠΎΠ³Π΄Π° ΠΌΡ Π²ΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌ Β«kubectl exec β¦Β», Π·Π°ΠΏΡΡΠΊΠ°Π΅ΡΡΡ ΠΏΡΠΎΡΠ΅ΡΡ. ΠΠ΅Π»Π°ΡΡ ΡΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π° Π»ΡΠ±ΠΎΠΉ ΠΌΠ°ΡΠΈΠ½Π΅ Ρ Π΄ΠΎΡΡΡΠΏΠΎΠΌ ΠΊ API-ΡΠ΅ΡΠ²Π΅ΡΡ K8s. ΠΡΠΈΠΌ. ΠΏΠ΅ΡΠ΅Π².: ΠΠ°Π»Π΅Π΅ Π² ΠΊΠΎΠ½ΡΠΎΠ»ΡΠ½ΡΡ Π»ΠΈΡΡΠΈΠ½Π³Π°Ρ Π°Π²ΡΠΎΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠΉ Β«any machineΒ», ΠΏΠΎΠ΄ΡΠ°Π·ΡΠΌΠ΅Π²Π°Ρ, ΡΡΠΎ ΠΏΠΎΡΠ»Π΅Π΄ΡΡΡΠΈΠ΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρ ΠΌΠΎΠΆΠ½ΠΎ Π²ΡΠΏΠΎΠ»Π½ΡΡΡ Π½Π° Π»ΡΠ±ΡΡ ΡΠ°ΠΊΠΈΡ ΠΌΠ°ΡΠΈΠ½Π°Ρ Ρ Π΄ΠΎΡΡΡΠΏΠΎΠΌ ΠΊ Kubernetes.
- : ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ Π½Π° ΠΌΠ°ΡΡΠ΅Ρ-ΡΠ·Π»Π΅, ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΡΡΠΈΠΉ Π΄ΠΎΡΡΡΠΏ ΠΊ API Kubernetes. ΠΡΠΎ ΡΡΠΎΠ½ΡΠ΅Π½Π΄ Π΄Π»Ρ control plane Π² Kubernetes.
- : Π°Π³Π΅Π½Ρ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ Π½Π° ΠΊΠ°ΠΆΠ΄ΠΎΠΌ ΡΠ·Π»Π΅ Π² ΠΊΠ»Π°ΡΡΠ΅ΡΠ΅. ΠΠ½ ΠΎΠ±Π΅ΡΠΏΠ΅ΡΠΈΠ²Π°Π΅Ρ ΡΠ°Π±ΠΎΡΡ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠΎΠ² Π² podβΠ΅.
- (ΠΈΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌΠ°Ρ ΡΡΠ΅Π΄Π° ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ°): ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ½ΠΎΠ΅ ΠΎΠ±Π΅ΡΠΏΠ΅ΡΠ΅Π½ΠΈΠ΅, ΠΎΡΠ²Π΅ΡΠ°ΡΡΠ΅Π΅ Π·Π° ΡΠ°Π±ΠΎΡΡ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠΎΠ². ΠΡΠΈΠΌΠ΅ΡΡ: Docker, CRI-O, containerdβ¦
- kernel: ΡΠ΄ΡΠΎ ΠΠ‘ Π½Π° ΡΠ°Π±ΠΎΡΠ΅ΠΌ ΡΠ·Π»Π΅; ΠΎΡΠ²Π΅ΡΠ°Π΅Ρ Π·Π° ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΡΠΎΡΠ΅ΡΡΠ°ΠΌΠΈ.
- target (ΡΠ΅Π»Π΅Π²ΠΎΠΉ) container: ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ, ΡΠ²Π»ΡΡΡΠΈΠΉΡΡ ΡΠ°ΡΡΡΡ podβΠ° ΠΈ ΡΡΠ½ΠΊΡΠΈΠΎΠ½ΠΈΡΡΡΡΠΈΠΉ Π½Π° ΠΎΠ΄Π½ΠΎΠΌ ΠΈΠ· ΡΠ°Π±ΠΎΡΠΈΡ ΡΠ·Π»ΠΎΠ².
Π§ΡΠΎ Ρ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠ»
1. ΠΠΊΡΠΈΠ²Π½ΠΎΡΡΡ Π½Π° ΡΡΠΎΡΠΎΠ½Π΅ ΠΊΠ»ΠΈΠ΅Π½ΡΠ°
Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ pod Π² ΠΏΡΠΎΡΡΡΠ°Π½ΡΡΠ²Π΅ ΠΈΠΌΠ΅Π½ 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ΠΡΠ»ΠΈ ΠΏΡΠΎΠ²Π΅ΡΠΈΡΡ ΡΠ΅ΡΠ΅Π²ΡΡ Π°ΠΊΡΠΈΠ²Π½ΠΎΡΡΡ ΠΏΡΠΎΡΠ΅ΡΡΠ°, ΠΌΡ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠΌ, ΡΡΠΎ Ρ Π½Π΅Π³ΠΎ Π΅ΡΡΡ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΡ ΠΊ api-serverβΡ (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-Π·Π°ΠΏΡΠΎΡ Ρ ΡΡΠ±ΡΠ΅ΡΡΡΡΠΎΠΌ exec ΠΈ ΠΏΠΎΡΡΠ»Π°Π΅Ρ 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)()

2. ΠΠΊΡΠΈΠ²Π½ΠΎΡΡΡ Π½Π° ΡΡΠΎΡΠΎΠ½Π΅ ΠΌΠ°ΡΡΠ΅Ρ-ΡΠ·Π»Π°
ΠΡ ΡΠ°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅ΠΌ Π½Π°Π±Π»ΡΠ΄Π°ΡΡ Π·Π°ΠΏΡΠΎΡ Π½Π° ΡΡΠΎΡΠΎΠ½Π΅ api-serverβΠ°:
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-Π·Π°ΠΏΡΠΎΡ Π²ΠΊΠ»ΡΡΠ°Π΅Ρ Π·Π°ΠΏΡΠΎΡ Π½Π° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΠΏΡΠΎΡΠΎΠΊΠΎΠ»Π°. ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ ΠΌΡΠ»ΡΡΠΈΠΏΠ»Π΅ΠΊΡΠΈΡΠΎΠ²Π°ΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠ΅ Β«ΠΏΠΎΡΠΎΠΊΠΈΒ» stdin/stdout/stderr/spdy-error ΡΠ΅ΡΠ΅Π· Π΅Π΄ΠΈΠ½ΠΎΠ΅ TCP-ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅.
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
}()
Π§ΡΠΎΠ±Ρ Π²ΡΠΏΠΎΠ»Π½ΠΈΡΡ ΡΡΠ΅Π±ΡΠ΅ΠΌΡΠ΅ Π΄Π΅ΠΉΡΡΠ²ΠΈΡ, api-server Π΄ΠΎΠ»ΠΆΠ΅Π½ Π·Π½Π°ΡΡ, Ρ ΠΊΠ°ΠΊΠΈΠΌ podβΠΎΠΌ Π΅ΠΌΡ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ ΡΠ²ΡΠ·Π°ΡΡΡΡ:
// 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")
}()
ΠΠΎΠ½Π΅ΡΠ½ΠΎ, Π΄Π°Π½Π½ΡΠ΅ ΠΎΠ± endpointβΠ΅ Π±Π΅ΡΡΡΡΡ ΠΈΠ· ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎΠ± ΡΠ·Π»Π΅:
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)()
Π£ΡΠ°! Π£ 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
}()
ΠΠ· Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ :
ΠΡΠΈ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΡ Π·Π°ΠΌΡΠΊΠ°ΡΡΡΡ Π½Π° HTTPS endpointβΠ΅ kubeletβΠ°. ΠΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ, apiserver Π½Π΅ ΠΏΡΠΎΠ²Π΅ΡΡΠ΅Ρ ΡΠ΅ΡΡΠΈΡΠΈΠΊΠ°Ρ kubeletβΠ°, ΡΡΠΎ Π΄Π΅Π»Π°Π΅Ρ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ ΡΡΠ·Π²ΠΈΠΌΡΠΌ ΠΊ Β«Π°ΡΠ°ΠΊΠ°ΠΌ ΠΏΠΎΡΡΠ΅Π΄Π½ΠΈΠΊΠ°Β» (MITM) ΠΈ Π½Π΅Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΡΠΌ Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ Π² Π½Π΅Π½Π°Π΄Π΅ΠΆΠ½ΡΡ ΠΈ/ΠΈΠ»ΠΈ ΠΏΡΠ±Π»ΠΈΡΠ½ΡΡ ΡΠ΅ΡΡΡ .
Π’Π΅ΠΏΠ΅ΡΡ API-ΡΠ΅ΡΠ²Π΅Ρ Π·Π½Π°Π΅Ρ endpoint ΠΈ ΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅Ρ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅:
// 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
}()
ΠΠ°Π²Π°ΠΉΡΠ΅ ΠΏΠΎΡΠΌΠΎΡΡΠΈΠΌ, ΡΡΠΎ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ Π½Π° ΠΌΠ°ΡΡΠ΅Ρ-ΡΠ·Π»Π΅.
Π‘Π½Π°ΡΠ°Π»Π° ΡΠ·Π½Π°Π΅ΠΌ 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ΠΠ°ΡΠ΅ΠΌ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΠΌ ΠΏΠΎΡΡ kubeletβΠ° (10250 Π² Π½Π°ΡΠ΅ΠΌ ΡΠ»ΡΡΠ°Π΅):
// any machine
$ kubectl get nodes k8s-node-1 -o jsonpath='{.status.daemonEndpoints.kubeletEndpoint}'
map[Port:10250] Π’Π΅ΠΏΠ΅ΡΡ ΠΏΠΎΡΠ° ΠΏΡΠΎΠ²Π΅ΡΠΈΡΡ ΡΠ΅ΡΡ. ΠΡΡΡ Π»ΠΈ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΊ ΡΠ°Π±ΠΎΡΠ΅ΠΌΡ ΡΠ·Π»Ρ (192.168.205.11)? ΠΠ½ΠΎ Π΅ΡΡΡ! ΠΡΠ»ΠΈ Β«ΡΠ±ΠΈΡΡΒ» ΠΏΡΠΎΡΠ΅ΡΡ exec, ΠΎΠ½ΠΎ ΠΈΡΡΠ΅Π·Π½Π΅Ρ, ΠΏΠΎΡΡΠΎΠΌΡ Ρ Π·Π½Π°Ρ, ΡΡΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ΠΎ api-serverβΠΎΠΌ ΠΊΠ°ΠΊ ΡΠ»Π΅Π΄ΡΡΠ²ΠΈΠ΅ Π²ΡΠΏΠΎΠ»Π½Π΅Π½Π½ΠΎΠΉ 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βΠΎΠΌ ΠΈ api-serverβΠΎΠΌ ΠΏΠΎ-ΠΏΡΠ΅ΠΆΠ½Π΅ΠΌΡ ΠΎΡΠΊΡΡΡΠΎ. ΠΡΠΎΠΌΠ΅ ΡΠΎΠ³ΠΎ, Π΅ΡΡΡ Π΅ΡΠ΅ ΠΎΠ΄Π½ΠΎ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅, ΡΠ²ΡΠ·ΡΠ²Π°ΡΡΠ΅Π΅ api-server ΠΈ kubelet.
3. ΠΠΊΡΠΈΠ²Π½ΠΎΡΡΡ Π½Π° ΡΠ°Π±ΠΎΡΠ΅ΠΌ ΡΠ·Π»Π΅
Π’Π΅ΠΏΠ΅ΡΡ Π΄Π°Π²Π°ΠΉΡΠ΅ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΠΌΡΡ ΠΊ worker-ΡΠ·Π»Ρ ΠΈ ΠΏΠΎΡΠΌΠΎΡΡΠΈΠΌ, ΡΡΠΎ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ Π½Π° Π½Π΅ΠΌ.
ΠΡΠ΅ΠΆΠ΄Π΅ Π²ΡΠ΅Π³ΠΎ ΠΌΡ Π²ΠΈΠ΄ΠΈΠΌ, ΡΡΠΎ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ Ρ Π½ΠΈΠΌ ΡΠ°ΠΊΠΆΠ΅ ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ΠΎ (Π²ΡΠΎΡΠ°Ρ ΡΡΡΠΎΠΊΠ°); 192.168.205.10 β ΡΡΠΎ IP master-ΡΠ·Π»Π°:
// 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
β¦ΠΠΎ ΠΏΠΎΡΡΠΎΠΉΡΠ΅: ΠΊΠ°ΠΊ kubelet ΠΏΡΠΎΠ²Π΅ΡΠ½ΡΠ» ΡΡΠΎ? Π kubelet Π΅ΡΡΡ Π΄Π΅ΠΌΠΎΠ½, ΠΊΠΎΡΠΎΡΡΠΉ ΠΎΡΠΊΡΡΠ²Π°Π΅Ρ Π΄ΠΎΡΡΡΠΏ ΠΊ API ΡΠ΅ΡΠ΅Π· ΠΏΠΎΡΡ Π΄Π»Ρ Π·Π°ΠΏΡΠΎΡΠΎΠ² api-serverβΠ°:
// 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
}()
Kubelet Π²ΡΡΠΈΡΠ»ΡΠ΅Ρ ΠΎΡΠ²Π΅ΡΠ½ΡΠΉ endpoint Π΄Π»Ρ 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
}()
ΠΠ΅ ΠΏΠ΅ΡΠ΅ΠΏΡΡΠ°ΠΉΡΠ΅. ΠΠ½ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ Π½Π΅ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ ΠΊΠΎΠΌΠ°Π½Π΄Ρ, Π° endpoint Π΄Π»Ρ ΡΠ²ΡΠ·ΠΈ:
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:"-"`
}()
Kubelet ΡΠ΅Π°Π»ΠΈΠ·ΡΠ΅Ρ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ RuntimeServiceClient, ΡΠ²Π»ΡΡΡΠΈΠΉΡΡ ΡΠ°ΡΡΡΡ Container Runtime Interface (ΠΏΠΎΠ΄ΡΠΎΠ±Π½Π΅Π΅ ΠΎ Π½ΡΠΌ ΠΌΡ ΠΏΠΈΡΠ°Π»ΠΈ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, β ΠΏΡΠΈΠΌ. ΠΏΠ΅ΡΠ΅Π².):
ΠΠ»ΠΈΠ½Π½ΡΠΉ Π»ΠΈΡΡΠΈΠ½Π³ ΠΈΠ· cri-api Π² kubernetes/kubernetes
// 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)
} ()
ΠΠ½ ΠΏΡΠΎΡΡΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ gRPC Π΄Π»Ρ Π²ΡΠ·ΠΎΠ²Π° ΠΌΠ΅ΡΠΎΠ΄Π° ΡΠ΅ΡΠ΅Π· Container Runtime Interface:
type runtimeServiceClient struct {
cc *grpc.ClientConn
}()
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
}()
Container Runtime ΠΎΡΠ²Π΅ΡΠ°Π΅Ρ Π·Π° ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ RuntimeServiceServer:
ΠΠ»ΠΈΠ½Π½ΡΠΉ Π»ΠΈΡΡΠΈΠ½Π³ ΠΈΠ· cri-api Π² kubernetes/kubernetes
// 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)
} ()

ΠΡΠ»ΠΈ ΡΡΠΎ ΡΠ°ΠΊ, ΠΌΡ Π΄ΠΎΠ»ΠΆΠ½Ρ Π²ΠΈΠ΄Π΅ΡΡ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ ΠΌΠ΅ΠΆΠ΄Ρ kubeletβΠΎΠΌ ΠΈ ΠΈΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌΠΎΠΉ ΡΡΠ΅Π΄ΠΎΠΉ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ°, ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ? ΠΠ°Π²Π°ΠΉΡΠ΅ ΠΏΡΠΎΠ²Π΅ΡΠΈΠΌ.
ΠΡΠΏΠΎΠ»Π½ΠΈΡΠ΅ ΡΡΡ ΠΊΠΎΠΌΠ°Π½Π΄Ρ Π΄ΠΎ ΠΈ ΠΏΠΎΡΠ»Π΅ exec-ΠΊΠΎΠΌΠ°Π½Π΄Ρ ΠΈ ΠΏΠΎΡΠΌΠΎΡΡΠΈΡΠ΅ Π½Π° ΠΎΡΠ»ΠΈΡΠΈΡ. Π ΠΌΠΎΠ΅ΠΌ ΡΠ»ΡΡΠ°Π΅ ΡΠ°Π·Π½ΠΈΡΠ° ΡΠ°ΠΊΠΎΠ²Π°:
// worker node
$ ss -a -p |grep kubelet
...
u_str ESTAB 0 0 * 157937 * 157387 users:(("kubelet",pid=5714,fd=33))
...Π₯ΠΌ-ΠΌ-ΠΌβ¦ ΠΠΎΠ²ΠΎΠ΅ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ ΡΠ΅ΡΠ΅Π· unix-ΡΠΎΠΊΠ΅ΡΡ ΠΌΠ΅ΠΆΠ΄Ρ kubeletβΠΎΠΌ (pid=5714) ΠΈ ΡΠ΅ΠΌ-ΡΠΎ Π½Π΅ΠΈΠ·Π²Π΅ΡΡΠ½ΡΠΌ. Π§ΡΠΎ ΠΆΠ΅ ΡΡΠΎ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ? ΠΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ, ΡΡΠΎ Docker (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))
...ΠΠ°ΠΊ Π²Ρ ΠΏΠΎΠΌΠ½ΠΈΡΠ΅, ΡΡΠΎ ΠΏΡΠΎΡΠ΅ΡΡ docker-Π΄Π΅ΠΌΠΎΠ½Π° (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, ΡΡΠΎΠ±Ρ ΠΏΠΎΠ½ΡΡΡ, ΡΡΠΎ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ. Π DockerβΠ΅ Π»ΠΎΠ³ΠΈΠΊΠ° Π°Π½Π°Π»ΠΎΠ³ΠΈΡΠ½Π°Ρ.
ΠΠΌΠ΅Π΅ΡΡΡ ΡΠ΅ΡΠ²Π΅Ρ, ΠΎΡΠ²Π΅ΡΠ°ΡΡΠΈΠΉ Π·Π° ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ 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
}()
// 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
}()
Π ΠΊΠΎΠ½ΡΠ΅ ΡΠ΅ΠΏΠΎΡΠΊΠΈ ΠΈΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌΠ°Ρ ΡΡΠ΅Π΄Π° ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ° Π²ΡΠΏΠΎΠ»Π½ΡΠ΅Ρ ΠΊΠΎΠΌΠ°Π½Π΄Ρ Π½Π° ΡΠ°Π±ΠΎΡΠ΅ΠΌ ΡΠ·Π»Π΅:
// 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
}()

ΠΠ°ΠΊΠΎΠ½Π΅Ρ, ΡΠ΄ΡΠΎ Π²ΡΠΏΠΎΠ»Π½ΡΠ΅Ρ ΠΊΠΎΠΌΠ°Π½Π΄Ρ:

ΠΠ°ΠΏΠΎΠΌΠΈΠ½Π°Π½ΠΈΡ
- API Server ΡΠ°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΠΎΠ²Π°ΡΡ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΠ΅ Ρ kubeletβΠΎΠΌ.
- Π‘Π»Π΅Π΄ΡΡΡΠΈΠ΅ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΡ ΡΠΎΡ
ΡΠ°Π½ΡΡΡΡΡ Π΄ΠΎ ΠΎΠΊΠΎΠ½ΡΠ°Π½ΠΈΡ ΠΈΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½ΠΎΠ³ΠΎ exec-ΡΠ΅Π°Π½ΡΠ°:
- ΠΌΠ΅ΠΆΠ΄Ρ kubectl ΠΈ api-serverβΠΎΠΌ;
- ΠΌΠ΅ΠΆΠ΄Ρ api-serverβΠΎΠΌ ΠΈ kubectl;
- ΠΌΠ΅ΠΆΠ΄Ρ kubeletβΠΎΠΌ ΠΈ ΠΈΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌΠΎΠΉ ΡΡΠ΅Π΄ΠΎΠΉ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ°.
- Kubectl ΠΈΠ»ΠΈ api-server Π½Π΅ ΠΌΠΎΠ³ΡΡ Π½ΠΈΡΠ΅Π³ΠΎ Π·Π°ΠΏΡΡΠΊΠ°ΡΡ Π½Π° ΡΠ°Π±ΠΎΡΠΈΡ ΡΠ·Π»Π°Ρ . Kubelet ΠΌΠΎΠΆΠ΅Ρ Π·Π°ΠΏΡΡΠΊΠ°ΡΡ, Π½ΠΎ Π΄Π»Ρ ΡΡΠΈΡ Π΄Π΅ΠΉΡΡΠ²ΠΈΠΉ ΠΎΠ½ ΡΠ°ΠΊΠΆΠ΅ Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΡΠ΅Ρ Ρ ΠΈΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌΠΎΠΉ ΡΡΠ΅Π΄ΠΎΠΉ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅ΡΠ°.
Π Π΅ΡΡΡΡΡ
- ΠΠ±ΡΡΠΆΠ΄Π΅Π½ΠΈΠ΅ «» Π² kubernetes-dev;
- Π‘ΡΠ°ΡΡΡ Β«Β»;
- ΠΠ±ΡΡΠΆΠ΄Π΅Π½ΠΈΠ΅ «» Π½Π° Server Fault.
P.S. ΠΎΡ ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄ΡΠΈΠΊΠ°
Π§ΠΈΡΠ°ΠΉΡΠ΅ ΡΠ°ΠΊΠΆΠ΅ Π² Π½Π°ΡΠ΅ΠΌ Π±Π»ΠΎΠ³Π΅:
- Β«Π§ΡΠΎ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ Π² Kubernetes ΠΏΡΠΈ Π·Π°ΠΏΡΡΠΊΠ΅ kubectl run?Β» ΠΈ ;
- «»;
- «»;
- «».
ΠΡΡΠΎΡΠ½ΠΈΠΊ: habr.com

