Pushing Dockerfiles

Jan. 2020

Pushing Dockerfiles

I have been recently studying Kubernetes to possibly move away from just Dockerizing directly. To gain some experience and knowledge, I have been following an excellent article on itnext. However, I ran into some issues, or rather the ability to properly reproduce some of the assumed tasks in the article. Nonetheless, I came through, and wanted to disseminate some of the information I have gathered.

If, like me, you are interested in pushing your Dockerfiles to a public/private repo such as docker then the following Makefile may be of interest. For instance, you have three images you have made, in my case, mysql, php-fpm and nginx for a symfony project,

The Dockerfile for mysql

ARG VERSION

FROM mysql:${VERSION} as prod

# Copy sql dump to initialize tables on stratup
COPY ./docker/mysql/init /docker-entrypoint-initdb.d

The Dockerfile for nginx

ARG VERSION

# Dev image
FROM nginx:${VERSION}-alpine as dev

# Copy nginx config
COPY ./docker/nginx/default.conf /etc/nginx/conf.d/default.conf

# Prod image
FROM dev as prod

# Copy assets
COPY ./assets /app/public

The Dockerfile for php-fpm

ARG VERSION


# Build image
FROM php:${VERSION}-fpm-alpine AS build

## Install system dependencies
RUN apk update && \
    apk add --no-cache zlib-dev libzip-dev

## Install php extensions
RUN docker-php-ext-install pdo_mysql zip

## Copy php default configuration
COPY ./docker/php-fpm/default.ini /usr/local/etc/php/conf.d/default.ini

## Install composer
RUN wget https://getcomposer.org/installer && \
    php installer --install-dir=/usr/local/bin/ --filename=composer && \
    rm installer && \
    composer global require hirak/prestissimo


# Dev image
FROM build AS dev

## Install system dependencies
RUN apk add --no-cache git autoconf gcc g++ make

## Install php extensions
RUN pecl install xdebug && \
    docker-php-ext-enable xdebug


# Test image
FROM dev AS test

ENV APP_ENV=dev
WORKDIR /app

## Copy project files to workdir
COPY . .

## Install application dependencies
RUN composer install --no-interaction --optimize-autoloader

## Change files owner to php-fpm default user
RUN chown -R www-data:www-data .

CMD ["php-fpm"]


# Prod image
FROM build AS prod

ENV APP_ENV=prod
WORKDIR /app

## Copy project files to workdir
COPY . .

## Remove dev dependencies
RUN composer install --no-dev --no-interaction --optimize-autoloader

## Change files owner to php-fpm default user
RUN chown -R www-data:www-data .

## Cleanup
#RUN rm /usr/local/bin/composer

CMD ["php-fpm"]

You could simply type export DOCKER_USER=username DOCKER_PASS=password; make build TYPE=mysql to build, tag and finally push to the repo. In general, I build the image with prod, though, dev, test or another build type could be used as well. Thereafter, I tag with the current commit short form and as well with latest. This way, under tags, a new tag will be created along with an update to the latest. I then log in and push the newly built and tagged images. You can see what it is doing by using docker images. Although, I believe the code is self-explanatory. However, if you have an issues, you may leave a message.

TAG                := $$(git rev-parse --short HEAD)

NAME_MYSQL      := symfony-dummy-project-mysql
IMG_MYSQL       := ${NAME_MYSQL}:${TAG}
LATEST_MYSQL    := ${NAME_MYSQL}:latest
VERSION_MYSQL    := 5.7

NAME_NGINX       := symfony-dummy-project-nginx
IMG_NGINX       := ${NAME_NGINX}:${TAG}
LATEST_NGINX    := ${NAME_NGINX}:latest
VERSION_NGINX    := 1.15

NAME_PHP_FPM       := symfony-dummy-project-php-fpm
IMG_PHP_FPM       := ${NAME_PHP_FPM}:${TAG}
LATEST_PHP_FPM    := ${NAME_PHP_FPM}:latest
VERSION_PHP_FPM    := 7.3

ifeq ($(TYPE),mysql)
build:
    @docker build --build-arg VERSION=${VERSION_MYSQL} --target prod -t ${NAME_MYSQL}:prod -f docker/mysql/Dockerfile  .
    @docker tag ${NAME_MYSQL}:prod damasu/${IMG_MYSQL}
    @docker tag ${NAME_MYSQL}:prod damasu/${LATEST_MYSQL}
    @docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
    @docker push damasu/${IMG_MYSQL}
    @docker push damasu/${LATEST_MYSQL}
else ifeq ($(TYPE),nginx)
build:
    @docker build --build-arg VERSION=${VERSION_NGINX} --target prod -t ${NAME_NGINX}:prod -f docker/nginx/Dockerfile  .
    @docker tag ${NAME_NGINX}:prod damasu/${IMG_NGINX}
    @docker tag ${NAME_NGINX}:prod damasu/${LATEST_NGINX}
    @docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
    @docker push damasu/${IMG_NGINX}
    @docker push damasu/${LATEST_NGINX}
else ifeq ($(TYPE),php)
build:
    @docker build --build-arg VERSION=${VERSION_PHP_FPM} --target prod -t ${NAME_PHP_FPM}:prod -f docker/php-fpm/Dockerfile  .
    @docker tag ${NAME_PHP_FPM}:prod damasu/${IMG_PHP_FPM}
    @docker tag ${NAME_PHP_FPM}:prod damasu/${LATEST_PHP_FPM}
    @docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
    @docker push damasu/${IMG_PHP_FPM}
    @docker push damasu/${LATEST_PHP_FPM}
endif

PS.

I use this rather than using automated build. If one wanted to use automated builds you would require to make a hooks/build file in the same directory as your Dockerfile(s).

The build file could contain something like

#!/bin/sh
docker build --build-arg VERSION=$VERSION -t $IMAGE_NAME .

where you pass a BUILD ENVIRONMENT VARIABLE - $VERSION possibly a target, if you're using a multistage Dockerfile, etc. However, the challenge with this method is correctly using your context especially if you require COPY in your Dockerfile(s).

Back