diff options
author | 2022-06-22 16:58:03 -0700 | |
---|---|---|
committer | 2022-06-22 16:58:03 -0700 | |
commit | de8adc8e50b119abff3a9db4ed3a71d8b1187e1b (patch) | |
tree | bd7333c8d6271af7aeb086794fed48051f182788 | |
parent | Add a docker container for rsync. (diff) | |
download | rsync-service-de8adc8e50b119abff3a9db4ed3a71d8b1187e1b.tar.gz rsync-service-de8adc8e50b119abff3a9db4ed3a71d8b1187e1b.tar.bz2 rsync-service-de8adc8e50b119abff3a9db4ed3a71d8b1187e1b.zip |
Update this codebase.
maffblaster wants to use it, so fix a bunch of bugs.
Signed-off-by: Alec Warner <antarus@gentoo.org>
-rw-r--r-- | Dockerfile | 22 | ||||
-rwxr-xr-x | rsyncd.conf | 20 | ||||
-rwxr-xr-x | wrap_rsync.sh | 71 |
3 files changed, 56 insertions, 57 deletions
@@ -1,36 +1,26 @@ -# Base Image -FROM gentoo/stage3-amd64-hardened:latest AS base -WORKDIR / - +# image is based on stage3-amd64 +FROM gentoo/stage3-amd64:latest +COPY rsyncd.conf /opt/rsync/rsyncd.conf COPY wrap_rsync.sh /opt/rsync/wrap_rsync.sh -COPY ./rsync-mirror /etc/xinetd.d/rsync-mirror # Normally I would advocate for ARG here and pass arguments to wrap_rsync. # This would enable new docker builds with arguments like: # docker build . --build_arg WAIT_TIME=30m -t gentoo/rsync # However, ARG's cannot be passed to ENTRYPOINTs, so we set these as ENV instead. - # Mirror to get data from. ENV SOURCE_MIRROR=rsync://turnstone.gentoo.org./gentoo-portage # ENV SOURCE_MIRROR=rsync://rsync.us.gentoo.org/gentoo-portage - -# Possibly a stateful volume +# Where to write the data in this container. ENV DEST_DIR=/srv/gentoo -# A memory-backed volume -ENV TMP_DIR=/srv/ephemeral - # How long to wait between syncs; must be a valid argument to sleep ENV WAIT_TIME=30m -# Create TMP_DIR and DEST_DIR -WORKDIR $TMP_DIR +# This needs to exist in the container. WORKDIR $DEST_DIR # Expose Rsync port EXPOSE 873 -# Stop xinetd; wrap_rsync will start it when the container is started. -CMD /etc/init.d/xinetd stop +CMD mount -t tmpfs -o size=2g,nr_inodes=2000000 tmpfs /srv/gentoo -# Execute wrapper. ENTRYPOINT /opt/rsync/wrap_rsync.sh diff --git a/rsyncd.conf b/rsyncd.conf new file mode 100755 index 0000000..bc9ccc0 --- /dev/null +++ b/rsyncd.conf @@ -0,0 +1,20 @@ + +uid = nobody +gid = nobody +# We are in a container, who cares. +use chroot = no +# Let clients use as much as they want; CPU control is in a load balancer in front of us. +max connections = 0 + +motd file = ./rsyncd.motd +# On GCP, the default log collector only collects syslog, so just toss logs in there. +log file = /var/log/syslog +transfer logging = yes +log format = %t %a %m %f %b +syslog facility = daemon +timeout = 300 + +[gentoo-portage] +path = /srv/gentoo/serving +comment = Gentoo Linux Portage tree mirror +exclude = distfiles diff --git a/wrap_rsync.sh b/wrap_rsync.sh index fb420dc..60f7687 100755 --- a/wrap_rsync.sh +++ b/wrap_rsync.sh @@ -1,21 +1,23 @@ -#!/bin/bash - -set -x +#!/bin/bash -x # On container start, run an rsync to get a good copy of the tree. -# Once we have a copy, start xinetd to start serving. +# Then execute rsyncd; we will start serving once the sync completes. # Then keep syncing in the background every 30m. +# We keep the trees in a TMPFS, we need a million inodes and 1300MB of space. +mount -t tmpfs -o size=1300m,nr_inodes=1000000 tmpfs "${DEST_DIR}" +if [[ $? -ne 0 ]]; then + logger -t rsync "Init: Failed to create tmpfs: ${DEST_DIR}" + return 1 +fi + # Maintain 2 'partitions' of the tree. # "serving" - This copy is served to users and is not mutated. # "updating" - This copy is a shadow copy used for updates. # Create the two partitions on startup. -# We will swap between them at runtime. PARTITION1=$(mktemp -d -p "${DEST_DIR}" XXXXXX) PARTITION2=$(mktemp -d -p "${DEST_DIR}" XXXXXX) -# Our stateful copy. -UPDATES=${DEST_DIR}/stateful # Function sync syncs dest ("${2}") from source ("${1}") function sync() { @@ -46,62 +48,49 @@ function sync() { return 0 } -# Init will update the stateful tree copy. -# Then it will copy that to the stateless partition. +# Function init does a first sync, to populate the serving partition and +# setup symlinks, and begin serving data. +# "${1}" is the serving partition. "${2}" is the update partition function init() { - sync "${UPDATING}" "${SOURCE_MIRROR}" # this is synchronous. - sync "${UPDATING}" "${PARTITION1}" + sync "${1}" "${SOURCE_MIRROR}" # this is synchronous. - # We serve out of ${TMP_DIR}/serving - ln -s "${PARTITION1}" "${TMP_DIR}/serving" - # The second partition will be for stateless updates. - ln -s "${PARTITION2}" "${TMP_DIR}/update" + # We serve out of ${DEST_DIR}/serving + ln -s "${1}" "${DEST_DIR}/serving" + # Setup the update partition + ln -s "${2}" "${DEST_DIR}/update" - # Then launch xinetd; it will detech into the background and serve. - /etc/init.d/xinetd start + # Then launch rsyncd; it will detach into the background and serve from serving. + rsync --daemon --config="/opt/rsync/rsyncd.conf" + return 0 } -# function update syncs the UPDATING partition (stateful.) -# Then it syncs it into the ${TMP_DIR}/update partition (stateless.) -# Then we swap the two stateless partitions (via symlinks swapping.) +# Function update syncs the 'update' partition and, if successful, swaps the partitions. function update() { - update=$(readlink "${TMP_DIR}/update") - # Try to update our stateful tree copy. - if ! sync "${UPDATING}" "${SOURCE_MIRROR}"; then + update=$(readlink "${DEST_DIR}/update") + # If we fail to sync, just return false and avoid modifying the serving state. + if ! sync "${update}" "${SOURCE_MIRROR}"; then return 1 fi - # Try to update our stateless copy. - if ! sync "${update}" "${UPDATING}"; then - return 1 - fi - - # Quasi-atomic swap with symlinks. # Save the previous serving partition - old_serving=$(readlink "${TMP_DIR}/serving") + old_serving=$(readlink "${DEST_DIR}/serving") # Point the serving symlink at the update partition; now freshly updated. - mv -fT "${TMP_DIR}/update" "${TMP_DIR}/serving" + mv -fT "${DEST_DIR}/update" "${DEST_DIR}/serving" # Point the update partition at the old serving partition. - ln -sf "${old_serving}" "${TMP_DIR}/update" - - # Its plausible here that users may still be accessing the old_serving copy, so we don't delete it or anything. -} - -function health() { - /etc/init.d/xinetd status + ln -sf "${old_serving}" "${DEST_DIR}/update" } function serve() { while true do + # TODO(antarus): Add exponential backoff. sleep "${WAIT_TIME}" update - # If xinetd died, just suicide and docker will restart us. - health || exit 5 done } -init +# Partition1 starts as "serving", partition2 as "update" +init "${PARTITION1}" "${PARTITION2}" if [[ $? -ne 0 ]]; then exit 1 fi |