Sei sulla pagina 1di 38

KELLY ANDREWS

CODESHIP DEVELOPER ADVOCATE

Using Docker and


Codeship for
Ruby Development

CO D E S H I P.CO M - B LO G .CO D E S H I P.CO M - R E S O U RC E S .CO D E S H I P.CO M


Share this

Codeship Guid
e

About the Author.


Kelly J. Andrews is the Developer Advocate at Codeship.
When he's not busy defending JavaScript, you can find
him somewhere singing karaoke. If you see him in the
wild, ask him to do a magic trick.

Codeship is a fully customizable hosted Continuous


Integration and Delivery platform that helps you
build, test, and deploy web applications fast and with
confidence.

Learn more about Codeship here.

-2-
Share this

Codeship Guid
e

Using Docker and


Codeship for Ruby
Development
Docker is an amazing tool for developers. It allows us
to build and replicate images on any host, removing
the inconsistencies of dev environments and reducing
onboarding timelines considerably.

To provide an example of how you might move to


containerized development, I built a simple todo API
with Ruby on Rails and PostgreSQL using Docker
Compose for development, testing, and eventually in my
CI/CD pipeline.
ABOUT THIS EBOOK

The first part of this eBook will cover developing and testing
a Ruby app with Docker compose. The second part will
focus on how to further create a CI/CD pipeline for your app
using Codeship.

-3-
Share this

Codeship Guid
e

Part 1: Using Docker


Compose for Ruby
Development
This tutorial requires you to have a few items before you
can get started.

 Install Docker Community Edition


 Install Docker Compose
 Download Todo app example Non-Docker branch

The todo app here is essentially a stand-in, and you


could replace it with your own application. Some of the
setup here is specific for this application, and the needs
of your application may not be covered, but it should be a
good starting point for you to get the concepts needed to
Dockerize your own applications.

Creating the Dockerfile

At the foundation of any Dockerized application, you


will find a Dockerfile. The Dockerfile contains all of the
instructions used to build out the application image.
You can set this up by installing Ruby and all of its
dependencies. However, the Docker ecosystem has an
image repository with a Ruby image already created and
ready to use.

-4-
Share this

Codeship Guid
e

In the root directory of the application, create a new


Dockerfile .
CODE

1 /> touch Dockerfile

Open the newly created Dockerfile in your favorite


editor. The first instruction, FROM , will tell Docker to use
the prebuilt Ruby image. There are several choices, but
this project uses the ruby:2.4.0-alpine image. For more
details about why I'm using alpine here over the other
options, you can read this post.
CODE

1 FROM ruby:2.4.0-alpine

If you run docker build . , you will see something


similar to the following:

1 Sending build context to Docker daemon 284.7 kB


2 Step 1/1 : FROM ruby:2.4.0-alpine
3 2.4.0-alpine: Pulling from library/ruby
4 709515475419: Pull complete
5 7fb3cefb04d0: Pull complete
6 c05571fbf599: Pull complete
CODE

7 02f8fc71784d: Pull complete


8 f419b347345c: Pull complete
9 Digest: sha256:49cd3c5e577ff2898fa7ff82271f4ccf1054d12adc7f7a6595da87b35f9f3972
10 Status: Downloaded newer image for ruby:2.4.0-alpine
11 ---> 18dbdfdbe6bd
12 Successfully built 18dbdfdbe6bd

-5-
Share this

Codeship Guid
e

With only one instruction in the Dockerfile, this doesn't


do too much, but it does show you the build process
without too much happening. At this point, you now have
an image created, and running docker images will show
you the images you have available:

1 REPOSITORY TAG IMAGE ID CREATED SIZE


CODE

2 ruby 2.4.0-alpine 18dbdfdbe6bd 5 weeks ago 60.8 MB

The Dockerfile needs more instructions to build out


the application. Currently it's only creating an image
with Ruby installed, but we still need our application
code to run inside the container. Let's add some more
instructions to do this and build this image again.

This particular Docker file uses RUN , COPY , and


WORKDIR . You can read more about those on Docker's
reference page to get a deeper understanding.

Let's add the instructions to the Dockerfile now:

1 FROM ruby:2.4.0-alpine
2
3 RUN apk update && apk add nodejs build-base libxml2-dev libxslt-dev postgresql
postgresql-dev
4 RUN mkdir /app
5 WORKDIR /app
CODE

6
7 COPY Gemfile ./Gemfile
8 COPY Gemfile.lock ./Gemfile.lock
9
10 RUN bundle install -j 20
11 COPY . .

-6-
Share this

Codeship Guid
e

Here is what is happening:

 Update apk packages and then install a few additional


requirements for Rails
 Make the directory /app
 Set the working directory to /app
 Copy Gemfile and Gemfile.lock
 Run bundle install with 20 concurrent workers
 Copy all the files from the project's root to /app

You can now run docker build . again and see the
results:

1 Sending build context to Docker daemon 284.7 kB


2 Step 1/8 : FROM ruby:2.4.0-alpine
3 ---> 18dbdfdbe6bd
4 Step 2/8 : RUN apk update && apk add nodejs build-base libxml2-dev libxslt-dev
postgresql postgresql-dev
5 ---> Running in 4b3fb288b182
6 fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/main/x86_64/APKINDEX.tar.gz
7 fetch http://dl-cdn.alpinelinux.org/alpine/v3.4/community/x86_64/APKINDEX.tar.gz
8 v3.4.6-109-g6ab23d6 [http://dl-cdn.alpinelinux.org/alpine/v3.4/main]
9 v3.4.6-83-g67e50bc [http://dl-cdn.alpinelinux.org/alpine/v3.4/community]
10 OK: 5973 distinct packages available
11
CODE

12 ## APK packages installed ##


13
14 Executing busybox-1.24.2-r13.trigger
15 OK: 221 MiB in 60 packages
16 ---> 3c6e4955df84
17 Removing intermediate container 4b3fb288b182
18 Step 3/8 : RUN mkdir /app
19 ---> Running in 4adabffb85ba
20 ---> 33417cb15b58
21 Removing intermediate container 4adabffb85ba
22 Step 4/8 : WORKDIR /app
23 ---> 51aa10a6cc98
24 Removing intermediate container 3df0cb7f8a3f

-7-
Share this

Codeship Guid
e

25 Step 5/8 : COPY Gemfile ./Gemfile


26 ---> 36f744d9ef97
27 Removing intermediate container 1aad134620c7
28 Step 6/8 : COPY Gemfile.lock ./Gemfile.lock
29 ---> 4ecf00a75513
30 Removing intermediate container 5fa016426543
31 Step 7/8 : RUN bundle install -j 20
32 ---> Running in f15edb779ff4
33
CODE

34 ## Gems Installed ##
35
36 Bundle complete! 16 Gemfile dependencies, 70 gems now installed.
37 Bundled gems are installed into /usr/local/bundle.
38 ---> e78a78f6e485
39 Removing intermediate container f15edb779ff4
40 Step 8/8 : COPY . .
41 ---> f4178bf3a235
42 Removing intermediate container 4cfe9df1be1f
43 Successfully built f4178bf3a235

You have now successfully created the application image


using Docker. Currently however, our app won't do much
since we still need a database, and we want to connect
everything together. This is where Docker Compose will
help us out.

Docker Compose Services

Now that you know how to create an image with a


Dockerfile , let's create an application as a service and
connect it to a database. Then we can run some setup
commands and be on our way to creating that new todo
list.

-8-
Share this

Codeship Guid
e

Create the file docker-compose.yml :


CODE

1 /> touch docker-compose.yml

The Docker Compose file will define and run the


containers based on a configuration file. We are using
compose file version 2 syntax, and you can read up on it
on Docker's site.

An important concept to understand is that Docker


Compose works at "runtime." Up until now, we have
been building images using docker build . this is
"buildtime." This is especially important when we add
things like volumes and command because they will
override what is set up at "buildtime."

For example, the app directory will be created during


the build. We then map that directory to the host
machine, and the host machine app code will be used
during "runtime." This allows us to make changes locally,
and those are then accessible in the container.

Open your docker-compose.yml file in your editor, and


copy paste the following lines:

1 version: '2'
2 services:
3 web:
CODE

4 build: .
5 command: bundle exec rails s -b 0.0.0.0
6 volumes:

-9-
Share this

Codeship Guid
e

7 - .:/app
8 ports:
9 - "3000:3000"
10 links:
11 - postgres
12 environment:
CODE

13 DATABASE_URL: postgres://todoapp@postgres/todos
14 postgres:
15 image: postgres:9.6.2-alpine
16 environment:
17 POSTGRES_USER: todoapp
18 POSTGRES_DB: todos

This will take a bit to unpack, but let's break it down by


service.

The web service


The first directive in the web service is to build the
image based on our Dockerfile . This will recreate
the image we used before, but it will now be named
according to the project we are in, name . After that, we
are giving the service some specific instructions on how
it should operate:

 command: bundle exec rails s -b 0.0.0.0 Once the


image is built, and the container is running, this will start
the application.
 volumes: This section will mount paths between the
host and the container.
 .:/app/ This will mount the root directory to our
working directory in the container.

- 10 -
Share this

Codeship Guid
e

 links: This will create a dependency and create the


environment variable to network the services together.
 environment: The application itself expects the
environment variable DATABASE_URL to run. This is set in
./config/database.yml .
 ports: This will publish the container's port, in this
case 3000 , to the host as port 3000 .

The DATABASE_URL is the connection string.


postgres://todoapp@postgres/todos connects using the
todoapp user, on the host postgres , using the
database todos .

This Compose file uses "links." The preference here is


depends_on , however Codeship currently doesn't
support depends_on . This will make the transition from
local to CI/CD easier at a later step.

The Postgres service


Like the Ruby image we used, the Docker Store has a
prebuilt image for PostgreSQL. Instead of using a build
directive, we can use the name of the image, and Docker
will grab that image for us and use it. In this case, we are
using postgres:9.6.2-alpine . We could leave it like that,
but it has environment variables to let us customize it a
bit.

- 11 -
Share this

Codeship Guid
e

 This particular image accepts a couple


environment:
environment variables so we can customize things to our
needs.
 POSTGRES_USER: todoapp This creates the user
todoapp as the default user for PostgreSQL.
 POSTGRES_DB: todos This will create the default
database as todos.

Running The Application

Now that we have our services defined, we can build the


application using docker-compose up. This will show the
images being built and eventually starting. After the initial
build, you will see the names of the containers being
created.

1 Pulling postgres (postgres:9.6.2-alpine)


2 9.6.2-alpine: Pulling from library/postgres
3 627beaf3eaaf: Pull complete
4 e351d01eba53: Pull complete
5 cbc11f1629f1: Pull complete
6 2931b310bc1e: Pull complete
7 2996796a1321: Pull complete
8 ebdf8bbd1a35: Pull complete
9 47255f8e1bca: Pull complete
CODE

10 4945582dcf7d: Pull complete


11 92139846ff88: Pull complete
12 Digest: sha256:7f3a59bc91a4c80c9a3ff0430ec012f7ce82f906ab0a2d7176fcbbf24ea9f893
13 Status: Downloaded newer image for postgres:9.6.2-alpine
14 Building web
15 ...
16 Creating rubyrailstodoapp_postgres_1
17 Creating rubyrailstodoapp_web_1
18 ...

- 12 -
Share this

Codeship Guid
e

19 web_1 | => Booting Puma


20 web_1 | => Rails 5.0.2 application starting in development on http://0.0.0.0:3000
21 web_1 | => Run 'rails server -h' for more startup options
22 web_1 | Puma starting in single mode...
CODE

23 web_1 | * Version 3.8.2 (ruby 2.4.0-p0), codename: Sassy Salamander


24 web_1 | * Min threads: 5, max threads: 5
25 web_1 | * Environment: development
26 web_1 | * Listening on tcp://0.0.0.0:3000
27 web_1 | Use Ctrl-C to stop

At this point, the application is running, and you will see


log output in the console. You can also run the services
as a background process, using docker-compose up -d .
During development, I prefer to run without -d
and create a second terminal window to run other
commands. If you want to run it as a background process
and view the logs, you can run docker-compose logs .

At a new command prompt, you can run docker-


compose ps to view your running containers. You should
see something like the following:

1 Name Command State Ports


2 -----------------------------------------------------------------------------------------
CODE

3 rubyrailstodoapp_postgres_1 docker-entrypoint.sh postgres Up 5432/tcp


4 rubyrailstodoapp_web_1 bundle exec rails s -b 0.0.0.0 Up 0.0.0.0:3000-
>3000/tcp

This will tell you the name of the services, the command
used to start it, its current state, and the ports. Notice
rubyrailstodoapp_web_1 has listed the port as
0.0.0.0:3000->3000/tcp . This tells us that you can
access the application using localhost:3000/todos on
the host machine.

- 13 -
Share this

Codeship Guid
e

Migrate the database schema


A small but important step not to overlook is the schema
migration for the database. Compose comes with an
exec command that will execute a one-off command
on a running container. The typical function to migrate
schemas is bundle exec rake db:migrate . We can run
that on the web service using docker-compose exec .

1 /> docker-compose exec web bundle exec rake db:migrate


2
3 == 20170321180544 CreateTodos: migrating ======================================
CODE

4 -- create_table(:todos)
5 -> 0.0220s
6 == 20170321180544 CreateTodos: migrated (0.0221s) =============================

Now we can try out the API:

1 /> curl localhost:3000/todos


CODE

2
3 []

The schema and all of the data in the container will persist
as long as the postgres:9.6.2-alpine image is not
removed. Eventually, however, it would be good to check
how your app will build with a clean setup. You can run
docker-compose down , which will clear things that are
built and let you see what is happening with a fresh start.

Feel free to check out the source code, play around a bit,
and see how things go for you.

- 14 -
Share this

Codeship Guid
e

Testing the Application

The application itself includes some integration tests


built using rspec . There are various ways to go about
testing with Docker, including creating Dockerfile.test
and docker-compose.test.yml files specific for the test
environment. That's a bit beyond the current scope of
this article, but I want to show you how to run the tests
using the current setup.

The current containers are running using the project


name rubyrailstodoapp . This is a default from the
directory name. If we attempt to run commands, it will
use the same project, and containers will restart. This is
what we don't want.

Instead, we will use a different project name to run the


application, isolating the tests into their own environment.
Since containers are ephemeral (short-lived), running
your tests in a separate set of containers makes certain
that your app is behaving exactly as it should in a clean
environment.

In your terminal, run the following command:

1 /> docker-compose -p tests run -p 3000 --rm web bundle exec rspec
2 ...............
CODE

3
4 Finished in 1.9 seconds (files took 5.29 seconds to load)
5 15 examples, 0 failures

- 15 -
Share this

Codeship Guid
e

The docker-compose command accepts several options,


followed by a command. In this case, you are using
-p tests to run the services under the tests project
name. The command being used is run , which will
execute a one-time command against a service.

Since the docker-compose.yml file specifies a port, we


use -p 3000 to create a random port to prevent port
collision. The --rm option will remove the containers
when we stop the containers. Finally, we are running in
the web service bundle exec rspec .

- 16 -
Share this

Codeship Guid
e

Part 2: Using Codeship


for Ruby Application
Deployments
This part of the tutorial requires you to have a few items
before you can get started:

 Codeship account
 Codeship Jet CLI
 Heroku account
 GitHub, Bitbucket, or GitLab account

In our example, we are deploying our code directly into


Heroku without Docker. While we will be using Docker
locally and in CI/CD, Codeship Pro allows us to deploy
Docker apps to production even when production is not
using Docker, such as classic Heroku. The deployment in
this case can be exchanged with any other server, image
repository, or Docker host.

Essentially, you can modify this example to suit your


needs, by using any Docker image that does what
you need, eg, deploy to AWS. Our documentation has
examples that you can review.

- 17 -
Share this

Codeship Guid
e

Testing Ruby Apps with Codeship

Continuous integration is defined as follows:

"In software engineering, continuous integration (CI)


is the practice of merging all developer working
copies to a shared mainline (like a master branch)
several times a day."

We want to automate all of this with Codeship so every


pushed commit will be built and tested prior to allowing
a merge. Let's set up the necessary files and test our
pipeline locally.

Codeship services file


The first file we need to create is the Codeship services
definition file. This will instruct Codeship as to the
services we need during the build.
CODE

1 #> touch codeship-services.yml

The codeship-services.yml syntax is directly influenced


by Docker Compose syntax, and the docker-compose.yml
file created in the last post gets us most of the way there.
Open the codeship-services.yml in your editor, copy
the following code, and paste there.

- 18 -
Share this

Codeship Guid
e

1 version: '2'
2 services:
3 web:
4 build: ./
5 links:
6 - postgres
7 environment:
CODE

8 DATABASE_URL: postgres://todoapp@postgres/todos
9 cached: true
10 postgres:
11 image: postgres:9.6.2-alpine
12 environment:
13 POSTGRES_USER: todoapp
14 POSTGRES_DB: todos
15 cached: true

The differences from the original docker-compose.yml


file to define the codeship-services.yml file are quite
minimal here. Next we can define the actual steps to run
during the build and automated tests.

Codeship steps file


The next file to create is the codeship-steps.yml file,
which will tell Codeship what steps to run and in what
order. You can learn more about the codeship-steps.yml
file in our documentation. Go ahead and create the new
steps file and open it up in your editor.
CODE

1 #> touch codeship-steps.yml

Each step defined in the codeship-steps.yml file is


based on what you define as your pipeline. This example

- 19 -
Share this

Codeship Guid
e

starts with a lint test and integration test, and


eventually will deploy the code as well. Your pipeline can
be completely customized to suit your specific needs.

1 - type: parallel
2 steps:
3 - name: rspec
4 service: web
CODE

5 command: rspec
6 - name: rubocop
7 service: web
8 command: rubocop

The steps here run in parallel: rspec and rubocop .


Running our tests simultaneously works great here,
because if either of these tests fail, the build will fail,
and saves us some time overall. You can find more
information on Codeship Pro step types here.

Bin scripts
Now that things are more automated, we end up
introducing a small race case that we can easily get
around with some simple scripts. As the project is built,
the integration tests require that Postgres is running
to perform the migrations. We can't guarantee that will
happen with the codeship-services.yml file alone. One
approach is to check that the database is available before
starting the migration.

1 #> mkdir bin && touch bin/{wait-for-postgres,ci}


CODE

2 #> chmod +x bin/**

- 20 -
Share this

Codeship Guid
e

wait-for postgres
The contents of the wait-for-postgres script are the
following:

1 #!/bin/sh
2 # wait-for-postgres
3 set -e
4
5 TIMEOUT=60
6 COUNT=0
7
CODE

8 until pg_isready -h "postgres" -p "5432" || [ $COUNT -eq $TIMEOUT ];


9 do
10 echo $COUNT
11 echo $TIMEOUT
12 sleep 1
13 COUNT=$((COUNT+1))
14 done

I am relying on the pg_isready function to check for


the readiness of the postgres database. If it isn't ready,
we sleep for a second and try again. We need to make a
small change to the Dockerfile to add the pg_isready
function.

- 21 -
Share this

Codeship Guid
e

1 RUN apk update && apk add nodejs build-base libxml2-dev libxslt-dev postgresql
CODE

postgresql-dev

The function times out after 60 seconds, but you can


tweak that if you see it taking longer. It typically takes
only a few seconds to connect.

ci
The ci script is as follows:

1 #!/bin/sh
2 # Usage: bin/ci [setup]
3 set -e
4
5 bin/wait-for-postgres
CODE

6
7 time bundle exec rake db:create
8 time bundle exec rake db:migrate
9 time bundle exec rake db:seed
10
11 time bundle exec rspec $2

Not too much heavy lifting here. This script runs the
wait-for-postgres script to finish up, then it will
perform the migration, and the last line takes the first
parameter and will run that. This allows me to change a
line in codeship-steps.yml to run this script first.

1 - name: rspec
CODE

2 service: web
3 command: bin/ci rspec # you only need to change the command here

- 22 -
Share this

Codeship Guid
e

Local testing with Codeship Jet CLI

We are able to test our setup locally using


Codeship Jet CLI . If you haven't done it yet, check
out our Getting Started with Codeship Jet CLI
documentation. Codeship Jet CLI will run through the
steps file just as it would on Codeship. This is a quick way
to catch errors early before committing and pushing to
your repository.

1 #> jet steps


CODE

2 ## build stuff, run tests ##


3 {StepFinished=step_name:"tests" type:STEP_FINISHED_TYPE_SUCCESS}

If you see the final result above, you have set everything
up correctly. If you receive instead type:STEP_FINISHED_
TYPE_ERROR , something didn't go right and you should
check some things. Codeship Jet CLI will produce
a log that you can review to try to locate the problem.

- 23 -
Share this

Codeship Guid
e

Having the ability to catch any errors prior to pushing to


your repository makes this a particularly powerful tool in
the Codeship arsenal.

The finished version of this project is also available on


GitHub, in case you get stuck and want to skip ahead.

Setting Up Your Codeship Pro Project

The next phase is to set up the Codeship project, so that


new pushes into your repository will kick off the build. In
this step, we will make sure you have a repository set up,
create the project, and push a build.

Source code management setup


Log into your GitHub, Bitbucket, or GitLab account and
create a new repo there that you have admin access
to. Grab the clone URL and switch to the Codeship
application.

Create project
When your repo is ready to go, you can now set up the
Codeship project.

- 24 -
Share this

Codeship Guid
e

Navigate to Projects, then click the "New Project" button.

Connect your SCM by selecting the source code


management (SCM) tool you set up in the previous step.

If you originally signed up with an SCM different than above, we will


connect to the service during this step.

- 25 -
Share this

Codeship Guid
e

Choose your repository by copy/pasting the Repository


Clone URL link from the previous step. Click the Connect
button.

- 26 -
Share this

Codeship Guid
e

Configure your project by clicking the "Select Pro Project"


button.

Your project is set up at this time, and any code


committed and pushed to the repository will now run
builds automatically.

Push your first build


Let's run a build now. In your command line, or using
your favorite git tool, make sure the repo is initialized,
connect the remote, add the files, commit, and push.

1 #> git init


2 #> git remote add origin <<YOUR_REPOSITORY_CLONE_URL>>
CODE

3 #> git add .


4 #> git commit -am "initial build"
5 #> git push -u origin master

- 27 -
Share this

Codeship Guid
e

If you head back to Codeship and click into your project,


you should see the build running. Make sure at this point
you get a green build. Otherwise you may need to go
back and check some things:

 Make sure you have added all of your files to your repo.
 Review your codeship-services.yml and
codeship-steps.yml files.
 Run Codeship Jet CLI locally to double-check it works
locally.

Once you have the CI working, you can move on to the


deployment section, where we will automatically deploy
our code to Heroku.

Continuous Deployment to Heroku


with Codeship Pro

So far, we have the integration steps running, and every


new commit will run tests to make sure that the code is
ready to merge.

When the branch is master , however, we want to run


another step to actually deploy the application when the
tests are passing.

- 28 -
Share this

Codeship Guid
e

Creating the Heroku app


You can create an application using the Heroku UI, which
is the simplest way to get started. If you are familiar with
the Heroku CLI, you can also perform these steps using
that as well. Once you have the application created, there
are a some steps to get everything Codeship needs for
the deployment.

Heroku PostgreSQL add-on


These steps will set up the Heroku PostgreSQL add-on
for your app to use as its database when deployed.

1. Click Resources.
2. Under Add-ons, search for postgres .
3. In the results dropdown, click Heroku Postgres.
4. Leave the selection as Hobby Dev Free, then click
Provision.

Get your Heroku API key


1. Click on your avatar in the upper right, then click Account
Settings.
2. Near the bottom of the settings page, you will find an API
key. Click Reveal.
3. Copy the API key.

- 29 -
Share this

Codeship Guid
e

Set this up in a deployment.env file with your terminal:


CODE

1 #> echo "HEROKU_API_KEY=YOUR_API_KEY_HERE" > deployment.env

Create the Heroku procfile


This is a simple one-liner, but it's required for Heroku to run
the application:
CODE

1 #> echo "web: bundle exec puma -C config/puma.rb" > Procfile

Encrypting with Codeship Jet CLI


Since we have some sensitive keys that we will need to
use, Codeship Jet CLI provides some encryption tools
to secure your keys. In this instance, it's an environment
variable needed for the deployment, however you can
encrypt all of your secrets. For more information, you
can read the Codeship documentation on Encrypting
Environment Variables.

In the Codeship UI, follow these steps:

1. Click Projects, then the project you are currently working in.
2. Click Settings, then click General.
3. Locate and copy the AES Key .

- 30 -
Share this

Codeship Guid
e

In your terminal, include:


CODE

1 #> echo "YOUR_AES_KEY" > codeship.aes

NOTE: Make sure that both codeship.aes and deployment.env are ignored in
your .gitignore file. Since they contain sensitive data, you don't want these to be
pushed into your repository.

All that's left to do is encrypt the deployment.env file.


We do this using Codeship Jet CLI .
CODE

1 #> jet encrypt deployment.env deployment.env.encrypted

The deployment.env.encrypted will then be included in


your repository and decrypted in Codeship.

Adding the Heroku deployment service


In Codeship Pro, we are able to create any service we
need using Docker. What this means to you is that if it
runs on Docker, it runs on Codeship. In this example, we
are using a service that Codeship provides specifically
for Heroku deployment. Let's add the service to the end
of our codeship-services.yml file.

1 version: '2'
2 services:
CODE

3 web:
4 build: ./
5 links:

- 31 -
Share this

Codeship Guid
e

6 - postgres
7 environment:
8 DATABASE_URL: postgres://todoapp@postgres/todos
9 cached: true
10 postgres:
11 image: postgres:9.6.2-alpine
12 environment:
CODE

13 POSTGRES_USER: todoapp
14 POSTGRES_DB: todos
15 cached: true
16 deploy:
17 image: codeship/heroku-deployment
18 encrypted_env_file: deployment.env.encrypted
19 volumes:
20 - ./:/deploy

Note that we add the encrypted_env_file here. This


instructs Codeship as to which file to use to find the
Heroku API key used inside the container.

Adding the deployment step


The only steps left is to tell Codeship when to use the
deploy service. Open up the codeship-steps.yml file
and add the following:

1 - type: parallel
2 steps:
3 - name: rspec
4 service: web
5 command: bin/ci rspec
CODE

6 - name: rubocop
7 service: web
8 command: rubocop
9 - name: deploy #step added
10 tag: master

- 32 -
Share this

Codeship Guid
e

11 service: deploy
12 command: codeship_heroku deploy /deploy ruby-rails-todoapp
13 - name: migrate #step added
CODE

14 tag: master
15 service: deploy
16 command: heroku run --app ruby-rails-todoapp -- bundle exec rake db:migrate

What this codeship-steps.yml file indicates now is that


if the branch, listed as tag , is equal to master , it will
run the deploy service. If we push to any other branch,
everything will run except the deployment. You can
read more about the tag and exclude attributes and
limiting steps in Codeship Pro.

The image codeship/heroku-deployment has the


command codeship_heroku deploy , which accepts two
parameters: the path of the files and the name of the
Heroku app. In this case, these are /deploy and ruby-
rails-todoapp . As long as all tests pass and the branch
is master , our code will be deployed to Heroku.
The codeship/heroku-deployment image also allows us
to run Heroku command line tools as well. In this case,
after we have deployed the application, codeship-steps.
yml will then perform heroku run --app ruby-rails-
todoapp -- bundle exec rake db:migrate to make sure
our latest schema is migrated to the Heroku PostgreSQL
database.

The codeship/heroku-deployment image is running


several commands to test for proper access to the

- 33 -
Share this

Codeship Guid
e

application, tarball the code, and deploy to Heroku with


their API. Just to reiterate, this can be anything you need
it to be, eg, deployment to AWS. If it runs in a Docker
container, Codeship Pro can run it.

Starting the build on Codeship Pro


Now we can push these changes into our SCM and let
Codeship take it from here.

1 git add .
CODE

2 git commit -am "adding deployment"


3 git push

Make sure you have added your codeship.aes and


deployment.env files to .gitignore . You want to
ignore these files so you are not publicly exposing secrets
in your repository. Do that now if you haven't already.

Head over to your Codeship Pro project and watch your


build and deployment from the dashboard.

- 34 -
Share this

Codeship Guid
e

Conclusion

This eBook covered quite a bit. Initially, we are tasked


with testing and deploying our application manually. This
process is not only tedious, it takes up someone's time
and is error prone.

First we covered setting up our local development and


testing workflow using Docker Compose. We then used
Codeship Pro and Jet CLI to automate our steps even
further. Now we are able to push a new commit to
master , which then runs our tests and deploys to
Heroku all through Codeship Pro.

Here are some of the resources used for this tutorial:

Docker
 Docker Community Edition
 Dockerfile Reference
 Docker Compose Overview
 Docker Compose CLI

Docker Images Used


 Ruby
 PostgreSQL
 Codeship Heroku Deployment

- 35 -
Share this

Codeship Guid
e

Codeship Pro
 Codeship Pro Documentation
 Codeship Jet CLI Docs
 Encrypting Environment Variables
 Codeship Steps Docs
 Codeship Services Docs

Article Resources
 Using Docker Compose for Ruby Development
 Ruby on Rails Todo App Repo
 Alpine Based Docker Images Make a Difference in Real
World Apps

Additional Tools and Resources Used


 Ruby on Rails
 Rubocop
 Rspec
 Heroku
 GitHub
 Bitbucket
 GitLab
 PostgreSQL
 Todo Backend

- 36 -
Share this

Codeship Guid
e

More Codeship Resources.

Orchestrate Containers with


Docker Compose.
EBOOKS

In this eBook you will learn how to easily recreate a


microservice architecture with Docker Compose..
Download this eBook

Dockerizing Ruby Apps and


Effectively Testing them.
EBOOKS

In this eBook you will learn how to dockerize Ruby


applications and how to test them.
Download this eBook

Best Practices for Building


Minimal Docker Images.
EBOOKS

In this eBook we will look at some ways to streamline


your Docker image as small as possible.
Download this eBook

- 37 -
Share this

Codeship Guid
e

About Codeship.
Codeship is a hosted Continuous Integration service that fits all your needs.
Codeship Basic provides pre-installed dependencies and a simple setup UI
that let you incorporate CI and CD in only minutes. Codeship Pro has native
Docker support and gives you full control of your CI and CD setup while
providing the convenience of a hosted solution.

Codeship Basic Codeship Pro


A simple out-of-the-box Continuous A fully customizable hosted
Integration service that just works. Continuous Integration service.

Starting at $0/month. Starting at $0/month.

Works out of the box Customizability & Full Autonomy

Preinstalled CI dependencies Local CLI tool

Optimized hosted infrastructure Dedicated single-tenant instances

Quick & simple setup Deploy anywhere

LEARN MORE LEARN MORE

Potrebbero piacerti anche