Deploying a PHP project via GitLabs pipeline

· 790 ord · Læsetid 4 minutter

I have a Symfony project on GitLab, I wanted to deploy to a server over ssh via deployer - and I found some minor issues with, the guides I found online - so, here is my take on how to go about it.

Up ‘n running - pipeline in process.

Up ‘n running - pipeline in process.

All deployment steps will be executed on the target server, so the server needs a deployer user, with sufficient rights to do whatever steps you need it to do. The steps I need are pretty basic, so I don’t have spedial permissions set up on the server.

Setting up the target server πŸ”—

On the server, add the deployer user.

sudo adduser --disabled-password --create-home deployer

Fill in what ever information you want - or just hit Enter.

Then generate ssh-keys for that user and add the public key to the users authorized_keys file - The later is needed for the ssh connection from GitLab.

sudo su - deployer
ssh-keygen -t ed25519 -C "deployer@example.com"
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys

The php project πŸ”—

deployer.php πŸ”—

Here you need to add deployer as a dev dependency.

php composer require --dev deployer/deployer

The content of the file will wary a lot - based on your projects needs - but here are a basic example - this one is build on the Symfony recipe.

<?php
namespace Deployer;

require 'recipe/symfony.php';

set('repository', 'git@gitlab.com:xyz/project.example.com.git');
set('application', 'project.example.com');
set('git_tty', true);
set('keep_releases', 3);
set('writable_mode', 'acl');
set('http_user', 'www-data');
set('default_stage', 'prod');
set('allow_anonymous_stats', false);
set('ssh_multiplexing', true);

set('bin/php', function() {
    return which('php8.2');
});

set('env', [
    'APP_ENV' => 'prod',
]);

host('project.example.com')
    ->setHostname('project.example.com')
    ->setRemoteUser('deployer')
    ->set('identity_file', '/root/.ssh/id_ed25519')
    ->set('labels', ['stage' => 'prod'])
    ->set('branch', 'main')
    ->set('deploy_path', '/var/www/project.example.com');

task('asset-map-compile', function () {
    writeln('Compiling asset map');
    run('cd {{release_or_current_path}} && {{bin/php}} bin/console asset-map:compile');
});

after('deploy:publish', 'database:migrate');
after('database:migrate', 'asset-map-compile');
after('deploy:failed', 'deploy:unlock');

.gitlab-ci.yml πŸ”—

You also need a .gitlab-ci.yml file. This file will contain the different steps you want your pipeline to execute.

Mine is set up to only trigger the deployment step when pushing to the main branch - and only if all tests have passed.

variables:
  COMPOSER_ALLOW_SUPERUSER: 1
  COMPOSER_DEFAULT_OPTIONS: '--optimize-autoloader --classmap-authoritative --no-progress --no-suggest --prefer-dist'
  DOCKER_IMAGE_PHP: 'registry.gitlab.com/xyz/project.example.com:latest'

stages:
  - build
  - test
  - deploy

build:
  stage: build
  image: ${DOCKER_IMAGE_PHP}
  script:
    - composer install ${COMPOSER_DEFAULT_OPTIONS}
  variables:
    APP_ENV: test
  artifacts:
    paths:
      - vendor/
      - bin/

phpunit:
  stage: test
  image: ${DOCKER_IMAGE_PHP}
  script:
    - php vendor/bin/phpunit
  variables:
    APP_ENV: test
  dependencies:
    - build

deploy_prod:
  stage: deploy
  image: ${DOCKER_IMAGE_PHP}
  only:
    - main
  before_script:
    - mkdir ~/.ssh
    - chmod 700 ~/.ssh
    - cp $DEPLOYER_SSH_KEY ~/.ssh/id_ed25519
    - ssh-keyscan -t rsa,ed25519 "${DEPLOYER_SSH_HOST}" >> ~/.ssh/known_hosts
    - chmod 600 ~/.ssh/*
    - eval $(ssh-agent -s)
    - ssh-add ~/.ssh/id_ed25519
  script:
    - php vendor/bin/dep deploy --revision="${CI_COMMIT_SHA}"
  dependencies:
    - build
  when: on_success

The DEPLOYER_SSH_KEY and DEPLOYER_SSH_HOST variables are set as secrets in your pipeline.

Please note that the DEPLOYER_SSH_KEY is your private key for the deployer user - and must be set up as a File variable on your project - and when you paste the private key into the input file, you must be sure to end the file with a newline (don’t ask)!

The variables can be found under Settings -> CI/CD -> Variables on the project.

Setting up CI/CD variables.

Setting up CI/CD variables.

DOCKER_IMAGE_PHP πŸ”—

Yes, you need to build your own docker image to run your build/test/deployment on - and add that one to GitLabs repository.

Note! It is important that the image is named the same as your project - otherwise it will be rejected by GitLab.

Building the image is again up to your needs, but the Dockerfile could look like this.

FROM php:8.2-fpm

ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

RUN chmod +x /usr/local/bin/install-php-extensions \
    && install-php-extensions amqp gd zip pdo_mysql igbinary intl

RUN apt-get -y update \
    && apt-get -y --no-install-recommends install git libzip-dev \
    zip unzip wget curl rsync openssl openssh-client libicu-dev

COPY --from=composer /usr/bin/composer /usr/bin/composer

You need to log in to registry.gitlab.com in order to upload the image. You use your email and a password/access token to login.

docker login registry.gitlab.com

The Access Token must be set up with the following permissions read_api, read_registry, write_registry - and it must have an expirey date set. You can setup Access Tokens under Settings -> Access Tokens

Setting up Access Tokens

Setting up Access Tokens

The build and push the image.

docker build -t registry.gitlab.com/xyz/project.example.com .
docker push registry.gitlab.com/xyz/project.example.com

Done and ready - I hope πŸ”—

All the pieces should be in place now - so the next push to the main branch, should tigger the build -> test -> deploy pipeline.

If any of your tests fail, the pipeline should fail and the deploy step should be skipped. If all tests pass, the the code should be deployed to the server.

Pushes to any other branch will only trigger the build -> test part of the pipeline.

Credits where credits due πŸ”—

I based my guide on these articles

Some resources on the other tools can be found here