Aws-cli: [v2] Distribute binaries for alpine / musl libc

Created on 20 Nov 2019  ·  26Comments  ·  Source: aws/aws-cli

docker run -it --rm docker:latest sh
wget "https://d1vvhvl2y92vvt.cloudfront.net/awscli-exe-linux-x86_64.zip" -O "awscliv2.zip"
# curl not installed
unzip awscliv2.zip
./aws/install
# sudo not install

Errors with:

/ # ./aws/install
./aws/install: line 78: /aws/dist/aws2: not found
You can now run: /usr/local/bin/aws2 --version
/ # aws2
sh: aws2: not found
confusing-error v2

Most helpful comment

The reason that the adoptopenjdk image works is because they include the missing glibc libraries. Alphine linux is based on 'musl glibc', a light-weight alternative to a fullblown glibc. The aws cli v2 binaries do not work with musl, they need a few more libraries to work.

Instead of using the Java image, you can also use the Dockerfile below as an example how to run AWS CLI v2 on Alpine Linux:

FROM alpine:3.11

ENV GLIBC_VER=2.31-r0

# install glibc compatibility for alpine
RUN apk --no-cache add \
        binutils \
        curl \
    && curl -sL https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub -o /etc/apk/keys/sgerrand.rsa.pub \
    && curl -sLO https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VER}/glibc-${GLIBC_VER}.apk \
    && curl -sLO https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VER}/glibc-bin-${GLIBC_VER}.apk \
    && apk add --no-cache \
        glibc-${GLIBC_VER}.apk \
        glibc-bin-${GLIBC_VER}.apk \
    && curl -sL https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -o awscliv2.zip \
    && unzip awscliv2.zip \
    && aws/install \
    && rm -rf \
        awscliv2.zip \
        aws \
        /usr/local/aws-cli/v2/*/dist/aws_completer \
        /usr/local/aws-cli/v2/*/dist/awscli/data/ac.index \
        /usr/local/aws-cli/v2/*/dist/awscli/examples \
    && apk --no-cache del \
        binutils \
        curl \
    && rm glibc-${GLIBC_VER}.apk \
    && rm glibc-bin-${GLIBC_VER}.apk \
    && rm -rf /var/cache/apk/*

The above will download the glibc libraries, download and install AWS CLI v2, and remove some stuff we probably don't need, such as auto-complete. The resulting image is about 100MB in size

All 26 comments

I tried to debug the issue, and couldn't figure out why it fails. It seems to me the install script is just a bit flaky. I tried to add the v2 label as well.

The binaries we publish won't work on docker images based on alpine because we're compiling them against glibc. The sh: aws2: not found is what happens when you try to execute this binary. If you try it on an image like ubuntu:latest the installer will work.

Additionally, I don't think we'd ever release an alpine compatible binary like we're doing for our general linux binary. I think we'd be more inclined to just release a docker image that comes with our binary built on alpine.

Thanks for your answer. Is it possible to document that here https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html under "Prerequisites for Linux", or make the error more user friendly in the alpine based image case?

We'll make sure to get this updated in the user guide. I also like the idea of making a more user friendly error message for this case.

@joguSD bummer, alpine is the standard for docker images. Especially with build / ci systems wanting to automate & not wanting to continually download very large docker images.

Can use an official alpine docker image in a multi stage build to copy the binary into another docker image though.

@chadgrant - can you give an example please of your multi-stage dockerfile?

@BarakBD-Globality I do not have one but if the aws-alpine image ever gets created you can just do:

FROM aws_alpine_image:latest as aws
FROM alpine:latest
COPY --from=aws /usr/local/bin/binary /usr/local/bin/binary

Too bad they don't support alpine. It is a standard image.

Additionally, I don't think we'd ever release an alpine compatible binary like we're doing for our general linux binary. I think we'd be more inclined to just release a docker image that comes with our binary built on alpine.

@joguSD has there been any discussion or motion on this? It seems like a pretty big deal considering the prevalence of alpine. People like me use aws in their cicd and other automation, so if it's not available for alpine-based images, using version 2 is not even an option without significant work! Thanks.

also can't install in busybox

Interestingly if you use this as a base image the AWS cli will install and work properly.

FROM adoptopenjdk/openjdk11:alpine

I was able to do this using the following Dockerfile

FROM adoptopenjdk/openjdk11:alpine

RUN apk add --no-cache \
        ca-certificates \
        unzip \
        curl \
        groff \
        less  \
        bash  \
        openssh-client

WORKDIR /home
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \
    && unzip awscliv2.zip \
    && ./aws/install \
    && rm awscliv2.zip \
    && rm -R aws

The reason that the adoptopenjdk image works is because they include the missing glibc libraries. Alphine linux is based on 'musl glibc', a light-weight alternative to a fullblown glibc. The aws cli v2 binaries do not work with musl, they need a few more libraries to work.

Instead of using the Java image, you can also use the Dockerfile below as an example how to run AWS CLI v2 on Alpine Linux:

FROM alpine:3.11

ENV GLIBC_VER=2.31-r0

# install glibc compatibility for alpine
RUN apk --no-cache add \
        binutils \
        curl \
    && curl -sL https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub -o /etc/apk/keys/sgerrand.rsa.pub \
    && curl -sLO https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VER}/glibc-${GLIBC_VER}.apk \
    && curl -sLO https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VER}/glibc-bin-${GLIBC_VER}.apk \
    && apk add --no-cache \
        glibc-${GLIBC_VER}.apk \
        glibc-bin-${GLIBC_VER}.apk \
    && curl -sL https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -o awscliv2.zip \
    && unzip awscliv2.zip \
    && aws/install \
    && rm -rf \
        awscliv2.zip \
        aws \
        /usr/local/aws-cli/v2/*/dist/aws_completer \
        /usr/local/aws-cli/v2/*/dist/awscli/data/ac.index \
        /usr/local/aws-cli/v2/*/dist/awscli/examples \
    && apk --no-cache del \
        binutils \
        curl \
    && rm glibc-${GLIBC_VER}.apk \
    && rm glibc-bin-${GLIBC_VER}.apk \
    && rm -rf /var/cache/apk/*

The above will download the glibc libraries, download and install AWS CLI v2, and remove some stuff we probably don't need, such as auto-complete. The resulting image is about 100MB in size

So what's the shortest path to get a aws ecr get-login-password … | docker login … inside a docker container running? (which implies: docker:dind )?

I'd love to avoid to build my own docker:stable image with docker:dind support. At least I could thanks to https://github.com/aws/aws-cli/issues/4685#issuecomment-615872019 solution. Thanks for that!

@bentolor Since docker:dind also uses alpine, I suspect all you have to do is change the first line of the above mentioned Dockerfile. Instead of FROM alpine:3.11 you should use FROM docker:dind. It does mean you're building your own image, though....

thanks @blagerweij works really nicely

Also thanks @blagerweij : I grasped the nettle and built a public, custom docker image by copy&pasting your solution: https://hub.docker.com/r/bentolor/docker-dind-awscli

@bentolor I noticed in your repo that your Dockerfile uses FROM docker:stable instead of FROM docker:dind. I thought the whole point was to create an image for Docker in Docker with AWS support ?

@blagerweij See the image below from the official Docker image for illustration.

The typical usage is:

  1. One (or your ci) starts a docker:dind instance and then
  2. Starts a separate docker:stable instance which is linked to the started docker:dind container
  3. You start executing docker commands inside the second container which redirects the comamnds via socket connection to docker daemon running inside the first docker:dind container.

Therefore: While it's true that the docker:dind instance eventually executes your Docker commands, your CLI frontend actually lives in a docker:stable instance. And that's the place where one i.e. needs to login into AWS ;-)

At least this is the usage pattern I'm aware of (i.e. on using Gitlab CI to build docker images). Maybe there are also direct docker:dind usage scenarios?

Usage of <a href="docker:stable">docker:stable</a> vs. <a href="docker:didn">docker:didn</a>

One may use alpine image with glibc from https://hub.docker.com/r/frolvlad/alpine-glibc/
and install aws cli following steps from aws documentation. Worked for me.

@blagerweij I used your script with the docker in docker image. aws cli works fine. I just got an error in build:
/usr/glibc-compat/sbin/ldconfig: /usr/glibc-compat/lib/ld-linux-x86-64.so.2 is not a symbolic link

@amiram Yes I also receive that message, that's a warning that you can simply ignore: It's expecting a symlink, but the file present is a regular file. See https://github.com/envoyproxy/envoy/issues/9078#issuecomment-576837432

Could someone rename this issue to something like: "Distribute binaries for alpine / musl"?

@joguSD @jordanst3wart

Yeah, i'm happy with "Distribute binaries for alpine / musl libc". Let me know if you have any issues

I only need aws s3 cp for a passion project, so I just installed awscli V1 with pip:

FROM alpine:3.12
RUN pip install awscli        
RUN aws s3 ..

The reason that the adoptopenjdk image works is because they include the missing glibc libraries. Alphine linux is based on 'musl glibc', a light-weight alternative to a fullblown glibc. The aws cli v2 binaries do not work with musl, they need a few more libraries to work.

Instead of using the Java image, you can also use the Dockerfile below as an example how to run AWS CLI v2 on Alpine Linux:

snip

The above will download the glibc libraries, download and install AWS CLI v2, and remove some stuff we probably don't need, such as auto-complete. The resulting image is about 100MB in size

this adjustment will fetch the latest version automatically if you don't want to hard code the glibc version:

FROM alpine

# install glibc compatibility for alpine
RUN apk --no-cache add \
    binutils \
    curl \
    && GLIBC_VER=$(curl -s https://api.github.com/repos/sgerrand/alpine-pkg-glibc/releases/latest | grep tag_name | cut -d : -f 2,3 | tr -d \",' ') \
    && curl -sL https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub -o /etc/apk/keys/sgerrand.rsa.pub \
    && curl -sLO https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VER}/glibc-${GLIBC_VER}.apk \
    && curl -sLO https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VER}/glibc-bin-${GLIBC_VER}.apk \
    && apk add --no-cache \
    glibc-${GLIBC_VER}.apk \
    glibc-bin-${GLIBC_VER}.apk \
    && curl -sL https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -o awscliv2.zip \
    && unzip awscliv2.zip \
    && aws/install \
    && rm -rf \
    awscliv2.zip \
    aws \
    /usr/local/aws-cli/v2/*/dist/aws_completer \
    /usr/local/aws-cli/v2/*/dist/awscli/data/ac.index \
    /usr/local/aws-cli/v2/*/dist/awscli/examples \
    && apk --no-cache del \
    binutils \
    curl \
    && rm glibc-${GLIBC_VER}.apk \
    && rm glibc-bin-${GLIBC_VER}.apk \
    && rm -rf /var/cache/apk/*

you can also use the Dockerfile below as an example how to run AWS CLI v2 on Alpine Linux:

The provided Dockerfile/script works but displays this error, which doesn't seem to prevent it from working:

aws/install: line 78: /aws/dist/aws: not found

Strangely, running apk add mandoc just before aws/install avoids displaying this error, and deleting that package after installing has no ill effect. Random other packages do not seem to have this protective effect.

Any thoughts?

P.S. apk add less is required for aws help to work correctly (it will complain about missing package groff otherwise).

Was this page helpful?
0 / 5 - 0 ratings