How to auto deploy symfony app with gitlab-ci in private server?

I recently discovered gitlab-ci in order to automate a lot of things for my development. So I managed to set up a "test" stage, which allows me to run my unit tests. Perfect, everything works well. Now I'm blocked: I would like to be able to deploy automatically on my dedicated server when I validate the MR. In the gitlab documentation, the example is above AWS, or makefile but without example!

Ideas of how to do it on a private dedicated server?

For information, it is a PHP project Symfony 3. In any case, I would like a deployment quite common: pull the master branch, execute a composer install and restore the rights of the various folders. My application is under Docker, so I have to not only be able to access my remote server, but also access my container that manages my application.

2 answers

  • answered 2017-06-17 18:08 Rufinus

    My deployment step looks like this - maybe it gives you some ideas:

    build:app:
      image: docker:latest
      services:
        - docker:dind
      stage: build
      variables:
        SYMFONY_ENV: "prod"
      before_script:
        - docker login -u "gitlab-ci-token" -p "$CI_JOB_TOKEN" $CI_REGISTRY
        - export IMAGE_TAG=$(echo -en $CI_COMMIT_REF_NAME | tr -c '[:alnum:]_.-' '-')
      script:
        - docker run --rm --name composerinstall -v "$PWD":/var/www/app -w /var/www/app composer:1.4 install --no-dev --ignore-platform-reqs --no-suggest --no-progress --no-scripts --prefer-dist
        - cp app/config/parameters.yml.dist app/config/parameters.yml
        - docker build --pull -t "$CI_REGISTRY_IMAGE:$IMAGE_TAG" .
        - docker push "$CI_REGISTRY_IMAGE:$IMAGE_TAG"
      only: [develop]
      dependencies:
        - test:php7.0
        - test:php7.1
      tags:
        - autoscale
    
    
    deploy:staging:
      variables:
        DOCKER_TLS_VERIFY: "1"
        DOCKER_HOST: "tcp://123.123.123.123:2376"
        DOCKER_CERT_PATH: "/home/gitlab-runner/.docker/machine/machines/dockerhost1"
        DOCKER_MACHINE_NAME: "dockerhost1"
        SYMFONY_ENV: prod
        MYSQL_PROD_PASSWORD: "$MYSQL_PROD_PASSWORD"
        SECRET_TOKEN: "$SECRET_TOKEN"
        COMPOSE_PROJECT_NAME: "myproject_staging"
        COMPOSER_CACHE_DIR: "$(pwd -P)/.composer-cache"
        ASSET_VERSION: ${CI_COMMIT_SHA:0:8}
      before_script:
        - export BRANCH=$(echo -en $CI_COMMIT_REF_NAME | tr -c '[:alnum:]_.-' '-')
        - docker-compose -f docker-compose.prod.yml pull --parallel
      script:
        - docker-compose -f docker-compose.prod.yml up -d --force-recreate app
        - docker-compose -f docker-compose.prod.yml restart php
        - docker-compose -f docker-compose.prod.yml exec -T --user www-data php composer install --no-dev --no-suggest --no-progress --prefer-dist --optimize-autoloader
        - docker-compose -f docker-compose.prod.yml exec -T --user www-data php bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration
      stage: deploy
      environment:
        name: staging
        url: http://staging.myproject.com
      only: [develop]
      dependencies:
        - build:app
      tags:
        - deploy
    

    The Dockerfile for the build step is now only:

    FROM busybox:latest
    
    COPY . /app
    RUN mkdir -p /app/vendor && \
        mkdir -p /app/mediastore  && \
        mkdir -p /app/web/imagecache  && \
        mkdir -p /.composer && \
        chown 33:33 -R /.composer && \
        chown 33:33 -R /app/var && \
        chown 33:33 -R /app/mediastore && \
        chown 33:33 -R /app/vendor && \
        chown 33:33 -R /app/web/imagecache && \
        chown 33:33 /app/app/config/parameters.yml
    

    To benefit of the env variables i have this in my parameters.yml:

    parameters:
      database_host:     '%env(DATABASE_HOST)%'
      database_port:     '%env(DATABASE_PORT)%'
      database_name:     '%env(DATABASE_NAME)%'
      database_user:     '%env(DATABASE_USERNAME)%'
      database_password: '%env(DATABASE_PASSWORD)%'    
      secret:            '%env(SECRET_TOKEN)%'    
      asset_version:     '%env(ASSET_VERSION)%'
    
      # Default fallback if env not set
      env(DATABASE_HOST):     127.0.0.1
      env(DATABASE_PORT):     ~
      env(DATABASE_NAME):     symfony
      env(DATABASE_USERNAME): root
      env(DATABASE_PASSWORD): ~    
      env(ASSET_VERSION): ~
      env(SECRET_TOKEN): ~
    

    And the Docker-Compose:

    version: '2'
    
    volumes:
        database-volume:
    
    services:
        app:
            image: "dockerhub.mydomain.com/mygroup/myproject:${BRANCH}"
            environment:
                BRANCH: develop
            tty: true
            restart: always
    
        mysql:
            image: mysql:5.7
            volumes:
                - database-volume:/var/lib/mysql
            environment:
                MYSQL_PASSWORD: "${MYSQL_PROD_PASSWORD}"
                MYSQL_USER: myuser
                MYSQL_DATABASE: mydatabase
                MYSQL_ALLOW_EMPTY_PASSWORD: 1
            restart: always
    
        php:
            image: dockerhub.mydomain.com/docker/php/fpm:7.0
            working_dir: /var/www/app
            environment:
                DATABASE_HOST: "mysql"
                DATABASE_PORT: "3306"
                DATABASE_NAME: "mydatabase"
                DATABASE_USERNAME: "myuser"
                DATABASE_PASSWORD: "${MYSQL_PROD_PASSWORD}"
                SECRET_TOKEN: "${SECRET_TOKEN}"
                SYMFONY_ENV: prod
            volumes_from:
                - app
            restart: always
    
        apache:
            image: dockerhub.mydomain.com/mygroup/myproject:apache
            volumes_from:
                - app
            environment:
                VIRTUAL_HOST: "staging.myproject.com,staging.myproject.eu"
            restart: always
    
    networks:
      default:
        external:
          name: nginx-proxy
    

    dockerhub.mydomain.com is the given url for the docker registry built into gitlab.

  • answered 2017-06-17 18:08 Coco Jr

    Thanks!

    Your sample is really nice! I don't use docker hub to host my dockerfile :/ But i found the solution for my use case! In first, i add my private SSH key in a variable in my gitlab repository (i found this here: https://docs.gitlab.com/ee/ci/ssh_keys/README.html). After that, i can connect to my host and execute remote command! So my gitlab-ci.yml look like:

    deploy:app: stage: deploy environment: name: staging url: http://example.com only: - master script: - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' - eval $(ssh-agent -s) - ssh-add <(echo "$SSH_PRIVATE_KEY") - mkdir -p ~/.ssh - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' - ssh -t user@example.com 'cd ~/docker && git pull origin master && docker exec docker_engine_1 bash -c " ./bin/console doctrine:schema:update -f && composer install && chmod -R 777 var/cache/prod/ " '

    But your example give me some idea: use doctrine migration for example ...