2018年9月7日金曜日

k8s 上で fluentd を使って外部の ElasticSearch にログを集めてみた

概要

k8s で fluentd を使ってログを収集する場合多くのケースで DaemonSet の仕組みが使われます
DaemonSet は簡単に言えば各 Pod に自動で 1 つコンテナを作成するための定義です
DaemonSet が定義されていると新規でノードが追加された際に自動でログ収集用の fluentd が起動するといった感じです

環境

  • macOS 10.13.6
  • minikube v0.28.2
  • fluent/fluentd-kubernetes-daemonset:elasticsearch (v0.12)

事前作業

ElasticSearch の構築

別の VM に構築しています
方法は何でも OK です
k8s 上に構築したコンテナからエンドポイントにアクセスできるようにしましょう

Kibana の構築 (任意)

確認に使うだけなので任意です
直接 ElasticSearch の API がコールできるのであればそれでも OK です

fluentd の DaemonSet の作成

fluentd が公式で出している定義を流用します
仕組みとしては logging driver は使わずに dockerd が動作しているホストのコンテナログが出力されるパス (/var/lib/docker/containers) をマウントしてそこに出力されるログを ElasticSearch に送ります

namespace や label は変更しても問題ないと思います

  • vim fluentd-ds.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
    version: v1
    kubernetes.io/cluster-service: "true"
spec:
  template:
    metadata:
      labels:
        k8s-app: fluentd-logging
        version: v1
        kubernetes.io/cluster-service: "true"
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd
        image: fluent/fluentd-kubernetes-daemonset:elasticsearch
        env:
          - name: FLUENT_ELASTICSEARCH_HOST
            value: "192.168.99.1"
          - name: FLUENT_ELASTICSEARCH_PORT
            value: "9200"
          - name: FLUENT_ELASTICSEARCH_SCHEME
            value: "http"
          - name: FLUENT_UID
            value: "0"
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

変更するべきは env の部分だけです
ここの IP や Port を外部に構築した ElasticSearch のものに変更すれば OK です
公式だとそれ以外にも env のありましたが不要なので削除しました

  • kubectl create -f fluentd-ds.yaml
  • kubectl get all -n kube-system -l k8s-app=fluentd-logging
NAME                READY     STATUS    RESTARTS   AGE
pod/fluentd-zhnnj   1/1       Running   0          1m

NAME                     DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/fluentd   1         1         1         1            1           <none>          1m

こんな感じで fluentd のコンテナが DaemonSet の仕組みで起動してきます

テスト用のコンテナ作成

ログを出力し続けるコンテナを適当に作成します
動作確認なので Deployment からではなく直接 Pod を作成します

  • vim counter-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']
  • kubectl create -f counter-pod.yaml
  • kubectl get po
NAME      READY     STATUS    RESTARTS   AGE
counter   1/1       Running   0          29s

動作確認

Kibana があればそれを見てみましょう
インデックス (logstash-*) を作成し
k8s_fluentd1.png

タイムスタンプのフィールド (@timestamp) を作成し
k8s_fluentd2.png

Disvoery で dockercontainer_id で絞り込めばログが確認できると思います
k8s_fluentd3.png

インデックス名は logstash-2018.09.03 という感じで日付形式で来るようになっています
fluent.conf の設定はここが参考になると思います

トラブルシューティング

fluentd コンテナが立ち上がったときにログ用のパスがマウントできないエラーが発生しました

2018-09-03 00:05:58 +0000 [error]: unexpected error error_class=Errno::EACCES error=#<Errno::EACCES: Permission denied @ rb_sysopen - /var/log/fluentd-containers.log.pos> 

どうやら fluent ユーザに権限がないために上記のエラーになっているようです
fluentd-ds.yaml に FLUENT_UID の値を 0 に設定することで対応できました
同じような現象に関しての issue が Github にもありました
https://github.com/fluent/fluentd-docker-image/issues/90

お掃除

  • kubectl delete po counter
  • kubectl delete ds fluentd -n kube-system

最後に

kubernetes 上に DaemonSet の仕組みを使って fluentd コンテナを作成してみました
これを作成しておけば
今回は ElasticSearch に向かってログを投げましたが、他にもいろいろなログ集約のサービスに投げれるようです

仕組みとしてはコンテナログのファイルを監視しているだけなので標準出力/標準エラーにログを吐くコンテナであれば kubernetes 上に立てるだけでログが ElasticSearch に送られます

実は今回 minikube 上で実現しています
kubernetes 上でも同じようにできるので minikube にしました
minikube にはアドオンで「efk」というアドオンがあり実はこれを使ってもできます
が、これは e (elasticsearch) と f (fluentd) と k (kibana) のコンテナをすべて minikube 上に展開します
今回の構成は e と k は外部のホストに構築しています
なので kubernetes 上にあるのは f だけになります
f はどうしても k8s になければいけないコンテナになります
普通は e と k は別ホストにあると思うのでそれを想定して動作させてみました

参考サイト

2018年9月6日木曜日

Docker コンテナのログを fluentd を経由して外部の ElasticSearch に送信する方法

概要

fluentd を docker 上で動かしてログを外部の ElasticSearch に送ってみたいと思います
外部というのは fluentd コンテナが動作しているホストとは別のホストのことで例えば ElasticSearch はコンテナではなく VM の上で動作している場合などを想定しています
もちろん ElasticSearch の API のエンドポイントには fluentd コンテナからアクセスできる必要があります

環境

  • macOS 10.13.6
  • docker 18.06.0-ce
  • fluentd (v1.2)
  • ElasticSearch (docker.elastic.co/elasticsearch/elasticsearch:6.4.0)
  • Kibana (docker.elastic.co/kibana/kibana:6.2.4)

Elasticsearch Output Plugin

Dockerhub で公開されている fluentd には基本的にプラグインがインストールされていません
なので ElasticSearch のプラグインがインストールされたイメージを作成します

  • vim Dockerfile
FROM fluent/fluentd

RUN apk add --update --virtual .build-deps \
        sudo build-base ruby-dev \
 && sudo gem install \
        fluent-plugin-elasticsearch \
 && sudo gem sources --clear-all \
 && apk del .build-deps \
 && rm -rf /var/cache/apk/* \
           /home/fluent/.gem/ruby/2.4.0/cache/*.gem
  • docker build -t my_fluentd .

fluent.conf の作成

インストールしたプラグインを使って ElasticSearch にログを送信する設定ファイルを作成します

  • vim fluent.conf
<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

<match docker.**>
  @type copy
  <store>
    @type stdout
  </store>
  <store>
    @type elasticsearch
    host 192.168.99.1
    port 9200
    index_name fluentd
    type_name fluentd
  </store>
</match>

ElasticSearch にログを送るには @type elasticsearch を使います
そして host で外部の ElasticSearch の IP を指定します
今回は copy を使って stdout にも出力していますが不要であれば削除してください

この設定ファイルを使って fluentd コンテナを起動します

fluentd コンテナの起動

  • docker run -d -p 24224:24224 -p 24224:24224/udp -v $(pwd):/fluentd/etc -e FLUENTD_CONF=fluent.conf my_fluentd

先程作成した fluent.conf があるディレクトリを /fluentd/etc にマウントします
そして設定ファイル名を指定するための環境変数 FLUENTD_CONF に設定ファイル名を指定します

あとは TCP/UCP で 24224 をオープンすれば OK です
この 24224 ポートに対して各コンテナはログを投げつけます

動作確認

適当にログを投げつけるコンテナを作成しましょう

  • docker run --rm --log-driver=fluentd --log-opt fluentd-address=192.168.99.1:24224 --log-opt tag="docker.{{.Name}}" alpine /bin/sh -c "while :;do date; sleep 3; done;"

ポイントとなる必須のオプションは 3 つです

  • --log-driver=fluentd
  • --log-opt fluentd-address=192.168.99.1:24224
  • --log-opt tag="docker.{{.Name}}"

1 つ目はロギングドライバを指定するオプションです
2 つ目は外部にある ElasticSearch のエンドポイントです
当然ですがコンテナからアクセスできる必要があります
3 つ目は tag では fluent.conf に定義した match に合わせる必要があります

これでコンテナの stdout が fluentd に拾われて ElasticSearch に投げられます

Kibana を見てみる

ElasticSearch が Kibana と連携しているのであればあとはインデックス (fluentd) を作成するだけでログが見れるようになります

Kibana にアクセスしてインデックスを作成しましょう
ちなみに ElasticSearch に送信されるインデックス名は fluent.conf の index_name fluentd で指定することができます

「index pattern」に「fluentd」と入力して「Next step」を選択します
fluentd_to_es_on_docker1.png

「Create index pattern」を選択します
fluentd_to_es_on_docker2.png

あとは Discover などを見れば ElasticSearch にあるログが確認できると思います
fluentd_to_es_on_docker3.png

最後に

Docker 上で fluentd を動かして各コンテナから受け取ったログを外部の ElasticSearch に送信する方法を紹介しました
各コンポーネントがバラバラに動作しているケースには今回紹介した方法が使えると思います

多くのコンテナからログを受けつける場合には fluentd コンテナのサイズなどを調整しましょう
fluentd クラスタなどを組んでも良いですが今回は紹介しないので興味があれば調べてみてください

2018年9月5日水曜日

ElasticSearch と Kibana を Docker 上に個別に立てて連携する

概要

よく docker-compose.yaml を使って同一ホスト上に ElasticSearch と Kibana を構築例があります
ただその場合だとそれぞれを別のホストでコンテナとして立ち上げたい場合に困ります
今回はそれぞれのコンテナを別のホストに構築することを想定して立ち上げて連携する方法を紹介します

環境

  • macOS 10.13.6
  • docker 18.06.0-ce
  • ElasticSearch (docker.elastic.co/elasticsearch/elasticsearch:6.4.0)
  • Kibana (docker.elastic.co/kibana/kibana:6.2.4)

ElasticSearch を立ち上げる

  • docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.4.0

これで OK です
ホストマシンの IP の 9200 番にアクセスすることができます

  • curl http://192.168.99.1:9200/_cat/health

基本はここがエンドポイントになるので Kibana もここにアクセスします

Kibana を立ち上げる

  • docker run -d -e ELASTICSEARCH_URL=http://192.168.99.1:9200 -p 5601:5601 docker.elastic.co/kibana/kibana:6.2.4

ポイントは ELASTICSEARCH_URL でホストの IP で公開した ElasticSearch を指定するところです
これで Kibana を立ち上げるとホストの IP の 5601 でダッシュボードにアクセスすることができます

http://192.168.99.1:5601/

今回は ElasticSearch も Kibana も同一 IP のホストで run しましたが、それぞれ別のホストで立ち上げても問題なく動作します

今回の構成のポイント

ElasticSearch と Kibana の通信はコンテナ間で直接行われるわけではなく、一旦ホストマシンを介して行われることになります
docker-compose の link 機能などを使うとわざわざホストにポートを公開することなく ElasticSearch と Kibana 間で通知することができます

ただ、その場合は同一ホストもしくはホストを跨いでもコンテナ間で通信できるように Swarm なりを構築する必要があります
しかし今回の構成であればホストの IP に対してアクセスできればいいだけなので Swarm を組んだりする必要はなくホスト間で単純に通信できれば OK なだけです

ElasticSearch はいろいろな場所から参照される可能性があるので今回の構成のようにホストの IP で通信もしくは LB 経由で通信できるようになっている便利かなと思います

最後に

Docker 上で ElasticSearch と Kibana を別のホストで動かす方法を紹介しました
わざわざ Swarm を組んだり docker-compose を作成する必要がないです
もちろん docker-compose で一元管理したいと言う場合はそれでも問題ないですが ElasticSearch はスケールアウトも考慮すると個別のホスト (またはクラスタ) で管理したくなるのが心情かなと思います

参考サイト

2018年9月4日火曜日

minikube + Persistent Volume でデータを永続化してみた

概要

minikube 上で Persistent Volume を試してみました
Persistent Volume はコンテナのデータを永続化するための仕組みです
Persistent Volume にはたくさんのタイプがあります
クラウドサービスのストレージを選択したりすることもできます
今回は一番簡単に構築できる nfs を使ってみます

環境

  • macOS 10.13.6
  • minikube v0.28.2
  • kubectl 1.11.2
  • Ubuntu 16.04 (nfs)
  • nfs-kernel-server 1.2.8 (nfs)

nfs サーバ構築

VirtualBox 上に 1 台 Ubuntu を構築してその上に nfs サーバを構築します
minikube に接続されているネットワーク (vboxnet1 など) を同じように接続して minikube と通信できる必要があります

  • apt -y update
  • apt -y install nfs-kernel-server
  • mkdir -p /opt/nfs
  • echo "/opt/nfs 192.168.99.0/24(rw,async,no_subtree_check,no_root_squash)" >> /etc/exports
  • exportfs -ra
  • systemctl start nfs-kernel-server

マウントできるか確認 (任意)

  • minikube ssh

ログインします

  • sudo mkdir -p /opt/nfs
  • sudo mount -t nfs 192.168.99.200:/opt/nfs /opt/nfs
  • sudo umount /opt/nfs/

Persistent Volume の作成

  • vim pv1.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
  annotations:
    volume.beta.kubernetes.io/storage-class: "slow"
spec:
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    server: 192.168.99.200
    path: /opt/nfs

ポイントは spec -> nfs の部分です
ここで先程構築した nfs サーバの IP と export したマウントポイントを指定します
persistentVolumeReclaimPolicy: Recycle にするとコンテナが削除されたときにマウントした領域のデータを削除します
あまり領域の空きがないのでとりあえず 2GB 分だけ使います
storageClass の名前を「slow」にしておきます
あとは Persistent Volume を作成して確認しましょう

  • kubectl create -f pv1.yaml
  • kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
pv1       2Gi        RWO            Recycle          Available             slow                     48s

この 2GB の領域から更に pod にどれくらい割り当てるか要求します

Persistent Volume Claim の作成

  • vim pvc1.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: slow

先程作成した Persistent Volume から 1GB 使うように要求します
storageClassName: slow で先程の作成した Persistent Volume を指定します
Claim を作成して確認しましょう

  • kubectl create -f pvc1.yaml
  • kubectl get pvc
NAME      STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1      Bound     pv1       2Gi        RWO            slow           3s

VOLUME がちゃんと作成した Persistent Volume の名前になっていることを確認します
この Claim を pod に紐づけます

Pod の作成

分かりやすいように nginx のドキュメントルートを nfs の領域にして index.html を書き換えることで挙動を確認します

  • deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 1
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
        - name: my-nginx
          image: nginx
          ports:
            - containerPort: 80
          volumeMounts:
            - name: pvc1
              mountPath: "/usr/share/nginx/html"
      volumes:
        - name: pvc1
          persistentVolumeClaim:
            claimName: pvc1

deployment, replicaset, pod を定義します
ポイントは volumes と volumeMounts でまず volumes で Claim を指定します
そして Claim の領域を volumeMounts で Pod にマウントするように定義します
あとは普通に nginx の deployment, replicaset, pod を定義する感じです

  • kubectl create -f pod.yaml
  • kubectl get all
NAME                            READY     STATUS    RESTARTS   AGE
pod/my-nginx-67bccc7c55-r7m54   1/1       Running   0          6s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   21h

NAME                       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-nginx   1         1         1            1           6s

NAME                                  DESIRED   CURRENT   READY     AGE
replicaset.apps/my-nginx-67bccc7c55   1         1         1         6s

サービスを作成する

nginx にアクセスできるようにします

  • kubectl expose deployment my-nginx --type=NodePort
  • kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        21h
my-nginx     NodePort    10.101.28.191   <none>        80:32257/TCP   3m
  • curl http://$(minikube ip):32257

たぶんこれで 403 が返ってくると思います
403 が返ってくるのはまだ nfs の領域に index.html がないからです

  • kubectl exec my-nginx-67bccc7c55-r7m54 ls /usr/share/nginx/html

index.html を作成する

nfs 上に index.html を作成してみます
nfs サーバ上で直接実行しました

  • cd /opt/nfs
  • echo 'Hello nfs server!' > index.html

これで再度 curl を実行すると上記の文字列が返ってくると思います
また pod に直接ファイルがあるか確認しても良いと思います

自分が確認した感じだと nfs 側にファイルを作成してからコンテナ側に反映されるまでに 10 秒くらいラグがありました
仕組みを詳しくわかりませんがリアルタイムではなさそうです (環境依存の可能性もあるかも)

最後に

minikube で Persistent Volume を試してみました
流れとしては Persistent Volume 作成 -> Persistent Volume Claim 作成 -> Pod でマウントという感じでした

Pod の定義をするときに YAML でちゃんと Claim とコンテナでマウントする際のマウントポイントをちゃんと指定する必要があります
また PV -> PVC を作成するときにちゃんと PVC が作成した PV を使っているのか確認しましょう

あとは若干ラグがあるのが気になりました
もしかすると nfs の export 時のオプションを変更するともう少し早く同期するかもしれません

参考サイト

2018年9月3日月曜日

Kubernetes のローリングアップデートを試してみる

概要

k8s のローリングアップデート (rollout) は Deployment と ReplicaSet を使うことで実現できます
今回はローリングアップデート/ロールバックを試してみました
なお、環境は minikube を使っています

環境

  • macOS 10.13.6
  • minikube v0.28.2
  • kubectl 1.11.2

Deployment 作成

  • vim echoserver.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-echo
spec:
  selector:
    matchLabels:
      run: my-echo
  replicas: 1
  template:
    metadata:
      labels:
        run: my-echo
    spec:
      containers:
        - name: my-echo-c
          image: k8s.gcr.io/echoserver:1.3
          ports:
            - containerPort: 8080
  • kubectl create -f echoserver.yaml --record

--record を付与することで rollout の一覧を確認したときに実行時のコマンドを残すことができます (後述)

Service 作成

  • vim echoserver_svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-echo
spec:
  type: NodePort
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    run: my-echo

アクセスしやすいように Service を作成しておきます

  • kubectl get svc my-echo
NAME      TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
my-echo   NodePort   10.97.177.175   <none>        8080:30390/TCP   1m

minikube の IP の 30390 にアクセスすることでアプリにコンテナにアクセスできます

コンテナ確認

  • curl -s $(minikube ip):30390 | grep 'server_version'
server_version=nginx: 1.9.11 - lua: 10001

nginx のバージョンが 1.9.11 になっています

ローリングアップデート

先程の Deployment のファイルを新たに作成します
内容はほぼ同じで今回は image の部分を更新しています

  • vim echoserver.yaml.1.4
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-echo
spec:
  selector:
    matchLabels:
      run: my-echo
  replicas: 1
  template:
    metadata:
      labels:
        run: my-echo
    spec:
      containers:
        - name: my-echo-c
          image: k8s.gcr.io/echoserver:1.4
          ports:
            - containerPort: 8080
  • kubectl apply -f echoserver.yaml.1.4 --record

create ではなく apply を使います

コンテナ確認

しばらくすると新たにコンテナが作成されます
Pods の状態を見てるとわかりますが、古いコンテナが削除されて新しいコンテナが起動するのが確認できると思います

  • curl -s $(minikube ip):30390 | grep 'server_version'
server_version=nginx: 1.10.0 - lua: 10001

同じエンドポイントにアクセスしていますが先程のバージョンとは違うバージョンになっていることがわかります

ロールバック

やっぱり元に戻したい場合に行います

  • kubectl get rs
NAME                 DESIRED   CURRENT   READY     AGE
my-echo-5bd677d688   1         1         1         54s
my-echo-7f5b5f6bf    0         0         0         3m

k8s はコンテナのデプロイ履歴を ReplicaSet として保存しています
数字が 0 になっていない方が現在デプロイされている ReplicaSet になります

  • kubectl rollout history deployment my-echo
deployments "my-echo"
REVISION  CHANGE-CAUSE
1         kubectl create --filename=echoserver.yaml --record=true
2         kubectl apply --filename=echoserver.yaml.1.4 --record=true

rollout コマンドを使うことでその履歴を確認することができます
リビジョン番号が大きいほうが新しいバージョンになります
今回は 1 番に戻したいので以下のコマンドを使います

  • kubectl rollout undo deployment my-echo --to-revision=1

要するにリビジョン 1 番のときに実行したコマンドの状態に戻すということをしています

コンテナ確認

  • curl -s $(minikube ip):30390 | grep 'server_version'
server_version=nginx: 1.9.11 - lua: 10001

という感じでまた元のバージョンに戻っていることが確認できると思います
リビジョンもまた新しい番号が振られています

  • kubectl rollout history deployment my-echo
deployments "my-echo"
REVISION  CHANGE-CAUSE
2         kubectl apply --filename=echoserver.yaml.1.4 --record=true
3         kubectl create --filename=echoserver.yaml --record=true

最後に

k8s でローリングアップデートとロールバックを試してみました
アップデートは Deployment を更新することで自動的にコンテナの再作成を行ってくれます
ロールバックは ReplicaSet の履歴管理を使うことで実現します
履歴に振られているリビジョン番号を指定することで戻りたい状態に簡単に戻すことができます

なお今回試したローリングアップデータは k8s で言うところの rollout になります
同じような機能で rolling-update というコマンドがありますが、それは現在だと非推奨になっています
rolling-update は Replication Controller に対して実行できるアップデート機能で rollout は Deployments に対して行えるアップデート機能です

参考サイト

2018年9月2日日曜日

minikube で Ingress の機能を使ってみた

概要

minikube ではアドオンの概念がありこれを使って Ingress を ON にするだけでホスト名ベースのロードバランシングができるようになります
今回は簡単な Web アプリを Ingress にぶら下げて挙動を確認してみました

環境

  • macOS 10.13.6
  • minikube v0.28.2
  • kubectl 1.11.2

Ingress を有効にする

  • minikube addons enable ingress

これだけで OK です

  • minikube addons list

アドオンの一覧を確認できます
enable になっていれば OK です

  • kubectl get all -n kube-system

pod の一覧を調べると Ingress 用の pod が生成されています
どうやら内部的には nginx が立ち上がりそれを使って Web アプリとして立ち上げた pod をバランシングしているようです

pod/nginx-ingress-controller-5984b97644-rb48q
deployment.apps/nginx-ingress-controller
replicaset.apps/nginx-ingress-controller-5984b97644

Ingress を試す

では Ingress の挙動を確認しましょう
まずは pod を作成し service を expose します

  • kubectl run hello-minikube --image=k8s.gcr.io/echoserver:1.4 --port=8080
  • kubectl expose deployment hello-minikube

これでコンテナ自体は立ち上がります
ただしまだ外部からアクセスすることができません
これを Ingress にぶら下げてアクセスできるようにします

Ingress の生成

Ingress は YAML ファイルの定義から生成します

  • vim ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello
spec:
  rules:
  - host: hello.minikube.dev
    http:
      paths:
      - backend:
          serviceName: hello-minikube
          servicePort: 8080

ポイントは backend の部分でここで指定している serviceName を Ingress にぶら下げてます
Ingress から servicePort で指定した Web アプリケーションのポートに対してリクエストを投げます
なので servicePort で指定するポートは expose したポートにします

  • kubectl create -f ingress.yaml

これで Ingress を登録しましょう
登録した Ingress の一覧は get で確認できます

  • kubectl get ingress

動作確認

ぶら下げた Web アプリにホストベースでアクセスしてみます
今回は ingress.yaml で hello.minikube.dev という名前のホストとして Web アプリをぶら下げたのでヘッダに指定します

  • curl http://minikube ip-H "Host: hello.minikube.dev"

Host の指定がないと Ingress (nginx) が振り先がわからず 404 になってしまします
これでアクセスすると今回の Web アプリ (echoserver) からレスポンスが返ってくると思います

CLIENT VALUES:
client_address=172.17.0.4
command=GET
real path=/
query=nil
request_version=1.1
request_uri=http://hello.minikube.dev:8080/

SERVER VALUES:
server_version=nginx: 1.10.0 - lua: 10001

HEADERS RECEIVED:
accept=*/*
connection=close
host=hello.minikube.dev
user-agent=curl/7.54.0
x-forwarded-for=192.168.99.1
x-forwarded-host=hello.minikube.dev
x-forwarded-port=80
x-forwarded-proto=http
x-original-uri=/
x-real-ip=192.168.99.1
x-request-id=9e3a2e5f6076a7d956fddd0783b7d1ec
x-scheme=http
BODY:
-no body in request-

さらっと minikube の IP にアクセスしていますが minikube はホストが 1 台しかないので当然と言えば当然です
本番環境の kubernetes などでは Ingress 用のホストを準備してそこに nginx が立ち上がりバランシングする感じになります
GKE などでは Google Cloud Load Balancing があるのでそれを使うことが推奨されています

お掃除

Ingress の削除

  • kubectl delete ingress hello

or

  • kubectl delete -f ingress.yaml

Service および Deployment, Replicaset, Pod の削除

  • kubectl delete service,deploy hello-minikube

minikube の停止

  • minikube stop

最後に

minikube で Ingress を試してみました
minikube の場合ホストが 1 台しかないのでアクセスできる IP は --type=NodePort と同じですが今回の場合は Ingress (nginx) を通してコンテナにアクセスします
正直本番に近い構成にすることは難しいですが Ingress の感じを掴んだり、Ingress の定義の登録ができるかなどの確認には使えると思います

参考サイト

2018年9月1日土曜日

GKE で Persistent Disk を使ってみた

概要

GKE 上で MySQL などデータを永続化する場合には Persistent Disk を使用します
今回は Persistent Disk を使って本当にコンテナのデータの永続化ができるか確認してみました
なお、作業は Cloud Shell 上で行っています

環境

  • Google Cloud Kubernetes Engine (2018/08/30 時点)

リージョン設定

  • gcloud config set compute/zone us-central1-b

サンプルダウンロード

  • git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
  • cd kubernetes-engine-samples/wordpress-persistent-disks

クラスタ作成

  • gcloud container clusters create persistent-disk-tutorial --num-nodes=3

gke_persistent_disk1.png

永続ディスク (Google Cloud Persistent Disk) を作成

  • gcloud compute disks create --size 200GB mysql-disk
  • gcloud compute disks create --size 200GB wordpress-disk

gke_persistent_disk2.png

MySQL 用のシークレットの作成

  • kubectl create secret generic mysql --from-literal=password=password12345
  • kubectl get secret
NAME                  TYPE                                  DATA      AGE
default-token-d9kzn   kubernetes.io/service-account-token   3         7m
mysql                 Opaque                                1         7s

MySQL のデプロイ

mysql.yaml を少し修正します

  • vim mysql.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - image: mysql:5.6
          name: mysql
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql
                  key: password
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: mysql-persistent-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: mysql-persistent-storage
          gcePersistentDisk:
            pdName: mysql-disk
            fsType: ext4
  • kubectl create -f mysql.yaml
  • kubectl get all
NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/mysql   1         1         1            1           2m

NAME                  DESIRED   CURRENT   READY     AGE
rs/mysql-7bfbdc45b7   1         1         1         2m

NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/mysql   1         1         1            1           2m

NAME                  DESIRED   CURRENT   READY     AGE
rs/mysql-7bfbdc45b7   1         1         1         2m

NAME                        READY     STATUS    RESTARTS   AGE
po/mysql-7bfbdc45b7-fgwj8   1/1       Running   0          2m

NAME             TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
svc/kubernetes   ClusterIP   10.39.240.1   <none>        443/TCP   25m

pod の STATUS が Running になるまで待ちます
Persistent Disk のアタッチ作業があるため多少時間がかかります

MySQL のサービス作成

  • kubectl create -f mysql-service.yaml
  • kubectl get svc mysql
NAME      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
mysql     ClusterIP   10.39.246.175   <none>        3306/TCP   34s

Wordpress のデプロイ

  • vim wordpress.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - image: wordpress
          name: wordpress
          env:
          - name: WORDPRESS_DB_HOST
            value: mysql:3306
          - name: WORDPRESS_DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysql
                key: password
          ports:
            - containerPort: 80
              name: wordpress
          volumeMounts:
            - name: wordpress-persistent-storage
              mountPath: /var/www/html
      volumes:
        - name: wordpress-persistent-storage
          gcePersistentDisk:
            pdName: wordpress-disk
            fsType: ext4
  • kubectl create -f wordpress.yaml
  • kubectl get pod -l app=wordpress
NAME                         READY     STATUS    RESTARTS   AGE
wordpress-65d68b8b45-4ktbn   1/1       Running   0          2m

Running になれば OK です

Wordpress サービスの公開

  • kubectl create -f wordpress-service.yaml
  • kubectl get svc -l app=wordpress
NAME        TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)        AGE
wordpress   LoadBalancer   10.39.243.80   35.194.38.154   80:32711/TCP   3m

ロードバランサ (Google Cloud Load Balancer) が同時に作成されるので少し待ちます
EXTERNAL-IP が付与されれば OK です

gke_persistent_disk3.png

動作確認

Wordpress にアクセスする

先程の EXTERNAL-IP にアクセスすると Wordpress の初期化画面が表示されると思います
ブログのサイトやユーザ名/パスワードを適当に設定しましょう
gke_persistent_disk4.png

永続性をテストしてみる

意図的に MySQL の pod を削除してみましょう

  • kubectl delete pod -l app=mysql

すると自動で MySQL の pod を生成しなおしてくれます

  • kubectl get pod
NAME                         READY     STATUS              RESTARTS   AGE
mysql-7bfbdc45b7-fgwj8       0/1       Terminating         0          21m
mysql-7bfbdc45b7-zfkxh       0/1       ContainerCreating   0          4s
wordpress-65d68b8b45-4ktbn   1/1       Running             0          13m

新しい MySQL 用のコンテナが Running に慣れば OK です

NAME                         READY     STATUS    RESTARTS   AGE
mysql-7bfbdc45b7-zfkxh       1/1       Running   0          1m
wordpress-65d68b8b45-4ktbn   1/1       Running   0          14m

これで再度 Wordpress にアクセスしてみると再度初期化を行うことなく Wordpress が使えると思います
先程初期化したときのユーザ名/パスワードでログインできるか確認してみてください (記事などをすでに作成している場合はそれがあるか確認しても良いと思います)

お掃除

  • kubectl delete service wordpress
  • gcloud compute forwarding-rules list

これが Listed 0 items. になるのを待ちます

  • gcloud container clusters delete persistent-disk-tutorial
  • gcloud compute disks delete mysql-disk wordpress-disk

あとは GKE クラスタと Persistent Disk を削除すれば OK です 

最後に

GKE + Persistent Disk を試してみました
サンプルを動かしただけですがかなり簡単に動作させることができます

実際は自分たちのサービス用の YAML ファイルを作成する必要はありますがそれ以外は流用できるかなと思います

参考サイト