続・KubernetesによるDockerコンテナ管理入門

Dockerなどのコンテナ技術を使ったクラスタ技術の1つに「Kubernetes」がある。今回はこのKubernetesにおけるネットワーク関連の設定や、コンテナのデプロイについて紹介する。

今回紹介するKubernetesは、Googleが開発を主導するコンテナクラスタ構築ツールだ(図1)。Googleのクラウドサービスでも利用できることもあり、ここ数年で急激に利用者を増やしている。その勢いから、最近ではDockerも公式にKubernetesを公式にサポートすることを表明しており、今後もますます利用者が増えると予想される。

図1 KbernetesのWebサイト
図1 KbernetesのWebサイト

Kubernetesについては以前KubernetesによるDockerコンテナ管理入門という記事で基礎的な導入手順を紹介しているが、今回はネットワーク管理やサービスの管理など、より実用的な環境を構築するための方法を紹介する。

なお、本記事ではテスト環境としてCentOS 7.4およびCentOS 7.4で公式に提供されているKubernetes 1.5.2を使用した。それ以外の環境の場合、設定方法などが異なる場合があるかもしれないので注意いただきたい。

Kubernetesのネットワーク機能

KubernetesによるDockerコンテナ管理入門記事ではKubernetesのネットワーク機能についてあまり触れていなかったが、Kubernetesではコンテナ間やコンテナ内外の通信を管理するための複数の機能が用意されている。まずはこういったネットワーク関連の機能について紹介しよう。

DNSの利用

ほかのコンテナと通信が必要になるようなコンテナをKubernetes内で稼動させる場合、そのコンテナに割り当てられたIPアドレスを知る必要がある。以前の記事では環境変数を使ってほかのコンテナのIPアドレスを取得する方法を説明したが、DNSを利用してほかのコンテナと通信できるように設定することも可能だ。

この方法では、Kubernetesクラスタ内でDNSサーバーを稼働させ、Pod名やサービス名とそのIPアドレスを対応付けることで、Pod名やサービス名をホスト名として利用しての通信が可能になる。KubernetesではKubernetes向けのDNSサーバーが公式に提供されており、これを利用することで簡単な設定だけでDNSが利用できるようになる。

DNS(kube-dns)の設定

記事執筆時点でのKubernetesの最新版であるKubernetes 1.9ではアルファ版機能として「CoreDNS」という機能が用意されている。ただ、まだ実験的な段階ということで、今回はそれ以前のバージョンのKubernetesでも利用できる「kube-dns」というDNS機能について紹介する。

kube-dnsはKubernetes上のコンテナ内で稼動するようになっており、ほかのコンテナと同様、設定ファイルからリソースを作成することで利用が可能になる。kube-dnsを稼動させるための設定ファイルはGitHub上で公開されている。このうちkube-dns.yaml.sedファイルをダウンロードして「kube-dns.yaml」にリネームした後、ファイル中の「$DNS_SERVER_IP」をkube-dnsサービスに割り当てるIPアドレスに、「$DNS_DOMAIN」をネットワーク内のドメイン名に置き換えれば良い。$DNS_DOMAINについてはファイル中に複数記述されているので注意したい。

ここで$DNS_SERVER_IPで指定するIPアドレスはKubernetesのapiserverの設定ファイル(/etc/kubernetes/apiserver)中にある「KUBE_SERVICE_ADDRESSES」で指定したサービス用のIPアドレス範囲内である必要がある。たとえば次のように設定されていた場合、10.254.0.0/16内のIPアドレスでなければならない。

KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=10.254.0.0/16"

また、ネットワーク内のドメイン名($DNS_DOMAIN)は任意の名前を指定できるが、「cluster.local」などのようにドメイン名として利用できるものである必要がある。

なお、この設定ファイルのうちいくつかの項目は今回使用したKubernetes 1.5系では正しく認識されないため、次の部分をコメントアウトして利用した。

  template:
    metadata:
      labels:
        k8s-app: kube-dns
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ''
    spec:
#      priorityClassName: system-cluster-critical
#      tolerations:
#      - key: "CriticalAddonsOnly"
#        operator: "Exists"
      volumes:
      - name: kube-dns-config
        configMap:
          name: kube-dns
#          optional: true
      containers:
      - name: kubedns

編集後、次のようにしてこのファイルを使ってリソースを作成するとkube-dnsが起動し、クラスタ内から$DNS_SERVER_IPで指定したIPアドレスでアクセスできるようになる。

$ kubectl create -f kube-dns.yaml 

ちなみにkube-dnsは「kube-system」というネームスペース内で稼動するよう設定されている。ネームスペースはサービスを分離する単位で、特に指定しない場合は「default」が使われる。ネームスペースが明示された場合(default以外のネームスペースが指定された場合)、kubectlコマンドでは「-n <ネームスペース>」オプションで明示的にそのネームスペースを指定しない限り、そのリソースにはアクセスできない。ネームスペース一覧は「kubectl get namespace」コマンドで確認できる。

$ kubectl get namespace
NAME          STATUS    AGE
default       Active    7d
kube-system   Active    7d

今回のkube-dnsはkube-systemネームスペース内で稼動しているので、次のようにして稼働状況を確認できる。

$ kubectl -n kube-system get pod
NAME                        READY     STATUS    RESTARTS   AGE
kube-dns-1875453744-86v4w   3/3       Running   0          19h

また、kube-dnsにアクセスするためのIPアドレスは「kubectl get svc」コマンドでも確認できる。たとえば次の例では、「10.254.254.1」というIPアドレスでDNSサーバーが稼働していることが分かる。

$ kubectl -n kube-system get svc
NAME       CLUSTER-IP     EXTERNAL-IP   PORT(S)         AGE
kube-dns   10.254.254.1   <none>        53/UDP,53/TCP   2d

続いて、コンテナ内からこのDNSサーバーを使って名前解決を行えるように設定を行う。これは、各クラスタノード上の/etc/kubernetes/kubelet設定ファイル内に次の項目を追加すれば良い。

KUBELET_ARGS="--cluster-dns=<DNSサーバーのIPアドレス> --cluster-domain=<ネットワークのドメイン名>"

たとえばDNSサーバーのIPアドレスが「10.254.254.1」、ネットワークのドメイン名が「cluster.local」の場合、次のように指定する。

KUBELET_ARGS="--cluster-dns=10.254.254.1 --cluster-domain=cluster.local"

この設定を追加後、各クラスタノード上でkubeletサービスを再起動すると設定が反映される。設定の反映後は、起動されたコンテナ内の/etc/resolv.confファイル内に指定したIPアドレスが自動的に追加され、名前解決が行えるようになる。

/ # cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.254.254.1
nameserver 133.242.0.3
nameserver 133.242.0.4
options ndots:5

ただし、kubeletサービスの再起動時にすでに起動されているコンテナについてはこの設定は反映されないので注意しよう。

ポッド/サービスに対する名前割り当てルールはkubernetsのドキュメントに記載されているが、次のようなものになっている。

  • サービスの場合:<サービス名>.<ネームスペース>.svc.<指定したネットワークドメイン名>
  • Podの場合:<IPアドレスの「.」を「-」に置き換えた文字列>.<ネームスペース>.pod.<指定したネットワークドメイン名>

たとえばネットワークドメインが「cluster.local」で、kube-systemネームスペース内で動作するkube-dnsには、「kube-dns.kube-system.svc.cluster.local」というホスト名でアクセスできる。

# nslookup kube-dns.kube-system.svc.cluster.local
nslookup: can't resolve '(null)': Name does not resolve

Name:      kube-dns.kube-system.svc.cluster.local
Address 1: 10.254.254.1 kube-dns.kube-system.svc.cluster.local

実際にはresolv.confで同時にサーチパスの設定も行われるため、「<サービス名>.<ネームスペース名>」というホスト名でもアクセスが可能だ。

サービスに割り当てたIPアドレスと外部との通信

Kubernetesではサービスを作成してIPアドレスを割り当てることで、Podに対し固定されたIPアドレスを割り振ることができる。しかし、基本的にはこのIPアドレスに対し外部からの接続は行えない。そのため、Kubernetesではサービスを外部に公開するために次の2つの方法が用意されている。

  • LoadBalancer:外部ロードバランサーと連携させる。外部ロードバランサー側がKubernetesに対応している必要がある
  • NodePort:Kubernetesのマスター/ノードの指定したポートへのアクセスを指定したサービスに転送する

LoadBalancerではサービスに直接外部からアクセスできるIPアドレスを割り当てることができるが、対応するロードバランサーが必要となる。GoogleのGoogle Cloud Platform(GCP)やMicrosoft Azureといったクラウドサービスでは対応ロードバランサーが提供されているが、そうでない環境で独自に対応ロードバランサを構築するのはややハードルが高い。そのため、今回はNodePortを利用したサービスの公開について紹介する。

NodePortは、サービスの作成時に「type: NodePort」を指定するだけで利用可能になる。サービスの設定方法については以前の記事で説明しているので割愛するが、たとえば「httpd」というPodを作成し、このPodへのアクセスを行うサービスを作る場合の設定ファイルの例は次のようになる。

apiVersion: v1
kind: Pod
metadata:
  name: httpd
  labels:
    app: httpd
spec:
  containers:
  - name: httpd
    image: httpd
---
apiVersion: v1
kind: Service
metadata:
  name: httpd-svc
spec:
  type: NodePort
  ports:
    - port: 80
      nodePort: 30080
  selector:
    app: httpd

ちなみに、Kubernetesで使用する設定ファイルでは複数のリソースに対する設定を「---」で区切って1つのファイルにまとめることができる。このファイル前半(「kind: Pod」)がPodに関する設定、後半(「kind: Service」)がサービスに関する設定だ。前半のPodに関する設定では、「httpd」という名前で「httpd」イメージからコンテナを作成するよう記述している。そして後半のサービスに関する設定では、「httpd-svc」というサービスを作成し、「httpd」という名前のPodと関連付けるよう記述している。ここでは「type:」として「NodePort」を指定し、「ports」以下で「nodePort: 30080」を指定することで、30080番ポートへのアクセスを、この「httpd」Podの80番ポートに転送するよう指定している。

この設定ファイルを「svc-httpd.yml」という名前で保存し、kubectlコマンドでリソースを作成すると、「httpd」Podと「httpd-svc」サービスが作成される。

$ kubectl create -f svc-httpd.yml
pod "httpd" created
service "httpd-svc" created

「kubectl get svc」コマンドで作成したサービスを確認すると、このサービスに「10.254.147.51」という内部的なIPアドレスが割り当てられていることが確認できる。また、「EXTERNAL-IP」が「<nodes>」に、「PORT(S)」は「80:30080/TCP」となっており、ノードの30080番ポートへのアクセスがこのサービスに転送されることが確認できる。

$ kubectl get svc
NAME         CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
httpd-svc    10.254.147.51   <nodes>       80:30080/TCP   9m
kubernetes   10.254.0.1      <none>        443/TCP        7d

ここでKubernetesのマスターサーバーから次のように30080番ポートにアクセスしてみると、httpdからレスポンスが帰ってきていることが分かる。

$ curl -v 127.0.0.1:30080
* About to connect() to 127.0.0.1 port 30080 (#0)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 30080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 127.0.0.1:30080
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Thu, 15 Mar 2018 12:04:43 GMT
< Server: Apache/2.4.29 (Unix)
< Last-Modified: Mon, 11 Jun 2007 18:53:14 GMT
< ETag: "2d-432a5e4a73a80"
< Accept-Ranges: bytes
< Content-Length: 45
< Content-Type: text/html
<
<html><body><h1>It works!</h1></body></html>
* Connection #0 to host 127.0.0.1 left intact

さらに、マスターサーバーだけでなく各ノード上でも、127.0.0.1:30080へのアクセスは同様にhttpd Podに転送される。ここで注意したいのが、127.0.0.1(localhost)だけでなくそのホスト上のネットワークインターフェイスに割り当てられたすべてのIPアドレスに対し、指定したポートへの接続がすべて転送される点だ。

$ curl ***.***.***.***:30080  ←***.***.***.***はホストに割り当てたグローバルIPアドレス
<html><body><h1>It works!</h1></body></html>
$ curl 192.168.1.20:30080  ←192.168.1.20はホストに割り当てたプライベートなIPアドレス
<html><body><h1>It works!</h1></body></html>
$ curl 172.17.69.1:30080  ←172.17.69.1はDockerが使用するIPアドレス
<html><body><h1>It works!</h1></body></html>

そのため、別途ファイアウォールなどの設定を行って適切に接続を管理する必要がある。

Kubernetesで管理できるリソース

KubernetesによるDockerコンテナ管理入門記事では、「Pod」と呼ばれる単位でコンテナを管理できることを解説した。この記事では1つのPodに対し1つのコンテナを割り当てていたが、Podでは協調して動作するような複数のコンテナを同時に管理することもできる。また、Podを複数のホストで動作させるための「ReplicaSet」や「Deployments」といった機能も用意されている。これらについても紹介しておこう。

複数のコンテナから構成されるPod

Podでは、「spec.containers」以下に複数のコンテナに関する情報を記述することができる。この場合、記述されたコンテナはすべて同一のノード上で実行される。次の例は、「mysql」コンテナおよび「wordpress」コンテナの2つを記述したものだ。ここではサービスを利用してmysqlコンテナに対しホスト名でアクセスできるよう設定している。そのため、実行時にはkube-dnsが動作している必要がある。

ちなみに、mysqlイメージwordpressイメージでは環境変数を指定することで起動時にデータベースを作成したり、WordPressで使用するデータベースの設定を行う仕組みが用意されており、今回はこれを使って初期設定を行っている。

apiVersion: v1
kind: Service  ←30080番ポートでwordpressコンテナの80番ポートにアクセスできるようサービスを作成
metadata:
  name: wordpress
spec:
  type: NodePort
  ports:
    - port: 80
      nodePort: 30080
  selector:
    app: wordpress
---
apiVersion: v1
kind: Service  ←mysqlコンテナにホスト名「wordpress-mysql」でアクセスできるようサービスを作成
metadata:
  name: wordpress-mysql
spec:
  ports:
    - port: 3306
  selector:
    app: wordpress
---
apiVersion: v1
kind: Pod
metadata:
  name: wordpress-app
  labels:
    app: wordpress
spec:
  containers:
  - name: mysql  ←mysqlコンテナに関する設定
    image: mysql
    env:
    - name: MYSQL_RANDOM_ROOT_PASSWORD
      value: "yes"
    - name: MYSQL_DATABASE
      value: wordpress
    - name: MYSQL_USER
      value: wordpress
    - name: MYSQL_PASSWORD
      value: <適切なパスワード>

  - name: wordpress  ←wordpressコンテナに関する設定
    image: wordpress
    env:
    - name: WORDPRESS_DB_HOST
      value: wordpress-mysql  ←ホスト名でmysqlコンテナを指定
    - name: WORDPRESS_DB_USER
      value: wordpress
    - name: WORDPRESS_DB_PASSWORD
      value: <適切なパスワード>
    - name: WORDPRESS_DB_NAME
      value: wordpress

この設定ファイルを「app-wordpress.yml」という名前で保存し、次のように実行するとPodおよびサービスが作成される。

$ kubectl create -f app-wordpress.yml

作成されたPodを確認すると、「READY」項目が「2/2」となっており、2つのコンテナが作成されていることが分かる。

$ kubectl get pod
NAME            READY     STATUS    RESTARTS   AGE
wordpress-app   2/2       Running   0          1m

また、「kubectl describe」コマンドでこのPodについての情報を確認すると、このPodは「centos04/192.168.1.103」というノード上で動作していることが分かる。

$ kubectl describe pod wordpress-app
Name:           wordpress-app
Namespace:      default
Node:           centos04/192.168.1.103
Start Time:     Thu, 15 Mar 2018 21:42:28 +0900
Labels:         app=wordpress
Status:         Pending
IP:
  
  

このホスト上で「docker ps」コマンドを実行すると、次のように2つのコンテナが実行されていることが分かる。

# docker ps
CONTAINER ID        IMAGE                                                           COMMAND                  CREATED              STATUS              PORTS               NAMES
feeb99d3f22e        wordpress                                                       "docker-entrypoint..."   29 seconds ago       Up 27 seconds                           k8s_wordpress.96db94c1_wordpress-app_default_51cf0d3e-284e-11e8-b45e-9ca3ba232de0_9456f743
b7acec30d55f        mysql                                                           "docker-entrypoint..."   About a minute ago   Up About a minute                       k8s_mysql.7bf8a0c_wordpress-app_default_51cf0d3e-284e-11e8-b45e-9ca3ba232de0_207dd98c

この状態でマスターサーバーの30080番ポートにWebブラウザでアクセスするとWordPressの初期設定画面が表示され、正しく2つのコンテナ間で通信できていることが確認できる。

複数のPodを稼動させ負荷分散を行う

1つのPodの設定内に複数のコンテナに関する情報を記述すると、それらのコンテナは必ず同じノード上で実行される。しかし、WordPressとMySQLの場合、コンテナ間で適切に通信さえ行えれば同一のノード上にある必要はない。また、WordPressコンテナについては複数のノード上で稼動させることができれば、それによって負荷分散を行うことができる。そこで続いては、1つのPodを複数のノードで実行させる例を紹介しよう。

1つのPodを複数のノードで実行させるには、「ReplicaSet」もしくは「Deployment」という機能を利用する。まずReplicaSetだが、こちらは指定した数のPodを自動的に作成するものだ。

ReplicaSetを利用して、WordPressを実行するPodについて2つのレプリカを作成して実行する設定ファイルは以下のようになる。

apiVersion: v1
kind: Service
metadata:
  name: wordpress
spec:
  type: NodePort
  ports:
    - port: 80
      nodePort: 30080
  selector:
    app: wordpress
---
apiVersion: v1
kind: Service
metadata:
  name: wordpress-mysql
spec:
  ports:
    - port: 3306
  selector:
    app: wordpress-mysql
---
apiVersion: extensions/v1beta1
kind: ReplicaSet  ←ReplicaSetの定義
metadata:
  name: wordpress-reps
  labels:
    app: wordpress-reps
spec:
  replicas: 2  ←レプリカ数を指定
  template:  ←作成するPodに関する設定を記述
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
      - name: wordpress
        image: wordpress
        env:
        - name: WORDPRESS_DB_HOST
          value: wordpress-mysql
        - name: WORDPRESS_DB_USER
          value: wordpress
        - name: WORDPRESS_DB_PASSWORD
          value: <適切なパスワード>
        - name: WORDPRESS_DB_NAME
          value: wordpress
---
apiVersion: v1
kind: Pod  ←mysqlコンテナについては単体のPodとして作成
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress-mysql
spec:
  containers:
  - name: mysql
    image: mysql
    env:
    - name: MYSQL_RANDOM_ROOT_PASSWORD
      value: "yes"
    - name: MYSQL_DATABASE
      value: wordpress
    - name: MYSQL_USER
      value: wordpress
    - name: MYSQL_PASSWORD
      value: <適切なパスワード>

ここで、「kind: ReplicaSet」以下がReplicaSetに関する設定だ。作成するPodに関する設定は「template:」以下に記述する。この設定ファイルを「app-wordpress2.yml」というファイルに保存し、ここからリソースを作成すると、2つの「wordress-reps-」Podと「wordpres-mysql」Podが作成される。

$ kubectl create app-wordpress2.yml

作成されたPodについて確認してみると、「wordpress-reps-0m621」と「wordpress-reps-wvjk6」という2つのPodがあり、片方はcentos03、もう片方はcentos02というノード上で実行されていることが分かる。

$ kubectl get pod -o wide
NAME                   READY     STATUS    RESTARTS   AGE       IP            NODE
wordpress-mysql        1/1       Running   0          5m        172.17.95.5   centos01
wordpress-reps-0m621   1/1       Running   0          3m        172.17.34.5   centos03
wordpress-reps-wvjk6   1/1       Running   0          3m        172.17.37.6   centos02

ちなみにReplicaSetに関する情報は「kubectl get replicaset」コマンドで確認できる。

$ kubectl get replicaset -o wide
NAME             DESIRED   CURRENT   READY     AGE       CONTAINER(S)   IMAGE(S)    SELECTOR
wordpress-reps   2         2         2         3m        wordpress      wordpress   app=wordpress

ReplicaSetを使って作成したPodを削除するには、「kubectl delete replicaset」コマンドを使用する。

$ kubectl delete replicaset wordpress-reps

Deploymentを使う

続いてDeploymentだが、こちらはReplicaSetのように複数のレプリカを管理する機能に加えて、さらにデプロイ管理機能も提供するものだ。Deploymentを利用する場合の設定ファイルは次のようになる。先のReplicaSetを利用する設定ファイルと中身はほぼ同じで、違いは「ReplicaSet」と指定していた部分を「Deployment」に変更しただけだ。

apiVersion: v1
kind: Service
metadata:
  name: wordpress
spec:
  type: NodePort
  ports:
    - port: 80
      nodePort: 30080
  selector:
    app: wordpress
---
apiVersion: v1
kind: Service
metadata:
  name: wordpress-mysql
spec:
  ports:
    - port: 3306
  selector:
    app: wordpress-mysql
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: wordpress
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
      - name: wordpress
        image: wordpress
        env:
        - name: WORDPRESS_DB_HOST
          value: wordpress-mysql
        - name: WORDPRESS_DB_USER
          value: wordpress
        - name: WORDPRESS_DB_PASSWORD
          value: <適切なパスワード>
        - name: WORDPRESS_DB_NAME
          value: wordpress
---
apiVersion: v1
kind: Pod
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress-mysql
spec:
  containers:
  - name: mysql
    image: mysql
    env:
    - name: MYSQL_RANDOM_ROOT_PASSWORD
      value: "yes"
    - name: MYSQL_DATABASE
      value: wordpress
    - name: MYSQL_USER
      value: wordpress
    - name: MYSQL_PASSWORD
      value: <適切なパスワード>

このファイルを「app-wordpress3.yml」として保存し、実行する。

$ kubectl create -f app-wordpress3.yml

作成されたPodを確認してみると、ReplicaSetを利用した場合と同様に異なるノード上で2つのwordpressコンテナが実行されている。

$ kubectl get pod -o wide
NAME                         READY     STATUS    RESTARTS   AGE       IP            NODE
wordpress-2562138459-ksg28   1/1       Running   0          45s       172.17.35.5   centos04
wordpress-2562138459-vtcm2   1/1       Running   0          45s       172.17.95.5   centos01
wordpress-mysql              1/1       Running   0          45s       172.17.37.6   centos02

DeploymentがReplicaSetと異なるのは、Podのローリングアップデートを行える点だ。たとえば、設定ファイルを編集してwordpressコンテナのイメージを「wordpress」から「wordpress:fpm」に変更したとしよう。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: wordpress
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
      - name: wordpress
        image: wordpress:fpm

変更した設定ファイルは、「kubectl apply」コマンドを実行することで反映できる。

$ kubectl apply -f app-wordpress3.yml
service "wordpress" configured
service "wordpress-mysql" configured
deployment "wordpress" configured
pod "wordpress-mysql" configured

すると、起動中のwordpressコンテナを残しつ、新たなコンテナを作成し、新たなコンテナの作成完了後に古いコンテナが削除される。

$ kubectl get pod -o wide
NAME                         READY     STATUS              RESTARTS   AGE       IP            NODE
curl                         1/1       Running             2          2h        172.17.34.4   centos03
wordpress-1833837272-d8705   0/1       ContainerCreating   0          8s        <none>        centos01
wordpress-1833837272-xj2n2   0/1       ContainerCreating   0          8s        <none>        centos03
wordpress-2562138459-ksg28   1/1       Running             0          4m        172.17.35.5   centos04
wordpress-mysql              1/1       Running             0          4m        172.17.37.6   centos02
$ kubectl get pod -o wide
NAME                         READY     STATUS    RESTARTS   AGE       IP            NODE
curl                         1/1       Running   2          2h        172.17.34.4   centos03
wordpress-1833837272-d8705   1/1       Running   0          2m        172.17.95.6   centos01
wordpress-1833837272-xj2n2   1/1       Running   0          2m        172.17.34.5   centos03
wordpress-mysql              1/1       Running   0          7m        172.17.37.6   centos02

ちなみにDeploymentではReplocaSetを利用してコンテナ世代管理を行っており、アップデート前のコンテナを定義していたReplicaSetはアップデート後も残される。これを利用することで、不具合発生時にロールバックなどの操作を行うことも可能だ。

$ kubectl get replicaset
NAME                   DESIRED   CURRENT   READY     AGE
wordpress-1833837272   2         2         2         2m
wordpress-2562138459   0         0         0         7m

Deploymentで作成したPodは、「kubectl delete deployment」で削除できる。

$ kubectl get deployment
NAME        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
wordpress   2         2         2            2           9m
$ kubectl delete deployment wordpress
deployment "wordpress" deleted

独自にKubernetesクラスタを構築するのはそれなりに大変

さて、以前の記事と合わせて独自にkubernetesクラスタを構築する方法について紹介してきた。これで、kubernetesクラスタを構築して運用する最小限の環境は構築できるはずだ。ただ、実際にkubernetesクラスタを構築・運用していく場合、それなりに手間がかかる点には注意したい。ドキュメントについても、Google Cloud Platform上で利用するための情報は多いものの、それ以外の環境で利用するための情報はやや不十分に感じられた。

また、実運用時に大きな問題となるであろう点はロードバランサの設定だろう。前述の通り、ロードバランサを独自のクラスタで使用するためには煩雑な設定が必要となる。技術的な詳細はサイバーエージェントの技術ブログが詳しいが、現状ではこの問題を簡単に解決する方法はないようだ。

また、Kubernetesは複雑なiptablesルールを生成してネットワークトラフィックの管理を行うため、問題が発生した際のトラブルシューティングが困難になることも考えられる。

とはいえ、Dockerも標準でクラスタ環境としてKubernetesをサポートするなど、コンテナクラスタインフラストラクチャとしてはKubernetesは事実上標準に近い状況になりつつある。今後のアップデートでこうした問題を解決する手段が提供されることを期待したい。