You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在NixOS Docker镜像中配置可调用的Nix包(如file工具)

在NixOS Docker镜像中集成file工具并让可执行文件调用它的解决方案

咱们先拆解一下你遇到的问题:你已经用Nix的dockerTools构建了镜像,把预编译的foobar放进去了,但它调用file工具识别MIME类型时失败,之前用buildInputs的方式没生效。本质原因是**buildInputs主要是给构建阶段提供依赖的,而不是运行时**——你得确保file工具和它的依赖在镜像运行时能被foobar找到才行。

我给你两个可行的修复方案,你可以根据自己的需求选:

方案一:用makeWrapper包装可执行文件(推荐)

这个方法最干净,直接把foobar需要的运行时环境(比如file的路径、依赖库)绑定到可执行文件本身,不需要在entrypoint里额外配置。修改你的docker.nix如下:

with import <nixpkgs> {};
let
  foobar_deriv = stdenv.mkDerivation rec {
    name = "foobar";
    builder = "${bash}/bin/bash";
    args = [ ./nix-builder.sh ];
    inherit coreutils openssl libyaml;
    system = builtins.currentSystem;
    schemapath = ../../schemas;
    foobarpath = ./foobar;
    # 加入makeWrapper用来包装可执行文件,保留buildInputs供构建使用
    buildInputs = [ pkgs.bash pkgs.file pkgs.makeWrapper ];

    installPhase = ''
      # 先把你的foobar可执行文件安装到输出目录
      mkdir -p $out/bin
      cp $foobarpath $out/bin/foobar
      chmod +x $out/bin/foobar

      # 用wrapProgram给foobar设置运行时环境:
      # 1. 把file的bin目录加到PATH,让foobar能找到file命令
      # 2. 把所有需要的依赖库路径加到LD_LIBRARY_PATH
      wrapProgram $out/bin/foobar \
        --prefix PATH : "${pkgs.file}/bin" \
        --prefix LD_LIBRARY_PATH : "${stdenv.lib.makeLibraryPath [ pkgs.openssl pkgs.libyaml pkgs.file ]}"
    '';
  };

  entrypoint = writeScript "entrypoint.sh" ''
    #!${stdenv.shell}
    exec $@
  '';
in pkgs.dockerTools.buildImage {
  name = "myaccount/foobar";
  tag = "0.3.0a11";
  created = "now";
  contents = foobar_deriv;
  config = {
    Cmd = [ "foobar" ];
    Entrypoint = [ entrypoint ];
    ExposedPorts = { "4949/tcp" = {}; };
    WorkingDir = "/";
  };
}

关键修改点:

  • 新增了pkgs.makeWrapperbuildInputs,这是Nix用来包装可执行文件、注入运行时环境的工具
  • 添加了installPhase,手动处理foobar的安装,并通过wrapProgram配置它的运行时PATH(包含file的二进制路径)和LD_LIBRARY_PATH(包含所有依赖库,包括file的依赖)
  • 去掉了之前的env buildEnv,因为wrapProgram已经帮我们搞定了运行时依赖路径的问题

方案二:直接把file工具加入镜像contents

如果你不想修改foobar的derivation,可以直接把file工具加到镜像的contents里,然后在entrypoint里配置环境变量让foobar能找到它:

with import <nixpkgs> {};
let
  foobar_deriv = stdenv.mkDerivation rec {
    name = "foobar";
    builder = "${bash}/bin/bash";
    args = [ ./nix-builder.sh ];
    inherit coreutils openssl libyaml;
    system = builtins.currentSystem;
    schemapath = ../../schemas;
    foobarpath = ./foobar;
    buildInputs = [ pkgs.bash pkgs.file ];
    env = buildEnv {
      name = name;
      paths = buildInputs;
    };
  };
  # 把file的库路径也加入LD_LIBRARY_PATH
  ld_path = stdenv.lib.makeLibraryPath [ pkgs.openssl pkgs.libyaml pkgs.file ];
  entrypoint = writeScript "entrypoint.sh" ''
    #!${stdenv.shell}
    export LD_LIBRARY_PATH=${ld_path}
    # 把file的bin目录加到系统PATH
    export PATH="${pkgs.file}/bin:$PATH"
    exec $@
  '';
in pkgs.dockerTools.buildImage {
  name = "myaccount/foobar";
  tag = "0.3.0a11";
  created = "now";
  # 把file工具直接加入镜像contents
  contents = [ foobar_deriv pkgs.file ];
  config = {
    Cmd = [ "foobar" ];
    Entrypoint = [ entrypoint ];
    ExposedPorts = { "4949/tcp" = {}; };
    WorkingDir = "/";
  };
}

这个方法的核心是:

  • pkgs.file加入contents,让镜像里存在file工具
  • 在entrypoint里把file的bin目录加到PATH,同时把file的库路径加到LD_LIBRARY_PATH

两种方案都能解决你的问题,推荐第一种,因为它把依赖和可执行文件绑定在一起,后续维护更省心。

内容的提问来源于stack exchange,提问作者James Fleming

火山引擎 最新活动