hidemium's blog

日々学んだことをアウトプットする。

KibanaでDockerコンテナのメトリクスを可視化する

前回、Sensuで取得したDockerコンテナのメトリクスをElasticsearchに転送することができたので、今回はメトリクスを可視化するためにKibanaの設定を行ってみました。なお、Dockerコンテナ内からはコンテナごとのリソース情報は見えず、ホスト全体のリソース情報しか見えないので、あくまで可視化の試みとなります。

構成

Ubuntu 12.04: Sensu Server、Sensu Client
Ubuntu 12.04: Elasticsearch
※上記はDocker 1.0.0上で動作しています。

Kibanaの設定

「http://<サーバのIPアドレス>:<ポート番号>」からKibanaにアクセスし、トップページの「Blank Dashboard」をクリックすると、まっさらダッシュボードが表示されるので、ここからスタートします。もちろん「Sample Dashboard」をベースに設定するのもよいかと思います。

QUERY

ダッシュボードの上部には「QUERY」というものが表示されています。ここにプロットしたいデータの抽出条件を定義します。
Sensuで取得したメトリクスは以下の形式となっていました。

Field Value
@timestamp 2014-07-15T17:03:20+00:00
_id a7f9a093d4945aab2be5b0d912193491
_index vmstat_metrics
_type vmstat_metrics
address 127.0.0.1
check_name vmstat_metrics
client elasticsearch
command /etc/sensu/plugins/system/vmstat-metrics.rb
key 62b7f582ef86.vmstat.cpu.system
occurrences 1
status 0
value 1

そこで、各コンテナのリソースごとのデータが見たかったので、以下のようにクエリを設定しました。

key:"62b7f582ef86.vmstat.cpu.system"

他のクエリの設定を含めると以下のようになりました。

f:id:hidemium:20140718135507p:plain

histogram

データの棒グラフや折れ線グラフをプロットするには、「histogram」というパネルを使用します。PanelタブのChart valueを「max」、Value Fieldを「value」に変更します。また、QueriesタブのQueriesを「seletcted」に変更し、上記で設定したクエリを選択します。
以下はhistogramの設定画面になります。

f:id:hidemium:20140718140234p:plain

histogramでプロットした画面が以下になります。
各リソースごとに、3つのDockerコンテナのリアルタイム24hデータをプロットしてみましたが、コンテナ内から取得したため、全てほぼ同じ値になっていることが分かります。

f:id:hidemium:20140718141153p:plain

設定の保存

パネルの設定は、ブラウザを閉じると消えてしまうので、ある程度パネルの設定ができたところで、右上のフロッピーアイコンをクリックしてセーブします。

パネルの紹介

他にも面白そうなパネルがあったので、いくつか紹介してみます。

table

データの内容を表示するパネルです。データの中身を確認することができます。

f:id:hidemium:20140718142654p:plain

terms

データ数をカウントしてくれるパネルです。円グラフ、表、棒グラフで表示することができます。

f:id:hidemium:20140718143616p:plain

sparklines

クエリごとの傾向をプロットしてくれるパネルです。全体をぱっと俯瞰したいときに便利かもしれません。

f:id:hidemium:20140718144120p:plain

stats

データの総数、最大値、最小値を表示するパネルです。リソースの最大値、最小値を見るのにいいかもしれません。

f:id:hidemium:20140718144852p:plain

おわりに

KibanaでSensuから取得したDockerコンテナのメトリクスを可視化することができました。
後は、ホストのcgroupからコンテナごとのリソース情報の取得ができれば、Dockerコンテナの監視の可視化が出来上がるかと思います。
また、Elasticsearchにメトリクスを転送できたので、現在リソースの負荷が高いコンテナだけを自動的にKibanaで表示することができれば便利そうだなと思っています。(できる方法ありましたら、どなたか教えてください。)

SensuからElasticsearchにメトリクスを転送する

前回、Elasticsearchのインストールができたため、今回はinokappa/sensu-handler-metrics-elasticsearchを用いて、SensuからElasticsearchにDockerコンテナのメトリクスを転送できるか試してみました。*1
なお、Sensuのメトリクスの可視化にはGraphiteがよく使われていますが、FluentdではElasticsearchが使われており、サーチエンジンを使い分けるのは大変そうだったので、Elasticsearchにしてみました。

構成

Ubuntu 12.04: Sensu Server、Sensu Client
Ubuntu 12.04: Elasticsearch
※上記はDocker 1.0.0上で動作しています。

Sensu Serverの設定

Sensu Server側でElasticsearchと連携するための設定を行います。
config.jsonに以下の設定を追加します。

$ vi /etc/sensu/config.json
{
  "rabbitmq": {
    "port": 5672,
    "host": "localhost",
    "user": "admin",
    "password": "admin",
    "vhost": "/"
  },
  "redis": {
    "host": "localhost",
    "port": 6379
  },
  "api": {
    "host": "localhost",
    "port": 4567
  },
  "dashboard": {
    "host": "localhost",
    "port": 8080,
    "user": "admin",
    "password": "admin"
  },
  "handlers": {
    "default": {
      "type": "pipe",
      "command": "true"
+   },
+   "elasticsearch-metrics": {
+     "type": "pipe",
+     "command": "/etc/sensu/handlers/metrics/elasticsearch-metrics.rb"
    }
  }
}

今回はvmstatの情報を転送するため、vmstatの監視設定を追加します。
typeは「metric」、handlersは「elasticsearch-metrics」とします。

$ vi /etc/sensu/conf.d/metrics/metric_vmstat.json
{
  "checks": {
    "vmstat_metrics": {
      "type": "metric",
      "handlers": ["elasticsearch-metrics"],
      "command": "/etc/sensu/plugins/system/vmstat-metrics.rb",
      "subscribers": [
        "default","sensu"
      ],
      "interval": 60
    }
  }
}

SensuのメトリクスをElasticsearchに転送するスクリプトを取得します。

$ cd /etc/sensu/handlers/metrics
$ wget https://raw.githubusercontent.com/inokappa/sensu-handler-metrics-elasticsearch/master/elasticsearch-metrics.rb
$ chmod 755 elasticsearch-metrics.rb

転送先のElasticsearchサーバを指定するファイルを取得します。
ファイルを取得後、ElasticsearchサーバのIPアドレスとポート番号を設定します。

$ cd /etc/sensu/conf.d
$ wget https://github.com/inokappa/sensu-handler-metrics-elasticsearch/blob/master/elasticsearch-metrics.json
$ vi elasticsearch-metrics.json
{
  "elasticsearch-metrics": {
    "host": "<IPアドレス>",
    "port": <ポート番号>,
    "index": "sensu-metrics",
    "type": "metrics"
  }
}

Elasticsearchは、データをインポートすると、ルールに従って自動的に各フィールドの型を識別し、マッピング(フィールドと型の組み合わせ)が自動で設定されます。
ただ、思ったとおりにマッピングが設定されない場合もあるため、Mapping Templateという機能を使い手動でマッピングを定義します。

$ curl -XPUT <ElasticsearchのIPアドレス>:<ポート番号>/_template/vmstat_metrics -d '
{
    "template" : "sensu-metrics*",
    "mappings" : {
      "vmstat_metrics" : {
        "numeric_detection" : true,
        "properties" : {
          "@timestamp" : { "type":"date", "format":"dateOptionalTime" },
          "client" : {type: "string"},
          "status": {type: "long"},
          "address": {type: "string"},
          "check_name": {type: "string"},
          "occurrences": {type: "long"},
          "value": {type: "long"},
          "command": {type: "string"},
          "key": {type: "string"}
          }
        }
      }
    }
  }
'

今回、手動マッピングでvalueフィールドをlong型で定義しているにも関わらず、valueフィールドに転送したデータの型により、valueフィールドがString型で定義される場合がありました。
この問題をつぶやいていたところ、以下のようなコメントを頂き、「numeric_detection」を使うことで問題を解決することができました。

最後に、Sensu Serverの再起動を行い、Elasticsearchとの連携設定を反映させます。
supervisorでSensu Serverのautorestartをオンにしているため、stopコマンドだけ実行しています。

$ service sensu-server stop

Elasticsearchの確認

マッピングが思ったようにできているか確認するため、「http://<サーバのIPアドレス>:9200/_plugin/head」にアクセスし、 Overviewタブ>インデックス(vmstat_metrics)>infoボタン>Index Metadataをクリックします。
valueフィールドのtypeを見るとlongになっていることが確認できます。

f:id:hidemium:20140716005458p:plain

「http://<サーバのIPアドレス>:<ポート番号>」からKibanaにアクセスし、トップページの「Sample Dashboard」をクリックすると、Sensuのメトリクスが転送されているのが確認できます。

f:id:hidemium:20140716005827p:plain

おわりに

SensuからElasticsearchにメトリクスを転送することができ、Dockerコンテナの監視の可視化に向けて一歩進んだかと思います。

今回の記事は、Sensuのスクリプトを作成された@inokaraさんのブログを参考にさせて頂きました。また、マッピングの問題では@johtaniさんにコメントを頂き助かりました。
@inokaraさん、@johtaniさんありがとうございした。

*1:Dockerコンテナ内からはコンテナごとのリソース情報は見えず、ホスト全体のリソース情報しか見えないので、今回はメトリクスの転送をすることを目的としています。

Elasticsearch + Kibanaを起動するDockerfileを書いてみた

前回、sensuでDockerコンテナを監視することができたため、今回はDockerコンテナの監視を可視化するための前段階として、最近注目されているサーチエンジンであるElasticsearchを起動するDockerfileを書いてみました。また、Elasticsearchのフロントエンドを提供するKibanaも合わせてインストールします。

構成

Ubuntu 12.04: サーバ構築対象
Ubuntu 12.04はDocker 1.0.0上で動作しています。

Dockerfile

Dockerfileの構成

Dockerfileの構成は以下の通りです。

elasticsearch
├──Dockerfile
├──id_rsa.pub            ... 公開鍵
├──sources.list          ... ミラーサイト一覧
├──config.js             ... Kibanaの定義ファイル
├──default.conf          ... nginxの定義ファイル
├──elasticsearch.conf    ... supervisorの定義ファイル(Elasticsearch用)
├──nginx.conf       ... supervisorの定義ファイル(nginx用)
└──supervisord.conf    ... supervisorの定義ファイル

Dockerfile

Dockerfileを以下のように作成します。

$ mkdir -p docker/elasticsearch
$ cd docker/elasticsearch
$ vi Dockerfile
FROM ubuntu:12.04

MAINTAINER hidemium

# Ubuntu update
ADD sources.list /etc/apt/sources.list
RUN apt-get -y update

# Install ssh
RUN apt-get -y install openssh-server
RUN apt-get -y install python-setuptools
RUN apt-get clean
RUN easy_install supervisor

RUN mkdir /var/run/sshd
RUN echo 'root:root' | chpasswd

# config sshd
ADD id_rsa.pub /root/id_rsa.pub
RUN mkdir /root/.ssh/
RUN mv /root/id_rsa.pub /root/.ssh/authorized_keys
RUN chmod 700 /root/.ssh
RUN chmod 600 /root/.ssh/authorized_keys
RUN sed -i -e '/^UsePAM\s\+yes/d' /etc/ssh/sshd_config

# config supervisor
RUN mkdir -p /var/log/supervisor
RUN mkdir -p /etc/supervisor/conf.d/
ADD supervisord.conf /etc/supervisord.conf

# Install Java
RUN apt-get install -y python-software-properties debconf-utils
RUN add-apt-repository ppa:webupd8team/java
RUN apt-get update
RUN echo "oracle-java7-installer shared/accepted-oracle-license-v1-1 boolean true" | debconf-set-selections
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get install -y oracle-java7-installer

# Install Elasticsearch
RUN apt-get install -y curl
RUN curl http://packages.elasticsearch.org/GPG-KEY-elasticsearch | apt-key add -
RUN echo "deb http://packages.elasticsearch.org/elasticsearch/1.2/debian stable main" >> /etc/apt/sources.list.d/elasticsearch.list
RUN apt-get update
RUN apt-get install -y elasticsearch
RUN /usr/share/elasticsearch/bin/plugin -install mobz/elasticsearch-head
ADD elasticsearch.conf /etc/supervisor/conf.d/elasticsearch.conf

# Install Kibana
RUN apt-get install -y unzip
RUN wget http://download.elasticsearch.org/kibana/kibana/kibana-latest.zip
RUN unzip kibana-latest.zip
RUN mv kibana-latest /usr/local/kibana
ADD config.js /usr/local/kibana/config.js

# Install Nginx
RUN echo "deb http://nginx.org/packages/ubuntu/ precise nginx" >> /etc/apt/sources.list.d/nginx.list
RUN echo "deb-src http://nginx.org/packages/ubuntu/ precise nginx" >> /etc/apt/sources.list.d/nginx.list
RUN curl http://nginx.org/keys/nginx_signing.key | apt-key add -
RUN apt-get update
RUN apt-get install -y nginx
ADD nginx.conf /etc/supervisor/conf.d/nginx.conf
ADD default.conf /etc/nginx/conf.d/default.conf

# Expose ports.
EXPOSE 22 80 9200 9300

# Define default command.
CMD ["supervisord", "-n"]
  • 「RUN echo "oracle-java7-installer shared/accepted-oracle-..」と「ENV DEBIAN_FRONTEND noninteractive」は、Javaのライセンス承諾画面を飛ばすためにやっています。
  • 「RUN /usr/share/elasticsearch/bin/plugin -install mobz/elasticsearch-head」は、 elasticsearch-headを使用できるようにプラグインをインストールしています。

Dockerfileの中で、読み込みを行っているファイルは以下の通りです。

sources.list

日本のミラーサイトを指定しています。

$ cat sources.list
deb http://jp.archive.ubuntu.com/ubuntu precise main restricted
deb-src http://jp.archive.ubuntu.com/ubuntu precise main restricted
deb http://jp.archive.ubuntu.com/ubuntu precise-updates main restricted
deb-src http://jp.archive.ubuntu.com/ubuntu precise-updates main restricted

config.js

Kibanaのconfig.jsを環境に合わせて修正しています。

$ cat config.js
:
    /*
    elasticsearch: "http://"+window.location.hostname+":9200",
     */
    elasticsearch: "http://<IPアドレス>:9200",
:

default.conf

kibanaにアクセスできるように、nginxのdefault.confを修正しています。

$ cat default.conf
:
    #location / {
    #    root   /usr/share/nginx/html;
    #    index  index.html index.htm;
    #}

    location / {
        root   /usr/local/kibana/;
        index  index.html index.htm;
    }
:

supervisord.conf

supervisorの設定とsshdの起動を定義しています。

$ cat supervisord.conf
[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)

[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10           ; (num of main logfile rotation backups;default 10)
loglevel=info                ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false               ; (start in foreground if true;default false)
minfds=1024                  ; (min. avail startup file descriptors;default 1024)
minprocs=200                 ; (min. avail process descriptors;default 200)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket

[include]
files = /etc/supervisor/conf.d/*.conf

[program:sshd]
command=/usr/sbin/sshd -D
autostart=true
autorestart=true

elasticsearch.conf

Elasticsearchの起動を定義しています。

$ cat elasticsearch.conf
[program:elasticsearch]
command=/usr/share/elasticsearch/bin/elasticsearch -f
autostart=true
autorestart=true

nginx.conf

Nginxの起動を定義しています。

$ cat nginx.conf
[program:nginx]
command=/usr/sbin/nginx -c /etc/nginx/nginx.conf
autostart=true
autorestart=tru

Elasticsearchコンテナの起動

以下のコマンドで、Dockerfileからビルドし、Dockerコンテナの起動を行います。

$ sudo docker build -t ubuntu-elasticsearch:12.04 .
$ sudo docker run -d -p 22 -p 80 -p 9200:9200 -p 9300:9300 ubuntu-elasticsearch:12.04

Dockerコンテナの80番ポートにフォワードしているポート番号を確認します。

$ sudo docker port <コンテナID> 80
0.0.0.0:<ポート番号>

「http://<サーバのIPアドレス>:<ポート番号>」にアクセスし、Kibanaに接続できることを確認します。

f:id:hidemium:20140714023026p:plain

また、「http://<サーバのIPアドレス>:9200/_plugin/head」にアクセスし、 elasticsearch-headに接続できることを確認します。

f:id:hidemium:20140714023139p:plain

ChefからDockerコンテナにSensu Clientをインストールする

前回、ChefからDockerコンテナにSensu Serverをインストールすることができました。そこで、今回はChefからDockerコンテナにSensu Clientをインストールして、Dockerコンテナが自動的に監視対象として追加されるか試してみました。
また、Clientの監視定義として、CPU使用率監視、メモリ使用率監視、ログ監視が設定されるようにしてみました。

構成

Ubuntu 12.04: サーバ構築対象
Ubuntu 12.04はDocker 1.0.0上で動作しています。

Dockerコンテナ

前回作成したDockerコンテナを使用します。

Sensu ClientのChefレシピ

それでは、Sensu Clientをインストーするレシピについて見ていきます。

cookbookの構成

cookbookの構成は以下の通りです。

site-cookbook
└──sensu-client
      └──recipes
      │    └──default.rb
      └──templates
            └──default
                  ├──client.json.erb ... Sensu Clientの監視定義ファイル
                  ├──config.json.erb ... Sensu Serverの定義ファイル
                  ├──sensu.list.erb  ... リポジトリの定義ファイル
                  └──sensu-client.conf.erb ... supervisorの定義ファイル

recipesファイル

recipesファイルは以下の通りです。

$ vi site-cookbooks/sensu-client/recipes/default.rb
%w{wget}.each do | pkg |
    package pkg do
        action :install
    end
end

template "/etc/apt/sources.list.d/sensu.list" do
    owner "root"
    mode 0644
    source "sensu.list.erb"
end

bash "add sensu repos" do
    code "wget -q http://repos.sensuapp.org/apt/pubkey.gpg -O- | apt-key add -"
    action :run
end

bash "apt-get update" do
    code "apt-get update"
    action :run
end

%w{sensu git-core bc rsyslog}.each do | pkg |
    package pkg do
        action :install
    end
end

git "/tmp/sensu_plugins" do
    repository "git://github.com/sensu/sensu-community-plugins.git"
    reference "master"
    action :sync
end

bash "copy sensu plugins" do
    code "cp -Rf /tmp/sensu_plugins/plugins /etc/sensu/"
    action :run
end

bash "chmod sensu plugins" do
    code "find /etc/sensu/plugins/ -name *.rb -exec chmod +x {} \\;"
    action :run
end

template "sensu-client.conf" do
    path "/etc/supervisor/conf.d/sensu-client.conf"
    owner "root"
    group "root"
    mode "0644"
    source "sensu-client.conf.erb"
end

template "config.json" do
    path "/etc/sensu/config.json"
    owner "root"
    group "root"
    mode "0644"
    source "config.json.erb"
end

template "client.json" do
    path "/etc/sensu/conf.d/client.json"
    owner "root"
    group "root"
    mode "0644"
    source "client.json.erb"
end

bash "ln ruby path" do
    code   "ln -s /opt/sensu/embedded/bin/ruby /usr/bin/ruby"
    action :run
end

bash "supervisorctl reload" do
    code   "supervisorctl reload"
    action :run
end
  • Sensu Clientをインストールするために、Sensuのリポジトリを指定しています。
  • GitHubからSensuのCommunity Pluginを取得しています。
  • Sensu Clientを起動するため、supervisorの定義ファイルであるsensu-client.confを配置しています。

templateファイル

templateファイルは以下の通りです。

client.json

client.jsonは監視対象の情報や監視定義を設定するファイルであり、Sensu Client側に配置します。
checks_*.jsonの"subscribers"に定義した名称をsubscriptionsに指定することで、監視定義を設定することができます。
"sensu"はSensu Server用にしているので、"default"のみを設定します。

$ vi site-cookbooks/sensu-client/templates/default/client.json.erb
{
    "client": {
      "name": "sensu-client",
      "address": "127.0.0.1",
      "subscriptions": [ "default" ]
    }
}
config.json

config.jsonは接続先のSensu Serverの定義ファイルになります。
"rabbitmq"には、追加したユーザ名を指定しています。

$ vi site-cookbooks/sensu-client/templates/default/config.json.erb
{
  "rabbitmq": {
    "port": 5672,
    "host": "<IPアドレス>",
    "user": "admin",
    "password": "admin",
    "vhost": "/"
  }
}
sensu.list

sensu.listには、Sensuのリポジトリを指定しています。

$ vi site-cookbooks/sensu-client/templates/default/sensu.list.erb
deb http://repos.sensuapp.org/apt sensu main
sensu-client.conf

sensu-client.confには、supervisorからSensu Clientを起動する設定をしています。
シスログ監視のサンプルのため、rsyslogの起動も設定しています。

$ vi site-cookbooks/sensu-client/templates/default/sensu-client.conf.erb
[program:sensu-client]
priority=40
directory=/tmp
command=/opt/sensu/bin/sensu-client -c /etc/sensu/config.json -d /etc/sensu -e /etc/sensu/extensions -v -l /var/log/sensu/client.log
user=root
autostart=true
autorestart=true

[program:rsyslogd]
command=/usr/sbin/rsyslogd
autostart=true
autorestart=true

Chefレシピの実行

Dockerコンテナを起動します。
22のポートを外部からアクセスできるようにしておきます。

$ sudo docker run -d -p 22 ubuntu-supervisor:12.04
$ sudo docker ps -a
CONTAINER ID        IMAGE                                 COMMAND                CREATED             STATUS                  PORTS                                                                                                                        NAMES
365a226d1a9d        ubuntu-supervisor:12.04               supervisord -n         3 days ago          Up 3 days               0.0.0.0:[ポート番号]->22/tcp                                                                                                        high_elion

Sensu ClientのChefレシピを流します。
Chefを実行する時は、22番ポートにフォワードされているポート番号を指定します。

$ vi nodes/<IPアドレス>.json
{
  "run_list": [
    "recipe[sensu-client]"
  ]
}
$ knife solo bootstrap root@<IPアドレス> -p [ポート番号]

Sensu Serverの管理画面

Sensu Clientが追加されたことを確認するため、「http://<サーバのIPアドレス>:8080」にアクセスし、Sensuのダッシュボードに接続します。ダッシュボードの「Clients」をクリックすると、先ほど追加したDockerコンテナがいるのが分かります。

f:id:hidemium:20140708031053p:plain

また、「http://<サーバのIPアドレス>:15672」にアクセスし、RabbitMQの管理画面に接続すると、接続数が1つ増えていることが分かります。

f:id:hidemium:20140708031106p:plain

おわりに

ChefからDockerコンテナにSensu Clientをインストールすることができました。また、Sensuの特徴として、クライアント(監視対象)の自動登録がありますが、これらを確認することができました。

Sensuの監視の仕組みとして、Sensu Server側で監視の動作を定義して、Sensu Client側はsubscriptionsを使ってその監視定義の中から必要なものだけを選択する仕組みとなっているため、監視を共通化できれば監視設定を簡単にスケールさせることができそうです。

ChefからSensuの監視定義を設定する

前回、ChefからDockerコンテナにSensu Serverをインストールすることができました。そこで、今回はSensuの監視定義として、CPU使用率監視、メモリ使用率監視、ログ監視、プロセス監視を設定してみました。

構成

Ubuntu 12.04: サーバ構築対象
Ubuntu 12.04はDocker 1.0.0上で動作しています。

Dockerコンテナ

前回作成したDockerコンテナを使用します。

Sensu ServerのChefレシピ

それでは、Sensuの監視定義をインストールするレシピについて見ていきます。

cookbookの構成

cookbookの構成は以下の通りです。

site-cookbook
└──sensu-server
      └──recipes
      │    └──default.rb
      └──templates
            └──default
                  ├──check_cpu.json.erb ... 監視動作定義ファイル(CPU使用率監視)
                  ├──check_memory_pcnt.json.erb ... 監視動作定義ファイル(メモリ使用率監視)
                  ├──check_log.json.erb ... 監視動作定義ファイル(ログ監視監視)
                  ├──checks_proc.json.erb ... 監視動作定義ファイル(プロセス監視)
                  ├──client.json.erb ... Sensu Clientの監視定義ファイル
                  ├──config.json.erb ... Sensu Serverの定義ファイル
                  ├──sensu.list.erb  ... リポジトリの定義ファイル
                  └──sensu-server.conf.erb ... supervisorの定義ファイル

recipesファイル

recipesファイルは以下の通りです。

$ vi site-cookbooks/sensu-server/recipes/default.rb
template "check_cpu.json" do
    path "/etc/sensu/conf.d/check_cpu.json"
    owner "root"
    group "root"
    mode "0644"
    source "check_cpu.json.erb"
end

template "check_memory_pcnt.json" do
    path "/etc/sensu/conf.d/check_memory_pcnt.json"
    owner "root"
    group "root"
    mode "0644"
    source "check_memory_pcnt.json.erb"
end

template "check_log.json" do
    path "/etc/sensu/conf.d/check_log.json"
    owner "root"
    group "root"
    mode "0644"
    source "check_log.json.erb"
end

template "checks_proc.json" do
    path "/etc/sensu/conf.d/checks_proc.json"
    owner "root"
    group "root"
    mode "0644"
    source "checks_proc.json.erb"
end

bash "sensu server restart" do
    code   "service sensu-server stop"
    action :run
end
  • Sensuの監視定義を有効にするため、Sensu Serverの再起動が必要となります。supervisorでSensu Serverプロセスのautorestartをオンにしているため、service sensu-server stopコマンドだけ実行しています。

templateファイル

templateファイルは以下の通りです。(監視定義のみ説明します。)*1

check_cpu.json

CPU使用率監視は、SensuのCommunity Pluginのcheck-cpu.rbを利用します。
上限警告値は-w、上限異常値は-cで設定します。
※bcコマンドを内部で使用しているため、予めインストールしておきます。

{
  "checks": {
    "check_cpu": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/system/check-cpu.rb -w 90 -c 100",
      "interval": 60,
      "occurrences": 5,
      "subscribers": ["default","sensu"]
    }
  }
}
check_memory_pcnt.json

メモリ使用率監視は、SensuのCommunity Pluginのcheck-memory-pcnt.shを利用します。
上限警告値は-w、上限異常値は-cで設定します。

{
  "checks": {
    "check_memory_pcnt": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/system/check-memory-pcnt.sh -w 90 -c 100",
      "interval": 60,
      "occurrences": 5,
      "subscribers": ["default","sensu"]
    }
  }
}
check_log.json

ログ監視は、SensuのCommunity Pluginのcheck-log.rb を利用します。
監視対象のログファイルは-f、監視文字列は-qで設定します。

{
  "checks": {
    "check_log": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/logging/check-log.rb -f /var/log/syslog -q test",
      "interval": 60,
      "occurrences": 5,
      "subscribers": ["default","sensu"]
    }
  }
}
checks_proc.json

プロセス監視は、SensuのCommunity Pluginのcheck-procs.rbを利用します。
監視対象のプロセスは-p、プロセス数の上限異常値/下限異常値は-C/-cで設定します。
プロセス数の上限警告値/下限警告値は-W/-wで設定します。

$ vi site-cookbooks/sensu-server/templates/default/checks_proc.json.erb
{
  "checks": {
    "sensu-rabbitmq-beam": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/processes/check-procs.rb -p beam -C 1 -w 4 -c 5",
      "interval": 60,
      "occurrences": 2,
      "refresh": 300,
      "subscribers": [ "sensu" ]
    },
    "sensu-rabbitmq-epmd": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/processes/check-procs.rb -p epmd -C 1 -w 1 -c 1",
      "interval": 60,
      "occurrences": 2,
      "refresh": 300,
      "subscribers": [ "sensu" ]
    },
    "sensu-redis": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/processes/check-procs.rb -p redis-server -C 1 -w 4 -c 5",
      "interval": 60,
      "occurrences": 2,
      "refresh": 300,
      "subscribers": [ "sensu" ]
    },
    "sensu-api": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/processes/check-procs.rb -p sensu-api -C 1 -w 4 -c 5",
      "interval": 60,
      "occurrences": 2,
      "refresh": 300,
      "subscribers": [ "sensu" ]
    },
    "sensu-dashboard": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/processes/check-procs.rb -p sensu-dashboard -C 1 -w 1 -c 1",
      "interval": 60,
      "occurrences": 2,
      "refresh": 300,
      "subscribers": [ "sensu" ]
    }
  }
}

Chefレシピの実行

Dockerコンテナのポート番号を確認します。

$ sudo docker ps -a
CONTAINER ID        IMAGE                                 COMMAND                CREATED             STATUS                    PORTS                                                                                                                        NAMES
b245647afb46        ubuntu-supervisor:12.04               supervisord -n         27 minutes ago      Up 27 minutes             0.0.0.0:4567->4567/tcp, 0.0.0.0:5672->5672/tcp, 0.0.0.0:15672->15672/tcp, 0.0.0.0:[ポート番号]->22/tcp, 0.0.0.0:8080->8080/tcp     drunk_mayer

Sensu ServerのChefレシピを流します。
Chefを実行する時は、22番ポートにフォワードされているポート番号を指定します。

$ vi nodes/<IPアドレス>.json
{
  "run_list": [
    "recipe[sensu-server]"
  ]
}
$ knife solo bootstrap root@<IPアドレス> -p [ポート番号]

Sensu Serverの管理画面

Sensu Serverの監視定義が反映されていることを確認するため、「http://<サーバのIPアドレス>:8080」にアクセスし、Sensuのダッシュボードに接続します。ダッシュボードの「Checks」をクリックし、監視定義が表示されることを確認します。

f:id:hidemium:20140708024238p:plain

おわりに

Sensu Serverに監視定義を設定することができました。基本的な監視定義ができたので、今後SensuのCommunity Pluginでどのような監視が可能か見てみようかと思います。

*1:SensuのCommunity Pluginの使用方法について、ドキュメントがあまり見つからなかったため、コード内に書かれた説明を参考にしました。

ChefからDockerコンテナにSensu Serverをインストールする

前回、ChefからDockerコンテナ内の起動プロセスを追加することが確認できました。そこで、今回はChefを使ってDockerコンテナに監視ツールのSensu Serverをインストールしてみました。

構成

Ubuntu 12.04: サーバ構築対象
Ubuntu 12.04はDocker 1.0.0上で動作しています。

Dockerコンテナ

Dockerfileの構成は以下の通りです。

sshd
├──Dockerfile
├──sources.list      ... ミラーサイト一覧
├──id_rsa.pub        ... Chef workstationの公開鍵
└──supervisord.conf  ... supervisorの定義ファイル

Dockerfileを以下のように作成します。
supervisorの定義ファイルは、/etc/supervisor/conf.d/配下にも置くことができるので、「RUN mkdir -p /etc/supervisor/conf.d/」でディレクトリを作成しています。

$ vi Dockerfile
FROM ubuntu:12.04

MAINTAINER hidemium

# Ubuntu update
ADD sources.list /etc/apt/sources.list
RUN apt-get -y update

# Hack for initctl not being available in Ubuntu
RUN dpkg-divert --local --rename --add /sbin/initctl
RUN ln -sf /bin/true /sbin/initctl

# ssh install
RUN apt-get -y install openssh-server
RUN apt-get -y install python-setuptools
RUN apt-get clean
RUN easy_install supervisor

RUN mkdir /var/run/sshd
RUN echo 'root:root' | chpasswd

# sshd config
ADD id_rsa.pub /root/id_rsa.pub
RUN mkdir /root/.ssh/
RUN mv /root/id_rsa.pub /root/.ssh/authorized_keys
RUN chmod 700 /root/.ssh
RUN chmod 600 /root/.ssh/authorized_keys
RUN sed -i -e '/^UsePAM\s\+yes/d' /etc/ssh/sshd_config

# supervisor config
RUN mkdir -p /var/log/supervisor
RUN mkdir -p /etc/supervisor/conf.d/
ADD supervisord.conf /etc/supervisord.conf

# Expose ports.
EXPOSE 22

# Define default command.
CMD ["supervisord", "-n"]

supervisorの定義ファイルは、デフォルトの定義にsshdの起動を追加したものにしています。

$ vi supervisord.conf
[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)

[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10           ; (num of main logfile rotation backups;default 10)
loglevel=info                ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false               ; (start in foreground if true;default false)
minfds=1024                  ; (min. avail startup file descriptors;default 1024)
minprocs=200                 ; (min. avail process descriptors;default 200)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket

[include]
files = /etc/supervisor/conf.d/*.conf

[program:sshd]
command=/usr/sbin/sshd -D
autostart=true
autorestart=true

docker buildコマンドにより、イメージを作成します。
実行後、ubuntu-supervisorというイメージが作成されていることを確認します。

$ sudo docker build -t ubuntu-supervisor:12.04 .
$ sudo docker images

Sensu ServerのChefレシピ

それでは、Sensu Serverをインストーするレシピについて見ていきます。

cookbookの構成

cookbookの構成は以下の通りです。

site-cookbook
└──sensu-server
      └──recipes
      │    └──default.rb
      └──templates
            └──default
                  ├──checks_proc.json.erb ... 監視動作定義ファイル
                  ├──client.json.erb ... Sensu Clientの監視定義ファイル
                  ├──config.json.erb ... Sensu Serverの定義ファイル
                  ├──sensu.list.erb  ... リポジトリの定義ファイル
                  └──sensu-server.conf.erb ... supervisorの定義ファイル

recipesファイル

recipesファイルは以下の通りです。

$ vi site-cookbooks/sensu-server/recipes/default.rb
%w{wget python-software-properties}.each do | pkg |
    package pkg do
        action :install
    end
end

template "/etc/apt/sources.list.d/sensu.list" do
    owner "root"
    mode 0644
    source "sensu.list.erb"
end

bash "add sensu apt-key" do
    code "wget -q http://repos.sensuapp.org/apt/pubkey.gpg -O- | apt-key add -"
    action :run
end

bash "add rabbitmq apt-key" do
    code "wget -q http://www.rabbitmq.com/rabbitmq-signing-key-public.asc -O- | apt-key add -"
    action :run
end

bash "add redis repos" do
    code "add-apt-repository -y ppa:rwky/redis"
    action :run
end

bash "apt-get update" do
    code "apt-get update"
    action :run
end

%w{sensu rabbitmq-server redis-server git-core bc rsyslog}.each do | pkg |
    package pkg do
        action :install
    end
end

git "/tmp/sensu_plugins" do
    repository "git://github.com/sensu/sensu-community-plugins.git"
    reference "master"
    action :sync
end

bash "copy sensu plugins" do
    code "cp -Rf /tmp/sensu_plugins/plugins /etc/sensu/"
    action :run
end

bash "chmod sensu plugins" do
    code "find /etc/sensu/plugins/ -name *.rb -exec chmod +x {} \\;"
    action :run
end

template "sensu-server.conf" do
    path "/etc/supervisor/conf.d/sensu-server.conf"
    owner "root"
    group "root"
    mode "0644"
    source "sensu-server.conf.erb"
end

template "config.json" do
    path "/etc/sensu/config.json"
    owner "root"
    group "root"
    mode "0644"
    source "config.json.erb"
end

template "client.json" do
    path "/etc/sensu/conf.d/client.json"
    owner "root"
    group "root"
    mode "0644"
    source "client.json.erb"
end

template "checks.json" do
    path "/etc/sensu/conf.d/checks.json"
    owner "root"
    group "root"
    mode "0644"
    source "checks.json.erb"
end

bash "ln ruby path" do
    code   "ln -s /opt/sensu/embedded/bin/ruby /usr/bin/ruby"
    action :run
end

bash "supervisorctl reload" do
    code   "supervisorctl reload; sleep 10"
    action :run
end

bash "rabbitmqctl add_user" do
    code "rabbitmqctl add_user admin admin"
    action :run
end

bash "rabbitmqctl set_user_tags" do
    code "rabbitmqctl set_user_tags admin administrator"
    action :run
end

bash "rabbitmqctl set_permissions" do
    code "rabbitmqctl set_permissions -p / admin '.*' '.*' '.*'"
    action :run
end

bash "rabbitmq_management" do
    code "rabbitmq-plugins enable rabbitmq_management"
    action :run
end

bash "rabbitmq restart" do
    code   "rabbitmqctl stop"
    action :run
end
  • Sensu Serverをインストールするために、Sensuのリポジトリを指定しています。
  • 普通にapt-get install rabbitmq-serverとした場合、RabbitMQ 2.7.1がインストールされるため、RabbitMQのリポジトリを指定し、RabbitMQ 3系がインストールされるようにしています。
  • GitHubからSensuのCommunity Pluginを取得しています。
  • Sensu ServerやRabbitMQを起動するため、supervisorの定義ファイルであるsensu-server.confを配置しています。
  • RabbitMQ 3系では、RabbitMQの管理画面にguestユーザでログインするためには、localhost経由しかできない仕様となったため、adminユーザを追加しています。
  • RabbitMQの管理画面を有効にするため、rabbitmq-plugins enable rabbitmq_managementコマンドを実行後、RabbitMQを再起動が必要となります。supervisorでRabbitMQプロセスのautorestartをオンにしているため、rabbitmqctl stopコマンドだけ実行しています。

templateファイル

templateファイルは以下の通りです。

checks.json

checks.jsonは監視の動作を定義しているファイルであり、Sensu Server側に配置します。
command内のcheck-procs.rbはSensuのCommunity Pluginになります。

$ vi site-cookbooks/sensu-server/templates/default/checks_proc.json.erb
{
  "checks": {
    "sensu-rabbitmq-beam": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/processes/check-procs.rb -p beam -C 1 -w 4 -c 5",
      "interval": 60,
      "occurrences": 2,
      "refresh": 300,
      "subscribers": [ "sensu" ]
    },
    "sensu-rabbitmq-epmd": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/processes/check-procs.rb -p epmd -C 1 -w 1 -c 1",
      "interval": 60,
      "occurrences": 2,
      "refresh": 300,
      "subscribers": [ "sensu" ]
    },
    "sensu-redis": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/processes/check-procs.rb -p redis-server -C 1 -w 4 -c 5",
      "interval": 60,
      "occurrences": 2,
      "refresh": 300,
      "subscribers": [ "sensu" ]
    },
    "sensu-api": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/processes/check-procs.rb -p sensu-api -C 1 -w 4 -c 5",
      "interval": 60,
      "occurrences": 2,
      "refresh": 300,
      "subscribers": [ "sensu" ]
    },
    "sensu-dashboard": {
      "handlers": [
        "default"
      ],
      "command": "/etc/sensu/plugins/processes/check-procs.rb -p sensu-dashboard -C 1 -w 1 -c 1",
      "interval": 60,
      "occurrences": 2,
      "refresh": 300,
      "subscribers": [ "sensu" ]
    }
  }
}
client.json

client.jsonは監視対象の情報や監視定義を設定するファイルであり、Sensu Client側に配置します。
checks.jsonの"subscribers"に定義した名称をsubscriptionsに指定することで、監視定義を設定することができます。

$ vi site-cookbooks/sensu-server/templates/default/client.json.erb
{
    "client": {
      "name": "sensu-server",
      "address": "127.0.0.1",
      "subscriptions": [ "default", "sensu" ]
    }
}
config.json

config.jsonはSensu Serverの定義ファイルになります。
"rabbitmq"には、追加したユーザ名(guest以外)を指定しています。

$ vi site-cookbooks/sensu-server/templates/default/config.json.erb
{
  "rabbitmq": {
    "port": 5672,
    "host": "localhost",
    "user": "admin",
    "password": "admin",
    "vhost": "/"
  },
  "redis": {
    "host": "localhost",
    "port": 6379
  },
  "api": {
    "host": "localhost",
    "port": 4567
  },
  "dashboard": {
    "host": "localhost",
    "port": 8080,
    "user": "admin",
    "password": "admin"
  },
  "handlers": {
    "default": {
      "type": "pipe",
      "command": "true"
    }
  }
}
sensu.list

sensu.listには、SensuとRabbitMQのリポジトリを指定しています。

$ vi site-cookbooks/sensu-server/templates/default/sensu.list.erb
deb http://repos.sensuapp.org/apt sensu main
deb http://www.rabbitmq.com/debian/ testing main
sensu-server.conf

sensu-server.confには、supervisorからSensu Serverを起動する設定をしています。

$ vi site-cookbooks/sensu-server/templates/default/sensu-server.conf.erb
[program:rabbitmq]
priority=10
directory=/tmp
command=/usr/sbin/rabbitmq-server
user=root
autostart=true
autorestart=true
stopsignal=QUIT

[program:redis]
priority=10
directory=/tmp
command=redis-server
user=root
autostart=true
autorestart=true

[program:sensu-server]
priority=20
directory=/tmp
command=/opt/sensu/bin/sensu-server -c /etc/sensu/config.json -d /etc/sensu -e /etc/sensu/extensions -v -l /var/log/sensu/server.log
user=root
startsecs=5
autostart=true
autorestart=true

[program:sensu-api]
priority=30
directory=/tmp
command=/opt/sensu/bin/sensu-api -c /etc/sensu/config.json -d /etc/sensu -e /etc/sensu/extensions -v -l /var/log/sensu/api.log
user=root
startsecs=5
autostart=true
autorestart=true

[program:sensu-client]
priority=40
directory=/tmp
command=/opt/sensu/bin/sensu-client -c /etc/sensu/config.json -d /etc/sensu -e /etc/sensu/extensions -v -l /var/log/sensu/client.log
user=root
autostart=true
autorestart=true

[program:sensu-dashboard]
priority=50
directory=/tmp
command=/opt/sensu/bin/sensu-dashboard -c /etc/sensu/config.json -d /etc/sensu -e /etc/sensu/extensions -v -l /var/log/sensu/dashboard.log
user=root
autostart=true
autorestart=true

Chefレシピの実行

Dockerコンテナを起動します。
22、8080、4567、5672、15672のポートを外部からアクセスできるようにしておきます。

$ sudo docker run -d -p 22 -p 8080:8080 -p 4567:4567 -p 5672:5672 -p 15672:15672 ubuntu-supervisor:12.04
$ sudo docker ps -a
CONTAINER ID        IMAGE                                 COMMAND                CREATED             STATUS                    PORTS                                                                                                                        NAMES
b245647afb46        ubuntu-supervisor:12.04               supervisord -n         27 minutes ago      Up 27 minutes             0.0.0.0:4567->4567/tcp, 0.0.0.0:5672->5672/tcp, 0.0.0.0:15672->15672/tcp, 0.0.0.0:[ポート番号]->22/tcp, 0.0.0.0:8080->8080/tcp     drunk_mayer

Sensu ServerのChefレシピを流します。
Chefを実行する時は、22番ポートにフォワードされているポート番号を指定します。

$ vi nodes/<IPアドレス>.json
{
  "run_list": [
    "recipe[sensu-server]"
  ]
}
$ knife solo bootstrap root@<IPアドレス> -p [ポート番号]

Sensu Serverの管理画面

Sensu Serverの構築ができたことを確認するため、「http://<サーバのIPアドレス>:8080」にアクセスし、Sensuのダッシュボードに接続します。ログインはadmin/adminでできます。(config.jsonで定義しています。)

f:id:hidemium:20140701233758p:plain

また、「http://<サーバのIPアドレス>:15672」にアクセスし、RabbitMQの管理画面に接続します。ログインはadmin/adminでできます。(config.jsonで定義しています。)

f:id:hidemium:20140702001932p:plain

おわりに

ChefからDockerコンテナにSensu Serverをインストールすることができました。*1
Sensuの環境ができたので、これから色々試してみたいと思います。

Sensuの特徴として、クライアント(監視対象)の自動登録がありますが、Dockerのようにscrap & buildが多い仮想環境に合っているのではと思っています。
Dockerコンテナをプロビジョニングする際に、Sensu Clientをインストールする仕組みにしておけば、コンテナをいくら起動しても監視設定を自動的にすることができるかもしれません。

*1:Chefを使ったDockerコンテナのプロビジョニングは、慣れてくると意外といい気がしてきました。

VMwareでUbuntuのディスク容量を拡張する

VMware上のUbuntuサーバでDockerのイメージを色々作っていたら、ディスク容量がいつのまにか少なくなっていたので、ディスク容量の拡張を行いました。

構成

Ubuntu 12.04
※上記のサーバはVMware ESXi 5.1上で動作しています。

VMware

サーバを停止

サーバのハードウェアを追加するため、サーバをシャットダウンしておきます。

$ sudo shutdown -h now

仮想ディスクを拡張する

  1. 対象の仮想サーバを右クリックし、「設定の編集」をクリックします。
  2. ハードウェアタブ>追加ボタンをクリックします。
  3. ハードウェアの追加>ハードディスクを選択し、次へをクリックします。
  4. 新規仮想ディスクを作成を選択し、次へをクリックします。
  5. 追加するディスク容量とディスクプロビジョニングを入力し、次へをクリックします。
  6. 詳細オプションはなにも変更せず、次へをクリックします。
  7. 設定内容を確認し、終了をクリックする。
  8. 対象の仮想サーバを右クリックし、電源>パワーオンをクリックします。

※仮想ディスクの容量拡張がなぜかできなかったため、新たな仮想ディスクを追加しています。

ディスク容量の拡張

Ubuntuサーバのパーティション構成は、OSインストール時のデフォルト設定であるため、LVM(Logical Volume Manager)を使用しています。追加した仮想ディスクを既存のボリュームグループに追加することで、ディスク容量の拡張を行います。

物理ボリュームの作成

追加した仮想ディスクのパーティションを作成します。

$ sudo parted /dev/sdb
GNU Parted 2.3
/dev/sdb を使用
GNU Parted へようこそ! コマンド一覧を見るには 'help' と入力してください。
# パーティションテーブルを設定(printが実行できないので)
(parted) mklabel msdos
(parted) print
モデル: VMware Virtual disk (scsi)
ディスク /dev/sdb: 21.5GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: msdos

# 追加したディスクの全容量を新たなパーティションに
(parted) unit GB
(parted) mkpart extended 0 -1
(parted) mkpart logical 0 -1
(parted) print
モデル: VMware Virtual disk (scsi)
ディスク /dev/sdb: 21.5GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: msdos

番号  開始    終了    サイズ  タイプ    ファイルシステム  フラグ
 1    0.00GB  21.5GB  21.5GB  extended                    lba
 5    0.00GB  21.5GB  21.5GB  logical

# パーティションフラグをlvmに変更
(parted) set 5 lvm on
(parted) print
モデル: VMware Virtual disk (scsi)
ディスク /dev/sdb: 21.5GB
セクタサイズ (論理/物理): 512B/512B
パーティションテーブル: msdos

番号  開始    終了    サイズ  タイプ    ファイルシステム  フラグ
 1    0.00GB  21.5GB  21.5GB  extended                    lba
 5    0.00GB  21.5GB  21.5GB  logical                     lvm
(parted) quit

サーバをリブートします。

$ sudo shutdown -r now

物理ボリュームを作成します。

$ sudo ls -la /dev/sdb5
brw-rw---- 1 root disk 8, 21  6月 22 12:58 /dev/sdb5
$ sudo pvcreate /dev/sdb5
  Physical volume "/dev/sdb5" successfully created

ボリュームグループの拡張

現在のボリュームグループのサイズを確認します。

$ sudo vgdisplay -v <ボリュームグループ名>
  --- Volume group ---
  VG Size               19.76 GiB

ボリュームグループを拡張します。

$ sudo vgextend <ボリュームグループ名> /dev/sdb5

ボリュームグループが拡張されたことを確認します。

$ sudo vgdisplay -v <ボリュームグループ名>
  --- Volume group ---
  VG Size               39.75 GiB

論理ボリュームの拡張

現在の論理ボリュームのサイズを確認します。

$ sudo lvdisplay <論理ボリューム名>
  --- Logical volume ---
  LV Size                15.74 GiB

論理ボリュームを拡張します。
論理ボリュームのサイズは、エクステント数による指定を行い、無駄がないように調整します。

$ sudo lvresize -L 36616 <論理ボリューム名>
  Extending logical volume root to 35.76 GiB
  Logical volume root successfully resized

論理ボリュームが拡張されたことを確認します。

$ sudo lvdisplay <論理ボリューム名>
  --- Logical volume ---
  LV Size                35.76 GiB

ファイルシステムの拡張

現在のファイルシステムのサイズを確認します。

$ df -h
Filesystem                        Size  Used Avail Use% Mounted on
<論理ボリューム名>                 15G   14G    1G  90% /

ファイルシステムを拡張します。

$ sudo resize2fs <論理ボリューム名>
resize2fs 1.42 (29-Nov-2011)
Filesystem at <論理ボリューム名> is mounted on /; on-line resizing required
old desc_blocks = 1, new_desc_blocks = 2
Performing an on-line resize of <論理ボリューム名> to 5046272 (4k) blocks.
The filesystem on <論理ボリューム名> is now 5046272 blocks long.

ファイルシステムが拡張されたことを確認します。

$ df -h
Filesystem                        Size  Used Avail Use% Mounted on
<論理ボリューム名>                 36G   14G   21G  40% /

これでディスク容量を拡張することができました。