Use skopeo and DIND to download container images (#1561)

* Use skopeo to download container images

Separate container download from image build. This will allow to share
the downloaded images between multiple builds.

We won't store the Supervisor container with the version tag, just with
the latest tag. This allows to simplify the procedure a bit. It seems
there is no downside to this approach.

* Use official Docker in Docker images to build data partition

Instead of building our own Debian based image let's use the official
Docker in Docker image. This avoids building an image for the hassio
data partition and speeds up build as well.

This calls mount commands using sudo to mount the data partition as part
of the buildroot build now. This is not much different from before as
mount has been called as root inside the container, essentially equates
to the same "isolation" level.

* Use image digest as part of the file name

The landing page has no version information in the tag. To avoid
potentially source caching issues, use the digest as part of the file
name.
This commit is contained in:
Stefan Agner 2021-10-04 10:06:26 +02:00 committed by GitHub
parent 693507aaca
commit 762f098c14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 113 additions and 137 deletions

View File

@ -46,6 +46,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
vim \
&& rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y --no-install-recommends \
skopeo \
jq \
&& rm -rf /var/lib/apt/lists/*
# Init entry
COPY scripts/entry.sh /usr/sbin/
ENTRYPOINT ["/usr/sbin/entry.sh"]

View File

@ -1,23 +0,0 @@
FROM debian:buster
# Set shell
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Docker
RUN apt-get update && apt-get install -y --no-install-recommends \
apt-transport-https \
ca-certificates \
curl \
jq \
gpg-agent \
gpg \
dirmngr \
software-properties-common \
&& curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - \
&& add-apt-repository "deb https://download.docker.com/linux/debian $(lsb_release -cs) stable" \
&& apt-get update && apt-get install -y --no-install-recommends \
docker-ce docker-ce-cli containerd.io \
&& rm -rf /var/lib/apt/lists/*
COPY hostapp.sh /usr/bin/
ENTRYPOINT ["/usr/bin/hostapp.sh"]

View File

@ -1,104 +0,0 @@
#!/bin/bash
set -e
ARCH=
MACHINE=
DATA_IMG="/export/data.ext4"
VERSION_URL="https://version.home-assistant.io/stable.json"
APPARMOR_URL="https://version.home-assistant.io/apparmor.txt"
# Parse
while [[ $# -gt 0 ]]; do
key=$1
case $key in
--arch)
ARCH=$2
shift
;;
--machine)
MACHINE=$2
shift
;;
*)
exit 1
;;
esac
shift
done
VERSION_JSON="$(curl -s ${VERSION_URL})"
SUPERVISOR=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.supervisor | sub("{arch}"; $arch)')
DNS=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.dns | sub("{arch}"; $arch)')
AUDIO=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.audio | sub("{arch}"; $arch)')
CLI=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.cli | sub("{arch}"; $arch)')
MULTICAST=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.multicast | sub("{arch}"; $arch)')
OBSERVER=$(echo "${VERSION_JSON}" | jq -e -r --arg arch "${ARCH}" '.images.observer | sub("{arch}"; $arch)')
LANDINGPAGE=$(echo "${VERSION_JSON}" | jq -e -r --arg machine "${MACHINE}" '.images.core | sub("{machine}"; $machine)'):landingpage
SUPERVISOR_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.supervisor')
DNS_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.dns')
CLI_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.cli')
AUDIO_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.audio')
MULTICAST_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.multicast')
OBSERVER_VERSION=$(echo "${VERSION_JSON}" | jq -e -r '.observer')
# Make image
truncate --size="1G" "${DATA_IMG}"
mkfs.ext4 -L "hassos-data" -E lazy_itable_init=0,lazy_journal_init=0 ${DATA_IMG}
# Setup local user
if [ "${BUILDER_UID:0}" -ne 0 ] && [ "${BUILDER_GID:0}" -ne 0 ]; then
groupadd -g "${BUILDER_GID}" builder
useradd -m -u "${BUILDER_UID}" -g "${BUILDER_GID}" -G docker builder
chown builder:builder ${DATA_IMG}
fi
# Mount / init file structs
mkdir -p /mnt/data/
mount -o loop ${DATA_IMG} /mnt/data
mkdir -p /mnt/data/docker
# Run dockerd
dockerd -s overlay2 -g /mnt/data/docker &
DOCKER_PID=$!
DOCKER_COUNT=0
until docker info >/dev/null 2>&1; do
if [ ${DOCKER_COUNT} -gt 30 ]; then
exit 1
fi
sleep 1
DOCKER_COUNT=$((DOCKER_COUNT + 1))
done
# Install supervisor
docker pull "${SUPERVISOR}:${SUPERVISOR_VERSION}"
# Need match with the tag used by OS
docker tag "${SUPERVISOR}:${SUPERVISOR_VERSION}" "homeassistant/${ARCH}-hassio-supervisor:latest"
# Install Plugins
docker pull "${CLI}:${CLI_VERSION}"
docker pull "${DNS}:${DNS_VERSION}"
docker pull "${AUDIO}:${AUDIO_VERSION}"
docker pull "${MULTICAST}:${MULTICAST_VERSION}"
docker pull "${OBSERVER}:${OBSERVER_VERSION}"
# Install landing page
docker pull "${LANDINGPAGE}"
# Setup AppArmor
mkdir -p "/mnt/data/supervisor/apparmor"
curl -sL -o "/mnt/data/supervisor/apparmor/hassio-supervisor" "${APPARMOR_URL}"
# Finish
kill $DOCKER_PID && wait $DOCKER_PID
# Unmount resource
if ! umount /mnt/data; then
umount -f /mnt/data || echo "umount force fails!"
fi
exit 0

View File

@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -e
build_dir=$1
dst_dir=$2
data_img="${dst_dir}/data.ext4"
# Make image
rm -f "${data_img}"
truncate --size="1280M" "${data_img}"
mkfs.ext4 -L "hassos-data" -E lazy_itable_init=0,lazy_journal_init=0 "${data_img}"
# Mount / init file structs
mkdir -p "${build_dir}/data/"
sudo mount -o loop,discard "${data_img}" "${build_dir}/data/"
# Use official Docker in Docker images
# Ideally we use the same version as Buildroot is using in case the
# overlayfs2 storage format changes
container=$(docker run --privileged -e DOCKER_TLS_CERTDIR="" \
-v "${build_dir}/data/":/data \
-v "${build_dir}/data/docker/":/var/lib/docker \
-v "${build_dir}":/build \
-d docker:20.10-dind --storage-driver overlay2)
docker exec "${container}" sh /build/dind-import-containers.sh
docker stop "${container}"
# Unmount data image
sudo umount "${build_dir}/data/"

View File

@ -0,0 +1,19 @@
#!/bin/sh
set -e
APPARMOR_URL="https://version.home-assistant.io/apparmor.txt"
# Install supervisor
for image in /build/images/*.tar; do
docker load --input "${image}"
done
# Tag the Supervisor how the OS expects it to be tagged
supervisor=$(docker images --filter "label=io.hass.type=supervisor" --quiet)
arch=$(docker inspect --format '{{ index .Config.Labels "io.hass.arch" }}' "${supervisor}")
docker tag "${supervisor}" "homeassistant/${arch}-hassio-supervisor:latest"
# Setup AppArmor
mkdir -p "/data/supervisor/apparmor"
wget -O "/data/supervisor/apparmor/hassio-supervisor" "${APPARMOR_URL}"

View File

@ -0,0 +1,38 @@
#!/usr/bin/env bash
arch=$1
machine=$2
version_json=$3
image_json_name=$4
dl_dir=$5
dst_dir=$6
set -e
image_name=$(jq -e -r --arg image_json_name "${image_json_name}" \
--arg arch "${arch}" --arg machine "${machine}" \
'.images[$image_json_name] | sub("{arch}"; $arch) | sub("{machine}"; $machine)' \
< "${version_json}")
image_tag=$(jq -e -r --arg image_json_name "${image_json_name}" \
'.[$image_json_name]' < "${version_json}")
full_image_name="${image_name}:${image_tag}"
image_digest=$(skopeo inspect "docker://${full_image_name}" | jq -r '.Digest')
# Cleanup image name file name use
image_file_name="${full_image_name//[:\/]/_}@${image_digest//[:\/]/_}"
image_file_path="${dl_dir}/${image_file_name}.tar"
dst_image_file_path="${dst_dir}/${image_file_name}.tar"
if [ -f "${image_file_path}" ]
then
echo "Skipping download of existing image: ${full_image_name} (digest ${image_digest})"
cp "${image_file_path}" "${dst_image_file_path}"
exit 0
fi
# Use digest here to avoid race conditions of any sort...
echo "Fetching image: ${full_image_name}"
skopeo copy "docker://${image_name}@${image_digest}" "docker-archive:${image_file_path}:${full_image_name}"
cp "${image_file_path}" "${dst_image_file_path}"

View File

@ -9,18 +9,27 @@ HASSIO_LICENSE = Apache License 2.0
HASSIO_LICENSE_FILES = $(BR2_EXTERNAL_HASSOS_PATH)/../LICENSE
HASSIO_SITE = $(BR2_EXTERNAL_HASSOS_PATH)/package/hassio
HASSIO_SITE_METHOD = local
HASSIO_VERSION_URL = "https://version.home-assistant.io/stable.json"
HASSIO_CONTAINER_IMAGES_ARCH = supervisor dns audio cli multicast observer core
define HASSIO_CONFIGURE_CMDS
# Deploy only landing page for "core" by setting version to "landingpage"
curl -s $(HASSIO_VERSION_URL) | jq '.core = "landingpage"' > $(@D)/stable.json
$(Q)mkdir -p $(@D)/images
$(Q)mkdir -p $(HASSIO_DL_DIR)
$(foreach image,$(HASSIO_CONTAINER_IMAGES_ARCH),\
$(BR2_EXTERNAL_HASSOS_PATH)/package/hassio/fetch-container-image.sh \
$(BR2_PACKAGE_HASSIO_ARCH) $(BR2_PACKAGE_HASSIO_MACHINE) $(@D)/stable.json $(image) "$(HASSIO_DL_DIR)" "$(@D)/images"
)
define HASSIO_BUILD_CMDS
docker build --tag hassos-hostapps $(@D)/builder
endef
define HASSIO_INSTALL_TARGET_CMDS
docker run --rm --privileged \
-e BUILDER_UID="$(shell id -u)" -e BUILDER_GID="$(shell id -g)" \
-v $(BINARIES_DIR):/export \
hassos-hostapps \
--arch $(BR2_PACKAGE_HASSIO_ARCH) \
--machine $(BR2_PACKAGE_HASSIO_MACHINE)
HASSIO_INSTALL_IMAGES = YES
define HASSIO_INSTALL_IMAGES_CMDS
$(BR2_EXTERNAL_HASSOS_PATH)/package/hassio/create-data-partition.sh "$(@D)" "$(BINARIES_DIR)"
endef
$(eval $(generic-package))

View File

@ -15,7 +15,7 @@ BOOTSTATE_SIZE=8M
SYSTEM_SIZE=256M
KERNEL_SIZE=24M
OVERLAY_SIZE=96M
DATA_SIZE=1G
DATA_SIZE=1280M
function size2sectors() {