古いrailsアプリをdockerコンテナに封じ込めた話

古いrailsアプリをdockerコンテナに封じ込めた話


はじめに

seeds company Xチーム、エンジニアの浅野ともうします。

seeds companyは社内カンパニーとして、ATS (applicant tracking system) の企画・開発・運用・導入サポート、と関連する全ての作業を行っています。
私が所属するXチームでは主に新システムの構築を担当しています。

今日は社内の古いrailsアプリをDockerコンテナ化した話を書きます。🐳

背景

現在、Xチームは4月に新設されたDevOpsチームと協力して現行システムの改修を進めています。

現行システムで抱える課題

rubyとrailsに関する問題

バージョンが古いのですが、テストがないためバージョン上げるのに非常に工数がかかる状態となっています。😭

インフラとミドルウェアに関する問題

インフラや使用するミドルウェアのバージョンも古いです。😭

また、オンプレ環境を利用している関係で別組織の運用部隊に作業依頼をしなければならず、対応に時間かかるという問題を抱えています。

全体的に共通する問題

ドキュメント化や自動化がされてないため、都度手探り&手作業が発生し運用工数が負担となっています。

開発について

これらの問題は4月に新設された専任のDevOpsチームが目まぐるしい勢いでカイゼンをまわしており、日々ドキュメント整理や各種自動化が進められてきています。

なぜコンテナにしたのか

Xチームで構築中の新システムはDockerベースで開発しており、本番環境でもAWS上のECSやECRなどのコンテナソリューションを活用する予定です。

目指しているのは12 factorでいうところの開発本番一致です。https://12factor.net/ja/dev-prod-parity

もし現行システムがコンテナ化できれば、AWS上に新システムと同じ構成で移行と構築ができるのでは?という仮説を立てました。
この仮説が実証できれば下記が実現できるため、現行システムが抱える問題の解決に繋がりそうです。

  • インフラ部分のコード化によるドキュメント不足の解消・手動化対応の削減
  • Dockerfileによるinfrastructure as codeの実現
  • オンプレ環境からの移行によりDevOpsチームがインフラ管理できるようになる

Dockerfile

とりあえず本丸のrailsアプリをコンテナにしました。

Dockerfileは下記の通りです。
※バージョンの部分は適宜読み替えて下さい😳


FROM gliderlabs/alpine:latest

ENV BUILD_PACKAGES bash wget curl tar make gcc alpine-sdk zlib zlib-dev readline
ENV GEM_PACKAGES openssl openssl-dev libxml2-dev libxslt-dev mysql mariadb-dev imagemagick imagemagick-dev imagemagick-c++ ruby-rmagick

RUN apk update && \
  apk upgrade && \
  apk --no-cache add tzdata \
  $BUILD_PACKAGES $GEM_PACKAGES && \
  cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
  apk del tzdata

# build ruby
RUN wget -O ruby.tar.gz http://cache.ruby-lang.org/pub/ruby/x.y/ruby-x.y.z.tar.gz && \
  tar -zxvf ruby.tar.gz && \
  rm ruby.tar.gz

WORKDIR ruby
RUN ./configure --with-openssl-dir=/usr/bin
RUN make
RUN make install

# install rubygems
WORKDIR /
RUN wget -O rubygems.tgz http://rubygems.org/rubygems/rubygems-x.y.z.tgz && \
  tar -zxvf rubygems.tgz && \
  rm rubygems.tgz
WORKDIR rubygems
RUN ruby setup.rb

# install bundler
RUN gem install bundler

WORKDIR /app
COPY Gemfile /app/

# bundle install
RUN bundle install
COPY . /home/app

どのようにコンテナにしたのか

ダメ元でDockerHub上で古いruby/railsのコンテナを探してみましたが、信頼できそうなものは見つからなかったので、試しにrubyのビルドから環境を作ってみることにしました。

イチから作るのであれば、baseイメージも自由に選べます。ここではイメージサイズが小さく扱いやすいgliderlabsのalpinelinuxを選択しました。http://gliderlabs.viewdocs.io/docker-alpine/

rubyをソースからmakeするために必要だったパッケージを$BUILD_PACKAGESにまとめています。
同様にアプリで使っているgemに関連してインストールが必要だったパッケージは$GEM_PACKAGESにまとめています。

ハマったところ

  • rubyを./configureするときに–with-openssl-dirオプションでopensslのディレクトリを指定しないと、”bundle install”するときに https://rubygems.org などhttpsサイトから取得できないという問題がありました。
  • “mysql”gemをインストールするために、mysqlとmysql-devのパッケージが必要なのですが、alpinelinuxにはmysql-devパッケージがありません。これはmariadb-devで代用できました。

開発での利用について

ローカルで開発する時は下記のようなdocker-composeファイルを書いて、環境を立ち上げています

 version: '2'
 services:
     rails:
          build:
              context: /path/to/
              dockerfile: DockerfileName
          ports:
              - "3000:3000"
          depends_on:
              - db
          networks:
              - app
              - datastore
      db:
          image: mysql:5.6
          ports:
              - "3306:3306"
          environment:
              - TZ=JST-9
              - MYSQL_USER=required
              - MYSQL_PASSWORD=required
              - MYSQL_ROOT_PASSWORD=required
              - MYSQL_DATABASE=required
          volumes:
              - /path/to/mysql/dump/:/docker-entrypoint-initdb.d/
              - /path/to/mysql/conf/:/etc/mysql/conf.d
          privileged: true
          networks:
              - datastore
 networks:
      app:
      datastore:

これまでローカルの検証環境は秘伝のVMイメージを使っていたのですが、このようにDockerfileとdocker-compose.ymlファイルを定義しておけば開発者各自でローカルにコンテナを立てられるので数GBのVMイメージファイルをやり取りする必要がなくなります。

mysqlコンテナについて

DockerHubで提供されているofficialなmysqlコンテナを使用しています。
railsがそうだったように、officialでは古いバージョンのコンテナは提供されていませんでした。

こちらもrubyと同様にイチからビルドすることも考えましたが、いずれはミドルウェアのバージョンを上げることを想定していたため、ひとまず最新のバージョンで動作するか試してみることにしました。

因みに5.7は一部既存のマイグレーションが動作しなかったため使用を断念したのですが、使用するコンテナイメージのタグを5.7から5.6に差し替えるだけでミドルウェアのバージョンを変更できるというのもDockerコンテナの利点かと思います。

今後について

まずは動いたレベルなのでセキュリティ対応を進める必要があります。
docker-bench-securityなどでチェックしながら対応を進めていく予定です。https://github.com/docker/docker-bench-security

またDockerfileのリファクタリングとして、rubyのビルドとアプリケーションにまつわる部分は分離したほうがよいと考えています。
現状では初回のrubyビルドにそこそこ時間がかかってしまうためです。

ひとまずアプリがコンテナ化できたことで当初の仮説検証を進めることができそうです。😌