From 715cfbd02ccfbfe6de60c24486ffdf549f11a7e2 Mon Sep 17 00:00:00 2001 From: Avi Deitcher Date: Wed, 26 Apr 2017 09:40:04 +0300 Subject: [PATCH] First cut of swap image and example Signed-off-by: Avi Deitcher Fix spaces after commas Remove MAINTAINER Signed-off-by: Avi Deitcher Simplify swap.yml example to remove files section Signed-off-by: Avi Deitcher Switch swap.sh to sh from bash and remove bash from image Signed-off-by: Avi Deitcher Replace fallocate with dd and update calculation function to support it Signed-off-by: Avi Deitcher Fix indentation Signed-off-by: Avi Deitcher Change link to swap to just swap.sh Signed-off-by: Avi Deitcher Fix indent Signed-off-by: Avi Deitcher --- examples/swap.yml | 83 +++++++++++++++++++++++++++ pkg/swap/Dockerfile | 13 +++++ pkg/swap/Makefile | 30 ++++++++++ pkg/swap/README.md | 85 ++++++++++++++++++++++++++++ pkg/swap/swap.sh | 135 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 346 insertions(+) create mode 100644 examples/swap.yml create mode 100644 pkg/swap/Dockerfile create mode 100644 pkg/swap/Makefile create mode 100644 pkg/swap/README.md create mode 100755 pkg/swap/swap.sh diff --git a/examples/swap.yml b/examples/swap.yml new file mode 100644 index 000000000..6aea34c69 --- /dev/null +++ b/examples/swap.yml @@ -0,0 +1,83 @@ +kernel: + image: "linuxkit/kernel:4.9.x" + cmdline: "console=ttyS0 console=tty0 page_poison=1" +init: + - linuxkit/init:42fe8cb1508b3afed39eb89821906e3cc7a70551 + - linuxkit/runc:b0fb122e10dbb7e4e45115177a61a3f8d68c19a9 + - linuxkit/containerd:60e2486a74c665ba4df57e561729aec20758daed + - linuxkit/ca-certificates:eabc5a6e59f05aa91529d80e9a595b85b046f935 +onboot: + - name: sysctl + image: "linuxkit/sysctl:2cf2f9d5b4d314ba1bfc22b2fe931924af666d8c" + net: host + pid: host + ipc: host + capabilities: + - CAP_SYS_ADMIN + readonly: true + - name: binfmt + image: "linuxkit/binfmt:8881283ac627be1542811bd25c85e7782aebc692" + binds: + - /proc/sys/fs/binfmt_misc:/binfmt_misc + readonly: true + - name: dhcpcd + image: "linuxkit/dhcpcd:48e249ebef6a521eed886b3bce032db69fbb4afa" + binds: + - /var:/var + - /tmp/etc:/etc + capabilities: + - CAP_NET_ADMIN + - CAP_NET_BIND_SERVICE + - CAP_NET_RAW + net: host + command: ["/sbin/dhcpcd", "--nobackground", "-f", "/dhcpcd.conf", "-1"] + - name: format + image: "linuxkit/format:53748000acf515549d398e6ae68545c26c0f3a2e" + binds: + - /dev:/dev + capabilities: + - CAP_SYS_ADMIN + - CAP_MKNOD + - name: mount + image: "linuxkit/mount:d2669e7c8ddda99fa0618a414d44261eba6e299a" + binds: + - /dev:/dev + - /var:/var:rshared,rbind + capabilities: + - CAP_SYS_ADMIN + rootfsPropagation: shared + command: ["/mount.sh", "/var/external"] + - name: swap + image: "linuxkit/swap:68deee354ec4bbed405c5efedb25d60bb799b07d" + net: host + pid: host + capabilities: + - CAP_SYS_ADMIN + readonly: true + binds: + - /var:/var + - /dev:/dev + command: ["swap.sh", "--path", "/var/external/swap", "--size","1G"] +services: + - name: rngd + image: "linuxkit/rngd:3dad6dd43270fa632ac031e99d1947f20b22eec9" + capabilities: + - CAP_SYS_ADMIN + oomScoreAdj: -800 + readonly: true + - name: nginx + image: "nginx:alpine" + capabilities: + - CAP_NET_BIND_SERVICE + - CAP_CHOWN + - CAP_SETUID + - CAP_SETGID + - CAP_DAC_OVERRIDE + net: host +trust: + image: + - linuxkit/kernel +outputs: + - format: kernel+initrd + - format: iso-bios + - format: iso-efi diff --git a/pkg/swap/Dockerfile b/pkg/swap/Dockerfile new file mode 100644 index 000000000..7aa6e8e5c --- /dev/null +++ b/pkg/swap/Dockerfile @@ -0,0 +1,13 @@ +FROM alpine:3.5 + +# swap command - only minimal Alpine install + +# use util-linux to get swapfile utils +# at some point, hopefully use our own mkswap and swapon +RUN apk add --update util-linux + +# add the entrypoint and make it executable +COPY . ./ + +# ENTRYPOINT +ENTRYPOINT swap.sh diff --git a/pkg/swap/Makefile b/pkg/swap/Makefile new file mode 100644 index 000000000..06e89bdcd --- /dev/null +++ b/pkg/swap/Makefile @@ -0,0 +1,30 @@ +# copy from mount +.PHONY: tag push clean + +BASE=alpine:3.5 +IMAGE=linuxkit/swap + +default: push + +hash: Dockerfile swap.sh + DOCKER_CONTENT_TRUST=1 docker pull $(BASE) + tar cf - $^ | docker build --no-cache -t $(IMAGE):build - + docker run --rm --entrypoint /bin/sh $(IMAGE):build -c "cat $^ /lib/apk/db/installed | sha1sum" | sed 's/ .*//' > $@ + +push: hash + docker pull $(IMAGE):$(shell cat hash) || \ + (docker tag $(IMAGE):build $(IMAGE):$(shell cat hash) && \ + docker push $(IMAGE):$(shell cat hash)) + docker rmi $(IMAGE):build + rm -f hash + +tag: hash + docker pull $(IMAGE):$(shell cat hash) || \ + docker tag $(IMAGE):build $(IMAGE):$(shell cat hash) + docker rmi $(IMAGE):build + rm -f hash + +clean: + rm -f hash + +.DELETE_ON_ERROR: diff --git a/pkg/swap/README.md b/pkg/swap/README.md new file mode 100644 index 000000000..f964bd411 --- /dev/null +++ b/pkg/swap/README.md @@ -0,0 +1,85 @@ +# LinuxKit Swap +Image to enable creation of a swap file for a [linuxkit](https://github.com/linuxkit/linuxkit)-generated image. + + +## Usage +Normally, unless you are running explicitly in a desktop version, LinuxKit images do not have swap enabled. If you want swap, add the following to your `moby.yml`: + +``` +onboot: + - name: swap + image: "linuxkit/swap:1.0.0" + net: none + pid: host + capabilities: + - CAP_SYS_ADMIN + readonly: true + binds: + - /dev:/dev + - /var:/var + command: ["swap.sh","--path","/var/external/swap","--size","2G"] +``` + +Note that you **nust** mount the following: + +* `/var` to `/var` so it can place the swapfile in the right location. +* `/dev` to `/dev` so it can do the right thing for devices + +### Options + +Options are passed to it via command-line options. The following are the options. Details follow. + +|Option|Parameter|Default|Required|Notes| +|---|---|---|---|---| +|`--path`|Path to file as seen in the underlying OS||**Yes**|| +|`--size`|Target swapfile size||**Yes**|| +|`--condition`|_condition_||No|Condition that must be met to create a swapfile| +|`--debug`||No|Turns on verbose output from the command making the swap| + + +#### File +You can create a swapfile at the given path. You **must** provide exactly one swapfile path, or none will be created; there is no default. Passing fewer than or more than one `--path` option causes the container to exit with an error. + +The option `--path` takes a single argument _path_, path to the swapfile, e.g. `/var/mnt/swap2`. + +You **always** should put the swap file somewhere under `/var`, since that is where read-writable files and mounts exist in linuxkit images. + +#### Size +`--size ` indicates the desired swapfile size, e.g. `2G` `100M` `5670K` `8765432`. There is no default. Acceptable size units are `G`, `M`, `K` and bytes of no unit provided. + +If disk space on the requested partition is insufficient to create the swapfile, the container exits with an error. + +#### Conditions +You may want to create a swapfile only if certain conditions are met. Supported conditions are: + +* An external disk is available +* Partition on which the swapfile will sit is of a minimum size. + +**All** conditions must be met. If a condition fails, the swapfile is not created, but the container exits with no error, unless you set the condition as `required`. + +Conditions are structured as follows: _type_:_parameter_:_required_ + +In all cases, you may leave off _required_ if you wish to treat it as false, i.e. do not create swapfile but exit with no error if the condition is not met. + +##### Partition exists +LinuxKit may be running from a small disk, and you only want to run if a particular large external disk is available. In that case, pass `--condition part::` to indicate that swapfile is to be created only if _path_ already exists and is a mount point. + +Example: `--condition part:/var/mnt/external` + +##### Size +You may set a minimum size for a partition (likely the one on which the swapfile will be created) using the `size` condition of the format `--condition size:::` to indicate that swapfile is to be created only if the partition on which _path_ exists is of minimum size _size_. Acceptable sizes are identical to those for the swapfile. + +Examples: + +* `--condition partsize:/var/mnt/external:100G:true` + + +## Example +An example yml file is included in [examples/swap.yml](../../examples/swap.yml). `swap.yml`. Note that you need to attach an external disk - trying to create a swapfile in tmpfs `/var` will fail. + +The sample command to run the enclosed is: + +``` +moby build swap.yml +moby run -disk-size 4096 swap +``` diff --git a/pkg/swap/swap.sh b/pkg/swap/swap.sh new file mode 100755 index 000000000..77eff3358 --- /dev/null +++ b/pkg/swap/swap.sh @@ -0,0 +1,135 @@ +#!/bin/sh + +set -e + +while [ $# -gt 1 ]; do + key="$1" + + case $key in + --debug) + set -x + ;; + --path) + path="$2" + shift # past argument + ;; + --size) + size="$2" + shift # past argument + ;; + --encrypt) + ENCRYPT=true + ;; + --condition) + CONDITIONS="$CONDITIONS $2" + shift + ;; + *) + echo "Unknown option passed to swapmaker: $key" # unknown option + exit 1 + ;; + esac + shift # past argument or value +done + +function disksize_to_count { + local blocksize=$1 + local origsize=$2 + local ret + case $origsize in + *G) + ret=$(( ${origsize%%G} * 1024 * 1024 * 1024 )) + ;; + *M) + ret=$(( ${origsize%%M} * 1024 * 1024 )) + ;; + *K) + ret=$(( ${origsize%%K} * 1024 )) + ;; + *) + ret=$origsize + ;; + esac + ret=$(( $ret / $blocksize )) + echo $ret +} + + +## make sure path is valid +if [ -z "${path}" ]; then + echo "swap: --file must be defined" + exit 1 +fi +if [ "${path:0:5}" != "/var/" -o ${#path} -lt 6 ]; then + echo "--file option must be under /var" + exit 1 +fi +if [ -z "${size}" ]; then + echo "swap: --size must be defined" + exit 1 +fi + + + +## check each of our conditions +for cond in $CONDITIONS; do + # split the condition parts + IFS=: read condtype arg1 arg2 arg3 arg4 <<< "$cond" + case $condtype in + part) + partition=$arg1 + required=$arg2 + # check that the path exists as its own mount point + set +e + grep -qs $partition /proc/mounts + is_mnt=$? + set -e + if [ $is_mnt -ne 0 ]; then + [ "$required" == "true" ] && exit 1 + exit 0 + fi + ;; + partsize) + partition=$arg1 + minsize=$arg2 + required=$arg3 + # check that the partition on which it exists has sufficient size + partsize=$(df -k $partition | tail -1 | awk '{print $2}') + partsize=$(( $partsize * 1024 )) + # convert minsize to bytes + minsize=$(disksize_to_count 1 $minsize) + if [ $partsize -lt $minsize ]; then + [ "$required" == "true" ] && exit 1 + exit 0 + fi + ;; + *) + echo "Unknown condition: $cond" + exit 1 + ;; + esac +done +## if a condition failed: +### Required? exit 1 +### Else? exit 0 + + +## Allocate the file +dd if=/dev/zero of=$path bs=1024 count=$(disksize_to_count 1024 $size) +chmod 0600 $path + +## was it encrypted? use cryptsetup and get the mapped device +if [ "$ENCRYPT" == "true" ]; then + # might need + #loop=$(losetup -f) + #losetup ${loop} ${path} + + cryptsetup open --type plain --key-file /dev/urandom --key-size=256 --hash=sha256 --cipher=aes-cbc-essiv --offset=0 ${path} swapfile + SWAPDEV=/dev/mapper/swapfile +else + SWAPDEV=$path +fi + +## mkswap and swapon the device +/sbin/mkswap $SWAPDEV +/sbin/swapon $SWAPDEV