3

I need to reduce the Docker image size of my Laravel 5.8 project.

I have this right now

FROM composer:latest
COPY . /src
ADD .env.example /src/.env
WORKDIR /src
RUN composer install
RUN php artisan key:generate
RUN chmod -R 777 storage/
CMD php artisan serve --host=0.0.0.0

doccker-compose.yml

version: '3'
services:
  johnywalker5g:
    container_name: johnywalker5g
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - 8000:8000

To start I ran docker-compose up. My site running.

Then, I ran docker images I see

docker images  

I see

REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE           
johnywalker5g                latest              c404551006c7        About an hour ago   193MB          
composer                     latest              520372566db9        2 weeks ago         157MB          

The size is kind of too big 193MB + 157MB


docker history c404551006c7

520372566db9        2 weeks ago         /bin/sh -c #(nop)  CMD ["composer"]             0B                                                                                      
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["/bin/sh" "/d…   0B                                                                                      
<missing>           2 weeks ago         /bin/sh -c #(nop) WORKDIR /app                  0B                                                                                      
<missing>           2 weeks ago         /bin/sh -c #(nop) COPY file:098af1c9d6ed00c2…   1.13kB              
<missing>           2 weeks ago         /bin/sh -c curl --silent --fail --location -…   1.91MB              
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV COMPOSER_VERSION=1.8.4   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV COMPOSER_HOME=/tmp       0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV COMPOSER_ALLOW_SUPERU…   0B                  
<missing>           2 weeks ago         /bin/sh -c apk add --no-cache --virtual .bui…   1.83MB              
<missing>           2 weeks ago         /bin/sh -c echo "memory_limit=-1" > "$PHP_IN…   34B                 
<missing>           2 weeks ago         /bin/sh -c apk --no-cache add --virtual .com…   75.5MB              
<missing>           2 weeks ago         /bin/sh -c #(nop)  CMD ["php" "-a"]             0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-php-e…   0B                  
<missing>           2 weeks ago         /bin/sh -c docker-php-ext-enable sodium         167kB               
<missing>           2 weeks ago         /bin/sh -c #(nop) COPY multi:03970f7b3773444…   6.45kB              
<missing>           2 weeks ago         /bin/sh -c set -xe  && apk add --no-cache --…   58MB                
<missing>           2 weeks ago         /bin/sh -c #(nop) COPY file:ce57c04b70896f77…   587B                
<missing>           2 weeks ago         /bin/sh -c set -xe;   apk add --no-cache --v…   12MB                
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PHP_SHA256=6bb03e79a1…   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PHP_URL=https://secur…   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PHP_VERSION=7.3.3        0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV GPG_KEYS=CBAF69F173A0…   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PHP_LDFLAGS=-Wl,-O1 -…   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PHP_CPPFLAGS=-fstack-…   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PHP_CFLAGS=-fstack-pr…   0B                  
<missing>           2 weeks ago         /bin/sh -c set -eux;  mkdir -p "$PHP_INI_DIR…   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PHP_INI_DIR=/usr/loca…   0B                  
<missing>           2 weeks ago         /bin/sh -c set -x  && addgroup -g 82 -S www-…   4.85kB              
<missing>           2 weeks ago         /bin/sh -c apk add --no-cache   ca-certifica…   2.71MB              
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PHPIZE_DEPS=autoconf …   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop) ADD file:38bc6b51693b13d84…   4.41MB              

How would one go about and reduce the size of this further?

halfer
  • 19,824
  • 17
  • 99
  • 186
code-8
  • 54,650
  • 106
  • 352
  • 604
  • The size of your final image `johnywalker5g` is just 193MB and not 193MB+157MB. But anyways, I don't think you can shrink it much more as your `composer` base image is already using `php:7-alpine3.8` as base, which is one of the smaller PHP images. Also the size is what you see on your disk - if you push the image to Docker Hub, only the new layers compared to the `composer` image are pushed (~36MB in size). – Namoshek Mar 27 '19 at 20:57
  • can you post the result of `docker history c404551006c7` ? Also what is the purpose of `/storage`? Shouldn't it be a volume? – Thomasleveil Mar 27 '19 at 21:32
  • @Namoshek Are u saying that what I have now is not that bad ? – code-8 Mar 27 '19 at 21:33
  • @Thomasleveil I’ll update my post to include that – code-8 Mar 27 '19 at 21:41
  • from the `docker history` output, it seems `c404551006c7` is not the actual image produced from the Dockerfile you posted. Double check that – Thomasleveil Mar 27 '19 at 22:22

1 Answers1

4

Your docker image's layers are put on top of the compose docker image's layers which sum up to 157MB. Your image being 193MB, the only thing you can reduce is from the 36MB (193 - 157) you are adding on top of the compose image. Let see what can be squeezed.

COPY . /src

COPY . /src will put all the content of your working directory to the container /src directory. Which might be more than you want. One way to limit what will be copied over to the container is to make use of the .dockerignore file (here is a great article on the subject).

Such a file usually have content similar to:

.dockerignore

.git
.gitignore
README.md
Changelog.md
Dockerfile
docker-compose.yml
docs

The .git directory can be quite large on some projects. If you also have a directory containing database dumps or other huge files not needed in your docker image, make sure to add them to the .dockerignore file.


RUN composer install

RUN composer install will also install dev dependencies that should not be needed in your image. Change it to

RUN composer install --no-dev

Going further that path this SO answer sugests to use

RUN composer install --no-ansi --no-dev --no-interaction --no-progress --no-scripts --optimize-autoloader.


Dive into your image

Use dive to explore each of your image layer and figure out if unnecessary files where added of left behind at some point.


Leverage Docker multistage build

If composer is only useful for downloading and installing your PHP libraries, then once those libraries downloaded and installed, you might want to get rid of composer. This cannot be done from a classic Dockerfile because every single RUN/COPY or ADD directive will produce a new layer on top of the preceding one.

So let say you have one layer with a 100MB file, if you delete that file later on in the same Dockerfile, the deletion will occur in a new layer. The previous layer with that 100MB is still there in your layer sandwich.

One way docker brought us to fight that is the multistage build Dockerfile.

Your Dockerfile could then be structured as follow (not tested):

FROM composer:latest as build_stage
COPY . /src
ADD .env.example /src/.env
WORKDIR /src
RUN composer install
RUN php artisan key:generate

FROM php:7-fpm-alpine
COPY --from=build_stage /src /var/www/html

RUN mkdir /var/www/html/storage/ \
    && chmod -R 777 /var/www/html/storage/

warning: this is just the main structure of the Dockerfile, you might need to tweak it more to add missing php extensions. Refer to the official php docker image guide.

Thomasleveil
  • 95,867
  • 15
  • 119
  • 113
  • Also, do you suggest I should start looking into `alpine` or something else that might be smaller than `157` MB ? – code-8 Mar 27 '19 at 22:17
  • 1
    I don't, the [official composer image](https://github.com/composer/docker/blob/ebbb9efd87b78cf9b837f97105bcb6c5f0ee44ef/1.8/Dockerfile) is probably optimized. Agreed it might have some PHP extensions not needed for your image, but the time you would have to invest to come up with your own image (and maintaining it) is most likely not worth the hypothetical 40MB you could save. – Thomasleveil Mar 27 '19 at 22:19
  • 1
    I've updated my answer with **multistage build** which might be the way to go. `php:7-fpm-alpine` is just `79MB` while `composer:latest` is `154MB` – Thomasleveil Mar 27 '19 at 22:44
  • Ok, thank-you very much for that, the multi staging part is exactly what I am looking for. I’m sure a lot of people that looking to do the same thing as me, will learn a lot from your answer. Now, back to my goal, I want to challenge myself to make my image as small as possible, and only copy over the files that are required to run my project. Let’s assume your code is **working**. How many MB are we looking at here ? Is it meaning that my docker image now is only `40 MB` ? Am I understand you correctly ? – code-8 Mar 27 '19 at 22:51
  • 1
    it will be `79MB` (from the `php:7-fpm-alpine` layers) + the size of your layers – Thomasleveil Mar 27 '19 at 22:54
  • If I am going to copy the vendor/ over to my src/ should I just remove this line `RUN composer install` then - since all it does is re-generating the vendor/ folder ? Do you agree with what I am thinking about? If you do, that make `RUN composer install` not necessary as well as **composer** itself. – code-8 Mar 27 '19 at 22:56
  • I agree. However I like to keep the build process in my Dockerfile since I can make Jenkins (or other CI tool) checkout one of my git commit/branch/tag and build the image. This way, I can make sure that no uncommitted file makes its way into the final image. In other words: let machine do the build, you will avoid human mistakes. Read [https://reproducible-builds.org/](https://reproducible-builds.org/) – Thomasleveil Mar 27 '19 at 22:59
  • By removing this line `RUN composer install` will I reduce any size from (70 MB + the size of my layers) ? If no, I will leave it. – code-8 Mar 27 '19 at 23:32
  • 1
    I suggest you play with the _dive_ tool to explore each layer and get a better understanding of how each directive from your Dockerfile affects layers. Don't forget you need the PHP binary, PHP extensions, your php files and assets and a web server in the final image. Not everything can be removed easily. – Thomasleveil Mar 27 '19 at 23:36
  • `php artian serve` can act as a web server, so we don't need `Nginx` or `Apache` per say. – code-8 Mar 27 '19 at 23:37
  • Please have a look, I reduce all to **115 MB** total now. https://www.evernote.com/l/ATxp7Z2D_zdGW5TanSOZ-44EGnsH_mKZX3E – code-8 Mar 27 '19 at 23:48