コンテナでサイドカーを試す

openshiftや、kubernetes でPODを動かす際に、サイドカーというテクニックを使って、単一のPOD内で複数のコンテナを動かすテクニックがあるようです。

サイドカーというテクニック自体は、一般的な印象ですが、代表的な使い方として思い当たるのは、istio上でアプリケーションを動かす際に、アプリケーションコードのコンテナと、istio コントロールプレインで制御されるEnvoy をサイドカーで配置することで、アプリケーションのコードやコンテナに、istio サービスメッシュに載せるための拡張を分離してビルドできるものかなと思います。
言い方を変えると、istioのための拡張がアプリケーションコードを直接行わなくてよいということで、アプリケーションコード単体を別の環境で稼働させるような場合等も、同じコンテナイメージを使いまわせたり、機能を拡張する部分を、標準化できたりというメリットがあるのかなと思います。もし同様の効果が期待できるシーンがあれば積極的に活用できるのではないかと思います。

この記事では、実際に、サイドカーコンテナを動かしてみようと思います。なお、実験は、サイトlearnk8sの記事["Extending applications on Kubernetes with multi-container pods"](https://learnk8s.io/sidecar-containers-patterns)にしたがい、Openshift Local ( 4.10.18 )で行いました。

参考：[OpenShift Local の準備に関する記事](https://qiita.com/ArakiToshihiro/items/7f58169c609555ad56bc) (OpenShift local は、デスクトップ環境で動作可能な開発、動作検証用のOpenShift 環境です。)

## シングルコンテナのPod起動
まず最初に、シンプルな1コンテナ、1podの動作を確認します。ここでは、elasticsearchコンテナを動かします。

### 操作１ : Project の作成　
openshift local へユーザ”developer”でコマンドでログインします。Password は、デフォルトから変更していなければ、"developer"です。

```　
$ eval $(crc oc-env)
$ oc login -u developer https://api.crc.testing:6443
Authentication required for https://api.crc.testing:6443 (openshift)
Username: developer
Password: *************
Login successful.
...
$ 
```
oc new-project コマンドで実験用のproject を作成します。ここでは、project name: single-container-test としています。

```
$ oc new-project single-container-test
Now using project "single-container-test" on server "https://api.crc.testing:6443".

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app rails-postgresql-example

to build a new example application in Ruby. Or use kubectl to deploy a simple Kubernetes application:

    kubectl create deployment hello-node --image=k8s.gcr.io/e2e-test-images/agnhost:2.33 -- /agnhost serve-hostname

$ 
```

### 操作2 : マニフェストの用意
pod のデプロイは、deployment リソースを使用します。

テキストエディタで下記のようなyamlファイルを用意します。
``` elasticsearch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: elasticsearch
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: elasticsearch
  template:
    metadata:
      labels:
        app.kubernetes.io/name: elasticsearch
    spec:
      containers:
        - name: elasticsearch
          image: elasticsearch:7.9.3
          env:
            - name: discovery.type
              value: single-node
          ports:
            - name: http
              containerPort: 9200
---
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
spec:
  selector:
    app.kubernetes.io/name: elasticsearch
  ports:
    - port: 9200
      targetPort: 9200
```

### 操作3:デプロイの実行
oc apply コマンドでデプロイします。
```
$ oc apply -f elasticsearch.yaml
deployment.apps/elasticsearch created
service/elasticsearch created
$ 
```

oc get pod コマンドを実行して、STAUS=Runnning となっていることを確認します。Runnning になるまで適宜何度かoc get pod コマンドを繰り返します。

```
$ oc get pod -w
NAME                             READY   STATUS    RESTARTS   AGE
elasticsearch-5f9df6bcbf-cfghf   1/1     Running   0          11s
$ 
```

なお、ここでは、1pod 1container でデプロイしているため、READY欄の出力は、1/1 (= 1個中1個のコンテナが稼働中)となります。

### 操作4:elasticsearchサービスへ接続確認

oc run コマンドを使用して、同じProject内に、ワンタイムのコンテナを起動し、curl コマンド経由でelastichsearch サービスへアクセスします。正常に動作していれば、elastichsearch コンテナから応答が帰ってきます。

```
$ oc run -it --rm --image=curlimages/curl curl -- curl http://elasticsearch:9200
If you don't see a command prompt, try pressing enter.
{
  "name" : "elasticsearch-5f9df6bcbf-cfghf",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "hg6FO5UWSOOlBz_5gAMaZw",
  "version" : {
    "number" : "7.9.3",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "c4138e51121ef06a6404866cddc601906fe5c868",
    "build_date" : "2020-10-16T10:36:16.141335Z",
    "build_snapshot" : false,
    "lucene_version" : "8.6.2",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
Session ended, resume using 'oc attach curl -c curl -i -t' command when the pod is running
pod "curl" deleted
$ 
```
ここまでで、シンプルな1pod 1コンテナの動作が確認できました。
この実験では、elastichsearch コンテナが9200番ポートで、http でリッスンしている状態となります。

以降のサイドカーのケースでは、このelastichsearch コンテナの前段に、nginx コンテナを配置し、https でリッスンする用に構成する実験をします。

## マルチコンテナ(サイドカーコンテナ）のPod起動
前述のシングルコンテナーのケースと同様の流れで、マルチコンテナpod を稼働させます。ここでは、elasticsearchコンテナの前段に、nginx コンテナをproxy の要領で配置し、httpでリッスンしていた状態から、https へ変更される様子を見ることができます。

### 操作１ : Project の作成
openshift local へのログイン操作は割愛します。

oc new-project コマンドで実験用のproject を作成します。ここでは、project name: multi-container-test としています。

```
$ oc new-project multi-container-test
Now using project "multi-container-test" on server "https://api.crc.testing:6443".

You can add applications to this project with the 'new-app' command. For example, try:

    oc new-app rails-postgresql-example

to build a new example application in Ruby. Or use kubectl to deploy a simple Kubernetes application:

    kubectl create deployment hello-node --image=k8s.gcr.io/e2e-test-images/agnhost:2.33 -- /agnhost serve-hostname

[toaraki@fedora proxy-test]$
```

### 操作2 : マニフェストの用意

テキストエディタで下記のようなyamlファイルを用意します。

```es-secure-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: elasticsearch
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: elasticsearch
  template:
    metadata:
      labels:
        app.kubernetes.io/name: elasticsearch
    spec:
      containers:
        - name: elasticsearch
          image: elasticsearch:7.9.3
          env:
            - name: discovery.type
              value: single-node
            - name: network.host
              value: 127.0.0.1
            - name: http.port
              value: '9201'
        - name: nginx-proxy
          #image: nginx:1.19.5
          image: nginx:latest
          volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx/
              readOnly: true
            - name: certs
              mountPath: /certs
              readOnly: true
          ports:
            - name: https
              containerPort: 9200
      volumes:
        - name: nginx-config
          configMap:
            name: elasticsearch-nginx
        - name: certs
          secret:
            secretName: elasticsearch-tls
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: elasticsearch-nginx
data:
  #elasticsearch.conf: |
  nginx.conf: |
    user nginx;
    worker_processes auto;
    pid	/tmp/nginx.pid;

    events {
      worker_connections 1024;
    }

    http {
      proxy_temp_path /tmp/proxy_temp;
      client_body_temp_path /tmp/client_temp;
      fastcgi_temp_path /tmp/fastcgi_temp;
      uwsgi_temp_path /tmp/uwsgi_temp;
      scgi_temp_path /tmp/scgi_temp;
      server {
          listen 9200 ssl;
          server_name elasticsearch;
          ssl_certificate /certs/tls.crt;
          ssl_certificate_key /certs/tls.key;

          location / {
              proxy_pass http://localhost:9201;
          }

      }
    }
---
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
spec:
  selector:
    app.kubernetes.io/name: elasticsearch
  ports:
    - port: 9200
      targetPort: 9200
```

ポイント
* containers のセクションに、elasticsearch と、nginx のエントリーしている（先ほどは１つだけ）
* elastichsearch を、9201 、nginx を9200 ポートにそれぞれバインド
* nginx のコンフィグは、ConfigMap で/etc/nginx/nginx.conf を渡している
    * 参考サイトの方法だと、container上のディレクトリへのアクセス権限で不都合があったので、暫定的に動く方法に変更しました
    * 準備中：nginxがopenshift で起動しない
* secret オブジェクトを作成して、自己サーバ証明書を渡している


### 操作3:デプロイの実行
サイドカーで配置するnginxは、https-proxyのように動作させるので、サーバ証明書を用意してhttps 通信が可能なようにセットアップします。
まずは、secret/elastichsarch-tls を作成します。あらかじめ、サーバ証明書を用意しておきます。ファイルとしては、key , csr , crt ファイルが用意されている状態です。

```
$ ls
tls.crt  tls.csr  tls.key
$　oc create secret tls elasticsearch-tls --key=./tls.key --cert=./tls.crt
secret/elasticsearch-tls created
$ 
```

続けて、oc apply コマンドで、デプロイします。
```
$ oc apply -f es-secure-deployment.yaml 
deployment.apps/elasticsearch created
configmap/elasticsearch-nginx created
$
```
oc get pod コマンドを実行して、STAUS=Runnning となっていることを確認します。Runnning になるまで適宜何度かoc get pod コマンドを繰り返します。

```
$ oc get pod
NAME                             READY   STATUS    RESTARTS   AGE
elasticsearch-84cb95cc8c-c9v46   2/2     Running   0          108s
$ 
```

ここでは、1pod 2container でデプロイしているため、READY欄の出力は、2/2 (= 2個中2個のコンテナが稼働中)となります。

### 操作4:elasticsearchサービスへ接続確認

oc run コマンドを使用して、同じProject内に、ワンタイムのコンテナを起動し、curl コマンド経由でelastichsearch サービスへアクセスします。正常に動作していれば、elastichsearch コンテナから応答が帰ってきます。ここでは、elastichsearch の前段に配置したnginx がhttps でリッスンしていますので、curl コマンドは、httpsでのアクセスを指定します。また、サーバ証明書は自己証明書となるので、-kオプションもしています。

```
$ oc run -it --rm --image=curlimages/curl curl   -- curl -k https://elasticsearch:9200
If you don't see a command prompt, try pressing enter.
{
  "name" : "elasticsearch-84cb95cc8c-zmc2t",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "3lfwkLlnShiD5AJeZWZefA",
  "version" : {
    "number" : "7.9.3",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "c4138e51121ef06a6404866cddc601906fe5c868",
    "build_date" : "2020-10-16T10:36:16.141335Z",
    "build_snapshot" : false,
    "lucene_version" : "8.6.2",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
Session ended, resume using 'oc attach curl -c curl -i -t' command when the pod is running
pod "curl" deleted
$  
```

nginx に実際にアクセスがあったかどうかは、oc logs コマンドを使用して、nginxコンテナのログから確認できます。

```
$ oc get pod
NAME                             READY   STATUS    RESTARTS   AGE
elasticsearch-84cb95cc8c-zmc2t   2/2     Running   0          11m
$ oc logs elasticsearch-84cb95cc8c-zmc2t nginx-proxy | grep curl
10.217.0.69 - - [30/Jun/2022:15:15:45 +0000] "GET / HTTP/1.1" 200 559 "-" "curl/7.83.1-DEV"
$ 
```

また、ワンタイムコンテナからのアクセス確認で、curl コマンドからのリクエストをhttpとすると、エラーが返されます。(HTTP 400)

```
$ oc run -it --rm --image=curlimages/curl curl   -- curl http://elasticsearch:9200
If you don't see a command prompt, try pressing enter.
<html>
<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>The plain HTTP request was sent to HTTPS port</center>
<hr><center>nginx/1.23.0</center>
</body>
</html>
Session ended, resume using 'oc attach curl -c curl -i -t' command when the pod is running
pod "curl" deleted
$ 
```

ここまでの確認で、elasticsarch コンテナの前段に、nginxコンテナを配置し、https-proxyのように動作させることが出来ました。

以上