(第3回)【Oracle Cloud】OKE + FSSのマネージドな環境でASP.NET Core Webアプリを稼働する

(第3回)【Oracle Cloud】OKE + FSSのマネージドな環境でASP.NET Core Webアプリを稼働する

目次

前回の振り返り
FSSを作成する
OKEを設定する
kubectlを設定する
Deploymentマニュフェストを作成する
Serviceマニュフェストを作成する
FSS用のマニュフェストを作成する
OKEクラスターの稼働状況を確認する
さいごに

前回の振り返り

今回が3回目ということで、Oracle CloudのOKEとFSSでファイルをアップロードするWebアプリケーションの構築をしてきましたが、ついに今回で完成します。

前回まではASP.NET Core MVCでWebアプリケーションを作成しdocker buildで作成したイメージをOCIRにPushしました。また、作業用のコンピュートインスタンスにkubectlをインストールしOKEを操作できるように準備、OKE用にアイデンティティ(ポリシー/ユーザー)の設定をしました。
いよいよ大詰めですね。

今回の主な内容はOKEのマニュフェストファイルを作成していきます。
OKEにFSSをマウントできるようにしないといけないのですが、Oralceの公式ブログの記事を参考にして設定をしていきます。

  • Webアプリケーションを開発する(完了)
  • アイデンティティユーザーを作成しポリシーを設定する (完了)
  • OCIレジストリにDockerイメージを登録する (完了)
  • File Storage Serviceを設定する ←今回
  • OKEを設定する ←今回
  • 稼働!完成! ←今回!!

FSSを作成する

各コンテナがデータを格納する場所を用意します。OCIコンソールよりファイル・ストレージ・サービスでファイルシステムとマウントターゲットを作成します。

ファイルシステム・マウントターゲットの作成

作成後はマウントターゲットのIPアドレスを確認しておきます。
マウントターゲットにアクセスできるように適宜、セキュリティリスト、もしくはネットワークセキュリティグループで通信を許可をしておきます。

ファイルストレージのマウントターゲット

OKEを設定する

OKEクラスターを作成していきます。まずはOCIコンソールでクラスターを作成するところからです。
OCIコンソールからサクサク作っていきますが、注意点が3つあります

  • インスタンスのリソース制限状況を事前に確認しておく(制限された数以上のnodeは作れないので注意が必要です)
  • プライベートサブネットにはOCIのサービスゲートウェイを設定しておくこと
  • プライベートサブネットにはNATゲートウェイを設定しておくこと

この3つで実際にすごくハマりました。何度やってもノードプールのインスタンスが途中でコケてしまい、どうしようかと途方に暮れそうでした。OKEクラスターにsshをしてシスログを確認すると、オブジェクトストレージにアクセスが出来ないといったエラーが出ていることに気づきました。
また、NATゲートウェイに関しては、OCIR以外のリポジトリからイメージを取得する際に必要です。

上記点を事前に設定した上でクラスターの作成をします。「カスタム作成」で進めていきます。

OKEの作成1(カスタム作成を選択)

名前やコンパートメントは適宜設定をしましょう。

OKEの作成2(カスタム作成のクラスタ設定)

ノードプールのノードの数は実際にComputeで作成されるインスタンス数になります。
検証なので、1つだけにしておきます。

OKEの作成3(ノードプールの設定)

OKEクラスターが作成できました。
あとはOKEのノードがしっかり「Ready」になっていれば、クラスターの作成自体は成功です。

OKEノード

Kubectlを設定する

作成したOKEクラスターを操作するために、「kubectl」を使用します。OCIコンソール上に「Access Cluster」というボタンから、コマンドを確認できますので、作業インスタンスに設定します。
ただし、OCI CLIを事前にインストールしておく必要があります。

Access Cluster

設定が完了したら kubectlが利用可能な状態になります。
次にDocker Registry Secret を登録します。これでOKEがOCIRを使用することが出来るようになります。
<docker-registry-secret>には、任意の名前を入力します。

kubectl create secret docker-registry <docker-registry-secret> \
  --docker-server=nrt.ocir.io \
  --docker-username='<tenancy-namespace>/<username>' \
  --docker-password='<auth-token>' \
  --docker-email=<Email>'

Deploymentマニュフェストを作成する

今回は1つのPodにWebアプリコンテナとNginxコンテナが稼働するように設定します。
Nginxは「ConfigMap」で設定を記述しています。

また「/UploadFiles」ディレクトリに対してFSSをマウントするようにマニュフェストに定義します。「persistentVolumeClaim」は後述のFSS用のマニュフェストで定義します。

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fileupload-app
spec:
  selector:
    matchLabels:
      app: fileupload-app
  replicas: 1
  template:
    metadata:
      labels:
        app: fileupload-app
    spec:
      containers:
      - name: fileupload-app
        image: nrt.ocir.io/<tenancy-namespace>/file_upload_web_app:v03
        imagePullPolicy: Always
        ports:
        - containerPort: 5000
        volumeMounts:
        - mountPath: /UploadFiles
          name: nfs
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 8080
        volumeMounts:
        - mountPath: /etc/nginx
          readOnly: true
          name: nginx-conf
        - mountPath: /var/log/nginx
          name: log
      volumes:
      - name: nfs
        persistentVolumeClaim:
          claimName: oke-fsspvc
          readOnly: false
      - name: nginx-conf
        configMap:
          name: nginx-conf
          items:
            - key: nginx.conf
              path: nginx.conf
            - key: virtualhost.conf
              path: virtualhost/virtualhost.conf
      - name: log
        emptyDir: {}
      imagePullSecrets:
      - name: <docker-registry-secret>
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-conf
data:
  nginx.conf: |
    user nginx;
    worker_processes  3;
    error_log  /dev/stderr warn;
    events {
      worker_connections  10240;
    }
    http {
      log_format  main
              'remote_addr:$remote_addr\t'
              'time_local:$time_local\t'
              'method:$request_method\t'
              'uri:$request_uri\t'
              'host:$host\t'
              'status:$status\t'
              'bytes_sent:$body_bytes_sent\t'
              'referer:$http_referer\t'
              'useragent:$http_user_agent\t'
              'forwardedfor:$http_x_forwarded_for\t'
              'request_time:$request_time';

      access_log    /dev/stdout  main;
      include /etc/nginx/virtualhost/virtualhost.conf;
    }
  virtualhost.conf: |
    upstream app {
      server localhost;
      keepalive 1024;
    }

    server {
      listen 8080 default_server;
      server_name _;
      root /usr/local/app;

      access_log /dev/stdout  main;
      error_log  /dev/stderr  warn;

      location / {
        proxy_pass http://app;
        proxy_http_version 1.1;
      }
    }
kubectl apply -f deployment.yaml

Serviceマニュフェストを作成する

OKEのServiceではOracle Cloud のロードバランサが利用できます。
yamlを作成してこちらもapplyをします。

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: loadbalancer
spec:
  type: LoadBalancer
  selector:
    app: fileupload-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
kubectl apply -f service.yaml

これでOracle Cloudのロードバランサが作成されています。OCIコンソールからも確認が出来ました。

Oracle Cloud ロード・バランサ

FSS用のマニュフェストを作成する

これで最後のマニュフェストです。
mntTargetId」は、ファイル・ストレージ・サービスのマウントターゲットOCIDを入力します。「nfs」の「server」に記載するIPアドレスはマウントターゲットのIPアドレスを入力します。OCIコンソールより確認し入力しましょう。

storage.yaml

apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
  name: oci-fss
provisioner: oracle.com/oci-fss
parameters:
  mntTargetId: ocid1.mounttarget.oc1.ap_tokyo_1.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: oke-fsspv
spec:
  storageClassName: oci-fss
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  mountOptions:
    - nosuid
  nfs:
    server: 10.0.1.12
    path: /UploadFiles
    readOnly: false
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: oke-fsspvc
spec:
  storageClassName: oci-fss
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  volumeName: oke-fsspv
kubectl apply -f storage.yaml

3つyamlファイルを作成しましたが、インデントをしっかりしてないと怒られます。インデントの付け方はkubernetesのドキュメントで書き方を確認しておくのが良いかと思います。

OKEクラスターの稼働状況を確認する

これで、podが既に稼働しているはずですので、上手く稼働ができているか確認をしてみたいと思います。
podの状態が「Ready」となっていれば成功です。

kubectl get all
NAME                                  READY   STATUS    RESTARTS   AGE
pod/fileupload-app-7b8786bd5b-2rg58   2/2     Running   0          34m

NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)        AGE
service/kubernetes     ClusterIP      10.96.0.1       <none>           443/TCP        33h
service/loadbalancer   LoadBalancer   10.96.236.164   x.x.x.x          80:30957/TCP   6h41m

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/fileupload-app   1/1     1            1           3h57m

NAME                                        DESIRED   CURRENT   READY   AGE
replicaset.apps/fileupload-app-6df694d58d   0         0         0       3h57m
replicaset.apps/fileupload-app-78fbbd9544   0         0         0       3h47m
replicaset.apps/fileupload-app-7b8786bd5b   1         1         1       34m

Readyになってますね!ただ、実際には上手くいかなくて何回もトライ&エラーでした。…笑
podが上手く動作しない場合や詳細を確認する際には、以下のコマンドでイベントを確認できます。

# Podの情報やイベントを確認する
kubectl describe pods <pod名>
(例: kubectl describe pods fileupload-app-78fbbd9544-snjmt )
# ログを確認する
kubectl logs <pod名> <コンテナ名>
(例: kubectl logs fileupload-app-78fbbd9544-snjmt nginx )

※ 1つのPodに複数のコンテナがある場合は、コンテナ名の指定が必須です。

ということでk8sでWebアプリが稼働している状態になりましたので、ブラウザからも確認ができました。

ファイルアップロードWebアプリケーションのブラウザ上の画面

ブラウザからアップロードしたファイルが実際にファイルストレージサービスにも入っているか作業用コンピュートインスタンスに同じFSSをマウントして確認してみます。

[opc@itport-work ~]$ ls -l /UploadFiles/
total 64
-rw-r--r--. 1 root root 41089 Apr  7 14:12 itport.png

ブラウザからアップロードしたファイルがストレージに入ってます。OKですね!これで今回の目標は全て達成しました!

さいごに

これで今回の検証はすべて終わりとなります。いかがでしたでしょうか。
長い道のりに感じましたが、k8sは一度マニュフェストを作れば、似た構成の場合は、次回から参考にできるので便利だと思いました。

今まではコンピュートインスタンスを立てて、Webアプリケーションを構築する事が多かったのですが、OCIのマネージドサービスだけでWebアプリケーションの構築が出来たことは非常に良い勉強となりました。

今回、Nginxをあえて利用したのは、アクセスログなどは運用上必要だろうと思い利用するようにしてみました。まだまだk8sのことは未熟ですので、セキュリティ対策やロギングなどについては勉強が必要です。

データベースエンジンを利用する場合でも、Autonomous Transaction Processing を選択すれば、OKEとATPでマネージドサービスだけの環境が整いますね!

謝辞

NginxのConfigMapを作成する際に Qiita より @petiviolet さんの記事を参考にさせていただきました。
この場を借りて謝辞を申し上げます。