Kubernetes 上调试 distroless 容器

5d590e8049c4a36ecf9f4206d361ef5e.gif

作者 | Addo Zhang

来源 | 云原生指北

Distroless 镜像

Distroless 容器,顾名思义使用 Distroless 镜像[1]作为基础镜像运行的容器。

"Distroless" 镜像只包含了你的应用程序以及其运行时所需要的依赖。不包含你能在标准 Linxu 发行版里的可以找到的包管理器、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 镜像有什么用?

那些可能是构建镜像时需要的,但大部分并不是运行时需要的。这也是为什么上篇文章介绍 Buildpacks

其实控制体积并不是 distroless 镜像的主要作用。将运行时容器中的内容限制为应用程序所需的依赖,此外不应该安装任何东西。这种方式可能极大的提升容器的安全性,也是 distroless 镜像的最重要作用。

4b43376b617f33918ab4434ac0da85cb.png

这里并不会再深入探究 distroless 镜像,而是如何调试 distroless 容器

没有了包管理器,镜像构建完成后就不能再使用类似 aptyum 的包管理工具;没有了 shell,容器运行后无法再进入容器。

“就像一个没有任何门的房间,也无法安装门。” Distroless 镜像在提升容器安全性的同时,也为调试增加了难度。

使用 distroless 镜像

写个很简单的 golang 应用:

package main
import ("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 应用的基础镜像:

FROM golang:1.12 as build-env
WORKDIR /go/src/app
COPY . /go/src/app
RUN go get -d -v ./...
RUN go build -o /go/bin/app
FROM gcr.io/distroless/base-debian11
COPY --from=build-env /go/bin/app /
CMD ["/app"]

使用镜像创建 deployment

$ kubectl create deploy golang-distroless --image addozhang/golang-distroless-example:latest
$ kubectl get po
NAME                                READY   STATUS    RESTARTS   AGE
golang-distroless-784bb4875-srmmr   1/1     Running   0          3m2s

尝试进入容器:

$ kubectl exec -it golang-distroless-784bb4875-srmmr -- sh
error: Internal error occurred: error executing command in container: failed to exec in 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

如何调试 Distroless 容器

1. 使用 distroless debug 镜像

GoogleContainerTools 为每个 distroless 镜像都提供了 debug tag,适合在开发阶段进行调试。如何使用?替换容器的 base 镜像:

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

重新构建镜像并部署,得益于debug镜像中提供了 busybox shell 让我们可以 exec 到容器中。

2. debug 容器与共享进程命名空间

同一个 pod 中可以运行多个容器,通过设置 pod.spec.shareProcessNamespace 为 true,来让同一个 Pod 中的多容器共享同一个进程命名空间[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 容器,这里为了测试(后面解释)我们为原容器添加 securityContext.runAsUser: 1000,模拟两个容器使用不同的 UID 运行:

apiVersion: apps/v1
kind: Deployment
metadata:creationTimestamp: nulllabels:app: golang-distrolessname: golang-distroless
spec:replicas: 1selector:matchLabels:app: golang-distrolessstrategy: {}template:metadata:creationTimestamp: nulllabels:app: golang-distrolessspec:shareProcessNamespace: truecontainers:- image: addozhang/golang-distroless-example:latestname: golang-distroless-examplesecurityContext:runAsUser: 1000resources: {}- image: ubuntuname: debugargs: ['sleep', '1d']securityContext:capabilities:add:- SYS_PTRACEresources: {}
status: {}

更新 deployment 之后:

$ kubectl get po
NAME                                 READY   STATUS    RESTARTS   AGE
golang-distroless-85c4896c45-rkjwn   2/2     Running   0          3m12s
$ kubectl get po -o json | jq -r '.items[].spec.containers[].name'
golang-distroless-example
debug

然后通过 debug 容器来进入到 pod 中:

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

然后在容器中执行:

$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 14:54 ?        00:00:00 /pause                      # infra 容器
1000         7     0  0 14:54 ?        00:00:00 /app                         # 原容器,UID 为 1000
root        19     0  0 14:55 ?        00:00:00 sleep 1d                 # debug 容器
root        25     0  0 14:55 pts/0    00:00:00 sh
root        32    25  0 14:55 pts/0    00:00:00 ps -ef

尝试访问 进程 7 的进程空间:

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

我们需要为 debug 容器加上:

securityContext:capabilities:add:- SYS_PTRACE

之后再访问就正常了:

$ cat /proc/7/environ
PATH=/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

同样我们也可以访问进程的文件系统:

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

无需修改容器的基础镜像,使用 pod.spec.shareProcessNamespace: true 配合安全配置中增加 SYS_PTRACE 特性,为 debug 容器赋予完整的 shell 访问来调试应用。但是修改 YAML 和安全配置只适合在测试环境使用,到了生产环境这些都是不允许的。

我们就需要用到 kubectl debug 了。

3. Kubectl debug

针对不同的资源 kubectl debug 可以进行不同操作:

•负载:创建一个正在运行的 Pod 的拷贝,并可以修改部分属性。比如在拷贝中使用新版本的tag。•负载:为运行中的 Pod 增加一个临时容器(下面介绍),使用临时容器中的工具调试,无需重启 Pod。•节点:在节点上创建一个 Pod 运行在节点的 host 命名空间,可以访问节点的文件系统。

3.1 临时容器

从 Kubernetes 1.18 之后开始,可以使用 kubectl 为运行的 pod 添加一个临时容器。这个命令还处于 alpha 阶段,因此需要在“feature gate”[10]中打开。

在使用 k3d 创建 k3s 集群时,打开 EphemeralContainers feature:

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

然后创建临时容器,创建完成后会直接进入容器:

$ kubectl debug golang-distroless-85c4896c45-rkjwn -it --image=ubuntu --image-pull-policy=IfNotPresent
#临时容器 shell
$ apt update && apt install -y curl
$ curl localhost:8080
Hello world!
b4a020a074714d5e470f363d4e1fd585.png
临时容器

值得注意的是,临时容器无法与原容器共享进程命名空间:

$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 02:59 pts/0    00:00:00 bash
root      3042     1  0 03:02 pts/0    00:00:00 ps -ef

可以通过添加参数 --target=[container] 来将临时容器挂接到目标容器。这里与 pod.spec.shareProcessNamespace 并不同,进程号为 1 的进程是目标容器的进程,而后者的进程是 infra 容器的进程 /pause

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

注意:目前的版本还不支持删除临时容器,参考 issue[11],支持的版本:

6f580592ba5e1600f44c62f35df55307.png

3.2 拷贝 Pod 并添加容器

除了添加临时容器以外,另一种方式就是创建一个 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 调试,并不能为 pod 自动加上 SYS_PTRACE 安全特性,这就意味着如果容器使用的 UID 不一致,就无法访问进程空间。 截止发文,计划在 1.23 中支持[12]

总结

目前上面所有的都不适合在生产环境使用,无法在不修改 Pod 定义的情况下进行调试。

期望 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] 多容器共享同一个进程命名空间: 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] 计划在 1.23 中支持: https://github.com/kubernetes/kubernetes/issues/97103#issuecomment-899382147

ab8b2af355c29c59294ab8582b52a9df.gif

5429bfab70735d882df31edd137f538e.png

往期推荐

什么是RedCup?一文详解!

谷歌自研Tensor芯片,8核CPU,20核GPU

清华大学:2021元宇宙研究报告

Mendix 发布全球低代码报告

378a8afb76702912ca6c671ab2ebe8b0.gif

点分享

bee69063d6174ee075b4ede625822aac.gif

点收藏

15075ed2c51b4de3b95c62f5bf002e2a.gif

点点赞

2d04839933f806441de23b5320070126.gif

点在看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/513466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

技术干货 | 如何在 Library 中使用/依赖 mPaaS?

简介: 在使用 mPaaS 框架过程中,有时需要复用模块。复用时需要按照使用 Module 依赖的方式添加模块。 使用场景 在使用 mPaaS 框架过程中,有时需要复用模块。复用时需要按照使用 Module 依赖的方式添加模块。本文以将复用 mPaaS 扫码组件的…

Java单元测试技巧之PowerMock

简介: 高德的技术大佬向老师在谈论方法论时说到:“复杂的问题要简单化,简单的问题要深入化。” 这句话让我感触颇深,这何尝不是一套编写代码的方法——把一个复杂逻辑拆分为许多简单逻辑,然后把每一个简单逻辑进行深入…

OceanBase再破纪录!核心成员陈萌萌:坚持HTAP就是坚持我们做数据库的初心

简介: 2021年5月20日,据国际事务处理性能委员会(TPC,Transaction Processing Performance Council)官网披露,蚂蚁集团自主研发的分布式关系型数据库OceanBase在数据分析型基准测试(TPC-H&#x…

快成物流科技 x mPaaS | 小程序容器加持下的技术架构“提质增效”

简介: 大前端团队如何选型技术?如何快速上手?如何高效协同?让我们看看快成科技如何解决这一问题。 导言 从 2017 年开始,GMTC“移动技术大会”就更名为“大前端技术大会”。发展至今,混合开发、原生开发、前…

直接 root Android 设备,会「隐身」的恶意软件 AbstractEmu 正在偷偷作恶

整理 | 梦依丹出品 | CSDN(ID:CSDNnews)“我就点一下,钱就没了”!手机不仅给我们带来便利,而且还记录着我们方方面面的信息,甚至是一言一行。正因此,它成了漏洞制作者、恶意软件黑客…

进入中国内地第31年的麦当劳 ,为什么还能不断吸引新消费人群?

简介: 麦当劳的数字化转型从2016年开始全面推行,力求无论何时何地何种方式,消费者都能随心享受麦当劳的产品与服务,数字化转型在过去几年取得显著效果!而阿里云数据中台的引入,将成为麦当劳数字化转型在拓展…

配置审计(Config)变配报警设置

简介: 本文作者【紫极zj】,本篇将主要介绍通过配置审计的自定义规则等服务,对负载均衡进行预警行为的相关介绍。 前言 配置审计(Config)将您分散在各地域的资源整合为全局资源列表,可便捷地搜索全局资源&…

漫画:什么是 “元宇宙” ?

作者|小灰来源|程序员小灰什么是更高的自由度呢?或许有人觉得,我们在网络游戏当中,不是也很自由吗?想怎么玩就怎么玩。但是,无论一款网络游戏的元素有多么丰富,游戏当中的角色、任务、职业、道具、场景&…

程序员写好技术文章的几点小技巧

简介: 去年成为了内网技术分享平台的年度作者,受邀写一篇关于“如何写好文章”的文章。我本身并不喜欢写字,去年写的几篇文章,涉及的话题自带流量,所以阅读量多了一些,谈不上有多擅长。不过还是决定分享一下…

雅虎、领英接连退出中国,GitHub 会受到影响吗?

整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)继半个月前微软宣布关闭领英(即 LinkedIn)在华业务后,本周二,雅虎也宣布了最新消息:自 2021 年 11 月 1 日起,用户将无法从中国大…

高德打车构建可观测性系统实践

简介: 互联网工程的高速发展,分布式、微服务、容器化架构的流行,互联网已全面进入云原生时代。构建系统的方式由最初的单体大应用演变为分布式架构,一台服务器可能仅存几小时甚至几分钟,这种复杂性大大增加了把系统运行…

java script 代码放在jsp 还是放在servlet_ServletContext JSP

会话:四种:1 :Session–保存在服务器上默认的30分2:Cookie 客户端的,maxAge3:重写 url - > url;jsessionidxxxxxxx - > response.encodeRedirectUri(url);4:隐藏表单 1:Serv…

飞猪基于 Serverless 的云+端实践与思考

简介: 过去两年,飞猪前端一直在积极地进行 Serverless 建设和实践,2019 年 - 2020 年我们和集团 Node 架构组、研发平台一起完成了基础能力的建设和业务试点,成为集团率先落地 Serverless 实践的 BU,2020 年 - 2021 年…

unc 目录不受支持_Shopify平台对于店铺模版都提供哪些支持

在自定义Shopify模版之前,请确保您了解可用的支持级别。如果您要进行基本的自定义,则可以从模版开发人员处获取支持。如果您要对模版进行大量更改,请参阅我们的模版支持的其他资源列表。若要了解 Shopify 不支持的自定义,请参阅我…

CSS——定位、CSS高级技巧、修饰属性

1、定位 作用&#xff1a;灵活的改变盒子在网页中的位置 实现&#xff1a; 定位模式&#xff1a;position边偏移&#xff1a;设置盒子的位置 leftrighttopbottom 1.1 相对定位 position&#xff1a;relative <!DOCTYPE html> <html lang"en"> <…

Hologres如何支持亿级用户UV计算

简介&#xff1a; 本文将介绍阿里云Hologres如何基于RoaringBitmap进行UV等高复杂度计算的方案&#xff0c;实现亿级用户万级标签亚秒级分析&#xff0c;帮助用户从Kylin平滑迁移到Hologres&#xff0c;实现更实时、开发更灵活、功能更完善的多维分析能力。 背景介绍 在用户行…

location 拦截所有_电脑广告拦截软件 Adguard Premium

每日一谈我们上个网的时候经常会遇到很多烦人的广告、在线跟踪等&#xff0c;不仅导致你的网站加载速度非常的慢&#xff0c;并且还可能会导致你遇到一些恶意软件和威胁。为了避免这种情况的产生&#xff0c;今天我为大家推荐这款广告拦截软件来阻止你浏览器中的广告&#xff0…

事务消息应用场景、实现原理与项目实战(附全部源码)

简介&#xff1a; 从应用场景出发&#xff0c;给出解决方案与实现原理&#xff0c;并提供整套工业级实现源码。 作者&#xff1a;丁威 活动中心场景介绍 在电商系统上线初期&#xff0c;往往会进行一些“拉新”活动&#xff0c;例如活动部门提出新用户注册送积分、送优惠券活…

request用法_3分钟短文:说说Laravel页面会话之间的数据保存Session用法

引言我们知HTTP请求是没有状态的&#xff0c;两个请求之间没有直接的关联关系。但大多数情况下&#xff0c; 我们需要保持用户的会话间数据的连续性&#xff0c;这时&#xff0c;为了数据安全起见&#xff0c; 有必要在服务器上临时存储一些上下文数据了。这就是 session 设计的…

调研邀请:我们到底需要什么样的低代码平台?

《乔布斯传》中有这样一段话&#xff1a;“有人会说&#xff0c;顾客想要什么产品就提供什么产品&#xff0c;但这并不是我的做事方式。我的职责是在人们还没有意识到需求之前&#xff0c;就研发出他们想要的&#xff0c;我们的任务是搞定那些还没有形成“定论”的事情。”这段…