Невозможно создать приложение Spring Boot с тестовыми контейнерами с использованием Nix из-за зависимости времени сборки Docker

Я не могу получить базовое приложение Spring Boot с тестовыми контейнерами, созданными с использованием Nix, из-за зависимости времени сборки от рабочей среды докера. Я вложил все свои усилия в свой GitHub, чтобы с этого момента сохранить свои знания.

Моя цель

  • Использование Java 17
  • Весенние ботинки
  • Пользовательская JRE (как можно меньше)
  • Тестовые контейнеры для интеграционного тестирования с JUnit
  • aarch64 Создается образ Docker

Моя установка

  • MacBook Pro 16 дюймов, 2021 г.; M1 Max; 32 ГБ ОЗУ
  • macOS Сонома 14.4
  • Настройка nix-darwin
  • Linux-строитель включен:
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;
      };
    };
  };

Здание

Мой 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/*
          '';
        };
        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;

      }
    );
}

Чтобы создать образ докера, я запускаю nix build -vvv .#packages.aarch64-linux.dockerImage --print-out-paths, потому что я хочу создать образ докера для запуска в докере aarch64-linux.

Ошибка

Попытка собрать образ, как описано выше, на самом деле терпит неудачу, поскольку Testcontainers не находит действительную среду Docker при сборке для aarch64.

Даже добавление рабочей среды докера в мой linux-builder не помогло.

Журнал ошибок:

 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_HOST

Мой linux-builder имеет действительную DOCKER_HOST и рабочую настройку докера:

[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)?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
173
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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. По сути, он просто работает ./gradlew test --no-daemon --debug на NixOS-VM, управляемой qemu-kvm. Поскольку я могу определить NixOS-VM, по сути, так же, как и любой NixOS-Host, я могу просто включить Docker с помощью одной строки.

Также ознакомьтесь со ссылкой на Интеграционные тесты NixOS и, возможно, полезными сообщениями в блоге, такими как вот эта.

Другие вопросы по теме