Docker(コンテナ型仮想化)と Kubernetes についての簡単な紹介

社内向けに書いた文書です。

コンテナ型仮想化とは何か?

OS上に、コンテナと呼ばれる、隔離されたアプリケーションの実行環境を作り、一台のホスト上であたかも複数のホストが動いているかのような環境を実現するのが、コンテナ型仮想化技術です。

コンテナ型仮想化と従来の VM 型仮想化を比較したものが以下の図です(Xen は AWS EC2 などの仮想化を実現する実装で、Docker はコンテナ型仮想化の代表的な実装)。

container

これらの仮想化技術に共通するのは、

  1. アプリケーションの実行環境(ホスト)を仮想化すること
  2. それらのスナップショットをイメージとして保存することで、アプリケーションを環境ごとパッケージング出来るようにすること

という二つの目的です。

VM 型仮想化ではハードウェアのレベルで仮想化が実現されているので、ホスト上で動く一つ一つの VM の中でそれぞれ別々の OS を動作させることができます。しかしその一方で、実行環境(VM)のサイズが大きくなり、アプリケーションの起動にかかる時間も長くなります。

コンテナ型仮想化では OS 上の実行環境を仮想化するので、OS 自体は限定されますが、その分実行環境(コンテナ)のサイズはコンパクトになり、起動にかかる時間も短くなります。

基本的に VM 型仮想化からコンテナ型仮想化への移行は、一つ一つのアプリケーションの粒度が小さくなる、いわゆるマイクロサービス化を促進します。インフラ上の更新の単位はより小さくなり、変更のサイクルは短くなります。

パッケージング技術の変遷

packaging

Kubernetes とは何か?

1. Docker 実行環境をクラスタ化する

通常、Docker の実行環境は一台のホストに閉じています。

docker-network

同一ホスト内で動くコンテナ同士は、プライベートネットワーク経由でやり取りができますが、ホストの外側とやり取りする場合は NAT (IP Masquerade) を経由する必要があります。

このように、標準の Docker 実行環境では、ホスト間の連携が煩雑になるため、コンテナの数が増えて要求されるリソースが大きくなった時に、容易にスケールアウトすることが出来ません。

この問題を解決するのが Kubernetes です。

Kubernetes によって、複数台のホストから構成される実行環境を あたかも一台の実行環境のように 扱うことができるようになります。

kube1

コンテナを起動する際は、イメージと台数を指定するだけでよく、クラスタのどこにどのように配置するかは Kubernetes 側で面倒を見てくれます(スケジューリング)。

そして、クラスタのリソース(CPU、メモリ、記憶領域など)が足りなくなった場合は、単純にノードを増やすだけで、既存のサービスに影響を与えることなく、いくらでも拡張することができます。

2. Self-healing – 耐障害性

kube2

上図はデプロイプロセスの詳細です。

  1. オペレーターが、どのようなコンテナを何台起動するかといった情報を Spec として Kubernetes 側に渡すと、
  2. Scheduler が、空きリソースを見ながらそれらをどのように配置するかを決定し、
  3. 各ノードに常駐している Kubelet というプログラムがその決定に従ってコンテナを起動します

オペレーターが直接コンテナを起動するのではなく、必要とする状態を Spec として渡すと、Kubernetes 側がクラスタの状態を Spec に合わせようする、というこの挙動が重要です。

仮に、運用中のコンテナに不具合があってサービスがダウンしたとします。Kubernetes はこの状態変化を察知し、Spec の状態に合わせようとして、そのコンテナを自動的に再起動します(Self-healing)。

多少の不具合であれば問題なく運用出来てしまう反面、問題の発覚するタイミングが遅れてしまう可能性もあるのでモニタリングが重要になります。

コンテナではなく、ノードとなるホストマシンに障害があった場合はどうなるか? Kubernetes 環境のセットアップによりますが、AWS の場合は Auto Scaling グループでクラスタが組まれているので、自動的にインスタンスが作り直され、その中に元の状態が復元されます。

3. Pod – 管理上の基本単位

Kubernetes 上で動作するプログラムの最小単位はコンテナですが、管理上の基本単位は Pod というものになります。

pod

Pod は、Volume という記憶領域を共有するコンテナの集まりで、Volume の他には一つのIPアドレスを共有しています。つまり、Pod は Kubernetes 上でホストに相当する単位です。

VM イメージによるパッケージングでは、一つのホストがデプロイの単位になっていました。あるいは、Java EE の WAR パッケージだと、必要最低限のプログラムだけをデプロイの単位に出来て軽量ですが、上図で言うと、デプロイの対象は webapp の部分だけになります。Docker/Kubernetes 環境でマイクロサービス化を推進すると、ホストを構成する様々な部品(webapp, nginx, log collectorなど)全てを、独立してデプロイ出来るようになります。

kubectl というコマンドがセットアップされていれば、以下のようにして Pod の一覧を見ることができます。

$ kubectl get pods
NAME                                 READY     STATUS    RESTARTS   AGE
cotoami-2660026290-81y2g             1/1       Running   0          9d
cotoami-2660026290-xzua4             1/1       Running   0          9d
default-http-backend-t4jid           1/1       Running   0          6d
grafana-807516790-8pszk              1/1       Running   0          63d
http-debug-server-2355350955-38jht   1/1       Running   0          6d
http-debug-server-2355350955-c64y1   1/1       Running   0          6d
http-debug-server-2355350955-g3rjq   1/1       Running   0          6d
nginx-ingress-controller-ka0od       1/1       Running   0          6d
node-exporter-556yr                  1/1       Running   0          63d
node-exporter-euprj                  1/1       Running   0          63d
node-exporter-hzdqk                  1/1       Running   0          63d
prometheus-1314804115-9v0yk          1/1       Running   0          54d
redis-master-517881005-ceams         1/1       Running   0          70d

コンテナのデバッグ

Pod 内のコンテナにログインしたい場合は、リストにある Pod の名前をパラメータにして以下のコマンドを実行します。

$ kubectl exec -it http-debug-server-2355350955-38jht /bin/sh
/app # ls
Dockerfile    README.md     circle.yml    index.js      node_modules  package.json

Pod に複数のコンテナが含まれる場合は、-c オプションでコンテナの名前を指定します。

$ kubectl exec -it POD -c CONTAINER /bin/sh

あるいは、以下のコマンドでコンテナが出力するログを見ることができます。

$ kubectl logs redis-master-517881005-ceams
1:M 16 Feb 03:27:35.080 * 1 changes in 3600 seconds. Saving...
1:M 16 Feb 03:27:35.081 * Background saving started by pid 226
226:C 16 Feb 03:27:35.084 * DB saved on disk
226:C 16 Feb 03:27:35.084 * RDB: 0 MB of memory used by copy-on-write
1:M 16 Feb 03:27:35.181 * Background saving terminated with success

# 複数のコンテナがある場合
$ kubectl logs POD CONTAINER 

4. Deployment – Pod の配備と冗長化

Pod の配備と冗長化を担当するのが Deployment という仕組みです。

deployment

ある Pod について、Spec で定義されたレプリカの数を維持する責任を負うのが Replica Set、Replica Set の配備・更新ポリシーを定義するのが Deployment です。

以下は、Deployment 定義の例です。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: http-debug-server
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: http-debug-server
    spec:
      containers:
      - name: http-debug-server
        image: cotoami/http-debug-server:latest
        ports:
        - containerPort: 3000

この内容を、例えば http-debug-server.yaml というファイルに保存し、以下のコマンドを実行すると Deployment として定義された Replica Set がクラスタ内に出来上がります。

$ kubectl create -f http-debug-server.yaml

$ kubectl get deployments
NAME                DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
http-debug-server   3         3         3            3           6s

Rolling Update

Deployment には標準で Rolling Update(無停止更新)の機能が備わっています。

例えば、以下のようなコマンドで Docker イメージのバージョンを更新すると、Replica Set の中のコンテナ全てを一斉に更新するのではなく、稼働中の Pod を何台か維持したまま、一つずつ順番に更新を行います。

$ kubectl set image deployment/DEPLOYMENT CONTAINER=IMAGE_NAME:TAG

5. Service – Pod へのアクセス

Pod への安定的なアクセス手段を提供するのが Service です。

Deployment で配備した Pod にアクセスしようと思っても、実際に Pod がどのノードに配備されているかは分かりません。仮に分かったとしても、Pod は頻繁に作り直されるので、いつまでも同じ Pod にアクセスできる保証はありません。Replica Set に対するロードバランシング機能も必要です。

ClusterIP

そこで Service は、Pod の集合(一般的には Replica Set)に対して安定的にアクセスできる仮想の IP アドレスを割り当てます。これを cluster IP と呼び、Kubernetes クラスタ内だけで通用するアドレスです。cluster IP にアクセスすると、Service の対象となる Pod 群の中のいずれかの Pod にアクセスできます。

NodePort

さらに Service は、クラスタの外部から Pod にアクセスするための経路も開いてくれます。具体的には、各ノードの特定のポートを経由して Pod にアクセス出来るようになります。

service

上図のように、Service の実体は、cluster IP のルーティングとロードバランシング機能を実現する kube-proxy というプログラムです。

クラスタの外部から Service が用意してくれたポートを経由してノードにアクセスすると、kube-proxy 経由で適切な Pod に接続することができます。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中