1. 如何在 Kubernetes 上調(diào)試 distroless 容器

        共 8304字,需瀏覽 17分鐘

         ·

        2021-12-05 21:14

        本文主要包括如下內(nèi)容:

        ?介紹 distroless 鏡像、作用以及簡單的使用?如何針對 distroless 容器的進(jìn)行調(diào)試?臨時容器(v.1.18+)的使用

        Distroless 鏡像

        Distroless 容器,顧名思義使用?Distroless 鏡像[1]作為基礎(chǔ)鏡像運(yùn)行的容器。

        "Distroless" 鏡像只包含了你的應(yīng)用程序以及其運(yùn)行時所需要的依賴。不包含你能在標(biāo)準(zhǔn) Linxu 發(fā)行版里的可以找到的包管理器、shells 或者其他程序。

        GoogleContainerTools/distroless[2]?針對不同語言提供了 distroless 鏡像:

        ?gcr.io/distroless/static-debian11[3]?gcr.io/distroless/base-debian11[4]?gcr.io/distroless/java-debian11[5]?gcr.io/distroless/cc-debian11[6]?gcr.io/distroless/nodejs-debian11[7]?gcr.io/distroless/python3-debian11[8]

        Distroless 鏡像有什么用?

        那些可能是構(gòu)建鏡像時需要的,但大部分并不是運(yùn)行時需要的。這也是為什么上篇文章介紹 Buildpacks?時說的一個 builder 的 stack 鏡像包含構(gòu)建時基礎(chǔ)鏡像和運(yùn)行時基礎(chǔ)鏡像,這樣可以做到鏡像的最小化。

        其實(shí)控制體積并不是 distroless 鏡像的主要作用。將運(yùn)行時容器中的內(nèi)容限制為應(yīng)用程序所需的依賴,此外不應(yīng)該安裝任何東西。這種方式可能極大的提升容器的安全性,也是 distroless 鏡像的最重要作用。

        d24b30b58a06607aa489e66f1e0061da.webp

        這里并不會再深入探究 distroless 鏡像,而是如何調(diào)試 distroless 容器

        沒有了包管理器,鏡像構(gòu)建完成后就不能再使用類似?aptyum?的包管理工具;沒有了?shell,容器運(yùn)行后無法再進(jìn)入容器。

        “就像一個沒有任何門的房間,也無法安裝門?!?/span>?Distroless 鏡像在提升容器安全性的同時,也為調(diào)試增加了難度。

        使用 distroless 鏡像

        寫個很簡單的 golang 應(yīng)用:

        package mainimport("fmt""net/http")func defaultHandler(w http.ResponseWriter, r *http.Request){    fmt.Fprintf(w,"Hello world!")}func main(){    http.HandleFunc("/", defaultHandler)    http.ListenAndServe(":8080",nil)}

        比如使用?gcr.io/distroless/base-debian11?作為 golang 應(yīng)用的基礎(chǔ)鏡像:

        FROM golang:1.12as build-envWORKDIR /go/src/appCOPY ./go/src/appRUN go get-d -v ./...RUN go build -o /go/bin/appFROM gcr.io/distroless/base-debian11COPY --from=build-env /go/bin/app /CMD ["/app"]

        使用鏡像創(chuàng)建 deployment

        $ kubectl create deploy golang-distroless --image addozhang/golang-distroless-example:latest$ kubectl get poNAME                                READY   STATUS    RESTARTS   AGEgolang-distroless-784bb4875-srmmr   1/1Running03m2s

        嘗試進(jìn)入容器:

        $ kubectl exec-it golang-distroless-784bb4875-srmmr -- sherror:Internal error occurred: error executing command in container: failed to execin container: failed to start exec"b76e800eafa85d39f909f39fcee4a4ba9fc2f37d5f674aa6620690b8e2939203": OCI runtime exec failed:exec failed: container_linux.go:380: starting container process caused:exec:"sh": executable file not found in $PATH: unknown

        如何調(diào)試 Distroless 容器

        1. 使用 distroless debug 鏡像

        GoogleContainerTools 為每個 distroless 鏡像都提供了?debug?tag,適合在開發(fā)階段進(jìn)行調(diào)試。如何使用?替換容器的 base 鏡像:

        FROM golang:1.12as build-envWORKDIR /go/src/appCOPY ./go/src/appRUN go get-d -v ./...RUN go build -o /go/bin/appFROM gcr.io/distroless/base-debian11:debug # use debug tag hereCOPY --from=build-env /go/bin/app /CMD ["/app"]

        重新構(gòu)建鏡像并部署,得益于debug鏡像中提供了 busybox shell 讓我們可以 exec 到容器中。

        2. debug 容器與共享進(jìn)程命名空間

        同一個 pod 中可以運(yùn)行多個容器,通過設(shè)置?pod.spec.shareProcessNamespace?為?true,來讓同一個 Pod 中的多容器共享同一個進(jìn)程命名空間[9]。

        Share a single process namespace between all of the containers in a pod. When this is set containers will be able to view and signal processes from other containers in the same pod, and the first process in each container will not be assigned PID 1. HostPID and ShareProcessNamespace cannot both be set. Optional: Default to false.

        添加一個使用?ubuntu?鏡像的?debug?容器,這里為了測試(后面解釋)我們?yōu)樵萜魈砑?securityContext.runAsUser: 1000,模擬兩個容器使用不同的 UID 運(yùn)行:

        apiVersion: apps/v1kind:Deploymentmetadata:  creationTimestamp:null  labels:    app: golang-distroless  name: golang-distrolessspec:  replicas:1  selector:    matchLabels:      app: golang-distroless  strategy:{}template:    metadata:      creationTimestamp:null      labels:        app: golang-distroless    spec:      shareProcessNamespace:true      containers:- image: addozhang/golang-distroless-example:latest        name: golang-distroless-example        securityContext:          runAsUser:1000        resources:{}- image: ubuntu        name: debug        args:['sleep','1d']        securityContext:          capabilities:            add:- SYS_PTRACE        resources:{}status:{}

        更新 deployment 之后:

        $ kubectl get poNAME                                 READY   STATUS    RESTARTS   AGEgolang-distroless-85c4896c45-rkjwn   2/2Running03m12s$ kubectl get po -o json | jq -r '.items[].spec.containers[].name'golang-distroless-exampledebug

        然后通過 debug 容器來進(jìn)入到 pod 中:

        $ kubectl exec-it golang-distroless-85c4896c45-rkjwn -c debug -- sh

        然后在容器中執(zhí)行:

        $ ps -efUID        PID  PPID  C STIME TTY          TIME CMDroot         10014:54?00:00:00/pause                      # infra 容器100070014:54?00:00:00/app                         # 原容器,UID 為 1000root        190014:55?00:00:00 sleep 1d# debug 容器root        250014:55 pts/000:00:00 shroot        3225014:55 pts/000:00:00 ps -ef

        嘗試訪問 進(jìn)程?7?的進(jìn)程空間:

        $ cat /proc/7/environ$ cat:/proc/7/environ:Permission denied

        我們需要為?debug?容器加上:

        securityContext:  capabilities:    add:- SYS_PTRACE

        之后再訪問就正常了:

        $ cat /proc/7/environPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=golang-distroless-58b6c5f455-v9zkvSSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crtKUBERNETES_PORT_443_TCP=tcp://10.43.0.1:443KUBERNETES_PORT_443_TCP_PROTO=tcpKUBERNETES_PORT_443_TCP_PORT=443KUBERNETES_PORT_443_TCP_ADDR=10.43.0.1KUBERNETES_SERVICE_HOST=10.43.0.1KUBERNETES_SERVICE_PORT=443KUBERNETES_SERVICE_PORT_HTTPS=443KUBERNETES_PORT=tcp://10.43.0.1:443HOME=/root

        同樣我們也可以訪問進(jìn)程的文件系統(tǒng):

        $ cd /proc/7/root$ lsapp  bin  boot  dev  etc  home  lib  lib64  proc  root  run  sbin  sys  tmp  usr  var

        無需修改容器的基礎(chǔ)鏡像,使用?pod.spec.shareProcessNamespace: true?配合安全配置中增加?SYS_PTRACE?特性,為 debug 容器賦予完整的 shell 訪問來調(diào)試應(yīng)用。但是修改 YAML 和安全配置只適合在測試環(huán)境使用,到了生產(chǎn)環(huán)境這些都是不允許的。

        我們就需要用到?kubectl debug?了。

        3. Kubectl debug

        針對不同的資源?kubectl debug?可以進(jìn)行不同操作:

        ?負(fù)載:創(chuàng)建一個正在運(yùn)行的 Pod 的拷貝,并可以修改部分屬性。比如在拷貝中使用新版本的tag。?負(fù)載:為運(yùn)行中的 Pod 增加一個臨時容器(下面介紹),使用臨時容器中的工具調(diào)試,無需重啟 Pod。?節(jié)點(diǎn):在節(jié)點(diǎn)上創(chuàng)建一個 Pod 運(yùn)行在節(jié)點(diǎn)的 host 命名空間,可以訪問節(jié)點(diǎn)的文件系統(tǒng)。

        3.1 臨時容器

        從 Kubernetes 1.18 之后開始,可以使用?kubectl?為運(yùn)行的 pod 添加一個臨時容器。這個命令還處于?alpha?階段,因此需要在“feature gate”[10]中打開。

        在使用 k3d 創(chuàng)建 k3s 集群時,打開?EphemeralContainers?feature:

        $ k3d cluster create test --k3s-arg "--kube-apiserver-arg=feature-gates=EphemeralContainers=true"@

        然后創(chuàng)建臨時容器,創(chuàng)建完成后會直接進(jìn)入容器:

        $ kubectl debug golang-distroless-85c4896c45-rkjwn -it --image=ubuntu --image-pull-policy=IfNotPresent#臨時容器 shell$ apt update && apt install -y curl$ curl localhost:8080Hello world!
        728152f5218d8a871c51d977cca5417c.webp臨時容器

        值得注意的是,臨時容器無法與原容器共享進(jìn)程命名空間:

        $ ps -efUID        PID  PPID  C STIME TTY          TIME CMDroot         10002:59 pts/000:00:00 bashroot      30421003:02 pts/000:00:00 ps -ef

        可以通過添加參數(shù)?--target=[container]?來將臨時容器掛接到目標(biāo)容器。這里與?pod.spec.shareProcessNamespace?并不同,進(jìn)程號為 1 的進(jìn)程是目標(biāo)容器的進(jìn)程,而后者的進(jìn)程是 infra 容器的進(jìn)程?/pause

        $ kubectl debug golang-distroless-85c4896c45-rkjwn -it --image=ubuntu --image-pull-policy=IfNotPresent--target=golang-distroless-example

        注意:目前的版本還不支持刪除臨時容器,參考?issue[11],支持的版本:

        2c4983976787925a44287b1ec264e1c1.webp

        3.2 拷貝 Pod 并添加容器

        除了添加臨時容器以外,另一種方式就是創(chuàng)建一個 Pod 的拷貝,并添加一個容器。注意這里的是普通容器,不是臨時容器。?注意這里加上了?--share-processes

        $ kubectl debug golang-distroless-85c4896c45-rkjwn -it --image=ubuntu --image-pull-policy=IfNotPresent--share-processes --copy-to=golang-distroless-debug

        注意這里加上了?--share-processes,會自動加上?pod.spec.shareProcessNamespace=true

        $ kubectl get po golang-distroless-debug -o jsonpath='{.spec.shareProcessNamespace}'true

        注意:使用?kubectl debug?調(diào)試,并不能為 pod 自動加上?SYS_PTRACE?安全特性,這就意味著如果容器使用的 UID 不一致,就無法訪問進(jìn)程空間。?截止發(fā)文,計(jì)劃在?1.23?中支持[12]。

        總結(jié)

        目前上面所有的都不適合在生產(chǎn)環(huán)境使用,無法在不修改 Pod 定義的情況下進(jìn)行調(diào)試。

        期望 Kubernetes 1.23 版本之后?debug?功能添加?SYS_PTRACE?的支持。到時候,再嘗試一下。

        引用鏈接

        [1]?Distroless 鏡像:?https://github.com/GoogleContainerTools/distroless
        [2]?GoogleContainerTools/distroless:?https://github.com/GoogleContainerTools/distroless
        [3]?gcr.io/distroless/static-debian11:?https://github.com/GoogleContainerTools/distroless/blob/main/base/README.md
        [4]?gcr.io/distroless/base-debian11:?https://github.com/GoogleContainerTools/distroless/blob/main/base/README.md
        [5]?gcr.io/distroless/java-debian11:?https://github.com/GoogleContainerTools/distroless/blob/main/java/README.md
        [6]?gcr.io/distroless/cc-debian11:?https://github.com/GoogleContainerTools/distroless/blob/main/cc/README.md
        [7]?gcr.io/distroless/nodejs-debian11:?https://github.com/GoogleContainerTools/distroless/blob/main/nodejs/README.md
        [8]?gcr.io/distroless/python3-debian11:?https://github.com/GoogleContainerTools/distroless/blob/main/experimental/python3/README.md
        [9]?多容器共享同一個進(jìn)程命名空間:?https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/
        [10]?“feature gate”:?k3d%20cluster%20create%20test%20--k3s-arg%20%22--kube-apiserver-arg=feature-gates=EphemeralContainers=true%22@
        [11]?issue:?https://github.com/kubernetes/kubernetes/issues/84764#issuecomment-872839644
        [12]?計(jì)劃在?1.23?中支持:?https://github.com/kubernetes/kubernetes/issues/97103#issuecomment-899382147

        ? ?

        b0dfe5b39fed93d24113848689606c7b.webp
        喜歡明哥文章的同學(xué)歡迎長按下圖訂閱!

        ???

        瀏覽 58
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. 伸进她的小内裤疯狂揉摸没声音 | 吴梦梦av| 扒开女同桌双腿嗯啊叫动漫 | 99re这里只有精品在线 | 色戒未删减版在线电影 |