This commit is contained in:
Graham Christensen 2017-08-11 22:47:47 -04:00
parent 7768b7e7d6
commit c596b2114f
No known key found for this signature in database
GPG key ID: 06121D366FE9435C
3 changed files with 281 additions and 2 deletions

View file

@ -1,7 +1,7 @@
{ pkgs ? import <nixpkgs> {} }:
let
lib = pkgs.lib;
sources = lib.sourceFilesBySuffices ./. [ ".xml" ];
sources = lib.sourceFilesBySuffices ./. [ ".xml" ".txt" ];
combined = pkgs.runCommand "nix-pills-combined"
{

View file

@ -4,5 +4,282 @@
version="5.0"
xml:id="why-you-should-give-it-try">
<title>why-you-should-give-it-try</title>
<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>
</chapter>

2
pills/01/which-bash.txt Normal file
View file

@ -0,0 +1,2 @@
$ ldd `which bash`
libc.so.6 => /nix/store/94n64qy99ja0vgbkf675nyk39g9b978n-glibc-2.19/lib/libc.so.6 (0x00007f0248cce000)