现在我们的应用已经跑起来了,我们最终希望把它部署到Kubernetes集群上。但是在那之前,我们得先构建镜像。
咱们开始吧。
构建容器图像 — Coherence 图像记得,这个待办事项应用包括三个部分:
前端和后端都是无状态的微服务,而Coherence则是有状态的。我们必须分别为它们构建容器镜像。我们先从构建Coherence的镜像开始。
但在构建镜像之前,我们必须改变配置Hibernate的模式。这是因为Hibernate配置文件包含了数据库的用户名称和密码,所以我们绝对不想将这些信息硬编码到镜像中。相反,在部署到Kubernetes时,我们最终希望使用Kubernetes Secret并将其挂载到pod的卷。至于钱包文件,我们希望使用Oracle Database Operator来为我们获取它,并将Hibernate配置文件的位置作为参数传递给程序。
首先,修改 coherence-cache-config.xml 并在 Hibernate CacheStore 的 init-param 中添加以下内容(除了 entityname 参数以外):
<cachestore-scheme> <class-scheme> <class-name> com.oracle.coherence.hibernate.cachestore.HibernateCacheStore </class-name> <init-params> <init-param> <param-type>java.lang.String</param-type> <param-value>{entityname}</param-value> </init-param> <init-param> <param-name>java.lang.String</param-name> <param-value>${coherence.hibernate.config hibernate.cfg.xml}</param-value> </init-param> </init-params> </class-scheme> </cachestore-scheme>
我们现在可以将 coherence.hibernate.config 作为系统属性来使用,以设置 Hibernate 配置文件的位置,不管是本地运行还是构建容器镜像时。
接下来,创建一个Dockerfile用于构建Coherence镜像。
# 第一阶段,构建应用程序 FROM container-registry.oracle.com/java/jdk-no-fee-term:21 as build # 安装 Maven WORKDIR /usr/share RUN set -x && \ curl -O https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz && \ tar -xvf apache-maven-*-bin.tar.gz && \ rm apache-maven-*-bin.tar.gz && \ mv apache-maven-* maven && \ ln -s /usr/share/maven/bin/mvn /bin/ WORKDIR /helidon # 创建一个缓存本地仓库中'Maven World'的初始层。 # 增量 Docker 构建在 pom.xml 更新之前会从这里开始。 ADD coherence/pom.xml . RUN mvn package -Dmaven.test.skip # 进行 Maven 构建! # 增量 Docker 构建在你修改源代码时会从这里继续。 ADD coherence coherence RUN mvn -f coherence/pom.xml clean package -DskipTests RUN echo "done!" # 第二阶段,构建运行时镜像 FROM container-registry.oracle.com/java/jdk-no-fee-term:21 WORKDIR /helidon # 复制第一阶段构建的二进制文件 COPY --from=build /helidon/coherence/target/coherence.jar ./ COPY --from=build /helidon/coherence/target/libs ./libs HEALTHCHECK --start-period=10s --interval=30s \ CMD ["java", \ "-cp", "/helidon/coherence.jar", \ "com.tangosol.util.HealthCheckClient", \ "http://127.0.0.1:6676/ready", \ "||", "exit", "1"] # 设置环境变量 # 默认使用 POF ENV COHERENCE_POF_ENABLED=true ENV COHERENCE_SERIALIZER=pof # 设置健康检查端口为固定值(与上面的命令相对应) ENV COHERENCE_HEALTH_HTTP_PORT=6676 # 启用 Coherence 指标 ENV COHERENCE_METRICS_HTTP_ENABLED=true # 将日志级别设置为调试 ENV COHERENCE_LOG_LEVEL=9 CMD ["java", "-Dcoherence.grpc.server.port=1408", "-Dcoherence.hibernate.config=/hibernate/hibernate.cfg.xml", "-jar", "coherence.jar"] EXPOSE 1408 EXPOSE 9612
我们现在可以动手构建和测试容器镜像了。
使用 `docker buildx build` 命令构建镜像,参数 `--no-cache` 表示不使用缓存,`--platform=linux/amd64` 表示目标平台为 Linux AMD64,`-t coherence` 指定镜像名称为 `coherence`,`-f docker/Dockerfile.coherence` 指定 Dockerfile 的路径。
我们现在就用刚才构建的容器来运行Coherence。
# 导出 HIBERNATE_CFG_XML 和 WALLET 环境变量 export HIBERNATE_CFG_XML=/path/to/hibernate.cfg.xml export WALLET=/path/to/extracted/wallet # 使用 Docker 运行容器,挂载所需文件路径并启动容器 docker run --rm -it -v $HIBERNATE_CFG_XML:/hibernate/hibernate.cfg.xml -v $WALLET:/wallets/task_db coherence
我们现在可以看到Coherence开始启动了。
构建后端容器镜像我们现在来构建后端容器镜像。回想一下,后端模块依赖于Coherence模块这一点。创建后端容器镜像的Dockerfile文件。
# 第一阶段,构建应用 FROM container-registry.oracle.com/java/jdk-no-fee-term:21 as build # 安装 Maven 模块构建工具 WORKDIR /usr/share RUN set -x && \ curl -O https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz && \ tar -xvf apache-maven-*-bin.tar.gz && \ rm apache-maven-*-bin.tar.gz && \ mv apache-maven-* maven && \ ln -s /usr/share/maven/bin/mvn /bin/ WORKDIR /helidon ADD coherence coherence RUN mvn -f coherence/pom.xml install -DskipTests ADD backend backend RUN mvn -f backend/pom.xml clean package -DskipTests RUN echo "完成!" # 第二阶段,构建运行时镜像环境 FROM container-registry.oracle.com/java/jdk-no-fee-term:21 WORKDIR /helidon # 从第一阶段复制编译后的二进制文件 COPY --from=build /helidon/backend/target/backend.jar ./ COPY --from=build /helidon/backend/target/libs ./libs 设置环境变量 COHERENCE_CLUSTER=todo CMD ["java", "-Dcoherence.pof.enabled=true", "-Dcoherence.hibernate.config=/hibernate/hibernate.cfg.xml", "-jar", "backend.jar"] EXPOSE 8080
我们现在可以按照同样的方法来构建和测试这个图像了。
使用 Docker 构建一个名为 coherence 的镜像: docker build -t coherence . 运行并挂载指定目录的 Docker 容器: docker run --rm -it -v $HIBERNATE_CFG_XML:/hibernate/hibernate.cfg.xml -v $WALLET:/wallets/task_db backend
请注意,我们使用了Hibernate配置文件和数据库凭证作为参数。这是因为我们希望在启动时将现有数据预载入缓存。
预加载数据(通过Coherence CacheLoader)处理数据库的一致性涉及两个概念,
import java.io.File; import java.util.Collection; 导入 com.oracle.coherence.hibernate.cachestore.HibernateCacheLoader; 导入 org.hibernate.Session; 导入 org.hibernate.query.Query;
public class CachePreloader extends HibernateCacheLoader { // 初始化缓存预加载器 public CachePreloader(String entity) { super(entity, new File(System.getProperty("coherence.hibernate.config"))); } public Collection<String> getAllKeys() { ensureInitialized(); // 确保初始化 Session session = openSession(); // 打开会话 try { Query<String> query = session.createQuery("SELECT e.id FROM " + getEntityName() + " e", String.class); // 创建查询 return query.list(); // 返回列表 } finally { closeSession(session); // 关闭会话 } } }
并且添加一个方便的 REST API 以便调用它
@GET @Path("/preload") @Produces(APPLICATION_JSON) public Response preload() { Collection<String> keys = new CachePreloader("todo.Task").getAllKeys(); String message = "预加载数据,找到键共=" + keys.size() + "."; LOG.log(INFO, message); svc.preloadTasks(keys);
返回 Response.ok(message + "\n").build();
你现在可以通过调用这个预加载方法来提前加载数据:
```preload()
运行此命令来预加载后端数据
curl http://localhost:8080/api/backend/preload
如果你的数据库里已经有数据,如下所示: 正在预加载数据,找到的键值为11。 ## 构建容器镜像文件 — 前端容器镜像 在开发的不同阶段,我们处于不同的时间点。有些开发者希望能够在本地进行开发和测试。最终我们希望能够测试整个系统。但在那之前,我们如何才能不用频繁修改代码和配置来进行测试呢?Helidon 提供了[配置文件](https://helidon.io/docs/v4/se/config/config-profiles),让你可以预设每个环境的配置。例如,在本地开发时,我们最初可能只想使用 jar 包和容器。创建一个名为 application-local 的文件,并设置如下配置:
services: # 后端服务的端点地址 backend.endpoint: "http://localhost:8080"
我们也可以为本地容器环境创建一个类似的。
services: backend.endpoint: "http://host.docker.internal:8080"
对于 Kubernetes 来说:
服务:
后端端点: "http://backend:8080"
我们现在可以挑选要运行的配置。比如说我们要用本地配置。
运行带有特定配置的Java命令
java -Dconfig.profile=local -Xmx512m -Xms512m -jar target/frontend.jar
如果我们想本地运行容器呢?别担心,我们已经搞定了。
docker run --rm -it -v -e CONFIG_PROFILE='docker' $HIBERNATE_CFG_XML:/hibernate/hibernate.cfg.xml -v $WALLET:/wallets/task_db frontend
这里使用了 `docker` 命令来运行一个容器。命令中的 `-rm` 参数表示容器在停止后会被自动删除,`-it` 参数则为容器提供了交互式的终端。`-v` 参数用来挂载宿主机的文件或目录到容器内部,而 `-e` 参数设置环境变量 `CONFIG_PROFILE` 的值为 `'docker'`。最后,`$HIBERNATE_CFG_XML` 和 `$WALLET` 分别指向了宿主机上的文件路径,这些路径会被映射到容器内的指定路径中。`frontend` 则指定了要运行的镜像名称。 在我们准备在 Kubernetes 集群中运行时,只需将配置设置成环境变量,比如: 比如可以设置为环境变量。
kind: Deployment apiVersion: apps/v1 metadata: name: 前端 namespace: todo spec: 副本数: 1 选择器: 匹配标签: app: 前端 模板: metadata: 标签: app: 前端 version: v1 spec: 容器: - 名称: 前端 image: ocir.<region>.oci.oraclecloud.com/<tenancy_namespace>/todo/frontend:v3 环境变量: - 名称: "CONFIG_PROFILE" 值: "k8s" 拉取策略: Always 端口: - 容器端口: 7001 镜像拉取密钥: - 名称: ocir-secret
一旦配置好基本设置,只需根据不同的环境调整这些设置的位置即可。 ## 概述 在这篇文章中,我们简要但必要地讨论了如何在 Kubernetes 集群中构建容器镜像。我们探讨了如何安全地在各个开发阶段中包含 Hibernate 配置,以及如何根据 Helidon 的配置文件来调整配置。 希望你喜欢这篇文章,敬请期待下一期。 我想感谢我的同事 Tim Middleton、Sherwood Zern 和(罗曼·格雷库尔)Romain Grecourt,他们帮助了这篇文章的完成。