Giter Site home page Giter Site logo

convox-services-test-rails's Introduction

Rails on Convox Example

This repository contains an example Ruby on Rails 4.2 app configured for local development and deployment to Convox.

The following is a step-by-step walkthrough of how the app was configured and why.

The Rails App

The first commit contains all the files generated by running rails new example via the rails 4.2.6 gem.

Convox Init

The Convox CLI contains a command called convox init, which we used to generate the files in the second commit, Dockerfile, docker-compose.yml, and .dockerignore.

Since Convox uses Docker for containerization, these files are necessary to describe the application's containers and how to build them. The generated files are good defaults for most Rails apps, and they can also be edited to suit your specific needs.

Dockerfile

convox/rails Docker image

The generated Dockerfile inherits from the convox/rails Docker image, which has all the packages and configuration necessary to run your Rails app both locally and in production. This includes:

  • OS libraries to support PostgreSQL, MySQL, and sqlite3 databases
  • nodejs for compiling Javascript assets
  • nginx for proxying client connections
  • a Convox-friendly nginx config file
  • a convox.rb file for logging to STDOUT
  • a bin/web script for booting the app

how Dockerfile describes the build

Starting from the convox/rails image, the generated Dockerfile executes the remaining build steps that your Rails app needs. There are basically 3 steps in this process, and they are executed in a particular order to take advantage of Docker's build caching behavior.

  1. Gemfile and Gemfile.lock are copied and bundle install is run. This happens first because it is slow and something that's done infrequently. After running once, this step will be cached unless the cache is busted by later edits to Gemfile or Gemfile.lock.

  2. All the files necessary for the Rails asset pipeline are copied, and assets are built. Again, this is done early in the build process to optimize caching. The asset building step will only be run in the future if these files have changed.

  3. The rest of the application source is copied over. These files will change frequently, so this step of the build will very rarely be cached.

docker-compose.yml

The docker-compose.yml file explains how to run the containers that make up your app. This generated file describes a web container which will be your main Rails web process. The various sections of the web configuration are described below:

build

build: .

This entry declares that the web container should use an image built from the top level of your application directory using the Dockerfile found there.

labels

labels:
  - convox.port.443.protocol=tls
  - convox.port.443.proxy=true

The labels section is used by Convox for configuration not covered by the official Compose spec. Here we're using it to configure how the load balancer handles traffic on port 443.

convox.port.443.protocol=tls means that the load balancer listens on port 443 in TLS mode, accepting encrypted traffic and using your application's certificate to decrypt the messages.

convox.port.443.proxy=true means that PROXY protocol TCP headers are injected into requests on port 443. These headers can then be used by nginx to set the HTTP headers your Rails application expects.

See the load balancer documentation for more detailed info.

ports

ports:
  - 80:4000
  - 443:4001

The ports section describes which ports your application listens on and which ports of the web container they map to. In this case the application is listening on ports 80 and 443 for http and https traffic. These requests get routed to the web container on ports 4000 and 4001, respectively.

.dockerignore

convox init also generates a .dockerignore file that ignores files and directories not needed in the app's Docker image. It's important to have a good .dockerignore to keep images small and builds, pushes and pulls fast.

Linking a database container

Up to this point the app has been using sqlite3 for its database. In a production environment, however, a database like PostgreSQL is more likely to be used. Linking a Postgres container to your app is pretty straightforward. Here's how we did it in this example.

Update the app

First we removed the sqlite3 gem and added the pg gem.

Next we removed the config/database.yml file. We'll be configuring the database via the DATABASE_URL environment variable going forward.

Add a database container

We want to run a Postgres container for local development, so the next step is to add it to docker-compose.yml.

We define a new process called database, and use the convox/postgres image:

database:
  image: convox/postgres
  ports:
    - 5432

We want the database to listen for connections on port 5432. When we deploy this app an internal TCP load balancer will be created to listen on that port.

Persisting Data

Since the development database will run locally as a Docker container, it will start fresh with an empty database every time we run convox start. While this blank-slate behavior can be nice sometimes, in this case we want to keep the data in our database across starts. This can be accomplished using Docker volumes.

By mounting a host volume onto our database container, we can keep many of the files the database creates. For convox/postgres, we need to mount a host volume to /var/lib/postgresql/data in the container.

The database section of docker-compose.yml now looks like:

database:
  image: convox/postgres
  ports:
    - 5432
  volumes:
    - /var/lib/postgresql/data

NOTE: By omitting the host side of the host:container volume specification, we let Convox choose a good location for the host volume. Development volumes are namespaced by app and stored in ~/.convox/volumes.

Link database to web

Lastly, we need to link the database container to the web container. We do this by adding a links section to web:

links:
  - database

This will cause a DATABASE_URL environment variable to be injected into the web environment, which it will use to connect to the database. You can read more about container linking here.

A linked container works well for local development. However, when you deploy this app, you'll want a "real" Postgres. To accomplish this you can provision an hosted Postgres instance via convox services, scale your database process count in your app to 0, and set the DATABASE_URL environment variable to point to the hosted Postgres.

Running the app Locally

convox start

Developing in the container

Once you have your app up and running, you can take advantage of Convox code sync to execute rails and rake commands inside the container, while still editing and committing code on your host machine.

You can use the docker exec command to get a bash session on your web container:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                              NAMES
026cfe0ad0cb        convox/proxy        "proxy-link 443 4001 "   54 seconds ago      Up 52 seconds       0.0.0.0:443->443/tcp                               rails-web-proxy-443
96a7d05ed465        convox/proxy        "proxy-link 80 4000 t"   54 seconds ago      Up 53 seconds       0.0.0.0:80->80/tcp                                 rails-web-proxy-80
2776dea3621e        rails/web           "bin/web"                55 seconds ago      Up 54 seconds       0.0.0.0:32807->4000/tcp, 0.0.0.0:32806->4001/tcp   rails-web
e70c8bf0c74f        rails/database      "/docker-entrypoint.s"   55 seconds ago      Up 54 seconds       0.0.0.0:32805->5432/tcp                            rails-database

Grab the CONTAINER ID of the rails/web process and exec onto the container:

$ docker exec -it 2776dea3621e bash
root@2776dea3621e:/app#

Once you're in the container you can run a Rails generator:

root@2776dea3621e:/app# rails g scaffold Book author:string title:string
Running via Spring preloader in process 52
      invoke  active_record
      create    db/migrate/20160808061714_create_books.rb
      create    /models/book.rb
      invoke    test_unit
      create      test/models/book_test.rb
      create      test/fixtures/books.yml
      invoke  resource_route
       route    resources :books
      invoke  scaffold_controller
      create    /controllers/books_controller.rb
      invoke    erb
      create      /views/books
      create      /views/books/index.html.erb
      create      /views/books/edit.html.erb
      create      /views/books/show.html.erb
      create      /views/books/new.html.erb
      create      /views/books/_form.html.erb
      invoke    test_unit
      create      test/controllers/books_controller_test.rb
      invoke    helper
      create      /helpers/books_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      /views/books/index.json.jbuilder
      create      /views/books/show.json.jbuilder
      invoke  assets
      invoke    coffee
      create      /assets/javascripts/books.coffee
      invoke    scss
      create      /assets/stylesheets/books.scss
      invoke  scss
      create    /assets/stylesheets/scaffolds.scss

And note that convox start has synced the generated files back to your host where they can be edited and commited:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

  modified:   config/routes.rb

Untracked files:
  (use "git add <file>..." to include in what will be committed)

  app/assets/javascripts/books.coffee
  app/assets/stylesheets/books.scss
  app/assets/stylesheets/scaffolds.scss
  app/controllers/books_controller.rb
  app/helpers/books_helper.rb
  app/models/book.rb
  app/views/books/
  db/migrate/
  test/controllers/books_controller_test.rb
  test/fixtures/books.yml
  test/models/book_test.rb

no changes added to commit (use "git add" and/or "git commit -a")

You can execute the generated database migration on the container as well:

root@2776dea3621e:/app# rake db:migrate
== 20160808061714 CreateBooks: migrating ======================================
-- create_table(:books)
   -> 0.0590s
== 20160808061714 CreateBooks: migrated (0.0593s) =============================

You can now visit https://localhost/books and create a book record:

books form

book created

Since you're persisting Postgres data you can quit convox start and run it again and the data will still be there!

list books

Deploying the application

After installing a Rack create an app and deploy your code to it:

convox apps create myapp
convox deploy -a myapp

You should also create a Postgres service:

$ convox services create postgres --name myapp-db
$ convox services info myapp-db
Name    myapp-db
Status  running
Exports
  URL: postgres://postgres:UWKXRYGYYRRKOSQFDDQPFUYQDOVHGX@convox-myapp-db.cbm068zjzjcr.us-east-1.rds.amazonaws.com:5432/app

Update your deployed app's environment to use the database service

$ convox env set postgres://postgres:UWKXRYGYYRRKOSQFDDQPFUYQDOVHGX@convox-myapp-db.cbm068zjzjcr.us-east-1.rds.amazonaws.com:5432/app --promote -a myapp

Don't forget to run the migrations:

$ convox run web rake db:migrate -a myapp

Now that your deployed app is using a Postgres service, it no longer needs the database container. Run the following command to stop running the container and deprovision its load balancer:

$ convox scale database --count=-1 -a myapp

convox-services-test-rails's People

Contributors

ddollar avatar mattmanning avatar oss92 avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.