How to upload large docker images to google container registry? - docker

I'm creating a lap image from this (https://github.com/ulsmith/alpine-apache-php7) and in my application, I have a 2.5 GB folder of images the image is built successfully but when I try to push it to google container registry it doesn't work due to large size and sometimes server timeout and that will be like after a minimum of 20 min. I get the error of image after 15 mins or more and before that the google cloud SDK is showing nothing.
Here is the command I am using: gcloud builds submit --tag us.gcr.io/[project-id]/test:v1
I checked crcmod and it is enabled.
This is my Dockerfile:+
FROM alpine:edge
MAINTAINER Paul Smith <pa.ulsmith.net>
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
RUN apk update && apk upgrade && apk add \
bash apache2 php7-apache2 curl ca-certificates openssl openssh git php7 php7-phar php7-json php7-iconv php7-openssl tzdata openntpd nano
RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
RUN apk add \
php7-ftp \
php7-xdebug \
php7-mcrypt \
php7-mbstring \
php7-soap \
php7-gmp \
php7-pdo_odbc \
php7-dom \
php7-pdo \
php7-zip \
php7-mysqli \
php7-sqlite3 \
php7-pdo_pgsql \
php7-bcmath \
php7-gd \
php7-odbc \
php7-pdo_mysql \
php7-pdo_sqlite \
php7-gettext \
php7-xml \
php7-xmlreader \
php7-xmlwriter \
php7-tokenizer \
php7-xmlrpc \
php7-bz2 \
php7-pdo_dblib \
php7-curl \
php7-ctype \
php7-session \
php7-redis \
php7-exif \
php7-intl \
php7-fileinfo \
php7-ldap \
php7-apcu
RUN apk add php7-simplexml
RUN cp /usr/bin/php7 /usr/bin/php \
&& rm -f /var/cache/apk/*
RUN sed -i "s/#LoadModule\ rewrite_module/LoadModule\ rewrite_module/" /etc/apache2/httpd.conf \
&& sed -i "s/#LoadModule\ session_module/LoadModule\ session_module/" /etc/apache2/httpd.conf \
&& sed -i "s/#LoadModule\ session_cookie_module/LoadModule\ session_cookie_module/" /etc/apache2/httpd.conf \
&& sed -i "s/#LoadModule\ session_crypto_module/LoadModule\ session_crypto_module/" /etc/apache2/httpd.conf \
&& sed -i "s/#LoadModule\ deflate_module/LoadModule\ deflate_module/" /etc/apache2/httpd.conf \
&& sed -i "s#^DocumentRoot \".*#DocumentRoot \"/app/public\"#g" /etc/apache2/httpd.conf \
&& sed -i "s#/var/www/localhost/htdocs#/app/public#" /etc/apache2/httpd.conf \
&& printf "\n<Directory \"/app/public\">\n\tAllowOverride All\n</Directory>\n" >> /etc/apache2/httpd.conf
ENV PHP_ALLOW_URL_INCLUDE=On
RUN mkdir /app && mkdir /app/public && chown -R apache:apache /app && chmod -R 755 /app && mkdir bootstrap
COPY app/ /app/public/
ADD start.sh /bootstrap/
RUN chmod +x /bootstrap/start.sh
EXPOSE 80
ENTRYPOINT ["/bootstrap/start.sh"]
Is there a better way to push large images?

Please contact us with the un-redacted image at gcr-contact#google.com
In general (this may not be an issue for you), the problem with large images is that the short-lived access tokens you receive via our normal token exchange will result in failed uploads. You're going to have to explore JSON key authentication in order to enable those very long sessions when uploading your images to GCR
Also to note that Container Registry does not support Docker chunked uploads. Some container image tools support uploading large container images with either chunked uploads or a single monolithic upload. You must use monolithic uploads when you push container images to Container Registry.

If you are pushing a large image to GCR a much quicker way would be to:
Spin up a GCP VM instance
Upload your Dockerfile (and any relevant files needed for the build)
Build the container and push to GCR from there

Related

Run 'opentsdb' image as non-root

I'm trying to build a custom image of opentsdb to run as non-root user. Our k8s clusters have security policies that doesn't allow containers to run as root. I'm utilizing an existing Dockerfile from here https://hub.docker.com/r/petergrace/opentsdb-docker/dockerfile
Below is my Docker file where I have added extra step to create a new user 'opentsdb' and at the end running it as USER 'opentsdb'
FROM alpine:latest
ENV TINI_VERSION v0.18.0
ENV TSDB_VERSION 2.4.0
ENV HBASE_VERSION 1.4.4
ENV GNUPLOT_VERSION 5.2.4
ENV JAVA_HOME /usr/lib/jvm/java-1.8-openjdk
ENV PATH $PATH:/usr/lib/jvm/java-1.8-openjdk/bin/
ENV ALPINE_PACKAGES "rsyslog bash openjdk8 make wget libgd libpng libjpeg libwebp libjpeg-turbo cairo pango lua"
ENV BUILD_PACKAGES "build-base autoconf automake git python3-dev cairo-dev pango-dev gd-dev lua-dev readline-dev libpng-dev libjpeg-turbo-dev libwebp-dev sed"
ENV HBASE_OPTS "-XX:+UseConcMarkSweepGC -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
ENV JVMARGS "-XX:+UseConcMarkSweepGC -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -enableassertions -enablesystemassertions"
RUN addgroup opentsdb && adduser -D -u 100 -G opentsdb opentsdb
# Tini is a tiny init that helps when a container is being culled to stop things nicely
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static-amd64 /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]
# Add the base packages we'll need
RUN apk --update add apk-tools \
&& apk add ${ALPINE_PACKAGES} \
# repo required for gnuplot \
--repository http://dl-cdn.alpinelinux.org/alpine/v3.0/testing/ \
&& mkdir -p /opt/opentsdb
WORKDIR /opt/opentsdb/
# Add build deps, build opentsdb, and clean up afterwards.
RUN set -ex && apk add --virtual builddeps ${BUILD_PACKAGES}
RUN ln -s /usr/bin/python3 /usr/bin/python
RUN wget --no-check-certificate \
-O v${TSDB_VERSION}.zip \
https://github.com/OpenTSDB/opentsdb/archive/v${TSDB_VERSION}.zip \
&& unzip v${TSDB_VERSION}.zip \
&& rm v${TSDB_VERSION}.zip \
&& cd /opt/opentsdb/opentsdb-${TSDB_VERSION} \
&& echo "tsd.http.request.enable_chunked = true" >> src/opentsdb.conf \
&& echo "tsd.http.request.max_chunk = 1000000" >> src/opentsdb.conf
RUN cd /opt/opentsdb/opentsdb-${TSDB_VERSION} \
&& find . | xargs grep -s central.maven.org | cut -f1 -d : | xargs sed -i "s/http:\/\/central/https:\/\/repo1/g" \
&& find . | xargs grep -s repo1.maven.org | cut -f1 -d : | xargs sed -i "s/http:\/\/repo1/https:\/\/repo1/g" \
&& ./build.sh \
&& cp build-aux/install-sh build/build-aux \
&& cd build \
&& make install \
&& cd / \
&& rm -rf /opt/opentsdb/opentsdb-${TSDB_VERSION}
RUN cd /tmp && \
wget --no-check-certificate https://sourceforge.net/projects/gnuplot/files/gnuplot/${GNUPLOT_VERSION}/gnuplot-${GNUPLOT_VERSION}.tar.gz && \
tar xzf gnuplot-${GNUPLOT_VERSION}.tar.gz && \
cd gnuplot-${GNUPLOT_VERSION} && \
./configure && \
make install && \
cd /tmp && rm -rf /tmp/gnuplot-${GNUPLOT_VERSION} && rm /tmp/gnuplot-${GNUPLOT_VERSION}.tar.gz
RUN apk del builddeps && rm -rf /var/cache/apk/*
#Install HBase and scripts
RUN mkdir -p /data/hbase /root/.profile.d /opt/downloads
WORKDIR /opt/downloads
RUN wget -O hbase-${HBASE_VERSION}.bin.tar.gz http://archive.apache.org/dist/hbase/${HBASE_VERSION}/hbase-${HBASE_VERSION}-bin.tar.gz \
&& tar xzvf hbase-${HBASE_VERSION}.bin.tar.gz \
&& mv hbase-${HBASE_VERSION} /opt/hbase \
&& rm -r /opt/hbase/docs \
&& rm hbase-${HBASE_VERSION}.bin.tar.gz
# Add misc startup files
RUN ln -s /usr/local/share/opentsdb/etc/opentsdb /etc/opentsdb \
&& rm /etc/opentsdb/opentsdb.conf \
&& mkdir /opentsdb-plugins
ADD files/opentsdb.conf /etc/opentsdb/opentsdb.conf.sample
ADD files/hbase-site.xml /opt/hbase/conf/hbase-site.xml.sample
ADD files/start_opentsdb.sh /opt/bin/
ADD files/create_tsdb_tables.sh /opt/bin/
ADD files/start_hbase.sh /opt/bin/
ADD files/entrypoint.sh /entrypoint.sh
# Fix ENV variables in installed scripts
RUN for i in /opt/bin/start_hbase.sh /opt/bin/start_opentsdb.sh /opt/bin/create_tsdb_tables.sh; \
do \
sed -i "s#::JAVA_HOME::#$JAVA_HOME#g; s#::PATH::#$PATH#g; s#::TSDB_VERSION::#$TSDB_VERSION#g;" $i; \
done
RUN echo "export HBASE_OPTS=\"${HBASE_OPTS}\"" >> /opt/hbase/conf/hbase-env.sh
#4242 is tsdb, rest are hbase ports
EXPOSE 60000 60010 60030 4242 16010 16070
USER opentsdb
#HBase is configured to store data in /data/hbase, vol-mount it to persist your data.
VOLUME ["/data/hbase", "/tmp", "/opentsdb-plugins"]
CMD ["/entrypoint.sh"]
however the newly built image is throwing error and says permission denied for /opt/bin/ files. And the opentsdb is not getting deployed correctly.
On local using docker desktop, everything works fine using root, when I run below command
docker run -dp 4242:4242 petergrace/opentsdb-docker
Do i need to use any chown commands too ?
Could you help how to make opentsdb get deployed correctly using uid 100 ? Thanks in advance!

docker standard_init_linux.go:228: no such file or directory

I am struggling to find the cause of the following error after building an image and trying to run it. The error is bellow:
standard_init_linux.go:228: exec user process caused: no such file or directory
the Dockerfile
FROM rocker/r-ver:3.6.3
RUN apt-get update && apt-get install -y \
sudo \
gdebi-core \
pandoc \
pandoc-citeproc \
libcurl4-gnutls-dev \
libcairo2-dev \
libxt-dev \
xtail \
wget \
dos2unix
RUN wget --no-verbose https://download3.rstudio.org/ubuntu-14.04/x86_64/VERSION -O "version.txt" && \
VERSION=$(cat version.txt) && \
wget --no-verbose "https://download3.rstudio.org/ubuntu-14.04/x86_64/shiny-server-$VERSION-
amd64.deb" -O ss-latest.deb && \
gdebi -n ss-latest.deb && \
rm -f version.txt ss-latest.deb && \
. /etc/environment && \
R -e "install.packages(c('shiny', 'rmarkdown'), repos='$MRAN')" && \
cp -R /usr/local/lib/R/site-library/shiny/examples/* /srv/shiny-server/ && \
chown shiny:shiny /var/lib/shiny-server
EXPOSE 3838
COPY shiny-server.sh /usr/bin/shiny-server.sh
CMD ["/usr/bin/shiny-server.sh"]
the file shiny-server.sh
#!/bin/sh
# Make sure the directory for individual app logs exists
mkdir -p /var/log/shiny-server
chown shiny.shiny /var/log/shiny-server
if [ "$APPLICATION_LOGS_TO_STDOUT" != "false" ];
then
# push the "real" application logs to stdout with xtail in detached mode
exec xtail /var/log/shiny-server/ &
fi
# start shiny server
exec shiny-server 2>&1
Appreciate any help
correct the windows line endings with notepad++. That should work.

Super Slow to public application on Docker

I researched everything and I can't find a solution.
I'm running the docker-compose compilation that takes 10 to 15 minutes to go up in production, it's crazy. I've tried everything, tried to erase the volume, tried to clean the entire system, changed the network connection, uninstalled / reinstalled the Docker, ran a complete docker system prune -a - nothing.
I don't know if it is because of this delay, but it seems that even the dns gets lost and cannot find the application after all this time. I have to press ctrl + f5 to load the new update.
But this time is driving me crazy, 15 minutes to get everything up.
I appreciate any help.
.yml:
version: '3'
volumes:
app-volume:
services:
nginx:
container_name: nginx
restart: always
build:
context: ./nginx/
dockerfile: ./Dockerfile
depends_on:
- app
volumes:
- app-volume:/app
env_file:
- ./nginx/.envs/nginx.env
ports:
- "80:80"
- "443:443"
app:
container_name: app
build:
context: ./app/
dockerfile: ./compose/production/Dockerfile
image: app
volumes:
# - ./app/:/app
- app-volume:/home/app
env_file:
- ./app/.envs/.production/app.env
app Dockefile:
FROM node:12-alpine as builder
WORKDIR /home/app
RUN apk update && \
apk add --no-cache python make g++ iputils
RUN npm install -g #angular/cli
COPY package*.json ./
COPY *.lock ./
RUN yarn install --unsafe-perm
COPY . .
ARG FORCE_BUILD_ENV=1
ARG ENV_BUILD=0
RUN export $(grep -v '^#' .envs/.production/app.env | xargs) && \
npm run build:env && \
ls -lah && \
cat ./src/environments/environment.ts && \
cat ./src/environments/environment.prod.ts
CMD ["ng", "build", "--prod", "--outputPath=dist"]
nginx/Dockerfile:
FROM alpine:3.10
LABEL maintainer="NGINX Docker Maintainers <docker-maint#nginx.com>"
ENV NGINX_VERSION 1.16.1
ENV NGX_BROTLI_COMMIT e505dce68acc190cc5a1e780a3b0275e39f160ca
RUN GPG_KEYS=B0F4253373F8F6F510D42178520A9993A1C052F8 \
&& CONFIG="\
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_stub_status_module \
--with-http_auth_request_module \
--with-http_xslt_module=dynamic \
--with-http_image_filter_module=dynamic \
--with-http_geoip_module=dynamic \
--with-http_perl_module=dynamic \
--with-threads \
--with-stream \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-stream_realip_module \
--with-stream_geoip_module=dynamic \
--with-http_slice_module \
--with-mail \
--with-mail_ssl_module \
--with-compat \
--with-file-aio \
--with-http_v2_module \
--add-module=/usr/src/ngx_brotli \
" \
&& addgroup -S nginx \
&& adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx \
&& apk add --no-cache --virtual .build-deps \
gcc \
libc-dev \
make \
openssl-dev \
pcre-dev \
zlib-dev \
linux-headers \
curl \
gnupg1 \
libxslt-dev \
gd-dev \
geoip-dev \
perl-dev \
&& apk add --no-cache --virtual .brotli-build-deps \
autoconf \
libtool \
automake \
git \
g++ \
cmake \
&& mkdir -p /usr/src \
&& cd /usr/src \
&& git clone --recursive https://github.com/google/ngx_brotli.git \
&& cd ngx_brotli \
&& git checkout -b $NGX_BROTLI_COMMIT $NGX_BROTLI_COMMIT \
&& cd .. \
&& curl -fSL https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz -o nginx.tar.gz \
&& curl -fSL https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz.asc -o nginx.tar.gz.asc \
&& sha512sum nginx.tar.gz nginx.tar.gz.asc \
&& export GNUPGHOME="$(mktemp -d)" \
&& gpg --keyserver ipv4.pool.sks-keyservers.net --recv-keys "$GPG_KEYS" \
&& gpg --batch --verify nginx.tar.gz.asc nginx.tar.gz \
&& rm -rf "$GNUPGHOME" nginx.tar.gz.asc \
&& mkdir -p /usr/src \
&& tar -zxC /usr/src -f nginx.tar.gz \
&& rm nginx.tar.gz \
&& cd /usr/src/nginx-$NGINX_VERSION \
&& ./configure $CONFIG --with-debug \
&& make -j$(getconf _NPROCESSORS_ONLN) \
&& mv objs/nginx objs/nginx-debug \
&& mv objs/ngx_http_xslt_filter_module.so objs/ngx_http_xslt_filter_module-debug.so \
&& mv objs/ngx_http_image_filter_module.so objs/ngx_http_image_filter_module-debug.so \
&& mv objs/ngx_http_geoip_module.so objs/ngx_http_geoip_module-debug.so \
&& mv objs/ngx_http_perl_module.so objs/ngx_http_perl_module-debug.so \
&& mv objs/ngx_stream_geoip_module.so objs/ngx_stream_geoip_module-debug.so \
&& ./configure $CONFIG \
&& make -j$(getconf _NPROCESSORS_ONLN) \
&& make install \
&& rm -rf /etc/nginx/html/ \
&& mkdir /etc/nginx/conf.d/ \
&& mkdir -p /usr/share/nginx/html/ \
&& install -m644 html/index.html /usr/share/nginx/html/ \
&& install -m644 html/50x.html /usr/share/nginx/html/ \
&& install -m755 objs/nginx-debug /usr/sbin/nginx-debug \
&& install -m755 objs/ngx_http_xslt_filter_module-debug.so /usr/lib/nginx/modules/ngx_http_xslt_filter_module-debug.so \
&& install -m755 objs/ngx_http_image_filter_module-debug.so /usr/lib/nginx/modules/ngx_http_image_filter_module-debug.so \
&& install -m755 objs/ngx_http_geoip_module-debug.so /usr/lib/nginx/modules/ngx_http_geoip_module-debug.so \
&& install -m755 objs/ngx_http_perl_module-debug.so /usr/lib/nginx/modules/ngx_http_perl_module-debug.so \
&& install -m755 objs/ngx_stream_geoip_module-debug.so /usr/lib/nginx/modules/ngx_stream_geoip_module-debug.so \
&& ln -s ../../usr/lib/nginx/modules /etc/nginx/modules \
&& strip /usr/sbin/nginx* \
&& strip /usr/lib/nginx/modules/*.so \
&& rm -rf /usr/src/nginx-$NGINX_VERSION \
&& rm -rf /usr/src/ngx_brotli \
\
# Bring in gettext so we can get `envsubst`, then throw
# the rest away. To do this, we need to install `gettext`
# then move `envsubst` out of the way so `gettext` can
# be deleted completely, then move `envsubst` back.
&& apk add --no-cache --virtual .gettext gettext \
&& mv /usr/bin/envsubst /tmp/ \
\
&& runDeps="$( \
scanelf --needed --nobanner /usr/sbin/nginx /usr/lib/nginx/modules/*.so /tmp/envsubst \
| awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
| sort -u \
| xargs -r apk info --installed \
| sort -u \
)" \
&& apk add --no-cache --virtual .nginx-rundeps tzdata $runDeps \
&& apk del .build-deps \
&& apk del .brotli-build-deps \
&& apk del .gettext \
&& mv /tmp/envsubst /usr/local/bin/ \
\
# forward request and error logs to docker log collector
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \
apk update && \
apk add --no-cache openssl brotli vim && \
rm -rf /var/cache/apk/*
RUN mkdir -p /etc/ssl/certs/private/
COPY ./server.crt /etc/ssl/certs/private/siap.crt
COPY ./server.key /etc/ssl/certs/private/siap.key
RUN openssl dhparam -out /etc/ssl/certs/private/dhparam.pem 4096
RUN rm -rf /etc/nginx/conf.d/*
COPY ./scripts/nginx.conf /etc/nginx/nginx.conf
COPY ./scripts/conf.d/brotli.conf /etc/nginx/conf.d/brotli.conf
COPY ./scripts/conf.d/siap.http.conf /etc/nginx/conf.d/siap.http.conf
COPY ./scripts/conf.d/siap.https.conf /etc/nginx/conf.d/siap.https.conf
COPY .envs ./.envs
RUN export $(grep -v '^#' .envs/nginx.env | xargs) && \
sed -i '2s/^.*$/ server '$SERVER_ENDERECO';/g' /etc/nginx/conf.d/siap.https.conf;
EXPOSE 80
STOPSIGNAL SIGTERM
RUN head /etc/nginx/conf.d/siap.https.conf
# RUN nginx -t
CMD ["nginx", "-g", "daemon off;"]
You're rebuilding your application every time you start the container stack. While I wouldn't expect this to usually take 10+ minutes, it's better to build the application just once and built it into a Docker image that can be reused.
You can use a multi-stage build to build the application into static files, and then package it into an Nginx server. The Dockerfile you show is actually most of the way there. You need to change the final CMD to a RUN so it runs during the build, and then add the additional stage to COPY the files into an nginx-based image:
FROM node:12-alpine AS builder
WORKDIR /home/app
... exactly what is in the question ...
RUN ["ng", "build", "--prod", "--outputPath=dist"] # not CMD
FROM alpine:3.10 AS server
... the existing nginx/Dockerfile ...
FROM server
COPY --from=builder /home/app/dist /usr/share/nginx/html
With the extended nginx/Dockerfile in the question, you'll need to do a little bit of additional work to combine these together. You need to COPY files from both the nginx and app subtrees. To make this work, you need to move the Dockerfile up to the top level of your directory tree, and when you COPY files into the image, you need to COPY app/package*.json or COPY nginx/scripts/nginx.conf from one of the subdirectories.
In your docker-compose.yml file, then, you can remove the service that's only used to build files, since this is included in the image-build sequence. You can also remove the volume, again since the compiled application is included in the image.
version: '3.8'
services:
nginx:
build: . # Dockerfile needs to COPY app/... and COPY nginx/...
restart: always
env_file:
- ./nginx/.envs/nginx.env
ports:
- "80:80"
- "443:443"
# no volumes:; do not need to override container_name:
# no app: container
# no top-level volumes:
You can do even better than this in production. If you have access to a Docker registry -- Docker Hub, something your cloud provider offers, something you run yourself -- you can build this image separately, run whatever integration tests you need, and then push it to the registry. Instead of build:, set image: to point to the specific tagged image you need (it's good practice to use a different tag for each build).
services:
nginx:
image: my/nginx:20210524
# no build:
restart: always
env_file: [...]
ports: [...]
Now when you go to do a deploy, build, tag, and push the image, maybe using a CI system for automation. Once you've pushed the image, log into the production machine and change image: to point to the new build, and run docker-compose pull; docker-compose up -d. Compose will pull the updated image and then recreate the container using it. The image contains the prebuilt application, and you pulled it while the old container was already running, so there should be almost no downtime to do this update.

How do I reclaim space in Docker Image after deleting files no longer need (java jdk 11 used to make a jre)

I use Docker with a Java application, previously I used Java 8 JRE and my total docker image size was 163MB, I then moved to use Java 11 JRE and size increased to 230MB, I would prefer not to increase the size if possible.
But Java 11 allows you to build your own JRE (using jlink from the JDK) containing only the modules you need. So I modified my DockerFile to be based on a JDK rather than JRE, I then used this to build the JRE with only the modules I needed and created this within my application folder. I then used rm -fr /opt/java to remove the JDK as I no longer need it, assuming this would shrink the image size down, but it doesn't the image is now 553MB. My Applications runs but there is no point using jlink if I cannot shrink the image down in size, what am I doing wrong ?
Docker File below:
FROM adoptopenjdk/openjdk11:alpine
RUN apk --no-cache add \
ca-certificates \
curl \
fontconfig \
msttcorefonts-installer \
tini \
&& update-ms-fonts \
&& fc-cache -f
RUN mkdir -p /opt \
&& curl http://www.jthink.net/songkong/downloads/build1114/songkong-linux-docker.tgz?val=130| tar -C /opt -xzf - \
&& find /opt/songkong -perm /u+x -type f -print0 | xargs -0 chmod a+x
RUN /opt/java/openjdk/bin/jlink --module-path=/opt/java/openjdk/jmods \
--add-modules java.desktop,java.datatransfer,java.logging,java.management,java.naming,java.net.http,java.prefs,java.scripting,java.sql,jdk.management,jdk.unsupported,jdk.scripting.nashorn \
--output /opt/songkong/jre
RUN rm -fr /opt/java
EXPOSE 4567
ENTRYPOINT ["/sbin/tini"]
# Config, License, Logs, Reports and Internal Database
VOLUME /songkong
# Music folder should be mounted here
VOLUME /music
WORKDIR /opt/songkong
CMD /opt/songkong/songkongremote.sh
It's normal by the nature of a docker image.
A Docker image is base on multiple layer that stack together and each layer are immutable (could not update an another layer content).
So when a file is created in one layer and deleted in an another layer, the file still exist, but no more available and most instruction in a Dockerfile create a new layer.
In your case, we will reduce your image to 3 layer for the demonstration:
[Layer that remove the /opt/java folder]
[Layer with the new JRE]
[Base image with the JDK]
But, in fine, your image have the 3 layers with all the data.
You can visualize it by running docker image history myimage, you will have a list of layers and their size.
If you want to reduce the size, you will need to do a multiple stage build:
- The first stage create the JRE
- The second stage import the JRE and add your code on it
Each stage have different base image, so the second one could use a small base image:
# First stage - Create the JRE
FROM adoptopenjdk/openjdk11:alpine AS jre
RUN /opt/java/openjdk/bin/jlink --module-path=/opt/java/openjdk/jmods \
--add-modules java.desktop,java.datatransfer,java.logging,java.management,java.naming,java.net.http,java.prefs,java.scripting,java.sql,jdk.management,jdk.unsupported,jdk.scripting.nashorn \
--output /opt/songkong/jre
# Second stage
FROM alpine
RUN apk --no-cache add \
ca-certificates \
curl \
fontconfig \
msttcorefonts-installer \
tini \
&& update-ms-fonts \
&& fc-cache -f
COPY --from=jre /opt/songkong/jre /opt/songkong/jre
RUN mkdir -p /opt \
&& curl http://www.jthink.net/songkong/downloads/build1114/songkong-linux-docker.tgz?val=130| tar -C /opt -xzf - \
&& find /opt/songkong -perm /u+x -type f -print0 | xargs -0 chmod a+x
EXPOSE 4567
ENTRYPOINT ["/sbin/tini"]
# Config, License, Logs, Reports and Internal Database
VOLUME /songkong
# Music folder should be mounted here
VOLUME /music
WORKDIR /opt/songkong
CMD /opt/songkong/songkongremote.sh
For more information about multi-stage build: https://docs.docker.com/develop/develop-images/multistage-build/
For more information about image and layers: https://docs.docker.com/storage/storagedriver/#images-and-layers
Ive done some reading and now understand that each layer is built on top of other layer. Since /opt/java is create as part of the base layer it will always exist in the image, even if I delete it in a later layer.
What I need to do is multi-stage builds instead whereby I build my jre in one stage, and then in a second stage based on base image without the jdk I can just copy the jre I created in the first stage into the second stage
I found article explained things quite well.
https://medium.com/#greut/java11-jlink-and-docker-2fec885fb2d
But I had one issue the base alpine:3.11 image cannot run Java out of the box, I nabbed a line from adoptopenjdk/openjdk11:alpine-jre to install various additional packages but I dont know which ones are needed to just run java with provided jre so I have probably installed to much.
FROM adoptopenjdk/openjdk11:alpine AS build
RUN /opt/java/openjdk/bin/jlink --module-path=/opt/java/openjdk/jmods \
--add-modules java.desktop,java.datatransfer,java.logging,java.management,java.naming,java.net.http,java.prefs,java.scripting,java.sql,jdk.management,jdk.unsupported,jdk.scripting.nashorn \
--output /opt/songkong/jre
RUN apk --no-cache add \
curl \
tini
RUN mkdir -p /opt \
&& curl http://www.jthink.net/songkong/downloads/build1114/songkong-linux-docker.tgz?val=131| tar -C /opt -xzf - \
&& find /opt/songkong -perm /u+x -type f -print0 | xargs -0 chmod a+x
RUN rm -fr /opt/java
FROM alpine:3.11
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'
RUN apk add --no-cache --virtual .build-deps curl binutils \
&& GLIBC_VER="2.31-r0" \
&& ALPINE_GLIBC_REPO="https://github.com/sgerrand/alpine-pkg-glibc/releases/download" \
&& GCC_LIBS_URL="https://archive.archlinux.org/packages/g/gcc-libs/gcc-libs-9.1.0-2-x86_64.pkg.tar.xz" \
&& GCC_LIBS_SHA256="91dba90f3c20d32fcf7f1dbe91523653018aa0b8d2230b00f822f6722804cf08" \
&& ZLIB_URL="https://archive.archlinux.org/packages/z/zlib/zlib-1%3A1.2.11-3-x86_64.pkg.tar.xz" \
&& ZLIB_SHA256=17aede0b9f8baa789c5aa3f358fbf8c68a5f1228c5e6cba1a5dd34102ef4d4e5 \
&& curl -LfsS https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub -o /etc/apk/keys/sgerrand.rsa.pub \
&& SGERRAND_RSA_SHA256="823b54589c93b02497f1ba4dc622eaef9c813e6b0f0ebbb2f771e32adf9f4ef2" \
&& echo "${SGERRAND_RSA_SHA256} */etc/apk/keys/sgerrand.rsa.pub" | sha256sum -c - \
&& curl -LfsS ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-${GLIBC_VER}.apk > /tmp/glibc-${GLIBC_VER}.apk \
&& apk add --no-cache /tmp/glibc-${GLIBC_VER}.apk \
&& curl -LfsS ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-bin-${GLIBC_VER}.apk > /tmp/glibc-bin-${GLIBC_VER}.apk \
&& apk add --no-cache /tmp/glibc-bin-${GLIBC_VER}.apk \
&& curl -Ls ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-i18n-${GLIBC_VER}.apk > /tmp/glibc-i18n-${GLIBC_VER}.apk \
&& apk add --no-cache /tmp/glibc-i18n-${GLIBC_VER}.apk \
&& /usr/glibc-compat/bin/localedef --force --inputfile POSIX --charmap UTF-8 "$LANG" || true \
&& echo "export LANG=$LANG" > /etc/profile.d/locale.sh \
&& curl -LfsS ${GCC_LIBS_URL} -o /tmp/gcc-libs.tar.xz \
&& echo "${GCC_LIBS_SHA256} */tmp/gcc-libs.tar.xz" | sha256sum -c - \
&& mkdir /tmp/gcc \
&& tar -xf /tmp/gcc-libs.tar.xz -C /tmp/gcc \
&& mv /tmp/gcc/usr/lib/libgcc* /tmp/gcc/usr/lib/libstdc++* /usr/glibc-compat/lib \
&& strip /usr/glibc-compat/lib/libgcc_s.so.* /usr/glibc-compat/lib/libstdc++.so* \
&& curl -LfsS ${ZLIB_URL} -o /tmp/libz.tar.xz \
&& echo "${ZLIB_SHA256} */tmp/libz.tar.xz" | sha256sum -c - \
&& mkdir /tmp/libz \
&& tar -xf /tmp/libz.tar.xz -C /tmp/libz \
&& mv /tmp/libz/usr/lib/libz.so* /usr/glibc-compat/lib \
&& apk del --purge .build-deps glibc-i18n \
&& rm -rf /tmp/*.apk /tmp/gcc /tmp/gcc-libs.tar.xz /tmp/libz /tmp/libz.tar.xz /var/cache/apk/*
RUN mkdir -p /opt
COPY --from=build /opt/songkong /opt/songkong
RUN apk --no-cache add \
ca-certificates \
curl \
fontconfig \
msttcorefonts-installer \
tini \
&& update-ms-fonts \
&& fc-cache -f
EXPOSE 4567
ENTRYPOINT ["/sbin/tini"]
# Config, License, Logs, Reports and Internal Database
VOLUME /songkong
# Music folder should be mounted here
VOLUME /music
WORKDIR /opt/songkong
CMD /opt/songkong/songkongremote.sh
You can also squash the image:
docker build --squash -t your/imagename .
This will combine all layers, e.g. flatten the layers.

Non-Alpine dind docker image

Is there an existing non-Alpine dind docker image?
Bind-mounting the host's docker socket does not work for me. I need proper dind. Docker's dind images seem to be all Alpine based, which also doesn't work for me.
Not exactly an answer to your question, but might solve your needs:
I assume that you don't really need non-Alpine, but rather GLIBC-enabled image.
I wanted an Docker-in-Docker capable image for Gitlab CI, which would have OpenJDK 12.
Such image is not yet available - AdoptOpenJDK images do not have DinD, and the official docker:* images can't install normal OpenJDK.
So I combined adoptopenjdk:12 with docker:stable, and it seems to work.
docker build --label docker-with-openjdk12 .
# ------------------------------------------------------------------------------
# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh"
#
# PLEASE DO NOT EDIT IT DIRECTLY.
# ------------------------------------------------------------------------------
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
FROM docker:stable
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'
RUN apk add --no-cache --virtual .build-deps curl binutils \
&& GLIBC_VER="2.29-r0" \
&& ALPINE_GLIBC_REPO="https://github.com/sgerrand/alpine-pkg-glibc/releases/download" \
&& GCC_LIBS_URL="https://archive.archlinux.org/packages/g/gcc-libs/gcc-libs-9.1.0-2-x86_64.pkg.tar.xz" \
&& GCC_LIBS_SHA256="91dba90f3c20d32fcf7f1dbe91523653018aa0b8d2230b00f822f6722804cf08" \
&& ZLIB_URL="https://archive.archlinux.org/packages/z/zlib/zlib-1%3A1.2.11-3-x86_64.pkg.tar.xz" \
&& ZLIB_SHA256=17aede0b9f8baa789c5aa3f358fbf8c68a5f1228c5e6cba1a5dd34102ef4d4e5 \
&& curl -LfsS https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub -o /etc/apk/keys/sgerrand.rsa.pub \
&& SGERRAND_RSA_SHA256="823b54589c93b02497f1ba4dc622eaef9c813e6b0f0ebbb2f771e32adf9f4ef2" \
&& echo "${SGERRAND_RSA_SHA256} */etc/apk/keys/sgerrand.rsa.pub" | sha256sum -c - \
&& curl -LfsS ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-${GLIBC_VER}.apk > /tmp/glibc-${GLIBC_VER}.apk \
&& apk add /tmp/glibc-${GLIBC_VER}.apk \
&& curl -LfsS ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-bin-${GLIBC_VER}.apk > /tmp/glibc-bin-${GLIBC_VER}.apk \
&& apk add /tmp/glibc-bin-${GLIBC_VER}.apk \
&& curl -Ls ${ALPINE_GLIBC_REPO}/${GLIBC_VER}/glibc-i18n-${GLIBC_VER}.apk > /tmp/glibc-i18n-${GLIBC_VER}.apk \
&& apk add /tmp/glibc-i18n-${GLIBC_VER}.apk \
&& /usr/glibc-compat/bin/localedef --force --inputfile POSIX --charmap UTF-8 "$LANG" || true \
&& echo "export LANG=$LANG" > /etc/profile.d/locale.sh \
&& curl -LfsS ${GCC_LIBS_URL} -o /tmp/gcc-libs.tar.xz \
&& echo "${GCC_LIBS_SHA256} */tmp/gcc-libs.tar.xz" | sha256sum -c - \
&& mkdir /tmp/gcc \
&& tar -xf /tmp/gcc-libs.tar.xz -C /tmp/gcc \
&& mv /tmp/gcc/usr/lib/libgcc* /tmp/gcc/usr/lib/libstdc++* /usr/glibc-compat/lib \
&& strip /usr/glibc-compat/lib/libgcc_s.so.* /usr/glibc-compat/lib/libstdc++.so* \
&& curl -LfsS ${ZLIB_URL} -o /tmp/libz.tar.xz \
&& echo "${ZLIB_SHA256} */tmp/libz.tar.xz" | sha256sum -c - \
&& mkdir /tmp/libz \
&& tar -xf /tmp/libz.tar.xz -C /tmp/libz \
&& mv /tmp/libz/usr/lib/libz.so* /usr/glibc-compat/lib \
&& apk del --purge .build-deps glibc-i18n \
&& rm -rf /tmp/*.apk /tmp/gcc /tmp/gcc-libs.tar.xz /tmp/libz /tmp/libz.tar.xz /var/cache/apk/*
ENV JAVA_VERSION jdk-12.0.2+10
RUN set -eux; \
apk add --virtual .fetch-deps curl; \
ARCH="$(apk --print-arch)"; \
case "${ARCH}" in \
aarch64|arm64) \
ESUM='855f046afc5a5230ad6da45a5c811194267acd1748f16b648bfe5710703fe8c6'; \
BINARY_URL='https://github.com/AdoptOpenJDK/openjdk12-binaries/releases/download/jdk-12.0.2%2B10/OpenJDK12U-jdk_aarch64_linux_hotspot_12.0.2_10.tar.gz'; \
;; \
armhf) \
ESUM='9fec85826ffb7b2b2cf2853a6ed3e001b528ed5cf13e435cd13026398b5178d8'; \
BINARY_URL='https://github.com/AdoptOpenJDK/openjdk12-binaries/releases/download/jdk-12.0.2%2B10/OpenJDK12U-jdk_arm_linux_hotspot_12.0.2_10.tar.gz'; \
;; \
ppc64el|ppc64le) \
ESUM='4b0c9f5cdea1b26d7f079fa6478aceebf1923c947c4209d5709c0869dd71b98f'; \
BINARY_URL='https://github.com/AdoptOpenJDK/openjdk12-binaries/releases/download/jdk-12.0.2%2B10/OpenJDK12U-jdk_ppc64le_linux_hotspot_12.0.2_10.tar.gz'; \
;; \
s390x) \
ESUM='9897deeaf7a2c90374fbaca8b3eb8e63267d8fc1863b43b21c0bfc86e4783470'; \
BINARY_URL='https://github.com/AdoptOpenJDK/openjdk12-binaries/releases/download/jdk-12.0.2%2B10/OpenJDK12U-jdk_s390x_linux_hotspot_12.0.2_10.tar.gz'; \
;; \
amd64|x86_64) \
ESUM='1202f536984c28d68681d51207a84b6c76e5998579132d3fe1b8085aa6a5f21e'; \
BINARY_URL='https://github.com/AdoptOpenJDK/openjdk12-binaries/releases/download/jdk-12.0.2%2B10/OpenJDK12U-jdk_x64_linux_hotspot_12.0.2_10.tar.gz'; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
curl -LfsSo /tmp/openjdk.tar.gz ${BINARY_URL}; \
echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \
mkdir -p /opt/java/openjdk; \
cd /opt/java/openjdk; \
tar -xf /tmp/openjdk.tar.gz --strip-components=1; \
apk del --purge .fetch-deps; \
rm -rf /var/cache/apk/*; \
rm -rf /tmp/openjdk.tar.gz;
ENV JAVA_HOME=/opt/java/openjdk \
PATH="/opt/java/openjdk/bin:$PATH"
CMD ["jshell"]
You just want to run Docker to perform CI System (build, run, push container images to hub) in Jenkins. Jenkins Master will launch Jenkins Slave as container and it will perform CI operation in it.
Yes you can run docker commands in inside jenkins slave container.
We required Docker binaries files in jenkins slave container, here while creating docker image through docker file we have added docker binaries file to it.
Once image build, we have just to volume mount Docker sock /var/run/docker.sock of host machine on container /var/run/docker.sock.
Here we are executing Docker-daemon from host machine and Docker client as jenkins slave container.
Please refer below GIT and Docker Hub repo:
https://github.com/Nilesh7756/dind-jnlp-slave.git
https://hub.docker.com/r/nilesh7756/jnlp-slave/

Resources