Skip to content

Infrastructure Adapter を作成し、アプリケーションをデプロイする

本チュートリアルでは、Qmonus Value Streamにおいて、持ち込みのKubernetesのManifestからInfrastructure Adapterを生成し、ビルドしたアプリケーションをデプロイする手順を解説します。

Infrastructure Adapterについて

Infrastructure Adapterとは、システムのインフラストラクチャを構成するクラウドリソース(e.g. kubernetesや各種クラウドプロバイダのリソースなど)をパラメータによりカスタマイズ可能な形に抽象化したリソースです。 Official Infrastructure Adapterの一覧については、Official Cloud Native Adapter 公式リポジトリから確認できます。

実施する前に、アプリケーションのコンテナイメージを作成するのチュートリアルが完了していることをご確認ください。

以下のステップを通して、Infrastructure Adapterを生成し、アプリケーションをデプロイします。

  • Qmonus Value Stream CLIのqvsctl adapter importコマンドを使用してサンプルのManifestからInfrastructure Adapterを生成し、パラメータ化を行います。
  • AssemblyLineを実行し、Infrastructure Adapterで定義したリソースとコンテナイメージ(アプリケーションのコンテナイメージを作成するでコンテナレジストリにPushしたイメージ)がデプロイされていることを確認します。

本章で使用するサンプルManifestでデプロイされるアプリケーションは、以下の図に示すように、Kubernetes DeploymentリソースとServiceリソース、Ingressリソースで構成されるHTTP APIサーバです。概要・前提条件の前提条件に記載していたGKEクラスタへデプロイします。
(Horizontal Pod Autoscalerは次章のInfrastructure Adapter をカスタマイズし、機能を追加するで追加します。)

Infrastructure Adapter

1. サンプルManifestの準備(CLI)

本チュートリアルでは、持ち込みのKubernetesのManifestからInfrastructure Adapterを生成してQmonus Value Streamを実行することを想定し、サンプルのManifestを使用して手順を解説します。

アプリケーションのコンテナイメージを作成するで使用したプライベートリポジトリを使用します。 まず、作業用ブランチを作成します。

bash
# ローカルリポジトリへ移動
cd <repository name>

# 本チュートリアル用のブランチをmainブランチから作成
git switch -c deploy-own-application main

# 作業ディレクトリへ移動
cd .valuestream

準備として、サンプルのKubernetes Manifestを作成します。
Kubernetes Ingress、Service、Deploymentのリソース群を定義します。

REPLACE_NSおよびREPLACE_IMAGEは、後ほどInfrastructure Adapterのパラメータ化にて置換するため、このままで進めてください。

bash
# サンプルのKubernetes Manifestを作成する
vim sample-manifests.yaml
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: flask-demo
  namespace: REPLACE_NS
spec:
  replicas: 1
  selector:
    matchLabels:
      app: flask-demo
  template:
    metadata:
      labels:
        app: flask-demo
    spec:
      containers:
      - name: flask-demo
        image: REPLACE_IMAGE
        ports:
        - containerPort: 5000
        resources:
          requests:
            cpu: 5m
            memory: 32Mi
          limits:
            cpu: 100m
            memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
  name: flask-demo
  namespace: REPLACE_NS
spec:
  type: ClusterIP
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 5000
  selector:
    app: flask-demo
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: flask-demo
  namespace: REPLACE_NS
spec:
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: flask-demo
                port:
                  name: http

2. CUEモジュールファイルの作成

自作したAdapterをQmonus Value Streamで利用するためには、CUE のパッケージ機能を使用します。 以下のコマンドを実行し、cue mod initコマンドでモジュールを定義することにより、モジュールルートとなるcue.mod/module.cueファイルが生成され、自作のAdapterを作成する準備が整います。

CUEのモジュールファイルの詳細についてはCloud Native Adapterのパッケージ仕様を参照してください。

bash
# CUEのモジュールを定義
cue mod init github.com/qmonus/sample

# catコマンドを実行し、モジュールのパスが出力されることを確認
cat cue.mod/module.cue
 module: "github.com/qmonus/sample"

3. Infrastructure Adapterの生成(CLI)

qvsctl adapter import コマンドを使用して、サンプルのKubernetes ManifestからInfrastructure Adapterを生成し、ローカルリポジトリへ出力します。

bash
# Kubernetes ManifestからInfrastructure Adapterを生成
qvsctl adapter import -f sample-manifests.yaml -o local/flask-app.cue

この時点でのフォルダ構成は以下のようになります。

<repository name>/
├── .valuestream/
│   ├── cue.mod/
│   │   └── module.cue
│   ├── local/
│   │   └── flask-app.cue
│   ├── output/
│   │   └── pipeline.yaml
│   ├── assemblyline-dev.yaml
│   ├── qvs.yaml
│   └── sample-manifests.yaml
├── app.py
├── Dockerfile
└── requirements.txt

4. Infrastructure Adapterのパラメータ化(CLI)

出力されたInfrastructure Adapterに対し、生成元のManifestへ割り当てていたパラメータを継承します。 変更箇所はDIFF、正式なコードはCODEブロックを確認ください。

  • package にはアプリケーションに対するKubernetesリソースとしてflaskappというpackage名を入力しています。
  • Deployment、Service、Ingressに使用されるk8sNamespaceとDeploymentで使用されるimageNameは、QVS ConfigやAssemblyLineから値を指定できるようにするため、Infrastructure Adapterのparametersとして定義しています。
  • _constとして、name(アプリケーションの名前)、port(Serviceのポート番号)、targetPort(アプリケーションのコンテナがリッスンするポート番号)として定義し、DeploymentとServiceの両方で参照するように変更しています。
    • 自作で作成するアプリケーションのポート番号が5000番と異なる場合は、targetPortの番号を変更してください。
bash
# Infrastructure Adapterをパラメータ化する
vim local/flask-app.cue
go
package REPLACE_ME
package flaskapp

DesignPattern: {
    name:        "REPLACE_ME"
    description: "REPLACE_ME"
    name:        "flaskapp"
    description: "Flask application deployment with Infrastructure Adapter."

     parameters: {
         k8sNamespace: string
         imageName: string
     }

     _const: {
         #name      : "flask-demo"
         #port      : 80
         #targetPort: 5000
     }

resources: {
        app: {
            resource0: {
                apiVersion: "apps/v1"
                kind:       "Deployment"
                metadata: {
                    name:      "flask-demo"
                    namespace: "REPLACE_NS"
                    name:      _const.#name 
                    namespace: parameters.k8sNamespace
                }
                spec: {
                    replicas: 1
                    selector: {
                        matchLabels: {
                            app: "flask-demo"
                            app: _const.#name 
                        }
                    }
                    template: {
                        metadata: {
                            labels: {
                               app: "flask-demo"
                               app: _const.#name 
                            }
                        }
                        spec: {
                            containers: [{
                                name:  "flask-demo"
                                image: "REPLACE_IMAGE"
                                name:  _const.#name 
                                image: parameters.imageName
                                ports: [{
                                   containerPort: 5000
                                   containerPort: _const.#targetPort 
                                }]
                                resources: {
                                    requests: {
                                        cpu:    "5m"
                                        memory: "32Mi"
                                    }
                                    limits: {
                                        cpu:    "100m"
                                        memory: "128Mi"
                                    }
                                }
                            }]
                        }
                    }
                }
            }
            resource1: {
                apiVersion: "v1"
                kind:       "Service"
                metadata: {
                    name:      "flask-demo"
                    namespace: "REPLACE_NS"
                    name:      _const.#name 
                    namespace: parameters.k8sNamespace
                }
                spec: {
                    type: "ClusterIP"
                    ports: [{
                        name:       "http"
                        port:       80
                        protocol:   "TCP"
                        targetPort: 5000
                        targetPort: _const.#targetPort 
                    }]
                    selector: {
                        app: "flask-demo"
                        app: _const.#name 
                    }
                }
            }
            resource2: {
                apiVersion: "networking.k8s.io/v1"
                kind:       "Ingress"
                metadata: {
                    name: "flask-demo"
                    namespace: "REPLACE_NS"
                    name: _const.#name 
                    namespace: parameters.k8sNamespace
                }
                spec: {
                    rules: [{
                        http: {
                            paths: [{
                                path:     "/"
                                pathType: "Prefix"
                                backend: {
                                    service: {
                                        name: "flask-demo"
                                        name: _const.#name 
                                        port: {
                                            name: "http"
                                        }
                                    }
                                }
                            }]
                        }
                    }]
                }
            }
        }
    }
}
go
package flaskapp

DesignPattern: {
    name:        "flaskapp"
    description: "Flask application deployment with Infrastructure Adapter."

    parameters: {
        k8sNamespace: string
        imageName: string
    }

    _const: {
        #name      : "flask-demo"
        #port      : 80
        #targetPort: 5000
    }

    resources: {
        app: {
            resource0: {
                apiVersion: "apps/v1"
                kind:       "Deployment"
                metadata: {
                    name:      _const.#name
                    namespace: parameters.k8sNamespace
                }
                spec: {
                    replicas: 1
                    selector: {
                        matchLabels: {
                            app: _const.#name
                        }
                    }
                    template: {
                        metadata: {
                            labels: {
                                app: _const.#name
                            }
                        }
                        spec: {
                            containers: [{
                                name:  _const.#name
                                image: parameters.imageName
                                ports: [{
                                   containerPort: _const.#targetPort
                                }]
                                resources: {
                                    requests: {
                                        cpu:    "5m"
                                        memory: "32Mi"
                                    }
                                    limits: {
                                        cpu:    "100m"
                                        memory: "128Mi"
                                    }
                                }
                            }]
                        }
                    }
                }
            }
            resource1: {
                apiVersion: "v1"
                kind:       "Service"
                metadata: {
                    name:      _const.#name
                    namespace: parameters.k8sNamespace
                }
                spec: {
                      type: "ClusterIP"
                      ports: [{
                          name:       "http"
                          port:       80
                          protocol:   "TCP"
                          targetPort: _const.#targetPort
                      }]
                    selector: {
                        app: _const.#name
                    }
                }
            }
            resource2: {
                apiVersion: "networking.k8s.io/v1"
                kind:       "Ingress"
                metadata: {
                    name: _const.#name
                    namespace: parameters.k8sNamespace
                }
                spec: {
                    rules: [{
                        http: {
                            paths: [{
                            path:     "/"
                            pathType: "Prefix"
                                backend: {
                                    service: {
                                        name: _const.#name
                                        port: {
                                            name: "http"
                                        }
                                    }
                                }
                            }]
                        }
                    }]
                }
            }
        }
    }
}

5. QVS Configの修正(CLI)

アプリケーションのコンテナイメージを作成するで使用したQVS Configを修正します。
ここでは、手順4で作成したInfrastructure Adapterflaskappをコンパイルするように指定しています。

またアプリケーションをデプロイするPipelineとして、以下のOfficial CI/CD Adapterを利用します。

  • deploy:simple: Infrastructure Adapterで定義されたKubernetesリソースをデプロイするパイプライン
bash
vim qvs.yaml
yaml
params:
   - name: k8sNamespace
     type: string
   - name: imageName
     type: string

modules:
   - name: github.com/qmonus/sample  # 2.CUEモジュールファイルの作成で定義したモジュール名
     local:
       path: .                       # QVS Configがあるディレクトリから、CUEモジュールのルートディレクトリまでの相対パス
  - name: qmonus.net/adapter/official

designPatterns:
  # Infrastructure Adapter
   - pattern: github.com/qmonus/sample/local:flaskapp # 4.Infrastructure Adapterのパラメータ化で作成したpackage名を指定
     params:
       k8sNamespace: $(params.k8sNamespace)
       imageName: $(params.imageName)

  # CI/CD Adapter
  - pattern: qmonus.net/adapter/official/pipeline/build:buildkitGcp
  - pattern: qmonus.net/adapter/official/pipeline/deploy:simple
yaml
params:
  - name: k8sNamespace
    type: string
  - name: imageName
    type: string

modules:
  - name: github.com/qmonus/sample      # 2.CUEモジュールファイルの作成で定義したモジュール名
    local:
      path: .                           # QVS Configがあるディレクトリから、CUEモジュールのルートディレクトリまでの相対パス
  - name: qmonus.net/adapter/official

designPatterns:
  # Infrastructure Adapter
  - pattern: github.com/qmonus/sample/local:flaskapp # 4.Infrastructure Adapterのパラメータ化で作成したpackage名を指定
    params:
      k8sNamespace: $(params.k8sNamespace)
      imageName: $(params.imageName)

  # CI/CD Adapter
  - pattern: qmonus.net/adapter/official/pipeline/build:buildkitGcp
  - pattern: qmonus.net/adapter/official/pipeline/deploy:simple

6. Infrastructure Adapterの動作確認(CLI)

本手順では、qvsctl manifest compileコマンドとparams.jsonのパラメータファイルを用いて、Infrastructure Adapterが正しく動作するかどうかを確認します。

qvsctl manifest compileとparams.jsonについて

qvsctl manifest compile コマンドは、Infrastructure Adapterをローカル環境でコンパイルし、生成されるKubernetes Manifestを事前に確認するためのコマンドです。 params.jsonはローカルでの動作確認時に、パラメータを明示的に指定するために用意します。 これにより、デプロイ前にInfrastructure Adapterの構成やパラメータ展開が正しく動作するかを検証できます。

bash
# params.jsonを作成する(実際にはQmonus Value Streamの中で自動的に作成される)
vim params.json
json
{
    "params": [
        {
            "name": "k8sNamespace",
            "value": "flask-demo"
        },
        {
            "name": "imageName",
            "value": "dummyimage"
        }
    ]
}

Infrastructure Adapterが正しくコンパイルできるかどうかを確認します。

bash
# qvsctlでKubernetes Manifestにコンパイル
qvsctl manifest compile -m . -c qvs.yaml -p params.json -o output/manifest.yaml
less output/manifest.yaml

この時点でのフォルダ構成は以下のようになります。

<repository name>/
├── .valuestream/
│   ├── cue.mod/
│   │   └── module.cue
│   ├── local/
│   │   └── flask-app.cue
│   ├── output/
│   │   ├── manifest.yaml
│   │   └── pipeline.yaml
│   ├── assemblyline-dev.yaml
│   ├── params.json
│   ├── qvs.yaml
│   └── sample-manifests.yaml
├── app.py
├── Dockerfile
└── requirements.txt

7. Pipeline Manifestの生成および登録 (CLI)

アプリケーションのコンテナイメージを作成すると同様に、qvsctl pipeline compileコマンドを用いてPipeline Manifestを生成し、Qmonus Value Streamへ登録します。

bash
# コンパイルする
qvsctl pipeline compile -c qvs.yaml --prefix flask-demo -o output/pipeline.yaml

# qvsctl auth コマンド使用して認証する(出力されたurlへアクセスしてください)
qvsctl auth

# Project Nameを取得する
qvsctl project list

# 取得したProject Nameを環境変数に設定する
export projectName=<PROJECT NAME>

# Pipeline Manifestを登録する
qvsctl pipeline apply -p ${projectName} -f output/pipeline.yaml

新たに1つのpipelineflask-demo-deployと2つのtaskflask-demo-compile-design-patternflask-demo-deployment-workerが生成されることを確認してください。

8. AssemblyLineの修正(CLI)

AssemblyLineに、deployのStageを定義し、作成したPipelineおよびDeploymentを指定します。 デプロイ対象であるimageNameはresultsとして定義していた$(stages.build.results.imageFullNameTag)を指定します。 また、artifactsを定義し、デプロイするリソースのKubernetes ManifestをAssemblyLine実行後にダウンロードできるようにします。

bash
# assemblyline-dev.yamlを編集する
vim assemblyline-dev.yaml
yaml
apiVersion: vs.axis-dev.io/v1
kind: AssemblyLine
metadata:
  name: flask-demo-dev
spec:
  params:
    - name: gitRevision
      type: string
  stages:
    - name: build
      spec:
        pipeline: flask-demo-build
        deployment:
          app: flask-demo
          name: flask-demo-dev
        params:
          - name: gitRevision
            value: $(inputs.gitRevision)
     - name: deploy
       spec:
         pipeline: flask-demo-deploy
         deployment:
           app: flask-demo
           name: flask-demo-dev
         params:
           - name: gitRevision
             value: $(inputs.gitRevision)
           - name: imageName
             value: $(stages.build.results.imageFullNameTag)
       runAfter:
         - build
  results:
    - name: gitRevision
      value: $(inputs.gitRevision)
    - name: imageName
      value: $(stages.build.results.imageFullNameTag)
  artifacts:   
    - path: manifests
yaml
apiVersion: vs.axis-dev.io/v1
kind: AssemblyLine
metadata:
  name: flask-demo-dev
spec:
  params:
    - name: gitRevision
      type: string
  stages:
    - name: build
      spec:
        pipeline: flask-demo-build
        deployment:
          app: flask-demo
          name: flask-demo-dev
        params:
          - name: gitRevision
            value: $(inputs.gitRevision)
    - name: deploy
      spec:
        pipeline: flask-demo-deploy
        deployment:
          app: flask-demo
          name: flask-demo-dev
        params:
          - name: gitRevision
            value: $(inputs.gitRevision)
          - name: imageName
            value: $(stages.build.results.imageFullNameTag)
      runAfter:
        - build
  results:
    - name: gitRevision
      value: $(inputs.gitRevision)
    - name: imageName
      value: $(stages.build.results.imageFullNameTag)
  artifacts:
    - path: manifests

artifactsについて

spec.artifactsフィールドを定義すると、AssemblyLine実行中にコンパイル・生成されたファイルを、AssemblyLineの実行結果からダウンロードできるようになります。 manifestsを指定した場合は、Infrastructure AdapterからコンパイルされたKubernetes Manifestをダウンロードできるようになります。 そのほかにも.の全ファイル指定や、特定のフォルダ・ファイルの指定が可能です。仕様については、What is AssemblyLineを確認ください。 Artifactsボタン

9. AssemblyLineの登録 (CLI)

修正したAssemblyLineを Qmonus Value Stream のProjectへ登録します。

bash
# AssemblyLineをApplyして更新する
qvsctl pipeline apply -p ${projectName} -f assemblyline-dev.yaml

git add、 git commit、 git pushを行い、リモートリポジトリに変更を反映します。 またコミットIDをAssemblyLineを実行するときに指定するため控えておきます。

bash
# リモートリポジトリにpushする
git add --all
git commit -m "Deploy Own Application QVS Tutorial"
git push origin deploy-own-application

# コミットIDを控える
git log -1

10. Environmentの準備

アプリケーションのコンテナイメージを作成するにて作成したQmonus Value StreamのEnvironmentのProvisioning TargetにKubernetesを追加します。

  1. 左メニューより、Environmentを選択します。
  2. flask-demo-devを選択します。
  3. 画面右上のEditボタンをクリックします。
  4. Provisioning Targetセクションで、+ボタンをクリックします。
  5. 各フォームに以下の値を入力します。
    • Kind: kubernetes
    • Display Name: k8s-cluster-dev
    • Alias: (空白)

以下は入力例です。

  1. 画面右下のSaveボタンを押下してEnvironmentを更新します。

11. Deploymentの準備

アプリケーションのコンテナイメージを作成するにて作成したQmonus Value StreamのDeploymentに、デプロイ先のKubernetesクラスタ情報を設定します。

  1. 左メニューより、Applicationを選択します。
  2. flask-demoを選択します。
  3. 表示されるDeploymentsから、flask-demo-devを選択します。
  4. 画面右上のEditボタンをクリックします。
  5. Credentialsセクションで、以下の認証情報を追加します。
    • Kubernetes
      • namespace: flask-demo
        • デプロイ先のKubernetesのNamespace
      • kubeconfig: Kubernetesクラスタへのアクセス権限を持つKubeconfig(YAML形式)

Kubernetesのnamespaceについて

デプロイ先のKubernetesクラスタに、指定したnamespaceが存在しない場合は、事前に作成してください。

bash
gcloud container clusters get-credentials <クラスタ> --zone <ノードゾーン> --project <GCPプロジェクトID>

kubectl create namespace flask-demo

以下は入力例です。

  1. 画面右下の Validate kubeconfig ボタンを押下してValidateが成功することを確認します。
    • Successfully validated your kubeconfigと表示されれば成功です。
  2. 画面右下のSaveボタンを押下してDeploymentを更新します。

12. Deployment Configの設定 (GUI)

Qmonus Value StreamのAssemblyLineより、不足している下記のパラメータを入力します。

  1. 左メニューより、AssemblyLineを選択します。
  2. 画面中央に表示されるflask-demo-devを選択します。
  3. Pipeline Stagesに表示されているdeployカードを選択します。
  4. 画面右に表示されるEdit Deployment Configボタンをクリックします。
  5. YAMLモードのEditorが表示されますので、デプロイ先のnamespaceをYAML形式で入力します。必要なパラメータ一覧は以下の通りです。
    • k8sNamespace : デプロイ先のKubernetesのNamespace(例:flask-demo)

以下は入力例です。

yaml
k8sNamespace: flask-demo
imageRegistryPath: asia-northeast1-docker.pkg.dev/gcp-project/qmonus
imageShortName: flask-demo
imageTag: v0.0.1
pathToContext: .
  1. Saveボタンを押下し、deployのInput Parameterが全て緑で表示されていることを確認します。

13. AssemblyLineの実行 (GUI)

登録したAssemblyLineを実行し、flask-demoアプリケーションをKubernetesにデプロイします。

  1. 左メニューより、AssemblyLineを選択します。
  2. 画面中央に表示されるflask-demo-devを選択します。
  3. Pipelines(Stages)に表示されているbuild,deployカードを選択し、それぞれ全てのパラメータが埋まっていることを確認します。
  4. 以下のInput Parameterを入力し、RUNボタンを押下してCI/CDを開始します。
    • gitRevision: コミットID
      • このコミットIDは、手順9で控えたコミットIDを指定してください。

14. デプロイされたアプリケーションの確認(CLI)

Kubernetesにアクセスして、デプロイに成功したリソースとアプリケーションを確認します。 デプロイ完了後、Ingressが機能するまでに時間がかかりますので、5分ほどお待ちして確認ください。

bash
# GKEクラスタへ接続する
gcloud container clusters get-credentials <クラスタ> --zone <ノードゾーン> --project <GCPプロジェクトID>

# デプロイしたDeploymentを確認する
kubectl get deployment -n flask-demo
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
flask-demo   1/1     1            1           XX

# デプロイしたServiceを確認する
kubectl get service -n flask-demo
NAME         TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
flask-demo   ClusterIP    XX.XX.XX.XX     <none>        80/TCP   XX

# デプロイしたIngressを確認する(機能するまでに時間がかかるので、5分程待って実行)
kubectl get ingress -n flask-demo
NAME         CLASS    HOSTS   ADDRESS          PORTS   AGE
flask-demo   <none>   *       ${external_ip}   80      XX

# 上記IngressにアサインされたEXTERNAL-IPにアクセス
# Hello from Qmonus Value Streamが表示されることを確認
curl http://${external_ip}; echo
Hello from Qmonus Value Stream

解説

本チュートリアルでは、Qmonus Value Stream CLIのqvsctl adapter importコマンドを用いて、サンプルのKubernetes ManifestからInfrastructure Adapterを生成できることを確認しました。このように、Kubernetes Manifestを既に利用している場合は、同一のリソースを提供するInfrastructure Adapterのフォーマットに簡単に変換できます。 詳細については、Kubernetes ManifestからInfrastructure Adapterへの変換を参照してください。

Infrastructure Adapterの詳しい仕様については、Infrastructure Adapter Specificationを参照してください。

次のステップ

次は以下のチュートリアルに進んで、Infrastructure Adapterをカスタマイズしてみましょう。