Glide Note

glidenote's blog

「Docker/Kubernetes 実践コンテナ開発入門」がProduction環境でDockerを運用している人間にも良書だった

技術評論社様、著者の山田さんからご恵贈いただきましてありがとうございます。

かなり分厚い本で、内容も濃いので読むのに時間がかかりましたが簡単に感想を書いていきます。

TL;DR

  • Production環境でDockerを利用している人でも学びが多い一冊
  • Kubernetesに対応したDocker for Mac/Windowsが2018年07月に正式リリースされ、手軽にKubernetesが動かせるようになったいま、Kubernetesの初学書として最適
  • 実際にDockerでサービスを運用してきた著者ならではの視点で語られているコラムが非常に興味深い

前提

書評の前に私のDocker歴について簡単に書いておきます。

  • JAWS-UG コンテナ支部で登壇経験あり
  • Dockerに関する基本的な知識は習得済み
  • Amazon ECSを利用して2年ほど前からProduction環境でDocker運用中
  • Kubernetesは理解不足
  • 役職が変わってここ1年くらいからDockerと周辺技術の最新情報がキャッチアップ出来てない 😓

目次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.Dockerの基礎
2.Dockerコンテナのデプロイ
3.実用的なコンテナの構築とデプロイ
4.Swarmによる実践的なアプリケーション構築
5.Kubernetes入門
6.Kubernetesのデプロイ・クラスタ構築
7.Kubernetesの発展的な利用
8.コンテナの運用
9.より軽量なDockerイメージを作る
10.Dockerの様々な活用方法

Appendix-A セキュリティ
Appendix-B Dockerでの開発を支援するツール・サービス
Appendix-C 主要コマンドまとめ

書評

Docker

Dockerと周辺技術は変化の速度が非常に早く、情報キャッチアップのコストが高く、Web上にある情報もすぐ陳腐化して大半が役に立たないので、 私はかねてから公式ドキュメントを読むのが最も良いというスタンスだったのですが、 本書は公式ドキュメントの内容はもちろんカバーされており、運用周りに関しては公式ドキュメント以上の内容が書かれている。

Kubernetes

Kubernetesに関しては一番丁寧に手厚く解説されているので、Minikubeで一度挫折したことがある私にとっては、 サンプルコードを書いてサクサク進めることが出来たので、Kubernetesの採用の足がかりになりそうです。 先月リリースされたDocker for Mac/Windows(Stable)がKubernetesに対応しており、 手軽にKubernetes環境が用意出来るようになったので、Kubernetesをこれから始める人にも最適かと。

コラム

またコラムが大量にありどれも素晴らしいのですが、特に

  • 環境変数を積極的に使う
  • 認証情報をセキュアに環境変数へ設定する
  • Alpine Linuxベースのイメージを採用するべきか否か
  • DockerはVagrantの代替となるか?

においては、以前エンジニア同士で議論したことがある内容なので大変興味深かった。

コンテナの運用

運用部分は普段Amazon ECS上で運用しておりAWSに頼り切っている部分なので、 ログの保管、障害対策については、他社事例として大変参考になった。

本書を読むにあたって

本書のサンプルコマンドを実行するにあたり、BashやZshを事前に設定しておくことをオススメします。これがあるとないとでは効率が段違いです。 本当に重要です!!

まとめ

2018年においては、

を読めば、Docker/Kubernetesに関しては十分なのではないかと思う。

実際私も社内のエンジニアにも「Docker/Kubernetesはこれを読めばOK」と薦めています。

大変オススメです。

参考

GoReplayを導入して、Production環境へのリクエストを複製し、Staging環境に転送する仕組みを作った

結構前に作っていたんですが、いろいろと忙しくてブログに書くタイミングを失していたので年末のタイミングで紹介。

TL;DR

  • GoReplayを利用して、Production環境のリクエストを複製し、Stagins環境、開発環境に投げる仕組みを作った
  • インフラ構成の大きな変更無しで、手軽にProduction環境の実リクエストを複製し、開発、動作検証ができるようになった
  • 2016年の弊社サービスのDocker化や、インフラ構成の大幅な変更、ミドルウェアのアップデート、アプリの改修時のバグ事前検知と事故防止に大いに役に立った

GoReplayの説明

  • Goで書かれており、バイナリを設置し、オプションを指定し実行するだけで動作する
  • アプリが稼働しているサーバで動く。(例えばNginx+Railsが稼働しているサーバで一緒にGoReplayを動かす感じ。)
  • libpcap を利用しているので、tpcdumpと同じでデータリンク層(OSI layer2)レベルでパケットを取得しています。
  • GoReplay は gor listengor replay の2つが1セットで動作します。
    • gor listen がリクエストを複製し、Appgor replay に渡す。
    • gor replay がリクエストを転送したり、ファイルに書き込んだり、再生作業をする。
  • 複製の割合や、複数の転送先、リクエストのフィルタリング設定などがあり高機能。

私の理解が正しければ下記のような感じで動作してます。

Shadow Proxyとの比較

  • アプリサーバで必要な時に手軽に動かせる
  • 動作が軽快
  • インフラ構成的に利用時に事故が起こりにくい(サービスの全リクエストを裁くわけではないので)

今回なぜShadow Proxyを利用しなかったのかは後述。

実際に稼働している例で説明

実際に弊社で動かしている環境をざっくり図にして説明。

  • Productionリリース予定のコードがデプロイされている、Stagingサーバを用意。
  • Production frontendの1台で、GoReplayを動かしリクエストを複製し、ステージングサーバStaging frontendにリクエストをProductionと同じリクエストを投げる。
  • Productionのデータと一緒にならないように、ステージング用に staging aggregator と データ格納先の staging BigQuery を用意。
  • 下記コマンドのような形で動かし、Productionの1台に流れてきたリクエストを10%の割合で複製し、ステージングサーバに転送します。
  • echoサーバを作って、ベンチをとった結果(後述)、リクエストを100%複製し転送すると、パフォーマンスが劣化するので複製・転送の割合10%だけにしてます。
  • 実際の運用ではSupervisordを利用して、GoReplayの軌道を管理
  • 次期リリース予定のコードやインフラに問題があれば、StagingサーバのbugsnagなどがキャッチしてSlackにPostされる。
1
sudo gor --input-raw :8080 --output-http "http://stating901.example.com|10%"

GoReplay利用時のベンチマーク

nginx + echo-nginx-module を利用して、GoReplayの複製割合に応じてどれくらいパフォーマンスが劣化するか下記のような形でベンチマークを取得。

1
ab -n 10000 -c 100 http://hogemoge.example.com/hello

0%はGoReplayの利用無し、100%の場合はGoReplayで全トラフィックを複製して転送する。

  0% 10% 20% 50% 100%
RPS 2254.49 1944.42 1782.10 1403.82 1023.71

GoReplayで転送率を100%にして、全トラフィックを複製かけるとパフォーマンスが劣化するので、インフラを増強しておく必要あり。


ここから余談で、どうしてShadow Proxyを導入しなかったのか過去の経験と個人的な意見をちょっと書いてみる。

リクエストを複製するShadow Proxyの話

リクエストを複製するShadow Proxyと呼ばれるものは昔から存在します。

Shadow Proxy利用時の一般的な構成

Shadow Proxyの問題点(※1)

  • 構成的にロードバランサの次あたりに存在(リクエスト処理する構成の前段の方に存在)するので非常に高い信頼性が求められる。
  • 構成的に全リクエストがShadow Proxyを経由し、backendのサーバに到達するため、Shadow Proxyがボトルネックになりやすい。
  • インフラの構成的にサービスの全リクエストを処理する必要があるので、気軽に利用出来ない。稼働中のサービスに後から投入しにくい
  • 全リクエストがShadow Proxyを通過する構成になっていると、Shadow Proxyが落ちるとサービス全部落ちる
  • 上記を解消しShadow Proxyを安定的に稼働しようとすると、インフラが複雑化して障害点が増えやすい

※1 2年以上前の経験から得た知見なので、もしかして今は違うかもしれませんが。

この仕組みを作ったおかげで、Production環境のリクエストを利用し事前に動作検証が出来るので、今年は事故も心理的な負担がだいぶ減ったと思う。

AWS Application Load Balancer + Amazon ECS でDockerのホットデプロイ環境を構築した

TL;DR

  • AWS Application Load Balancer(以下ALB) + Amazon ECS でDockerのホットデプロイ環境を構築した
  • ALBのTarget GroupとECSのServiceを紐付けることで、ALB配下のコンテナの入れ替えが自動で行われるようになる

ALBは先日リリースされたばかりで、私もまだ色々と検証している段階なので、内容や認識等に誤りがあるかもしれないのでご容赦下さい。(詳しい人教えてください!!)

その他弊社の前提情報

  • GitHub + CircleCIが連携済み
  • Docker RepoにはAmazon EC2 Container Registry(以下ECR)を利用
  • DeployはGitHubのデプロイブランチへのマージを契機にCircleCI経由で、Docker Pushとecs-deployでDockerデプロイを実施

準備

ALBとECSの設定については@inokaraさんのブログが詳しいので、そちらを参照ください。 今回はALBのDynamic Port Mappingは利用してません。

デプロイフロー

デプロイフローは下記のような形になってます。

デプロイ前の稼働中の状態

  • ALBは80,443ポートで稼働
  • コンテナではnode.jsのアプリが3000ポートで稼働

デプロイ直後の状態

  • CircleCI経由で、ecs-deployを実行し、新しいコンテナを作成。
  • 新しいコンテナが起動すると、自動でALB配下に追加されアクセスを流れる
  • 古いコンテナと新しいコンテナが混在する

ecs-deployは下記のような形で実行。CircleCI上ではAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY を環境変数に設定し、引数に利用しているregionを-r us-west-1を追加して実行してます。

1
2
3
4
5
ecs-deploy \
-c クラスター名 \
-n サービス名 \
-i xxxxxxxxxxxx.dkr.ecr.us-west-1.amazonaws.com/kaizenplatform/kaizen-xxxxxxxx:947 \
-t 300

デプロイ部分のdocker pushecs-deploy合わせて1分半くらいで完了している。

デプロイ完了後の状態

  • しばらくすると古いコンテナが破棄され、新しいコンテナだけで稼働する
  • 一連の作業中にリクエストを流し続けて、サービスが停止しないことも確認

ecs-deployの実行が走ってから、新しいコンテナが起動し、古いコンテナが廃棄されるまで、 大体6分くらい要している。

ALBとECSの初期設定が結構面倒ですが、設定さえ終われば、 新旧コンテナの入れ替え、ALB配下への追加削除が自動で行われるようになるので大変便利。

引き続きDynamic Port Mappingを利用した構成も検証中。

参考

Alpine LinuxなDockerイメージにServerspecを実行する環境を作った

TL;DR

  • Alpine LinuxなDockerイメージにServerspecを実行する環境をMacとCircleCI上に作った
  • CircleCI環境では lxc-attach コマンドが必要なので、spec_helper.rb で環境差を吸収するようにした
  • 今後よく利用しそうなので、流用しやすい形でGithubに置いておいた

前提

最近Kaizen Platform, Inc.で利用している一部Docker ImageをAmazon LinuxベースのものからAlpine Linuxベースのものに置き換え中。 Alpine Linuxについては@stormcat24さんのブログが一番参考になります。

今までDockerイメージにServerspecを実行するときには、コンテナ内でsshdを立てて、ssh経由でknife-soloでプロビジョニングを実行し、その後同じくssh経由でServerspecを流していた。

Alpine Linuxベースに切り替えてから、出来るだけイメージサイズを小さくするためにknife-soloは利用せずに、Dockerfile だけで構築している。(Dockerfileだけが良いかは現在検証中) knife-soloのためにコンテナ内でsshdを立てる必要はなくなったので、同じくssh経由で実行していたServerspecdocker-api 経由で実行するようにした。

実行環境

  • Mac OS X
    • ProductVersion: 10.11.6
    • Ruby 2.3.0
    • Docker 1.12.0
  • CircleCI
    • Ruby 2.3.0
    • Docker 1.9.1-circleci-cp-workaround
  • serverspec 2.36.0
  • docker-api 1.31.0

実際の作業

ファイル構成

先に作業後のファイル構成を書いておくと下記のような感じになります。

1
2
3
4
5
6
7
8
9
10
.
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── Rakefile
├── circle.yml
└── spec
    ├── docker
    │   └── package_spec.rb
    └── spec_helper.rb

Gemfile

Gemfileを用意して bundle install

1
2
3
4
5
source 'https://rubygems.org'

gem 'rake'
gem 'serverspec'
gem 'docker-api'

Dockerfile

Dockerfileを用意。debugしやすいようにbashだけ導入

1
2
3
FROM alpine:3.4

RUN apk add --no-cache bash

tagは docker-alpine-serverspec-sample としてbuild。

1
docker build -t docker-alpine-serverspec-sample .

Serverspec各種ファイル

serverspec-init で各種ファイルを作成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
⇒  serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 2

 + spec/
 + spec/localhost/
 + spec/localhost/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec

生成されるsampleのテストは利用しないので削除

1
rm -rf spec/localhost

ローカル用の spec_helper.rb

1
2
3
4
5
6
7
8
9
require 'serverspec'
require 'docker'

set :backend, :docker
set :docker_url, ENV["DOCKER_HOST"]
image = Docker::Image.build_from_dir('.')
set :docker_image, image.id

Excon.defaults[:ssl_verify_peer] = false

テストファイルの追加

ディレクトリの作成し、Dockerfileのパッケージインストールに対応するテスト spec/docker/package_spec.rb を追加

1
mkdir -p spec/docker/
1
2
3
4
5
require 'spec_helper'

describe package('bash') do
  it { should be_installed }
end

Serverspecの実行

テストの準備が整ったので、Serverspecを流してみる。

1
bundle exec rake spec

問題がなければ下記のようになる。

1
2
3
4
5
Package "bash"
  should be installed

Finished in 1.61 seconds (files took 0.67509 seconds to load)
1 example, 0 failures

CircleCI上での実行

ローカルに続いて、CircleCI環境でも動くようにする。

circle.yml の用意

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
machine:
  timezone: UTC
  ruby:
    version: 2.3.0
  services:
    - docker

dependencies:
  pre:
    - bundle install

test:
  pre:
    - docker version
    - docker info
    - docker build -t ${CIRCLE_PROJECT_REPONAME} . :
        timeout: 1200
    - docker run ${CIRCLE_PROJECT_REPONAME} &
    - sleep 5
  override:
    - bundle exec rspec
    - docker ps -a
    - docker images

CircleCI でも動くようにspec_helper.rbの修正

公式ドキュメント に記載されているようにCircleCIのDockerでは docker exec が利用できず、 lxc-attach コマンドを利用しないといけないので、spec_helper.rb でMacとの環境差を吸収し、同じコマンドでServerspecが流せるようにする。

元同僚の@ngs自身のブログに書いていたのでそれを利用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
require 'serverspec'
require 'docker'

set :backend, :docker
set :docker_url, ENV["DOCKER_HOST"]
image = Docker::Image.build_from_dir('.')
set :docker_image, image.id

Excon.defaults[:ssl_verify_peer] = false

# https://circleci.com/docs/docker#docker-exec
if ENV['CIRCLECI']
  module Docker
    class Container
      def exec(command, opts = {}, &block)
        command[2] = command[2].inspect # ['/bin/sh', '-c', 'YOUR COMMAND']
        cmd = %Q{sudo lxc-attach -n #{self.id} -- #{command.join(' ')}}
        stdin, stdout, stderr, wait_thread = Open3.popen3 cmd
        [stdout.read, [stderr.read], wait_thread.value.exitstatus]
      end
      def remove(options={})
        # do not delete container
      end
      alias_method :delete, :remove
      alias_method :kill, :remove
    end
  end
end

これで、Mac上でもCircleCI上でも同じ

1
bundle exec rake spec

でテストが実行できるようになった。

↑の画像のように、イメージのサイズが非常に小さいAlpine Linuxに切り替えてからbuildもtestもすぐに完了するので生産性が高くなった。

参考

Mackerelのページを瞬時に開くAlfred Workflowを作った

Mackerelを利用していて、

  • 特定のホストのメトリクス
  • 特定のサービスのメトリクス
  • 特定のダッシュボード

などを見るときにページにアクセスして探すのに若干手間がかかるので、Alfredから瞬時に開けるようにWorkflowを作った。

導入方法や使い方はREADMEを参照ください。 動作は下記のような感じになります。(サービスとダッシュボードを開く動作はキャプチャに秘匿情報が含まれているので割愛してます bow )

関連