OpenShift Data Foundation (ODF)  - Multicloud Object Gateway (MCG)

### OpenShift Data Foundation (ODF) 

Red Hat OpenShift Data Foundation とは

Red Hat® OpenShift® Data Foundation (旧 Red Hat OpenShift Container Storage) は、コンテナ用ソフトウェア・デファインド・ストレージであり、Red Hat OpenShift に緊密に統合されたデータサービスを提供します。Red Hat OpenShift Data Foundation は、クラウド全体でアプリケーションを迅速かつ効率的に開発し、デプロイするのに役立ちます。

https://www.redhat.com/ja/technologies/cloud-computing/openshift-data-foundation

### Multicloud Object Gateway (MCG)

ここでは、以下のドキュメントに基づいて、AWS 上の OpenShift Container Platform (OCP) IPI/UPI 環境における ODF 4.16.2 の `Multicloud Object Gateway (MCG)` の実装例をご紹介します。

https://docs.redhat.com/en/documentation/red_hat_openshift_data_foundation/4.16/html-single/managing_hybrid_and_multicloud_resources/index#about-the-Multicloud-object-gateway

> Multicloud Object Gateway (MCG) は OpenShift の軽量オブジェクトストレージサービスであり、ユーザーは必要に応じて、複数のクラスター、およびクラウドネイティブストレージを使用して、オンプレミスで小規模に開始し、その後にスケーリングできます。 

> AWS S3 を対象とするアプリケーションまたは AWS S3 Software Development Kit(SDK) を使用するコードを使用して、オブジェクトサービスにアクセスできます。アプリケーションは、Multicloud Object Gateway (MCG) エンドポイント、アクセスキー、およびシークレットアクセスキーを指定する必要があります。ターミナルまたは MCG CLI を使用して、この情報を取得できます。 

#### Accessing the Multicloud Object Gateway from the terminal

ODF を導入した後、OpenShift Data Foundation cluster (`StorageSystem CR`) を作成した時点で MCG が使用可能になります。MCG の情報は `openshift-storage` Project に作成される `NooBaa CR` に登録されています。
```shell
$ oc project -q
openshift-storage

$ oc get noobaa -o wide
NAME     S3-ENDPOINTS                     STS-ENDPOINTS                    SYSLOG-ENDPOINTS   IMAGE                                                                                                            PHASE   AGE
noobaa   ["https://99.888.77.72:31372"]   ["https://99.888.77.72:31116"]                      registry.redhat.io/odf4/mcg-core-rhel9@sha256:b93834768ad50a259cabcdefg102930dkbo10d1a65642462e294a347166a87f7   Ready   5d23h
```
```shell
$ oc explain noobaa
GROUP:      noobaa.io
KIND:       NooBaa
VERSION:    v1alpha1

DESCRIPTION:
    NooBaa is the Schema for the NooBaas API

FIELDS:
  apiVersion    <string>
    APIVersion defines the versioned schema of this representation of an object.
    Servers should convert recognized schemas to the latest internal value, and
    may reject unrecognized values. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

  kind  <string>
    Kind is a string value representing the REST resource this object
    represents. Servers may infer this from the endpoint the client submits
    requests to. Cannot be updated. In CamelCase. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

  metadata      <ObjectMeta>
    Standard object's metadata. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

  spec  <Object>
    Specification of the desired behavior of the noobaa system.

  status        <Object>
    Most recently observed status of the noobaa system.
```

`oc describe` で表示される下記を参照します。
```shell
$ oc describe noobaa | awk '/Welcome to NooBaa!/,/^Events:/'
  Welcome to NooBaa!
  -----------------
  NooBaa Core Version:     5.16.2-1c40d7b
  NooBaa Operator Version: 5.16.2

  Lets get started:

  Test S3 client:

    kubectl port-forward -n openshift-storage service/s3 10443:443 &
    NOOBAA_ACCESS_KEY=$(kubectl get secret noobaa-admin -n openshift-storage -o json | jq -r '.data.AWS_ACCESS_KEY_ID|@base64d')
    NOOBAA_SECRET_KEY=$(kubectl get secret noobaa-admin -n openshift-storage -o json | jq -r '.data.AWS_SECRET_ACCESS_KEY|@base64d')
    alias s3='AWS_ACCESS_KEY_ID=$NOOBAA_ACCESS_KEY AWS_SECRET_ACCESS_KEY=$NOOBAA_SECRET_KEY aws --endpoint https://localhost:10443 --no-verify-ssl s3'
    s3 ls


  Services:
    Service Mgmt:
      External DNS:
        https://noobaa-mgmt-openshift-storage.apps.ocp40.mycluster.com:443
      Internal DNS:
        https://noobaa-mgmt.openshift-storage.svc:443
      Internal IP:
        https://444.55.224.215:443
      Node Ports:
        https://99.888.77.72:0
      Pod Ports:
        https://11.222.8.40:8443
    serviceS3:
      External DNS:
        https://s3-openshift-storage.apps.ocp40.mycluster.com:443
        https://a9web1nd90wqlndl7319634ijdlknban-1538916590.us-eastwest-99.elb.amazonaws.com:443
      Internal DNS:
        https://s3.openshift-storage.svc:443
      Internal IP:
        https://444.55.96.92:443
      Node Ports:
        https://99.888.77.72:31372
      Pod Ports:
        https://11.222.8.41:6443
    Service Sts:
      External DNS:
        https://sts-openshift-storage.apps.ocp40.mycluster.com:443
        https://98137ejknbnlds910duoihkeqdknbla3-1697748801.us-eastwest-99.elb.amazonaws.com:443
      Internal DNS:
        https://sts.openshift-storage.svc:443
      Internal IP:
        https://444.55.137.103:443
      Node Ports:
        https://99.888.77.72:31116
      Pod Ports:
        https://11.222.8.41:7443
    Service Syslog:
Events:  <none>
```
S3 の情報は下記に記載されています。
```shell
    serviceS3:
      External DNS:
        https://s3-openshift-storage.apps.ocp40.mycluster.com:443
        https://a9web1nd90wqlndl7319634ijdlknban-1538916590.us-eastwest-99.elb.amazonaws.com:443
      Internal DNS:
        https://s3.openshift-storage.svc:443
```
`External DNS` の1行目は OCP Default Router 経由の URL になります。
```shell
$ oc get route s3
NAME   HOST/PORT                                       PATH   SERVICES   PORT       TERMINATION       WILDCARD
s3     s3-openshift-storage.apps.ocp40.mycluster.com          s3         s3-https   reencrypt/Allow   None
```
`External DNS` の2行目は Service 経由の URL になります。
```shell
$ oc get service/s3 -o wide
NAME   TYPE           CLUSTER-IP     EXTERNAL-IP                                                                    PORT(S)                                                    AGE   SELECTOR
s3     LoadBalancer   444.55.96.92   a9web1nd90wqlndl7319634ijdlknban-1538916590.us-eastwest-99.elb.amazonaws.com   80:32566/TCP,443:31372/TCP,8444:32059/TCP,7004:31791/TCP   6d    noobaa-s3=noobaa
```
`LoadBalancer` type であるため、LB が AWS 側に作成されます。

https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer

```shell
$ aws elb describe-load-balancers | jq -r '.LoadBalancerDescriptions[] | select(.DNSName == "a9web1nd90wqlndl7319634ijdlknban-1538916590.us-eastwest-99.elb.amazonaws.com")'
{
  "LoadBalancerName": "a9web1nd90wqlndl7319634ijdlknban",
  "DNSName": "a9web1nd90wqlndl7319634ijdlknban-1538916590.us-eastwest-99.elb.amazonaws.com",
  "CanonicalHostedZoneName": "a9web1nd90wqlndl7319634ijdlknban-1538916590.us-eastwest-99.elb.amazonaws.com",
  "CanonicalHostedZoneNameID": "AZYB2DB",
  "ListenerDescriptions": [
    {
      "Listener": {
        "Protocol": "TCP",
        "LoadBalancerPort": 7004,
        "InstanceProtocol": "TCP",
        "InstancePort": 31791
      },
      "PolicyNames": []
    },
    {
      "Listener": {
        "Protocol": "TCP",
        "LoadBalancerPort": 8444,
        "InstanceProtocol": "TCP",
        "InstancePort": 32059
      },
      "PolicyNames": []
    },
    {
      "Listener": {
        "Protocol": "TCP",
        "LoadBalancerPort": 443,
        "InstanceProtocol": "TCP",
        "InstancePort": 31372
      },
      "PolicyNames": []
    },
    {
      "Listener": {
        "Protocol": "TCP",
        "LoadBalancerPort": 80,
        "InstanceProtocol": "TCP",
        "InstancePort": 32566
      },
      "PolicyNames": []
    }
  ],
～省略～
```
Service Network 経由でアクセスする場合は `Internal DNS` が使用可能です。

`External DNS` `Internal DNS` に関しては、マニュアルに記載のある通り、通信コストの差異が発生する場合があります。

>  The output from the oc describe noobaa command lists the internal and external DNS names that are available. When using the internal DNS, the traffic is free. The external DNS uses Load Balancing to process the traffic, and therefore has a cost per hour. 


#### Test S3 client 
`Test S3 client` の手順を確認するために `kubectl` コマンドの代替に `oc` コマンドを使用してみます。この手順を実施するためには、あらかじめ `aws CLI` を使用可能にしておく必要があります。

```shell
$ oc port-forward -n openshift-storage service/s3 10443:443 &
[1] 73859
$ Forwarding from 127.0.0.1:10443 -> 6443

$ NOOBAA_ACCESS_KEY=$(oc get secret noobaa-admin -n openshift-storage -o json | jq -r '.data.AWS_ACCESS_KEY_ID|@base64d')
$ echo $NOOBAA_ACCESS_KEY
***********************
$ NOOBAA_SECRET_KEY=$(oc get secret noobaa-admin -n openshift-storage -o json | jq -r '.data.AWS_SECRET_ACCESS_KEY|@base64d')
$ echo $NOOBAA_SECRET_KEY
***********************

$ alias s3='AWS_ACCESS_KEY_ID=$NOOBAA_ACCESS_KEY AWS_SECRET_ACCESS_KEY=$NOOBAA_SECRET_KEY aws --endpoint https://localhost:10443 --no-verify-ssl s3'
$ alias s3
alias s3='AWS_ACCESS_KEY_ID=$NOOBAA_ACCESS_KEY AWS_SECRET_ACCESS_KEY=$NOOBAA_SECRET_KEY aws --endpoint https://localhost:10443 --no-verify-ssl s3'

$ s3 ls
Handling connection for 10443
urllib3/connectionpool.py:1064: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings
2024-10-31 16:34:08 first.bucket
```
`oc port-forward` に Service を指定することで、`Internal DNS` 経由のアクセスを行います。`oc port-forward` を使用せずに、`alias s3` の `--endpoint` に `External DNS` を指定してもアクセス可能です。

`alias s3` で設定したコマンドは、Service 経由で以下の Pod 内 `endpoint` Container の Port `6443` に HTTPS Request を送信することになります。
```shell
$ oc get pod noobaa-endpoint-abcdodibnw-98173 -o wide
NAME                               READY   STATUS    RESTARTS   AGE   IP            NODE                                              NOMINATED NODE   READINESS GATES
noobaa-endpoint-abcdodibnw-98173   1/1     Running   0          6d    11.222.8.41   ip-99-888-77-72.us-eastwest-99.compute.internal   <none>           <none>

$ oc exec -ti noobaa-endpoint-abcdodibnw-98173 -- lsof -iTCP -sTCP:LISTEN -P -n
COMMAND PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
noobaa   13 root   19u  IPv6 67834706      0t0  TCP *:8444 (LISTEN)
noobaa   13 root   20u  IPv6 67834707      0t0  TCP *:6001 (LISTEN)
noobaa   13 root   22u  IPv6 67834708      0t0  TCP *:6443 (LISTEN)
noobaa   13 root   23u  IPv6 67834709      0t0  TCP *:7443 (LISTEN)
noobaa   13 root   24u  IPv6 67834710      0t0  TCP *:7004 (LISTEN)
```
テスト用のファイルを S3 に Upload してみます。
```shell
$ touch TEST_FILE.txt

$ s3 cp TEST_FILE.txt s3://first.bucket 2>/dev/null
Handling connection for 10443
upload: ./TEST_FILE.txt to s3://first.bucket/TEST_FILE.txt

$ s3 ls first.bucket 2>/dev/null
Handling connection for 10443
2024-11-06 17:52:45          0 TEST_FILE.txt
```

以上のように、簡単に S3 を使用する事が出来ます。