0
点赞
收藏
分享

微信扫一扫

Kubernetes Java 服务最小化镜像方案


本篇不是介绍如何把一个大的镜像如何变小,主要实现的是,如何把 Java 服务镜像变化的部分变的尽可能的小。
方案依赖于 Kubernetes Pod 运行机制(纯Docker有兼容方案)。

Java服务的镜像都很大,当遇到离线环境需要拷贝的时候,每次都需要传递几百G的镜像,拷贝频繁时影响更大。

Java服务的镜像大主要在于基础运行环境,一个 openjdk 的镜像在 200M以上(jre环境更小,但是有限制),为了方便运维还会安装一些工具,最终可能会产生一个300M~500M左右的基础镜像。基础镜像的内容是基本不变的,可以联网传递 Docker 镜像时,分层机制也会只传输变化的层,真正的数据量基本上就是 Java 服务的 jar 包。但是离线环境时,传递的都是一个完整的镜像。

为了将传输的部分变的更小,可以将运行环境和程序分成两个独立的镜像,运行环境不会经常改变,离线部署时,只有第一次需要发送运行环境的镜像。

将 Java 打包好的 jar 文件放到一个单独的镜像中,这个镜像的大小基本上就是 jar 包的大小,每次更新服务的时候,只需要提供这个镜像。

1. 主容器

将容器运行的基础环境作为主容器进行运行,例如选择一个配置好各种工具的 openJdk11 镜像作为主容器。

2. 初始化容器

将程序打包的 jar,用最小镜像打包,最小镜像中只需要支持 mv 移动文件的命令即可(可用 busybox)。

初始化容器就是包含了服务程序的镜像,这个镜像无法单独运行,需要配合 主容器 运行。

3. 运行方式

纯 yaml 时可以一次性配置好,使用 Rancher 时需要分两步进行配置。

3.1 Rancher 方式

下面示例操作时,使用 Nginx + busybox 演示的,busybox 是init容器,会输出 ​​index.html​​ 文件。

选择主容器的镜像配置一个服务,配置好启动脚本(例如 ​​java -jar /opt/dubbo-app/app.jar​​​),挂载一个 emptyDir 到 ​​/opt/dubbo-app​​ 目录,由于这个目录下面没有任何东西,所以命令启动的时候肯定会出错起不来。

主容器配置好后,在这个服务上点击【添加Sidecar】

Kubernetes Java 服务最小化镜像方案_运行环境

配置带有服务程序的镜像为 Init容器

Kubernetes Java 服务最小化镜像方案_jar_02

配置【数据卷】,这里会显示主容器已经挂载的卷,添加映射配置一个别的目录名(别覆盖容器中的已有目录)

Kubernetes Java 服务最小化镜像方案_最小镜像_03


打开高级选项配置【命令】,这一步就是要执行命令,把 app.jar 程序拷贝到 ​​/app​​​ 目录(也就是主容器的 ​​/opt/dubbo-app​​ 目录,这俩是同一个目录在两个容器中的不同路径),命令内容配置如下:

Kubernetes Java 服务最小化镜像方案_docker_04


点击下方的【启动】即可。

启动时,初始化容器先把程序拷贝到正确的位置,主容器此时在启动 app.jar 时就能启动成功。

后续服务升级的时候,只需要升级init初始化容器的版本。

3.2 yaml 方式

直接导出上面 Rancher 方式对应的 yaml,修改初始化容器的镜像和deploy的名称等各项信息。

apiVersion: apps/v1
kind: Deployment
metadata:
name: 服务名
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
workload.user.cattle.io/workloadselector: deployment-default-服务名
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
template:
metadata:
labels:
workload.user.cattle.io/workloadselector: deployment-default-服务名
spec:
containers:
- image: openjdk11运行环境
imagePullPolicy: Always
name: 服务名
ports:
- containerPort: 80
name: 80tcp01
protocol: TCP
resources: {}
securityContext:
allowPrivilegeEscalation: false
capabilities: {}
privileged: false
procMount: Default
readOnlyRootFilesystem: false
runAsNonRoot: false
stdin: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
tty: true
volumeMounts:
- mountPath: /usr/share/nginx/html
name: vol1
dnsPolicy: ClusterFirst
initContainers:
- args:
- -c
- mv app.jar /app/app.jar
command:
- /bin/sh
image: 服务镜像jar:1.0.0
imagePullPolicy: Always
name: app-jar
resources: {}
securityContext:
allowPrivilegeEscalation: false
capabilities: {}
privileged: false
procMount: Default
readOnlyRootFilesystem: false
runAsNonRoot: false
stdin: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
tty: true
volumeMounts:
- mountPath: /html
name: vol1
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- emptyDir: {}
name:

以上内容没有经过实际操作,仅供参考。

4. 纯 Docker 兼容方案

将带有 app.jar 的镜像发送给客户后,使用 Docker 多阶段构建创建一个新的镜像:

FROM 服务app-jar镜像 as app
FROM openjdk运行环境镜像
COPY --from=app app.jar /opt/dubbo-app/

通过上面的方式就可以将第一个镜像中的程序拷贝到第二个镜像中。

通过 ​​docker build -t 服务镜像:版本号 .​​ 就可以创建一个新镜像。启动创建的新镜像即可。

5. 参考

5.1 多阶段构建参考文章:

​​multistage-build​​​​docker-images-part1-reducing-image-size​​

5.2 多容器Pod

​​communicate-containers-same-pod-shared-volume​​​​inject-data-application/define-command-argument-container​​


举报

相关推荐

0 条评论