-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Layers contains duplicate dependencies #41
Comments
Actually, I think I'm wrong, I think I was misreading |
No, it does seem to contain duplicate files. I cleared docker directory and checked |
@gytis-ivaskevicius since these layers are built in non-dependent derivations, we can't remove duplicated files. There are however several way to fix this.
|
Can you explain how the second option works (creating a dependency)? I'm running into the same issue and haven't been able to wrap my head around why sometimes I get duplicate packages and sometimes I don't. Here is another example: {
inputs.nix2container.url = "github:nlewo/nix2container";
outputs = { self, nixpkgs, nix2container }:
let
pkgs = import nixpkgs { system = "aarch64-linux"; };
nix2containerPkgs = nix2container.packages.aarch64-linux;
n2c = nix2containerPkgs.nix2container;
test1 = pkgs.writeScriptBin "test1" ''
#!${pkgs.runtimeShell}
echo "Test1"
'';
test2 = pkgs.writeScriptBin "test2" ''
#!${pkgs.runtimeShell}
echo "Test2"
'';
in
{
packages.aarch64-linux.hello = n2c.buildImage {
name = "hello";
config = {
entrypoint = [ "${pkgs.hello}/bin/hello" ];
};
layers = [
(n2c.buildLayer {
deps = [ test1 ];
})
(n2c.buildLayer {
deps = [ test2 ];
})
];
};
};
} The same issue of {
inputs.nix2container.url = "github:nlewo/nix2container";
outputs = { self, nixpkgs, nix2container }:
let
pkgs = import nixpkgs { system = "aarch64-linux"; };
nix2containerPkgs = nix2container.packages.aarch64-linux;
n2c = nix2containerPkgs.nix2container;
test1 = pkgs.writeScriptBin "test1" ''
#!${pkgs.runtimeShell}
echo "Test1"
'';
test2 = pkgs.writeScriptBin "test2" ''
#!${pkgs.runtimeShell}
echo "Test2"
'';
in
{
packages.aarch64-linux.hello = n2c.buildImage {
name = "hello";
config = {
entrypoint = [ "${pkgs.hello}/bin/hello" ];
};
layers = [
(n2c.buildLayer {
deps = [ pkgs.glibc ];
layers = [
(n2c.buildLayer {
deps = [ test1 ];
})
(n2c.buildLayer {
deps = [ test2 ];
})
];
})
];
};
};
} Produces a single layer that contains everything, including the entrypoint. Is there a way to avoid this? |
Running into the same issue, this makes the layers feature a bit pointless I'm afraid. |
@jmgilman Sorry, i forgot to answer your questions:/
Considering the following example:
We are building a layer (lets call it
Hm, i'm surprise since i would expected at least 3 layers. I need to reproduce to understand what is happening. However, this is not the way it is supposed to work;) I think you would instead want something such as:
I never tried such kind of things, but i would expect to produce an image with the |
@niklaskorz if my above comment doesn't help you, do not hesitate to share your issue/usecase. |
I'll try to come up with a "smallest breaking example" case and report back :) |
@nlewo so here's my example: [click here to view] flake.nix{
description = "A very basic flake";
inputs = {
nix2container.url = "github:nlewo/nix2container";
nix2container.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, nixpkgs, nix2container }: let
build_system = "x86_64-darwin"; # update to the system you're building on
host_system = "x86_64-linux";
build_pkgs = nixpkgs.legacyPackages.${build_system};
host_pkgs = nixpkgs.legacyPackages.${host_system};
n2c = nix2container.packages.${build_system}.nix2container;
in {
packages.${build_system}.default = let
find_pkg = name: list: build_pkgs.lib.lists.findFirst (x: (x.pname or "") == name) null list;
package = host_pkgs.salt;
python = find_pkg "python3" package.nativeBuildInputs;
in n2c.buildImage {
name = "saltstack";
copyToRoot = [
(build_pkgs.buildEnv {
name = "root";
paths = [ package ];
})
];
layers = [
# (n2c.buildLayer {
# deps = [ python ];
# })
# (n2c.buildLayer {
# deps = package.propagatedBuildInputs;
# })
(n2c.buildLayer {
deps = [ python ];
layers = [
(n2c.buildLayer {
deps = package.propagatedBuildInputs;
})
];
})
];
};
devShells.${build_system}.default = with build_pkgs; mkShell {
packages = [
dive
];
};
};
} [click here to view] flake.lock (probably not needed){
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nix2container": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1688922987,
"narHash": "sha256-RnQwrCD5anqWfyDAVbfFIeU+Ha6cwt5QcIwIkaGRzQw=",
"owner": "nlewo",
"repo": "nix2container",
"rev": "ab381a7d714ebf96a83882264245dbd34f0a7ec8",
"type": "github"
},
"original": {
"owner": "nlewo",
"repo": "nix2container",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1690441914,
"narHash": "sha256-Ac+kJQ5z9MDAMyzSc0i0zJDx2i3qi9NjlW5Lz285G/I=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "db8672b8d0a2593c2405aed0c1dfa64b2a2f428f",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"nix2container": "nix2container",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
} So to summarize: container with single
|
@nlewo is there anything I could provide to help identify the problem? Or if I'm using things incorrectly, how the call should be modified to get expected results? |
I think this feature should be implemented as part of nix2container. I don't believe that there is much of a point in hacking solution on top of it due to complexity reasons |
@takeda I have been able to reproduce and i confirm there is an issue. I however didn't find time yet to dig more (i should be able to in the next 2 weeks). |
@takeda Your expression is producing an empty layer because the layer containing the nix2container should either support empty layers or explicitly fails on empty layers. I don't know yet what to do... |
Sorry, but I'm confused I assumed that How can I correctly do to achieve what I want, basically:
|
I tried reverse, i.e.: layers = [
(n2c.buildLayer {
deps = package.propagatedBuildInputs;
layers = [
(n2c.buildLayer {
deps = [ python ];
})
];
})
]; This results in just two layers:
So that still doesn't look right. |
After thinking about what I got in previous comment I tried something once more: layers = [
(n2c.buildLayer {
deps = [ python ];
})
(n2c.buildLayer {
deps = package.propagatedBuildInputs;
layers = [
(n2c.buildLayer {
deps = [ python ];
})
];
})
]; I think this works, I do see there are 2 openssl packages with different hashes, but have a suspicion that it is problem with the salt package. So it looks like I'm getting:
So I think the layers feature, while very powerful it is probably the most difficult to get feature of containers2nix I think more documentation how it works is needed. I also think that since the But this still can get complex, especially when somebody needs more layers. One has to be very cautious to exclude everything that's in previous layers. I think what I suggested in previous comment would greatly simplify the work and make it much more intuitive e.g.: layers = [
{
deps = [ python ];
}
{
deps = package.propagatedBuildInputs;
}
{
copyToRoot = pkgs.buildEnv {
name = "root";
paths = [ pkgs.bashInteractive pkgs.coreutils ];
pathsToLink = [ "/bin" ];
};
} The code then would go, and:
I believe that would be much simpler for the user and much more robust. What do you think? |
I think I got hang of it, and probably the best way is to use it with # organize the container into 3 layers:
# - base layer with python and busybox
# - layer with application dependencies
# - our application
layers = with nix2container; let
layer-1 = buildLayer {
deps = [
pkgs.busybox
config.out_lean_python
];
};
layer-2 = buildLayer {
deps = config.out_python.propagatedBuildInputs;
layers = [
layer-1
];
};
in [
layer-1
layer-2
]; I believe if I needed to add |
I stumbled upon this problem myself and wrote a little blog post about my fix: https://blog.eigenvalue.net/2023-nix2container-everything-once/ Maybe it is useful to you or other people discovering this issue! |
@kolloch Thank you very much for this post! (I think it could be linked in this nix2container readme section) |
Take the suggestion from this comment: nlewo#41 (comment) And add a link to https://blog.eigenvalue.net/2023-nix2container-everything-once/
I independently came to the same solution as @takeda, though it would be great if this could be handled more automatically by nix2container— one option could be shipping and recommending the layer-folding function that @kolloch suggested, another could be having some kind of post-processing filter step applied at- or after JSON generation that automatically removes dupes (assuming stable layer order). Another thing that would be nice to have is a way of defining a layer based on the intersection of two other layers. This could dovetail with auto-deduping, where you basically have a layer that's Django and everything below it, a separate layer that's Flask and everything below it, and then a Python-Common layer that's everything in both of those. Then no matter whether you're making a container that has Flask or Django or both, you only ever have those three layers, and the common stuff is shared— but all that only really works with automatic deduping (and it isn't really expressable at all today, short of manually discovering and enumerating the common stuff). I also proposed an idea for making a smarter automatic layering system able to detect these types of relationships between multiple containers over time, please see #113 if you're interested to weigh in on that. |
@mikepurvis you might want to look into this PR in nixpkgs. The example layer algo\pipeline linked from PR description works perfectly for python or nodejs applications and doesn’t suffer from any duplication in layers. This could possibly be ported to here. |
When using
layers
results in layers containing multiple identical packagesExample:
Result:
glibc (and few other deps) exist in both bash and zsh layers
I noticed there is
ignore
option but seems to accept only a single store path. Is there a way to deduplicate derivations?The text was updated successfully, but these errors were encountered: