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 租车管理系统_jsp+servlet+jdbc实现的java web共享租车信息管理系统,包括登陆注册,页面框架Easy UI...

项目描述初学java web项目时做的一个课设,基于servletjspjdbc的后台管理系统,包含5个模块:汽车账户部管理、租车账户部管理、汽车信息管理表、租车记录表、租车租聘表。功能完整,均能实现增删查改。运行环境jdk8tomcat8.5mysql5.7Eclipse项目…

Java单元测试技巧之PowerMock

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

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

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

python小波分解与重构_小波分解和重构

小波变换能够很好地表征一大类以低频信息为主要成分的信号,小波包变换可以对高频部分提供更精细的分解详见(http://www.cnblogs.com/welen/articles/5667217.html)小波分解函数和重构函数的应用和区别(https://www.baidu.com/link?urlNsLWcGxYPabqB0JEFzkjHzeLmcvG…

Java中string与String区别_JAVA中String与StringBuffer的区别

JAVA中String与StringBuffer的区别2009-12-3文字大小:大中小在java中有3个类来负责字符的操作。1.Character 是执行单个字符操作的,2.String 对一串字符执行操作。不可变类。3.StringBuffer 也是对一串字符执行操作,但是可变类。String:是对象不是原始类…

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

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

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

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

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

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

tomcat jsvc java_opts_Tomcat 学习笔记(2) - 使用 jsvc 启动tomcat

jsvc 是个什么是么高端武器呢全称:Java Service还是没有弄明白是什么,那就继续往下看。我们赞不讨论 tomcat 应不应该运行在80端口上。假如我们有需求,需要 tomcat 运行在 80 端口上。那么一个限制就出现了。*nix操作系统只用root权限才能监听…

python 元类 type_python Class:面向对象高级编程 元类:type

type的用法:1、普通的type用法:检查类型class my(object):def hello(self, nameworld):print(Hello, %s. % name)h my()print(type(my))print(type(h))运行结果:my是class, 所以它的类型是type,h是class的实例,所以它的类型是cla…

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

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

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

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

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

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

js传中文参数 java取_js中文转码传输java后台 适用于用url传递中文参数

第一种方法: 解决方法如下: 1、在JS里对中文参数进行两次转码 var login_name document.getElementById("loginname").value;login_name encodeURI(login_name);login_name encodeURI(login_name);2、在服务器端对参数进行解码 String logi…

python信用卡违约预测分析_Python数据分析及可视化实例之银行信用卡违约预测(24)...

1.项目背景:银行体系对于信用可违约进行预测,原始数据集如下:2.分析步骤:(1)数据清洗(Data Cleaning)(2) 探索性可视化(Exploratory Visualization)(3) 特征工程(Feature Engineering)(4)基本建模&评估(Basic Modeling& E…

雅虎、领英接连退出中国,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 年…