簡體   English   中英

Testcontainers,Docker in Docker with custom.network,容器不屬於.network

[英]Testcontainers, Docker in Docker with custom network, Containers don't belong to network

我正在嘗試使用 Docker 構建器映像讓 Testcontainers 在 TeamCity 上運行。

測試在本地運行良好(不在構建器映像內)。 並且僅部分位於 TeamCity 上的構建器映像中。 我遵循了 DinD 上的指南,但沒有關於 docker.network如何發揮作用的示例。

我們在 TeamCity 中開始構建的方式(注意 -.network 參數,ryuk 由於存在連接問題而被禁用):

docker network create --driver bridge custom_network

docker run --rm -it -v $PWD:$PWD -w $PWD \
  --privileged \
  --network=custom_network \
  -e TESTCONTAINERS_RYUK_DISABLED=true \
  -e _JAVA_OPTIONS="" \
  -e DOCKER_HOST="unix:///var/run/docker.sock" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /home/teamcity/.docker:/home/java/.docker
  -v /local/maven/cache/repository:/opt/m2/repository \
  registry.ch/java:11-builder \
  mvn verify

構建運行非常正常:junit 測試開始,下載我們使用的自定義 oracle-xe 映像,日志表明它已啟動。 但在本地我可以看到 testcontainers 正在輪詢以創建連接,在 TeamCity 上構建只是繼續並遇到錯誤:

[14:01:07] :     [Step 3/3] 14:01:07.006 [tc-okhttp-stream-276714561] DEBUG com.github.dockerjava.core.command.PullImageResultCallback - ResponseItem(stream=null, status=Extracting, progressDetail=ResponseItem.ProgressDetail(current=625569807, total=625569807, start=null), progress=[==================================================>]  625.6MB/625.6MB, id=2538d1d7e815, from=null, time=null, errorDetail=null, error=null, aux=null)
[14:01:07] :     [Step 3/3] 14:01:07.211 [tc-okhttp-stream-276714561] DEBUG com.github.dockerjava.core.command.PullImageResultCallback - ResponseItem(stream=null, status=Pull complete, progressDetail=ResponseItem.ProgressDetail(current=null, total=null, start=null), progress=null, id=2538d1d7e815, from=null, time=null, errorDetail=null, error=null, aux=null)
...
[14:01:07] :     [Step 3/3] 14:01:07.228 [tc-okhttp-stream-276714561] INFO    [registry/private/oracle/database:18c_xe] - Pull complete. 2 layers, pulled in 46s (downloaded 637 MB at 13 MB/s)
[14:01:07] :     [Step 3/3] 14:01:07.228 [main] DEBUG com.github.dockerjava.core.command.AbstrDockerCmd - Cmd: registry/private/oracle/database:18c_xe
...
[14:01:07]i:     [Step 3/3] Docker event: {"status":"pull","id":"registry/private/oracle/database:18c_xe","Type":"image","Action":"pull","Actor":{"ID":"registry/private/oracle/database:18c_xe","Attributes":{"name":"registry/private/oracle/database"}},"scope":"local","time":1588075267,"timeNano":1588075267227817791}
...
[14:01:08] :     [Step 3/3]  :: Spring Boot ::        (v2.2.6.RELEASE)
[14:01:08] :     [Step 3/3] 
[14:01:08] :     [Step 3/3] 2020-04-28 14:01:08.502 ERROR 47 --- [           main] o.s.boot.SpringApplication               : Application run failed
[14:01:08] :     [Step 3/3] 
[14:01:08] :     [Step 3/3] java.lang.IllegalStateException: Mapped port can only be obtained after the container is started
[14:01:08] :     [Step 3/3]     at org.testcontainers.shaded.com.google.common.base.Preconditions.checkState(Preconditions.java:174) ~[testcontainers-1.14.1.jar:na]
[14:01:08] :     [Step 3/3]     at org.testcontainers.containers.ContainerState.getMappedPort(ContainerState.java:129) ~[testcontainers-1.14.1.jar:na]
[14:01:08] :     [Step 3/3]     at org.testcontainers.containers.OracleContainer.getOraclePort(OracleContainer.java:95) ~[oracle-xe-1.14.1.jar:na]
[14:01:08] :     [Step 3/3]     at org.testcontainers.containers.OracleContainer.getJdbcUrl(OracleContainer.java:64) ~[oracle-xe-1.14.1.jar:na]
[14:01:08] :     [Step 3/3]     at ch.package.OracleFlywayDatabaseTest$Initializer.initialize(OracleFlywayDatabaseTest.java:35) ~[test-classes/:na]
[14:01:08] :     [Step 3/3]     at org.springframework.boot.SpringApplication.applyInitializers(SpringApplication.java:626) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
...
[14:01:08] :     [Step 3/3]     at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) ~[junit-platform-launcher-1.3.1.jar:1.3.1]
...
[14:01:08] :     [Step 3/3]     at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:418) ~[surefire-booter-2.22.2.jar:2.22.2]
[14:01:08] :     [Step 3/3] 
...
[14:01:08] :     [Step 3/3] org.testcontainers.containers.ContainerLaunchException: Container startup failed
[14:01:08] :     [Step 3/3] Caused by: org.testcontainers.containers.ContainerFetchException: Can't get Docker image: RemoteDockerImage(imageName=registry/private/oracle/database:18c_xe, imagePullPolicy=DefaultPullPolicy())
[14:01:08] :     [Step 3/3] Caused by: java.time.format.DateTimeParseException: Text '2020-03-04T15:17:25.025952651+01:00' could not be parsed at index 29
[14:01:08] :     [Step 3/3] 

我不確定最后一個異常,它似乎還不錯,問題似乎是我們啟動的 oracle 容器“不可見”。 DateTimeParseException 中的日期是我們的注冊表中的 oracle-xe 映像的創建日期。

我也嘗試使用構建器上的withNetwork選項創建容器:

@Testcontainers
public abstract class OracleFlywayDatabaseTest {

  @Container
  private static final OracleContainer oracle =
        new OracleContainer("registry/private/oracle/database:18c_xe")
                // .withNetwork(Network.builder().id("custom_network").build())
                .withUsername("TESTUSR")
                .withPassword("TESTPWD");

如果我使用docker.network inspect custom.network在本地調查此問題,則由 Testcontainers 啟動的數據庫容器不在 that.network 中。

將容器放入那個網絡的正確方法是什么? 意味着構建器鏡像最初啟動的是同一個網絡嗎? id真的是docker創建時分配給.network的id嗎? (我試過了,但也許我做錯了什么)。

我們已經找到了一種方法來完成這項工作。 這不是人們所說的美麗......但這是目前有效的:

使用 docker 命令創建自定義網絡(“custom_nework”是本示例中的網絡名稱):

docker network ls|grep custom_network > /dev/null || docker network create --driver bridge custom_network

然后確定該網絡的 id:

network_id=`docker network inspect custom_network --format "{{.ID}}"`

並且設置是和環境變量。

在 Testcontainers 測試中,您現在可以通過以下方式引用此網絡:(在 IDE 的本地環境中,當您只運行一次測試時,我們必須區分是否有自定義網絡(CI 服務器),或者是否沒有(IDE) )

import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.BeforeAll;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.testcontainers.containers.OracleContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
@ContextConfiguration(initializers = OracleFlywayDatabaseTest.Initializer.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public abstract class OracleFlywayDatabaseTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(OracleFlywayDatabaseTest.class);

    private static final String NETWORK_ID = "NETWORK_ID";

    @Container
    protected static final OracleContainer oracle;

    static {
        String networkId = System.getenv(NETWORK_ID);
        if (StringUtils.isBlank(networkId)) {
            oracle = new OracleContainer("diemobiliar/minimized-oraclexe-image:18.4.0-xe");
        } else {
            oracle = new NetworkOracleContainer("diemobiliar/minimized-oraclexe-image:18.4.0-xe", networkId);
        }
        oracle.withUsername("AOO_TESTS").withPassword("AOO_TESTS");
    }

    @BeforeAll
    public static void setupOracle() {
        LOGGER.info("ORACLE 18 JDBC URL: " + oracle.getJdbcUrl());
    }

    public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

        @Override
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            TestPropertyValues.of("spring.datasource.platform=" + "ORACLE", //
                    "spring.datasource.url=" + oracle.getJdbcUrl(), //
                    "spring.datasource.username=" + oracle.getUsername(), //
                    "spring.datasource.password=" + oracle.getPassword()) //
                    .applyTo(configurableApplicationContext.getEnvironment());
            LOGGER.info("spring.datasource. Properties set.");
        }
    }

}

和幫助 NetworkOracleContainer class:

import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.testcontainers.containers.Network;

public class NetworkOracleContainer extends LocalOracleContainer {

    private static final String CONTAINER_NAME = "oracle";

    public NetworkOracleContainer(String dockerImageName, String networkId) {
        super(dockerImageName);
        this.withNetwork(new ExistingNetwork(networkId))
                .withCreateContainerCmdModifier(cmd -> cmd.withName(CONTAINER_NAME));
    }

    @Override
    public String getHost() {
        return CONTAINER_NAME;
    }

    @Override
    public Integer getOraclePort() {
        return 1521;
    }

    private static class ExistingNetwork implements Network {

        private final String networkId;

        ExistingNetwork(String networkId) {
            this.networkId = networkId;
        }

        @Override
        public String getId() {
            return networkId;
        }

        @Override
        public void close() {
            // noop
        }

        @Override
        public Statement apply(Statement base, Description description) {
            return base;
        }
    }

}

我們還沒有在 Testcontainers API 中找到更好的方法來執行此操作。 也許在較新的版本中(目前為 1.14.3)

這是我在 TeamCity 代理中針對 org.testcontainers:testcontainers:1.16.3 的解決方案,而 TeamCity 代理又是一個 docker 容器本身。

var agentName = System.getenv("AGENT_NAME");
GenericContainer container = ...;
container.withNetwork(new ExistingNetwork("tagent-docker_default"))
    .withCreateContainerCmdModifier(cmd -> cmd.withHostName("myoracle-" + agentName));
// you don't even need to expose 1521 port
// set other container params. waitingFor, etc
container.start();

然后使用"jdbc:oracle:thin:@myoracle-" + agentName + ":1521/ORCLPDB1"
PS: ruyk在,不用關掉
幸好ExistingNetwork借用了wemu的答案

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM