メドピア開発者ブログ

集合知により医療を再発明しようと邁進しているヘルステックリーディングカンパニーのエンジニアブログです。PHPからRubyへ絶賛移行中!継続的にアウトプットを出し続けられるようにみんなでがんばりまっす!

事業拡大に伴いコーポレートサイトのリニューアルを行いました!そして、コーポレートサイトだから色々技術的に挑戦してみました

Introduction

こんにちは、元Sake部長@shinofara(篠原)です。
Golang(Go言語)を採用して、たった二人で基盤となるAPIゲートウェイを開発した話 以来の登場です。

今回調子に乗って、各セクションを英語にしてみたのですが、途中で力尽きてしまいました。。

それはさておき、みなさんお気づきでしたか?
実は2016年11月10にフルリニューアルした、弊社コーポレートサイト(以下、COJP)を公開していました。

f:id:shinofara:20161125205953p:plain:w300

今までと比べると、とても最近感がありますよね!
ちなみに、11月9日時点までのサイトは、以下の画像です。
f:id:shinofara:20161125205635p:plain:w300

デザイン刷新しただけ?の様に思えますが、技術的な観点でも旧COJPと全く違う物になりました!!
今回は、その裏側を紹介したいと思います。
デザイン観点でのブログはこちら

※ちなみに僕がアサインされたのはリリース3週間前

Background of renewal.

今までのCOJPには様々な課題がありました。
あるあるなモノから、メドピアだけのものだとか

  • エンジニアがいないとデザイン修正が出来ない
  • 1台のサーバに WordPress が複数稼働している状態で、冗長構成に出来ない。
  • そもそも WordPress魔改造されていてセキュリティ対応し難いし、脆弱性多発しやすい
    CVEレポートがおおい
  • そもそも構成を理解している人が居ない(外注)為、ローカルで再現出来ず開発出来ない
  • そもそもデザイン自体が古いので、新しくしたい

Renewal overview.

リニューアルに対しての、企画要望と課題、そして対応方法

Requirement.

リニューアルを行う際に、下記の様な要望がありました。

  1. 更新のある記事は、WordPress 等のCMSで入稿したい
  2. TOPページとか、更新がめったにないサイトは、デザイナが直接更新したい
  3. 問い合わせに対して、自動返答+社内に履歴を残したい
  4. 旧社長ブログや、プレスリリースなどのSNSシェア数などは可能な限り引き継ぎたい(可能な限りとはあるが、これは限りなく100%に近い可能な限り)

という感じで、ざっくりまとめると今より色々な面で良くしたいけど、過去は全部引き継いでね♡という感じでした。

Issue.

あがった要望に対して、見えてきた課題もあります。

  1. WordPress 本番運用はセキュリティ的な問題で、NG
  2. 運用者が、FTPクライアントなどでupload出来る場所が必要
  3. 問い合わせ処理と管理は動的な物なので、サーバサイドの仕組みが何かしら必要
  4. 旧サイトのURLは動的URL (https://medpeer.co.jp/?p=1632

How to respond?

そんな課題に対して、今回行った対応です。

  1. ステージング環境に構築した WordPress から本番用S3に静的ファイルの書きだしを行う事で対応する。(StaticPressを利用) *Updateが止まってるのが悩み
  2. S3に専用バケットを作成して、運用者ごとのIAMでAccess出来る様にする
  3. 会社としてRails化を進めているのでPOSTを受けてメール送信を行う処理をRails5で作成
  4. StaticPressを使う関係上URLは、静的な物(https://medpeer.co.jp/blog/1632.html)になってしまう為、この場合でも旧URLのソーシャルスコアを維持できるように対応

Technical point of view

技術的な取り組みとしても幾つか新しい事をやったりもしています。 今回のリニューアルでは、技術的に以下の取組を行いました。

1. All applications are operated by Docker

最近では珍しくなくなってきたDockerをフル活用しています。 Dockerを使う事で、再現性のある環境を共有・展開出来るようになり、とある環境では動かないと言った問題が起こりにくくなります。

2. Blue Green Deployment(Operation not using SSH)

DockerとECSを使う事で、EC2に対してSSHを用いる事無く、コンテナの作り捨てが出来るようになります。 また、イメージが存在していればトラブルが発生していても、過去の動いていたバージョンにロールバックする事も容易に出来ます。

3. Engineerless operation

エンジニアにデプロイ依頼が発生しているとスピード感が損なわれる為、動的部分以外はエンジニア介入しない公開フローと公開できる仕組みを整えました。

4. Infrastructure as Code

Terraformでインフラ構成管理、Dockerでアプリケーションサーバ構成管理を行う事で、環境構築の再現性を確保 また、コード化する事で、PRによるレビューも行い、インフラ事故を軽減

Infrastructure

今回のCOJPでは、開発環境は、 docker-compose で、ステージング、本番は AWS を使っており、更にステージングではWordPressPHPで動いているのに対して、本番ではS3からの静的配信になっています。
その為、各環境で使っている物が少しずつ変わってきます。

各環境毎に利用しているサービスの違い

本番/ステージングがAWSですが、開発環境の構成も可能な限り同じものを用意してます。

要素 本番 ステージング 開発
SSL AWS Certificate Manager AWS Certificate Manager オレオレCA認証SSL証明書
Load Balancer ALB ALB HAProxy Container
WordPress S3 WordPress Container Wordpress Container
Static File Storage S3 S3 Nginx Container
Rails Rails(Puma) Continer Rails(Puma) Continer Rails(Puma) Continer
SMTP SES SES MailCatcher Container
Mail Log Data Storage DynamoDB DynamoDB DynamoDB Local Container
Wordpress Data Storage None RDS (MariaDB) MariaDB Container
Log Cloud Watch Logs Cloud Watch Logs Stdout

How about services provided by AWS?

ALB,DynamoDB,etc..と言ったAWS提供サービスは、ローカルには存在しませんので、 HAProxy, DynamoDB-local, MailCatcherで代用してます。

ALBの代わりに使ってるHAProxyって何?

HAProxy は、簡単に書くと多機能プロキシサーバで、ALBの様にPATH Routingが出来ます。

DynamoDBの代わりに使ってるDynamoDB-localって何?

RDSの場合は、MySQLなどで代用は可能ですが、AWS提供のDynamoDBは代わりが効きませんが、DynamoDB Local が公式提供されていますので、こちらを使います。

SESの代わりに使ってるMailCathcerって何?

MailCatcherは、SMTPサーバとメールクライアントを同時に提供してくれるアプリケーションになります。 開発環境に導入する事で間違えて本番に.....という事故を防ぐ事ができます。

それぞれの環境毎のinfrastructureについて

環境 ツール
本番・ステージング Terraform / ECR / ECS
開発 Docker Compose

本番/ステージングと開発環境では、ECS/ECRとdocker-composeと違いはあるものの 各アプリケーションは同じDocker Imageを使いまわせる為、便利ですね。

全ての環境の構成図

f:id:shinofara:20161115141208p:plain:w300

About AWS configuration

今回はAWSのインフラに関して、更に深く書いてみたいと思います。

Services

  • ALB
  • ECS
  • ECR
  • S3
  • DynamoDB
  • RDS ( stg only )
  • Cloud Watch Logs

Policy

どのように接続したか

EC2から各サービスを利用するにあたって、IAM Userを作るのでは無く、EC2にIAM ROLEを割り当てました。

なぜ?

  1. IAMの管理がめんどくさい
  2. 何かしらの事情で、キーやシークレットが流出したら削除、もしくは変更しないといけない
  3. そもそも秘密情報の管理方法を考えなくてはいけない

など管理面でワズらしい事がおきてしまいます。

EC2に紐付けるとどうなるの?

EC2に紐付けると上記問題は解決できますし、この割当てられたEC2からしか各サービスへのアクションを行えなくなります。 これだとEC2に入れれば許可された範囲で何かできてしまうのでは?となりますが、この点に関してはキーを発行した場合でも同様の事が言える為、別の問題という事になります。

公式ドキュメントはこちら

ECSに対して付与したPolicy

{
  "Version": "2012-10-17",
  "Statement": [
      {
      "Action": "elasticloadbalancing:*",
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Action": "ecs:*",
      "Effect": "Allow",
      "Resource": "*"
    },
    {
    "Action": ["logs:CreateLogStream","logs:PutLogEvents"],
      "Effect": "Allow",
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer",
        "ecr:GetAuthorizationToken"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": [
          "arn:aws:s3:::<Bucket Name>",
          "arn:aws:s3:::<Bucket Name>/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "dynamodb:PutItem",
      "Resource": [
             "arn:aws:dynamodb:ap-northeast-1:<AWS Account ID>:table/<Table Mame>",
      ]
    },
    {
      "Effect": "Allow",
      "Action": ["SES:SendEmail", "ses:SendRawEmail"],
      "Resource": [
             "arn:aws:ses:us-west-2:<AWS Account ID>:identity/<Domain>",
      ]
    }

  ]
}

TerraformとECS、ECR

Terraformでも、ECS/ECRの操作は出来ますが、今回は2重管理は避けたかったので、 ECS Clusterを作成するところまでをTerraform、 ECS Service作成、Task Difinition作成などは、自作のスクリプトで行っています。 理由としては、task.jsonをterraform管理ではなく、デプロイスクリプト管理にしたかったという理由です

Terraform

Terraformは、開発も活発で結構頻繁にバージョンがあがるのですが、たまにバージョンアップの影響で forces new resource と強制再構築されそうになる時がたまにあります。
過去の例としては、0.6.14 から 0.6.15 にあげる際に、 security_groups ではなく vpc_security_group_ids に書き換えないといけない変更が入っていて、plan せずに apply すると再構築に....
今例に上げた問題は、CHANGELOG.md#0615-april-22-2016 にも書かれているので、見ていれば気付けるのですが、見逃すと怖いですね。

aws_instance - if you still use security_groups field for SG IDs - i.e. inside VPC, this will generate diffs during plan and apply will recreate the resource. Terraform expects IDs (VPC SGs) inside vpc_security_group_ids.

更に言えば、複数のサービスに関わっていると、それぞれを実行したバージョンが違ったりと、毎回最新に追従できていれば問題無いのですが、なかなか出来ない事もあります。

ですので、弊社では誰がどこで実行しても、バージョンによる問題が起きないように、Terraform の実行にも Docker を使っています。
Dockerイメージは、Docker Hubで公開されている hashicorp/terraformを使っています。

簡単な使い方は、下記のとおりです。

OPTION=''
docker run --rm -v ~/.aws:/root/.aws:ro -v ${PWD}:/work -w /work hashicorp/terraform:0.7.13 plan ${OPTION}
docker run --rm -v ~/.aws:/root/.aws:ro -v ${PWD}:/work -w /work hashicorp/terraform:latest plan ${OPTION}

その他利用可能なバージョンはこちら

このようにして置くことで、違うバージョンで実行してしまって再構築が走ったりするリスクは回避できます。 ※でも頑張って追従しましょうね。

ECS with ALB

いいツールが見つからなかったので、自作しました。

Finally

このような感じで、COJPのリニューアルが完了しました。 Dockerイメージ管理下の、WordPressや、Railsはエンジニアの手が必要ですが、日々運用に置いてはエンジニアレスを実現する事ができました。
また、全てのコンテンツを静的配信化することで、スケールしやすくもなり、負荷に怯える日々もなくなりました。(S3のお金は...)

それに、ECSを使うことで、厳密にはDockerを使う事で、CVEなどの対応時に、稼働中のサーバ更新・再起動して動くか分からないと怯えるという事があったのが、動く事を保証した上で、イメージ再作成も行えるようになりました。 罠は多いですが、

では、皆さんよきECS/ECR生活を(๑•̀ㅂ•́)و✧