Environment differences between RUN and shell command?

I’m trying to build MongoDB with SSL support for Alpine Linux, and I’m trying to understand the reason behind a difference of behavior between two approaches. (The bottom line is GCC complaining about endianness not being defined in the latter approach.)

On the one hand, I can successfully build Mongo if all the commands are fully contained within the Dockerfile:

FROM alpine:edge
ENV TERM=xterm

RUN echo http://dl-4.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories \
  && echo /home/builder/packages/main >> /etc/apk/repositories \
  && echo /home/builder/packages/testing >> /etc/apk/repositories \
  && apk -U add alpine-sdk coreutils \
  && adduser -G abuild -g "Alpine Package Builder" -s /bin/sh -D builder \
  && echo "builder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
  && rm -rf /var/cache/apk/*
ADD abuild.conf /etc/abuild.conf
ADD abuild.rsa.pub /etc/apk/keys/abuild.rsa.pub
ADD abuild.rsa /home/builder/abuild.rsa
ADD c12e.patch /tmp/c12e.patch
RUN mkdir -p /home/builder/package/ && chown -R builder /home/builder/

USER builder
WORKDIR /home/builder/package
ENV PACKAGER_PRIVKEY /home/builder/abuild.rsa

RUN LAYER=c12e-aports-git \
  && git clone https://github.com/alpinelinux/aports.git /tmp/aports \
  && cd /tmp \
  && patch --verbose -p0 < c12e.patch

RUN LAYER=c12e-aports-boost \
  && cd /tmp/aports/main/boost \
  && abuild-apk update && abuild -r

RUN LAYER=c12e-aports-mongo-ssl \
  && cd /tmp/aports/testing/mongodb \
  && abuild-apk update && abuild -r

Basically, setup the APK build system to pull from alpine:edge and local repos, add a user, grab the build scaffolding from aports, patch as necessary, and build dependencies and Mongo. No issues there, but the downside is that the build scaffolding and generated artifacts are inside the (very large) container.

It would be much nicer if I could mount some volumes containing those things; however, that’s not possible when building the container, but rather only when running it, so the alternative is to move the APK build steps into a shell script contained within the container, make it the ENTRYPOINT, and run the container with mounts.

Here is what seems to me to be the equivalent build approach with volumes:


FROM alpine:edge
ENV TERM=xterm

RUN echo http://dl-4.alpinelinux.org/alpine/edge/testing >> /etc/apk/repositories \
  && echo /home/builder/packages/main >> /etc/apk/repositories \
  && echo /home/builder/packages/testing >> /etc/apk/repositories \
  && apk -U add alpine-sdk coreutils \
  && adduser -G abuild -g "Alpine Package Builder" -s /bin/sh -D builder \
  && echo "builder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers \
  && rm -rf /var/cache/apk/*
ADD entrypoint.sh /bin/
ADD abuild.conf /etc/abuild.conf
RUN mkdir -p /home/builder/package/ && chown -R builder /home/builder/

USER builder
ENTRYPOINT ["/bin/entrypoint.sh"]
WORKDIR /home/builder/package
ENV PACKAGER_PRIVKEY /home/builder/abuild.rsa

The contents of entrypoint.sh, which runs the builds as I’d expect from the RUN commands in the former approach):

#!/usr/bin/env sh
set -e

cd /home/builder/package/main/boost
abuild-apk update && abuild -r

cd /home/builder/package/testing/mongodb
abuild-apk update && abuild -r

And I run it mounting the (already patched) source and destination directories:

git clone https://github.com/alpinelinux/aports.git
patch -p0 < c12e.patch
docker build -t "apk-builder" .
docker run --rm -it -e RSA_PRIVATE_KEY="$(cat ./abuild.rsa)" -v $(pwd)/abuild.rsa.pub:/etc/apk/keys/abuild.rsa.pub -v $(pwd)/aports:/home/builder/package -v $(pwd)/packages:/home/builder/packages "apk-builder"

Both approaches use the same abuild.conf configuration, execute the APK build chain, pull in dependencies, run GCC, etc. However, where the former approach succeeds, that latter fails when compiling a dependency of Mongo, with a g++ complaint that endianness cannot be determined.

I’ve docker run -it both containers, and the filesystems appear to be the same. Running env inside both shows nothing of interest. However, there must be some runtime difference between the two containers when running GCC as spawned by the builds. Since the build succeeds in the former approach, I’d rule out the APK and GCC build chain themselves as culprits, and since the filesystems are the same, that seems to point towards something in the container build vs. execution environments.

Any ideas on what the differences might be or what more to investigate?

Looks like i’ll answer my own question: the difference in environment was actually the mounted volume. When compiling under the Alpine image, the MongoDB build expects a case-sensitive file system; however, the mounted volume was HFS+ from my host Mac laptop, which is case-insensitive.

Hey David,
Is the c12e.patch a custom patch ? I’m trying to build mongodb on alpine linux with ssl support.

It is a custom patch that upgrades the Boost APK build from 1.60.0 to 1.61.0 an adds --ssl to the Mongo build config:

diff -rup aports/main/boost/APKBUILD aports.c12e/main/boost/APKBUILD
--- aports/main/boost/APKBUILD	2016-07-21 18:03:47.000000000 -0500
+++ aports.c12e/main/boost/APKBUILD	2016-07-21 18:13:45.000000000 -0500
@@ -1,6 +1,6 @@
 # Maintainer: Natanael Copa <ncopa@alpinelinux.org>

 _x=${pkgver%%.*}	# strip .y.z
 _y=${pkgver%.*}		# strip .z
@@ -80,6 +80,7 @@ _options="--user-config=\"$_builddir/use

 case "$CARCH" in
 	armhf|aarch64) _boostarch=arm;;
+	*) _boostarch=$CARCH;;

@@ -144,8 +144,8 @@ thread() { _mvlib thread; }
 unit_test_framework() { _mvlib unit_test_framework; }
 wave() { _mvlib wave; }
 wserialization() { _mvlib wserialization; }

-md5sums="28f58b9a33469388302110562bdf6188  boost_1_60_0.tar.gz"
-sha256sums="21ef30e7940bc09a0b77a6e59a8eee95f01a766aa03cdfa02f8e167491716ee4  boost_1_60_0.tar.gz"
-sha512sums="f36264bea3b30193f4d4756e6244797350258e0492a4a3601300ee732682eba85a9aed88ced30da3ff187c3774f9ea4d6f9686b001d3af9726a1cd967cd795c5  boost_1_60_0.tar.gz"
+md5sums="874805ba2e2ee415b1877ef3297bf8ad  boost_1_61_0.tar.gz"
+sha256sums="a77c7cc660ec02704c6884fbb20c552d52d60a18f26573c9cee0788bf00ed7e6  boost_1_61_0.tar.gz"
+sha512sums="7d4d60c8ef3b9ea5ad594e1f7cc791c2c245228f10bfb0b2af2ee11c6db63fc51481e5d8c40ebd9a3a5b354f53cbb6bcdb82146471260561742738feea818b07  boost_1_61_0.tar.gz"
diff -rup aports/testing/mongodb/APKBUILD aports.c12e/testing/mongodb/APKBUILD
--- aports/testing/mongodb/APKBUILD	2016-07-21 18:03:48.000000000 -0500
+++ aports.c12e/testing/mongodb/APKBUILD	2016-07-21 18:16:29.000000000 -0500
@@ -51,6 +51,7 @@ _buildopts="
 		--use-system-wiredtiger \
 		--use-system-snappy \
 		--use-system-zlib \
+		--ssl \

 prepare() {

Thanks @daldridge. I’m building the mongodb package for alpine:3.4. I’m facing an issue when I run abuild -r inside aports/main/boost, it complains about unable to install python2-dev which is one of the dependencies. I also manually tried to add python2-dev but no luck with it. Is the reason being that I’m using alpine:3.4 and not edge. Do I need to build the boost directory for mongodb ? Do you have already existing mongodb package compiled with ssl that I could use ?

I’m using alpine-jazz-hands (modified slightly to be able to build APKs from .../testing/..., not just .../main/...) to build the APKs. Take a look at its sourcecode to see how it bootstraps the build environment. I did not have to manually install Python 2.7 manually, as I think it was taken care of by the bootstrapping build container, which installs alpine-sdk and coreutils.

To successfully build MongoDB, I had to build:

  • …/main/boost
  • …/tesing/asio
  • …/testing/wiredtiger
  • …/testing/mongodb

Note that I am not actually running the generated mongo server, as I merely needed to mongo shell compiled with SSL.