私は何年にもわたって、Apache、Bind、MySQL、PHPなど、基本的サービスを行うサーバを使用してきました。別のプロバイダが安価になると、別のサーバに移行することがあります。けれどもWebサイトの一部がPHPの特定のバージョンに依存しているため、サーバ上でホストされている複数のWebサイトがあると、更新が難しくなることがあります。このような問題があるため、多くの場合、サーバは維持することにし、移行に伴うトラブルを避けることになります。この手の話は皆さんよくご存じですね。

しばらくはDockerエコシステムを使っていましたが、コンテナに移行すれば、問題が解決することが分かりました。それで、フランスでホストされている3つのGentooから、米国でホストされている2つのDebianとフランスでホストされている1つのGentooへ移行することにしたのです。

これから、Kubernetesを使用してDockerのインストールとコンテナの管理を行う方法を説明します。

コンテナを管理するシステムはいくつかあります。Amazon EC2コンテナサービスRancher、Kubernetesなど。複数の環境でインストールでき、1つのプロバイダに拘束されることがないので、Kubernetesを選択しました。Nuxeoでは、Kubernetesも使用します。

Dockerのインストール

最初のステップは非常に簡単です。

DebianでDockerのインストール:

apt-get install docker.io

GentooでDockerのインストール:

emerge -v docker

次のステップでKubernetesをインストールします。

Kubernetesのインストール

Dockerの簡単なインストール手順を使用する場合は、クラスタがなく、Kubernetesのメリットを失うことになります。

複数のDockerのほうが優れたソリューションです。まず、Dockerサービスを使用して、共有ネットワークを有効にするetcdflannelを起動すると、Kubernetesはその構成を保存・共有することができます。

[email protected]:/home/shared# etcdctl member list
eacd7f155934262: name=b5.loopingz.com peerURLs=http://91.121.82.118:2380 clientURLs=http://91.121.82.118:2379,http://91.121.82.118:4001
2f0f8b2f17fffe3c: name=c2.loopingz.com peerURLs=http://198.245.51.134:2380 clientURLs=http://198.245.51.134:2379,http://198.245.51.134:4001
88314cdfe9bc1797: name=default peerURLs=http://142.4.214.129:2380 clientURLs=http://142.4.214.129:2379,http://142.4.214.129:4001

これで様々なネットワークが利用できます。

  • 0.0.0/16は、Kubernetesサービス/負荷分散装置を表します。
  • 1.0.0/16は、ポッドが作成される場合、クラスタの各ノードがそのポッド用に10.1.xx.0/24を持ちます。
flannel.1 Link encap:Ethernet  HWaddr c2:67:be:06:2c:11
inet addr:10.1.72.0  Bcast:0.0.0.0  Mask:255.255.0.0
inet6 addr: fe80::c067:beff:fe06:2c11/64 Scope:Link
UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
RX packets:5412360 errors:0 dropped:0 overruns:0 frame:0
TX packets:4174530 errors:0 dropped:21 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1651767659 (1.5 GiB)  TX bytes:3453452284 (3.2 GiB)

GlusterFSのインストール

ポッド構成やノード選択に応じて、コンテナは、クラスタの任意のノード上に作成することができます。つまり、ノード間のストレージを共有することが必要になります。Amazon EFS(ベータ版)、Google Cloud StorageGlusterFSなど、様々なソリューションがあります。

GlusterFSは、共有ストレージのオープンソリューションです。これは、デーモンとして動作し、UDPポート24007を使用します。次のブログでは、GlusterFSのインストールと私が設定したベンチマークの詳細を共有します。

ファイアウォールルールのインストール

一度にすべてのサーバでファイアウォールを更新するために、etcdノード上の変更を待機させ、ルールとクラスタノードに応じてファイアウォールを更新する小さなシェルスクリプトを作成しました。

ファイアウォールの構成を更新

ファイアウォールは、GlusterFSのボリューム上で共有し、fw.confファイルで構成されています。

#!/bin/sh
MD5_TARGET=`md5sum /home/shared/configs/firewall/fw.conf | awk '{print $1}'`
MD5_NEW=`md5sum fw.conf | awk '{print $1}'`
if [ "$MD5_TARGET" == "$MD5_NEW" ]; then
   echo "Not config change"
   exit 0
fi
cp fw.conf /home/shared/configs/firewall/
etcdctl set /cluster/firewall/update $MD5_NEW

値が上記のスクリプトによって変更された場合に限り、?wait=trueのcurlコマンドはタイムアウトになります。それから、ホストのファイアウォールが更新されます。

while :
do
   curl -L http://127.0.0.1:4001/v2/keys/cluster/firewall/update?wait=true
   NEW_HASH=`etcdctl get /cluster/firewall/update`
   if [ "$NEW_HASH" != "$FW_HASH" ]; then
      echo "Update the firewall"
      source /usr/local/bin/firewall_builder
      FW_HASH=$NEW_HASH
   fi
done

Dockerリポジトリのインストール

Kubernetesで使用するコンテナの定義を保存するには、自分専用のコンテナリポジトリを持つことが適切です。

それでは、Kubernetesを使用して最初のポッドをデプロイしてみましょう。

デフォルト形式はYAMLですが、私自身はJSONが気に入っています。

docker-rc.jsonがあります。

{
  "apiVersion": "v1",
  "kind": "ReplicationController",
  "metadata": {
     "name": "docker-repository",
     "labels": {
        "app": "docker-repository",
        "version": "v1"
     }
   },
   "spec": {
      "replicas": 1,
      "selector": {
         "app": "docker-repository",
         "version": "v1"
      },
      "template": {
         "metadata": {
            "labels": {
               "app": "docker-repository",
               "version": "v1"
            }
         },
      "spec": {
         "volumes": [
          {
             "name": "config",
             "hostPath": { "path": "/home/shared/configs/docker" }
          },{
             "name": "data",
             "hostPath": { "path": "/home/shared/docker" }
          }
         ],
         "containers": [
          {
             "name": "registry",
             "image": "registry:2.2.1",
             "volumeMounts": [{"name":"config", "mountPath":"/etc/docker/"},{"name":"data", "mountPath": "/var/lib/registry
"}],
             "resources": {
                "limits": {
                   "cpu": "100m",
                   "memory": "50Mi"
                },
                "requests": {
                   "cpu": "100m",
                   "memory": "50Mi"
                }
             },
             "ports": [
              {
                 "containerPort": 5000
              }
              ]
          }
         ]
        }
      }
   }
}

他にもdocker-svc.jsonがあります。

{
   "apiVersion": "v1",
   "kind": "Service",
   "metadata": {
       "name": "docker-repository",
       "labels": {
           "app": "docker-repository"
       }
    },
    "spec": {
       "type": "LoadBalancer",
       "selector": {
           "app": "docker-repository"
       },
       "clusterIP": "10.0.0.204",
       "ports": [
        {
            "protocol": "TCP",
            "port": 5000,
            "targetPort": 5000
        }
       ]
    }
}

nginxプロキシのインストール

サーバ上で複数のドメインをホストできるように、リバースプロキシが必要となります。nginxのインストールは簡単ですが、追加する際はヘッダに注意してください。

私の場合は、最初のホストはdocker.loopingz.comになります。

server {
  listen 443;
  server_name docker.loopingz.com;
  access_log /var/log/nginx/docker.loopingz.com_access_log main;
  error_log /var/log/nginx/docker.loopingz.com_error_log info;
  client_max_body_size 0;
  chunked_transfer_encoding on;
  location / {
     include /etc/nginx/conf.d/dev-auth;
     proxy_pass        http://10.0.0.204:5000;
     proxy_set_header  X-Real-IP  $remote_addr;
     proxy_set_header  X-Forwarded-Host $host;
     proxy_set_header  X-Forwarded-Server $host;
     proxy_set_header  X-Forwarded-Proto $scheme;
     proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
     add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;
  }
  ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!CAMELLIA;
  ssl_prefer_server_ciphers   on;
  ssl_certificate      /etc/letsencrypt/live/docker.loopingz.com/fullchain.pem;
  ssl_certificate_key  /etc/letsencrypt/live/docker.loopingz.com/privkey.pem;
}

docker-svc.jsonで定義されるクラスタIPであるIP 10.0.0.204を使用することがこのファイルから分かります。Kubernetesは、当社のDockerレジストリコンテナにこのIPをマップします。

すべてのノードからクラスタへのhttp/httpsエントリポイントがあるように、nginxをクラスタのノードすべてにデプロイしたいと思います。それでは、nginxのreplicationControllerを定義してみましょう。

{
   "apiVersion": "v1",
   "kind": "ReplicationController",
   "metadata": {
      "name": "nginx",
      "labels": {
         "app": "nginx",
         "version": "v1"
      }
   },
   "spec": {
      "replicas": 3,
      "selector": {
         "app": "nginx",
         "version": "v1"
      },
      "template": {
         "metadata": {
         "labels": {
            "app": "nginx",
            "version": "v1"
         }
      },
      "spec": {
         "volumes": [
          {
             "name": "config",
             "hostPath": { "path": "/home/shared/configs/nginx/" }
          },{
             "name": "logs",
             "hostPath": { "path": "/home/shared/logs/nginx/" }
          },{
             "name": "certs",
             "hostPath": { "path": "/home/shared/letsencrypt/" }
          },{
             "name": "static",
             "hostPath": { "path": "/home/shared/nginx/" }
            }
          ],
          "containers": [
          {
             "name": "nginx",
             "image": "nginx:latest",
             "volumeMounts": [{"name": "static", "readOnly": true, "mountPath": "/var/www/"},{"name": "certs", "readOnly": true, "mountPath": "/etc/letsencrypt"}, {"name":"logs", "mountPath":"/var/log/nginx/"},{"name":"config", "readOnly": true, "mountPath":"/etc/nginx/conf.d/"}],
             "resources": {
                "limits": {
                   "cpu": "100m",
                   "memory": "50Mi"
                },
                "requests": {
                   "cpu": "100m",
                   "memory": "50Mi"
                 }
              },
              "ports": [
              {
                  "containerPort": 80,
                  "hostPort": 80
              },{
                  "containerPort": 443,
                  "hostPort": 443
                }
              ]
            }
           ]
          }
       }
    }
}

Let’s Encrypt

すべてのバーチャルホストでSSLを有効にするには、3カ月間無料のSSL証明書を取得できるLet’s Encryptを使用します。順序が自動化されているので、すべてのnginxホストは、その構成の中にこれを有しています。

location /.well-known/acme-challenge {
   add_header    "Content-Type:" "application/jose+json" always;
   root /etc/nginx/conf.d;
}

Kubernetesのインストールについて十分に理解できました。さあ、試してみましょう!次に、GlusterFSをベンチマーキングし、新しいサービスを作成する方法についてお話ししますので、ご期待ください!