由于 Docker 构建时间依赖,无法使用 Nix 构建带有 Testcontainers 的 Spring Boot 应用程序

问题描述 投票:0回答:1

我无法获得使用 Nix 构建测试容器的基本 Spring Boot 应用程序,因为构建时间依赖于工作 docker 环境。 我将我的努力投入到我的 GitHub,以便从现在开始保存我的学习成果。

我的目标

  • 使用Java 17
  • 春季启动
  • 自定义 JRE(尽可能小)
  • 用于与 JUnit 集成测试的测试容器
  • aarch64 Docker 镜像正在构建

我的设置

  • MacBook Pro 16 英寸 2021 年;M1 Max;32 GB 内存
  • macOS 索诺玛 14.4
  • nix-darwin 设置
  • linux-builder 已启用:
linux-builder = {
    enable = true;
    ephemeral = true;
    maxJobs = 4;
    config = {
      nix.settings.sandbox = false; # cannot get it working in a pure fashion
      networking = {
        nameservers = [ "8.8.8.8" "1.1.1.1" ];
      };
      virtualisation = {
        darwin-builder = {
          diskSize = 40 * 1024;
          memorySize = 8 * 1024;
        };
        docker = {
          enable = true;
          rootless = {
            enable = true;
            setSocketVariable = true;
          };
        };
        cores = 6;
      };
    };
  };

建筑

我的薄片看起来像这样:

{
  description = "Inventory Backend Flake";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils, ... }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
        };
        version = "0.0.1-SNAPSHOT";
        inventory-jre = pkgs.stdenv.mkDerivation {
          name = "inventory-jre";
          buildInputs = [ pkgs.openjdk17 ];
          src = self;
          buildPhase = ''
            jlink --add-modules java.base,java.xml --output custom-jre
          '';
          installPhase = ''
            mkdir -p $out
            cp -r custom-jre/* $out/
            chmod +x $out/bin/*
          '';
        };
        application = pkgs.stdenv.mkDerivation {
          # disabling sandbox
          __noChroot = true;
          name = "inventory-backend";
          src = self;
          version = version;
          buildInputs = [ pkgs.openjdk17 ];

          buildPhase = ''
            export GRADLE_USER_HOME=$(mktemp -d)
            chmod +x ./gradlew
            ./gradlew clean build --info
          '';

          installPhase = ''
            mkdir -p $out
            cp -r build/libs/inventory-backend-${version}.jar $out/
          '';
        };

        dockerImage = pkgs.dockerTools.buildImage {
          name = "inventory-backend";
          tag = "latest";
          created = builtins.substring 0 8 self.lastModifiedDate;
          copyToRoot = [application inventory-jre];

          config = {
            Cmd = [ "${inventory-jre}/bin/java" "-jar" "${application}/inventory-${version}.jar" ];
            ExposedPorts = {
              "8080/tcp" = {};
            };
            Volumes = {
              "/tmp" = {};
            };
          };
        };
      in {
        devShells.default = pkgs.mkShell {
          buildInputs = [ pkgs.openjdk17 ];
        };

        packages.default = application;

        packages.dockerImage = dockerImage;

        defaultPackage = self.packages.default;

      }
    );
}

为了构建 docker 镜像,我正在运行

nix build -vvv .#packages.aarch64-linux.dockerImage --print-out-paths
,因为我想创建一个在 aarch64-linux docker 上运行的 docker 镜像。

错误

尝试按照上述方式构建镜像,实际上失败了,因为在为 aarch64 构建时,Testcontainers 找不到有效的 Docker 环境。

即使向我的 linux-builder 添加一个工作的 docker 环境也没有成功。

错误日志:

 2024-03-18T14:39:38.826Z ERROR 3789 --- [    Test worker] o.t.d.DockerClientProviderStrategy       : Could not find a valid Docker environment. Please check configuration. Attempted configurations were:
    As no valid configuration was found, execution cannot continue.
    See https://www.testcontainers.org/on_failure.html for more details.
    2024-03-18T14:39:39.831Z ERROR 3789 --- [    Test worker] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Exception during pool initialization.

    java.lang.IllegalStateException: Could not find a valid Docker environment. Please see logs and check configuration
        at org.testcontainers.dockerclient.DockerClientProviderStrategy.lambda$getFirstValidStrategy$7(DockerClientProviderStrategy.java:256)
        at java.base/java.util.Optional.orElseThrow(Optional.java:403)
        at org.testcontainers.dockerclient.DockerClientProviderStrategy.getFirstValidStrategy(DockerClientProviderStrategy.java:247)
        at org.testcontainers.DockerClientFactory.getOrInitializeStrategy(DockerClientFactory.java:150)
        at org.testcontainers.DockerClientFactory.client(DockerClientFactory.java:186)
        at org.testcontainers.DockerClientFactory$1.getDockerClient(DockerClientFactory.java:104)
        at com.github.dockerjava.api.DockerClientDelegate.authConfig(DockerClientDelegate.java:108)
        at org.testcontainers.containers.GenericContainer.start(GenericContainer.java:321)
        at org.testcontainers.jdbc.ContainerDatabaseDriver.connect(ContainerDatabaseDriver.java:134)
        at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138)
        at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:359)
        at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:201)
        at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:470)
        at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:561)
        at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:100)
        at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112)

我所知道的

  • Testcontainers 在运行我的项目的 JUnit 测试时会启动 docker 容器。
  • 跳过测试不是可行的选择
  • 放弃测试容器目前不是一个可行的选择
  • 测试容器寻找
    DOCKER_HOST

我的 linux-builder 有一个有效的

DOCKER_HOST
和一个有效的 docker 设置:

[builder@nixos:~]$ echo $DOCKER_HOST
unix:///run/user/1000/docker.sock

但是日志中写着

WARN 3789 --- [    Test worker] o.t.d.DockerClientProviderStrategy       : DOCKER_HOST unix:///var/run/docker.sock is not listening

所以我想,它使用单独的环境。


如何在 Nix(OS) 中正确设置 docker 构建依赖项?

java docker gradle testcontainers nix
1个回答
0
投票

Nix Flakes 确实提供了通过将派生放入

checks
属性中并以
nix flake check
运行它们来添加测试的可能性。 因此,我在构建过程中跳过 Gradle 测试,而是将它们作为检查运行,这似乎是解决该问题的更正确方法。

它可能还不完美(我将不胜感激),但我的 Flake 现在看起来像这样:

{
  description = "Inventory Backend Flake";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";
  };

  outputs = { self, nixpkgs, flake-utils, ... }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
        };
        version = "0.0.1-SNAPSHOT";
        inventory-jre = pkgs.stdenv.mkDerivation {
          name = "inventory-jre";
          buildInputs = [ pkgs.openjdk17 ];
          src = self;
          buildPhase = ''
            jlink --add-modules java.base,java.xml --output custom-jre
          '';
          installPhase = ''
            mkdir -p $out
            cp -r custom-jre/* $out/
            chmod +x $out/bin/*
          '';
        };
        applicationSource = pkgs.stdenv.mkDerivation {
          name = "inventory-backend-src";
          src = self;
          version = version;
          installPhase = ''
            mkdir -p $out
            cp -r ./* $out/
          '';
        };
        application = pkgs.stdenv.mkDerivation {
          # disabling sandbox
          __noChroot = true;
          name = "inventory-backend";
          version = version;
          buildInputs = [ pkgs.openjdk17 ];

          buildPhase = ''
            export GRADLE_USER_HOME=$(mktemp -d)
            chmod +x ./gradlew
            ./gradlew clean build --info
          '';

          installPhase = ''
            mkdir -p $out
            cp -r build/libs/inventory-backend-${version}.jar $out/
          '';
        };

        dockerImage = pkgs.dockerTools.buildImage {
          name = "inventory-backend";
          tag = "latest";
          created = builtins.substring 0 8 self.lastModifiedDate;
          copyToRoot = [application inventory-jre];

          config = {
            Cmd = [ "${inventory-jre}/bin/java" "-jar" "${application}/inventory-${version}.jar" ];
            ExposedPorts = {
              "8080/tcp" = {};
            };
            Volumes = {
              "/tmp" = {};
            };
          };
        };
      in {
        devShells.default = pkgs.mkShell {
          buildInputs = [ pkgs.openjdk17 ];
        };

        packages.default = application;

        packages.dockerImage = dockerImage;

        checks.gradletests = pkgs.testers.runNixOSTest {
          name = "Gradle Test: Inventory Backend Stub";

          nodes = {
            machine1 = { pkgs, ... }: {
              environment.systemPackages = [pkgs.openjdk17 applicationSource];
              nix.settings.sandbox = false;
              virtualisation.docker.enable = true;

              virtualisation.memorySize = 2 * 1024;
              virtualisation.msize = 128 * 1024;
              virtualisation.cores = 2;
           };
         };

         testScript = ''
           machine1.wait_for_unit("network-online.target")
           machine1.execute("cp -r ${applicationSource}/* ${applicationSource}/.* .")
           machine1.execute("java -version")
           machine1.succeed("./gradlew test --no-daemon --debug");
         '';
       };
      }
    );
}

靠近底部,您可以在属性

checks.gradletests
中找到测试定义。它本质上只是在 qemu-kvm 运行的 NixOS-VM 中运行
./gradlew test --no-daemon --debug
。因为我基本上可以像每个 NixOS-Host 一样定义 NixOS-VM,所以我可以简单地使用一行代码启用 docker。

另请查看 NixOS 集成测试 的参考资料,以及可能有用的博客文章,例如 this one

© www.soinside.com 2019 - 2024. All rights reserved.