Heroku Container Registryを使ってみた
こんにちは。
Heroku Container Registryを早速使ってみたので、まとめます。普通に、デプロイするだけだと他の人がたくさんやっていて面白くないので、ローカルの開発環境の構築からDeployするところまでやってみます。
今回の構成
Ruby on Rails のサンプルアプリを作ります。ローカルの開発環境はDocker上にRuby on Rails のコンテナとPostgreSQLのコンテナを立てて、docker-composeで管理します。dockerのimageは全部Docker Hubにある公式のリポジトリを使います。本番環境は、Heroku Container Registory と Heroku Postgresを使います。
ディレクトリ構成
HerokuSampleApp ├── .dockerignore ├── .env ├── Dockerfile ├── HerokuSampleApp │ ├── .gitignore │ ├── Gemfile │ ├── Gemfile.lock │ ├── README.md │ ├── Rakefile │ ├── app │ ├── bin │ ├── config │ ├── config.ru │ ├── db │ ├── lib │ ├── log │ ├── public │ ├── tmp │ └── vendor └── docker-compose.yml
手元の開発環境を構築
まずは、ローカルのDocker環境で動かせるように整備していきます。
Rails プロジェクトを作成
docker コマンドを使ってrailsのプロジェクトを生成します。
docker run -it --rm -w /HerokuSampleApp/ -v $(pwd)/:/HerokuSampleApp/ rails rails new HerokuSampleApp -TB --database=postgresql
ちょっと解説すると、-wオプション(--workdir)は作業ディレクトリを指定しており、-v(--volume)オプションはホストのHerokuSampleAppディレクトリをマウントしています。workdirを指定して、ホストのディレクトリをworkdir上にマウントすることで、rails newでホストのディレクトリにプロジェクトを生成することができます。
Rails用のDocker imageを作成
Dockerfileを下記のようにしました。
FROM rails:latest MAINTAINER ishikawa_pro ENV RAILS_ENV=production RACK_ENV=production WORKDIR /usr/src/HerokuSampleApp COPY ./HerokuSampleApp/Gemfile* ./ RUN bundle install COPY ./HerokuSampleApp ./ CMD ["rails","s", "-b", "0.0.0.0"]
プロジェクトのGemfileとGemfile.lockだけを先にマウントしてbundle install しています。こうすることで、再度buildする際にGemfileが変更されていなければ、bundle installのキャッシュを利用してくれるので高速で buildが完了します。また、MOUNTでファイルをマウントせずにCOPYでrailsのプロジェクトをコピーするようにしています。これは、ホストのディレクトリをマウントしてしまうと手元の開発環境では問題ないですが、デプロイ先のコンテナでプロジェクトのディレクトリがマウントできないからです。
あと重要なことは、heroku container registryはENTRYPOINT コマンドに対応していないようなので、ENTRYPOINTではなくCMDを使わないといけないようです。
.dockerignoreの作成
docker-composeでrailsを手元で動かしている状態等でdocker buildし直してデプロイしてしまうと、railsプロジェクト内のtmp/pids/server.pidが残ったままになってしまい本番環境でrailsサーバーが起動しないという自体がよく起こるので、.dockerignoreにtmp/pids/server.pidを登録してbuildする際にdocker デーモンに送らないようにします。
./HerokuSampleApp/tmp/pids/server.pid
scaffold
作ったimageを使ってscafoldします。
docker run -it --rm -v $(pwd)/HerokuSampleApp/:/usr/src/HerokuSampleApp/ heroku_sample_app rails generate scaffold user name:string age:integer
テーブルスキーマは、
user |
---|
name:string age:integer |
です。
docker-composeによる管理
Railsコンテナの設定はここで一旦休憩です。ここからは、docker-composeでRailsコンテナとPostgreSQLコンテナを管理できるようにします。今回PostgreSQLコンテナに関しては、公式リポジトリを使うため特にDockerfileを書いたりはしません。 (PostgreSQLイメージの使い方はDocker HubのPostgreSQLのページを参照してください)
docker-compose.ymlは以下のようにしました。
version: '2' volumes: pgdb: driver: 'local' services: db: image: library/postgres ports: - "5432:5432" env_file: .env volumes: - pgdb:/var/lib/postgresql/data container_name: HerokuSamplePostgres web: build: . image: rails/heroku_sample_app:latest ports: - "3000:3000" env_file: .env environment: POSTGRES_HOST: db RAILS_ENV: development RACK_ENV: develobpment links: - db:postgres volumes: - ./HerokuSampleApp/:/usr/src/HerokuSampleApp/ container_name: HerokuSampleApp restart: on-failure stdin_open: true tty: true
pgdbという名前のvolumeを作成して、/var/lib/postgresql/data にあるdatabase fileをvolumeにマウントさせることでデータベースのデータを永続させています。
詳しく過去の記事でまとめているので読んでみてください。
ishikawa-pro.hatenablog.com
railsコンテナの環境変数でRACK_ENVとRAILS_ENVの設定を、Dockerfile側ではproductionにしておき、docker-compose側ではdevelopmentにすることで、手元の開発環境はdevelopmentで動き、本番環境ではproductionで動くようにしています。
あと、.envファイルを読み込んでコンテナ間で共通の環境変数を設定しています。
とりえあず、postgreSQLのユーザー名とパスワードだけです。
POSTGRES_USER=ユーザー名 POSTGRES_PASSWORD=パスワード
データベース周りの設定
railsのdatabse.ymlを編集してとりあえず、postgreSQLコンテナに繋がるようにだけします。
default: &default adapter: postgresql encoding: unicode pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> username: <%= ENV['POSTGRES_USER'] %> password: <%= ENV['POSTGRES_PASSWORD'] %> host: <%= ENV['POSTGRES_HOST'] %> development: <<: *default database: HerokuSampleApp_development
.envにユーザー名とパスワードを書いているので、環境変数を使ってdatabase.ymlの中身を書きます。
docker-composeでコンテナ2つを立てて、rake db:createとmigrateして、データベースを作成します。
docker-compose up -d docker-compose exec web rails db:create docker-compose exec web rails db:migrate
一旦動作確認
ここまでで、超雑なユーザー管理アプリ(?)みたいなものができてるはずなのでザックリ動作確認しましょう。あと、データが永続化できているか確認するためにコンテナを落として、立ち上げ直します。
docker-compose restart
データが消えてなければローカルの環境設定は完了です。
Herokuへデプロイする
CLIインストール
mac 限定でいきます。Heroku CLIとContainer Registry用のプラグインをインストールします。
(mac 以外の人は、👉を参照。 https://devcenter.heroku.com/articles/heroku-cli )
brew install heroku/brew/heroku heroku plugins:install heroku-container-registry
インストールが終われば、HerokuへのログインとContainer Registoryへログインします。
heroku login heroku container:login
database.ymlのproduction部分を修正
Heroku postgresを使うので、database.ymlのproduction部分だけHeroku postgres用の設定に直します。
production: url: <%= ENV['DATABASE_URL'] %>
rails newするときに --database=postgresqlをつけておけば、databse.ymlが最初からpostgreSQL用の記述になっており、Herokuの場合の設定の仕方もコメントで書いてあるので細かい説明は省略します。
SECRET_KEY_BASE生成
SECRETE_KEY_BASEを生成して、herokuの環境変数に追加します。
SECRET_BASE_KEY=$(docker run -it --rm -v $(pwd)/HerokuSampleApp/:/usr/src/HerokuSampleApp/ rails/heroku_sample_app bundle exec rake secret) heroku config:add SECRET_KEY_BASE=$SECRET_BASE_KEY
No app specifiedが出る場合は、--app オプションでアプリ名を入れる。アプリ名は、
heroku apps
で取得する。
Heroku Postgresの設定
Heroku Postgresを有効にするのはコマンド一発で終わりです笑
heroku addons:create heroku-postgresql:hobby-dev
Heroku Postgresの設定が終わると自動でDATABASE_URLの環境変数を追加してくれているので確認してます。ちゃんと環境変数が設定されていれば、テーブル作成します。
heroku config heroku run rake db:migrate
動作確認
以上の作業でデプロイ完了です。ページを開いてみてうまく動いていれば完璧です。
heroku open
まとめ
記事が長くなりましたが、これで開発環境構築からデプロイまで完了です。
Heroku Container Registoryを使ってみて、自分の作ったdocker imageを使ってめちゃくちゃ簡単にデプロイできるので最高じゃん!という感じです。特にハマるところもなくすんなりデプロイできましたし、無料枠内でアプリ1つは動かせるので僕が今作ってるアプリもHerokuへデプロイしようと思います!
長くなりましたが、今日はこれで失礼します。