Serverless on Kubernetes : Fission を使ってみた

Kubernetes 上で Serverless を実現する Fission を使ってみた

jedipunkz

2 minute read

こんにちは。 @jedipunkz です。

今日は Kubernetes を使って Serverless を実現するソフトウェア Fission を紹介します。

AWS の Lambda とよく似た動きをします。Lambda も内部では各言語に特化したコンテナが起動してユーザが開発した Lambda Function を実行してくれるのですが、Fission も各言語がインストールされた Docker コンテナを起動しユーザが開発したコードを実行し応答を返してくれます。

それでは早速なのですが、Fission を動かしてみましょう。

動作させるための環境

macOS か Linux を前提として下記の環境を用意する必要があります。また Kubernetes 環境は minikube が手っ取り早いので用いますが、もちろん minikube 以外の kubernetes 環境でも動作します。

  • macOS or Linux
  • minikube or kubernetes
  • kubectl
  • fission

ソフトウェアのインストール方法

簡単にですが、ソフトウェアのインストール方法を書きます。

OS

私は Linux で動作させましたが筆者の方は macOS を使っている方が多数だと思いますので、この手順では macOS を使った利用方法を書いていきます。

minikube

ここでは簡単な手順で kubernetes 環境を構築できる minikube をインストールします。

curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.16.0/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/fission

kubectl

直接必要ではありませんが、kubectl があると minikube で構築した kubernetes 環境を操作できますのでインストールしておきます。

curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/v1.5.2/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/

Fission

Fission のインストールです。

curl http://fission.io/linux/fission > fission && chmod +x fission && sudo mv fission /usr/local/bin/fission

kubernetes の起動

ソフトウェアのインストールが完了したら minikube を使って kubernetes を起動します。

$ minikube start
$ minikube status
minikubeVM: Running
localkube: Running
$ kubectl get nodes
NAME       STATUS    AGE
minikube   Ready     1h

Fission の起動と環境変数の設定

Fission を起動します。

$ kubectl create -f http://fission.io/fission.yaml
$ kubectl create -f http://fission.io/fission-nodeport.yaml

次に環境変数を設定します。

$ export FISSION_URL=http://$(minikube ip):31313
$ export FISSION_ROUTER=$(minikube ip):31314

Fission を使って Python のコードを実行する

例として Python の Hello World を用意します。hello.py として保存します。

def main():
        return "Hello, world!\n"

ではいよいよ、kubernetes と Fission を使って上記の Hello World を実行させます。

まず Fission が用意してくれている Docker コンテナを扱うように ‘env’ を作ります。

$ fission env create --name python-env --image fission/python-env

次に Fission で Function を作ります。その際に上記の env と python コードを指定します。つまり、hello.py を fission/python-env という Docker コンテナで稼働する、という意味です。

$ fission function create --name python-hello -env python-env --code ./hello.py

次に Router を作ります。クエリの Path に対して Fuction を関連付けることができます。

$ fission route add --function python-hello --url /python-hello

Function を実行する環境ができました。実際に curl を使ってアクセスしてみましょう。

$ curl http://$FISSION_ROUTER/python-hello
Hello, world!

hello.py の実行結果が得られました。

まとめと考察

結果から Fission は “各言語の実行環境として Docker コンテナを用いていて、ユーザが開発したコードをそのコンテナ上で起動し実行結果を得られる。また各コード毎に URL パスが指定することができ、それをルータとして関係性を持たせられる” ということが分かりました。AWS の Lambda とほぼ同じことが実現出来ていることが分かると思います。

AWS には Lambda の実行結果を応答するための API Gateway があり、このマネージド HTTP サーバと併用することで API 環境を用意出来るのですが Fission の場合には HTTP サーバも込みで提供されていることも分かります。

あとは、この Fission を提供している元が “Platform9” という企業なのですが、この企業は OpenStack や kubernetes を使ったホスティングサービスを提供しているようです。開発元が一企業ということと、完全な OSS 開発体制になっていない可能性があって、万が一この企業に何かあった場合にこの Fission を使い続けられるのか問題がしばらくはありそうです。Fission 同等のソフトウェアを kubernetes が取り込むという話題は…あるのかなぁ?

kubernetes の Job Scheduler が同等の機能を提供してくれるかもしれませんが、まだ Job Scheduler は利用するには枯れていない印象があります。Fission と Job Scheduler 、どちらがいち早く完成度を上げられるのでしょうか。

Kubernetes Deployments を使ってみた!

Kubernetes Replication Controller の次世代版 Deployments を使ってみました

jedipunkz

4 minute read

こんにちは。 @jedipunkz です。

いま僕らは職場では GKE 上に Replication Controller と Services を使って Pod を起動しているのですが最近の Kubernetes 関連のドキュメントを拝見すると Deployments を使っている記事をよく見掛けます。Kubernetes 1.2 から実装されたようです。今回は Kubernetes の Replication Controller の次世代版と言われている Deployments について調べてみましたので理解したことを書いていこうかと思います。

参考資料

今回は Kubernetes 公式の下記のドキュメントに記されているコマンドを一通り実行していきます。追加の情報もあります。

Deployments を使って nginx Pod を起動

nginx をデプロイするための Yaml ファイルを用意します。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

作成した yaml ファイルを指定して Pod を作ります。 下記の通りここで “–record” と記しているのは、後に Deployments の履歴を表示する際に “何を行ったか” を出力するためです。このオプションを指定しないと “何を行ったか” の出力が “NONE” となります。

$ kubectl create -f nginx.yaml --record

ここで

  • deployments
  • replica set
  • pod
  • rollout

の状態をそれぞれ確認してみます。

$ kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2         2         2            2           8s

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-4087004473   2         2         2         10s

$ kubectl get pods --show-labels
NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-4087004473-6csa7   1/1       Running   0          21s       app=nginx,pod-template-hash=4087004473
nginx-deployment-4087004473-teyzc   1/1       Running   0          21s       app=nginx,pod-template-hash=4087004473

$ kubectl rollout status deployment/nginx-deployment
deployment nginx-deployment successfully rolled out

結果から、下記の事が分かります。

  • yaml に記した通り “nginx-deployment” という名前で deployment が生成された
  • “nginx-deployment-4087004473” という名前の rs (レプリカセット) が生成された
  • yaml に記した通り2つの Pod が起動した
  • “nginx-deployment” が正常に Rollout された

Replication Controller でデプロイした際には作られない replica set, rollout というモノが出てきました。後に Deployments を使うメリットに繋がっていきます。

nginx イメージの Tag を更新してみる

ここで yaml ファイル内で指定していた “image: nginx:1.7.9” を “image: nginx:1.9.1” と更新してみます。 Replication Controller で言う Rolling-Update になります。後に述べますが他にも更新方法があります。

$ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

ここで先ほどと同様に状態を確認してみます。

$ kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2         2         2            2           2m

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   2         2         2         39s
nginx-deployment-4087004473   0         0         0         2m

$ kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-3599678771-0vj9m   1/1       Running   0          53s
nginx-deployment-3599678771-t1y62   1/1       Running   0          53s

ここで…

  • 新しい Replica Set “nginx-deployment-3599678771” が作成された
  • 古い Replica Set “nginx-deployment-4087004473” の Pod は 0 個になった
  • Pod 内コンテナが更新された (NAME より判断)

となったことが分かります。 Replication Controller と異なり、Deployments では以前の状態が Replica Set として保存されていて状態の履歴が追えるようになっています。

ここで Rollout の履歴を確認してみます。

$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION        CHANGE-CAUSE
1               kubectl create -f nginx.yaml --record
2               kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

REVISION という名前で履歴番号が付き、どの様な作業を行ったか CHANGE-CAUSE という項目で記されていることがわかります。作業の履歴がリビジョン管理されています。

下記のように REVSION 番号を付与して履歴内容を表示することも可能です。

$ kubectl rollout history deployment/nginx-deployment --revision=2
deployments "nginx-deployment" with revision #2
  Labels:       app=nginx
        pod-template-hash=3599678771
  Annotations:  kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
  Containers:
   nginx:
    Image:      nginx:1.9.1
    Port:       80/TCP
    Volume Mounts:      <none>
    Environment Variables:      <none>
  No volumes.

作業を切り戻してみる

先程 nginx の Image Tag を更新しましたが、ここで Deployments の機能を使って作業を切り戻してみます。下記の様に実行します。

$ kubectl rollout undo deployment/nginx-deployment

状態を確認します。

$ kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2         2         2            2           5m

$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION        CHANGE-CAUSE
2               kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
3               kubectl create -f nginx.yaml --record

ここでは…

  • コンテナが2個、正常に起動した
  • REVISION 番号 3 として初期構築の状態 (kubectl create ..) が新たに保存

ということが分かります。注意したいのは REVSION 番号 1 が削除され 3 が生成されたことです。1 と 3 は同じ作業ということと推測します。

念のため ‘nginx’ コンテナの Image Tag が切り戻っているか確認してみます。

$ kubectl describe pod nginx-deployment-4087004473-nq35u | grep "Image:"
    Image:              nginx:1.7.9

最初の Yaml ファイルに記した ‘nginx’ イメージ Tag “1.7.9” となっていることが確認できました。set image … でイメージ更新をした作業が正常に切り戻ったことになります。

レプリカ数を 2->3 へスケールしてみる

更に replicas の数値を 2 から 3 へスケールしてみます。

$ kubectl scale deployment nginx-deployment --replicas 3

同様に状態を確認してみます。

kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-4087004473-esj5l   1/1       Running   0          6s
nginx-deployment-4087004473-nq35u   1/1       Running   0          4m
nginx-deployment-4087004473-tyibo   1/1       Running   0          4m

kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   0         0         0         9m
nginx-deployment-4087004473   3         3         3         11m

kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION        CHANGE-CAUSE
2               kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
3               kubectl scale deployment nginx-deployment --replicas 3

ここで気になるのは REVISION 3 が上書きされたことです。REVSION 番号 4 が新たに作成されると思っていたからです。先程 REVISION 番号 3 として保存されていた下記の履歴が消えてしまいました。この点については引き続き検証してみます。今の自分には理解できませんでした。ご存知の方いましたら、コメントお願いします!

3               kubectl create -f nginx.yaml --record

Puase, Resume 機能を使ってみる

次は deployments の機能を使って Image Tag を更に 1.9.1 へ変更し、その処理をポーズしてみます。

kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1; kubectl

同様に状態を確認してみます。

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   2         2         2         10m
nginx-deployment-4087004473   2         2         2         12m

$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
Ctrl-C #<--- キー入力

rollout status で deployment “deployment/nginx-deployment” を確認すると “waiting for rollout to finish” と表示され処理がポーズされていることが確認できました。また古い Deployment “nginx-deployment-4087004473” 上に未だコンテナが残り、新しい Deployment もコンテナが生成中であることが分かります。

では Resume します。

$ kubectl rollout resume deployment/nginx-deployment
deployment "nginx-deployment" resumed

この時点の状態を確認しましょう。

$ kubectl rollout status deployment/nginx-deployment
deployment nginx-deployment successfully rolled out

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   3         3         3         11m
nginx-deployment-4087004473   0         0         0         14m

ここからは…

  • 正常に Deployment “nginx-deployment” が Rollout されたこと
  • 古い Deployment 上のコンテナ数が 0 に、新しい Deployment 上のコンテナ数が 3 になった

ということが分かります。

Rolling-Update 相当の作業を行う方法

前述した通り、Replication Controller 時代にあった Rolling-Update 作業 (イメージタグ・レプリカ数等の更新) ですが、Deployments では下記の方法をとることが出来ます。

set オプションを付与する場合

set オプションを付与して Key 項目に対して新しい Value を渡します。

$ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

yaml ファイルを修正する場合

yaml ファイルの内容を更新して適用したい場合、下記のように apply オプションを付与します。

$ kubectl apply -f <新しいYamlファイル>

まとめと考察

REVISION の履歴が上書きされる点など、まだ未完成な感が否めませんでしたが(自分の勘違いかもしれません!)、Replication Controller と比べると作業の切り戻しや、履歴が保存され履歴内容も確認できる点など機能が追加されていることが分かりました。公式ドキュメントを読んでいてもコマンド結果等怪しい点があって流石に API バージョンが “v1beta1” だなぁという感じではありますが、機能が整理されていて利便性が上がっているので Replication Controller を使っているユーザは自然と今後、Deployments に移行していくのではないかと感じました。

fluentd-sidecar-gcp と Kubernetes Volumes で Cloud Logging ログ転送

fluentd-sidecar-gcp と Kubernetes Volumes で Cloud Logging へログ転送

2 minute read

こんにちは @jedipunkz です。

今回は Kubernetes を使った構成で Google-Fluentd をどのコンテナに載せるか?ってことを考えてみたので書きたいと思います。

Kubernetes は Docker を利用したソフトウェアなので Docker と同じく “1コンテナ, 1プロセス” というポリシがあります。つまり、コンテナ上のプロセスが停止したら Kubernetes がそれを検知してコンテナを起動しなおしてくれます。ですが、複数プロセスを1コンテナに稼働させると、それが出来ません。そうは言っても中には複数のプロセスを稼働させたい場面があります。その場面として考えられる具体的な例として HTTPD サーバのログを Google-Fluentd を使って GCP Cloud Logging に転送したい場合があります。

今回は上記の例を fluentd-sidecar-gcp と kubernetes volumes を使って解決する方法を記したいと思います。

構成のシナリオ

シナリオとしては下記のとおりです。

  • マルチコンテナポッドを扱う
  • 1つの Kubernetes Volumes を複数コンテナで共有する
  • HTTPD ログをその Volume に出力
  • 隣接する Google-Fluentd コンテナでその Volume に出力されたログを読み込みログ転送

fluentd-sidecar-gcp とは

次に説明するのは fluentd-sidecar-gcp の概略です。これは Kubernetes が contrib で扱っているコンテナです。下記の URL にあります。

https://github.com/kubernetes/contrib/tree/master/logging/fluentd-sidecar-gcp

Google-Fluentd を稼働させる Dockerfile が用意されているのですが、下記の記述を確認するとこのコンテナに環境変数 $FILES_TO_COLLECT を渡すと Google Fluentd でログを取得してくれることが分かります。

https://github.com/kubernetes/contrib/blob/master/logging/fluentd-sidecar-gcp/config_generator.sh#L22-L37

つまり、fluentd-sidecar-gcp コンテナに隣接する HTTPD コンテナのログが出力される Kubernetes Volumes 上のファイルパスを指定すれば HTTPD のログが取得でき、Google Cloud Logging へログが転送できます。

サンプルの Kubernetes YAML

下記にサンプルとして Kubernetes YAML を記します。

apiVersion: v1
kind: Pod
metadata:
  labels:
    run:  my-nginx
  name: nginx-fluentd-logging-example
spec:
  containers:
  - name: nginx-container
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: log-storage
      mountPath: /var/log/nginx
  - name: sidecar-log-collector
    image: gcr.io/google_containers/fluentd-sidecar-gcp:1.4
    resources:
      limits:
        cpu: 100m
        memory: 200Mi
    env:
    - name: FILES_TO_COLLECT
      value: "/mnt/log/nginx/access.log /mnt/log/nginx/error.log"
    volumeMounts:
    - name: log-storage
      readOnly: true
      mountPath: /mnt/log/nginx
  volumes:
  - name: log-storage
    emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx

特徴を下記に解説します。

  • nginx-container, sidecar-log-collector のマルチコンテナポッドです。
  • sidecar-log-collector の image: としては gcr.io/google_containers/fluentd-sidecar-gcp:1.4 が指定されています
  • ‘log-storage’ として nginx-container の /var/log/nginx が sidecar-log-collector の /mnt/log/nginx として共有されています
  • FILES_TO_COLLECT として共有 Volume 上の access.log, error.log が指定されています

結果、Nginx コンテナのログが Kubernetes Volume で Google-Fluentd コンテナに読み込み専用で共有され (readOnly 行) 、この Google-Fluentd は環境変数で渡された /mnt/log/nginx/access.log と /mnt/log/nginx/error.log を読み込み開始し、内容を Google Cloud Logging へ転送します。

デプロイ方法

デプロイは下記の通り実施します。

$ kubectl create -f <上記のファイル名>

結果とまとめ

それぞれのログファイルを Tag を Google-Fluentd で付けた形で Google Cloud Logging へ転送出来ました。ログ毎に結果を Cloud Logging UI 上で確認できます。 本来、Docker なので標準出力にログを出力し Kubernetes がその標準出力を Cloud Logging へ転送してくれるのですが、それだと Tag が付けられないため、ログを分離するのが一苦労だと思います。ですが、今回紹介した方法では Google-Fluentd で Tag を付けてログ転送出来たため、その心配はありません。

この Kubernetes Volumes は他にも利用方法がありそうです。

本来、GKE や Kubernetes を利用される方は Microservice Architecture が採用出来ている方々だと思うのですが、fluentd をアプリコンテナから分離するのは結構悩むところじゃないかと思うので、今回紹介した方法はそう言った場合に有用かと思います。