confluent-kafka-go: Static binary on Alpine – cannot find -lsasl2

I’m trying to create a static binary on Alpine, but can’t figure out how to solve the problem of a missing sasl2 library.

Here’s a list of packages that I install on Alpine:

libressl2.6-libcrypto
libressl2.6-libssl
python
git
bash
openssh
openssl
lz4-dev
libsasl
yajl-dev
zlib-dev
cyrus-sasl-dev
openssl-dev
libevent
build-base
coreutils

I also built librdkafka from source using HEAD (the build output showed support for sasl was found, and enabled).

go build -tags static main.go works as expected:

ldd main
	/lib/ld-musl-x86_64.so.1 (0x7f1fd3370000)
	libsasl2.so.3 => /usr/lib/libsasl2.so.3 (0x7f1fd3157000)
	libssl.so.1.0.0 => /lib/libssl.so.1.0.0 (0x7f1fd2eee000)
	liblz4.so.1 => /usr/lib/liblz4.so.1 (0x7f1fd2cdb000)
	libcrypto.so.1.0.0 => /lib/libcrypto.so.1.0.0 (0x7f1fd28bb000)
	libz.so.1 => /lib/libz.so.1 (0x7f1fd26a4000)
	libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f1fd3370000)

You can see the pointer to /usr/lib/libsasl2.so.3.

But go build -tags static_all main.go won’t work:

go build -tags static_all main.go
# github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka
/usr/lib/gcc/x86_64-alpine-linux-musl/6.4.0/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find -lsasl2
collect2: error: ld returned 1 exit status
Here's the full `go build -x -tags static_all main.go` output
WORK=/tmp/go-build967075363
mkdir -p $WORK/b115/
cd /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka
pkg-config --cflags --static -- rdkafka
pkg-config --libs --static -- rdkafka
CGO_LDFLAGS='"-g" "-O2" "-static" "-L/usr/local/lib" "-lrdkafka" "-L/lib" "-lsasl2" "-lssl" "-llz4" "-lcrypto" "-L/lib" "-lz" "-ldl" "-lpthread" "-lrt"' /usr/local/go/pkg/tool/linux_amd64/cgo -objdir $WORK/b115/ -importpath github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -- -I/usr/local/include -I $WORK/b115/ -g -O2 ./00version.go ./build_static_all.go ./config.go ./consumer.go ./error.go ./event.go ./generated_errors.go ./handle.go ./header.go ./kafka.go ./message.go ./metadata.go ./misc.go ./offset.go ./producer.go ./testhelpers.go
cd $WORK
gcc -fno-caret-diagnostics -c -x c - || true
gcc -Qunused-arguments -c -x c - || true
gcc -fdebug-prefix-map=a=b -c -x c - || true
gcc -gno-record-gcc-switches -c -x c - || true
cd $WORK/b115
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x001.o -c _cgo_export.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x002.o -c 00version.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x003.o -c build_static_all.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x004.o -c config.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x005.o -c consumer.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x006.o -c error.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x007.o -c event.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x008.o -c generated_errors.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x009.o -c handle.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x010.o -c header.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x011.o -c kafka.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x012.o -c message.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x013.o -c metadata.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x014.o -c misc.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x015.o -c offset.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x016.o -c producer.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_x017.o -c testhelpers.cgo2.c
gcc -I /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -I/usr/local/include -I ./ -g -O2 -o ./_cgo_main.o -c _cgo_main.c
cd /go/src/github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka
gcc -I . -fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=$WORK/b115=/tmp/go-build -gno-record-gcc-switches -o $WORK/b115/_cgo_.o $WORK/b115/_cgo_main.o $WORK/b115/_x001.o $WORK/b115/_x002.o $WORK/b115/_x003.o $WORK/b115/_x004.o $WORK/b115/_x005.o $WORK/b115/_x006.o $WORK/b115/_x007.o $WORK/b115/_x008.o $WORK/b115/_x009.o $WORK/b115/_x010.o $WORK/b115/_x011.o $WORK/b115/_x012.o $WORK/b115/_x013.o $WORK/b115/_x014.o $WORK/b115/_x015.o $WORK/b115/_x016.o $WORK/b115/_x017.o -g -O2 -static -L/usr/local/lib -lrdkafka -L/lib -lsasl2 -lssl -llz4 -lcrypto -L/lib -lz -ldl -lpthread -lrt
# github.com/blendle/stream-processor-file-importer/vendor/github.com/confluentinc/confluent-kafka-go/kafka
/usr/lib/gcc/x86_64-alpine-linux-musl/6.4.0/../../../../x86_64-alpine-linux-musl/bin/ld: cannot find -lsasl2
collect2: error: ld returned 1 exit status
And here's the output of `find / -name *sasl*`
/run/saslauthd
/usr/lib/libgsasl.so
/usr/lib/libgsasl.a
/usr/lib/libgsasl.so.7
/usr/lib/sasl2
/usr/lib/sasl2/libsasldb.so.3
/usr/lib/sasl2/libsasldb.so
/usr/lib/sasl2/libsasldb.so.3.0.0
/usr/lib/pkgconfig/libsasl2.pc
/usr/lib/pkgconfig/libgsasl.pc
/usr/lib/libgsasl.so.7.9.6
/usr/lib/libsasl2.so.3.0.0
/usr/lib/libsasl2.so
/usr/lib/libsasl2.so.3
/usr/sbin/sasldblistusers2
/usr/sbin/testsaslauthd
/usr/sbin/saslauthd
/usr/sbin/saslpasswd2
/usr/include/c++/6.4.0/javax/security/sasl
/usr/include/c++/6.4.0/gnu/javax/crypto/sasl
/usr/include/sasl
/usr/include/sasl/saslplug.h
/usr/include/sasl/saslutil.h
/usr/include/sasl/sasl.h
/usr/include/gsasl-mech.h
/usr/include/gsasl-compat.h
/usr/include/gsasl.h
/etc/init.d/saslauthd

Two questions:

  • any thoughts on what might be causing this issue?
  • is there any way to disable SASL support and create a static binary without this dependency?

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 16 (6 by maintainers)

Most upvoted comments

I can at least put the Dockerfile here that made this work for me, with the commands on how to use it:

# First build phase (of two), this part uses Alpine to install all required
# dependencies required to build a project that requires librdkafka. It will
# then build a static binary that can be used in a smaller "scratch" image.
FROM golang:1.10-alpine

# The default librdkafka version is the latest stable release on GitHub. You can
# use the `--build-arg` argument for `docker build` to specify a different
# version to be installed.
#
# e.g.: docker build --build-arg LIBRDKAFKA_VERSION=4e7a46701ecce7297b2298885da980be7856e5f9
#
ARG LIBRDKAFKA_VERSION=v0.11.3

# Set the workdir to the full GOPATH of your project.
WORKDIR $GOPATH/src/PATH/TO/PROJECT

# Install all dependencies required to build the project as a static binary.
RUN apk add -U \
    bash \
    build-base \
    coreutils \
    curl \
    cyrus-sasl-dev \
    git \
    libevent \
    libressl2.6-libcrypto \
    libressl2.6-libssl \
    libsasl \
    lz4-dev \
    openssh \
    openssl \
    openssl-dev \
    python \
    yajl-dev \
    zlib-dev

# Install `dep`, the official Golang package manager to install dependencies.
RUN apk add -U -X http://nl.alpinelinux.org/alpine/edge/testing dep

# SASL support is disabled for now, due to issues when compiling a static
# binary. See: https://git.io/vAFFm
RUN cd $(mktemp -d) \
 && curl -sL "https://github.com/edenhill/librdkafka/archive/$LIBRDKAFKA_VERSION.tar.gz" | \
    tar -xz --strip-components=1 -f - \
 && ./configure --disable-sasl \
 && make -j \
 && make install

# Copy the entire project tree into the container, so we can build the binary.
COPY . .

# Install any defined project dependencies using `dep`.
RUN dep ensure -vendor-only

# Build a completely static binary, able to be used in a `scratch` container.
RUN go build -o /tmp/run -tags static_all

# Second build phase, copy the generated binary from the first build phase into
# a "scratch" image, and set the entrypoint to run the binary.
FROM scratch
ENTRYPOINT ["/run"]
COPY --from=0 /tmp/run .

I then build the image as follows:

$ docker build \
  --build-arg LIBRDKAFKA_VERSION=4e7a46701ecce7297b2298885da980be7856e5f9 \
  --tag static-test .

It succeeds, and I can see the relatively small container size as a result:

$ docker images | grep static-test

static-test, latest, 8cd387b3607e, About a minute ago, 33.4MB

And finally, I can run it as expected:

$ docker run static-test
hello world!

This is useful for many people, would you mind blogging a small tutorial or outlining it here?

Do we have any updates on getting librdkafka to work with gssapi (kerberos authentication) inside alpine?

The plugins are being compiled into the static libsasl2. The problem is that some plugins depend on libraries (e.g. Berkeley db, Kerberos 5) that are not available as static libraries in Alpine. If you don’t need those plugins, then you can compile the SASL library statically without them, then compile librdkafka, then compile your Go program.

I only have need for SCRAM support in SASL, so this worked for me to create an imagine that can then be used to compile the Go program:

FROM golang:1.12.0-alpine3.9

RUN apk add --no-cache  \
      bash              \
      build-base        \
      coreutils         \
      gcc               \
      git               \
      make              \
      musl-dev          \
      openssl-dev       \
      rpm               \
      lz4-dev           \
      zlib-dev          \
      wget          &&  \
    #
    # Build Cyrus SASL from source.
    cd $(mktemp -d) && \
    wget -nv -O cyrus-sasl-2.1.27.tar.gz https://github.com/cyrusimap/cyrus-sasl/releases/download/cyrus-sasl-2.1.27/cyrus-sasl-2.1.27.tar.gz && \
    tar -xz --strip-components=1 -f cyrus-sasl-2.1.27.tar.gz && \
    rm -f cyrus-sasl-2.1.27.tar.gz && \
    ./configure --prefix=/usr --disable-sample --disable-obsolete_cram_attr --disable-obsolete_digest_attr --enable-static --disable-shared \
        --disable-checkapop --disable-cram --disable-digest --enable-scram --disable-otp --disable-gssapi --with-dblib=none --with-pic && \
    make && \
    make install && \
    #
    # Build librdkafka from source.
    cd $(mktemp -d) && \
    wget -nv -O v0.11.6.tar.gz https://github.com/edenhill/librdkafka/archive/v0.11.6.tar.gz && \
    tar -xz --strip-components=1 -f v0.11.6.tar.gz && \
    rm -f v0.11.6.tar.gz && \
    ./configure --prefix=/usr --enable-sasl && \
    make -j && \
    make install

After that you can use go build -tags static_all . to build your program.

Well, sorry for jumping the gun on this issue. I’ve actually managed to solve the second question by sifting through the librdkafka repository, and using ./configure --disable-sasl to disable SASL support.

After that the build went fine, and it looks like the binary is now statically linked:

ldd ./main
ldd: ./main: Not a valid dynamic program

The problem obviously still remains, but it’s less of an issue for me right now. I’ll see if I can get it to work with SASL support on Alpine and report back when I do, until then, I’ll close this issue.

edit: was posting while you responded @edenhill, thank you for the pointers, appreciated 👍.