2017-08-11 21:41:14 -04:00
|
|
|
<chapter xmlns="http://docbook.org/ns/docbook"
|
2017-08-11 18:22:51 -04:00
|
|
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"
|
|
|
|
version="5.0"
|
2017-08-11 21:41:14 -04:00
|
|
|
xml:id="why-you-should-give-it-try">
|
2017-08-11 18:22:51 -04:00
|
|
|
|
2017-08-11 22:47:47 -04:00
|
|
|
<title>why you should give it try</title>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Introduction</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Welcome to the first post of the <link
|
|
|
|
xlink:href="https://nixos.org/nix">Nix</link> in pills series.
|
|
|
|
Nix is a purely functional package manager and deployment
|
|
|
|
system for POSIX. There's a lot of documentation that
|
|
|
|
describes what Nix, <link
|
|
|
|
xlink:href="https://nixos.org/nixos">NixOS</link> and related
|
|
|
|
projects are.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
The purpose of this post is to convince you to give Nix a try.
|
|
|
|
Installing NixOS is not required, but sometimes I may refer to
|
|
|
|
NixOS as a real world example of Nix usage for building a whole
|
|
|
|
operating system.
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Rationale of pills</title>
|
|
|
|
<para>
|
|
|
|
The <link xlink:href="https://nixos.org/nix/manual/">Nix
|
|
|
|
manual</link>, <link
|
|
|
|
xlink:href="http://nixos.org/docs/papers.html">related
|
|
|
|
papers</link>, <link
|
|
|
|
xlink:href="http://www.infoq.com/articles/configuration-management-with-nix">articles</link>
|
|
|
|
and wiki are excellent in explaining how Nix/NixOS works, how
|
|
|
|
you can use it and the amount of cools thing being done with it,
|
|
|
|
however sometimes you may feel some magic happening behind the
|
|
|
|
scenes hard to grasp in the beginning.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
This series aims to complement the missing explanations from the
|
|
|
|
more formal documents.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Follows a description of Nix. Just as pills, I'll try to be as
|
|
|
|
short as possible.
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Not being pure functional</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Most, if not all, widely used package managers (<link
|
|
|
|
xlink:href="https://wiki.debian.org/dpkg">dpkg</link>, <link
|
|
|
|
xlink:href="http://www.rpm.org/">rpm</link>, ...) mutate the
|
|
|
|
global state of the system. If a package
|
|
|
|
<literal>foo-1.0</literal> installs a program to
|
|
|
|
<literal>/usr/bin/foo</literal>, you cannot install
|
|
|
|
<literal>foo-1.1</literal> as well, unless you change the
|
|
|
|
installation paths or the binary name.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Changing the binary names means breaking possible users of
|
|
|
|
that binary.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Debian for example partially solves the problem with the
|
|
|
|
<link
|
|
|
|
xlink:href="https://wiki.debian.org/DebianAlternatives">alternatives</link>.
|
|
|
|
</para>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
So in theory it's possible with the current systems to install
|
|
|
|
multiple versions of the same package, in practice it's very
|
|
|
|
painful.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Let's say you need an nginx service and also an nginx-openresty
|
|
|
|
service. You have to create a new package that changes all the
|
|
|
|
paths with e.g. an -openresty suffix.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Or you want to run two different instances of mysql, 5.2 and
|
|
|
|
5.5. Same thing applies plus you have to also make sure the two
|
|
|
|
mysqlclient libraries do not collide.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
This is not impossible but very inconvenient. If you want to
|
|
|
|
install two whole stack of software like GNOME 3.10 and GNOME
|
|
|
|
3.12, you can imagine the amount of work.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
From an administrator view point: containers to the rescue. The
|
|
|
|
solution nowadays is to create a container per service,
|
|
|
|
especially when different versions are needed. That somehow
|
|
|
|
solves the problem, but at a different level and with other
|
|
|
|
drawbacks. Needing orchestration tools, setting up a shared
|
|
|
|
cache of packages, and new wild machines to monitor rather than
|
|
|
|
simple services.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
From a developer view point: virtualenv for python or jhbuild
|
|
|
|
for gnome or whatelse. But then, how do you mix the two stacks?
|
|
|
|
How do you avoid recompiling the same thing when it could be
|
|
|
|
instead be shared? Also you need to setup your development
|
|
|
|
tools to point to the different directories where libraries are
|
|
|
|
installed. Not only, there's the risk that some of the software
|
|
|
|
incorrectly uses system libraries.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
And so on. Nix solves all this at the packaging level and
|
|
|
|
solves it well. A single tool to rule them all.
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title> Being pure functional</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
Nix does no assumption about the global state of the system.
|
|
|
|
This has many advantages, but also some drawbacks of course.
|
|
|
|
The core of a Nix based system is the Nix store, usually
|
|
|
|
installed under /nix/store, and some tools to manipulate the
|
|
|
|
store. In Nix there's the notion of derivation rather than
|
|
|
|
package. The difference might be subtle at the beginning, so I
|
|
|
|
will often mix both of the words, ignore that.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Derivations/packages are stored in the nix store as follows:
|
|
|
|
<literal>/nix/store/<replaceable>hash-name</replaceable></literal>,
|
|
|
|
where the hash uniquely identifies the derivation (not true,
|
|
|
|
it's a little more complex than this), and name is the name of
|
|
|
|
the derivation.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Let's take the bash derivation as example:
|
|
|
|
<literal>/nix/store/s4zia7hhqkin1di0f187b79sa2srhv6k-bash-4.2-p45/</literal>.
|
|
|
|
It is a directory that contains <literal>bin/bash</literal>.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
You get it, there's no <literal>/bin/bash</literal>, there's only that
|
|
|
|
self-contained build output in the store. Same goes for
|
|
|
|
coreutils and everything else. To make their usage convenient
|
|
|
|
from the shell, nix will put those paths in
|
|
|
|
<varname>PATH</varname>.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
What we have is basically a store of all packages and different
|
|
|
|
versions of them, and things in the nix store are immutable.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
There's no ldconfig cache either. So where does bash find libc?
|
|
|
|
</para>
|
|
|
|
<screen><xi:include href="./01/which-bash.txt" parse="text" /></screen>
|
|
|
|
<para>
|
|
|
|
Turns out that when bash was built, it used that specific
|
|
|
|
version of glibc and at runtime it will require exactly that
|
|
|
|
glibc version.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Don't be doubtful about the version in the derivation name:
|
|
|
|
it's only a name for us humans. You may end up having a
|
|
|
|
different hash given the same derivation name.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
What does it all mean? You could run mysql 5.2 with glibc-2.18,
|
|
|
|
and mysql 5.5 with glibc-2.19. You could use your python module
|
|
|
|
with python 2.7 compiled with gcc 4.6 and the same python
|
|
|
|
module with python 3 compiled with gcc 4.8, all in the same
|
|
|
|
system.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
In other words, no dependency hell, not even a dependency
|
|
|
|
resolution algorithm. Straight dependencies from derivations to
|
|
|
|
other derivations.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
From an administrator view point: need an old PHP version from
|
|
|
|
lenny and want to upgrade to wheezy, not painful.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
From a developer view point: want to develop webkit with llvm
|
|
|
|
3.4 and 3.3, not painful.
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Mutable vs immutable</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
When upgrading a library, package managers replace it in-place.
|
|
|
|
All new applications run afterwards with the new library
|
|
|
|
without being recompiled. After all, they all refer dynamically
|
|
|
|
to libc6.so.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
While being immutable with Nix, upgrading a library like glibc
|
|
|
|
means recompiling all applications, because the glibc path to
|
|
|
|
the nix store is being hardcoded.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
So how do we deal with security updates? In Nix we have some
|
|
|
|
tricks (yet pure) to solve this problem, but that's another
|
|
|
|
story.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Another problem is that unless software has in mind a pure
|
|
|
|
functional model, or it can be adapted to, it's hard to compose
|
|
|
|
applications at runtime.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Let's take for example firefox. You install flash, and you get
|
|
|
|
it working because firefox looks in a global path for plugins.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
In Nix, there's no such global path for plugins. Firefox
|
|
|
|
therefore must know explicitly about the path to flash. We wrap
|
|
|
|
the firefox binary to setup the necessary environment to make
|
|
|
|
it find flash in the nix store. That will produce a new firefox
|
|
|
|
derivation: be aware it takes a few seconds, but it made
|
|
|
|
composition harder at runtime.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
No upgrade/downgrade scripts for your data. It doesn't make
|
|
|
|
sense with this approach, because there's no real derivation to
|
|
|
|
be upgraded. With nix you switch to using another software with
|
|
|
|
its own stack of dependencies, but there's no formal notion of
|
|
|
|
upgrade or downgrade when doing so.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Migrating to the new data format is your own responsibility.
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
|
|
<title>Conclusion</title>
|
|
|
|
<para>
|
|
|
|
Nix lets you compose software at build time with maximum
|
|
|
|
flexibility and with builds being as reproducible as possible.
|
|
|
|
Not only, due to its nature deploying systems in the cloud is
|
|
|
|
so easy, consistent and reliable that in the Nix world all
|
|
|
|
existing self-containment and orchestration tools are
|
|
|
|
deprecated by <link
|
|
|
|
xlink:href="http://nixos.org/nixops/">NixOps</link>. Talking
|
|
|
|
about this, no comparison with other tools is fair, Nix rocks.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
It however <emphasis>currently</emphasis> falls off when
|
|
|
|
speaking about dynamic composition at runtime or replacing low
|
|
|
|
level libraries due to massive rebuilds.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
That may sound scary, however after running NixOS on both a
|
|
|
|
server and a laptop desktop, I'm very satisfied so far. Some of
|
|
|
|
the architectural problems just need some man power, other
|
|
|
|
design problems are to be solved as a community yet.
|
|
|
|
</para>
|
|
|
|
<para>
|
|
|
|
Considering <link
|
|
|
|
xlink:href="https://nixos.org/nixpkgs/">Nixpkgs</link> (<link
|
|
|
|
xlink:href="https://github.com/NixOS/nixpkgs">github
|
|
|
|
link</link>) is a completely new repository of all the existing
|
|
|
|
software, with a completely fresh concept, and with few core
|
|
|
|
developers but overall year-over-year increasing contributions,
|
|
|
|
the current state is way more than acceptable and beyond the
|
|
|
|
experimental stage. In other words, it's worth your investment.
|
|
|
|
</para>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<title>Next pill...</title>
|
|
|
|
|
|
|
|
<para>
|
|
|
|
...we will install Nix on top of your current system (I assume
|
|
|
|
GNU/Linux, but we also have OSX users) and start inspecting the
|
|
|
|
installed stuff.
|
|
|
|
</para>
|
|
|
|
</section>
|
2017-08-11 21:41:14 -04:00
|
|
|
</chapter>
|