Fossil SCM

Moved the sectio about elaborating the container runtime layer down into the section of the doc where we talk about other customizations. Its prior location was because it was a tangent off a prior point, but it's just as easy to jump down via hyperlink. Assorted other small improvements while in there.

wyoung 2023-04-19 11:29 trunk
Commit 301d4f21bcd2260024f70f9075cb5d1d991a7983cc276550a082ee72fdcc38bc
1 file changed +159 -163
+159 -163
--- www/containers.md
+++ www/containers.md
@@ -69,11 +69,11 @@
6969
The wrong way is to use the `Dockerfile COPY` command, because by baking
7070
the repo into the image at build time, it will become one of the image’s
7171
base layers. The end result is that each time you build a container from
7272
that image, the repo will be reset to its build-time state. Worse,
7373
restarting the container will do the same thing, since the base image
74
-layers are immutable in Docker. This is almost certainly not what you
74
+layers are immutable. This is almost certainly not what you
7575
want.
7676
7777
The correct ways put the repo into the _container_ created from the
7878
_image_, not in the image itself.
7979
@@ -114,11 +114,11 @@
114114
this with `chmod`: simply reload the browser, and Fossil will try again.
115115
116116
117117
### 2.2 <a id="bind-mount"></a>Storing the Repo Outside the Container
118118
119
-The simple storage method above has a problem: Docker containers are
119
+The simple storage method above has a problem: containers are
120120
designed to be killed off at the slightest cause, rebuilt, and
121121
redeployed. If you do that with the repo inside the container, it gets
122122
destroyed, too. The solution is to replace the “run” command above with
123123
the following:
124124
@@ -133,12 +133,12 @@
133133
Because this bind mount maps a host-side directory (`~/museum`) into the
134134
container, you don’t need to `docker cp` the repo into the container at
135135
all. It still expects to find the repository as `repo.fossil` under that
136136
directory, but now both the host and the container can see that repo DB.
137137
138
-Instead of a bind mount, you could instead set up a separate [Docker
139
-volume](https://docs.docker.com/storage/volumes/), at which point you
138
+Instead of a bind mount, you could instead set up a separate
139
+[volume](https://docs.docker.com/storage/volumes/), at which point you
140140
_would_ need to `docker cp` the repo file into the container.
141141
142142
Either way, files in these mounted directories have a lifetime
143143
independent of the container(s) they’re mounted into. When you need to
144144
rebuild the container or its underlying image — such as to upgrade to a
@@ -183,152 +183,25 @@
183183
184184
## 3. <a id="security"></a>Security
185185
186186
### 3.1 <a id="chroot"></a>Why Not Chroot?
187187
188
-Prior to 2023.03.26, the stock Fossil container made use of [the chroot
189
-jail feature](./chroot.md) in order to wall away the shell and other
190
-tools provided by [BusyBox](https://www.busybox.net/BusyBox.html). This
191
-author made a living for years in the early 1990s using Unix systems
192
-that offered less power, so there was a legitimate worry that if someone
193
-ever figured out how to get a shell on one of these Fossil containers,
194
-it would constitute a powerful island from which to attack the rest of
195
-the network.
196
-
197
-The thing is, Fossil is self-contained, needing none of that power in
198
-the main-line use cases. The only reason we included BusyBox in the
199
-container at all was on the off chance that someone needed it for
200
-debugging.
201
-
202
-That justification collapsed when we realized you could restore this
203
-basic shell environment on an as-needed basis with a one-line change to
204
-the `Dockerfile`, as we show in the next section.
205
-
206
-
207
-### 3.2 <a id="run"></a>Swapping Out the Run Layer
208
-
209
-If you want a basic shell environment for temporary debugging of the
210
-running container, that’s easily added. Simply change this line in the
211
-`Dockerfile`…
212
-
213
- FROM scratch AS run
214
-
215
-…to this:
216
-
217
- FROM busybox AS run
218
-
219
-Rebuild, redeploy, and your Fossil container now has a BusyBox based
220
-shell environment that you can get into via:
221
-
222
- $ docker exec -it -u fossil $(make container-version) sh
223
-
224
-(That command assumes you built the container via “`make container`” and
225
-are therefore using its versioning scheme.)
226
-
227
-Another case where you might need to replace this bare-bones “`run`”
228
-layer with something more functional is that you’re setting up [email
229
-alerts](./alerts.md) and need some way to integrate with the host’s
230
-[MTA]. There are a number of alternatives in that linked document, so
231
-for the sake of discussion, we’ll say you’ve chosen [Method
232
-2](./alerts.md#db), which requires a Tcl interpreter and its SQLite
233
-extension to push messages into the outbound email queue DB, presumably
234
-bind-mounted into the container.
235
-
236
-You can do that by replacing STAGEs 2 and 3 in the stock `Dockerfile`
237
-with this:
238
-
239
-```
240
- ## ---------------------------------------------------------------------
241
- ## STAGE 2: Pare that back to the bare essentials, plus Tcl.
242
- ## ---------------------------------------------------------------------
243
- FROM alpine AS run
244
- ARG UID=499
245
- ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
246
- COPY --from=builder /tmp/fossil /bin/
247
- COPY tools/email-sender.tcl /bin/
248
- RUN set -x \
249
- && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
250
- && echo "fossil:x:${UID}:fossil" >> /etc/group \
251
- && install -d -m 700 -o fossil -g fossil log museum \
252
- && apk add --no-cache tcl sqlite-tcl
253
-```
254
-
255
-Build it and test that it works like so:
256
-
257
-```
258
- $ make container-run &&
259
- echo 'puts [info patchlevel]' |
260
- docker exec -i $(make container-version) tclsh
261
- 8.6.12
262
-```
263
-
264
-You should remove the `PATH` override in the “RUN”
265
-stage, since it’s written for the case where everything is in `/bin`.
266
-With these additions, we need the longer `PATH` shown above to have
267
-ready access to them all.
268
-
269
-Another useful case to consider is that you’ve installed a [server
270
-extension](./serverext.wiki) and you need an interpreter for that
271
-script. The first option above won’t work except in the unlikely case that
272
-it’s written for one of the bare-bones script interpreters that BusyBox
273
-ships.(^BusyBox’s `/bin/sh` is based on the old 4.4BSD Lite Almquist
274
-shell, implementing little more than what POSIX specified in 1989, plus
275
-equally stripped-down versions of `awk` and `sed`.)
276
-
277
-Let’s say the extension is written in Python. While you could handle it
278
-the same way we do with the Tcl example above, Python is more
279
-popular, giving us more options. Let’s inject a Python environment into
280
-the stock Fossil container via a suitable “[distroless]” image instead:
281
-
282
-```
283
- ## ---------------------------------------------------------------------
284
- ## STAGE 2: Pare that back to the bare essentials, plus Python.
285
- ## ---------------------------------------------------------------------
286
- FROM cgr.dev/chainguard/python:latest
287
- USER root
288
- ARG UID=499
289
- ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
290
- COPY --from=builder /tmp/fossil /bin/
291
- COPY --from=builder /bin/busybox.static /bin/busybox
292
- RUN [ "/bin/busybox", "--install", "/bin" ]
293
- RUN set -x \
294
- && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
295
- && echo "fossil:x:${UID}:fossil" >> /etc/group \
296
- && install -d -m 700 -o fossil -g fossil log museum
297
-```
298
-
299
-You will also have to add `busybox-static` to the APK package list in
300
-STAGE 1 for the `RUN` script at the end of that stage to work, since the
301
-[Chainguard Python image][cgimgs] lacks a shell, on purpose. The need to
302
-install root-level binaries is why we change `USER` temporarily here.
303
-
304
-Build it and test that it works like so:
305
-
306
-```
307
- $ make container-run &&
308
- docker exec -i $(make container-version) python --version
309
- 3.11.2
310
-```
311
-
312
-The compensation for the hassle of using Chainguard over something more
313
-general purpose like Alpine + “`apk add python`”
314
-is huge: we no longer leave a package manager sitting around inside the
315
-container, waiting for some malefactor to figure out how to abuse it.
316
-
317
-Beware that there’s a limit to this über-jail’s ability to save you when
318
-you go and provide a more capable OS layer like this. The container
319
-layer should stop an attacker from accessing any files out on the host
320
-that you haven’t explicitly mounted into the container’s namespace, but
321
-it can’t stop them from making outbound network connections or modifying
322
-the repo DB inside the container.
323
-
324
-[cgimgs]: https://github.com/chainguard-images/images/tree/main/images
325
-[distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future
326
-[MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent
327
-
328
-
329
-### 3.3 <a id="caps"></a>Dropping Unnecessary Capabilities
188
+Prior to 2023.03.26, the stock Fossil container relied on [the chroot
189
+jail feature](./chroot.md) to wall away the shell and other tools
190
+provided by [BusyBox]. It included that as a bare-bones operating system
191
+inside the container on the off chance that someone might need it for
192
+debugging, but the thing is, Fossil is self-contained, needing none of
193
+that power in the main-line use cases.
194
+
195
+Our weak “you might need it” justification collapsed when we realized
196
+you could restore this basic shell environment with a one-line change to
197
+the `Dockerfile`, as shown [below](#run).
198
+
199
+[BusyBox]: https://www.busybox.net/BusyBox.html
200
+
201
+
202
+### 3.2 <a id="caps"></a>Dropping Unnecessary Capabilities
330203
331204
The example commands above create the container with [a default set of
332205
Linux kernel capabilities][defcap]. Although Docker strips away almost
333206
all of the traditional root capabilities by default, and Fossil doesn’t
334207
need any of those it does take away, Docker does leave some enabled that
@@ -443,12 +316,12 @@
443316
444317
445318
## 4. <a id="static"></a>Extracting a Static Binary
446319
447320
Our 2-stage build process uses Alpine Linux only as a build host. Once
448
-we’ve got everything reduced to the two key static binaries — Fossil and
449
-BusyBox — we throw all the rest of it away.
321
+we’ve got everything reduced to a single static Fossil binary,
322
+we throw all the rest of it away.
450323
451324
A secondary benefit falls out of this process for free: it’s arguably
452325
the easiest way to build a purely static Fossil binary for Linux. Most
453326
modern Linux distros make this [surprisingly difficult][lsl], but Alpine’s
454327
back-to-basics nature makes static builds work the way they used to,
@@ -466,11 +339,11 @@
466339
at about 6 MiB. (It’s built stripped.)
467340
468341
[lsl]: https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead
469342
470343
471
-## 5. <a id="args"></a>Container Build Arguments
344
+## 5. <a id="custom" name="args"></a>Customization Points
472345
473346
### <a id="pkg-vers"></a> 5.1 Fossil Version
474347
475348
The default version of Fossil fetched in the build is the version in the
476349
checkout directory at the time you run it. You could override it to get
@@ -486,21 +359,21 @@
486359
$ make container-image DBFLAGS='--build-arg FSLVER=version-2.20'
487360
```
488361
489362
While you could instead use the generic
490363
“`release`” tag here, it’s better to use a specific version number
491
-since Docker caches downloaded files and tries to
364
+since container builders cache downloaded files, hoping to
492365
reuse them across builds. If you ask for “`release`” before a new
493366
version is tagged and then immediately after, you might expect to get
494367
two different tarballs, but because the underlying source tarball URL
495368
remains the same when you do that, you’ll end up reusing the
496
-old tarball from your Docker cache. This will occur
369
+old tarball from cache. This will occur
497370
even if you pass the “`docker build --no-cache`” option.
498371
499372
This is why we default to pulling the Fossil tarball by checkin ID
500373
rather than let it default to the generic “`trunk`” tag: so the URL will
501
-change each time you update your Fossil source tree, forcing Docker to
374
+change each time you update your Fossil source tree, forcing the builder to
502375
pull a fresh tarball.
503376
504377
505378
### 5.2 <a id="uids"></a>User & Group IDs
506379
@@ -517,11 +390,11 @@
517390
$ make container-image \
518391
DBFLAGS='--build-arg UID=501'
519392
```
520393
521394
This is particularly useful if you’re putting your repository on a
522
-Docker volume since the IDs “leak” out into the host environment via
395
+separate volume since the IDs “leak” out into the host environment via
523396
file permissions. You may therefore wish them to mean something on both
524397
sides of the container barrier rather than have “499” appear on the host
525398
in “`ls -l`” output.
526399
527400
@@ -557,10 +430,132 @@
557430
558431
```
559432
$ make CENGINE=podman container-run
560433
```
561434
435
+
436
+### 5.3 <a id="run"></a>Elaborating the Run Layer
437
+
438
+If you want a basic shell environment for temporary debugging of the
439
+running container, that’s easily added. Simply change this line in the
440
+`Dockerfile`…
441
+
442
+ FROM scratch AS run
443
+
444
+…to this:
445
+
446
+ FROM busybox AS run
447
+
448
+Rebuild, redeploy, and your Fossil container will have a [BusyBox]-based
449
+shell environment that you can get into via:
450
+
451
+ $ docker exec -it -u fossil $(make container-version) sh
452
+
453
+(That command assumes you built it via “`make container`” and are
454
+therefore using its versioning scheme.)
455
+
456
+Another case where you might need to replace this bare-bones “`run`”
457
+layer with something more functional is that you’re setting up [email
458
+alerts](./alerts.md) and need some way to integrate with the host’s
459
+[MTA]. There are a number of alternatives in that linked document, so
460
+for the sake of discussion, we’ll say you’ve chosen [Method
461
+2](./alerts.md#db), which requires a Tcl interpreter and its SQLite
462
+extension to push messages into the outbound email queue DB, presumably
463
+bind-mounted into the container.
464
+
465
+You can do that by replacing STAGEs 2 and 3 in the stock `Dockerfile`
466
+with this:
467
+
468
+```
469
+ ## ---------------------------------------------------------------------
470
+ ## STAGE 2: Pare that back to the bare essentials, plus Tcl.
471
+ ## ---------------------------------------------------------------------
472
+ FROM alpine AS run
473
+ ARG UID=499
474
+ ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
475
+ COPY --from=builder /tmp/fossil /bin/
476
+ COPY tools/email-sender.tcl /bin/
477
+ RUN set -x \
478
+ && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
479
+ && echo "fossil:x:${UID}:fossil" >> /etc/group \
480
+ && install -d -m 700 -o fossil -g fossil log museum \
481
+ && apk add --no-cache tcl sqlite-tcl
482
+```
483
+
484
+Build it and test that it works like so:
485
+
486
+```
487
+ $ make container-run &&
488
+ echo 'puts [info patchlevel]' |
489
+ docker exec -i $(make container-version) tclsh
490
+ 8.6.12
491
+```
492
+
493
+You should remove the `PATH` override in the “RUN”
494
+stage, since it’s written for the case where everything is in `/bin`.
495
+With these additions, we need the longer `PATH` shown above to have
496
+ready access to them all.
497
+
498
+Another useful case to consider is that you’ve installed a [server
499
+extension](./serverext.wiki) and you need an interpreter for that
500
+script. The first option above won’t work except in the unlikely case that
501
+it’s written for one of the bare-bones script interpreters that BusyBox
502
+ships.(^[BusyBox]’s `/bin/sh` is based on the old 4.4BSD Lite Almquist
503
+shell, implementing little more than what POSIX specified in 1989, plus
504
+equally stripped-down versions of `awk` and `sed`.)
505
+
506
+Let’s say the extension is written in Python. While you could handle it
507
+the same way we do with the Tcl example above, Python is more
508
+popular, giving us more options. Let’s inject a Python environment into
509
+the stock Fossil container via a suitable “[distroless]” image instead:
510
+
511
+```
512
+ ## ---------------------------------------------------------------------
513
+ ## STAGE 2: Pare that back to the bare essentials, plus Python.
514
+ ## ---------------------------------------------------------------------
515
+ FROM cgr.dev/chainguard/python:latest
516
+ USER root
517
+ ARG UID=499
518
+ ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
519
+ COPY --from=builder /tmp/fossil /bin/
520
+ COPY --from=builder /bin/busybox.static /bin/busybox
521
+ RUN [ "/bin/busybox", "--install", "/bin" ]
522
+ RUN set -x \
523
+ && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
524
+ && echo "fossil:x:${UID}:fossil" >> /etc/group \
525
+ && install -d -m 700 -o fossil -g fossil log museum
526
+```
527
+
528
+You will also have to add `busybox-static` to the APK package list in
529
+STAGE 1 for the `RUN` script at the end of that stage to work, since the
530
+[Chainguard Python image][cgimgs] lacks a shell, on purpose. The need to
531
+install root-level binaries is why we change `USER` temporarily here.
532
+
533
+Build it and test that it works like so:
534
+
535
+```
536
+ $ make container-run &&
537
+ docker exec -i $(make container-version) python --version
538
+ 3.11.2
539
+```
540
+
541
+The compensation for the hassle of using Chainguard over something more
542
+general purpose like Alpine + “`apk add python`”
543
+is huge: we no longer leave a package manager sitting around inside the
544
+container, waiting for some malefactor to figure out how to abuse it.
545
+
546
+Beware that there’s a limit to this über-jail’s ability to save you when
547
+you go and provide a more capable OS layer like this. The container
548
+layer should stop an attacker from accessing any files out on the host
549
+that you haven’t explicitly mounted into the container’s namespace, but
550
+it can’t stop them from making outbound network connections or modifying
551
+the repo DB inside the container.
552
+
553
+[cgimgs]: https://github.com/chainguard-images/images/tree/main/images
554
+[distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future
555
+[MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent
556
+
562557
563558
## 6. <a id="light"></a>Lightweight Alternatives to Docker
564559
565560
Those afflicted with sticker shock at seeing the size of a [Docker
566561
Desktop][DD] installation — 1.65 GB here — might’ve immediately
@@ -567,13 +562,13 @@
567562
“noped” out of the whole concept of containers. The first thing to
568563
realize is that when it comes to actually serving simple containers like
569564
the ones shown above is that [Docker Engine][DE] suffices, at about a
570565
quarter of the size.
571566
572
-Yet on a small server — say, a $4/month 10 GiB Digital Ocean droplet —
573
-that’s still a big chunk of your storage budget. It takes 100:1 overhead
574
-just to run a 4 MiB Fossil server container? Once again, I wouldn’t
567
+Yet on a small server — say, a $4/month ten gig Digital Ocean droplet —
568
+that’s still a big chunk of your storage budget. It takes ~60:1 overhead
569
+merely to run a Fossil server container? Once again, I wouldn’t
575570
blame you if you noped right on out of here, but if you will be patient,
576571
you will find that there are ways to run Fossil inside a container even
577572
on entry-level cloud VPSes. These are well-suited to running Fossil; you
578573
don’t have to resort to [raw Fossil service][srv] to succeed,
579574
leaving the benefits of containerization to those with bigger budgets.
@@ -587,11 +582,11 @@
587582
--publish 127.0.0.1:9999:8080
588583
```
589584
590585
The assumption is that there’s a reverse proxy running somewhere that
591586
redirects public web hits to localhost port 9999, which in turn goes to
592
-port 8080 inside the container. This use of Docker/Podman port
587
+port 8080 inside the container. This use of port
593588
publishing effectively replaces the use of the
594589
“`fossil server --localhost`” option.
595590
596591
For the nginx case, you need to add `--scgi` to these commands, and you
597592
might also need to specify `--baseurl`.
@@ -616,17 +611,18 @@
616611
617612
618613
### 6.1 <a id="nerdctl" name="containerd"></a>Stripping Docker Engine Down
619614
620615
The core of Docker Engine is its [`containerd`][ctrd] daemon and the
621
-[`runc`][runc] container runner. Add to this the out-of-core CLI program
616
+[`runc`][runc] container runtime. Add to this the out-of-core CLI program
622617
[`nerdctl`][nerdctl] and you have enough of the engine to run Fossil
623618
containers. The big things you’re missing are:
624619
625620
* **BuildKit**: The container build engine, which doesn’t matter if
626
- you’re building elsewhere and using a container registry as an
627
- intermediary between that build host and the deployment host.
621
+ you’re building elsewhere and shipping the images to the target.
622
+ A good example is using a container registry as an
623
+ intermediary between the build and deployment hosts.
628624
629625
* **SwarmKit**: A powerful yet simple orchestrator for Docker that you
630626
probably aren’t using with Fossil anyway.
631627
632628
In exchange, you get a runtime that’s about half the size of Docker
@@ -788,11 +784,11 @@
788784
* The path in the host-side part of the `Bind` value must point at the
789785
directory containing the `repo.fossil` file referenced in said
790786
command so that `/museum/repo.fossil` refers to your repo out
791787
on the host for the reasons given [above](#bind-mount).
792788
793
-That being done, we also need a generic systemd unit file called
789
+That being done, we also need a generic `systemd` unit file called
794790
`/etc/systemd/system/[email protected]`, containing:
795791
796792
----
797793
798794
```
@@ -841,11 +837,11 @@
841837
```
842838
843839
You would also need to un-drop the `CAP_NET_BIND_SERVICE` capability
844840
to allow Fossil to bind to this low-numbered port.
845841
846
-We use systemd’s template file feature to allow multiple Fossil
842
+We use the `systemd` template file feature to allow multiple Fossil
847843
servers running on a single machine, each on a different TCP port,
848844
as when proxying them out as subdirectories of a larger site.
849845
To add another project, you must first clone the base “machine” layer:
850846
851847
```
@@ -996,11 +992,11 @@
996992
you’re serving Fossil from, you may need to know which assumptions
997993
our container violates and the resulting consequences.
998994
999995
Some of it we discussed above already, but there’s one big class of
1000996
problems we haven’t covered yet. It stems from the fact that our stock
1001
-container starts a single static executable inside a barebones container
997
+container starts a single static executable inside a bare-bones container
1002998
rather than “boot” an OS image. That causes a bunch of commands to fail:
1003999
10041000
* **`machinectl poweroff`** will fail because the container
10051001
isn’t running dbus.
10061002
10071003
--- www/containers.md
+++ www/containers.md
@@ -69,11 +69,11 @@
69 The wrong way is to use the `Dockerfile COPY` command, because by baking
70 the repo into the image at build time, it will become one of the image’s
71 base layers. The end result is that each time you build a container from
72 that image, the repo will be reset to its build-time state. Worse,
73 restarting the container will do the same thing, since the base image
74 layers are immutable in Docker. This is almost certainly not what you
75 want.
76
77 The correct ways put the repo into the _container_ created from the
78 _image_, not in the image itself.
79
@@ -114,11 +114,11 @@
114 this with `chmod`: simply reload the browser, and Fossil will try again.
115
116
117 ### 2.2 <a id="bind-mount"></a>Storing the Repo Outside the Container
118
119 The simple storage method above has a problem: Docker containers are
120 designed to be killed off at the slightest cause, rebuilt, and
121 redeployed. If you do that with the repo inside the container, it gets
122 destroyed, too. The solution is to replace the “run” command above with
123 the following:
124
@@ -133,12 +133,12 @@
133 Because this bind mount maps a host-side directory (`~/museum`) into the
134 container, you don’t need to `docker cp` the repo into the container at
135 all. It still expects to find the repository as `repo.fossil` under that
136 directory, but now both the host and the container can see that repo DB.
137
138 Instead of a bind mount, you could instead set up a separate [Docker
139 volume](https://docs.docker.com/storage/volumes/), at which point you
140 _would_ need to `docker cp` the repo file into the container.
141
142 Either way, files in these mounted directories have a lifetime
143 independent of the container(s) they’re mounted into. When you need to
144 rebuild the container or its underlying image — such as to upgrade to a
@@ -183,152 +183,25 @@
183
184 ## 3. <a id="security"></a>Security
185
186 ### 3.1 <a id="chroot"></a>Why Not Chroot?
187
188 Prior to 2023.03.26, the stock Fossil container made use of [the chroot
189 jail feature](./chroot.md) in order to wall away the shell and other
190 tools provided by [BusyBox](https://www.busybox.net/BusyBox.html). This
191 author made a living for years in the early 1990s using Unix systems
192 that offered less power, so there was a legitimate worry that if someone
193 ever figured out how to get a shell on one of these Fossil containers,
194 it would constitute a powerful island from which to attack the rest of
195 the network.
196
197 The thing is, Fossil is self-contained, needing none of that power in
198 the main-line use cases. The only reason we included BusyBox in the
199 container at all was on the off chance that someone needed it for
200 debugging.
201
202 That justification collapsed when we realized you could restore this
203 basic shell environment on an as-needed basis with a one-line change to
204 the `Dockerfile`, as we show in the next section.
205
206
207 ### 3.2 <a id="run"></a>Swapping Out the Run Layer
208
209 If you want a basic shell environment for temporary debugging of the
210 running container, that’s easily added. Simply change this line in the
211 `Dockerfile`…
212
213 FROM scratch AS run
214
215 …to this:
216
217 FROM busybox AS run
218
219 Rebuild, redeploy, and your Fossil container now has a BusyBox based
220 shell environment that you can get into via:
221
222 $ docker exec -it -u fossil $(make container-version) sh
223
224 (That command assumes you built the container via “`make container`” and
225 are therefore using its versioning scheme.)
226
227 Another case where you might need to replace this bare-bones “`run`”
228 layer with something more functional is that you’re setting up [email
229 alerts](./alerts.md) and need some way to integrate with the host’s
230 [MTA]. There are a number of alternatives in that linked document, so
231 for the sake of discussion, we’ll say you’ve chosen [Method
232 2](./alerts.md#db), which requires a Tcl interpreter and its SQLite
233 extension to push messages into the outbound email queue DB, presumably
234 bind-mounted into the container.
235
236 You can do that by replacing STAGEs 2 and 3 in the stock `Dockerfile`
237 with this:
238
239 ```
240 ## ---------------------------------------------------------------------
241 ## STAGE 2: Pare that back to the bare essentials, plus Tcl.
242 ## ---------------------------------------------------------------------
243 FROM alpine AS run
244 ARG UID=499
245 ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
246 COPY --from=builder /tmp/fossil /bin/
247 COPY tools/email-sender.tcl /bin/
248 RUN set -x \
249 && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
250 && echo "fossil:x:${UID}:fossil" >> /etc/group \
251 && install -d -m 700 -o fossil -g fossil log museum \
252 && apk add --no-cache tcl sqlite-tcl
253 ```
254
255 Build it and test that it works like so:
256
257 ```
258 $ make container-run &&
259 echo 'puts [info patchlevel]' |
260 docker exec -i $(make container-version) tclsh
261 8.6.12
262 ```
263
264 You should remove the `PATH` override in the “RUN”
265 stage, since it’s written for the case where everything is in `/bin`.
266 With these additions, we need the longer `PATH` shown above to have
267 ready access to them all.
268
269 Another useful case to consider is that you’ve installed a [server
270 extension](./serverext.wiki) and you need an interpreter for that
271 script. The first option above won’t work except in the unlikely case that
272 it’s written for one of the bare-bones script interpreters that BusyBox
273 ships.(^BusyBox’s `/bin/sh` is based on the old 4.4BSD Lite Almquist
274 shell, implementing little more than what POSIX specified in 1989, plus
275 equally stripped-down versions of `awk` and `sed`.)
276
277 Let’s say the extension is written in Python. While you could handle it
278 the same way we do with the Tcl example above, Python is more
279 popular, giving us more options. Let’s inject a Python environment into
280 the stock Fossil container via a suitable “[distroless]” image instead:
281
282 ```
283 ## ---------------------------------------------------------------------
284 ## STAGE 2: Pare that back to the bare essentials, plus Python.
285 ## ---------------------------------------------------------------------
286 FROM cgr.dev/chainguard/python:latest
287 USER root
288 ARG UID=499
289 ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
290 COPY --from=builder /tmp/fossil /bin/
291 COPY --from=builder /bin/busybox.static /bin/busybox
292 RUN [ "/bin/busybox", "--install", "/bin" ]
293 RUN set -x \
294 && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
295 && echo "fossil:x:${UID}:fossil" >> /etc/group \
296 && install -d -m 700 -o fossil -g fossil log museum
297 ```
298
299 You will also have to add `busybox-static` to the APK package list in
300 STAGE 1 for the `RUN` script at the end of that stage to work, since the
301 [Chainguard Python image][cgimgs] lacks a shell, on purpose. The need to
302 install root-level binaries is why we change `USER` temporarily here.
303
304 Build it and test that it works like so:
305
306 ```
307 $ make container-run &&
308 docker exec -i $(make container-version) python --version
309 3.11.2
310 ```
311
312 The compensation for the hassle of using Chainguard over something more
313 general purpose like Alpine + “`apk add python`”
314 is huge: we no longer leave a package manager sitting around inside the
315 container, waiting for some malefactor to figure out how to abuse it.
316
317 Beware that there’s a limit to this über-jail’s ability to save you when
318 you go and provide a more capable OS layer like this. The container
319 layer should stop an attacker from accessing any files out on the host
320 that you haven’t explicitly mounted into the container’s namespace, but
321 it can’t stop them from making outbound network connections or modifying
322 the repo DB inside the container.
323
324 [cgimgs]: https://github.com/chainguard-images/images/tree/main/images
325 [distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future
326 [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent
327
328
329 ### 3.3 <a id="caps"></a>Dropping Unnecessary Capabilities
330
331 The example commands above create the container with [a default set of
332 Linux kernel capabilities][defcap]. Although Docker strips away almost
333 all of the traditional root capabilities by default, and Fossil doesn’t
334 need any of those it does take away, Docker does leave some enabled that
@@ -443,12 +316,12 @@
443
444
445 ## 4. <a id="static"></a>Extracting a Static Binary
446
447 Our 2-stage build process uses Alpine Linux only as a build host. Once
448 we’ve got everything reduced to the two key static binaries — Fossil and
449 BusyBox — we throw all the rest of it away.
450
451 A secondary benefit falls out of this process for free: it’s arguably
452 the easiest way to build a purely static Fossil binary for Linux. Most
453 modern Linux distros make this [surprisingly difficult][lsl], but Alpine’s
454 back-to-basics nature makes static builds work the way they used to,
@@ -466,11 +339,11 @@
466 at about 6 MiB. (It’s built stripped.)
467
468 [lsl]: https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead
469
470
471 ## 5. <a id="args"></a>Container Build Arguments
472
473 ### <a id="pkg-vers"></a> 5.1 Fossil Version
474
475 The default version of Fossil fetched in the build is the version in the
476 checkout directory at the time you run it. You could override it to get
@@ -486,21 +359,21 @@
486 $ make container-image DBFLAGS='--build-arg FSLVER=version-2.20'
487 ```
488
489 While you could instead use the generic
490 “`release`” tag here, it’s better to use a specific version number
491 since Docker caches downloaded files and tries to
492 reuse them across builds. If you ask for “`release`” before a new
493 version is tagged and then immediately after, you might expect to get
494 two different tarballs, but because the underlying source tarball URL
495 remains the same when you do that, you’ll end up reusing the
496 old tarball from your Docker cache. This will occur
497 even if you pass the “`docker build --no-cache`” option.
498
499 This is why we default to pulling the Fossil tarball by checkin ID
500 rather than let it default to the generic “`trunk`” tag: so the URL will
501 change each time you update your Fossil source tree, forcing Docker to
502 pull a fresh tarball.
503
504
505 ### 5.2 <a id="uids"></a>User & Group IDs
506
@@ -517,11 +390,11 @@
517 $ make container-image \
518 DBFLAGS='--build-arg UID=501'
519 ```
520
521 This is particularly useful if you’re putting your repository on a
522 Docker volume since the IDs “leak” out into the host environment via
523 file permissions. You may therefore wish them to mean something on both
524 sides of the container barrier rather than have “499” appear on the host
525 in “`ls -l`” output.
526
527
@@ -557,10 +430,132 @@
557
558 ```
559 $ make CENGINE=podman container-run
560 ```
561
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
563 ## 6. <a id="light"></a>Lightweight Alternatives to Docker
564
565 Those afflicted with sticker shock at seeing the size of a [Docker
566 Desktop][DD] installation — 1.65 GB here — might’ve immediately
@@ -567,13 +562,13 @@
567 “noped” out of the whole concept of containers. The first thing to
568 realize is that when it comes to actually serving simple containers like
569 the ones shown above is that [Docker Engine][DE] suffices, at about a
570 quarter of the size.
571
572 Yet on a small server — say, a $4/month 10 GiB Digital Ocean droplet —
573 that’s still a big chunk of your storage budget. It takes 100:1 overhead
574 just to run a 4 MiB Fossil server container? Once again, I wouldn’t
575 blame you if you noped right on out of here, but if you will be patient,
576 you will find that there are ways to run Fossil inside a container even
577 on entry-level cloud VPSes. These are well-suited to running Fossil; you
578 don’t have to resort to [raw Fossil service][srv] to succeed,
579 leaving the benefits of containerization to those with bigger budgets.
@@ -587,11 +582,11 @@
587 --publish 127.0.0.1:9999:8080
588 ```
589
590 The assumption is that there’s a reverse proxy running somewhere that
591 redirects public web hits to localhost port 9999, which in turn goes to
592 port 8080 inside the container. This use of Docker/Podman port
593 publishing effectively replaces the use of the
594 “`fossil server --localhost`” option.
595
596 For the nginx case, you need to add `--scgi` to these commands, and you
597 might also need to specify `--baseurl`.
@@ -616,17 +611,18 @@
616
617
618 ### 6.1 <a id="nerdctl" name="containerd"></a>Stripping Docker Engine Down
619
620 The core of Docker Engine is its [`containerd`][ctrd] daemon and the
621 [`runc`][runc] container runner. Add to this the out-of-core CLI program
622 [`nerdctl`][nerdctl] and you have enough of the engine to run Fossil
623 containers. The big things you’re missing are:
624
625 * **BuildKit**: The container build engine, which doesn’t matter if
626 you’re building elsewhere and using a container registry as an
627 intermediary between that build host and the deployment host.
 
628
629 * **SwarmKit**: A powerful yet simple orchestrator for Docker that you
630 probably aren’t using with Fossil anyway.
631
632 In exchange, you get a runtime that’s about half the size of Docker
@@ -788,11 +784,11 @@
788 * The path in the host-side part of the `Bind` value must point at the
789 directory containing the `repo.fossil` file referenced in said
790 command so that `/museum/repo.fossil` refers to your repo out
791 on the host for the reasons given [above](#bind-mount).
792
793 That being done, we also need a generic systemd unit file called
794 `/etc/systemd/system/[email protected]`, containing:
795
796 ----
797
798 ```
@@ -841,11 +837,11 @@
841 ```
842
843 You would also need to un-drop the `CAP_NET_BIND_SERVICE` capability
844 to allow Fossil to bind to this low-numbered port.
845
846 We use systemd’s template file feature to allow multiple Fossil
847 servers running on a single machine, each on a different TCP port,
848 as when proxying them out as subdirectories of a larger site.
849 To add another project, you must first clone the base “machine” layer:
850
851 ```
@@ -996,11 +992,11 @@
996 you’re serving Fossil from, you may need to know which assumptions
997 our container violates and the resulting consequences.
998
999 Some of it we discussed above already, but there’s one big class of
1000 problems we haven’t covered yet. It stems from the fact that our stock
1001 container starts a single static executable inside a barebones container
1002 rather than “boot” an OS image. That causes a bunch of commands to fail:
1003
1004 * **`machinectl poweroff`** will fail because the container
1005 isn’t running dbus.
1006
1007
--- www/containers.md
+++ www/containers.md
@@ -69,11 +69,11 @@
69 The wrong way is to use the `Dockerfile COPY` command, because by baking
70 the repo into the image at build time, it will become one of the image’s
71 base layers. The end result is that each time you build a container from
72 that image, the repo will be reset to its build-time state. Worse,
73 restarting the container will do the same thing, since the base image
74 layers are immutable. This is almost certainly not what you
75 want.
76
77 The correct ways put the repo into the _container_ created from the
78 _image_, not in the image itself.
79
@@ -114,11 +114,11 @@
114 this with `chmod`: simply reload the browser, and Fossil will try again.
115
116
117 ### 2.2 <a id="bind-mount"></a>Storing the Repo Outside the Container
118
119 The simple storage method above has a problem: containers are
120 designed to be killed off at the slightest cause, rebuilt, and
121 redeployed. If you do that with the repo inside the container, it gets
122 destroyed, too. The solution is to replace the “run” command above with
123 the following:
124
@@ -133,12 +133,12 @@
133 Because this bind mount maps a host-side directory (`~/museum`) into the
134 container, you don’t need to `docker cp` the repo into the container at
135 all. It still expects to find the repository as `repo.fossil` under that
136 directory, but now both the host and the container can see that repo DB.
137
138 Instead of a bind mount, you could instead set up a separate
139 [volume](https://docs.docker.com/storage/volumes/), at which point you
140 _would_ need to `docker cp` the repo file into the container.
141
142 Either way, files in these mounted directories have a lifetime
143 independent of the container(s) they’re mounted into. When you need to
144 rebuild the container or its underlying image — such as to upgrade to a
@@ -183,152 +183,25 @@
183
184 ## 3. <a id="security"></a>Security
185
186 ### 3.1 <a id="chroot"></a>Why Not Chroot?
187
188 Prior to 2023.03.26, the stock Fossil container relied on [the chroot
189 jail feature](./chroot.md) to wall away the shell and other tools
190 provided by [BusyBox]. It included that as a bare-bones operating system
191 inside the container on the off chance that someone might need it for
192 debugging, but the thing is, Fossil is self-contained, needing none of
193 that power in the main-line use cases.
194
195 Our weak “you might need it” justification collapsed when we realized
196 you could restore this basic shell environment with a one-line change to
197 the `Dockerfile`, as shown [below](#run).
198
199 [BusyBox]: https://www.busybox.net/BusyBox.html
200
201
202 ### 3.2 <a id="caps"></a>Dropping Unnecessary Capabilities
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
204 The example commands above create the container with [a default set of
205 Linux kernel capabilities][defcap]. Although Docker strips away almost
206 all of the traditional root capabilities by default, and Fossil doesn’t
207 need any of those it does take away, Docker does leave some enabled that
@@ -443,12 +316,12 @@
316
317
318 ## 4. <a id="static"></a>Extracting a Static Binary
319
320 Our 2-stage build process uses Alpine Linux only as a build host. Once
321 we’ve got everything reduced to a single static Fossil binary,
322 we throw all the rest of it away.
323
324 A secondary benefit falls out of this process for free: it’s arguably
325 the easiest way to build a purely static Fossil binary for Linux. Most
326 modern Linux distros make this [surprisingly difficult][lsl], but Alpine’s
327 back-to-basics nature makes static builds work the way they used to,
@@ -466,11 +339,11 @@
339 at about 6 MiB. (It’s built stripped.)
340
341 [lsl]: https://stackoverflow.com/questions/3430400/linux-static-linking-is-dead
342
343
344 ## 5. <a id="custom" name="args"></a>Customization Points
345
346 ### <a id="pkg-vers"></a> 5.1 Fossil Version
347
348 The default version of Fossil fetched in the build is the version in the
349 checkout directory at the time you run it. You could override it to get
@@ -486,21 +359,21 @@
359 $ make container-image DBFLAGS='--build-arg FSLVER=version-2.20'
360 ```
361
362 While you could instead use the generic
363 “`release`” tag here, it’s better to use a specific version number
364 since container builders cache downloaded files, hoping to
365 reuse them across builds. If you ask for “`release`” before a new
366 version is tagged and then immediately after, you might expect to get
367 two different tarballs, but because the underlying source tarball URL
368 remains the same when you do that, you’ll end up reusing the
369 old tarball from cache. This will occur
370 even if you pass the “`docker build --no-cache`” option.
371
372 This is why we default to pulling the Fossil tarball by checkin ID
373 rather than let it default to the generic “`trunk`” tag: so the URL will
374 change each time you update your Fossil source tree, forcing the builder to
375 pull a fresh tarball.
376
377
378 ### 5.2 <a id="uids"></a>User & Group IDs
379
@@ -517,11 +390,11 @@
390 $ make container-image \
391 DBFLAGS='--build-arg UID=501'
392 ```
393
394 This is particularly useful if you’re putting your repository on a
395 separate volume since the IDs “leak” out into the host environment via
396 file permissions. You may therefore wish them to mean something on both
397 sides of the container barrier rather than have “499” appear on the host
398 in “`ls -l`” output.
399
400
@@ -557,10 +430,132 @@
430
431 ```
432 $ make CENGINE=podman container-run
433 ```
434
435
436 ### 5.3 <a id="run"></a>Elaborating the Run Layer
437
438 If you want a basic shell environment for temporary debugging of the
439 running container, that’s easily added. Simply change this line in the
440 `Dockerfile`…
441
442 FROM scratch AS run
443
444 …to this:
445
446 FROM busybox AS run
447
448 Rebuild, redeploy, and your Fossil container will have a [BusyBox]-based
449 shell environment that you can get into via:
450
451 $ docker exec -it -u fossil $(make container-version) sh
452
453 (That command assumes you built it via “`make container`” and are
454 therefore using its versioning scheme.)
455
456 Another case where you might need to replace this bare-bones “`run`”
457 layer with something more functional is that you’re setting up [email
458 alerts](./alerts.md) and need some way to integrate with the host’s
459 [MTA]. There are a number of alternatives in that linked document, so
460 for the sake of discussion, we’ll say you’ve chosen [Method
461 2](./alerts.md#db), which requires a Tcl interpreter and its SQLite
462 extension to push messages into the outbound email queue DB, presumably
463 bind-mounted into the container.
464
465 You can do that by replacing STAGEs 2 and 3 in the stock `Dockerfile`
466 with this:
467
468 ```
469 ## ---------------------------------------------------------------------
470 ## STAGE 2: Pare that back to the bare essentials, plus Tcl.
471 ## ---------------------------------------------------------------------
472 FROM alpine AS run
473 ARG UID=499
474 ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
475 COPY --from=builder /tmp/fossil /bin/
476 COPY tools/email-sender.tcl /bin/
477 RUN set -x \
478 && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
479 && echo "fossil:x:${UID}:fossil" >> /etc/group \
480 && install -d -m 700 -o fossil -g fossil log museum \
481 && apk add --no-cache tcl sqlite-tcl
482 ```
483
484 Build it and test that it works like so:
485
486 ```
487 $ make container-run &&
488 echo 'puts [info patchlevel]' |
489 docker exec -i $(make container-version) tclsh
490 8.6.12
491 ```
492
493 You should remove the `PATH` override in the “RUN”
494 stage, since it’s written for the case where everything is in `/bin`.
495 With these additions, we need the longer `PATH` shown above to have
496 ready access to them all.
497
498 Another useful case to consider is that you’ve installed a [server
499 extension](./serverext.wiki) and you need an interpreter for that
500 script. The first option above won’t work except in the unlikely case that
501 it’s written for one of the bare-bones script interpreters that BusyBox
502 ships.(^[BusyBox]’s `/bin/sh` is based on the old 4.4BSD Lite Almquist
503 shell, implementing little more than what POSIX specified in 1989, plus
504 equally stripped-down versions of `awk` and `sed`.)
505
506 Let’s say the extension is written in Python. While you could handle it
507 the same way we do with the Tcl example above, Python is more
508 popular, giving us more options. Let’s inject a Python environment into
509 the stock Fossil container via a suitable “[distroless]” image instead:
510
511 ```
512 ## ---------------------------------------------------------------------
513 ## STAGE 2: Pare that back to the bare essentials, plus Python.
514 ## ---------------------------------------------------------------------
515 FROM cgr.dev/chainguard/python:latest
516 USER root
517 ARG UID=499
518 ENV PATH "/sbin:/usr/sbin:/bin:/usr/bin"
519 COPY --from=builder /tmp/fossil /bin/
520 COPY --from=builder /bin/busybox.static /bin/busybox
521 RUN [ "/bin/busybox", "--install", "/bin" ]
522 RUN set -x \
523 && echo "fossil:x:${UID}:${UID}:User:/museum:/false" >> /etc/passwd \
524 && echo "fossil:x:${UID}:fossil" >> /etc/group \
525 && install -d -m 700 -o fossil -g fossil log museum
526 ```
527
528 You will also have to add `busybox-static` to the APK package list in
529 STAGE 1 for the `RUN` script at the end of that stage to work, since the
530 [Chainguard Python image][cgimgs] lacks a shell, on purpose. The need to
531 install root-level binaries is why we change `USER` temporarily here.
532
533 Build it and test that it works like so:
534
535 ```
536 $ make container-run &&
537 docker exec -i $(make container-version) python --version
538 3.11.2
539 ```
540
541 The compensation for the hassle of using Chainguard over something more
542 general purpose like Alpine + “`apk add python`”
543 is huge: we no longer leave a package manager sitting around inside the
544 container, waiting for some malefactor to figure out how to abuse it.
545
546 Beware that there’s a limit to this über-jail’s ability to save you when
547 you go and provide a more capable OS layer like this. The container
548 layer should stop an attacker from accessing any files out on the host
549 that you haven’t explicitly mounted into the container’s namespace, but
550 it can’t stop them from making outbound network connections or modifying
551 the repo DB inside the container.
552
553 [cgimgs]: https://github.com/chainguard-images/images/tree/main/images
554 [distroless]: https://www.chainguard.dev/unchained/minimal-container-images-towards-a-more-secure-future
555 [MTA]: https://en.wikipedia.org/wiki/Message_transfer_agent
556
557
558 ## 6. <a id="light"></a>Lightweight Alternatives to Docker
559
560 Those afflicted with sticker shock at seeing the size of a [Docker
561 Desktop][DD] installation — 1.65 GB here — might’ve immediately
@@ -567,13 +562,13 @@
562 “noped” out of the whole concept of containers. The first thing to
563 realize is that when it comes to actually serving simple containers like
564 the ones shown above is that [Docker Engine][DE] suffices, at about a
565 quarter of the size.
566
567 Yet on a small server — say, a $4/month ten gig Digital Ocean droplet —
568 that’s still a big chunk of your storage budget. It takes ~60:1 overhead
569 merely to run a Fossil server container? Once again, I wouldn’t
570 blame you if you noped right on out of here, but if you will be patient,
571 you will find that there are ways to run Fossil inside a container even
572 on entry-level cloud VPSes. These are well-suited to running Fossil; you
573 don’t have to resort to [raw Fossil service][srv] to succeed,
574 leaving the benefits of containerization to those with bigger budgets.
@@ -587,11 +582,11 @@
582 --publish 127.0.0.1:9999:8080
583 ```
584
585 The assumption is that there’s a reverse proxy running somewhere that
586 redirects public web hits to localhost port 9999, which in turn goes to
587 port 8080 inside the container. This use of port
588 publishing effectively replaces the use of the
589 “`fossil server --localhost`” option.
590
591 For the nginx case, you need to add `--scgi` to these commands, and you
592 might also need to specify `--baseurl`.
@@ -616,17 +611,18 @@
611
612
613 ### 6.1 <a id="nerdctl" name="containerd"></a>Stripping Docker Engine Down
614
615 The core of Docker Engine is its [`containerd`][ctrd] daemon and the
616 [`runc`][runc] container runtime. Add to this the out-of-core CLI program
617 [`nerdctl`][nerdctl] and you have enough of the engine to run Fossil
618 containers. The big things you’re missing are:
619
620 * **BuildKit**: The container build engine, which doesn’t matter if
621 you’re building elsewhere and shipping the images to the target.
622 A good example is using a container registry as an
623 intermediary between the build and deployment hosts.
624
625 * **SwarmKit**: A powerful yet simple orchestrator for Docker that you
626 probably aren’t using with Fossil anyway.
627
628 In exchange, you get a runtime that’s about half the size of Docker
@@ -788,11 +784,11 @@
784 * The path in the host-side part of the `Bind` value must point at the
785 directory containing the `repo.fossil` file referenced in said
786 command so that `/museum/repo.fossil` refers to your repo out
787 on the host for the reasons given [above](#bind-mount).
788
789 That being done, we also need a generic `systemd` unit file called
790 `/etc/systemd/system/[email protected]`, containing:
791
792 ----
793
794 ```
@@ -841,11 +837,11 @@
837 ```
838
839 You would also need to un-drop the `CAP_NET_BIND_SERVICE` capability
840 to allow Fossil to bind to this low-numbered port.
841
842 We use the `systemd` template file feature to allow multiple Fossil
843 servers running on a single machine, each on a different TCP port,
844 as when proxying them out as subdirectories of a larger site.
845 To add another project, you must first clone the base “machine” layer:
846
847 ```
@@ -996,11 +992,11 @@
992 you’re serving Fossil from, you may need to know which assumptions
993 our container violates and the resulting consequences.
994
995 Some of it we discussed above already, but there’s one big class of
996 problems we haven’t covered yet. It stems from the fact that our stock
997 container starts a single static executable inside a bare-bones container
998 rather than “boot” an OS image. That causes a bunch of commands to fail:
999
1000 * **`machinectl poweroff`** will fail because the container
1001 isn’t running dbus.
1002
1003

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button