135 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| { config, lib, pkgs, ... }:
 | |
| 
 | |
| with lib;
 | |
| with builtins;
 | |
| 
 | |
| let
 | |
|   cfg = config.virtualisation;
 | |
| 
 | |
|   sanitizeImageName = image: replaceStrings ["/"] ["-"] image.imageName;
 | |
|   hash = drv: head (split "-" (baseNameOf drv.outPath));
 | |
|   # The label of an ext4 FS is limited to 16 bytes
 | |
|   labelFromImage = image: substring 0 16 (hash image);
 | |
| 
 | |
|   # The Docker image is loaded and some files from /var/lib/docker/
 | |
|   # are written into a qcow image.
 | |
|   preload = image: pkgs.vmTools.runInLinuxVM (
 | |
|     pkgs.runCommand "docker-preload-image-${sanitizeImageName image}" {
 | |
|       buildInputs = with pkgs; [ docker e2fsprogs utillinux curl kmod ];
 | |
|       preVM = pkgs.vmTools.createEmptyImage {
 | |
|         size = cfg.dockerPreloader.qcowSize;
 | |
|         fullName = "docker-deamon-image.qcow2";
 | |
|       };
 | |
|     }
 | |
|     ''
 | |
|       mkfs.ext4 /dev/vda
 | |
|       e2label /dev/vda ${labelFromImage image}
 | |
|       mkdir -p /var/lib/docker
 | |
|       mount -t ext4 /dev/vda /var/lib/docker
 | |
| 
 | |
|       modprobe overlay
 | |
| 
 | |
|       # from https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount
 | |
|       mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
 | |
|       cd /sys/fs/cgroup
 | |
|       for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
 | |
|         mkdir -p $sys
 | |
|         if ! mountpoint -q $sys; then
 | |
|           if ! mount -n -t cgroup -o $sys cgroup $sys; then
 | |
|             rmdir $sys || true
 | |
|           fi
 | |
|         fi
 | |
|       done
 | |
| 
 | |
|       dockerd -H tcp://127.0.0.1:5555 -H unix:///var/run/docker.sock &
 | |
| 
 | |
|       until $(curl --output /dev/null --silent --connect-timeout 2 http://127.0.0.1:5555); do
 | |
|         printf '.'
 | |
|         sleep 1
 | |
|       done
 | |
| 
 | |
|       docker load -i ${image}
 | |
| 
 | |
|       kill %1
 | |
|       find /var/lib/docker/ -maxdepth 1 -mindepth 1 -not -name "image" -not -name "overlay2" | xargs rm -rf
 | |
|     '');
 | |
| 
 | |
|   preloadedImages = map preload cfg.dockerPreloader.images;
 | |
| 
 | |
| in
 | |
| 
 | |
| {
 | |
|   options.virtualisation.dockerPreloader = {
 | |
|     images = mkOption {
 | |
|       default = [ ];
 | |
|       type = types.listOf types.package;
 | |
|       description =
 | |
|       ''
 | |
|         A list of Docker images to preload (in the /var/lib/docker directory).
 | |
|       '';
 | |
|     };
 | |
|     qcowSize = mkOption {
 | |
|       default = 1024;
 | |
|       type = types.int;
 | |
|       description =
 | |
|       ''
 | |
|         The size (MB) of qcow files.
 | |
|       '';
 | |
|     };
 | |
|   };
 | |
| 
 | |
|   config = mkIf (cfg.dockerPreloader.images != []) {
 | |
|     assertions = [{
 | |
|       # If docker.storageDriver is null, Docker choose the storage
 | |
|       # driver. So, in this case, we cannot be sure overlay2 is used.
 | |
|       assertion = cfg.docker.storageDriver == "overlay2"
 | |
|         || cfg.docker.storageDriver == "overlay"
 | |
|         || cfg.docker.storageDriver == null;
 | |
|       message = "The Docker image Preloader only works with overlay2 storage driver!";
 | |
|     }];
 | |
| 
 | |
|     virtualisation.qemu.options =
 | |
|       map (path: "-drive if=virtio,file=${path}/disk-image.qcow2,readonly,media=cdrom,format=qcow2")
 | |
|       preloadedImages;
 | |
| 
 | |
| 
 | |
|     # All attached QCOW files are mounted and their contents are linked
 | |
|     # to /var/lib/docker/ in order to make image available.
 | |
|     systemd.services.docker-preloader = {
 | |
|       description = "Preloaded Docker images";
 | |
|       wantedBy = ["docker.service"];
 | |
|       after = ["network.target"];
 | |
|       path = with pkgs; [ mount rsync jq ];
 | |
|       script = ''
 | |
|         mkdir -p /var/lib/docker/overlay2/l /var/lib/docker/image/overlay2
 | |
|         echo '{}' > /tmp/repositories.json
 | |
| 
 | |
|         for i in ${concatStringsSep " " (map labelFromImage cfg.dockerPreloader.images)}; do
 | |
|           mkdir -p /mnt/docker-images/$i
 | |
| 
 | |
|           # The ext4 label is limited to 16 bytes
 | |
|           mount /dev/disk/by-label/$(echo $i | cut -c1-16) -o ro,noload /mnt/docker-images/$i
 | |
| 
 | |
|           find /mnt/docker-images/$i/overlay2/ -maxdepth 1 -mindepth 1 -not -name l\
 | |
|              -exec ln -s '{}' /var/lib/docker/overlay2/ \;
 | |
|           cp -P /mnt/docker-images/$i/overlay2/l/* /var/lib/docker/overlay2/l/
 | |
| 
 | |
|           rsync -a /mnt/docker-images/$i/image/ /var/lib/docker/image/
 | |
| 
 | |
|           # Accumulate image definitions
 | |
|           cp /tmp/repositories.json /tmp/repositories.json.tmp
 | |
|           jq -s '.[0] * .[1]' \
 | |
|             /tmp/repositories.json.tmp \
 | |
|             /mnt/docker-images/$i/image/overlay2/repositories.json \
 | |
|             > /tmp/repositories.json
 | |
|         done
 | |
| 
 | |
|         mv /tmp/repositories.json /var/lib/docker/image/overlay2/repositories.json
 | |
|       '';
 | |
|       serviceConfig = {
 | |
|         Type = "oneshot";
 | |
|       };
 | |
|     };
 | |
|   };
 | |
| }
 | 
