[コンテナ再入門]ホストにMySQLがインストールされていると、MySQLのコンテナが起動しないことがある

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門 の4章でMySQLのDockerコンテナを立ち上げる手順があるのだが、そこで詰まったのでメモ。

状況

  • ホスト Ubuntu18.04 server 64bit
  • Docker 18.09.3

Docker in Dockerの構成で、mysql:5.7 イメージを元にしたMySQLのDockerコンテナを起動させようとしたところ、起動しては以下のエラーを吐いてコンテナが停止する、という挙動を繰り返した。

$ docker container exec -it manager docker service lslogs todo_mysql_master
todo_mysql_master.1.(略)    | ERROR: mysqld failed while attempting to check config
todo_mysql_master.1.(略)    | command was: "mysqld --verbose --help"
todo_mysql_master.1.(略)    | 
todo_mysql_master.1.(略)    | mysqld: Can't read dir of '/etc/mysql/conf.d/' (Errcode: 13 - Permission denied)
todo_mysql_master.1.(略)    | mysqld: [ERROR] Fatal error in defaults handling. Program aborted!

「Docker in Docker」「MySQL」「起動しない」といったワードで検索していたところ、こちらの記事を発見。

qiita.com

まんまこのシチュエーションであった。

先に結論

コンテナを実行するホスト(母艦) にMySQLがインストールされていると、priviledged モードを有効にしたコンテナ上でのMySQL起動がAppArmorという強制アクセス制御システムの働きにより阻害されることがある。

何が起きていたか

書籍の手順では、Docker Swarmのような複数台のホストにまたがるコンテナオーケストレーションの挙動を1台のホスト(=読者のノートPCなど)上で再現して体験させるため、Docker in Dockerで「Dockerをホストするためのコンテナ群」の上に「MySQLやNginxなど本来複数台のホストにまたがって展開させたいコンテナ群」を構築することになっていた(3章 3.5.節以降)。
このDocker in Dockerで「Dockerをホストするためのコンテナ群」を構築する際、「Dockerをホストするためのコンテナ群」に本来のホスト(=読者のノートPCなど)とほぼ同じ権限をもたせるためにpriviledged モードを有効にしていたようだ (ようだ、というか、「Dockerをホストするためのコンテナ群」を起動するためのdocker-composeのYAMLファイルにはちゃんとprivileged: trueと書いてあったのだが、あまり意味がわかっておらず読み飛ばしていた。。。)。

docs.docker.com

こちらのドキュメントの「Runtime privilege and Linux capabilities」の項を読むと、priviledged モードというのはコンテナホストのデバイス等にアクセスを許可するためのフラグらしい。これがないと Docker in DockerのようにDockerコンテナ内部からDockerを起動させるような芸当ができない。
基本的にはホストとコンテナ間は疎結合である方が望ましいので、priviledged モードは開発環境以外では使わないものらしい。

AppArmorとは

www.usupi.org

AppArmor とは、プログラム単位でMAC(Mandatory Access Control - 強制アクセス制御) を行うためのセキュリティ機構です。
MACとは、従来のファイルのパーミッションの設定等とは関係なく、 強制的にアクセス制限を設けることができる機構です。

役割としてはSELinuxなどと同じようなもので、AppArmorの場合はプログラム単位で制限がかけられるらしい。 今回は、MySQLがAppArmorの管理対象だったところに Docker in Docker上でさらにMySQLを実行しようとしたため、AppArmorが「許可していないリソース上で MySQL が動作している」と認識して実行をブロックした、ということだったようだ。

対処

前述のQiita記事 Docker で MySQL コンテナーがなぜか起動できない問題 - Qiita に記載されていた2つの解消方法のうち、AppArmor の監視から MySQL を除外する方法を採った。

$ sudo ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/
$ sudo apparmor_parser -R /etc/apparmor.d/usr.sbin.mysqld

教訓

書籍やハンズオンで環境構築を行う場合は、なるべくまっさらなVMなどを用意してやりましょう。

…そうはいっても毎回そう上手く環境を用意できないかもしれないので、後で同じような状況で詰まる人(将来の自分を含む)の役にたてばと簡単にメモをまとめました。

参考にさせていただいたURL

[コンテナ再入門]Ubuntu 18.04 に Docker Compose をインストールする

kmiya-bbm.hatenablog.com

これの続き。

Docker Composeのインストールも公式ドキュメントに沿って進めればよい。

docs.docker.com

  • Githubのレポジトリで最新のバージョンを確認する。

  • 以下のコマンドで最新版のdocker-composeをダウンロードする

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  • ダウンロードしたdocker-compose に実行権を付与する
$ sudo chmod +x /usr/local/bin/docker-compose
  • 自分の環境では /usr/local/bin にPATHが通ってなかったので、公式ドキュメントの通りシンボリックリンクを張った
$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
  • インストールできたか確認する
$ docker-compose --version
docker-compose version 1.23.2, build 1110ad01

よさそう!

[コンテナ再入門]Ubuntu 18.04 に Docker CE をインストールする

今さらもいいところだが、コンテナを活用した開発フローやアーキテクチャについて基礎から再入門している。 Dockerについては、過去にもちょこちょこ触ってみたり社内のサーバで使ってみたりはしていたものの、ちゃんと実運用できるレベルで理解できていなかったので、勧められた以下の書籍で改めて勉強しなおしている。

Docker/Kubernetes 実践コンテナ開発入門

Docker/Kubernetes 実践コンテナ開発入門

せっかくなので勉強のログをメモしておこうと思う。

Ubuntu 18.04 に Docker CE をインストールする

基本的に公式ドキュメントの「Install using the repository」の手順をなぞるだけ。 docs.docker.com

前準備

  • 古いバージョンのDockerをアンインストールする
$ sudo apt-get remove docker docker-engine docker.io containerd runc

レポジトリを設定する

  • まずは apt パッケージのインデックスを更新する
$ sudo apt update
  • 依存するパッケージをインストールする
$ sudo apt install  apt-transport-https   ca-certificates   curl  gnupg-agent   software-properties-common
  • GPG key の登録
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
  • stable(安定版) のレポジトリをセットアップする。( nightly や test といった引数を最後のstable の後に追加すればそれらのチャンネルもセットアップされる模様)
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

Docker CEをインストールする

  • レポジトリを追加したので、もう一度 apt パッケージのインデックスを更新する
$ sudo apt update
  • 最新版の Docker CEをインストールする
$ sudo apt install docker-ce docker-ce-cli containerd.io

Dockerを一般ユーザで実行できるようにする

Docker のコマンドを一般ユーザで実行すると権限が足らずエラーが発生する。

$ docker info
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.39/info: dial unix /var/run/docker.sock: connect: permission denied

これを回避するには、毎回sudoをつけてroot権限で実行するほか、Dockerを実行したい一般ユーザを docker グループに所属させれば良い (と、公式ドキュメントに普通に書いてあった)。

If you would like to use Docker as a non-root user, you should now consider adding your user to the “docker” group with something like:
sudo usermod -aG docker your-user

結果確認

バージョン 18.09.3 がインストールされた。

$ docker version
Client:
 Version:           18.09.3
 API version:       1.39
 Go version:        go1.10.8
 Git commit:        774a1f4
 Built:             Thu Feb 28 06:53:11 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.3
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.8
  Git commit:       774a1f4
  Built:            Thu Feb 28 05:59:55 2019
  OS/Arch:          linux/amd64
  Experimental:     false

Proxyの設定

docs.docker.com

公式ドキュメントを参考に、dockerd (Daemon) にProxyを設定する場合の例。
上記手順でUbuntuにDockerをインストールした場合、systemd でDaemon化されているため、service設定を更新する。

$ sudo mkdir /lib/systemd/system/docker.service.d
$ sudo vi /lib/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTPS_PROXY=http://your.proxy.url:port/"

Proxy設定ファイルを作ったらdockerd を再起動して読み込ませる。

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker.service

Ubuntu 18.04 の日本語版デスクトップ環境を Vagrant + VirtualBox で作成する

Ubuntu 14.04 LTS のサポート期間が2019年4月末で切れるため、それを利用しているプロジェクトではアップデート計画を立てているのですが、いくつかのプロジェクトでは Ubuntu 16.04 LTS を飛ばして Ubuntu 18.04 LTS にジャンプアップする*1可能性が出てきたため、とりあえず Ubuntu 18.04 LTS の開発環境を準備していくことにしました。

普通に開発環境として使うだけであれば、Ubuntu Japanese Team が提供してくださっている 日本語 Remixバージョンを直にインストールするなりVirtualBoxなどにインストールするなりするのが一番はやいし確実なのですが、Windows/Mac/Linux を母艦とする環境間で開発環境のマシンイメージを共有したり移動したりしたいという要件があったりなかったりなので、Vagrant で管理できるようにします。(が、結構手作業が残ってしまったのであまり意味がn...)

前提

母艦となるマシンへのVirtualBoxVagrant のインストールについては完了しているものとします。
まだインストールが完了していない場合は、以下の公式サイトからダウンロード・インストールしてください。 (Windows/Mac/Ubuntu など母艦の環境によってインストールするパッケージファイルや手順が異なるので細かいことは省略します)

最新版にはバグがあったり、VagrantVirtualBox のバージョンの組み合わせによってはうまく動かないことがあったりするので、そういった場合は最新版にこだわらず古いバージョンをインストールし直して確認してみてください。

母艦の環境

以下の手順は私の手元の以下の環境下で動作確認済みです。

手順

流れとしては、

  • Box を選ぶ
  • Vagrantfile を作成する
  • 起動する
  • 日本語環境化を仕上げる

となります。

Boxを選ぶ

Ubuntuのboxを探すのであれば、以下のレポジトリ(?)から探すのが良いでしょう。

今回は chef社公式の bento/ubuntu-18.04 を使います。
https://app.vagrantup.com/bento/boxes/ubuntu-18.04

Vagrantfileを作成する

Vagrantを実行する母艦にて Vagrantfileを作成します。
肝は日本語化のための仕込みをプロビジョニングスクリプトに入れ込んでいる点で、その他は特に変わった内容はありません。 ホスト名の他、メモリの搭載量やネットワークの設定、Proxy設定の有無などは母艦の環境などによっても異なるためこの記事では省略しています。

# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  config.vm.box = "bento/ubuntu-18.04"
  config.vm.provider :virtualbox do |vb|
    vb.gui = true  # デスクトップ環境を使うので、GUIを有効にする
    vb.customize [
      "modifyvm", :id,
      "--vram", "256",
      "--clipboard", "bidirectional",
      "--accelerate3d", "on",
      "--hwvirtex", "on",
      "--nestedpaging", "on",
      "--largepages", "on",
      "--ioapic", "on",
      "--pae", "on",
      "--paravirtprovider", "kvm",
    ]
  end

  # 初期構築スクリプト
  config.vm.provision :shell, :inline => <<-EOS
    # 日本語化 https://www.ubuntulinux.jp/japanese のための準備
    wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | sudo apt-key add -
    wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | sudo apt-key add -
    sudo wget https://www.ubuntulinux.jp/sources.list.d/bionic.list -O /etc/apt/sources.list.d/ubuntu-ja.list
    sudo apt update -y
    sudo apt-get upgrade -y
    sudo apt-get install ubuntu-desktop -y
    sudo apt-get install ubuntu-defaults-ja -y

    # タイムゾーンを日本時間に変更
    sudo timedatectl set-timezone Asia/Tokyo
    # デフォルトのエディタを nano から vim に変更
    sudo update-alternatives --set editor $(update-alternatives --list editor | grep 'vim.basic')

EOS
end

起動する

Vagrantfile を作成したディレクトリで vagrant up コマンドを実行します。
初回は box のダウンロードやデスクトップ環境のインストールなどがあるためものすごく時間がかかりますが、しばらく待てば終わりますので、同じディレクトリで vagrant reload コマンドを実行し仮想ホストを再起動します。
上手く行けばここまでで Ubuntu 18.04 の仮想ホストの GUI が起動したはずです(ただし英語)。
vagrantユーザ(初期PW:vagrant) でログインし、vagrantユーザのパスワードを変更するなり、自分用のログインアカウントを作成しておくなりしてください。

日本語環境化を仕上げる

ここまでは母艦で作業してきましたが、ここからは起動した仮想ホストのGUIで操作を行います。
Vagrantfile 内の初期構築スクリプトにて、Ubuntu Japanese Team が提供する日本語用パッケージをインストールするところ(https://www.ubuntulinux.jp/japanese)までは完了していますので、あとはそれらを適用・有効化していくだけです。

日本語化に関してはこちらの記事を大変参考にさせていただきました。ありがとうございます。

kledgeb.blogspot.com

  • 使用する言語を日本語に設定する
    • デスクトップ画面左上の「Activities」をクリックし「Language Support」を検索して起動します。
    • 「The language support is not installed completely」というダイアログが表示されるので、「Install」を押してインストールします(root権限が必要なため、パスワードの入力を求められます)。
    • インストールが完了したら、「Language Support」のダイアログが表示されているので、表示されている「Language」タブの「Language for menus and windows:」のリストから「日本語」を探してリストの一番上になるようにドラッグします。
    • 「Apply System-Wide」ボタンを押してシステム全体に適用します(root権限が必要なため、パスワードの入力を求められます)。
    • 「Keyboard input method system:」が「IBus」になっていることを確認してください。
    • 続いて「Regional Formats」タブに移り、「Display numbers, dates currency amounts in the usual for:」を「日本語」に変更したら、「Apply System-Wide」ボタンを押してシステム全体に適用します(root権限が必要なため、パスワードの入力を求められます)。
  • 言語設定の確認と入力ソース(キーボードレイアウトと言語)の追加
    • デスクトップ画面左上の「Activities」をクリックし「Settings」を検索して起動します。
    • メニューから「Region & Language」を選択します。
    • 「Language」が「Japanese」、「Formats」が「Japan」になっていることを確認してください。
      • もしそうなっていなければ、それぞれの項目をクリックして選択肢から日本に該当するものを選択してください
    • 「Input Sources」の「+」ボタンを押して「Japanese」->「Japanese (Mozc)」を追加してください。
  • 日本語入力できるようにする
    • デスクトップ画面の右上にある言語インジケーター(おそらく「en▼」と表示されている)をクリックし、表示されるメニューから(Japanese (Mozc))を選択してください。
  • ここまで来たら、一度仮想ホストのOSを再起動する
    • 起動すると、「Downloads」「Desktop」などデフォルトで作成されるホームディレクトリ下のディレクトリ名を日本語に更新するかを尋ねるダイアログが表示されますが、更新しない(「次回から表示しない」にチェック&「古い名前のままにする」を選択)ことを推奨します。
    • かなと英数の切り替えは「半角/全角」ボタンで行います。

これで一通り表示が日本語化され、日本語入力が可能な状態になっているはずです。

まとめ

  • Ubuntu 18.04 LTS をVagrant + VirtualBox で作成する手順をまとめました
  • が、日本語化する設定が自動化できていないのでイマイチ

*1:標準化のために Ubuntu 14.04 LTS で開発しているものの、まだPoC段階で一般公開までにはまだまだ時間がかかりそうなプロジェクトなど。普通に一般公開しているサービスではこんな冒険はできません...

AWS DevDay Tokyo 2018 Software Design/Serverless & Mobile トラック+α まとめ

(2018/11/12 更新) Amazon Web Services ブログで資料が公開されましたので、末尾にリンクを追記しました。

AWS DevDay Tokyo 2018 が10/29-11/2 の5日間に渡って行われました。 私は10/31(水)のみ現地で参加し、11/1(木),2(金) はライブストリーミングでちょこちょこ参加しました。 aws.amazon.com

非常に内容が濃く、素晴らしいイベントでしたので、10/31(水)のSoftware DesignトラックとServerless & Mobile トラックについて自分の聴講メモと公開された資料をまとめて残したいと思います。

10/31(水) Software Design トラック

セキュア開発プロセスアジャイル開発に適用するには

講師: 徳丸 浩 さん(EGセキュアソリューションズ株式会社代表、独立行政法人情報処理推進機構IPA)非常勤研究員)

アジャイルのような段階的に機能を作り込んだり追加していったりするプロセスにおいて、セキュリティ施策をどう組み込んでいくか現実的な落とし所を考えるセッションでした。

聴講メモ

  • アジャイル開発とセキュアプログラミングは相性が悪い?
  • セキュリティ施策を分類する
    • セキュリティ方針決定系や教育系、予算確保は1回のみ
    • それ以外は繰り返す → 自動化したい
  • セキュリティ施策の優先度
    • エアバッグのない自動車は販売できる(法令上は)が、ブレーキのない自動車は販売できない
    • ログイン機能のないWebサイトはリリースできないが、二要素認証がないWebサイトはリリースできる
  • アジャイル型開発での脆弱性対処
  • 脆弱性診断をインクリメンタルに実施する
    • アジャイル開発の場合は新規にコミットしたソースコードが対象になる
    • リリースごとにサイト全体の診断をするのは現実的ではない
    • が、リグレッションの可能性もある
    • → 汎用の脆弱性診断ツールを用いる(OWASP ZAPのAPIを活用)
    • 汎用のテストツールで脆弱性診断できないか?
    • 継続的テストに特化したツール・サービスを用いる
    • ソースコード診断ツールを用いる
      • 高価
      • 誤検知が多い
  • プラットフォームのセキュリティ
    • OpenVASやNessusで自動化
  • デモ: OWASP ZAP で脆弱性診断したときのURLをPHPUnitで叩いて単体テスト脆弱性診断を組み込む
  • が、ツールによる診断では十分とは言えない

ドメインモデリングの始め方

講師: 加藤 潤一 さん(ChatWork株式会社) speakerdeck.com

あなたの推しテクをもっと伝えるプレゼンテーション術

講師: 長沢 智治 さん(エバンジェリズム研究所 代表) speakerdeck.com

The Twelve-Factor App で考える AWS のサービス開発

講師: 千葉 悠貴 さん、畑 史彦 さん(アマゾン ウェブ サービス ジャパン株式会社 ソリューションアーキテクト)

外部に依存したコードもテストで駆動する

講師: 和田 卓人 さん(タワーズ・クエスト株式会社 取締役社長、プログラマテスト駆動開発者)

Alexa Skillのコードを元にTDDでコードを改善していくセッションでした。

speakerdeck.com

聴講メモ

  • Alexa Skillのフレームワークに依存している
  • 網羅性はいらない。脳天気な正常系(Happy Path)でいいので動くテストがほしい
    • alexa-skill-test-framework
      • 高機能かつ抽象化されており、かゆいところに手が届きにくい
      • Easy指向
    • aws-lambda-mock-context
      • 機能が少なく、レイヤが薄い
      • Simple指向
    • 今回はaws-lambda-mock-contextを採用
  • ランダムな応答を返すもののテストはどうやるか?
    • レガシーコードのジレンマ
    • 接合部(Seam)を作る
    • 固定値を外からねじ込めるように書き換える
  • テストで品質は良くならない
    • テストは品質がよくなるためのきっかけ
  • モデルを分離する
    • Lambdaレベルのテストをグリーンに保ちながら、ロジックをモデルクラスに引き剥がしていく
    • モデルクラスはテスト駆動開発で新規開発する
  • 環境依存を減らす
  • アーキテクチャを定める
    • 安定依存の原則 … 変化しやすいものに依存しない
    • 情報 を扱うレイヤと 事実 を扱うレイヤを分ける

日経電子版におけるPWA活用事例

講師:安田 竜 さん (株式会社日本経済新聞社

実践!リーンなプロダクト開発 ~リーンでアジャイルなUI/UXデザイン検証~

講師: 奥原 拓也 さん (dely 株式会社) speakerdeck.com

10/31(水) Serverless & Mobile トラック

浸透するサーバーレス、実例に見るユースケースと実装パターン

講師: 杉 達也 さん (アマゾン ウェブ サービス ジャパン株式会社 事業開発マネージャ (サーバーレス担当))

Serverless Application Security on AWS

講師: 桐山 隼人 さん(アマゾン ウェブ サービス ジャパン株式会社 技術統括本部 ソリューションアーキテクト)

AWS Well-Architected Framework の Serverless Applications Lens を読み解くセッションでした。

聴講メモ

  • セキュリティの柱
  • IDとアクセス管理
    • APIアクセスの認証認可をどのようにしていますか
      • Cognito User Pools Authorizer
      • AWS IAM Authorization
      • Lambda Authorizers
    • LambdaファンクションがアクセスできるAWSサービスをどのように保護していますか
  • 発見適当性の考慮事項
    • サーバーレスアプリケーションのログをどのように分析していますか
      • アプリケーションログはCloudWatch Logsをつかう、とか
      • AWSサービスAPI呼び出しは CloudTrailをつかう、とか
      • ログに機微情報が含まれる可能性、とか
    • アプリケーションの依存関係や脆弱性をどのように監視していますか
  • インフラストラクチャー保護の考慮事項
    • LambdaファンクションがアクセスできるVPC内のAWSリソースに関して、どのようにネットワーク境界を保護していますか
      • ネットワークACLやセキュリティグループによるネットワーク境界保護
  • データ保護
    • サーバーレスアプリケーション内の機微な情報をどのように保護していますか
    • どのように入力値チェックをしますか
      • 通信データの暗号化
      • クライアントサイド暗号化
      • 保護すべきデータの外部化
        • AWS SystemManager Parameter Store
        • AWS Secrets Manager
        • Secrets Manager を使える場合はSecrets Managerを使うのがオススメ
  • インシデントレスポンスの考慮事項

開発者におくるサーバーレスモニタリング

講師: 小梁川 貴史 さん(アマゾン ウェブ サービス ジャパン株式会社 技術統括本部 ソリューションアーキテクト)

AWS Lambdaを中心にmetricsの意味や監視ポイントを解説するセッションでした。

聴講メモ

  • 自分が作ったものがどう使われているのか・動いているのかを把握できることが重要
  • AWS Lambdaの起動パターン
    • イベントベース
      • API Gateway
      • S3
      • など
      • イベント数:Lambda = n:n
    • ストリームベース
      • Kinesis Streams
      • DynamoDB Streams
      • ストリームのシャード数:Lambda = 1:1 -シャードの追加に自動追従 SQSベース
      • SQS
      • log polling型、Queueの数に応じてLambda起動数がAutoScaling
  • duration の意味
    • ENIの作成~ランタイム起動・初期化 までの時間はdurationに含まれない
  • SQS起動における注意
    • メッセージ数 > 処理量 の傾向が続いて処理が追いつかない状況になるとコンカレンシーリミットまでスケールする
    • コンカレンシーリミットを設定していないと、アカウントの上限までスケールして他のLambdaに影響を与えてしまう
  • AWS X-Ray
  • CloudWatch Logs filter pattern
    • 大文字・小文字は区別される
    • 正規表現は利用できない
    • 簡単なパターンマッチのみ利用可能
    • 検索しやすいログ設定を!
    • Amazon Elasticsearchに投げたりLambda経由でS3に投げて解析したりとか

ZOZOTOWN の膨大な画像ファイルを移行したときの過程の話(オンプレミスからクラウドへ)

講師: 柴田 翔 さん(株式会社ZOZOテクノロジーズ《旧:株式会社スタートトゥデイテクノロジーズ》ZOZOバックエンド部ネットワークブロック)

数億点の画像ファイルをS3に移行した事例についてのセッションでした。

聴講メモ

  • 移行背景
    • 容量不足
      • ZOZOUSED(中古=同じ商品が無い)のサービスが好調
      • 画像数億点 約60TB (オリジナル画像+リサイズ画像)
    • 監視したくない
      • 逼迫の恐怖
      • 画像アップロードの量が多い
        • オンプレミス画像サーバが耐えられない
      • オンプレミスの保守
      • 夜間アラート
        • 夜間アラートの60%が画像サーバ関連
    • スケーラビリティが必要
      • サービス拡大による画像アップロード数の増加
  • なぜS3なのか
    • 高い耐久性、容量制限がない
    • 公開ナレッジが多い
    • AWS SAの手厚い技術的サポート
  • 移行する上で重要視したポイント
    • 運用コスト削減のため、なるべくマネージド
    • 画像データの容量を気にしなくていい
    • 短期間での移行のため、開発対象を少なくし、テスト工数も削減したい
  • 画像移行パターン
    • パターン1:Snowballでの移行 → ボツ
      • 転送速度の計算
        • 約60TB
        • 平均オブジェクトサイズ 128KB
        • 転送速度 10MB/s
        • → 72日
      • 暗号化に時間がかかる
      • 出す・入れる で2倍
      • 移行用クライアントが必要
      • オフライン同期のため、差分同期が面倒
    • パターン2:S3に直接PUT → ボツ
      • 転送速度を計算
        • 転送速度 42MB/s(バッチ3並列の実測値)
        • → 17.3日
      • 転送速度4倍
      • Snowballの手配が不要に
    • パターン3:オリジン画像のみS3直接PUT、リサイズはLambda → 採用
      • 転送速度を計算
        • 約30TB(オリジナル画像のみ)
        • 転送速度 42MB/s(バッチ3並列の実測値)
        • → 8.6日
      • Snowballの8倍、全画像PUTの2倍 リサイズ用オンプレサーバ不要に
  • 移行後の構成
    • 1次受けバケット → Lambda → オリジナル画像、リサイズ画像バケット
    • オリジナル画像のコピーとリサイズ処理が終わったら1次受けバケットから画像を消す
    • 破損した画像ファイルや何らかの事情でLambdaが起動しなかった画像ファイルが1次受けバケットに残る
  • ポイント
    • Lambdaの修正の楽さ
      • 属人化が排除できた
    • 公開ナレッジの多さ
    • 画像変換用サーバが不要に
  • 移行過程で発生した問題
    • S3バケットの制限
      • S3 GET制限とPrefix
        • 800GET/sec
      • 現サーバへのリクエスト数(オリジナル画像+リサイズ画像)
        • 795GET/sec
      • バケットを分けてGET数を分散
      • S3 GET/PUTのパフォーマンス向上 のリリース
        • → クリア

クックパッドの動画事業での AppSync 活用事例 - Firebaseからの移行 -

講師: 渡辺 慎也 さん (メディアプロダクト開発部部長 CookpadTV株式会社取締役 CTO)

AppSyncをMicrservicesの一部として組み込んで使う事例のセッションでした。 speakerdeck.com

聴講メモ

  • コメントやハートの実装
    • Firebase Realtime Databaseを使っていた
    • 当時はまだAppSyncが東京リージョンに来ていなかった
  • 移行のモチベーション
    • データをAWSとFirebaseに分散させたくなかった
    • AppSyncはDynamoDBやLambdaなどコントローラブルな点が多い
  • WRITEはサーバー経由、READは直接
    • コメントはNGワードのフィルタリングなどをかけたいので
    • スタンプはポイントが必要なのでチェックが要る
  • AppSyncのボトルネック
    • Cognitoの制限
      • Cognitoの GetCredentialsForIdentityの同時実行数
      • API Keyを利用すれば回避できる
    • Mutation per second制限
    • アプリへのファンアウト数
      • 細かいメッセージをたくさん飛ばすより、ある程度バッファリングした方が良い
  • Service Mesh
    • SDK実装ではなくSidecar Proxy(Envoy)経由で通信する
  • cookpadTV message and MBR
    • メッセージ送信サービスはGoで実装
    • MBW ... Message Buffer Writerの略
      • AppSyncへの mutations per seconds を軽減させるため
      • 47分の放送時間中にハートが150万回送信された
        • 単純計算すると平均 532rps
      • 設計方針
        • コメントの反映は送らせない
        • 順序保証は行わない
        • 最悪データのロストを許容
          • 永続化は別で行っている
          • リアルタイ性がないコメントはあまり意味がない
          • ストレージ不要でオンメモリ処理が可能
        • Subscribeで流れてくるメッセージは番組毎
  • AppSync Shcema
    • 複数 Subscriptions
      • コメント、ハート、スタンプ、ユーザ数 はそれぞれ別 Subscription
    • 受信データは配列
      • サーバ側でバッファリングできるように
  • AppSync Resolver
    • 当初DynamoDB Data sourceを使っていたがwriteがボトルネック
    • 結局 Data source type None にした
  • サーバサイドからAppSyncに書き込むことが想定されてないのでSDKなどがなく、自前で書いた(Go)

Serverlessを極めるためにDynamoDBデータモデリングを極めよう

講師: 照井 将士 さん (株式会社サーバーワークス Solution Specialist)

AWS上のServerlessアプリケーションのデータストア層として採用されることが多いDynamoDBにおけるモデル設計についてのセッションでした。 speakerdeck.com

  • DynamoDB Best Practicesを読もう
  • Partition Keyのハッシュでパーティションが分散される
    • なので、Partition Keyに連番を振ってもあまり意味がない
    • 論理的に一意であればOK
  • DynamoDB内のデータは Partition Keyで分散した中で Sort Keyで整列 しているというイメージを持つ
  • 結果整合性 って大丈夫なの?
    • RDBでもリードレプリカから読み込む場合は結果整合性なので、変わらない
  • DynamoDBの設計アプローチとRDBの設計アプローチ
    • RDB
    • DynamoDB
      • スキーマレス
      • 非正規化
      • まずアクセス(使われ方)を考えて、それに合わせたデータを作る
      • アプリケーションとデータモデルは同時に設計する
  • ACIDトランザクションが無い問題
    • ACIDとは 原子性・一貫性・独立性・永続性
    • DynamoDBの場合、非正規化して1つの実体について1つのアイテムに収める
    • 1アイテムの更新はアトミックであり原子性は保たれる
  • 非正規化してあるので読み込みツラい問題
    • CQRS (Command Query Responsibility Segregation)
    • 書き込むデータと読み込むデータは同じである必要は無い
      • 結果整合性を受け入れて非同期で読み込みデータを作る
    • Command の完了をもってQuery用のデータを作る
      • マテビュー的な
    • とはいえ、複数の実体にまたがるトランザクションはできない

その他

ライブストリーミングで以下のセッションを聴講しました。
どれも素晴らしかったですが、DynamDB 関連のセッションが特に最高でした。

Amazon DynamoDB Advanced Design Pattern

講師: 江川 大地 さん(アマゾン ウェブ サービス ジャパン株式会社 技術統括本部 ソリューションアーキテクト)

DynamoDBの設計についてのセッションでした。

www.slideshare.net

おそらく昨年の re:Inventの以下↓ のセッションが基になっているのだと思うのですが、元のセッションも素晴らしかったですし、説明の順序が改善されて日本語になった本セッションは今まで聞いたDynamoDBの説明の中で一番わかりやすいと感じました。

www.youtube.com

DynamoDB Backed なテレコムコアシステムを構築・運用してる話

講師: 安川 健太 さん(株式会社ソラコム CTO)

あのSORACOMのバックエンドでDynamoDBをどのように使っているかのセッションでした。
複数の値を一度に更新したい場合の対処法 で紹介されていた方法は初見で、直接情報をアイテムに残すことが難しければデータ(事実)を記録してあとで必要な情報が生成できるようにすればよい、と理解しました。

www.slideshare.net

マイクロサービス時代の認証と認可

講師: 都元 ダイスケ さん(クラスメソッド株式会社 事業開発部 シニアシステムアーキテクト

認証と認可とは何か、から始まってよくあるWebシステムで必要とされる認証・認可について考えるセッションでした。
後半Twitchの中継が止まってしまって聞けなかったのが残念でした…

www.slideshare.net

さいごに

時間がかぶった関係で観られなかったセッションも多かったので、動画の公開が楽しみです!

Amazon Web Services ブログで公開された資料

aws.amazon.com

aws.amazon.com

Mackerel の1分粒度メトリックデータをCSVでバックアップする

私が現在運用しているシステムではシステム・サービス監視やキャパシティ管理のツールとして Mackerel https://mackerel.io/ というSaaS型のサービスを使っています。 各サーバのCPU利用率やメモリ利用率といったホストメトリック情報はもちろん、特定のサーバには紐付かない情報、例えばサービス利用ユーザ数やお問い合わせの件数といった値もサービスメトリックとしてMackerelに投稿し、Mackerel上でグラフ化したりしきい値を設定してアラートを上げたりできるので便利です。

サーバについてはパブリッククラウド上でインスタンスがぽこぽこ入れ替わるので、ホストメトリックを永続的に保管したい要求はあまりないのですが、ユーザ数の推移といったサービス運営状況に関わるデータは Mackerelのデータ保持期間( 2018年10月13日現在では460日間) を超えた長期間分のデータを確認したい場合があるため、常にMackerelと自分たちで管理できる他のDBの両方にデータを登録し続けるか、データを登録するのはMackerelのみにして定期的にMackerelからデータをバックアップするか、といった対処が必要になります。

今回は後者の、Mackerelから定期的にメトリックデータをバックアップする方法について調べてみました。 といっても、サービスメトリックもホストメトリックもデータを取得するためのAPIが公開されていますので、それをいかに効率よく叩くかだけを考えればOKです。

mackerel.io mackerel.io

APIドキュメントを参考にパラメータを変えながらAPIを叩いていると、from と to で指定するデータ取得対象期間の長さによって取得できるメトリックデータの粒度が変わることを発見しました。

例えば from = 1538319600 (epoch秒。日本時間で2018/10/01 00:00:00) 、to = 1538362800 (日本時間で2018/10/01 12:00:00) として12時間分のデータを取得した場合は60秒間隔のメトリック値が返ってきますが、from = 1538319600 (日本時間で2018/10/01 00:00:00) 、to = 1538406000 (日本時間で2018/10/02 00:00:00) として24時間分のデータを取得した場合は300秒間隔のメトリック値(おそらく60秒間隔のメトリック値5個分の平均値) が返ってきていました。1年前から今日までの1年間分データを取得した場合は86,400秒間隔、つまり1日間隔のメトリック値が返ってきました。

そこでもう少しいろいろ試して調べてみたところ、どうもサービスメトリック、ホストメトリック共に、以下のような仕様になっているようでした。

取得できるメトリックデータの粒度(秒) 最大データ取得期間(秒) 備考
60 72,239 1,204分(≒20時間強)未満までは1分粒度のデータを返す。
300 599,999 1,204分以上 10,000分(≒7日間弱)未満では5分平均のデータを返す。
600 866,999 10,000分以上 14,450分(≒10日間強)未満では10分平均のデータを返す。
3,600 7,199,999 14,450分以上 120,000分(≒2,000時間≒12週間弱)未満では1時間平均のデータを返す。
7,200 14,399,999 120,000分以上 240,000分(≒4,000時間≒約5.5ヶ月間)未満では2時間平均のデータを返す。
14,400 20,807,999 240,000分以上 346,800分(=5,780時間≒約8ヶ月間)未満では4時間平均のデータを返す。
86,400 20,808,000~ 346,800分以上の期間のデータを要求すると1日平均のデータを返す。

サービスメトリックで記録するようなデータのうち、ユーザ数の推移などは長期期間で眺める場合は1日間隔のデータで全く問題ないのですが、もし仮に最も細かい粒度である1分間隔のメトリックデータを長期期間分保存しておかなければならなくなった場合はどうやるのが良いか、を考えてみました。
上の表で見てわかるとおり、fromからtoまでの間隔が 72,239秒以内であればAPIは1分間隔のメトリック値を返してくれるので、このfrom とto を72,239秒づつずらしながらAPIを叩くようなバッチを作れば良さそうです。
また、APIのレスポンスはJSON形式なのでそのまま非エンジニアである企画・営業系のメンバーに共有しても集計やグラフ化などを行うにあたって扱いにくいため、CSV形式で保存できるようにします。

というわけで書いてみたスクリプトがこちら。curl コマンドと jq コマンドがインストールされている必要があります。

gist.github.com

いたってシンプルに、curlAPIを叩いてそのレスポンスをjqでCSVに変換する という処理をwhile文で繰り返しているだけです。 ここではMackerel のAPI Keyや各種パラメータをコード内にべた書きにしていますが、実際は環境変数コマンドライン引数として渡せるようにしてcronで定期実行させるのが楽かと思います。

あとは注意点として、2017年12月1日以前のデータについては1分粒度のメトリックデータは取得できないようです。

mackerel.io

Ubuntu14.04にCloudMapperをインストール

同僚が社内チャットで教えてくれたCloudMapperが面白そうだったのでインストールして試してみました。

www.publickey1.jp

インストールするまで微妙にハマったのでメモを残します。

CloudMapper を動かすまで

$ sudo apt-get install autoconf automake libtool python-dev jq python-pip
  • AWS CLI のインストール
$ sudo pip install awscli
$ aws configure  
AWS Access Key ID [None]: (各自の環境に応じて)  
AWS Secret Access Key [None]: (各自の環境に応じて)   
Default region name [None]: (各自の環境に応じて)  
Default output format [None]: (各自の環境に応じて)  
  • CloudMapperのインストール
$ git clone https://github.com/duo-labs/cloudmapper.git
$ cd cloudmapper/
$ pip install -r requirements.txt
  • 後々必要になるpipモジュールのインストール
    • この後の手順でいろんなPythonスクリプトを実行するが、pipライブラリが足りなければその都度エラーで怒られるのでインストールしていく
    • 自分の環境の場合、以下の3つを入れたら最後まで進めました
$ sudo pip install boto3 pyjq netaddr
  • AWSのアカウント情報をCloudMapperの設定ファイルに書く
    • config.json.demo を config.json にコピーして自分のAWSのアカウントIDに書き変える
    • name は自由につけてOK。例えば dev とします
    • ciders のところはよくわからず。変更しなくても動作はしてるっぽい?
$ cat config.json
{  "accounts":
    [
        {"id": "xxxxxxxxxxxx", "name": "dev", "default": true}
    ],
    "cidrs":
    {
        "1.1.1.1/32": {"name": "SF Office"},
        "2.2.2.2/28": {"name": "NY Office"}
    }
}
  • AWSアカウント内の構成情報を集める
$ python cloudmapper.py gather --account-name dev
  • CloudMapper表示用にデータを整形する
$ python cloudmapper.py prepare --account dev
  • Web サーバを立ち上げて公開する
$ python cloudmapper.py serve
  • これだとローカルホストから http://127.0.0.1:8000 でならアクセスできるものの、リモートからアクセスできない。 リモートからアクセスする場合は --public オプションをつける。
$ python cloudmapper.py serve --public

結果

f:id:kmiya_bbm:20180223171914p:plain

ちゃんと情報取れてそう! 吐き出された図はそのままでは見にくいところもあるので、手でグリグリ修正するほうが良さそうです。