តើ kubectl exec αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž™αŸ‰αžΆαž„αžŠαžΌαž…αž˜αŸ’αžαŸαž…?

αž…αŸ†αžŽαžΆαŸ†αŸ” αž”αž€αž”αŸ’αžšαŸ‚αŸ– αž’αŸ’αž“αž€αž“αž·αž–αž“αŸ’αž’αž’αžαŸ’αžαž”αž‘ - Erkan Erol αžœαž·αžŸαŸ’αžœαž€αžšαž˜αž€αž–αžΈ SAP - αž…αŸ‚αž€αžšαŸ†αž›αŸ‚αž€αž€αžΆαžšαžŸαž·αž€αŸ’αžŸαžΆαžšαž”αžŸαŸ‹αž‚αžΆαžαŸ‹αž’αŸ†αž–αžΈαž™αž“αŸ’αžαž€αžΆαžšαž“αŸƒαžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž€αŸ’αžšαž»αž˜ kubectl execαžŠαžΌαž…αŸ’αž“αŸαŸ‡αžŸαŸ’αž‚αžΆαž›αŸ‹αž‚αŸ’αžšαž”αŸ‹αž‚αŸ’αž“αžΆαžŠαŸ‚αž›αž’αŸ’αžœαžΎαž€αžΆαžšαž‡αžΆαž˜αž½αž™ Kubernetes αŸ” αž‚αžΆαžαŸ‹αž’αž˜αž‡αžΆαž˜αž½αž™αž€αŸ’αž”αž½αž“αžŠαŸ„αŸ‡αžŸαŸ’αžšαžΆαž™αž‘αžΆαŸ†αž„αž˜αžΌαž›αž‡αžΆαž˜αž½αž™αž“αžΉαž„αž€αžΆαžšαž…αž»αŸ‡αž”αž‰αŸ’αž‡αžΈαž“αŸƒαž€αžΌαžŠαž”αŸ’αžšαž—αž– Kubernetes (αž“αž·αž„αž‚αž˜αŸ’αžšαŸ„αž„αž–αžΆαž€αŸ‹αž–αŸαž“αŸ’αž’) αžŠαŸ‚αž›αž’αž“αž»αž‰αŸ’αž‰αžΆαžαž±αŸ’αž™αž’αŸ’αž“αž€αž™αž›αŸ‹αž–αžΈαž”αŸ’αžšαž’αžΆαž“αž”αž‘αž™αŸ‰αžΆαž„αžŸαŸŠαžΈαž‡αž˜αŸ’αžšαŸ…αžαžΆαž˜αžαž˜αŸ’αžšαžΌαžœαž€αžΆαžšαŸ”

តើ kubectl exec αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž™αŸ‰αžΆαž„αžŠαžΌαž…αž˜αŸ’αžαŸαž…?

αžαŸ’αž„αŸƒαžŸαž»αž€αŸ’αžšαž˜αž½αž™ αžŸαž αžŸαŸαžœαž·αž€αž˜αŸ’αž“αžΆαž€αŸ‹αž”αžΆαž“αž˜αž€αžšαž€αžαŸ’αž‰αž»αŸ† αž αžΎαž™αžŸαž½αžšαž–αžΈαžšαž”αŸ€αž”αž”αŸ’αžšαžαž·αž”αžαŸ’αžαž·αž–αžΆαž€αŸ’αž™αž”αž‰αŸ’αž‡αžΆαž“αŸ…αž€αŸ’αž“αž»αž„αž•αžαžŠαŸ„αž™αž”αŸ’αžšαžΎ αž’αžαž·αžαž·αž‡αž“αž‘αŸ…. αžαŸ’αž‰αž»αŸ†β€‹αž˜αž·αž“β€‹αž’αžΆαž…β€‹αž†αŸ’αž›αžΎαž™β€‹αž‚αžΆαžαŸ‹β€‹αž”αžΆαž“β€‹αž‘αŸ αž αžΎαž™β€‹αžŸαŸ’αžšαžΆαž”αŸ‹αžαŸ‚β€‹αžŠαžΉαž„β€‹αžαžΆβ€‹αžαŸ’αž‰αž»αŸ†β€‹αž˜αž·αž“β€‹αžŠαžΉαž„β€‹αž’αŸ’αžœαžΈβ€‹αžŸαŸ„αŸ‡β€‹αž’αŸ†αž–αžΈβ€‹αž™αž“αŸ’αžαž€αžΆαžšβ€‹αž“αŸƒβ€‹αž€αžΆαžšβ€‹αž”αŸ’αžšαžαž·αž”αžαŸ’αžαž·αž€αžΆαžš kubectl exec. αž”αžΆαž‘/αž…αžΆαžŸ αžαŸ’αž‰αž»αŸ†αž˜αžΆαž“αž‚αŸ†αž“αž·αžαž‡αžΆαž€αŸ‹αž›αžΆαž€αŸ‹αž’αŸ†αž–αžΈαžšαž…αž“αžΆαžŸαž˜αŸ’αž–αŸαž“αŸ’αž’αžšαž”αžŸαŸ‹αžœαžΆ αž”αŸ‰αž»αž“αŸ’αžαŸ‚αžαŸ’αž‰αž»αŸ†αž˜αž·αž“αž”αŸ’αžšαžΆαž€αžŠ 100% αž“αŸƒαž—αžΆαž–αžαŸ’αžšαžΉαž˜αžαŸ’αžšαžΌαžœαžšαž”αžŸαŸ‹αž–αž½αž€αž‚αŸαž‘αŸ αžŠαžΌαž…αŸ’αž“αŸαŸ‡αž αžΎαž™αž”αžΆαž“αžŸαž˜αŸ’αžšαŸαž…αž…αž·αžαŸ’αžαžŠαŸ„αŸ‡αžŸαŸ’αžšαžΆαž™αž”αž‰αŸ’αž αžΆαž“αŸαŸ‡αŸ” αžŠαŸ„αž™αž”αžΆαž“αžŸαž·αž€αŸ’αžŸαžΆαž”αŸ’αž›αž»αž€ αž―αž€αžŸαžΆαžš αž“αž·αž„αž€αžΌαžŠαž”αŸ’αžšαž—αž– αžαŸ’αž‰αž»αŸ†αž”αžΆαž“αžšαŸ€αž“αž’αŸ’αžœαžΈαžαŸ’αž˜αžΈαŸ—αž‡αžΆαž…αŸ’αžšαžΎαž“ αž αžΎαž™αž“αŸ…αž€αŸ’αž“αž»αž„αž’αžαŸ’αžαž”αž‘αž“αŸαŸ‡ αžαŸ’αž‰αž»αŸ†αž…αž„αŸ‹αž…αŸ‚αž€αžšαŸ†αž›αŸ‚αž€αž€αžΆαžšαžšαž€αžƒαžΎαž‰ αž“αž·αž„αž€αžΆαžšαž™αž›αŸ‹αžŠαžΉαž„αžšαž”αžŸαŸ‹αžαŸ’αž‰αž»αŸ†αŸ” αž”αžΎαž˜αžΆαž“αž’αŸ’αžœαžΈαžαž»αžŸ αžŸαžΌαž˜αž‘αžΆαž€αŸ‹αž‘αž„αž˜αž€αžαŸ’αž‰αž»αŸ†αžαžΆαž˜αž›αŸαž Twitter.

αž€αžΆαžšαžšαŸ€αž”αž…αŸ†

αžŠαžΎαž˜αŸ’αž”αžΈαž”αž„αŸ’αž€αžΎαžαž…αž„αŸ’αž€αŸ„αž˜αž“αŸ…αž›αžΎ MacBook αžαŸ’αž‰αž»αŸ†αž”αžΆαž“αž€αŸ’αž›αžΌαž“ ecomm-integration-ballerina/kubernetes-cluster. αž”αž“αŸ’αž‘αžΆαž”αŸ‹αž˜αž€αžαŸ’αž‰αž»αŸ†αž”αžΆαž“αž€αŸ‚αž’αžΆαžŸαž™αžŠαŸ’αž‹αžΆαž“ IP αžšαž”αžŸαŸ‹αžαŸ’αž“αžΆαŸ†αž„αž“αŸ…αž€αŸ’αž“αž»αž„αž€αžΆαžšαž€αŸ†αžŽαžαŸ‹ kubelet αž…αžΆαž”αŸ‹αžαžΆαŸ†αž„αž–αžΈαž€αžΆαžšαž€αŸ†αžŽαžαŸ‹αž›αŸ†αž“αžΆαŸ†αžŠαžΎαž˜αž˜αž·αž“αž’αž“αž»αž‰αŸ’αž‰αžΆαžαŸ” kubectl exec. αž’αŸ’αž“αž€αž’αžΆαž…αž’αžΆαž“αž”αž“αŸ’αžαŸ‚αž˜αž’αŸ†αž–αžΈαž αŸαžαž»αž•αž›αž…αž˜αŸ’αž”αž„αžŸαž˜αŸ’αžšαžΆαž”αŸ‹αžšαžΏαž„αž“αŸαŸ‡ αž“αŸ…αž‘αžΈαž“αŸαŸ‡.

  • αžšαžαž™αž“αŸ’αžαžŽαžΆαž˜αž½αž™ = αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“ MacBook αžšαž”αžŸαŸ‹αžαŸ’αž‰αž»αŸ†
  • αžαŸ’αž“αžΆαŸ†αž„αž˜αŸ IP = 192.168.205.10
  • αžαŸ’αž“αžΆαŸ†αž„ IP = 192.168.205.11
  • αž…αŸ’αžšαž€αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸ API = 6443

αžŸαž˜αžΆαžŸαž—αžΆαž‚

តើ kubectl exec αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž™αŸ‰αžΆαž„αžŠαžΌαž…αž˜αŸ’αžαŸαž…?

  • kubectl αžŠαŸ†αžŽαžΎαžšαž€αžΆαžš execαŸ– αž“αŸ…αž–αŸαž›αžŠαŸ‚αž›αž™αžΎαž„αž”αŸ’αžšαžαž·αž”αžαŸ’αžαž· β€œkubectl exec…” αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαžαŸ’αžšαžΌαžœαž”αžΆαž“αž…αžΆαž”αŸ‹αž•αŸ’αžαžΎαž˜αŸ” αž“αŸαŸ‡αž’αžΆαž…αžαŸ’αžšαžΌαžœαž”αžΆαž“αž’αŸ’αžœαžΎαž“αŸ…αž›αžΎαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αžŽαžΆαž˜αž½αž™αžŠαŸ‚αž›αž˜αžΆαž“αžŸαž·αž‘αŸ’αž’αž·αž…αžΌαž›αž”αŸ’αžšαžΎαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸ K8s API αŸ” αž…αŸ†αžŽαžΆαŸ† αž”αž€αž”αŸ’αžšαŸ‚αŸ– αž”αž“αŸ’αžαŸ‚αž˜αž‘αŸ€αžαž“αŸ…αž€αŸ’αž“αž»αž„αž”αž‰αŸ’αž‡αžΈαž€αž»αž„αžŸαžΌαž› αž’αŸ’αž“αž€αž“αž·αž–αž“αŸ’αž’αž”αŸ’αžšαžΎαž˜αžαž·αž™αŸ„αž”αž›αŸ‹ "αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αžŽαžΆαž˜αž½αž™" αžŠαŸ„αž™αž”αž„αŸ’αž€αž”αŸ‹αž“αŸαž™αžαžΆαž–αžΆαž€αŸ’αž™αž”αž‰αŸ’αž‡αžΆαž‡αžΆαž”αž“αŸ’αžαž”αž“αŸ’αž‘αžΆαž”αŸ‹αž’αžΆαž…αžαŸ’αžšαžΌαžœαž”αžΆαž“αž”αŸ’αžšαžαž·αž”αžαŸ’αžαž·αž›αžΎαž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž”αŸ‚αž”αž“αŸαŸ‡αžŠαŸ‚αž›αž˜αžΆαž“αžŸαž·αž‘αŸ’αž’αž·αž…αžΌαž›αž”αŸ’αžšαžΎ Kubernetes αŸ”
  • αž˜αŸ‰αžΆαžŸαŸŠαžΈαž“αž˜αŸ apiαŸ– αžŸαž˜αžΆαžŸαž’αžΆαžαž»αž˜αž½αž™αž“αŸ…αž›αžΎαžαŸ’αž“αžΆαŸ†αž„αž˜αŸαžŠαŸ‚αž›αž•αŸ’αžαž›αŸ‹αžŸαž·αž‘αŸ’αž’αž·αž…αžΌαž›αž”αŸ’αžšαžΎ Kubernetes API αŸ” αž“αŸαŸ‡αž‚αžΊαž‡αžΆαž•αŸ’αž“αŸ‚αž€αžαžΆαž„αž˜αž»αžαžŸαž˜αŸ’αžšαžΆαž”αŸ‹αž™αž“αŸ’αžαž αŸ„αŸ‡αž”αž‰αŸ’αž‡αžΆαž“αŸ…αž€αŸ’αž“αž»αž„ Kubernetes αŸ”
  • αž‚αžΌαž”αŸ– αž—αŸ’αž“αžΆαž€αŸ‹αž„αžΆαžšαžŠαŸ‚αž›αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž›αžΎαž‚αŸ’αžšαž”αŸ‹ node αž€αŸ’αž“αž»αž„ cluster αŸ” αžœαžΆαž’αžΆαž“αžΆαž“αžΌαžœαž”αŸ’αžšαžαž·αž”αžαŸ’αžαž·αž€αžΆαžšαž“αŸƒαž’αž»αž„αž“αŸ…αž€αŸ’αž“αž»αž„αž•αžαŸ”
  • αžšαž™αŸˆαž–αŸαž›αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž€αž»αž„αžαžΊαž“αŸαžš (container runtime): αž€αž˜αŸ’αž˜αžœαž·αž’αžΈαžŠαŸ‚αž›αž‘αž‘αž½αž›αžαž»αžŸαžαŸ’αžšαžΌαžœαžŸαž˜αŸ’αžšαžΆαž”αŸ‹αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž€αž»αž„αžαžΊαž“αŸαžšαŸ” αž§αž‘αžΆαž αžšαžŽαŸαŸ– Docker, CRI-O, container...
  • αžαžΊαžŽαŸ‚αž›αŸ– αžαžΊαžŽαŸ‚αž› OS αž“αŸ…αž›αžΎαžαŸ’αž“αžΆαŸ†αž„αž€αž˜αŸ’αž˜αž€αžš; αž‘αž‘αž½αž›αžαž»αžŸαžαŸ’αžšαžΌαžœαž…αŸ†αž–αŸ„αŸ‡αž€αžΆαžšαž‚αŸ’αžšαž”αŸ‹αž‚αŸ’αžšαž„αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαŸ”
  • αž‚αŸ„αž›αžŠαŸ… (αž‚αŸ„αž›αžŠαŸ…) αž’αž»αž„αŸ– αž’αž»αž„αžŠαŸ‚αž›αž‡αžΆαž•αŸ’αž“αŸ‚αž€αž˜αž½αž™αž“αŸƒαž•αž αž αžΎαž™αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž›αžΎαžαŸ’αž“αžΆαŸ†αž„αž€αž˜αŸ’αž˜αž€αžšαž˜αž½αž™αŸ”

αž’αŸ’αžœαžΈαžŠαŸ‚αž›αžαŸ’αž‰αž»αŸ†αž”αžΆαž“αžšαž€αžƒαžΎαž‰

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

αž”αŸ’αžšαžŸαž·αž“αž”αžΎαž™αžΎαž„αž–αž·αž“αž·αžαŸ’αž™αž˜αžΎαž›αžŸαž€αž˜αŸ’αž˜αž—αžΆαž–αž”αžŽαŸ’αžαžΆαž‰αž“αŸƒαžŠαŸ†αžŽαžΎαžšαž€αžΆαžš αž™αžΎαž„αž“αžΉαž„αžƒαžΎαž‰αžαžΆαžœαžΆαž˜αžΆαž“αž€αžΆαžšαžαž—αŸ’αž‡αžΆαž”αŸ‹αž‘αŸ…αž€αžΆαž“αŸ‹ 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)

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

តើ kubectl exec αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž™αŸ‰αžΆαž„αžŠαžΌαž…αž˜αŸ’αžαŸαž…?

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 αžšαž½αž˜αž”αž‰αŸ’αž…αžΌαž›αžŸαŸ†αžŽαžΎαžŠαžΎαž˜αŸ’αž”αžΈαž•αŸ’αž›αžΆαžŸαŸ‹αž”αŸ’αžαžΌαžšαž–αž·αž’αžΈαž€αžΆαžšαŸ” αž’αŸαžŸαž—αžΈαžŒαžΈ αž’αž“αž»αž‰αŸ’αž‰αžΆαžαž±αŸ’αž™αž’αŸ’αž“αž€ multiplex αž”αž»αž‚αŸ’αž‚αž› stdin/stdout/stderr/spdy-error "streams" αž›αžΎαž€αžΆαžšαžαž—αŸ’αž‡αžΆαž”αŸ‹ 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
}

(pkg/apis/core/types.go)

αžŠαžΎαž˜αŸ’αž”αžΈαž’αž“αž»αžœαžαŸ’αžαžŸαž€αž˜αŸ’αž˜αž—αžΆαž–αžŠαŸ‚αž›αžαŸ’αžšαžΌαžœαž€αžΆαžšαž“αŸ„αŸ‡ 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")
}

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

αž–αžΈαž―αž€αžŸαžΆαžš αž€αžΆαžšαž‘αŸ†αž“αžΆαž€αŸ‹αž‘αŸ†αž“αž„ Master-Node > Master to Cluster > apiserver to kubelet:

αž€αžΆαžšαžαž—αŸ’αž‡αžΆαž”αŸ‹αž‘αžΆαŸ†αž„αž“αŸαŸ‡αžαŸ’αžšαžΌαžœαž”αžΆαž“αž’αŸ’αžœαžΎαž‘αžΎαž„αž‘αŸ…αž€αžΆαž“αŸ‹αž…αŸ†αžŽαž»αž…αž”αž‰αŸ’αž…αž”αŸ‹ HTTPS αžšαž”αžŸαŸ‹ kubelet αŸ” αžαžΆαž˜αž›αŸ†αž“αžΆαŸ†αžŠαžΎαž˜ apiserver αž˜αž·αž“αž•αŸ’αž‘αŸ€αž„αž•αŸ’αž‘αžΆαžαŸ‹αžœαž·αž‰αŸ’αž‰αžΆαž”αž“αž”αžαŸ’αžšαžšαž”αžŸαŸ‹ kubelet αžŠαŸ‚αž›αž’αŸ’αžœαžΎαž±αŸ’αž™αž€αžΆαžšαžαž—αŸ’αž‡αžΆαž”αŸ‹αž„αžΆαž™αžšαž„αž€αžΆαžšαžœαžΆαž™αž”αŸ’αžšαž αžΆαžšαžŠαŸ„αž™ man-in-the-middle (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)

αžαŸ„αŸ‡αž˜αžΎαž›αž’αŸ’αžœαžΈαžŠαŸ‚αž›αž€αŸ†αž–αž»αž„αž€αžΎαžαž‘αžΎαž„αž“αŸ…αž›αžΎαžαŸ’αž“αžΆαŸ†αž„αž˜αŸαŸ”

αžŠαŸ†αž”αžΌαž„αž™αžΎαž„αžŸαŸ’αžœαŸ‚αž„αžšαž€ 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 exec αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž™αŸ‰αžΆαž„αžŠαžΌαž…αž˜αŸ’αžαŸαž…?

αž€αžΆαžšαžαž—αŸ’αž‡αžΆαž”αŸ‹αžšαžœαžΆαž„ kubectl αž“αž·αž„ api-server αž“αŸ…αžαŸ‚αž”αžΎαž€αŸ” αž›αžΎαžŸαž–αžΈαž“αŸαŸ‡αž‘αŸ€αžαž˜αžΆαž“αž€αžΆαžšαžαž—αŸ’αž‡αžΆαž”αŸ‹αž˜αž½αž™αž•αŸ’αžŸαŸαž„αž‘αŸ€αžαžŠαŸ‚αž›αž—αŸ’αž‡αžΆαž”αŸ‹ api-server αž“αž·αž„ kubelet αŸ”

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
  …

αž”αŸ‰αž»αž“αŸ’αžαŸ‚αžšαž„αŸ‹αž…αžΆαŸ†αŸ– តើ 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
}

(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αžŠαŸ‚αž›αž‡αžΆαž•αŸ’αž“αŸ‚αž€αž˜αž½αž™αž“αŸƒ 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)
}

(cri-api/pkg/apis/runtime/v1alpha2/api.pb.go)
αžœαžΆαž‚αŸ’αžšαžΆαž“αŸ‹αžαŸ‚αž”αŸ’αžšαžΎ gRPC αžŠαžΎαž˜αŸ’αž”αžΈαž αŸ…αžœαž·αž’αžΈαžŸαžΆαžŸαŸ’αžαŸ’αžšαžαžΆαž˜αžšαž™αŸˆ Container Runtime InterfaceαŸ–

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)

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

(cri-api/pkg/apis/runtime/v1alpha2/api.pb.go)
តើ kubectl exec αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž™αŸ‰αžΆαž„αžŠαžΌαž…αž˜αŸ’αžαŸαž…?

αž”αžΎαž’αž‰αŸ’αž…αžΉαž„ αž™αžΎαž„αž‚αž½αžšαžαŸ‚αžƒαžΎαž‰αž€αžΆαžšαžαž—αŸ’αž‡αžΆαž”αŸ‹αžšαžœαžΆαž„ kubelet αž“αž·αž„αž€αž»αž„αžαžΊαž“αŸαžš runtime αž˜αŸ‚αž“αž‘αŸ? αžŸαžΌαž˜αž–αž·αž“αž·αžαŸ’αž™αž˜αžΎαž›αŸ”

αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž–αžΆαž€αŸ’αž™αž”αž‰αŸ’αž‡αžΆαž“αŸαŸ‡αž˜αž»αž“ αž“αž·αž„αž€αŸ’αžšαŸ„αž™αž–αžΆαž€αŸ’αž™αž”αž‰αŸ’αž‡αžΆ 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 daemon (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
}

(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 αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž™αŸ‰αžΆαž„αžŠαžΌαž…αž˜αŸ’αžαŸαž…?

апоминания

  • API Server αž€αŸαž’αžΆαž…αž…αžΆαž”αŸ‹αž•αŸ’αžαžΎαž˜αž€αžΆαžšαžαž—αŸ’αž‡αžΆαž”αŸ‹αž‘αŸ… kubelet αž•αž„αžŠαŸ‚αžšαŸ”
  • αž€αžΆαžšαžαž—αŸ’αž‡αžΆαž”αŸ‹αžαžΆαž„αž€αŸ’αžšαŸ„αž˜αž“αŸ…αžαŸ‚αž”αž“αŸ’αžαžšαž αžΌαžαžŠαž›αŸ‹αžœαž‚αŸ’αž‚ exec αž’αž“αŸ’αžαžšαž€αž˜αŸ’αž˜αž”αž‰αŸ’αž…αž”αŸ‹αŸ–
    • αžšαžœαžΆαž„ kubectl αž“αž·αž„ api-server;
    • αžšαžœαžΆαž„ api-server αž“αž·αž„ kubectl;
    • αžšαžœαžΆαž„ kubelet αž“αž·αž„αž€αž»αž„αžαžΊαž“αŸαžšαžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαŸ”
  • Kubectl ឬ api-server αž˜αž·αž“αž’αžΆαž…αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž’αŸ’αžœαžΈαž“αŸ…αž›αžΎαžαŸ’αž“αžΆαŸ†αž„αž€αž˜αŸ’αž˜αž€αžšαž”αžΆαž“αž‘αŸαŸ” Kubelet αž’αžΆαž…αžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž”αžΆαž“ αž”αŸ‰αž»αž“αŸ’αžαŸ‚αžœαžΆαž€αŸαž˜αžΆαž“αž’αž“αŸ’αžαžšαž€αž˜αŸ’αž˜αž‡αžΆαž˜αž½αž™αž“αžΉαž„αž€αž»αž„αžαžΊαž“αŸαžšαžŠαŸ†αžŽαžΎαžšαž€αžΆαžšαž•αž„αžŠαŸ‚αžš αžŠαžΎαž˜αŸ’αž”αžΈαž’αŸ’αžœαžΎαž€αž·αž…αŸ’αž…αž€αžΆαžšαž‘αžΆαŸ†αž„αž“αŸ„αŸ‡αŸ”

αž’αž“αž’αžΆαž“

PS αž–αžΈαž’αŸ’αž“αž€αž”αž€αž”αŸ’αžšαŸ‚

αžŸαžΌαž˜αž’αžΆαž“αž•αž„αžŠαŸ‚αžšαž“αŸ…αž›αžΎαž”αŸ’αž›αž€αŸ‹αžšαž”αžŸαŸ‹αž™αžΎαž„αŸ–

αž”αŸ’αžšαž—αž–: www.habr.com

αž”αž“αŸ’αžαŸ‚αž˜αž˜αžαž·αž™αŸ„αž”αž›αŸ‹