At this point, we have already set up a PHP command-line application using Laravel’s Artisan commands and Docker Compose. The rest of this tutorial will go through the process of setting up Codeship Pro to automate your continuous integration and deployments.

Continuous Integration

With our application and test suite running locally, the next thing we want to do is set up some kind of continuous integration system. You could set up your own servers to do this, but that’s a fair amount of work, so I typically recommend a service like Codeship Pro.

Running tests locally with Jet

Before you run your tests on Codeship, I recommend setting up a local version of their CI platform, Jet. This will allow you to get things working faster; your configuration files might need some tweaking as your application evolves.

Once you’ve installed Jet, create two new files in the root directory of your project:

As you can see, the codeship-steps.yml file calls a shell script that we haven’t created yet. This script is going to make sure the app and database containers are up, the database migrations are run, and that the tests pass. It may sound like a lot, but it’s actually a pretty simple file. Put this script in ./docker/codeship-run.sh:

The first thing this script does is attempt to connect to the database. Codeship’s software automatically brings up the app and database containers, but MySQL takes a few seconds to initialize, so we have to retry the test_database() function until we get a success (or 100 tries). This is outlined in more detail in Codeship’s Docker documentation.

Once the script is able to verify a database connection, it creates the default database (called laravel). Then it runs the migrations to create the database table and the test suite via PHPUnit.

Finally, in order to test that this configuration works and the tests will pass, we can run everything with Jet:

$ jet steps

If everything is working, you should see a bunch of output as the containers are built and commands are run with a success message at the end:

Connecting our repository to Codeship

If you haven’t yet, add and commit your local code and push it to a new repository on GitHub or Bitbucket. Codeship automatically pulls code from your repository (public or private) each time you push changes, so now we just need to give Codeship the repository to watch.

Now your project is linked to Codeship, and the next time you push your code, Codeship will attempt to build it and run your tests using the same codeship-steps.yml and codeship-services.yml files that you used locally. The only problem at this point is that you were using your local .env file, and that should not be committed to your repository. Fortunately, there’s an easy way to set environmental variables without compromising security.

Encrypting your environmental variables

Since it’s best practice not to push our .env file up to the continuous integration server, we need to come up with a way to securely pass variables up to Codeship. That’s where an encrypted .env file comes in.

First, find your AES key on Codeship (it’s in the General page of your project settings), and put it in a file at the root of your local project called codeship.aes. Don’t forget to add this file to your .gitignore file, as it’s an encryption key that should not be shared.

If everything above was done correctly, you should eventually get a successful build:

!Sign up for a free Codeship Account

Automated Deployments

A continuous integration service that tells us if our tests are passing is great, but in order to get the most value out of Codeship, it’s very useful to have it automate our deployments as well. In order to do this, we’ll need a few things:

The code repository manually configured and deployed on a server

An SSH key on the server that has permission to pull from our repository

A script on the server to update the code and restart the containers

With those things in place, we will be able to build a deployer container whose job is to SSH into the server and run the update script at the end of our build process.

I should say that this is just one way to deploy code using containers, and it may not be the best way for your production environment. Another option might be to use a container registry like Docker Hub to build your images and then update the containers directly from there. I’ve found that the best practices for Docker in production are still being established, so I favor this approach because it’s simple and works for us.

Here’s that process that I use in more detail:

Manually deploying code for the first time

This step will vary quite a bit depending on your hosting provider, but basically you will need a server provisioned that has Git, Docker, and Docker Compose installed. SSH into the server and:

This file should be called deploy.sh and be put into the docker/ folder within your repository.

At this point, it’s probably a good idea to make sure this file is on the server, so push your changes to the repository and pull them from the server. Test the script out by running $ bash docker/deploy.sh from the server and making sure that your containers are still working.

Creating a deployer container

As I mentioned above, we now need a container to run this deploy script remotely from Codeship’s CI server after the build and test process has completed. Create a new folder called deployer/ in your repository with a Dockerfile, .env file, and a shell script called execute.sh:

We also want to encrypt the new deployer/.env file so that we can commit it to the repository without exposing our server’s SSH key. So just like we did with the main code repository, we’re going to use Jet to encrypt the environment file:

Subscribe via Email

Over 60,000 people from companies like Netflix, Apple, Spotify and O'Reilly are reading our articles. Subscribe to receive a weekly newsletter with articles around Continuous Integration, Docker, and software development best practices.

We promise that we won't spam you. You can unsubscribe any time.

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.