เดŽเด™เตเด™เดจเต†เดฏเดพเดฃเต kubectl exec เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจเดคเต?

เด•เตเดฑเดฟเดชเตเดชเต. เดตเดฟเดตเตผเดคเตเดคเดจเด‚.: เดฒเต‡เด–เดจเดคเตเดคเดฟเดจเตเดฑเต† เดฐเดšเดฏเดฟเดคเดพเดตเต - เดŽเดธเตเดŽเดชเดฟเดฏเดฟเตฝ เดจเดฟเดจเตเดจเตเดณเตเดณ เดŽเดžเตเดšเดฟเดจเต€เดฏเดฑเดพเดฏ เดŽเตผเด•เดพเตป เดŽเดฑเต‹เตพ - เดŸเต€เด‚ เดชเตเดฐเดตเตผเดคเตเดคเดจเดคเตเดคเดฟเดจเตเดฑเต† เดธเด‚เดตเดฟเดงเดพเดจเด™เตเด™เดณเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเตเดณเตเดณ เดคเดจเตเดฑเต† เดชเด เดจเด‚ เดชเด™เตเด•เดฟเดŸเตเดจเตเดจเต. kubectl exec, เด•เตเดฌเตผเดจเต†เดฑเตเดฑเดธเดฟเดจเตŠเดชเตเดชเด‚ เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจ เดŽเดฒเตเดฒเดพเดตเตผเด•เตเด•เตเด‚ เดชเดฐเดฟเดšเดฟเดคเดฎเดพเดฃเต. เด•เตเดฌเต†เตผเดจเต†เดฑเตเดฑเดธเต เดธเต‹เดดเตเดธเต เด•เต‹เดกเดฟเดจเตเดฑเต† (เด…เดจเตเดฌเดจเตเดง เดชเตเดฐเต‹เดœเด•เตเดฑเตเดฑเตเด•เดณเตเดŸเต†) เดฒเดฟเดธเตเดฑเตเดฑเดฟเด‚เด—เตเด•เตพเด•เตเด•เตŠเดชเตเดชเด‚ เดฎเตเดดเตเดตเตป เด…เตฝเด—เต‹เดฐเดฟเดคเดตเตเด‚ เด…เดฆเตเดฆเต‡เดนเด‚ เด…เดจเตเด—เดฎเดฟเด•เตเด•เตเดจเตเดจเต, เด…เดคเต เดตเดฟเดทเดฏเด‚ เด†เดตเดถเตเดฏเดฎเตเดณเตเดณเดคเตเดฐ เด†เดดเดคเตเดคเดฟเตฝ เดฎเดจเดธเตเดธเดฟเดฒเดพเด•เตเด•เดพเตป เดจเดฟเด™เตเด™เดณเต† เด…เดจเตเดตเดฆเดฟเด•เตเด•เตเดจเตเดจเต.

เดŽเด™เตเด™เดจเต†เดฏเดพเดฃเต kubectl exec เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจเดคเต?

เด’เดฐเต เดตเต†เดณเตเดณเดฟเดฏเดพเดดเตเดš, เด’เดฐเต เดธเดนเดชเตเดฐเดตเตผเดคเตเดคเด•เตป เดŽเดจเตเดฑเต† เด…เดŸเตเดคเตเดคเต เดตเดจเตเดจเต เด’เดฐเต เดชเต‹เดกเดฟเตฝ เด’เดฐเต เด•เดฎเดพเตปเดกเต เดŽเด™เตเด™เดจเต† เดŽเด•เตเดธเดฟเด•เตเดฏเต‚เดŸเตเดŸเต เดšเต†เดฏเตเดฏเดพเดฎเต†เดจเตเดจเต เดšเต‹เดฆเดฟเดšเตเดšเต เด•เตเดฒเดฏเดจเตเดฑเต-เด—เต‹. เดŽเดจเดฟเด•เตเด•เต เด…เดฆเตเดฆเต‡เดนเดคเตเดคเดฟเดจเต เด‰เดคเตเดคเดฐเด‚ เดจเตฝเด•เดพเตป เด•เดดเดฟเดžเตเดžเดฟเดฒเตเดฒ, เดชเตเดฐเดตเตผเดคเตเดคเดจเดคเตเดคเดฟเดจเตเดฑเต† เดฎเต†เด•เตเด•เดพเดจเดฟเดธเดคเตเดคเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเต เดŽเดจเดฟเด•เตเด•เต เด’เดจเตเดจเตเด‚ เด…เดฑเดฟเดฏเดฟเดฒเตเดฒเต†เดจเตเดจเต เดชเต†เดŸเตเดŸเต†เดจเตเดจเต เดฎเดจเดธเตเดธเดฟเดฒเดพเดฏเดฟ kubectl exec. เด…เดคเต†, เด…เดคเดฟเดจเตเดฑเต† เด˜เดŸเดจเดฏเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเต เดŽเดจเดฟเด•เตเด•เต เดšเดฟเดฒ เด†เดถเดฏเด™เตเด™เตพ เด‰เดฃเตเดŸเดพเดฏเดฟเดฐเตเดจเตเดจเต, เดŽเดจเตเดจเดพเตฝ เด…เดตเดฏเตเดŸเต† เด•เตƒเดคเตเดฏเดคเดฏเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเต เดŽเดจเดฟเด•เตเด•เต 100% เด‰เดฑเดชเตเดชเดฟเดฒเตเดฒ, เด…เดคเดฟเดจเดพเตฝ เดˆ เดชเตเดฐเดถเตเดจเด‚ เดชเดฐเดฟเดนเดฐเดฟเด•เตเด•เดพเตป เดคเต€เดฐเตเดฎเดพเดจเดฟเดšเตเดšเต. เดฌเตเดฒเต‹เด—เตเด•เตพ, เดกเต‹เด•เตเดฏเตเดฎเต†เดจเตเดฑเต‡เดทเตป, เดธเต‹เดดเตโ€Œเดธเต เด•เต‹เดกเต เดŽเดจเตเดจเดฟเดต เดชเด เดฟเดšเตเดšเดคเดฟเดจเดพเตฝ, เดžเดพเตป เดงเดพเดฐเดพเดณเด‚ เดชเตเดคเดฟเดฏ เด•เดพเดฐเตเดฏเด™เตเด™เตพ เดชเด เดฟเดšเตเดšเต, เดˆ เดฒเต‡เด–เดจเดคเตเดคเดฟเตฝ เดŽเดจเตเดฑเต† เด•เดฃเตเดŸเต†เดคเตเดคเดฒเตเด•เดณเตเด‚ เดงเดพเดฐเดฃเด•เดณเตเด‚ เดชเด™เตเด•เดฟเดŸเดพเตป เดžเดพเตป เด†เด—เตเดฐเดนเดฟเด•เตเด•เตเดจเตเดจเต. เดŽเดจเตเดคเต†เด™เตเด•เดฟเดฒเตเด‚ เดคเต†เดฑเตเดฑเตเดฃเตเดŸเต†เด™เตเด•เดฟเตฝ, เดฆเดฏเดตเดพเดฏเดฟ เดŽเดจเตเดจเต† เดฌเดจเตเดงเดชเตเดชเต†เดŸเตเด• เดŸเตเดตเดฟเดฑเตเดฑเตผ.

เดคเดฏเตเดฏเดพเดฑเดพเด•เตเด•เตฝ

เด’เดฐเต เดฎเดพเด•เตเดฌเตเด•เตเด•เดฟเตฝ เด’เดฐเต เด•เตเดฒเดธเตเดฑเตเดฑเตผ เดธเตƒเดทเตเดŸเดฟเด•เตเด•เดพเตป, เดžเดพเตป เด•เตเดฒเต‹เตบ เดšเต†เดฏเตเดคเต ecomm-integration-ballerina/kubernetes-cluster. เดธเตเดฅเดฟเดฐเดธเตเดฅเดฟเดคเดฟ เด•เตเดฐเดฎเต€เด•เดฐเดฃเด™เตเด™เตพ เด…เดจเตเดตเดฆเดฟเด•เตเด•เดพเดคเตเดคเดคเดฟเดจเดพเตฝ, เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเต เด•เต‹เตบเดซเดฟเด—เดฑเดฟเดฒเต† เดจเต‹เดกเตเด•เดณเตเดŸเต† เดเดชเดฟ เดตเดฟเดฒเดพเดธเด™เตเด™เตพ เดžเดพเตป เดถเดฐเดฟเดฏเดพเด•เตเด•เดฟ kubectl exec. เด‡เดคเดฟเดจเตเดฑเต† เดชเตเดฐเดงเดพเดจ เด•เดพเดฐเดฃเดคเตเดคเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเต เดจเดฟเด™เตเด™เตพเด•เตเด•เต เด•เต‚เดŸเตเดคเตฝ เดตเดพเดฏเดฟเด•เตเด•เดพเด‚ เด‡เดตเดฟเดŸเต†.

  • เดเดคเต†เด™เตเด•เดฟเดฒเตเด‚ เด•เดพเตผ = เดŽเดจเตเดฑเต† เดฎเดพเด•เตเดฌเตเด•เตเด•เต
  • เดฎเดพเดธเตเดฑเตเดฑเตผ เดจเต‹เดกเต IP = 192.168.205.10
  • เดตเตผเด•เตเด•เตผ เดจเต‹เดกเต IP = 192.168.205.11
  • API เดธเต†เตผเดตเตผ เดชเต‹เตผเดŸเตเดŸเต = 6443

เด˜เดŸเด•เด™เตเด™เตพ

เดŽเด™เตเด™เดจเต†เดฏเดพเดฃเต kubectl exec เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจเดคเต?

  • kubectl เดŽเด•เตเดธเดฟเด•เตเดฏเต‚เดŸเตเดŸเต€เดตเต เดชเตเดฐเด•เตเดฐเดฟเดฏ: เดจเดฎเตเดฎเตพ "kubectl exec..." เดŽเด•เตเดธเดฟเด•เตเดฏเต‚เดŸเตเดŸเต เดšเต†เดฏเตเดฏเตเดฎเตเดชเต‹เตพ เดชเตเดฐเด•เตเดฐเดฟเดฏ เด†เดฐเด‚เดญเดฟเด•เตเด•เตเดจเตเดจเต. K8s API เดธเต†เตผเดตเดฑเดฟเดฒเต‡เด•เตเด•เตเดณเตเดณ เด†เด•เตโ€Œเดธเดธเต เด‰เดณเตเดณ เดเดคเต เดฎเต†เดทเต€เดจเดฟเดฒเตเด‚ เด‡เดคเต เดšเต†เดฏเตเดฏเดพเตป เด•เดดเดฟเดฏเตเด‚. เด•เตเดฑเดฟเดชเตเดชเต transl.: เด•เต‚เดŸเตเดคเตฝ เด•เตบเดธเต‹เตพ เดฒเดฟเดธเตเดฑเตเดฑเดฟเด‚เด—เตเด•เดณเดฟเตฝ, เดฐเดšเดฏเดฟเดคเดพเดตเต "เดเดคเต เดฎเต†เดทเต€เดจเตเด‚" เดŽเดจเตเดจ เด•เดฎเดจเตเดฑเต เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เตเดจเตเดจเต, เด•เตเดฌเต†เตผเดจเต†เดฑเตเดฑเดธเดฟเดฒเต‡เด•เตเด•เตเดณเตเดณ เด†เด•เตโ€Œเดธเดธเต เด‰เดณเตเดณ เด…เดคเตเดคเดฐเด‚ เดเดคเต เดฎเต†เดทเต€เดจเตเด•เดณเดฟเดฒเตเด‚ เดคเตเดŸเตผเดจเตเดจเตเดณเตเดณ เด•เดฎเดพเตปเดกเตเด•เตพ เดŽเด•เตโ€Œเดธเดฟเด•เตเดฏเต‚เดŸเตเดŸเต เดšเต†เดฏเตเดฏเดพเตป เด•เดดเดฟเดฏเตเดฎเต†เดจเตเดจเต เดธเต‚เดšเดฟเดชเตเดชเดฟเด•เตเด•เตเดจเตเดจเต.
  • api เดธเต†เตผเดตเตผ: Kubernetes API-เดฒเต‡เด•เตเด•เต เด†เด•เตเดธเดธเต เดจเตฝเด•เตเดจเตเดจ เดฎเดพเดธเตเดฑเตเดฑเตผ เดจเต‹เดกเดฟเดฒเต† เด’เดฐเต เด˜เดŸเด•เด‚. เด•เตเดฌเตผเดจเต†เดฑเตเดฑเดธเดฟเดฒเต† เดจเดฟเดฏเดจเตเดคเตเดฐเดฃ เดตเดฟเดฎเดพเดจเดคเตเดคเดฟเดจเตเดฑเต† เดฎเตเตปเดญเดพเด—เดฎเดพเดฃเดฟเดคเต.
  • เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเต: เด•เตเดฒเดธเตเดฑเตเดฑเดฑเดฟเดฒเต† เดŽเดฒเตเดฒเดพ เดจเต‹เดกเดฟเดฒเตเด‚ เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจ เด’เดฐเต เดเดœเดจเตเดฑเต. เด‡เดคเต เดชเต‹เดกเดฟเดฒเต† เดชเดพเดคเตเดฐเด™เตเด™เดณเตเดŸเต† เดชเตเดฐเดตเตผเดคเตเดคเดจเด‚ เด‰เดฑเดชเตเดชเดพเด•เตเด•เตเดจเตเดจเต.
  • เด•เดฃเตเดŸเต†เดฏเตเดจเตผ เดฑเตบเดŸเตˆเด‚ (เด•เดฃเตเดŸเต†เดฏเตโ€Œเดจเตผ เดฑเตบเดŸเตˆเด‚): เด•เดฃเตเดŸเต†เดฏเตโ€Œเดจเดฑเตเด•เตพ เดชเตเดฐเดตเตผเดคเตเดคเดฟเดชเตเดชเดฟเด•เตเด•เตเดจเตเดจเดคเดฟเดจเต เด‰เดคเตเดคเดฐเดตเดพเดฆเดฟเดคเตเดคเดฎเตเดณเตเดณ เดธเต‹เดซเตเดฑเตเดฑเตโ€Œเดตเต†เดฏเตผ. เด‰เดฆเดพเดนเดฐเดฃเด™เตเด™เตพ: เดกเต‹เด•เตเด•เตผ, CRI-O, เด•เดฃเตเดŸเต†เดฏเตโ€Œเดจเตผเดกเต...
  • เด•เต†เตผเดฃเตฝ: เดตเตผเด•เตเด•เตผ เดจเต‹เดกเดฟเดฒเต† 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-เดธเต†เตผเดตเดฑเดฟเดฒเต‡เด•เตเด•เต เด•เดฃเด•เตเดทเดจเตเด•เตพ เด‰เดฃเตเดŸเต†เดจเตเดจเต เดžเด™เตเด™เตพ เด•เดฃเตเดŸเต†เดคเตเดคเตเด‚ (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 exec เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจเดคเต?

2. เดฎเดพเดธเตเดฑเตเดฑเตผ เดจเต‹เดกเต เดตเดถเดคเตเดคเต† เดชเตเดฐเดตเตผเดคเตเดคเดจเด‚

เดŽเดชเดฟเด-เดธเต†เตผเดตเตผ เดตเดถเดคเตเดคเตเด‚ เดžเด™เตเด™เตพเด•เตเด•เต เด…เดญเตเดฏเตผเดคเตเดฅเดจ เดจเดฟเดฐเต€เด•เตเดทเดฟเด•เตเด•เดพเตป เด•เดดเดฟเดฏเตเด‚:

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

HTTP เด…เดญเตเดฏเตผเดคเตเดฅเดจเดฏเดฟเตฝ เดชเตเดฐเต‹เดŸเตเดŸเต‹เด•เตเด•เต‹เตพ เดฎเดพเดฑเตเดฑเดพเดจเตเดณเตเดณ เด…เดญเตเดฏเตผเดคเตเดฅเดจ เด‰เตพเดชเตเดชเต†เดŸเตเดจเตเดจเต เดŽเดจเตเดจเดคเต เดถเตเดฐเดฆเตเดงเดฟเด•เตเด•เตเด•. เดŽเดธเต.เดชเดฟ.เดกเดฟ.เดตเตˆ เด’เดฐเตŠเดฑเตเดฑ TCP เด•เดฃเด•เตเดทเดจเดฟเดฒเต‚เดŸเต† เดตเตเดฏเด•เตเดคเดฟเด—เดค stdin/stdout/stderr/spdy-error "เดธเตเดŸเตเดฐเต€เดฎเตเด•เตพ" เดฎเตพเดŸเตเดŸเดฟเดชเตเดฒเด•เตเดธเต เดšเต†เดฏเตเดฏเดพเตป เดจเดฟเด™เตเด™เดณเต† เด…เดจเตเดตเดฆเดฟเด•เตเด•เตเดจเตเดจเต.

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-เดธเต†เตผเดตเตผ เด…เดฑเดฟเดžเตเดžเดฟเดฐเดฟเด•เตเด•เดฃเด‚:

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

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

เดคเต€เตผเดšเตเดšเดฏเดพเดฏเตเด‚, เดŽเตปเดกเต เดชเต‹เดฏเดฟเดจเตเดฑเดฟเดจเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเตเดณเตเดณ เดกเดพเดฑเตเดฑ เดจเต‹เดกเดฟเดจเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเตเดณเตเดณ เดตเดฟเดตเดฐเด™เตเด™เดณเดฟเตฝ เดจเดฟเดจเตเดจเดพเดฃเต เดŽเดŸเตเดคเตเดคเดคเต:

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

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

เดนเต‚เดฑเต‡! เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเดฟเดจเต เด‡เดชเตเดชเต‹เตพ เด’เดฐเต เดคเตเดฑเดฎเตเด–เดฎเตเดฃเตเดŸเต (node.Status.DaemonEndpoints.KubeletEndpoint.Port), เด‡เดคเดฟเดฒเต‡เด•เตเด•เต API เดธเต†เตผเดตเดฑเดฟเดจเต เดฌเดจเตเดงเดฟเดชเตเดชเดฟเด•เตเด•เดพเตป เด•เดดเดฟเดฏเตเด‚:

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

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

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

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

(pkg/kubelet/client/kubelet_client.go)

เดกเต‹เด•เตเดฏเตเดฎเต†เดจเตเดฑเต‡เดทเดจเดฟเตฝ เดจเดฟเดจเตเดจเต เดฎเดพเดธเตเดฑเตเดฑเตผ-เดจเต‹เดกเต เด•เดฎเตเดฎเตเดฏเต‚เดฃเดฟเด•เตเด•เต‡เดทเตป > เดฎเดพเดธเตเดฑเตเดฑเตผ เดŸเต เด•เตเดฒเดธเตเดฑเตเดฑเตผ > apiserver to kubelet:

เดˆ เด•เดฃเด•เตเดทเดจเตเด•เตพ เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเดฟเดจเตเดฑเต† HTTPS เดŽเตปเดกเตโ€Œเดชเต‹เดฏเดฟเดจเตเดฑเดฟเดฒเต‡เด•เตเด•เดพเดฃเต เดจเดฟเตผเดฎเตเดฎเดฟเดšเตเดšเดฟเดฐเดฟเด•เตเด•เตเดจเตเดจเดคเต. เดกเดฟเดซเต‹เตพเดŸเตเดŸเดพเดฏเดฟ, apiserver kubelet-เดจเตเดฑเต† เดธเตผเดŸเตเดŸเดฟเดซเดฟเด•เตเด•เดฑเตเดฑเต เดชเดฐเดฟเดถเต‹เดงเดฟเดšเตเดšเตเดฑเดชเตเดชเดฟเด•เตเด•เตเดจเตเดจเดฟเดฒเตเดฒ, เด‡เดคเต เด•เดฃเด•เตเดทเดจเต† เดฎเดพเตป-เด‡เตป-เดฆเดฟ-เดฎเดฟเดกเดฟเตฝ (MITM) เด†เด•เตเดฐเดฎเดฃเด™เตเด™เตพเด•เตเด•เต เดตเดฟเดงเต‡เดฏเดฎเดพเด•เตเด•เตเดจเตเดจเต. เดธเตเดฐเด•เตเดทเดฟเดคเดฎเดฒเตเดฒเดพเดคเตเดค เดตเดฟเดถเตเดตเดธเดจเต€เดฏเดฎเดฒเตเดฒเดพเดคเตเดค เด•เต‚เดŸเดพเดคเต†/เด…เดฒเตเดฒเต†เด™เตเด•เดฟเตฝ เดชเตŠเดคเต เดจเต†เดฑเตเดฑเตโ€Œเดตเตผเด•เตเด•เตเด•เดณเดฟเตฝ เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจเดคเดฟเดจเต.

เด‡เดชเตเดชเต‹เตพ API เดธเต†เตผเดตเดฑเดฟเดจเต เดŽเตปเดกเตโ€Œเดชเต‹เดฏเดฟเดจเตเดฑเต เด…เดฑเดฟเดฏเตเด•เดฏเตเด‚ เด•เดฃเด•เตเดทเตป เดธเตเดฅเดพเดชเดฟเด•เตเด•เตเด•เดฏเตเด‚ เดšเต†เดฏเตเดฏเตเดจเตเดจเต:

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

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

เดฎเดพเดธเตเดฑเตเดฑเตผ เดจเต‹เดกเดฟเตฝ เดŽเดจเตเดคเดพเดฃเต เดธเด‚เดญเดตเดฟเด•เตเด•เตเดจเตเดจเดคเต†เดจเตเดจเต เดจเต‹เด•เตเด•เดพเด‚.

เด†เดฆเตเดฏเด‚, เดตเตผเด•เตเด•เตผ เดจเต‹เดกเดฟเดจเตเดฑเต† เดเดชเดฟ เดžเด™เตเด™เตพ เด•เดฃเตเดŸเต†เดคเตเดคเตเดจเตเดจเต. เดžเด™เตเด™เดณเตเดŸเต† เด•เดพเดฐเตเดฏเดคเตเดคเดฟเตฝ เด‡เดคเต 192.168.205.11 เด†เดฃเต:

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

เดคเตเดŸเตผเดจเตเดจเต เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเต เดชเต‹เตผเดŸเตเดŸเต เดธเดœเตเดœเดฎเดพเด•เตเด•เตเด• (เดžเด™เตเด™เดณเตเดŸเต† เด•เดพเดฐเตเดฏเดคเตเดคเดฟเตฝ 10250):

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

เด‡เดชเตเดชเต‹เตพ เดจเต†เดฑเตเดฑเตโ€Œเดตเตผเด•เตเด•เต เดชเดฐเดฟเดถเต‹เดงเดฟเด•เตเด•เดพเดจเตเดณเตเดณ เดธเดฎเดฏเดฎเดพเดฃเต. เดตเตผเด•เตเด•เตผ เดจเต‹เดกเตเดฎเดพเดฏเดฟ (192.168.205.11) เด•เดฃเด•เตเดทเดจเตเดฃเตเดŸเต‹? เด…เดคเต! เดจเดฟเด™เตเด™เตพ เด’เดฐเต เดชเตเดฐเด•เตเดฐเดฟเดฏเดฏเต† เด•เตŠเดฒเตเดฒเตเด•เดฏเดพเดฃเต†เด™เตเด•เดฟเตฝ exec, เด…เดคเต เด…เดชเตเดฐเดคเตเดฏเด•เตเดทเดฎเดพเด•เตเด‚, เด…เดคเดฟเดจเดพเตฝ เดŽเด•เตเดธเดฟเด•เตเดฏเต‚เดŸเตเดŸเต เดšเต†เดฏเตเดค เดŽเด•เตเดธเดฟเด•เตเดฏเต‚เดŸเตเดŸเต เด•เดฎเดพเตปเดกเดฟเดจเตเดฑเต† เด…เดจเดจเตเดคเดฐเดซเดฒเดฎเดพเดฏเดฟ api-เดธเต†เตผเดตเตผ เด†เดฃเต เด•เดฃเด•เตเดทเตป เดธเตเดฅเดพเดชเดฟเดšเตเดšเดคเต†เดจเตเดจเต เดŽเดจเดฟเด•เตเด•เดฑเดฟเดฏเดพเด‚.

// 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
  โ€ฆ

เดŽเดจเตเดจเดพเตฝ เด•เดพเดคเตเดคเดฟเดฐเดฟเด•เตเด•เตเด•: เดŽเด™เตเด™เดจเต†เดฏเดพเดฃเต เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเต เด‡เดคเต เดชเตเดฑเดคเตเดคเต†เดŸเตเดคเตเดคเดคเต? api-เดธเต†เตผเดตเตผ เด…เดญเตเดฏเตผเดคเตเดฅเดจเด•เตพเด•เตเด•เดพเดฏเดฟ เดชเต‹เตผเดŸเตเดŸเต เดตเดดเดฟ API-เดฒเต‡เด•เตเด•เต เด†เด•เตเดธเดธเต เดจเตฝเด•เตเดจเตเดจ เด’เดฐเต เดกเต†เดฎเตบ kubelet-เดจเตเดฃเตเดŸเต:

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

เดŽเด•เตโ€Œเดธเดฟเด•เต เด…เดญเตเดฏเตผเดคเตเดฅเดจเด•เตพเด•เตเด•เตเดณเตเดณ เดชเตเดฐเดคเดฟเด•เดฐเดฃ เดŽเตปเดกเตโ€Œเดชเต‹เดฏเดฟเดจเตเดฑเต เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเต เด•เดฃเด•เตเด•เดพเด•เตเด•เตเดจเตเดจเต:

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

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

เด†เดถเดฏเด•เตเด•เตเดดเดชเตเดชเดคเตเดคเดฟเดฒเดพเด•เดฐเตเดคเต. เด‡เดคเต เด•เดฎเดพเตปเดกเดฟเดจเตเดฑเต† เดซเดฒเด‚ เดจเตฝเด•เตเดจเตเดจเดฟเดฒเตเดฒ, เด†เดถเดฏเดตเดฟเดจเดฟเดฎเดฏเดคเตเดคเดฟเดจเตเดณเตเดณ เด…เดตเดธเดพเดจ เดชเต‹เดฏเดฟเดจเตเดฑเดพเดฃเต:

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

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

เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเต เด‡เดจเตเดฑเตผเดซเต‡เดธเต เดจเดŸเดชเตเดชเดฟเดฒเดพเด•เตเด•เตเดจเตเดจเต RuntimeServiceClient, เด‡เดคเต เด•เดฃเตเดŸเต†เดฏเตเดจเตผ เดฑเตบเดŸเตˆเด‚ เด‡เดจเตเดฑเตผเดซเต‡เดธเดฟเดจเตเดฑเต† เดญเดพเด—เดฎเดพเดฃเต (เดžเด™เตเด™เตพ เด…เดคเดฟเดจเต†เด•เตเด•เตเดฑเดฟเดšเตเดšเต เด•เต‚เดŸเตเดคเตฝ เดŽเดดเตเดคเดฟ, เด‰เดฆเดพเดนเดฐเดฃเดคเตเดคเดฟเดจเต, เด‡เดตเดฟเดŸเต† - เดเด•เดฆเต‡เดถเด‚. เดชเดฐเดฟเดญเดพเดท.):

kubernetes/kubernetes-เดฒเต† cri-api-เตฝ เดจเดฟเดจเตเดจเตเดณเตเดณ เดจเต€เดฃเตเดŸ เดฒเดฟเดธเตโ€Œเดฑเตเดฑเดฟเด‚เด—เต

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

(cri-api/pkg/apis/runtime/v1alpha2/api.pb.go)
เด•เดฃเตเดŸเต†เดฏเตเดจเตผ เดฑเตบเดŸเตˆเด‚ เด‡เดจเตเดฑเตผเดซเต‡เดธเต เดตเดดเดฟ เด’เดฐเต เดฐเต€เดคเดฟ เดตเดฟเดณเดฟเด•เตเด•เดพเตป เด‡เดคเต gRPC เด‰เดชเดฏเต‹เด—เดฟเด•เตเด•เตเดจเตเดจเต:

type runtimeServiceClient struct {
        cc *grpc.ClientConn
}

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

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

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

เด•เดฃเตเดŸเต†เดฏเตเดจเตผ เดฑเตบเดŸเตˆเด‚ เดจเดŸเดชเตเดชเดฟเดฒเดพเด•เตเด•เตเดจเตเดจเดคเดฟเดจเตเดณเตเดณ เด‰เดคเตเดคเดฐเดตเดพเดฆเดฟเดคเตเดคเดฎเดพเดฃเต RuntimeServiceServer:

kubernetes/kubernetes-เดฒเต† cri-api-เตฝ เดจเดฟเดจเตเดจเตเดณเตเดณ เดจเต€เดฃเตเดŸ เดฒเดฟเดธเตโ€Œเดฑเตเดฑเดฟเด‚เด—เต

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

(cri-api/pkg/apis/runtime/v1alpha2/api.pb.go)
เดŽเด™เตเด™เดจเต†เดฏเดพเดฃเต kubectl exec เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจเดคเต?

เด…เด™เตเด™เดจเต†เดฏเดพเดฃเต†เด™เตเด•เดฟเตฝ, เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเตเด‚ เด•เดฃเตเดŸเต†เดฏเตโ€Œเดจเตผ เดฑเตบเดŸเตˆเดฎเตเด‚ เดคเดฎเตเดฎเดฟเตฝ เด’เดฐเต เด•เดฃเด•เตเดทเตป เดจเดฎเตเดฎเตพ เด•เดพเดฃเดฃเด‚, เด…เดฒเตเดฒเต‡? เดจเดฎเตเด•เตเด•เต เดชเดฐเดฟเดถเต‹เดงเดฟเด•เตเด•เดพเด‚.

exec เด•เดฎเดพเตปเดกเดฟเดจเต เดฎเตเดฎเตเดชเตเด‚ เดถเต‡เดทเดตเตเด‚ เดˆ เด•เดฎเดพเตปเดกเต เดชเตเดฐเดตเตผเดคเตเดคเดฟเดชเตเดชเดฟเดšเตเดšเต เดตเตเดฏเดคเตเดฏเดพเดธเด™เตเด™เตพ เดจเต‹เด•เตเด•เตเด•. เดŽเดจเตเดฑเต† เด•เดพเดฐเตเดฏเดคเตเดคเดฟเตฝ, เดตเตเดฏเดคเตเดฏเดพเดธเด‚ เด‡เดคเดพเดฃเต:

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

เดนเตเด‚... เด’เดฐเต เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเดฟเดจเตเด‚ (pid=5714) เด…เดœเตเดžเดพเดคเดฎเดพเดฏ เดšเดฟเดฒเดคเดฟเดจเตเด‚ เด‡เดŸเดฏเดฟเตฝ unix เดธเต‹เด•เตเด•เดฑเตเดฑเตเด•เตพ เดตเดดเดฟเดฏเตเดณเตเดณ เด’เดฐเต เดชเตเดคเดฟเดฏ เด•เดฃเด•เตเดทเตป. เด…เดคเต เดŽเดจเตเดคเดพเดฏเดฟเดฐเดฟเด•เตเด•เดพเด‚? เด…เดคเต เดถเดฐเดฟเดฏเดพเดฃเต, เด‡เดคเต เดกเต‹เด•เตเด•เดฑเดพเดฃเต (pid=1186)!

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

เดจเดฟเด™เตเด™เตพ เด“เตผเด•เตเด•เตเดจเตเดจเดคเตเดชเต‹เดฒเต†, เด‡เดคเดพเดฃเต เดกเต‹เด•เตเด•เตผ เดกเต†เดฎเตบ เดชเตเดฐเต‹เดธเดธเตเดธเต (pid=1186) เด…เดคเต เดžเด™เตเด™เดณเตเดŸเต† เด•เดฎเดพเตปเดกเต เดŽเด•เตเดธเดฟเด•เตเดฏเต‚เดŸเตเดŸเต เดšเต†เดฏเตเดฏเตเดจเตเดจเต:

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

4. เด•เดฃเตเดŸเต†เดฏเตเดจเตผ เดฑเตบเดŸเตˆเดฎเดฟเดฒเต† เดชเตเดฐเดตเตผเดคเตเดคเดจเด‚

เดŽเดจเตเดคเดพเดฃเต เดธเด‚เดญเดตเดฟเด•เตเด•เตเดจเตเดจเดคเต†เดจเตเดจเต เดฎเดจเดธเดฟเดฒเดพเด•เตเด•เดพเตป เดจเดฎเตเด•เตเด•เต CRI-O เดธเต‹เดดเตเดธเต เด•เต‹เดกเต เดชเดฐเดฟเดถเต‹เดงเดฟเด•เตเด•เดพเด‚. เดกเต‹เด•เตเด•เดฑเดฟเตฝ เดฏเตเด•เตเดคเดฟ เดธเดฎเดพเดจเดฎเดพเดฃเต.

เดจเดŸเดชเตเดชเดฟเดฒเดพเด•เตเด•เตเดจเตเดจเดคเดฟเดจเต เด’เดฐเต เดธเต†เตผเดตเดฑเตเดฃเตเดŸเต RuntimeServiceServer:

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

        appArmorProfile string
        hostIP          string
        bindAddress     string

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

        updateLock sync.RWMutex

        seccompEnabled  bool
        appArmorEnabled bool
}

(cri-o/server/server.go)

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

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

        return resp, nil
}

(cri-o/erver/container_exec.go)

เดถเตƒเด‚เด–เดฒเดฏเตเดŸเต† เด…เดตเดธเดพเดจเด‚, เด•เดฃเตเดŸเต†เดฏเตเดจเตผ เดฑเตบเดŸเตˆเด‚ เดตเตผเด•เตเด•เตผ เดจเต‹เดกเดฟเดฒเต† เด•เดฎเดพเตปเดกเต เดจเดŸเดชเตเดชเดฟเดฒเดพเด•เตเด•เตเดจเตเดจเต:

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

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

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

                cmdErr = execCmd.Run()
        }

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

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

เดŽเด™เตเด™เดจเต†เดฏเดพเดฃเต kubectl exec เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจเดคเต?

เด…เดตเดธเดพเดจเดฎเดพเดฏเดฟ, เด•เต‡เตผเดฃเตฝ เด•เดฎเดพเตปเดกเตเด•เตพ เดจเดŸเดชเตเดชเดฟเดฒเดพเด•เตเด•เตเดจเตเดจเต:

เดŽเด™เตเด™เดจเต†เดฏเดพเดฃเต kubectl exec เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เตเดจเตเดจเดคเต?

ะœะธะฝะฐะฝะธัะพะผะธะฝะฐะฝะธั

  • API เดธเต†เตผเดตเดฑเดฟเดจเต kubelet-เดฒเต‡เด•เตเด•เต เด’เดฐเต เด•เดฃเด•เตเดทเตป เด†เดฐเด‚เดญเดฟเด•เตเด•เดพเดจเตเด‚ เด•เดดเดฟเดฏเตเด‚.
  • เดธเด‚เดตเต‡เดฆเดจเดพเดคเตเดฎเด• เดŽเด•เตเดธเดฟเด•เตเดฏเต‚เดŸเตเดŸเต€เดตเต เดธเต†เดทเตป เด…เดตเดธเดพเดจเดฟเด•เตเด•เตเดจเตเดจเดคเต เดตเดฐเต† เด‡เดจเดฟเดชเตเดชเดฑเดฏเตเดจเตเดจ เด•เดฃเด•เตเดทเดจเตเด•เตพ เดจเดฟเดฒเดจเดฟเตฝเด•เตเด•เตเด‚:
    • kubectl-เดจเตเด‚ api-server-เดจเตเด‚ เด‡เดŸเดฏเดฟเตฝ;
    • api-server-เดจเตเด‚ kubectl-เดจเตเด‚ เด‡เดŸเดฏเดฟเตฝ;
    • เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเดฟเดจเตเด‚ เด•เดฃเตเดŸเต†เดฏเตโ€Œเดจเตผ เดฑเตบเดŸเตˆเดฎเดฟเดจเตเด‚ เด‡เดŸเดฏเดฟเตฝ.
  • Kubectl เด…เดฒเตเดฒเต†เด™เตเด•เดฟเตฝ api-server เดตเตผเด•เตเด•เตผ เดจเต‹เดกเตเด•เดณเดฟเตฝ เด’เดจเตเดจเตเด‚ เดชเตเดฐเดตเตผเดคเตเดคเดฟเดชเตเดชเดฟเด•เตเด•เดพเตป เด•เดดเดฟเดฏเดฟเดฒเตเดฒ. เด•เตเดฌเต†เดฒเต†เดฑเตเดฑเดฟเดจเต เดชเตเดฐเดตเตผเดคเตเดคเดฟเด•เตเด•เดพเตป เด•เดดเดฟเดฏเตเด‚, เดŽเดจเตเดจเดพเตฝ เด† เด•เดพเดฐเตเดฏเด™เตเด™เตพ เดšเต†เดฏเตเดฏเดพเตป เด…เดคเต เด•เดฃเตเดŸเต†เดฏเตโ€Œเดจเตผ เดฑเตบเดŸเตˆเดฎเตเดฎเดพเดฏเดฟ เดธเด‚เดตเดฆเดฟเด•เตเด•เตเด•เดฏเตเด‚ เดšเต†เดฏเตเดฏเตเดจเตเดจเต.

เดตเดฟเดญเดตเด™เตเด™เตพ

เดตเดฟเดตเตผเดคเตเดคเด•เดจเดฟเตฝ เดจเดฟเดจเตเดจเต เดชเดฟ.เดŽเดธเต

เดžเด™เตเด™เดณเตเดŸเต† เดฌเตเดฒเต‹เด—เดฟเดฒเตเด‚ เดตเดพเดฏเดฟเด•เตเด•เตเด•:

เด…เดตเดฒเด‚เดฌเด‚: www.habr.com

เด’เดฐเต เด…เดญเดฟเดชเตเดฐเดพเดฏเด‚ เดšเต‡เตผเด•เตเด•เตเด•