How can I use DinD inside an image that I'm building?

I am currently building a docker image on top of php8.0-fpm where I am adding composer, sentry-cli etc… and then using this image in future jobs to test and deploy my application. However, in the latest version of Laravel Vapor, it is recommended that you dockerize your application before deploying it. The issue is that the php8.0-fpm that I am using doesn’t have docker installed. I will add both my Dockerfile and gitlab-ci script below for further context.

I would appreciate any help possible! Thank you.

gitlab-ci.yml

stages:
  - build
  - test
  - deploy

cache:
    paths:
        - vendor/

variables:
  DOCKER_TLS_CERTDIR: ""
  DOCKER_DRIVER: overlay2
  DOCKER_HOST: tcp://docker:2375

build:
  image: docker:latest
  stage: build
  services:
    - docker:dind
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker pull $CI_REGISTRY_IMAGE:latest || true
    - docker build --cache-from $CI_REGISTRY_IMAGE:latest --tag $CI_REGISTRY_IMAGE:latest .
    - docker push $CI_REGISTRY_IMAGE:latest

test:
  image: $CI_REGISTRY_IMAGE:latest
  stage: test
  services:
    - docker:dind
  artifacts:
        paths:
          - vendor/
  script:
    - composer install
    - touch database/database.sqlite
    - php artisan key:generate --env=testing
    - php artisan migrate --env=testing
    - php artisan passport:install --env=testing
    - php artisan db:seed --env=testing
    - php artisan geoip:update --env=testing
    - php artisan test --env=testing

deploy_development:
  image: $CI_REGISTRY_IMAGE:latest
  stage: deploy
  services:
    - docker:dind
  artifacts:
        paths:
          - vendor/
  environment:
    name: development
    url: https://dev.xxx.io
  script:
    - VERSION=$(sentry-cli releases propose-version)
    - sentry-cli releases new -p api $VERSION
    - sentry-cli releases set-commits --auto $VERSION
    - composer install
    - php vendor/bin/vapor deploy development --commit="$CI_COMMIT_SHA"
  only:
    - development

deploy_staging:
  image: $CI_REGISTRY_IMAGE:latest
  stage: deploy
  services:
    - docker:dind
  artifacts:
        paths:
          - vendor/
  environment:
    name: staging
    url: https://stage.xxx.io
  script:
    - VERSION=$(sentry-cli releases propose-version)
    - sentry-cli releases new -p api $VERSION
    - sentry-cli releases set-commits --auto $VERSION
    - composer install
    - php vendor/bin/vapor deploy staging --commit="$CI_COMMIT_SHA"
  only:
    - master

deploy_production:
  image: $CI_REGISTRY_IMAGE:latest
  stage: deploy
  services:
    - docker:dind
  artifacts:
        paths:
          - vendor/
  when: manual
  environment:
    name: production
    url: https://xxx.io
  script:
    - VERSION=$(sentry-cli releases propose-version)
    - sentry-cli releases new -p api $VERSION
    - sentry-cli releases set-commits --auto $VERSION
    - sentry-cli releases finalize "$VERSION"
    - composer install
    - php vendor/bin/vapor deploy production --commit="$CI_COMMIT_SHA"
  only:
    - master

Dockerfile

    FROM php:8.0-fpm as builder

    # Require composer dependency manager
    COPY --from=composer /usr/bin/composer /usr/bin/composer

    # Require install-php-extensions script (https://github.com/mlocati/docker-php-extension-installer)
    COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/

    # Update apt repository
    RUN apt-get update -y

    # Install requirements
    RUN apt-get install -y libgmp-dev re2c libmhash-dev libmcrypt-dev file libzip-dev zlib1g-dev libicu-dev g++
    RUN ln -s /usr/include/x86_64-linux-gnu/gmp.h /usr/local/include/
    RUN docker-php-ext-configure gmp
    RUN docker-php-ext-configure intl

    # Install php extensions
    RUN docker-php-ext-install gmp intl zip pdo

    # Install php extensions (via 3rd party script)
    RUN install-php-extensions http

    # Install Sentry-CLI
    RUN curl -sL https://sentry.io/get-cli/ | bash

Hi @KieronWiltshire
You don’t want to use DinD inside an image. You build the image using DinD :slight_smile:
You are already building container image so it should be easy to built it on top. Looking at this blog you need to do some changes to Vapor setup and provide a .Dockerfile for Vapor to build it. Now I suppose here that it can take any image you provide and not just official laravelphp/vapor images.

You don’t need to have services: - docker:dind in every job. Only in the ones you are using it.
I would also use cache: for vendor/ instead of artifacts. Unless you need it to be downloadable from GitLab.

I usually keep the php-fpm base image in a separate repository so it doesn’t get build every time I change something in the code. And to more easily identify if it’s broken by code change or some dependency/requirement got updated and broke it :slight_smile: But let’s make it in a single repo for now.

So you have the container image you are using for test job. You need to add Dockerfile per environment like development.Dockerfile file to your repository and update the deploy_* jobs script steps.
development.Dockerfile should be fairly simple, just copy everything to the image. I don’t recommend running apps as root user. Adjust accordingly.

FROM _path_to_your_fpm_base_image_:latest

# Add user for laravel application
RUN groupadd -g 1000 www &&\
    useradd -u 1000 -ms /bin/bash -g www www

# Copy existing application directory permissions
COPY --chown=www:www . /var/www

# Set working directory
WORKDIR /var/www

# Change current user to www
USER www

Add another step (before vapor deploy) to your script: in your deploy_* jobs and install docker into your php-fpm image (it is Debian based). Looking at VaporCLI code it just calls docker command on shell.

Yeah the problem is I can’t install the composer dependencies from the Vapor image I’m sure… I would have to check but I don’t believe that will solve my problem, or it will cause issues in other steps that will be just as troublesome to solve.

Anyways, I’ll give it a shot and post an update here. Thanks for your help.

hi @KieronWiltshire

You have the wrong understanding of what is happening in these common modern setups.

You run a php container where your php app is, lavarel or other does not matter, inside a docker container, same as you do for other containers you need such as nginx etc, and you build them in a consistent way unaware of the outside docker world.

Once your docker stack (i use docker-compose) is working on your machine you can use GitLabCI with a DIND image to run that stack inside the CI server.

So you push your changes, GitlabCI pulls your config and sees you enabled DIND with your docker image with minimal tooling (no php with docker on it) → this runs something like docker-compose up that spins all those containers on DIND. Whatever runs on it is totally up to you ie when your docker stack is up you can then exec your sentry tests on a sentry specific CLI container.

For instance here i spin up a tech stack with php

This in turn spins all the containers in the docker-compose … composer runs in its own container

Once composer is done the php amq library is installed. In your composer all your set up would run the same way and make sure Laravel and all its dependencies are in place.

The service that runs PHP lives in another container with an image where composer is not installed

In your case a similar setup can will spin the DIND service on Gitlab, docker-compose up-> this will bring up nginx, php, mysql and run composer install in each separate containers. Use a waitforit in general to ensure spinning of the containers is successful.

Then you can run all the tests you need ie docker-compose exec -it php sentry something here …

Having DIND or docker inside a php container is definitely wrong, and you better off segregating all the tooling in each container too as one of the most important thing is separation of concerns also in the infrastructure world.

I hope this helps