Skip to content

Instantly share code, notes, and snippets.

@robd003
Created April 19, 2023 20:48
Show Gist options
  • Save robd003/ff587c755cd360093ba7f70e3af83d98 to your computer and use it in GitHub Desktop.
Save robd003/ff587c755cd360093ba7f70e3af83d98 to your computer and use it in GitHub Desktop.
Emissary-Ingress Dockerfile for 3.6.0
########################################
# The Emissary build stage
########################################
FROM golang:1.20.3-bullseye as emissary-factory
# See comment in `envoy-factory` for explanation
# of this symlink's purpose
RUN bash -c 'rm /bin/sh && ln /bin/bash /bin/sh'
# TODO(the-wondersmith): Make the build process below dependant upon the non-existence
# of pre-compiled, architecture-specific binaries mounted into the
# build container with -v "{artifacts/emissary-os-arch}:{/tmp/artifacts/}"
# Build the Emissary Golang binaries
RUN apt-get update \
&& apt-get install -y patch \
&& mkdir -p /tmp/artifacts \
&& git clone --recursive https://github.com/emissary-ingress/emissary.git /tmp/emissary \
&& cd /tmp/emissary \
&& git checkout release/v3.6 \
&& git pull --no-ff \
&& echo "Building golang binaries for Emissary ..." \
&& go mod vendor \
&& go build -v -mod=vendor -o /tmp/artifacts ./cmd/... \
&& echo "Done!" \
&& cd /go \
# Add the 'housekeeping' script \
&& curl https://gist.githubusercontent.com/robd003/6688db597eeb0c74c6931ae7367e330a/raw/fdc8b242580fa28ffd1ea65aaf423c521179e0d5/housekeeping.py > /tmp/housekeeping.py
########################################
# The Debian/Python build stage
########################################
FROM debian:stable-slim as artifactory
# See comment in `envoy-factory` for explanation
# of this symlink's purpose
RUN bash -c 'rm /bin/sh && ln /bin/bash /bin/sh'
# Start by setting a known-good "save point" to return to when we're done
RUN export HERE="$(pwd)" \
# Make the directories we'll be using as transient storage \
&& mkdir -p /tmp/{deb-pkgs,holding-cell,artifacts,pyyaml} \
&& mkdir -p /tmp/artifacts/tmp \
# Install the system-level packages we need in order to compile \
# the LibYAML C-library and to pull down the packages we *actually* \
# need in the final distroless image \
&& apt-get update \
&& apt-get install -yq apt-rdepends python3.9 python3.9-full python3.9-dev python3-pip gcc make musl-dev curl cython3 libcap-dev libffi-dev libssl-dev libyaml-dev patchelf \
# Install the latest version of setuptools to ensure compatibility with \
# the Cython directives when compiling PyYAML \
# the Cython directives when compiling PyYAML \
&& python3 -m pip install -U setuptools \
&& cd /tmp/pyyaml \
# Pull down the latest PyYAML source tarball ... \
&& curl -o pyyaml-6.0.tar.gz -L "https://github.com/yaml/pyyaml/archive/refs/tags/6.0.tar.gz" &> /dev/null \
&& tar xzf pyyaml-6.0.tar.gz \
&& cd pyyaml-6.0 \
# ... and compile it into a valid wheel for whatever architecture \
# we're currently running on, ensuring that we also compile the native \
# C-library extension too \
&& CFLAGS="-w" CPPFLAGS="-w" CXXFLAGS="-w" PYTHONWARNINGS='ignore' python3 setup.py --with-libyaml bdist_wheel --universal \
&& mv ./dist/PyYAML-*.whl /tmp/artifacts/tmp/ \
# Distroless images don't have a shell executable, so any RUN \
# commands we'll need to run will have to be accomplished via Python. \
# Seeing as we're building multi-arch images, it won't be easy to tell what \
# the file name of the compiled PyYAML wheel will actually be, so we'll create \
# a little "installer" script to handle that for us once we're "inside" the final \
# distroless image
&& echo '#!/usr/bin/python' > /tmp/artifacts/tmp/install_pyyaml.py \
&& echo 'import sys, subprocess' >> /tmp/artifacts/tmp/install_pyyaml.py \
&& echo 'from pathlib import Path' >> /tmp/artifacts/tmp/install_pyyaml.py \
&& echo '' >> /tmp/artifacts/tmp/install_pyyaml.py \
&& echo "yaml_wheel = Path('$(printf "/tmp/%s\n" "$(ls /tmp/artifacts/tmp/*.whl | xargs basename --)")')" >> /tmp/artifacts/tmp/install_pyyaml.py \
&& echo 'subprocess.check_call([sys.executable, "-m", "pip", "install", str(yaml_wheel)])' >> /tmp/artifacts/tmp/install_pyyaml.py \
&& echo "yaml_wheel.unlink()" >> /tmp/artifacts/tmp/install_pyyaml.py \
&& echo '' >> /tmp/artifacts/tmp/install_pyyaml.py \
&& cd /tmp/deb-pkgs \
# Keep it clean folks \
&& rm -rf /tmp/pyyaml \
# Apt will complain if the permissions on the current directory \
# aren't "correct", so we'll set them to ultra-permissive. Fair \
# warning, apt will *most likely* complain anyway. It's totally \
# safe to ignore it \
&& chmod -R 777 /tmp \
# Instruct apt-cache to recursively trace out the dependencies for \
# each of the system-level packages we'll need to "install" in the \
# final distroless image, passing the list of resolved packages to \
# be downloaded by `apt-get`. This is sort of a hacky way to accomplish \
# what we need, but doing it this way avoids breaking one of the \
# fundamental security guarantees of distroless images \
&& for PKG in "$(apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances bash coreutils curl libcap htop libc-bin libc-dev-bin libc6 libc6-dev libcap-dev libcrypt1 libgcc-s1 gcc-10-base libcyaml1 libyaml-0-2 | grep '^\w' | sort -u)"; do apt-get download $PKG; done \
# ... and unpack all of the packages we just downloaded into the temporary \
# artifacts directory. The resulting directory will essentially be "overlaid" \
# onto the distroless image, thereby perfectly mimicking the behavior of `apt-get install` \
&& for PKG in $(ls /tmp/deb-pkgs/*.deb); do dpkg-deb -xv "${PKG}" "/tmp/artifacts"; done \
# Ain't nobody got time to not keep it clean \
&& rm -rf /tmp/{deb-pkgs,holding-cell} \
# Navigate into the directory we just populated with package contents \
# so that when we archive it the structure of the resulting tarball \
# properly mirrors that of a running system (i.e. directory root == /) \
&& cd /tmp/artifacts \
# Use Python's `tarfile` module to pack up the extracted packages \
# as the distroless image doesn't have a tar binary of its own \
&& python3 -c "import tarfile; artifacts = tarfile.open('/tmp/artifacts.tar.gz', mode='w:gz'); artifacts.add('./', recursive=True); artifacts.close()" \
# Return to our known-good "save point" ... \
&& cd "${HERE}" \
# And of course, we run a tight ship and keep it clean \
&& unset HERE
########################################
# The final distroless image build stage
########################################
FROM gcr.io/distroless/python3 as distroless-factory
# Pull over the archive of "installed" packages from the
# intermediate `package-factory` image
COPY --from=artifactory /tmp/artifacts.tar.gz /tmp/
# Copy over emissary's Python and Golang artifacts
COPY --from=emissary-factory /tmp/artifacts /opt/ambassador/bin
COPY --from=emissary-factory /tmp/emissary/python /opt/ambassador/python
COPY --from=emissary-factory /tmp/housekeeping.py /ambassador/housekeeping.py
COPY --from=emissary-factory /tmp/emissary/demo/services /ambassador/demo-services
COPY --from=emissary-factory /tmp/emissary/demo/config /ambassador/ambassador-demo-config
# Copy over `envoy`'s artifacts
COPY --from=local_build/envoy:1.25.4 /artifacts/envoy-static-stripped /opt/ambassador/bin/envoy-static-stripped
COPY --from=local_build/envoy:1.25.4 /artifacts/envoy-static-stripped /usr/bin/envoy
#COPY --from=docker.io/envoyproxy/envoy:v1.24.6 /usr/local/bin/envoy /opt/ambassador/bin/envoy-static-stripped
#COPY --from=docker.io/envoyproxy/envoy:v1.24.6 /usr/local/bin/envoy /usr/bin/
# Run the 'housekeeping' script
RUN python /ambassador/housekeeping.py
# Force the HOME environment variable to a directory that'll always be writeable.
# We use /tmp/ambassador for this, and make sure it exists in our entrypoint,
# because trying to create it here in the Dockerfile doesn't always work very
# well in the face of situations like KAT volume-mounting /tmp/ambassador or
# the like.
ENV HOME=/tmp/ambassador
ENTRYPOINT ["/bin/bash", "/opt/ambassador/python/entrypoint.sh"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment