Skip to content
This repository has been archived by the owner on Sep 7, 2018. It is now read-only.

Image improvements #146

Merged
merged 18 commits into from
Apr 13, 2018
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
FROM debian:jessie
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we try something like debian:9.3-slim while at it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, forgot about that. Will look into it.


ARG DOWNLOAD_URL="https://s3-us-west-2.amazonaws.com/grafana-releases/master/grafana_latest_amd64.deb"
ARG GRAFANA_VERSION="latest"
ARG GF_HOME="/usr/share/grafana"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of defining defaults in the run.sh I reckon to set the ENV-defaults in the Dockerfile and use GF_PATH_HOME directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Should make it easier to change in the future.


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO you can add build argument for architecture - see https://github.com/monitoringartist/grafana-xxl/pull/8/files. Then you can build also arm images easily.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jangaraj It's a bit out of scope for this PR but that's definitely something to look into when we have inhouse ARM builds of Grafana.

RUN apt-get update && \
apt-get -y --no-install-recommends install libfontconfig curl ca-certificates && \
apt-get clean && \
curl ${DOWNLOAD_URL} > /tmp/grafana.deb && \
dpkg -i /tmp/grafana.deb && \
rm /tmp/grafana.deb && \
curl -L https://github.com/tianon/gosu/releases/download/1.10/gosu-amd64 > /usr/sbin/gosu && \
chmod +x /usr/sbin/gosu && \
RUN apt-get update && apt-get install -qq -y wget tar sqlite libfontconfig curl ca-certificates && \
wget -O /tmp/grafana.tar.gz https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-$GRAFANA_VERSION.linux-x64.tar.gz && \
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You install both curl and wget? Just use curl here and don't use wget.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of puting the tar in a file, use

mkdir -p $GF_PATHS_HOME && \
wget -qO- $GF_URL/grafana-$GRAFANA_VERSION.linux-x64.tar.gz |tar xfvz - --strip-components=1 --exclude=tools -C $GF_PATHS_HOME

Skips writing the file to a file and need to cleanup, as well as directly extracts in the GF_PATH_HOME.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. We need tools though. May change in the future.

tar -zxvf /tmp/grafana.tar.gz -C /tmp && rm /tmp/grafana.tar.gz && \
mv /tmp/grafana-* $GF_HOME && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*

RUN mkdir -p /etc/grafana/provisioning/datasources && \
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mkdir takes multiple directories as an argument

Copy link
Contributor Author

@xlson xlson Mar 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Not sure why I didn't do that originally.

mkdir -p /etc/grafana/provisioning/dashboards && \
mkdir -p /var/lib/grafana/plugins && \
mkdir -p /var/log/grafana && \
cp $GF_HOME/conf/sample.ini /etc/grafana/grafana.ini && \
cp $GF_HOME/conf/ldap.toml /etc/grafana/ldap.toml && \
cp $GF_HOME/bin/grafana-server /usr/sbin/grafana-server && \
cp $GF_HOME/bin/grafana-cli /usr/sbin/grafana-cli && \
chown -R nobody:nogroup /var/lib/grafana && \
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use nobody as a user for a service, even inside docker. Create a specific user for this service. You don't want someone to bind mount things and files to be owned by the user nobody . nobody is special in unix due to some nasty legacy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There doesn't seem to be a good way to solve this problem when you're trying to mount directories from the host filesystem as volumes, because you need to ensure that there is a matching uid & gid between the process running in the container and on the host.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes nobody and even worse option, since nobody has a very explicit meaning and you should never run services as nobody.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some discussion with @xlson the proposed solution is to explicitly set a uid and gid (exact value tbd) for the grafana user & group in the container, make all Grafana files world-readable in the container, and use USER grafana:grafana. Users looking to mount external volumes can either chown them to that uid or use the -u docker option to run as a different uid.

If the uid and gid are specified with ARG it would also be relatively straightforward to build a custom image with whatever uid/gid the end user required.

chown -R nobody:nogroup $GF_HOME && \
chown -R nobody:nogroup /var/log/grafana

VOLUME ["/var/lib/grafana", "/var/log/grafana", "/etc/grafana"]
VOLUME ["/var/lib/grafana"]

EXPOSE 3000

COPY ./run.sh /run.sh

ENTRYPOINT ["/run.sh"]
USER nobody

ENTRYPOINT [ "/run.sh" ]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rather use CMD then ENTRYPOINT, as I find it always cumbersome to debug, as one can not simply use docker run -ti --rm grafana/grafana-docker bash.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm like that too. Will have to check if there are any good reasons to keep it as an entrypoint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I agree that CMD would have been a better option it would be a breaking change and as such I don't think its enough of an improvement to warrant it.

25 changes: 16 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,22 @@ More information in the grafana configuration documentation: http://docs.grafana
## Grafana container with persistent storage (recommended)

```
# create /var/lib/grafana as persistent volume storage
docker run -d -v /var/lib/grafana --name grafana-storage busybox:latest
# create a persistent volume for your data in /var/lib/grafana (database and plugins)
docker volume create grafana-storage

# start grafana
docker run \
-d \
-p 3000:3000 \
--name=grafana \
--volumes-from grafana-storage \
-v grafana-storage:/var/lib/grafana \
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should probably have :z as an argument as well, in case of SELinux?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never seen that one before. Will look into it. Thanks for reviewing the container, much appreciated.

grafana/grafana
```

Note: An unnamed volume will be created for you when you boot Grafana,
using `docker volume create grafana-storage` just makes it easier to find
by giving it a name.

## Installing plugins for Grafana 3

Pass the plugins you want installed to docker with the `GF_INSTALL_PLUGINS` environment variable as a comma seperated list. This will pass each plugin name to `grafana-cli plugins install ${plugin}`.
Expand All @@ -72,22 +76,25 @@ docker run \

Dockerfile:
```Dockerfile
FROM grafana/grafana:5.0.0
FROM grafana/grafana:master
ENV GF_PATHS_PLUGINS=/opt/grafana-plugins
RUN mkdir -p $GF_PATHS_PLUGINS
RUN grafana-cli --pluginsDir $GF_PATHS_PLUGINS plugins install grafana-clock-panel
USER root
RUN mkdir -p $GF_PATHS_PLUGINS && chown nobody:nogroup $GF_PATHS_PLUGINS
USER nobody
RUN grafana-cli --pluginsDir $GF_PATHS_PLUGINS plugins install grafana-clock-panel && \
grafana-cli --pluginsDir $GF_PATHS_PLUGINS plugins install grafana-simple-json-datasource
```

Add lines with `RUN grafana-cli ...` for each plugin you wish to install in your custom image. Don't forget to specify what version of Grafana you wish to build from (replace 5.0.0 in the example).
Add lines with `grafana-cli ...` for each plugin you wish to install in your custom image. Don't forget to specify what version of Grafana you wish to build from (replace master in the example).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add support for passing GF_PLUGINS as a build arg, then using RUN to iterate over them in the standard Dockerfile?

Copy link
Contributor Author

@xlson xlson Mar 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that something you think we might use ourselves? Without someone with that explicit need I don't see a big advantage over just extending our Dockerfile instead as actually using this feature would require forking our repo instead of just creating a new file.


Example of how to build and run:
```bash
docker build -t grafana:5.0.0-custom .
docker build -t grafana:master-with-plugins .
docker run \
-d \
-p 3000:3000 \
--name=grafana \
grafana:5.0.0-custom
grafana:master-with-plugins
```

## Running specific version of Grafana
Expand Down
8 changes: 3 additions & 5 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
#!/bin/bash

_grafana_tag=$1
_grafana_version=${_grafana_tag:1}
_grafana_version=$1

if [ "$_grafana_version" != "" ]; then
echo "Building version ${_grafana_version}"
echo "Download url: https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${_grafana_version}_amd64.deb"
docker build \
--build-arg DOWNLOAD_URL=https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${_grafana_version}_amd64.deb \
--build-arg GRAFANA_VERSION=${_grafana_version} \
--tag "grafana/grafana:${_grafana_version}" \
--no-cache=true .
docker tag grafana/grafana:${_grafana_version} grafana/grafana:latest
Expand All @@ -16,5 +14,5 @@ else
echo "Building latest for master"
docker build \
--tag "grafana/grafana:master" \
--no-cache=true .
.
fi
56 changes: 38 additions & 18 deletions run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,70 @@

: "${GF_PATHS_CONFIG:=/etc/grafana/grafana.ini}"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll put the defaults in the Dockerfile using ENV, by doing so one can overwrite the default and it's clear where they are set.

: "${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}"

chown -R grafana:grafana "$GF_PATHS_DATA" "$GF_PATHS_LOGS" || true
PERMISSIONS_OK=0

if [ ! -r "$GF_PATHS_CONFIG" ]; then
echo "GF_PATHS_CONFIG='$GF_PATHS_CONFIG' is not readable."
PERMISSIONS_OK=1
fi

if [ ! -w "$GF_PATHS_DATA" ]; then
echo "GF_PATHS_DATA='$GF_PATHS_DATA' is not writable."
PERMISSIONS_OK=1
fi

if [ ! -r "$GF_PATHS_HOME" ]; then
echo "GF_PATHS_HOME='$GF_PATHS_HOME' is not readable."
PERMISSIONS_OK=1
fi

if [ $PERMISSIONS_OK -eq 1 ]; then
echo "You may have issues with file permissions, more information here: http://docs.grafana.org/installation/docker/#migration-from-a-previous-version-of-the-docker-container-to-5-1-or-later"
fi


if [ ! -z ${GF_AWS_PROFILES+x} ]; then
mkdir -p ~grafana/.aws/
> ~grafana/.aws/credentials
mkdir -p "$GF_PATHS_HOME/.aws/"
> "$GF_PATHS_HOME/.aws/credentials"

for profile in ${GF_AWS_PROFILES}; do
access_key_varname="GF_AWS_${profile}_ACCESS_KEY_ID"
secret_key_varname="GF_AWS_${profile}_SECRET_ACCESS_KEY"
region_varname="GF_AWS_${profile}_REGION"

if [ ! -z "${!access_key_varname}" -a ! -z "${!secret_key_varname}" ]; then
echo "[${profile}]" >> ~grafana/.aws/credentials
echo "aws_access_key_id = ${!access_key_varname}" >> ~grafana/.aws/credentials
echo "aws_secret_access_key = ${!secret_key_varname}" >> ~grafana/.aws/credentials
echo "[${profile}]" >> "$GF_PATHS_HOME/.aws/credentials"
echo "aws_access_key_id = ${!access_key_varname}" >> "$GF_PATHS_HOME/.aws/credentials"
echo "aws_secret_access_key = ${!secret_key_varname}" >> "$GF_PATHS_HOME/.aws/credentials"
if [ ! -z "${!region_varname}" ]; then
echo "region = ${!region_varname}" >> ~grafana/.aws/credentials
echo "region = ${!region_varname}" >> "$GF_PATHS_HOME/.aws/credentials"
fi
fi
done

chown grafana:grafana -R ~grafana/.aws
chmod 600 ~grafana/.aws/credentials
chmod 600 "$GF_PATHS_HOME/.aws/credentials"
fi

if [ ! -z "${GF_INSTALL_PLUGINS}" ]; then
OLDIFS=$IFS
IFS=','
for plugin in ${GF_INSTALL_PLUGINS}; do
IFS=$OLDIFS
gosu grafana grafana-cli --pluginsDir "${GF_PATHS_PLUGINS}" plugins install ${plugin}
grafana-cli --pluginsDir "${GF_PATHS_PLUGINS}" plugins install ${plugin}
done
fi

exec gosu grafana /usr/sbin/grafana-server \
--homepath=/usr/share/grafana \
--config="$GF_PATHS_CONFIG" \
cfg:default.log.mode="console" \
cfg:default.paths.data="$GF_PATHS_DATA" \
cfg:default.paths.logs="$GF_PATHS_LOGS" \
cfg:default.paths.plugins="$GF_PATHS_PLUGINS" \
cfg:default.paths.provisioning=$GF_PATHS_PROVISIONING \
exec /usr/sbin/grafana-server \
--homepath="$GF_PATHS_HOME" \
--config="$GF_PATHS_CONFIG" \
cfg:default.log.mode="console" \
cfg:default.paths.data="$GF_PATHS_DATA" \
cfg:default.paths.logs="$GF_PATHS_LOGS" \
cfg:default.paths.plugins="$GF_PATHS_PLUGINS" \
cfg:default.paths.provisioning="$GF_PATHS_PROVISIONING" \
"$@"