tech-ingolf-wagner-de/content/nixos/krops.md

518 lines
12 KiB
Markdown
Raw Normal View History

2018-08-15 15:12:54 +02:00
---
2018-08-20 15:11:21 +02:00
title: "krops"
2018-08-15 15:12:54 +02:00
date: 2018-08-15T15:06:26+02:00
2018-08-15 15:38:52 +02:00
draft: false
tags:
- krebs
- NixOS
- nixOps
- password-store
2018-09-30 08:50:58 +02:00
summary: >
krops is an alternative to
2021-09-06 10:05:56 +02:00
NixOps trying to solve some of these flaws,
2018-09-30 08:50:58 +02:00
with some very simple concepts.
2021-09-06 10:05:56 +02:00
In this article we will see on how to deploy a server
2018-09-30 08:50:58 +02:00
using krops and discuss all involved parameters and steps.
2018-08-15 15:12:54 +02:00
---
2018-09-30 09:40:28 +02:00
[NixOps](https://nixos.org/nixops/)
the official DevOps tool of
2018-08-15 15:12:54 +02:00
[NixOS ](https://nixos.org) is nice, but it has some flaws.
2018-09-29 19:49:25 +02:00
[krops](https://cgit.krebsco.de/krops/about/) is an alternative to
2021-09-06 10:05:56 +02:00
[NixOps](https://nixos.org/nixops/) trying to solve some of these flaws,
2018-08-15 15:12:54 +02:00
with some very simple concepts.
2018-08-15 15:38:52 +02:00
If you're looking for a good document on how to use
[NixOps](https://nixos.org/nixops/) in the fields,
2018-08-18 20:54:56 +02:00
have a look at
2022-04-18 16:37:27 +02:00
[this excellent article](https://web.archive.org/web/20181002051558/https://blog.wearewizards.io/how-to-use-nixops-in-a-team).
2018-08-15 15:12:54 +02:00
2021-08-23 20:41:21 +02:00
## krops vs. NixOps (Feature Comparison)
2018-08-15 15:12:54 +02:00
2018-08-18 20:54:56 +02:00
<table class="comparison">
2018-09-30 09:40:28 +02:00
<thead>
<tr>
<th class="text">Feature</th>
<th>NixOps</th>
<th>krops</th>
</tr>
</thead>
<tbody>
<tr>
<td title="serverA is unstable, serverB is stable, serverC on commit=XY">
Precise versioning for every machine.
</td>
<td class="bad"> No </td>
<td class="good"> Yes </td>
</tr>
<tr>
<td class="text">Well documented</td>
<td class="good"> Yes </td>
<td class="bad"> No</td>
</tr>
<tr>
<td class="text">Lightweight</td>
<td class="ok"> Kinda </td>
<td class="good"> Yes </td>
</tr>
<tr>
<td class="text"
title="krops uses passwordstore.org, see more below">
Native File Encryption
</td>
<td class="bad"> No </td>
<td class="good"> Yes </td>
</tr>
<tr>
<td class="text"
title="nixops has deployment.keys">
TMPFS Key Management
</td>
<td class="good"> Yes </td>
<td class="bad"> No </td>
</tr>
<tr>
<td class="text"
title="run nixos-rebuild on the target system">
Manual Deployment Possible
</td>
<td class="bad"> No </td>
<td class="good"> Yes </td>
</tr>
<tr>
<td class="text" >
Needs Database
</td>
<td class="bad"> Yes </td>
<td class="good"> No </td>
</tr>
<tr>
<td class="text"
title="where are the .drv files are created">
Build and Download happens on
</td>
<td class="ok"> Client </td>
<td class="good"> Target </td>
</tr>
</tbody>
2018-08-18 20:54:56 +02:00
</table>
2018-08-15 15:12:54 +02:00
2021-08-23 20:41:21 +02:00
## krops Structure by Example
2018-08-25 17:13:01 +02:00
krops is not an executable like NixOps,
2021-09-06 10:05:56 +02:00
it is a library to write executables which do the actual deployment.
2018-08-25 17:13:01 +02:00
Let's say you have a very simple `configuration.nix`
2021-09-05 20:13:25 +02:00
```nix
2018-08-25 17:13:01 +02:00
{ pkgs, ... }:
{
environment.systemPackages = [ pkgs.git ];
}
```
2018-08-25 17:13:01 +02:00
Than you can use the following script (let's name it `krops.nix`) to deploy it
on the machine `server01.mydomain.org`.
2021-09-05 20:13:25 +02:00
```nix
let
2018-09-30 09:12:03 +02:00
krops = builtins.fetchGit {
url = "https://cgit.krebsco.de/krops/";
};
lib = import "${krops}/lib";
pkgs = import "${krops}/pkgs" {};
source = lib.evalSource [
{
nixpkgs.git = {
2021-07-20 18:08:41 +02:00
ref = "origin/nixos-21.05";
2018-09-30 09:12:03 +02:00
url = https://github.com/NixOS/nixpkgs-channels;
};
nixos-config.file = toString ./configuration.nix;
}
];
2018-09-30 09:24:50 +02:00
in {
server01 = pkgs.krops.writeDeploy "deploy-server01" {
source = source;
target = "root@server01.mydomain.org";
};
}
```
2018-09-30 09:12:03 +02:00
2018-08-25 17:13:01 +02:00
Now you can deploy the machine by running:
2018-09-30 09:12:03 +02:00
2021-09-05 20:13:25 +02:00
```shell
2021-07-20 18:08:41 +02:00
$> nix-build ./krops.nix -A server01 && ./result
```
2018-09-30 09:19:12 +02:00
2018-08-18 20:54:56 +02:00
You need to make sure you have ssh access to the root user on `server01.mydomain.org`
and `git` is installed on `server01.mydomain.org`.
{{% note %}}
2018-09-30 09:19:12 +02:00
2018-08-18 20:54:56 +02:00
If you run this command the first time you will most likely get a message like
2018-09-30 09:19:12 +02:00
2021-08-23 20:41:21 +02:00
```
2018-08-18 20:54:56 +02:00
error: missing sentinel file: server01.mydomain.org:/var/src/.populate
```
2018-09-30 09:19:12 +02:00
This is because you need to create `/var/src/.populate` before krops will do anything.
2018-08-25 17:13:01 +02:00
Once that file is created, you can run the command `./result` again.
2018-09-30 09:19:12 +02:00
{{% /note %}}
2018-08-25 17:13:01 +02:00
krops will copy the file `configuration.nix` to `/var/src/nixos-config` on `server01`
and will clone `nixpkgs` into `/var/src/nixpkgs`.
2018-09-30 09:40:28 +02:00
After that, krops will run `nixos-rebuild switch -I /var/src` which will provision
`server01`.
2021-08-23 20:41:21 +02:00
### The Different Parts Explained
2018-08-19 16:28:09 +02:00
Let's start with the cryptic part at the beginning.
2021-09-05 20:13:25 +02:00
```nix
let
2018-09-30 09:19:12 +02:00
krops = builtins.fetchGit {
url = "https://cgit.krebsco.de/krops/";
};
lib = import "${krops}/lib";
pkgs = import "${krops}/pkgs" {};
```
2018-09-30 09:19:12 +02:00
2018-08-25 17:13:01 +02:00
It downloads krops and makes its library and packages available
so they can be used it in the following script.
2021-09-05 20:13:25 +02:00
```nix
in {
2018-09-30 09:19:12 +02:00
server01 = pkgs.krops.writeDeploy "deploy-server01" {
source = source;
target = "root@server01.mydomain.org";
};
}
```
The executable `server01` is which results in the link `./result`.
It is the result of `krops.writeDeploy` with parameters
* `target` passed to the ssh command
2018-09-30 09:40:28 +02:00
* `source` the set of files and folders which should be made available beneath
`/var/src` on the target
2018-08-25 17:13:01 +02:00
{{% note %}}
2018-09-30 09:19:12 +02:00
2018-09-30 09:40:28 +02:00
`target` takes more argument parts than just the host,
you can for example set it to `root@server01:4444/etc/krops/`
2018-09-30 09:19:12 +02:00
2018-08-25 17:13:01 +02:00
to change the ssh port and the target folder it should be copied.
2018-09-30 09:19:12 +02:00
{{% /note %}}
2021-09-05 20:13:25 +02:00
```nix
2021-09-06 10:05:56 +02:00
source = lib.evalSource [
{
nixpkgs.git = {
ref = "nixos-18.03";
url = https://github.com/NixOS/nixpkgs-channels;
};
nixos-config.file = toString ./configuration.nix;
}
];
```
The list of folders and files are managed by the `source` parameter.
The keys in will be the names of the folders or files in `/var/src`.
`nixpkgs` and `nixos-config` are mandatory.
2018-08-19 16:28:09 +02:00
All other files/folders must be referenced in the resulting `nixos-config` file.
2021-08-23 20:41:21 +02:00
### Different Sources
<br>
2021-08-23 20:41:21 +02:00
#### Files and Folders
2018-08-18 20:54:56 +02:00
2018-08-25 17:13:01 +02:00
You can use the `file` attribute to transfer
files and folders from the build host to the target host.
2018-08-18 20:54:56 +02:00
But it always must be an absolute path.
2021-09-05 20:13:25 +02:00
```nix
2018-08-18 20:54:56 +02:00
source = lib.evalSource [
{
modules.file = toString ./modules; # toString generates an absoulte path
2018-08-18 20:54:56 +02:00
}
];
```
2018-09-30 09:19:12 +02:00
2018-08-18 20:54:56 +02:00
This copies `./modules` to `/var/src/modules`.
2021-08-23 20:41:21 +02:00
#### Symlinks
2018-08-18 20:54:56 +02:00
2018-08-25 17:13:01 +02:00
You can also use the `symlink` argument
2018-08-18 20:54:56 +02:00
to create symlinks on the target system.
2021-09-05 20:13:25 +02:00
```nix
2018-08-18 20:54:56 +02:00
source = lib.evalSource [
{
config.file = toString ./config;
nix-config.symlink = "config/server01/configuration.nix";
}
];
```
2018-09-30 09:19:12 +02:00
2018-08-18 20:54:56 +02:00
This copies `./config` to `/var/src/config` and creates a symlink
`/var/src/nix-config` to `config/server01/configuration.nix`.
{{% note %}}
krops will not check if the target is valid.
{{% /note %}}
2021-08-23 20:41:21 +02:00
#### Git Repositories
2018-08-18 20:54:56 +02:00
2018-08-25 17:13:01 +02:00
You can pull Git repositories using the `git` attribute
2018-08-18 20:54:56 +02:00
from everywhere you want,
2018-08-25 17:13:01 +02:00
as long as the target host is able to pull it.
2018-08-18 20:54:56 +02:00
2021-09-05 20:13:25 +02:00
```nix
2018-08-18 20:54:56 +02:00
source = lib.evalSource [
{
nix-writers.git = {
url = https://cgit.krebsco.de/nix-writers/;
ref = "4d0829328e885a6d7163b513998a975e60dd0a72";
};
}
];
```
2018-09-30 09:19:12 +02:00
2018-08-19 16:28:09 +02:00
This pulls the [nix-writers](https://cgit.krebsco.de/nix-writers/)
2018-08-18 20:54:56 +02:00
repository
to `/var/src/nix-writers`.
2018-08-18 20:54:56 +02:00
the `ref` parameter also accepts branches or tags.
2021-08-23 20:41:21 +02:00
#### Password Store (Native File Encryption)
lets assume `secrets` is a folder managed by
[passwordstore](https://www.passwordstore.org/).
2021-08-23 20:41:21 +02:00
```
secrets
|-- server01
| `-- wpa_supplicant.conf.gpg
`-- server02
`-- wpa_supplicant.conf.gpg
```
2018-08-25 17:13:01 +02:00
Use the `pass` argument to include the sub-folder `server01`
into your deployment.
2021-09-05 20:13:25 +02:00
```nix
source = lib.evalSource [
{
secrets.pass = {
2019-03-20 19:54:31 +01:00
dir = toString ./secrets;
name = "server01";
};
}
];
```
This copies `secrets/server01` to `/var/src/secrets` after it is decrypted.
You will be prompted to enter the password.
{{% warning %}}
2018-08-25 17:14:23 +02:00
The files in `/var/src/secrets` will be unencrypted!
{{% /warning %}}
2018-08-25 17:13:01 +02:00
2021-08-23 20:41:21 +02:00
### How to use Sources in configuration.nix
2018-08-18 20:54:56 +02:00
You can use folders copied by krops
very pleasantly in the `configuration.nix`.
2021-09-05 20:13:25 +02:00
```nix
{ config, libs, pkgs, ... }:
{
imports = [
<modules>
2018-08-18 20:54:56 +02:00
<config/service01/hardware-configuration.nix>
];
networking.supplicant."wlan0".configFile.path = toString <secrets/wpa_supplicant.conf>;
}
```
2021-08-23 20:41:21 +02:00
### How to Manually Rebuild the System
2018-08-18 20:54:56 +02:00
If you, for some reason, want to rebuild the system on the host itself,
you can do that simply by running as root
2021-09-05 20:13:25 +02:00
```shell
2018-08-18 20:54:56 +02:00
#> nixos-rebuild switch -I /var/src
```
2021-08-23 20:41:21 +02:00
## Some Tips
So far this is everything krops does.
It is simple and very close to the usual way Nix and NixOS works.
Let's look on some common pattern to solve some common issues.
2021-08-23 20:41:21 +02:00
### Multiple Server
2018-08-18 20:54:56 +02:00
If you want to manage multiple computers,
the following adjustments might help you.
Take a closer look to the `source` function and the parameter
`nixos-config` and `secrets`.
2021-09-05 20:13:25 +02:00
```nix
let
source = name: lib.evalSource [
{
2018-08-18 20:54:56 +02:00
config.file = toString ./config;
modules.file = toString ./modules;
nixos-config.symlink = "config/${name}/configuration.nix"
nixpkgs.git = {
ref = "nixos-18.03";
url = https://github.com/NixOS/nixpkgs-channels;
};
secrets.pass = {
dir = toString ./secrets";
2018-08-18 20:54:56 +02:00
name = "${name}";
};
}
];
server01 = pkgs.krops.writeDeploy "deploy-server01" {
source = source "server01";
target = "root@server01.mydomain.org";
};
server02 = pkgs.krops.writeDeploy "deploy-server02" {
source = source "server02";
2018-08-18 20:54:56 +02:00
target = "root@server02.mydomain.org";
};
in {
server01 = server01;
server02 = server02;
all = pkgs.writeScript "deploy-all-servers"
2021-07-20 18:08:41 +02:00
(lib.concatStringsSep "\n" [ server01 server02 ]);
}
```
2018-09-30 09:40:28 +02:00
Now you can create multiple `./result`s or you can use the
`-A` parameter of nix-build to choose what `./result` will be.
2018-08-18 20:54:56 +02:00
2021-09-05 20:13:25 +02:00
```shell
2018-08-18 20:54:56 +02:00
$> nix-build ./krops.nix -A server01 && ./result
$> nix-build ./krops.nix -A server02 && ./result
$> nix-build ./krops.nix -A all && ./result
```
2021-08-23 20:41:21 +02:00
### Update and Fixing Git Commits
2018-08-18 20:54:56 +02:00
2018-09-30 09:40:28 +02:00
Updating hashes for Git repositories is annoying and using branches
might break consistency.
To avoid editing files you can use the `nix-prefetch-git`
and `lib.importJson` to make your live easier.
2021-09-05 20:13:25 +02:00
```shell
2018-09-30 09:19:12 +02:00
$> nix-prefetch-git \
2018-08-18 20:54:56 +02:00
--url https://github.com/NixOS/nixpkgs-channels \
--rev refs/heads/nixos-18.03 \
> nixpkgs.json
```
results in a file `nixpkgs.json` which looks like this
2021-09-05 20:13:25 +02:00
```json
2018-08-18 20:54:56 +02:00
{
"url": "https://github.com/NixOS/nixpkgs-channels.git",
"rev": "9cbc7363543ebeb5a0182aa171f23bb19332b99f",
"date": "2018-08-14T14:00:50+02:00",
"sha256": "1i3iwc23cl085w429zm6qip1058bsi7zavj7pdwqiqm9nymy7plq",
"fetchSubmodules": true
}
```
2018-09-30 09:19:12 +02:00
2018-08-19 16:28:09 +02:00
And it can be imported in `./krops.nix` like this.
2018-08-18 20:54:56 +02:00
2021-09-05 20:13:25 +02:00
```nix
2018-08-18 20:54:56 +02:00
let
2018-09-30 09:19:12 +02:00
importJson = (import <nixpkgs> {}).lib.importJSON;
source = lib.evalSource [
{
nixpkgs.git = {
ref = (importJson ./nixpkgs.json).rev;
url = https://github.com/NixOS/nixpkgs-channels;
};
}
];
2018-08-18 20:54:56 +02:00
```
2018-08-18 20:54:56 +02:00
Now you can just have to call the `nix-prefetch-git` command
and the commit reference will be updated, and is fixed.
2018-08-18 20:54:56 +02:00
This should also make it simpler to maintain different channels on different machines.
2018-10-14 22:18:00 +02:00
2021-08-23 20:41:21 +02:00
### Use Packages from other channels
2018-10-14 22:18:00 +02:00
It is very easy to install packages from different channels.
For example add `nixpkgs-unstable` the same way you add `nixpkgs`.
2021-09-05 20:13:25 +02:00
```nix
2021-09-06 10:05:56 +02:00
source = lib.evalSource [
{
nixpkgs.git = {
ref = "nixos-18.09";
url = https://github.com/NixOS/nixpkgs-channels;
};
nixpkgs-unstable.git = {
ref = "nixos-unstable";
url = https://github.com/NixOS/nixpkgs-channels;
};
nixos-config.file = toString ./configuration.nix;
}
];
2018-10-14 22:18:00 +02:00
```
To install a package from the `unstable` channel you just have to import the channel
and call the packages from there.
2021-09-05 20:13:25 +02:00
```nix
2018-10-14 22:18:00 +02:00
{ config, pkgs, ... }:
let
unstable = import <nixpkgs-unstable> {};
in {
environment.systemPackages = [
# install gimp from stable channel
pkgs.gimp
# install inkscape from unstable channel
unstable.inkscape
];
}
```
2018-12-28 12:55:33 +01:00
2021-08-23 20:41:21 +02:00
### Channels and NIX_PATH
2018-12-28 12:55:33 +01:00
You might wonder how `nix-shell` is catching up with the
`nixpkgs` in `/var/src`.
`nix-shell` will still use the standard system setup,
including your channel configurations,
which you have to maintain on top of using krops.
If you don't like to do that (like me) you have to change
the `NIX_PATH` variable system-wide.
2021-09-05 20:13:25 +02:00
```nix
2018-12-28 12:55:33 +01:00
environment.variables.NIX_PATH = lib.mkForce "/var/src";
```
And `nix-shell` will also use `nixpkgs` from `/var/src`