Fossil SCM
Documented the runc and crun options for running a container, including the cryptic method for exporting an OCI bundle from Docker, allowing you to use both together: Docker Desktop on your big dev box in the office, then one of the two lightweight runtimes out in the cloud.
Commit
c9431ef4a3448c3e4528de9e89f9b8a9400a6610613d767a414115071e5ff15e
Parent
87a23d2a7ca12b8…
1 file changed
+120
-6
+120
-6
| --- www/containers.md | ||
| +++ www/containers.md | ||
| @@ -444,19 +444,119 @@ | ||
| 444 | 444 | |
| 445 | 445 | [DD]: https://www.docker.com/products/docker-desktop/ |
| 446 | 446 | [DE]: https://docs.docker.com/engine/ |
| 447 | 447 | [DNT]: ./server/debian/nginx.md |
| 448 | 448 | |
| 449 | + | |
| 450 | +### <a id="runc" name="containerd"></a>Stripping Docker Engine Down | |
| 451 | + | |
| 452 | +The core of Docker Engine is its [`containerd`][ctrd] daemon and the | |
| 453 | +[`runc`][runc] container runner. It’s possible to dig into the subtree | |
| 454 | +managed by `containerd` on the build host and extract what we need to | |
| 455 | +run our Fossil container elsewhere with `runc`, leaving out all the | |
| 456 | +rest. `runc` alone is about 18 MiB, and you can do without `containerd` | |
| 457 | +entirely, if you want. | |
| 458 | + | |
| 459 | +The method isn’t complicated, but it *is* cryptic enough to want a shell | |
| 460 | +script: | |
| 461 | + | |
| 462 | +```shell | |
| 463 | +#!/bin/sh | |
| 464 | +c=fossil | |
| 465 | +r=/containers/$c/rootfs | |
| 466 | +sudo mkdir -p $r | |
| 467 | +docker container export $c | sudo tar -C $r -xf - | |
| 468 | +id=$(docker inspect --format="{{.Id}}" $c) | |
| 469 | +sudo cat /run/containerd/io.containerd.runtime.v2.task/moby/$id/config.json | | |
| 470 | + jq '.root.path = "'$r'"' | | |
| 471 | + jq '.linux.cgroupsPath = ""' > $r/../config.json | |
| 472 | +``` | |
| 473 | + | |
| 474 | +The first two lines set configurables: the name of the Docker container | |
| 475 | +you’re exporting for use with `runc` and the path where you want its | |
| 476 | +file system root to live. They can be anything you like. | |
| 477 | + | |
| 478 | +The rest is generic, but you’re welcome to freestyle here. For instance, | |
| 479 | +you could untar the rootfs through SSH in order to transfer the | |
| 480 | +container to a remote system. Or, you could get really clever: unpack it | |
| 481 | +in a local temp directory, then `rsync` it to the remote system to avoid | |
| 482 | +transferring elements of the rootfs that haven’t changed since the last | |
| 483 | +update. | |
| 484 | + | |
| 485 | +We’re using [jq] for two separate purposes: | |
| 486 | + | |
| 487 | +1. To change the container configuration for `runc`: | |
| 488 | + | |
| 489 | + * point it where we unpacked the container’s exported rootfs | |
| 490 | + * accede to its wish to [manage cgroups by itself][ecg] | |
| 491 | + | |
| 492 | +2. To make the Docker-managed machine-readable `config.json` more | |
| 493 | + human-readable, in case there are other things you want changed in | |
| 494 | + this version of the container. Exposing the `config.json` file like | |
| 495 | + this means you don’t have to rebuild the container merely to change | |
| 496 | + a value like a mount point, the kernel capability set, and so forth. | |
| 497 | + | |
| 498 | +With the container exported like this, you can start it as: | |
| 499 | + | |
| 500 | +``` | |
| 501 | + $ cd /path/to/rootfs | |
| 502 | + $ c=any-name-you-like | |
| 503 | + $ sudo runc create $c | |
| 504 | + $ sudo runc start $c | |
| 505 | + $ sudo runc exec $c -t sh -l | |
| 506 | + ~ $ ls museum | |
| 507 | + repo.fossil | |
| 508 | + ~ $ ps -eaf | |
| 509 | + PID USER TIME COMMAND | |
| 510 | + 1 fossil 0:00 bin/fossil server --create … | |
| 511 | + ~ $ exit | |
| 512 | + $ sudo runc kill fossil-runc | |
| 513 | + $ sudo runc delete fossil-runc | |
| 514 | +``` | |
| 515 | + | |
| 516 | +The first command refers to wherever the rootfs ended up on the `runc` | |
| 517 | +host. If it’s the same as the export host, then this is simply `$r` | |
| 518 | +from the shell script above. If instead you’re doing something like the | |
| 519 | +SSH/rsync trickery suggested above, the remote rootfs directory might be | |
| 520 | +named differently on the `runc` host. | |
| 521 | + | |
| 522 | +There’s nothing that says the container name on the build host has to be | |
| 523 | +the same as that on the runc host, so we’ve defined a separate `c` | |
| 524 | +variable here to keep the commands short. | |
| 525 | + | |
| 526 | +The rest should be straightforward: create and start the container as | |
| 527 | +root so the `chroot(2)` call inside the container will succeed, then get | |
| 528 | +into it with a login shell and poke around to prove to ourselves that | |
| 529 | +everything is working properly. It is. Yay! | |
| 530 | + | |
| 531 | +The remaining commands show shutting the container down and destroying | |
| 532 | +it, simply to show how these commands change relative to using the | |
| 533 | +Docker Engine commands. It’s “kill,” not “stop,” and it’s “delete,” not | |
| 534 | +“rm.” | |
| 535 | + | |
| 536 | +[ctrd]: https://containerd.io/ | |
| 537 | +[ecg]: https://github.com/opencontainers/runc/pull/3131 | |
| 538 | +[jq]: https://stedolan.github.io/jq/ | |
| 539 | +[runc]: https://github.com/opencontainers/runc | |
| 540 | + | |
| 449 | 541 | |
| 450 | 542 | ### <a id="podman"></a>Podman |
| 543 | + | |
| 544 | +Although your humble author claims the `runc` method above is not | |
| 545 | +complicated, you might be recollecting the carefree commands at the top | |
| 546 | +of this document, pondering whether you can live without the | |
| 547 | +abstractions a proper container runtime system provides. | |
| 451 | 548 | |
| 452 | 549 | A lighter-weight alternative to Docker Engine that doesn’t give up so |
| 453 | 550 | much of its administrator affordances is [Podman], initially created by |
| 454 | 551 | Red Hat and thus popular on that family of OSes, although it will run on |
| 455 | -any flavor of Linux. On Ubuntu 22.04, it’s about a quarter the size of | |
| 456 | -Docker Engine. It can even be made to run [on macOS via Homebrew][pmmac] | |
| 552 | +any flavor of Linux. It can even be made to run [on macOS via Homebrew][pmmac] | |
| 457 | 553 | or [on Windows via WSL2][pmwin]. |
| 554 | + | |
| 555 | +On Ubuntu 22.04, it’s about a quarter the size of Docker Engine. That | |
| 556 | +isn’t nearly so slim as `runc`, but we may be willing to pay this | |
| 557 | +overhead to get shorter and fewer commands. | |
| 458 | 558 | |
| 459 | 559 | Although Podman [bills itself][whatis] as a drop-in replacement for the |
| 460 | 560 | `docker` command and everything that sits behind it, some of the tool’s |
| 461 | 561 | design decisions affect how our Fossil containers run, as compared to |
| 462 | 562 | using Docker. The most important of these is that, by default, Podman |
| @@ -464,12 +564,14 @@ | ||
| 464 | 564 | regular user. This is generally better for security, but [we dealt with |
| 465 | 565 | that risk differently above](#chroot) already. Since neither choice is |
| 466 | 566 | unassailably correct in all conditions, we’ll document both options |
| 467 | 567 | here. |
| 468 | 568 | |
| 469 | -[pmmac]: https://podman.io/getting-started/installation.html#macos | |
| 470 | -[pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md | |
| 569 | +[pmmac]: https://podman.io/getting-started/installation.html#macos | |
| 570 | +[pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md | |
| 571 | +[Podman]: https://podman.io/ | |
| 572 | +[whatis]: https://podman.io/whatis.html | |
| 471 | 573 | |
| 472 | 574 | |
| 473 | 575 | #### <a id="podman-rootless"></a>Fossil in a Rootless Podman Container |
| 474 | 576 | |
| 475 | 577 | If you build the stock Fossil container under `podman`, it will fail at |
| @@ -512,9 +614,21 @@ | ||
| 512 | 614 | container entirely, but they’ll have powerful tools like `wget`, and |
| 513 | 615 | they’ll be connected to the network the container runs on. Once the bad |
| 514 | 616 | guy is inside the house, he doesn’t necessarily have to go after the |
| 515 | 617 | residents directly to cause problems for them. |
| 516 | 618 | |
| 517 | -[Podman]: https://podman.io/ | |
| 518 | -[whatis]: https://podman.io/whatis.html | |
| 619 | + | |
| 620 | +#### <a id="crun"></a>`crun` | |
| 621 | + | |
| 622 | +In the same way that [Docker Engine is based on `runc`](#runc), Podman’s | |
| 623 | +engine is based on [`crun`][crun], a lighter-weight alternative to | |
| 624 | +`runc`. It’s only 1.4 MiB on the system I tested it on, yet it will run | |
| 625 | +the same container bundles as in my `runc` examples above. This makes it | |
| 626 | +a great option for tiny remote hosts. Above, we saved more than that by | |
| 627 | +compressing the container’s Fossil executable with UPX! | |
| 628 | + | |
| 629 | +This suggests a method around the problem of rootless Podman containers: | |
| 630 | +`sudo crun`, following the examples above. | |
| 631 | + | |
| 632 | +[crun]: https://github.com/containers/crun | |
| 519 | 633 | |
| 520 | 634 | <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
| 521 | 635 |
| --- www/containers.md | |
| +++ www/containers.md | |
| @@ -444,19 +444,119 @@ | |
| 444 | |
| 445 | [DD]: https://www.docker.com/products/docker-desktop/ |
| 446 | [DE]: https://docs.docker.com/engine/ |
| 447 | [DNT]: ./server/debian/nginx.md |
| 448 | |
| 449 | |
| 450 | ### <a id="podman"></a>Podman |
| 451 | |
| 452 | A lighter-weight alternative to Docker Engine that doesn’t give up so |
| 453 | much of its administrator affordances is [Podman], initially created by |
| 454 | Red Hat and thus popular on that family of OSes, although it will run on |
| 455 | any flavor of Linux. On Ubuntu 22.04, it’s about a quarter the size of |
| 456 | Docker Engine. It can even be made to run [on macOS via Homebrew][pmmac] |
| 457 | or [on Windows via WSL2][pmwin]. |
| 458 | |
| 459 | Although Podman [bills itself][whatis] as a drop-in replacement for the |
| 460 | `docker` command and everything that sits behind it, some of the tool’s |
| 461 | design decisions affect how our Fossil containers run, as compared to |
| 462 | using Docker. The most important of these is that, by default, Podman |
| @@ -464,12 +564,14 @@ | |
| 464 | regular user. This is generally better for security, but [we dealt with |
| 465 | that risk differently above](#chroot) already. Since neither choice is |
| 466 | unassailably correct in all conditions, we’ll document both options |
| 467 | here. |
| 468 | |
| 469 | [pmmac]: https://podman.io/getting-started/installation.html#macos |
| 470 | [pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md |
| 471 | |
| 472 | |
| 473 | #### <a id="podman-rootless"></a>Fossil in a Rootless Podman Container |
| 474 | |
| 475 | If you build the stock Fossil container under `podman`, it will fail at |
| @@ -512,9 +614,21 @@ | |
| 512 | container entirely, but they’ll have powerful tools like `wget`, and |
| 513 | they’ll be connected to the network the container runs on. Once the bad |
| 514 | guy is inside the house, he doesn’t necessarily have to go after the |
| 515 | residents directly to cause problems for them. |
| 516 | |
| 517 | [Podman]: https://podman.io/ |
| 518 | [whatis]: https://podman.io/whatis.html |
| 519 | |
| 520 | <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
| 521 |
| --- www/containers.md | |
| +++ www/containers.md | |
| @@ -444,19 +444,119 @@ | |
| 444 | |
| 445 | [DD]: https://www.docker.com/products/docker-desktop/ |
| 446 | [DE]: https://docs.docker.com/engine/ |
| 447 | [DNT]: ./server/debian/nginx.md |
| 448 | |
| 449 | |
| 450 | ### <a id="runc" name="containerd"></a>Stripping Docker Engine Down |
| 451 | |
| 452 | The core of Docker Engine is its [`containerd`][ctrd] daemon and the |
| 453 | [`runc`][runc] container runner. It’s possible to dig into the subtree |
| 454 | managed by `containerd` on the build host and extract what we need to |
| 455 | run our Fossil container elsewhere with `runc`, leaving out all the |
| 456 | rest. `runc` alone is about 18 MiB, and you can do without `containerd` |
| 457 | entirely, if you want. |
| 458 | |
| 459 | The method isn’t complicated, but it *is* cryptic enough to want a shell |
| 460 | script: |
| 461 | |
| 462 | ```shell |
| 463 | #!/bin/sh |
| 464 | c=fossil |
| 465 | r=/containers/$c/rootfs |
| 466 | sudo mkdir -p $r |
| 467 | docker container export $c | sudo tar -C $r -xf - |
| 468 | id=$(docker inspect --format="{{.Id}}" $c) |
| 469 | sudo cat /run/containerd/io.containerd.runtime.v2.task/moby/$id/config.json | |
| 470 | jq '.root.path = "'$r'"' | |
| 471 | jq '.linux.cgroupsPath = ""' > $r/../config.json |
| 472 | ``` |
| 473 | |
| 474 | The first two lines set configurables: the name of the Docker container |
| 475 | you’re exporting for use with `runc` and the path where you want its |
| 476 | file system root to live. They can be anything you like. |
| 477 | |
| 478 | The rest is generic, but you’re welcome to freestyle here. For instance, |
| 479 | you could untar the rootfs through SSH in order to transfer the |
| 480 | container to a remote system. Or, you could get really clever: unpack it |
| 481 | in a local temp directory, then `rsync` it to the remote system to avoid |
| 482 | transferring elements of the rootfs that haven’t changed since the last |
| 483 | update. |
| 484 | |
| 485 | We’re using [jq] for two separate purposes: |
| 486 | |
| 487 | 1. To change the container configuration for `runc`: |
| 488 | |
| 489 | * point it where we unpacked the container’s exported rootfs |
| 490 | * accede to its wish to [manage cgroups by itself][ecg] |
| 491 | |
| 492 | 2. To make the Docker-managed machine-readable `config.json` more |
| 493 | human-readable, in case there are other things you want changed in |
| 494 | this version of the container. Exposing the `config.json` file like |
| 495 | this means you don’t have to rebuild the container merely to change |
| 496 | a value like a mount point, the kernel capability set, and so forth. |
| 497 | |
| 498 | With the container exported like this, you can start it as: |
| 499 | |
| 500 | ``` |
| 501 | $ cd /path/to/rootfs |
| 502 | $ c=any-name-you-like |
| 503 | $ sudo runc create $c |
| 504 | $ sudo runc start $c |
| 505 | $ sudo runc exec $c -t sh -l |
| 506 | ~ $ ls museum |
| 507 | repo.fossil |
| 508 | ~ $ ps -eaf |
| 509 | PID USER TIME COMMAND |
| 510 | 1 fossil 0:00 bin/fossil server --create … |
| 511 | ~ $ exit |
| 512 | $ sudo runc kill fossil-runc |
| 513 | $ sudo runc delete fossil-runc |
| 514 | ``` |
| 515 | |
| 516 | The first command refers to wherever the rootfs ended up on the `runc` |
| 517 | host. If it’s the same as the export host, then this is simply `$r` |
| 518 | from the shell script above. If instead you’re doing something like the |
| 519 | SSH/rsync trickery suggested above, the remote rootfs directory might be |
| 520 | named differently on the `runc` host. |
| 521 | |
| 522 | There’s nothing that says the container name on the build host has to be |
| 523 | the same as that on the runc host, so we’ve defined a separate `c` |
| 524 | variable here to keep the commands short. |
| 525 | |
| 526 | The rest should be straightforward: create and start the container as |
| 527 | root so the `chroot(2)` call inside the container will succeed, then get |
| 528 | into it with a login shell and poke around to prove to ourselves that |
| 529 | everything is working properly. It is. Yay! |
| 530 | |
| 531 | The remaining commands show shutting the container down and destroying |
| 532 | it, simply to show how these commands change relative to using the |
| 533 | Docker Engine commands. It’s “kill,” not “stop,” and it’s “delete,” not |
| 534 | “rm.” |
| 535 | |
| 536 | [ctrd]: https://containerd.io/ |
| 537 | [ecg]: https://github.com/opencontainers/runc/pull/3131 |
| 538 | [jq]: https://stedolan.github.io/jq/ |
| 539 | [runc]: https://github.com/opencontainers/runc |
| 540 | |
| 541 | |
| 542 | ### <a id="podman"></a>Podman |
| 543 | |
| 544 | Although your humble author claims the `runc` method above is not |
| 545 | complicated, you might be recollecting the carefree commands at the top |
| 546 | of this document, pondering whether you can live without the |
| 547 | abstractions a proper container runtime system provides. |
| 548 | |
| 549 | A lighter-weight alternative to Docker Engine that doesn’t give up so |
| 550 | much of its administrator affordances is [Podman], initially created by |
| 551 | Red Hat and thus popular on that family of OSes, although it will run on |
| 552 | any flavor of Linux. It can even be made to run [on macOS via Homebrew][pmmac] |
| 553 | or [on Windows via WSL2][pmwin]. |
| 554 | |
| 555 | On Ubuntu 22.04, it’s about a quarter the size of Docker Engine. That |
| 556 | isn’t nearly so slim as `runc`, but we may be willing to pay this |
| 557 | overhead to get shorter and fewer commands. |
| 558 | |
| 559 | Although Podman [bills itself][whatis] as a drop-in replacement for the |
| 560 | `docker` command and everything that sits behind it, some of the tool’s |
| 561 | design decisions affect how our Fossil containers run, as compared to |
| 562 | using Docker. The most important of these is that, by default, Podman |
| @@ -464,12 +564,14 @@ | |
| 564 | regular user. This is generally better for security, but [we dealt with |
| 565 | that risk differently above](#chroot) already. Since neither choice is |
| 566 | unassailably correct in all conditions, we’ll document both options |
| 567 | here. |
| 568 | |
| 569 | [pmmac]: https://podman.io/getting-started/installation.html#macos |
| 570 | [pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md |
| 571 | [Podman]: https://podman.io/ |
| 572 | [whatis]: https://podman.io/whatis.html |
| 573 | |
| 574 | |
| 575 | #### <a id="podman-rootless"></a>Fossil in a Rootless Podman Container |
| 576 | |
| 577 | If you build the stock Fossil container under `podman`, it will fail at |
| @@ -512,9 +614,21 @@ | |
| 614 | container entirely, but they’ll have powerful tools like `wget`, and |
| 615 | they’ll be connected to the network the container runs on. Once the bad |
| 616 | guy is inside the house, he doesn’t necessarily have to go after the |
| 617 | residents directly to cause problems for them. |
| 618 | |
| 619 | |
| 620 | #### <a id="crun"></a>`crun` |
| 621 | |
| 622 | In the same way that [Docker Engine is based on `runc`](#runc), Podman’s |
| 623 | engine is based on [`crun`][crun], a lighter-weight alternative to |
| 624 | `runc`. It’s only 1.4 MiB on the system I tested it on, yet it will run |
| 625 | the same container bundles as in my `runc` examples above. This makes it |
| 626 | a great option for tiny remote hosts. Above, we saved more than that by |
| 627 | compressing the container’s Fossil executable with UPX! |
| 628 | |
| 629 | This suggests a method around the problem of rootless Podman containers: |
| 630 | `sudo crun`, following the examples above. |
| 631 | |
| 632 | [crun]: https://github.com/containers/crun |
| 633 | |
| 634 | <div style="height:50em" id="this-space-intentionally-left-blank"></div> |
| 635 |