HBase + TestContainers - 端口重映射
HBase + TestContainers - Port Remapping
我正在尝试使用 Test Containers 到 运行 针对在 Docker 容器中启动的 HBase 的集成测试。我 运行 遇到的问题可能与客户端与 HBase 交互的方式有点独特。
当 HBase Master 在容器中启动时,它会将其 hostname:port 存储在 Zookeeper 中,以便客户端可以找到它。在这种情况下,它存储 "localhost:16000".
在我的测试用例 运行ning 外部容器中,客户端从 Zookeeper 检索 "localhost:16000" 并且无法连接。连接失败,因为端口已被 TestContainers 重新映射到其他随机端口,而不是 16000。
有什么办法可以克服这个问题吗?
(1) 一个想法是找到一种方法告诉 HBase 客户端使用重新映射的端口,忽略它从 Zookeeper 检索到的值,但我还没有找到一种方法来做到这一点。
(2) 如果我能让 HBase Master 在 Zookeeper 中编写外部可访问的 host:port 也可以解决问题。但我不相信容器本身对测试容器如何进行端口重新映射有任何了解。
(3) 也许 Test Containers 为这种情况提供了不同的解决方案?
你可以看看KafkaContainer的实现,我们首先启动一个Socat(快速tcp代理)容器来获取一个半随机端口,然后用它来配置目标容器。
算法是:
- 在
doStart
中,首先启动 Socat,目标是原始容器的网络别名和端口,如 12345
- 获取映射端口(类似于 32109 指向 12345)
- 使原始容器(例如带有环境变量的容器)在原始容器之外使用映射端口,或者,如果只能配置一个端口,请参阅CouchbaseContainer 了解更高级的选项
- Return Socat 的主机和客户端端口
我们构建了一个新的 hbase 镜像以兼容测试容器。
使用这张图片:
docker 运行 --env HBASE_MASTER_PORT=16000 --env HBASE_REGION_PORT=16020 jcjabouille/hbase-standalone:2.4.9
然后创建这个 Container(这里用 scala)
private[test] class GenericHbase2Container
extends GenericContainer[GenericHbase2Container](
DockerImageName.parse("jcjabouille/hbase-standalone:2.4.9")
) {
private val randomMasterPort: Int = FreePortFinder.findFreeLocalPort(18000)
private val randomRegionPort: Int = FreePortFinder.findFreeLocalPort(20000)
private val hostName: String = InetAddress.getLocalHost.getHostName
val hbase2Configuration: Configuration = HBaseConfiguration.create
addExposedPort(randomMasterPort)
addExposedPort(randomRegionPort)
addExposedPort(2181)
withCreateContainerCmdModifier { cmd: CreateContainerCmd =>
cmd.withHostName(hostName)
()
}
waitingFor(Wait.forLogMessage(".*0 row.*", 1))
withStartupTimeout(Duration.ofMinutes(10))
withEnv("HBASE_MASTER_PORT", randomMasterPort.toString)
withEnv("HBASE_REGION_PORT", randomRegionPort.toString)
setPortBindings(Seq(s"$randomMasterPort:$randomMasterPort", s"$randomRegionPort:$randomRegionPort").asJava)
override protected def doStart(): Unit = {
super.doStart()
hbase2Configuration.set("hbase.client.pause", "200")
hbase2Configuration.set("hbase.client.retries.number", "10")
hbase2Configuration.set("hbase.rpc.timeout", "3000")
hbase2Configuration.set("hbase.client.operation.timeout", "3000")
hbase2Configuration.set("hbase.client.scanner.timeout.period", "10000")
hbase2Configuration.set("zookeeper.session.timeout", "10000")
hbase2Configuration.set("hbase.zookeeper.quorum", "localhost")
hbase2Configuration.set("hbase.zookeeper.property.clientPort", getMappedPort(2181).toString)
}
}
此处有更多详细信息:https://hub.docker.com/r/jcjabouille/hbase-standalone
我正在尝试使用 Test Containers 到 运行 针对在 Docker 容器中启动的 HBase 的集成测试。我 运行 遇到的问题可能与客户端与 HBase 交互的方式有点独特。
当 HBase Master 在容器中启动时,它会将其 hostname:port 存储在 Zookeeper 中,以便客户端可以找到它。在这种情况下,它存储 "localhost:16000".
在我的测试用例 运行ning 外部容器中,客户端从 Zookeeper 检索 "localhost:16000" 并且无法连接。连接失败,因为端口已被 TestContainers 重新映射到其他随机端口,而不是 16000。
有什么办法可以克服这个问题吗?
(1) 一个想法是找到一种方法告诉 HBase 客户端使用重新映射的端口,忽略它从 Zookeeper 检索到的值,但我还没有找到一种方法来做到这一点。
(2) 如果我能让 HBase Master 在 Zookeeper 中编写外部可访问的 host:port 也可以解决问题。但我不相信容器本身对测试容器如何进行端口重新映射有任何了解。
(3) 也许 Test Containers 为这种情况提供了不同的解决方案?
你可以看看KafkaContainer的实现,我们首先启动一个Socat(快速tcp代理)容器来获取一个半随机端口,然后用它来配置目标容器。
算法是:
- 在
doStart
中,首先启动 Socat,目标是原始容器的网络别名和端口,如 12345 - 获取映射端口(类似于 32109 指向 12345)
- 使原始容器(例如带有环境变量的容器)在原始容器之外使用映射端口,或者,如果只能配置一个端口,请参阅CouchbaseContainer 了解更高级的选项
- Return Socat 的主机和客户端端口
我们构建了一个新的 hbase 镜像以兼容测试容器。
使用这张图片:
docker 运行 --env HBASE_MASTER_PORT=16000 --env HBASE_REGION_PORT=16020 jcjabouille/hbase-standalone:2.4.9
然后创建这个 Container(这里用 scala)
private[test] class GenericHbase2Container
extends GenericContainer[GenericHbase2Container](
DockerImageName.parse("jcjabouille/hbase-standalone:2.4.9")
) {
private val randomMasterPort: Int = FreePortFinder.findFreeLocalPort(18000)
private val randomRegionPort: Int = FreePortFinder.findFreeLocalPort(20000)
private val hostName: String = InetAddress.getLocalHost.getHostName
val hbase2Configuration: Configuration = HBaseConfiguration.create
addExposedPort(randomMasterPort)
addExposedPort(randomRegionPort)
addExposedPort(2181)
withCreateContainerCmdModifier { cmd: CreateContainerCmd =>
cmd.withHostName(hostName)
()
}
waitingFor(Wait.forLogMessage(".*0 row.*", 1))
withStartupTimeout(Duration.ofMinutes(10))
withEnv("HBASE_MASTER_PORT", randomMasterPort.toString)
withEnv("HBASE_REGION_PORT", randomRegionPort.toString)
setPortBindings(Seq(s"$randomMasterPort:$randomMasterPort", s"$randomRegionPort:$randomRegionPort").asJava)
override protected def doStart(): Unit = {
super.doStart()
hbase2Configuration.set("hbase.client.pause", "200")
hbase2Configuration.set("hbase.client.retries.number", "10")
hbase2Configuration.set("hbase.rpc.timeout", "3000")
hbase2Configuration.set("hbase.client.operation.timeout", "3000")
hbase2Configuration.set("hbase.client.scanner.timeout.period", "10000")
hbase2Configuration.set("zookeeper.session.timeout", "10000")
hbase2Configuration.set("hbase.zookeeper.quorum", "localhost")
hbase2Configuration.set("hbase.zookeeper.property.clientPort", getMappedPort(2181).toString)
}
}
此处有更多详细信息:https://hub.docker.com/r/jcjabouille/hbase-standalone