现在我们的应用已经跑起来了,我们最终希望把它部署到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)处理数据库的一致性涉及两个概念,
- 一个 CacheStore,如我们上面提到的 HibernateCacheStore,它把数据写入数据库。
- 一个 CacheLoader 使 Coherence 集群能够从数据库加载现有数据到缓存。
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,他们帮助了这篇文章的完成。