ランサーズ(Lancers)エンジニアブログ > AWS > グループ会社のインフラをECS/Fargateに移行して振り返る

グループ会社のインフラをECS/Fargateに移行して振り返る

adachin|2020年05月11日
AWS

皆さん元気ですか!?SREチームの@adachin0817です。去年から行っていた移行プロジェクトで、グループ会社である、シクロマーケティング株式会社の「ミギウデ」をさくらVPSからAWSへ移行しました。今回、移行背景やECS/Fargateでのコンテナ運用について簡単にご紹介と振り返りを行ってみたいと思います。

なぜAWSへ移行するのか

AWSへ移行すると冗長性の担保などが挙げられますが、一番は開発環境やインフラなど、すべてランサーズに統一させるということが第一の目的です。それに伴い、ミギウデ自体のサービスがシンプルなインフラ構成ということもあり、インフラ運用の手間をなくしたいということから、ECS/Fargateで初の外部サービスとしてコンテナ運用にチャレンジしてみようとなりました。

目的とコンテナ化にするメリット

・内部統制対応
・S3、RDSを利用したバックアップ
・CloudWatchLogs+αを利用したログの保存
・冗長化による稼働率の向上
・開発効率のアップ
・共通開発環境の構築
・リリースの属人化の排除
・GitHubと連動してリリース(人件費削減)
・運用効率のアップ
・Immutableなサーバーの運用
・タスク単位で自由に割当のリソースが変更できる
・スケールアウトのしやすさ

まずは開発環境から紹介していきましょう。

開発環境

・構成

$ tree dev
dev
├── README.md
├── cyclo-app
│    ├── Dockerfile
│    ├── gemrc
│    ├── nginx
│    │   ├── app.conf
│    │   └── nginx.conf
│    └── supervisor
│         ├── app.conf
│         └── delayed_job.sh
├── docker-compose.yml
├── elb
│   ├── Dockerfile
│   ├── h2o
│   │   ├── conf.d
│   │   └── dev.cyclo-creativecloud.com.conf
│   │   └── h2o.conf
│   └── service.sh
└── mysql
    ├── Dockerfile
    ├── mysql_init.sh
    ├── mysqld.cnf
    └── service.sh

・docker-compose.yml

version: '2'

services:
  elb:
    build: ./elb
    image: cyclo-elb
    hostname: elb
    networks:
      lancers:
        ipv4_address: xx.xxx.xx.xx
    extra_hosts:
      - "dev.cyclo-creativecloud.com:xx.xxx.xx.xx"
    container_name: cyclo-elb-xx-xx
    ports:
      - 80:80
      - 443:443
  app:
    build: ./cyclo-app
    image: cyclo-app
    hostname: cyclo-app
    networks:
      lancers:
        ipv4_address: xx.xxx.xx.xx
    extra_hosts:
      - "dev.cyclo-creativecloud.com:xx.xxx.xx.xx"
    container_name: cyclo-app-xx-xx
    volumes:
      - ~/www/app:/var/www/app/
  mysql:
    build: ./mysql
    image: cyclo-mysql
    hostname: mysql
    networks:
      lancers:
      ipv4_address: xx.xxx.xx.xx
    container_name: cyclo-mysql-xx-xx
    ports:
      - 3306:3306

networks:
  lancers:
  external: true

・ランサーズ開発環境

開発環境ですが、リバースプロキシとして使うELB(H2O)コンテナ、App(Rails)、MySQLコンテナの3つで動作しています。これにより、AWSやランサーズの開発環境(上記図)と同じ構成に仕上がることができました。

このサービスはRailsでできているということもあり、bundle install はコンテナにログインしてから実行しています。また各アプリケーションの起動はSupervisorで管理しました。また、Dockerはプロセスがフォアグラウンドで動いてないと、コンテナが終了するため、フォアグラウンドで実行するように注意しましょう。

・cyclo-app/supervisor/app.conf


[supervisord]
nodaemon=true

[program:app]
command=bundle exec unicorn_rails -c config/dev_unicorn.rb -E development
autostart=true
autorestart=true
stopsignal=TERM
user=root
directory=/var/www/app/
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

[program:delayed_job]
command=sh /etc/supervisor/conf.d/delayed_job.sh
autostart=true
stopsignal=TERM
user=root
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0

[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autostart=true
autorestart=true
stopsignal=TERM
user=root
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

[program:sshd]
command=/usr/sbin/sshd -D
autostart=true
autorestart=true
stopsignal=TERM
user=root

Stg/本番環境

・Stg

・本番環境

・Docker(Stg/Prd)

$ tree
├── prd
│   └── cyclo-app
│       ├── Dockerfile
│       ├── README.md
│       ├── common
│       │   └── prompt.sh
│       ├── gemrc
│       ├── nginx
│       │   ├── app.conf
│       │   └── nginx.conf
│       └── supervisor
│           ├── app.conf
│           └── delayed_job.sh
└── stg
    └── cyclo-app
        ├── Dockerfile
        ├── README.md
        ├── common
        │   └── prompt.sh
        ├── gemrc
        ├── nginx
        │   ├── app.conf
        │   └── nginx.conf
        └── supervisor
            ├── app.conf
            └── delayed_job.sh

Stgと本番環境はすべてTerraformでインフラコード化をしています。これによりStg環境で一度作ってしまえば、本番環境構築に工数がかかることなく、設定変更時のミスが減ります。RDSAuroraを使い、コンテナのログはすべて標準出力をしてCloudWatch logに出力しています。各ログは今までLambdaを利用してS3に保存していましたが、Pythonのバージョンアップ対応でメンテをしたくないことから、今回はAmazon Kinesisを利用しました。他にもAppコンテナ以外にコマンドでRDSへ接続、aws cliの利用や開発者がrails consoleを使いたいため、Appコンテナとまったく同じコンテナであるdevopsコンテナを作成しました。このコンテナはランサーズの踏み台サーバーからSSHでログインできるようにVPC Peeringしています(下記図より)。SSMのセッションマネージャーもありますが、手軽に踏み台からSSHしたいということから、今回はこのような構成にしました。ちなみにECSのタスク定義ですが、AppコンテナはSupervisorで起動し、devopsコンテナはフォアグラウンドでSSHが起動するように制御しています。バッチは元々Railsのwheneverを入れて、schedule.rbファイルで管理していましたが、 ECS Scheduled Tasksを使って実装しました。監視はDatadogを使って、コンテナ内のCPU,メモリーなどのリソースと外形監視を行っています。

・VPC Peering

・コンテナデプロイの流れ

コンテナのデプロイはCircleCIで実装しました。GitHubdevelopブランチにマージするとStgが反映され、masterブランチにマージすると本番が反映されます。CIrspecCDCircleCIのOrbsを利用しており、Orbsがなかった頃はecs-deployを使ってシェルスクリプトを使っていましたが、簡単にDockerfileのbuild、ECRへpushECSリビジョンの更新、サービスタスク定義の更新、DBマイグレーションまで可能になりました。今までCapistranoによってサービスのリリースを行っていましたが、CircleCIによってリリースフローを確立できました。

移行プロジェクトと振り返り

今回3社合同で行われた移行プロジェクトですが、SREチームでコンテナの設計、デプロイなどを実装し、チームの皆様にフォローして頂きつつ、開発チームと連携してサーバーサイドの動きなどをヒアリングしながら進めていきました。また、リモートワークが当たり前になってきた頃には、とにかく通話してコミュニケーションを取るように心がけました。最後にチームで良かったところ、悪かったところを振り返ってみます。

Keep

・モダン技術へのチャレンジをやりきれた
・初めて外部サービスとしてコンテナ運用を行うことができた
・RailsでのECS/Fargateでの知見が高まった
・メンテナンス時にトラブルなく移行できた
・CircleCIのOrbsを使ったCI/CDの知見
・今後他サービスでのCI/CDの検証となった
・Datadogをサイドカーコンテナとして動かした際の知見
・コンテナの監視検証となった
・バッチ置き換えの検証となった
・内部統制対応

・ログの取得
・冗長性の確保
・データベースのバックアップが取れている
・リリースフローを確率できた

Problem

・当初の移行スケジュールが伸びてしまった
・開発環境が不十分なできになっていた
・コンテナの設計とやり直しに時間かかりすぎていた
・突発的なエラーにも時間かかりすぎていた
・デプロイの実装に時間かかりすぎていた

Try

・ECSの他サービスへの適用
・移行スケジュールとタスクを最初から明確にする

まとめ

今回外部サービスとしてコンテナ運用は初めてのチャレンジということもあり、やりきれて非常にいい経験となりました!今後はこの知見を活かしてランサーズもFargate化を計画をしていますので、その時はまたブログします。

※余談
今回の移行プロジェクトで会社で表彰頂きました!ありがとうございました!!