Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add alpine based grafana image #14913

Closed
wants to merge 7 commits into from
Closed

Conversation

jsravn
Copy link

@jsravn jsravn commented Jan 16, 2019

For #14182

Reasons:

  • Smaller image, a smaller security surface, so less CVEs and easier to keep up to date
  • Overall image is around 50MB smaller

I tagged the image similar to other projects (like node, nginx), with a suffix -alpine on the version.

This works for me when I build grafana-server in the grafana/build-container:1.22.

@CLAassistant
Copy link

CLAassistant commented Jan 16, 2019

CLA assistant check
Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@jsravn
Copy link
Author

jsravn commented Jan 16, 2019

I pushed it to jsravn/grafana:master-alpine if anyone would like to try it out. Built against 8fef2138b of master.

@xlson
Copy link
Contributor

xlson commented Jan 17, 2019

Great work. Would love to have an alpine image as an alternative to get the size down. Unfortunately image generation doesn't work. PhantomJS crashes due to missing libraries.

Output from running phantomjs in the container manually:

Error loading shared library libstdc++.so.6: No such file or directory (needed by ./phantomjs)
Error loading shared library libgcc_s.so.1: No such file or directory (needed by ./phantomjs)

@jsravn
Copy link
Author

jsravn commented Jan 17, 2019

Ah, phantomjs. Didn't see that.

sgerrand/alpine-pkg-glibc#13 makes it sound like this isn't too hopeful.

@xlson
Copy link
Contributor

xlson commented Jan 17, 2019

@jsravn No unfortunately not. On the other hand, we are hoping to remove PhantomJS in the near future, which would hopefully allow us to provide an alpine based image

@jsravn
Copy link
Author

jsravn commented Jan 17, 2019

@xlson I should have something soon. I used https://github.com/AdoptOpenJDK/openjdk-docker/tree/master/8/jdk/alpine as an example.

Basically you need to:

  1. Use glibc alpine package
  2. Copy the libraries phantomjs depends on, and ensure they are placed in the correct location

It looks like it's working now, I just need to wire up the build container to copy those libs over. I can copy from the build container directly but I'm not sure how up to date the build container is. Alternatively I could use the archlinux compiled libraries like AdoptOpenJDK uses.

/usr/share/grafana/tools/phantomjs $ /usr/glibc-compat/bin/ldd phantomjs
        linux-vdso.so.1 (0x00007ffc74502000)
        libz.so.1 => /usr/glibc-compat/lib/libz.so.1 (0x00007ff0df157000)
        libfontconfig.so.1 => /usr/glibc-compat/lib/libfontconfig.so.1 (0x00007ff0def1b000)
        libfreetype.so.6 => /usr/glibc-compat/lib/libfreetype.so.6 (0x00007ff0dec78000)
        libdl.so.2 => /usr/glibc-compat/lib/libdl.so.2 (0x00007ff0dea74000)
        librt.so.1 => /usr/glibc-compat/lib/librt.so.1 (0x00007ff0de86c000)
        libpthread.so.0 => /usr/glibc-compat/lib/libpthread.so.0 (0x00007ff0de64c000)
        libstdc++.so.6 => /usr/glibc-compat/lib/libstdc++.so.6 (0x00007ff0de348000)
        libm.so.6 => /usr/glibc-compat/lib/libm.so.6 (0x00007ff0ddfc6000)
        libgcc_s.so.1 => /usr/glibc-compat/lib/libgcc_s.so.1 (0x00007ff0dddb0000)
        libc.so.6 => /usr/glibc-compat/lib/libc.so.6 (0x00007ff0dd9f3000)
        /lib64/ld-linux-x86-64.so.2 => /usr/glibc-compat/lib/ld-linux-x86-64.so.2 (0x00007ff0df370000)
        libexpat.so.1 => /usr/glibc-compat/lib/libexpat.so.1 (0x00007ff0dd7c9000)
        libpng12.so.0 => /usr/glibc-compat/lib/libpng12.so.0 (0x00007ff0dd5a3000)
/usr/share/grafana/bin $ /usr/glibc-compat/bin/ldd grafana-server
        linux-vdso.so.1 (0x00007ffcf67f6000)
        libpthread.so.0 => /usr/glibc-compat/lib/libpthread.so.0 (0x00007feed6663000)
        libdl.so.2 => /usr/glibc-compat/lib/libdl.so.2 (0x00007feed645f000)
        libc.so.6 => /usr/glibc-compat/lib/libc.so.6 (0x00007feed60a2000)
        /lib64/ld-linux-x86-64.so.2 => /usr/glibc-compat/lib/ld-linux-x86-64.so.2 (0x00007feed6883000)
/usr/share/grafana/tools/phantomjs $ ./phantomjs 
phantomjs> console.log('hello world')
hello world
undefined
phantomjs> phantom.exit()

@xlson
Copy link
Contributor

xlson commented Jan 18, 2019

Wow, that looks really promising.

The build containers libraries are probably not that up-to-date as we are going for Centos6 compatibility.

This copies the required phantomjs libs from the arch repo, similar to
the approach AdoptOpenJDK takes
(https://github.com/AdoptOpenJDK/openjdk-docker/tree/master/8/jdk/alpine).

There's a fair amount of libs unfortunately, as recent versions of
freetype depend on harfbuzz (a font shaping lib), which pulls in
a lot of transitive dependencies.

An advantage is it is easy to update the libraries as needed.
* Combine ARGs/RUN into a single layer so it builds a lot faster.
* Reduce debug output.
@jsravn
Copy link
Author

jsravn commented Jan 18, 2019

@xlson I got it working now. I've pushed an image at jsravn/grafana:master-alpine-2 built with the latest commits.

I chose to go w/ the archlinux packages, same as AdoptOpenJDK. phantomjs has a lot of dependencies, so it is rather cumbersome. On the plus side, they are explicit and we only add the libraries we need in the alpine image. Let me know what you think about this approach.

@jsravn
Copy link
Author

jsravn commented Jan 20, 2019

Thinking about it some more - I'm wondering if it would be better to just try compiling a static phantomjs in a build container, then copy it over. I may give that shot.

@jsravn
Copy link
Author

jsravn commented Jan 21, 2019

@xlson Okay, I did that. It's a lot simpler now. I just fire up a debian container and copy the libraries out of it. This avoids having to maintain all the library versions when copying from Arch. I tested it and it all seems to work.

Here is an example render
image

Image pushed to jsravn/grafana:master-alpine-3.

This is a lot simpler and easier to maintain. Just update the debian
repository and install the latest fontconfig. Then copy the required
libraries into the alpine container.
@xlson
Copy link
Contributor

xlson commented Jan 22, 2019

Good work. I tested it locally and it works fine. It seems we've lost some of the size advantage with the addition of glibc, but that is to be expected.

The gometalinter issue should be solved by merging master.

The approach seems good to me. There is the question of maintainability and if there are enough users that require an alpine-based docker image to warrant the maintenance burden. I'll leave the PR open for a bit so that we get a chance to discuss.

@jeff-cook
Copy link

Having fewer vulnerabilities and easier to update to remove the vulnerabilities is important to everyone! If they know about it or not.

@kaspernissen
Copy link

Alpine support would be great. We are currently seeing the following results with Snyk.io docker scanning of Grafana version 5.4.3:

High: 9, Medium: 11, Low: 57

@lmprice
Copy link

lmprice commented Apr 24, 2019

We currently have a multistage build that starts from the official Grafana image and creates an up to date alpine image. It would be great if there was an Alpine image included as an official tag.

I was going to see about contributing it, but found this PR.

@kaspernissen
Copy link

@lmprice This sounds very interesting. Is it possible to have a look at it somewhere? Thanks.

@c10l
Copy link

c10l commented May 14, 2019

I just did a similar thing to what @lmprice said. Dockerfile below.

ARG GRAFANA_VERSION=6.1.6
FROM grafana/grafana:${GRAFANA_VERSION} AS upstream

FROM alpine:3.9
ARG GF_UID="472"
ARG GF_GID="472"

ENV PATH=/usr/share/grafana/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
  GF_PATHS_CONFIG="/etc/grafana/grafana.ini" \
  GF_PATHS_DATA="/var/lib/grafana" \
  GF_PATHS_HOME="/usr/share/grafana" \
  GF_PATHS_LOGS="/var/log/grafana" \
  GF_PATHS_PLUGINS="/var/lib/grafana/plugins" \
  GF_PATHS_PROVISIONING="/etc/grafana/provisioning"

WORKDIR $GF_PATHS_HOME

####### Alpine phantomjs compatibility lifted from https://github.com/grafana/grafana/pull/14913/files
RUN apk add --update --no-cache shadow ca-certificates curl bash file openssl fontconfig ttf-dejavu && \
  rm -rf /tmp/*.apk /var/cache/apk/*

# Add glibc - required by grafana-server, grafana-cli, and phantomjs.
ARG GLIBC_VER="2.28-r0"
RUN ALPINE_GLIBC_REPO="https://github.com/sgerrand/alpine-pkg-glibc/releases/download" && \
  curl -Ls https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub -o /etc/apk/keys/sgerrand.rsa.pub && \
  curl -Ls ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-${GLIBC_VER}.apk > /tmp/${GLIBC_VER}.apk && \
  apk add /tmp/${GLIBC_VER}.apk && \
  curl -Ls ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-bin-${GLIBC_VER}.apk > /tmp/${GLIBC_VER}-bin.apk && \
  apk add /tmp/${GLIBC_VER}-bin.apk && \
  rm -rf /tmp/*.apk /var/cache/apk/*

# Fix glibc ldd command - see https://github.com/sgerrand/alpine-pkg-glibc/issues/103.
RUN sed -i s/lib64/lib/ /usr/glibc-compat/bin/ldd

# Add required phantomjs libs.
COPY --from=upstream /lib/x86_64-linux-gnu/libz.so.1 /usr/glibc-compat/lib
COPY --from=upstream /usr/lib/x86_64-linux-gnu/libfontconfig.so.1 /usr/glibc-compat/lib
COPY --from=upstream /usr/lib/x86_64-linux-gnu/libfreetype.so.6 /usr/glibc-compat/lib
COPY --from=upstream /usr/lib/x86_64-linux-gnu/libstdc++.so.6 /usr/glibc-compat/lib
COPY --from=upstream /lib/x86_64-linux-gnu/libgcc_s.so.1 /usr/glibc-compat/lib
COPY --from=upstream /lib/x86_64-linux-gnu/libexpat.so.1 /usr/glibc-compat/lib
COPY --from=upstream /usr/lib/x86_64-linux-gnu/libpng16.so.16 /usr/glibc-compat/lib
RUN /usr/glibc-compat/sbin/ldconfig
########

COPY --from=upstream $GF_PATHS_CONFIG $GF_PATHS_CONFIG
COPY --from=upstream $GF_PATHS_DATA $GF_PATHS_DATA
COPY --from=upstream $GF_PATHS_HOME $GF_PATHS_HOME
COPY --from=upstream $GF_PATHS_LOGS $GF_PATHS_LOGS
COPY --from=upstream $GF_PATHS_PLUGINS $GF_PATHS_PLUGINS
COPY --from=upstream $GF_PATHS_PROVISIONING $GF_PATHS_PROVISIONING
COPY --from=upstream /run.sh /run.sh

RUN addgroup -g $GF_GID grafana && \
  adduser -D -u $GF_UID -G grafana grafana && \
  chown -R grafana:grafana "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" && \
  chmod 700 -R "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING"

EXPOSE 3000

USER grafana
ENTRYPOINT [ "/run.sh" ]

@lmprice
Copy link

lmprice commented May 14, 2019

@lmprice This sounds very interesting. Is it possible to have a look at it somewhere? Thanks.

It isn't exactly groundbreaking, but it served my needs. We're about to contribute a new OS project and our security team was going crazy with the Ubuntu and Debian images. Swapping to an Alpine base using something like this saved me a lot of time and effort.

FROM grafana/grafana:6.1.3 as base
FROM alpine:3.9

ARG GF_UID="1000"
ARG GF_GID="1000"

ENV PATH=/usr/share/grafana/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
    GF_PATHS_CONFIG="/etc/grafana/grafana.ini" \
    GF_PATHS_DATA="/var/lib/grafana" \
    GF_PATHS_HOME="/usr/share/grafana" \
    GF_PATHS_LOGS="/var/log/grafana" \
    GF_PATHS_PLUGINS="/var/lib/grafana/plugins" \
    GF_PATHS_PROVISIONING="/etc/grafana/provisioning"

WORKDIR $GF_PATHS_HOME

RUN apk update && apk upgrade && \
    apk add --update --no-cache ca-certificates libc6-compat ca-certificates && \
    rm -rf /var/cache/apk/*

COPY --from=base $GF_PATHS_HOME $GF_PATHS_HOME
COPY --from=base /run.sh /run.sh
USER root
RUN mkdir -p "$GF_PATHS_HOME/.aws" && \
    addgroup -S -g $GF_GID grafana && \
    adduser -S -u $GF_UID -g grafana grafana && \
    mkdir -p "$GF_PATHS_PROVISIONING/datasources" \
             "$GF_PATHS_PROVISIONING/dashboards" \
             "$GF_PATHS_PROVISIONING/notifiers" \
             "$GF_PATHS_LOGS" \
             "$GF_PATHS_PLUGINS" \
             "$GF_PATHS_DATA" && \
    cp "$GF_PATHS_HOME/conf/sample.ini" "$GF_PATHS_CONFIG" && \
    cp "$GF_PATHS_HOME/conf/ldap.toml" /etc/grafana/ldap.toml && \
    chown -R grafana:grafana "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" && \
    chmod 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" && \
    chmod -R 777 $GF_PATHS_HOME /run.sh && \
    sed -i 's/bash/sh/g' /run.sh

EXPOSE 3000
USER grafana
ENTRYPOINT [ "/run.sh" ]

@sbkg0002
Copy link

@xlson with so may suggestions, why don't we pick one that seems to fit best and get rid of the vulnerabilities?

@xlson
Copy link
Contributor

xlson commented Jun 10, 2019

@sbkg0002 Unfortunately a lot of these solutions doesn't work with PhantomJS and since we are in the process (actively) of replacing it we're holding off on introducing an alpine image. In the meantime we will most likely move to ubuntu as that has 0 high severity vulnerabilities at least.

@bergquist
Copy link
Contributor

Thank you for taking the time to contribute to Grafana.

I've decided to not merge this as we switched base image from debian to ubuntu in #17066 to get rid of all high severity issues. While we appreciate the effort of your solution its, not something we want to maintain.

We are working on replacing phantomjs and once that's done we should reevaluate switching to alpine as the default container.

@bergquist bergquist closed this Jun 18, 2019
@ying-jeanne ying-jeanne added the pr/external This PR is from external contributor label Apr 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr/external This PR is from external contributor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants