LevelUp Your Nix
Installing Nix
|
|
Enable Flakes
Add the following to ~/.config/nix/nix.conf or /etc/nix/nix.conf:
|
|
Nix Templates
You don’t have to start from zero, you can start with a template.
|
|
Important Files
flake.nix: Themainfile for your Nix Flake.flake.lock: The lock file that tells Nix what commits/dependencies to use for each of yourinputs..direnv: An optional file that allows you to automatically switch to your Shell when you enter the directory; requiresdirenvbe installed..gitignore: Every Flakeshouldneeds to be agitrepository; so we might want to have a.gitignore.
NOTE: Nix can only see files that have been added to the git repo.
Parts of a flake.nix
There are really only two parts to a flake.nix.
inputs
This section contains other Flakes you want to use or have available in your Flake. Each is assigned a local name that can be referenced in the Flake.
All three of the following are the same, just different styles of Nix code.
|
|
|
|
|
|
Note: You can’t mix styles if you are nesting things. The follwing is not valid.
|
|
The correct way would be to do:
|
|
or
|
|
outputs
This is the section the as the name implies is the output of your Flake. For the purpose of this tutorial session lets break the outputs section into two parts.
let… in block
This section is where you can create Nix variables that are the outputs of Nix functions. The functions can do things like create packages or create shells. General programming concepts can apply here, after all it is a programming language.
[ … ]( … ) block
This section is part of the flake.nix that is a little more strucutred. There are some standardized outputs that most all Flakes should have, such as packages or devShells.
Make an Executable Shell Script
A simple example of creating an executable shell script follows. It is a simple script that allows for running the Julia
package Pluto.jl from the command line without having to launch the Julia REPL. It gets saved as a Nix variable pluto
so we can use it elsewhere in our Flake. Below we also define the julia environment that will have the Pluto and PythonCall packages included. We call that Julia environment julia-env and notice how we used it in the pluto shell script. Finally notice that Nix lazily evaluates all of its code, so the order of defining things generally does not matter.
|
|
Making a Shell Environment
The following should generally go in the let in block of the outputs section of your flake or in something that gets imported into it. It is a function call that returns a buildEnv that we call shell-env. This isn’t 100% necessary that we do it seperate like we are, but it will allow us to define our environment once, and use it in both a devShell and a OCI compliant container. The function takes a couple of named arguments, the first being name which we just give it a descriptive name. The second argument is paths which is effectively a list of packages to include. For our example we include julia-env which we defined above.
|
|
Below is the actual definition of our shell. There are two arguments (that we have defined here) to pass into the mkShell function, buildInputs and shellHook. The buildInputs is the packages you want to have in your shell environment. Normally you could just place your packages you want inside the list, but because we don’t want to repeat our selves we broke out the above buildEnv and just use that in the buildInput section.
The shellHook can be thought of as your shell’s entrypoint script. It will execute when you enter your shell everytime. For our example we are just echoing some text into the figlet program.
|
|
Making an OCI Image
You spent all that time getting your environment just right to be able to run your application and now it needs to be containerized so it can run in Kubernetes somewhere. In the traditional Docker workflow you would have to create a Dockerfile and spend a lot of time making sure you included all the dependencies of your application, and their dependencies…and you would have to make sure that the version of the dependency in your base image’s package manager was the correct version (looking at you Debian and Redhat)… several hours later you finally have a Docker image.
With Nix and this way of making your development environment all you have to do to build a container image is the following:
|
|
This is just a single function call that we assign to the variable shell-img. The function takes four arguments, a name, atag, a drv, and a command which is akin to an entrypoint.
Take note of the drv argument. We are passing shell which is our shell derivation. We also provide a command that is our pluto script we wrote above. So what this will do is create a container image that the only things that exist in it are the things we included in our shell and their dependencies. The entrypoint for the container will be our pluto script.
The Outputs
All the above was in the let … in section of the outputs of our Flake, but now we need to actually export those things that we want to make public by defining some common Flake output variables.
|
|
NOTE: You can define as many shells and packages as it makes sense for your flake.
Using the Flake
Flakes don’t necessarily get installed. You can use any flake directly from the remote git repository that it resides or from within the local repository on your computer.
If this flake was in our current directory and it also resided on Gitlab (because GitHub is the devil!) then we could get a shell in our environment we defined either of these two ways.
|
|
You can build the container image and load it into Docker like this:
|
|
To run our Pluto program we can do any of the following:
|
|