我无法获得使用 Nix 构建测试容器的基本 Spring Boot 应用程序,因为构建时间依赖于工作 docker 环境。 我将我的努力投入到我的 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;
};
};
};
我的薄片看起来像这样:
{
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)
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 构建依赖项?
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。