Capistrano is a framework for building automated deployment scripts. Although Capistrano is written in Ruby, it can easily be used to deploy projects of any language or framework, whether Rails, Java, or PHP. For more details https://github.com/capistrano/capistrano/.
While working on a particular project, we deployed the development environment through docker/ansible, while we used capistrano to deploy the production environment. Deploying to staging and production environments was done manually.
To make it all automatically deploy when the branch is merged to staging or production, we decided to dockerize Capistrano and deploy when the pipeline is triggered.
- Dockerizing
Here is the basic docker sample of a ruby application we used.
FROM ruby:<version_tag>
RUN apk add -U openssh libc6-compat curl ca-certificates build-base git postgresql-dev postgresql-client shared-mime-info yarn
WORKDIR /path/to/workdir
#.ruby-version is required in GemFile
COPY Gemfile Gemfile.lock .ruby-version ./
RUN gem install bundler
RUN bundle lock --add-platform ruby && bundle install
COPY . .
CMD sh -c "/bin/sh deploy.sh"
The script deploy.sh actually does the deployment to the server. Let us look into it in a bit of detail.
- Deploy script
#!/bin/bash
which ssh-agent || ( apt update -y && apt install openssh-client -y )
eval ssh-agent
chmod 400 key.pem
ssh-add key.pem
ssh-add -l
echo $CI_ENV
cap $CI_ENV deploy
Here, firstly it checks if the ssh-agent command is available. If not, it updates the package list and installs the openssh-client package using apt to make sure the SSH client tools are available in the container.
Then it starts the SSH agent, which will handle private key authentication for your Git operations. We then need to change the permissions of the keyfile(key.pem).
We then add the key to the SSH agent, allowing it to be used for authenticating with the remote server, and then verify that the key was added successfully.
The echo part is optional but by doing so, we can be sure that the correct environment is deployed to the server.
Finally, the Capistrano deploy command is deployed to the server in its respective environment. e.g: cap production deploy, the $CI_ENV is passed when the deployment starts.
Now, let us look into implementing it in ci/cd.
- .gitlab-ci.yml
stages:
- deploy
deploy to staging:
stage: deploy
only:
- staging
before_script:
- cp $PEM_KEY key.pem
- docker build -t capistrano-deploy -f Dockerfile-Capistrano .
script:
- docker run --rm -e CI_ENV=staging capistrano-deploy
deploy to production:
stage: deploy
only:
- main
before_script:
- cp $PEM_KEY key.pem
- docker build -t capistrano-deploy -f Dockerfile-Capistrano .
script:
- docker run --rm -e CI_ENV=production capistrano-deploy
Here, firstly our server keyfile is copied and then docker is built. After finishing building the image, the docker run command will run the container with the appropriate env variables for staging and production according to the branches, and then the container will be removed.
The main reason behind opting for the shell executor over the Docker executor in GitLab CI/CD was due to the existence of several projects reliant on a centralized GitLab runner. Switching the executor would have had an effect on the pipelines of other projects since some are configured to use the shell executor. Additionally, running both shell and Docker executors on a single runner is not feasible.
Final Words
Gurzu is a full-cycle software development company. Since 2014, we have built for many startups and enterprises from all around the world using Agile methodology. Our team of experienced developers, designers, and test automation engineers can help to develop your next product.
Have a tech idea you want to turn into reality? Book a free consulting callor simply, drop us a message!