あすたぴのブログ

astap(あすたぴ)のブログ

なぜDockerを使うのかを整理する

※追記 整理とかいいつつ、雑に書きなぐっただけだったので少し文章と見栄えを直しました。

背景

いま作っているシステムではECSのクラスタ上に、Dockerコンテナを配置してアプリケーションをデプロイしている。 なぜ一般的なEC2にアプリケーションを配置する手法を使わずに、ECSでDockerを使用するのか。 自分の考えを整理する。

前提知識

ECSとは

AWSのサービスの1つでEC2ContainerServiceの略。 Dockerコンテナを運用をいい感じにやってくれるサービスです。

TL;DR;

  • アプリケーションの運用はいろいろ大変
  • 運用を楽にするために色々な技術がある
  • Dockerを使うとより楽になる

EC2にアプリケーションを配置する手法

通常EC2を使い際に、AWSが用意しているAMIをそのまま使用することは多くない。 理由としては幾つかあるが、雑に思いついたのはイカ

  • デフォルトでインストールされている言語以外、または別バージョンを使用したい
  • アプリケーションで使用するライブラリをインストールする必要がある
  • インスタンスSSHする際に使う鍵を起動時の設定とは別に用意したい

1つずつ詳しく解説していく。

デフォルトでインストールされている言語以外、または別バージョンを使用したい

いまは知らないが、一昔前のAmazonLinuxであればRubyのversionは2.0だった。 たぶん当時の最新は2.1とか2.2だったと思う。

バージョンがいくつ違うとかはまぁいいとして、重要なのは常に最新に追随するわけではないということ。 Rubyはversionupのたびに高速化している。 いままで最新バージョンを使用していて、既存のアプリケーションが動かなくなるというようなバグに遭遇したことはなく、基本的にはversionは上げ得である。 であれば、最新のメソッドも追加されているしRubyのversionは新しいほうがいい。

アプリケーションで使用するライブラリをインストールする必要がある

すぐに思いつくのはImageMagicとかだろうか。 画像処理を行うアプリケーションであればほぼ入れることになるであろうライブラリである。

インスタンスSSHする際に使う鍵を起動時の設定とは別に用意したい

EC2ではインスタンスSSHするためにkey pairと呼ばれる鍵を作成し、公開鍵をインスタンスに配置してくれる。 ユーザーは公開鍵を自身のマシンにセットし ec2-user とかAMIごとに指定のユーザーとしてSSHをする。

チームで開発する際に、SSHする可能性のあるメンバーには鍵を渡す必要がある。また、このデフォルトのユーザーはroot権限(sudo)を持っているため全員に配布するのはリスクがある。

そこで、インスタンスにデフォルトとは違うユーザーを作成し、そこにチームメンバーの公開鍵を配置する。 そうすることで各メンバーは自分のユーザーを持ち、自分の秘密鍵SSHが可能になる。

デフォルトのCloudwatchのメトリクスとは別にメトリクスを収集したい

Prometheusとか、そういうエージェントを入れることもあるよね。

どのようにEC2インスタンスを作るのか。(設定するのか)

簡単な方法だと、ユーザーデータと呼ばれるインスタンス起動時に実行されるスクリプトがある。ここに各種インストールのコマンドを記述すると起動時に色々インストールしてくれる。 しかし、これだと起動時に言語のビルドなども含まれると異常に起動が遅くなる。(正確に言うとすでに起動はしているので、起動後に自分の使いたいものが揃うのが遅くなる)

その為、一般的にはインスタンスを起動後、SSHして自分好みにセットアップし、AMI(インスタンスのイメージ)に保存する。 保存したAMIから起動することでセットアップされた状態でインスタンスを起動する。この手法を使うことで同じ環境のインスタンスを複数起動することが可能になる。

更に言うとそれらをいちいち手でセットアップするかというとそんな面倒なことはしたくない。その為、Chef、Ansibleなどの構成管理ツールと呼ばれるものを使用し、セットアップを行う。

それも面倒なのでPackerでAMIが自動生成されるようにしたりする。(実はPakcerはあんまりちゃんと使ったことがない)

EC2インスタンスで配置したアプリケーションの運用

アプリケーションを運用をしていく上で、いろいろな機能追加が入ると思う。 またRubyは毎年12月25日に最新バージョンがリリースされる。(バグフィックス等のバージョンアップは随時行われている)

Rubyのバージョンだけなら、アップデートしない。という選択もあるだろう。しかし、先述したようなImageMagickのようなライブラリが追加で必要になった場合はインスタンスにインストールをする必要がある。 運用しているサーバーが1台、2台ならSSHして手でセットアップ、または構成管理ツールでセットアップするという選択肢もあるだろう。(動いているサーバーにそんなことおれはやりたくないけど)

あんまりないけど再起動が必要なアップデートもある(近年ありがちなSSL,SSH脆弱性アップデートは再起動が必要だった気がする)そうなると、1台運用ならサーバーが停止することになり、複数台運用ならローリングアップデートが必要になる。

自分が昔関わっていたサイトでは毎月必ずインフラの定期メンテがあったりしたこともある。そこに併せて色々やる。

Dockerの良さ

ここまでの流れで運用の大変さとそれを回避するためにいろいろな技術があることがわかったと思う。

DockerはDockerイメージを作成しそのイメージからコンテナを生成しサーバーに配置する。 Dockerイメージは、イカのようなDockerfileから生成する。 イカから生成されるイメージはRuby2.4.1がインストールされたalpine linuxのイメージになる。 このイメージをベースに、自分のアプリケーションに必要な設定を追加していくことになる。

FROM ruby:2.4.1-apline

ADD . /app

すでにわかったかもしれないが、Dockerfileを更新し、イメージを再作成、再作成したイメージのコンテナをサーバーに配置する。これがDockerを利用した場合のアプリケーション(サーバー)の更新になる。

ここまでに記述したようなインフラの更新に必要なのは、 Dockerfileの更新、イメージのPush、新しいイメージからコンテナを配置になる。

またDockerは1つのコンテナに複数アプリを起動するようなことはせず、1コンテナ1プロセスが基本です。 EC2インスタンスで行うような1サーバーのリソースを有効に使うために複数アプリを起動したりするために1つのサーバーにいくつもの言語を入れたり、いくつものアプリケーションを起動するようなことはありません。1つ1つのDockerfileは1つのことをするための設定だけになり設定の管理としてもシンプルになりやすいと思っています。 それに加え、ECSクラスタ内のインスタンスにコンテナを配置する際にはMultiAZやリソースを考慮して配置するため、インスタンスのリソースを自動的に有効に使えるようになります。

Dockerを使いやすい環境

ただ、インフラアップデートが楽なだけでDockerを選択するわけではない。 ECSやGKEなどDockerのクラスタ運用を任せられるサービスがあるのが理由としては大きい。 さすがにクラスタの管理まですべて自前で、となると簡単に手が出せるものではなくなってくる。

正直な話し

Dockerになるとすごく楽になる。みたいな雰囲気で書きましたが、ECSではDockerコンテナを配置するインスタンスはEC2インスタンスになります。 面倒な部分が残っている可能性もあります。

今まで書いてきたようなEC2インスタンスと同じ使い方ではありませんが、もしSSHシたい場合は同じように設定が必要です。セキュリティアップデートも必要です。 それくらいなら、最新のAMIに変える。アップデートコマンドを叩く。等なので新しいAMIから起動してユーザーデータで設定で事足りるのでなんとかという考えです。 また、ECSではコンテナ配置時に配置させたくないインスタンスを設定できるため、比較的簡単にローリングアップデートを行うことができます。

A,Bインスタンスがあった場合に、Cインスタンスを起動。Aインスタンスにコンテナを配置しないようにする。 Aインスタンスを破棄。Dインスタンスを起動。 というようにすることで、安全にインスタンスを切り替えることができます。 Dockerコンテナも同じようなやり方で新しいコンテナに入れ替える。

Dockerの関するありがちな批判

デプロイごとにDockerイメージをbuildするの遅くない?

EC2を設定するのと同じように毎回、Ubuntuイメージとかをベースに使って、自分でRubyを入れて・・ みたいな事をしてたらクソ遅いです。 それをしないために、自分で作ったDockerイメージを保存するリポジトリがあります。 公式だとDockerHub、AWSにECRがあります。 アプリケーションに必要な、言語、ライブラリまでインストールしたイメージをリポジトリにPushしておき、 Deploy時はそのイメージをFROMに記述し、実行するアプリケーションのコードを追加、Rubyならbundle installする程度でしょう。bundle installも、Gemfileに更新がなければイメージに最初から含めておけばいいです。 一番早いのは、アプリケーションのコードを乗せるだけ。という使い方です。