Я не могу получить базовое приложение Spring Boot с тестовыми контейнерами, созданными с использованием Nix, из-за зависимости времени сборки от рабочей среды докера. Я вложил все свои усилия в свой GitHub, чтобы с этого момента сохранить свои знания.
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)
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)?
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 и, возможно, полезными сообщениями в блоге, такими как вот эта.