Java容器化实践:从Dockerfile构建到Kubernetes部署
第一部分:Dockerfile编写与基础JDK容器构建
1.1 基础JDK容器的构建
FROM alpine:3.19 RUN apk add --update --no-cache openjdk8 #1 ENV JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk ENV PATH="$JAVA_HOME/bin:${PATH}" RUN addgroup -S nonroot \ #2 && adduser -S nonroot -G nonroot
1由于我们的业务应用还在使用JDK8,到dockerhub官方维护的镜像中看,大多的JDK8容器镜像已经很久没有维护了,所以这里选择使用容器环境最流行的alpine作为操作系统基础包,再通过包管理器添加较新的jdk环境的方式作为基础镜像,在较新的版本中JVM的cgroups视角问题已经修复,JVM已经可以获取到正确的可支配内存了,详见下面的dashboard部分,所以建议是使用自己维护的JDK8容器镜像
2添加非root用户,后续在业务DockerFile的entrypoint/cmd前再切为这个用户,这是容器安全的最佳实践,值得注意的是,如果要在容器中操作文件,或其他需要root才能做到操作,可能会提示权限问题,需要自己处理好权限问题。在我们原本的jar中,本来会默认在当前目录写logback日志,由于权限nonroot不能读写,就只能在标准流输出了,避免了过多读写unionfs的问题。
后续使用
docker build -t xxxx/yyyy/jdk:jdk1.8.392.08-r1-alpine3.19 . (不要漏了.)
docker push xxxx/yyyy/jdk:jdk1.8.392.08-r1-alpine3.19
的方式使其可以作为一个类似官方维护的jdk基础镜像然后放到自己的容器私服中
1.2 业务JVM容器的构建
参考的DockerFile,使用上一个阶段的基础镜像作为基础运行环境
FROM xxxx/yyyy/jdk:jdk1.8.392.08-r1-alpine3.19 WORKDIR /app COPY xxx/<domain-jar-name>c.jar yyy/application.yml . RUN wget -P /app/ <jmx-exporter-config-fileserver-url>/config.yml;wget -P /app/ <jmx-exporter-javaagent-jar-fileserver-url>/jmx_prometheus_javaagent.jar;wget -P /app/ <otel-javaagent-jar-fileserver-url>/opentelemetry.jar EXPOSE 7006 8080 #K8S环境会进行实际端口映射实际上可有可无 USER nonroot ENTRYPOINT ["java", "-javaagent:/app/jmx_prometheus_javaagent.jar=8080:/app/config.yml","-javaagent:/app/opentelemetry-javaagent.jar","-Dotel.service.name=<OTEL-trace-name>","-Dotel.exporter.otlp.endpoint=<otel-colletor-endpoint>","-Xms256m", "-Xmx256m","-jar", "<domain-jar-name>.jar"]
1.2.1构建说明
由于我们公司业务需要运维多个2B环境,存在一些部署环境上的差异,所以我选择在CI阶段的jenkins服务器上构建jar包归档和兼容产物,而非容器化构建推荐的两阶段构建,仅在Dockerfile中加入构建好的jar包和基础配置文件(不要这个也可以,因为后续会在K8S中重新挂载configmap,主要是环境测试方便)
1.2.2java agent说明
由于可观测性的需要,从本地服务器上获取到官方提供的JMX exporter的jar包和默认config文件(JMX exporter维护团队推荐的行为,作为javaagent一起运行),OTEL agent的jar包,读者可以自行到对应的官方连接下载或者去除掉对应的配置参数),读者可以自行修改为官方提供的url或者自己的文件服务器,或者改为后续在k8s环境的pod配置里挂载一个固定的PV