Kubernetes でサイドカーパターンを試す

コンテナを用いたシステムのアーキテクチャパターンはいくつか提唱されています。今回はその中で、分散システムのデザインパターンであるサイドカーパターンを取り上げます。サイドカーパターンは、マイクロサービスの課題を解決するために生まれたサービスメッシュの概念につながるパターンです。

今回は、ローカルな K8s 環境で、サイドカーパターンを用いたアプリケーションを試しに動かしてみるところまでやってみます。

サイドカーパターンとは

  • 下図のように Kubernetes のポッド内で、主となるコンテナと共にそれを補助する役割を果たすコンテナを持つ構成をサイドカーパターンと呼びます。
    • ポッドが起動すると、内部の複数のコンテナが同時にスタートします。
    • 2 つのポッド間では、ネットワークやディスクなどの情報を共有しています。

f:id:linkode-okazaki:20200610100603p:plain

  • このパターンの利点を列挙すると、以下のようになります:
    • 都度、目的にあったコンテナのイメージをビルドするのではなく、過去に開発し、実績があるコンテナを容易に再利用が可能である
    • ポッド内のコンテナごとに CPU の使用時間の上限を設定することが可能
      • 例えば、メインとなるコンテナに CPU を優先的に割り当てて低レインテシで応答させ、補助コンテナはメインのコンテナがヒマなときに動作させるなど。
    • コンテナが障害の封じ込めの境界を提供することになるので、全体が停止するのを防止できる
      • サービスメッシュの考え方にもつながる利点
        • サービスメッシュについては、次回の記事で解説します
    • コンテナがデプロイの単位になるため、各機能をアップグレードし、必要に応じてロールバックすることが出来る

具体的な稼働例

  • 実際に、K8s を利用して、サイドカーパターンでアプリケーションをデプロイしてみます。
  • 今回は、Amazon EKS の上で試すのも大げさなので、手元で動く K8s 環境にて試してみます。
    • 手元で稼働する K8s 環境としては Minukube を利用するか Docker Desktop で K8s を利用する方法があります。いずれの方法でも下記の手順が成功することは確認しています。
    • それぞれのツールのインストール方法は、ここでは触れません。Minikube のインストールについては参考資料をご確認ください。
    • 以下、Minikube がインストールされている前提で進めていきます。
  • 試してみるのは下図のようなものです:
    • 汎用的なコンテナを 2 つ組み合わせた構成
      1. Web サーバ:nginx の公式イメージをそのまま利用します。
      2. 最新のコンテンツを GitLab のリポジトリから取り込む動作をするコンテナ
    • 2 つのコンテナ間で HTML コンテンツを格納するボリューム(emptyDir)を共有しています。
    • この構成により、開発者は GitLab のリポジトリに push するだけで、HTML コンテンツを最新化できます。
    • 2.について、自力でシェルスクリプトを実装したパターンとすでにあるコンテナイメージを利用したパターンの 2 つを試してみます。

f:id:linkode-okazaki:20200610100559p:plain

自力で Git からの取り込みコンテナを実装する

必要なファイルを用意する

  • 用意するのは以下の 3 つ:
contents-puller
#!/bin/bash
# 最新WebコンテンツをGitHubからコンテナへ取込む

# コンテンツ元の環境変数が無ければエラーで終了
if [ -z $CONTENTS_SOURCE_URL ]; then
   exit 1
fi

# 初回は GitLab からコンテンツをクローンする
git clone $CONTENTS_SOURCE_URL /data

# 2 回目以降は、1 分ごとに変更差分を取得する
cd /data
while true
do
   date
   sleep 60
   git pull
done
Dockerfile
  • Git からの取り込みコンテナの定義を記述します。
## Contents Cloner Image
FROM alpine:latest
RUN apt-get update && apt-get install -y git
COPY ./contents-puller /contents-puller
RUN chmod a+x /contents-puller
WORKDIR /
CMD ["/contents-puller"]
webserver.yml
  • サイドカーポッドのマニフェストファイルです。
    • デプロイメントとサービスを定義します。
      • デプロイメントでは 2 つのコンテナと共有ボリュームを定義します。
      • サービスでは NodePort を定義し、外部へ公開出来るようにします。
---
apiVersion: apps/v1
kind: Deployment
metadata:
    name: webserver
spec:
    replicas: 1
    selector:
    matchLabels:
        app: webserver
    template:
    metadata:
        labels:
        app: webserver
    spec:
        ## コンテナの定義
        containers:
        ## 1つ目のコンテナ:Nginx
        - name: nginx
          image: nginx
          volumeMounts:
          - mountPath: /usr/share/nginx/html
            name: contents-vol
            readOnly: true
        ## 2つ目のコンテナ:GitLabから pull してくるコンテナ
        - name: cloner
          image: maho/c-cloner:0.1
          env:
          - name: CONTENTS_SOURCE_URL
            # ここのリポジトリは適当な公開リポジトリを設定する
            value: https://gitlab.com/hogehoge/webcontent-test.git
          volumeMounts:
          - mountPath: /data
            name: contents-vol
        ## 共有ボリューム
        volumes:
        - name: contents-vol
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: webserver
  labels:
    app: webserver
spec:
  selector:
    app: webserver
  type: NodePort
  ports:
    - port: 80
      targetPort: 80

マニフェストを適用して確認する

  • 上記 3 ファイルを同じディレクトリ内に置き、マニフェストを適用します。
  • 適用したら、コンテナが稼働するまでしばらく待ちます。
  • 稼働したら minikube service webserver --url コマンドでアクセス先の URL を取得します。
$ kubectl apply -f webserver.yml
$ kubectl get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
webserver-5bfdd5d57d-qjv4g   2/2     Running   0          33s   172.17.0.5   minikube   <none>           <none>
$ kubectl get svc -o wide
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE   SELECTOR
kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        53d   <none>
webserver    NodePort    10.97.246.59   <none>        80:30523/TCP   48s   app=webserver
$ minikube service webserver --url
http://192.168.64.3:30523
  • 上記の場合、http://192.168.64.3:30523 にアクセスして、リポジトリにアップしたページが表示されることを確認できます。

f:id:linkode-okazaki:20200610100607p:plain

  • また、HTML コンテンツの内容を変更して、GitLab に push してみます。 1 分程度経過した後、再度上記 URL にアクセスしてみると、きちんと変更が反映されています。
    • コンテナの動作により、コンテンツが最新化されていることが分かります。

既存のコンテナ git-sync を利用する

  • 先程までの手順で、自力で実装した補助コンテナ(GitLab からコンテンツを pull してくる役割のコンテナ)ですが、同様の処理を行うコンテナが提供されています(git-sync)。
  • この git-sync を利用する場合、下記のマニフェストファイル 1 つ(git-sync.yml)を作成するだけで、同様の処理を実現することが出来ます。
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: git-sync
spec:
  replicas: 1
  selector:
    matchLabels:
      app: git-sync
  template:
    metadata:
      labels:
    app: git-sync
    spec:
    # コンテナの定義
      containers:
   # 1つ目のコンテナ:nginx
    - name: nginx
      image: nginx:alpin
      ports:
        - containerPort: 80
      volumeMounts:
        - name: git-sync-volume
          mountPath: /usr/share/nginx
   # 2つ目のコンテナ:git-sync
    - name: git-sync
      image: k8s.gcr.io/git-sync:v3.1.3
      volumeMounts:
        - name: git-sync-volume
          mountPath: /sync
      env:
        - name: GIT_SYNC_REPO
          # ここの URL は適当なリポジトリのものに変更する
          value: https://gitlab.com/hogehoge/webcontent-test.git
        - name: GIT_SYNC_BRANCH
          # ブランチ名も適当なものに変更する
          value: master
        - name: GIT_SYNC_ROOT
          value: /sync
        - name: GIT_SYNC_DEST
          value: html
      volumes:
    - name: git-sync-volume
      emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: git-sync
  labels:
    app: git-sync
spec:
  selector:
    app: git-sync
  type: NodePort
  ports:
    - port: 80
      targetPort: 80
$ kubectl apply -f git-sync.yml
deployment.apps/git-sync created
service/git-sync created
tsuchinoko@Tsuchinoko-MBP-9:~/d/b/side-car
$ kubectl get pods -o wide
NAME                        READY   STATUS              RESTARTS   AGE   IP       NODE       NOMINATED NODE   READINESS GATES
git-sync-78c65f6d77-qxls9   0/2     ContainerCreating   0          11s   <none>   minikube   <none>           <none>
tsuchinoko@Tsuchinoko-MBP-9:~/d/b/side-car
$ kubectl get pods -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
git-sync-78c65f6d77-qxls9   2/2     Running   0          57s   172.17.0.5   minikube   <none>           <none>
$ kubectl get svc -o wide
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE   SELECTOR
git-sync     NodePort    10.103.190.137   <none>        80:30383/TCP   84s   app=git-sync
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP        54d   <none>
$ minikube service git-sync --url
http://192.168.64.3:30383
  • 先程と同様に http://192.168.64.3:30383 にアクセスすると Git に push した HTML コンテンツが表示されていることが分かります。
    • また、更新を Git に push したら、約 1 分後にコンテンツが最新になっていることも確認できます。

まとめ

  • 分散システムのデザインパターンの 1 つである、サイドカーパターンとは何かを確認しました。
    • この手のデザインパターンには、適したコンテナが DockerHub で公開されていたりします。
    • それらを上手に活用することで、開発にかかるコストを下げることが出来ます。
  • サイドカーパターンで HTML コンテンツを表示する方法を実践してみました。
  • このパターンを念頭に置きながら、次回はサービスメッシュを理解するための記事を書きます。

参考資料