如何防止 github.com/ory/dockertest 将容器分配给随机端口?
Posted
技术标签:
【中文标题】如何防止 github.com/ory/dockertest 将容器分配给随机端口?【英文标题】:How to prevent github.com/ory/dockertest from assigning containers to random ports? 【发布时间】:2020-11-06 15:43:25 【问题描述】:我正在尝试编写使用github.com/ory/dockertest
在本地运行和使用Docker executor 类型在CircleCI 环境(其中设置了“CI”环境变量)中运行的单元测试。在容器中,我想使用google/cloud-sdk
图像运行Google Pub/Sub emulator。
作为一个简化的例子,我编写了这个 Go 程序:
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"os"
"time"
"cloud.google.com/go/pubsub"
"github.com/ory/dockertest"
"github.com/ory/dockertest/docker"
"google.golang.org/api/iterator"
)
var pubsubEmulatorHost string
func main()
flag.StringVar(&pubsubEmulatorHost, "pubsubEmulatorHost", "localhost:8085", "Google Pub/Sub emulator host")
flag.Parse()
if os.Getenv("CI") == ""
pool, err := dockertest.NewPool("")
if err != nil
log.Fatalf("Could not connect to Docker: %v", err)
opts := &dockertest.RunOptions
Hostname: "localhost",
Repository: "google/cloud-sdk",
Cmd: []string"gcloud", "beta", "emulators", "pubsub", "start", "--host-port", "127.0.0.1:8085",
ExposedPorts: []string"8085",
PortBindings: map[docker.Port][]docker.PortBinding
"8085/tcp": HostIP: "127.0.0.1", HostPort: "8085/tcp",
,
resource, err := pool.RunWithOptions(opts)
if err != nil
log.Fatalf("Could not start resource: %v", err)
pool.MaxWait = 10 * time.Second
if err := pool.Retry(func() error
_, err := net.Dial("tcp", "localhost:8085")
return err
); err != nil
log.Fatalf("Could not dial the Pub/Sub emulator: %v", err)
defer func()
if err := pool.Purge(resource); err != nil
log.Fatalf("Could not purge resource: %v", err)
()
os.Setenv("PUBSUB_EMULATOR_HOST", pubsubEmulatorHost)
defer os.Unsetenv("PUBSUB_EMULATOR_HOST")
client, err := pubsub.NewClient(context.Background(), "my-project")
if err != nil
log.Fatalf("NewClient: %v", err)
topic, err := client.CreateTopic(context.Background(), "my-topic")
if err != nil
log.Fatalf("CreateTopic: %v", err)
log.Println("Created topic:", topic)
topicIterator := client.Topics(context.Background())
for
topic, err := topicIterator.Next()
if err == iterator.Done
break
if err != nil
log.Fatalf("Next: %v", err)
fmt.Printf("%s\n", topic)
首先,我验证了在从命令行运行容器后将CI
环境变量设置为非空值运行它会产生预期的结果:
>
docker run -p "8085:8085" google/cloud-sdk gcloud beta emulators pubsub start --host-port=0.0.0.0:8085
Executing: /usr/lib/google-cloud-sdk/platform/pubsub-emulator/bin/cloud-pubsub-emulator --host=0.0.0.0 --port=8085
[pubsub] This is the Google Pub/Sub fake.
[pubsub] Implementation may be incomplete or differ from the real system.
[pubsub] Jul 16, 2020 9:21:33 PM com.google.cloud.pubsub.testing.v1.Main main
[pubsub] INFO: IAM integration is disabled. IAM policy methods and ACL checks are not supported
[pubsub] Jul 16, 2020 9:21:34 PM io.gapi.emulators.netty.NettyUtil applyJava7LongHostnameWorkaround
[pubsub] INFO: Applied Java 7 long hostname workaround.
[pubsub] Jul 16, 2020 9:21:34 PM com.google.cloud.pubsub.testing.v1.Main main
[pubsub] INFO: Server started, listening on 8085
紧随其后
> env CI=true go run main.go
2020/07/16 14:22:01 Created topic: projects/my-project/topics/my-topic
projects/my-project/topics/my-topic
注意此时容器上的8085端口如预期映射到主机上的8085端口:
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
76724696f9d9 google/cloud-sdk "gcloud beta emulato…" 55 seconds ago Up 54 seconds 0.0.0.0:8085->8085/tcp epic_ganguly
我不想在不设置CI
环境变量的情况下停止容器并运行程序,应该注意自动启动容器。然而,我观察到的是尝试建立连接时超时:
> go run main.go
2020/07/16 14:23:56 Could not dial the Pub/Sub emulator: dial tcp [::1]:8085: connect: connection refused
exit status 1
在检查容器时,它似乎映射到本地端口32778
而不是8085
:
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0df07ac232d5 google/cloud-sdk:latest "gcloud beta emulato…" 34 seconds ago Up 33 seconds 0.0.0.0:32778->8085/tcp wizardly_ptolemy
我认为像上面那样在RunOptions
中指定PortBindings
应该将容器上的端口8085 映射到主机上的端口8085,但似乎情况并非如此。有谁知道使该程序正常运行的正确运行选项?
【问题讨论】:
【参考方案1】:Dockertest 允许您使用resource.GetPort()
检索容器的映射端口,您可以使用它来将pubsubEmulatorHost
设置为正确的值:
port := "8085"
if os.Getenv("CI") == ""
// ...
pubsubEmulatorHost = opts.Hostname + resource.GetPort("8085/tcp")
// pubsubEmulatorHost = "localhost:32778"
// ...
【讨论】:
这种方法的问题是我必须让PUBSUB_EMULATOR_HOST
环境匹配随机生成的端口,而在 CircleCI 环境中它将是一个固定端口。这会使逻辑更加复杂;理想情况下,我想映射到一个固定端口,就像在 CircleCI Docker 执行器中一样。
我已经更新了我的答案以更好地回答你的问题,但如果这对你不起作用,那可能只是一个错误。但是,我不认为使用固定端口是你应该做的事情,最好是动态地使用健全的默认值,而不是静态的并且灾难性地失败。以上是关于如何防止 github.com/ory/dockertest 将容器分配给随机端口?的主要内容,如果未能解决你的问题,请参考以下文章