Skip to content

機密情報を取り扱うアプリケーションのデプロイ GCP編

本章では、Qmonus Value Streamの機能であるDeployment Secretを利用して機密情報を取り扱うアプリケーションのデプロイ手順を解説します。

Deployment Secretでは、GUIを通してユーザが利用する機密情報の管理サービス (e.g. GCPのSecret Manager, AzureのKey Valut, etc.)に保存できます。

本チュートリアルを実施するまえに、External Secrets Operatorの導入が完了していることをご確認ください。また、以下のプライベートリポジトリが作成されていることを前提とします。

  • 持ち込みのGitリポジトリ
    • GitHub、GitLabなどのインターネットからアクセス可能なGitリポジトリをご用意ください。
    • 本チュートリアルではGitHubを使って説明しますが、適宜ご利用されているサービスに読み替えてください。
    • GitHubリポジトリへの認証は Personal Access Token (GitLabの場合は Personal access tokens)を使用することを前提とします。

以降のステップでは主に下記の作業を通じて、Deployment Secretを用いた機密情報の登録とその機密情報を利用したアプリケーションのデプロイができていることを確認します。

  • Deployment Secret機能を利用して、機密情報をGCPのSecret Managerへ登録する
  • 簡単なWEB/DBサーバを作成しDBサーバへのログインを登録したSecretを用いて行う
機密情報を取り扱うアプリケーションのデプロイ

1. Kubeconfigの作成 (CLI)

Qmonus Value Streamは、Kubeconfigと呼ばれる認証ファイルを利用してKubernetesへアプリケーションをデプロイします。ここでは、今回デプロイする対象のKubernetes Namespaceに権限を持つKubeconfigを生成します。

以下の手順にしたがって、ユーザ自身で準備いただいたKubernetesクラスタにアクセスし、Kubeconfigを生成してください。ここで、gcp_project_idはGCP Project ID、cluster_nameはKubernetesクラスタ名、zoneはGKEのZone名を指定します。k8sNamespaceは、今回デプロイするKubernetes Namespace名です。ここで指定したNamespace名は、以降の手順でCDパイプラインを実行する際に、実行パラメータとして使用されます。

bash
# 利用するKubernetesへ接続
gcloud --project=${gcp_project_id} container clusters get-credentials ${cluster_name} --zone ${zone}

# Kubeconfigの生成
qvsctl plugin gen-kubeconfig -n ${k8sNamespace}

手順通りに実行するとKubeconfigはoutput.kubeconfig.yamlというファイルで生成されます。 なお、gcloudコマンドをはじめて実行する場合は、以下の手順によりgcloudコマンドを初期化してください。

bash
gcloud auth login

2. Applicationの登録 (GUI)

今回利用するサンプルアプリケーションを登録します。

  1. 左メニューより、Applicationを選択します。
  2. 画面右上の NEW APPLICATION ボタンを押下します。
  3. 各フォームに以下の値を入力し、画面右下の NEXT ボタンを押下します。
    • Display Name: Deployment Secret
    • Description: (任意の文章または空白)
    • QVS Config Repository: + Create New Repositoryを選択し、手順2-aに従いRepositoryを作成
    • QVS Config File Path: .valuestream/qvs.yaml

以下は、必要な値を入力した状態のApplicationの登録画面になります。

Applicationの登録

2-a. Repositoryの登録

今回用意したプライベートリポジトリをQmonus Value Streamに登録します。

以下のパラメータの値を変更し、画面右下のCREATEボタンを押下します。

  • Repository Kind: github
  • Git Clone Protocol: https
  • Repository Visibility: private
  • Git Clone URL: (チュートリアルで利用するリポジトリのURLを入力、例:https://github.com/sample-org/sample-repo-name.git)
  • Description: (任意の文章または空白)
  • Git Token:(プライベートリポジトリへの認証に利用するTokenを入力)

3. Deploymentの登録 (GUI)

手順1で生成した、クラスタ・スコープのkubeconfigを使用してDeploymentを作成します。

各フォームに以下の値を入力し、画面右下の CREATE ボタンを押下します。

  • Display Name: staging
  • Name: (Environment選択時に自動入力)
  • Environment: + Create New Environmentを選択し、手順3-aに従いEnvironmentを作成
    • 注) Application単位で、同一Environmentを選択できません
  • Credentials
    • kubeconfig: 手順1で生成した、output.kubeconfig.yamlの内容

以下は、必要な値を入力した状態のDeploymentの登録画面になります。

Deploymentの登録

3-a. Environmentの登録

デプロイ先となる環境を登録します。 また、Qmonus Value Streamが推奨する環境の考え方については、環境定義を参考にしてください。

各フォームに以下の値を入力し、画面右下のCREATEボタンを押下します。

  • Display Name: (staging など、小文字で任意の環境名)
  • Description: (任意の文章または空白)
  • Provisioning Target:
    • Kind:kubernetes
    • DisplayName: Provisioning Targetを一意に指定できるNameやID
    • Alias:(空白)
    • 同じタイプのProvisioning Targetを複数登録する際は、2つ目以降については Aliasのフォームに そのProvisioning Targetを特定できるエイリアス名を付与してください。

4. Official Cloud Native Adapterのダウンロード (CLI)

事前に準備いただいたプライベートリポジトリにて、Official Cloud Native AdapterのCI/CD AdapterとInfrastructure Adapterをqvsctlコマンドでダウンロードします。

bash
# プライベートリポジトリをcloneする
git clone https://gihub.com/${your_organization}/${your_repository}

# プライベートリポジトリに移動
cd ${your_repository}

# 本チュートリアル用のブランチを作成する
git checkout -b deployment_secret

# KubeconfigをGitリポジトリの管理から除外する
echo 'output.kubeconfig.yaml' >> .gitignore

# 本チュートリアル用のディレクトリを作成する
mkdir -p .valuestream/local
cd .valuestream

# CUEのImportパスを管理するファイルである module.cueを生成する
cue mod init github.com/qmonus/sample

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

# Official Cloud Native Adapter でダウンロードできるパッケージの確認
qvsctl adapter list

NAME                         LATEST  RELEASED DATE
qmonus.net/adapter/official  vx.x.x  YYYY-MM-DD hh:mm:ss

# Official Cloud Native Adapterをダウンロードする
qvsctl adapter get qmonus.net/adapter/official

5. Infrastructure Adapterの作成 (CLI)

今回デプロイするアプリケーションの構成ファイルとしてInfrastructure Adapterを作成します。下記の内容をコピーしてファイルを作成してください。 まずはvim(他のエディタでも可)でlocalディレクトリ配下にmain.cueというファイルを作成します。

bash
vim local/main.cue

main.cueには下記の内容を記載します。

go
package local

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/yaml"
	"strings"

	"qmonus.net/adapter/official/kubernetes:types"
)

#Secret: {
	key:     string
	version: string
}

#ExternalSecret: {
	provider:   "kubernetes"
	apiVersion: "external-secrets.io/v1beta1"
	kind:       "ExternalSecret"
	output: [...string]
	...
}

DesignPattern: {
	name: "sqlserver"

	parameters: {
		webappName:   string
		dbName:       string
		k8sNamespace: string
		dbuser:       #Secret
		dbpass:       #Secret
		dbrootpass:   #Secret
	}

	resources: app: {
		deployment1: _deployment1
		deployment2: _deployment2
		service1:    _service1
		service2:    _service2
		secret:      _secret
	}

	let _esData = yaml.Marshal(_secret.spec.data)

	let _hash = strings.SliceRunes(hex.Encode(sha256.Sum256(_esData)), 0, 10)

	_deployment1: types.#Deployment & {
		metadata: {
			name:      parameters.dbName
			namespace: parameters.k8sNamespace
			annotations: "vs.axis-dev.io/dependsOn": "external-secrets.io:ExternalSecret::\(parameters.k8sNamespace)/\(parameters.dbName)-\(_hash)"
		}
		spec: {
			replicas: 1
			selector: {
				matchLabels: {
					app: "mysql"
				}
			}
			template: {
				metadata: {
					labels: {
						app: "mysql"
					}
				}
				spec: {
					containers: [{
						name:  "mysql"
						image: "mysql:5.7"
						env: [{
							name: "MYSQL_ROOT_PASSWORD"
							valueFrom: secretKeyRef: {
								name: "\(parameters.dbName)-\(_hash)"
								key:  parameters.dbrootpass.key
							}
						}, {
							name:  "MYSQL_DATABASE"
							value: "mydb"
						}, {
							name: "MYSQL_PASSWORD"
							valueFrom: secretKeyRef: {
								name: "\(parameters.dbName)-\(_hash)"
								key:  parameters.dbpass.key
							}
						}, {
							name: "MYSQL_USER"
							valueFrom: secretKeyRef: {
								name: "\(parameters.dbName)-\(_hash)"
								key:  parameters.dbuser.key
							}
						}]
						ports: [{
							containerPort: 3306
							name:          "mysql"
						}]
					}]
				}
			}
		}
	}
	_service1: types.#Service & {
		metadata: {
			name:      parameters.dbName
			namespace: parameters.k8sNamespace
		}
		spec: {
			selector: {
				app: "mysql"
			}
			ports: [{
				port:       3306
				protocol:   "TCP"
				targetPort: 3306
			}]
		}
	}

	_deployment2: types.#Deployment & {
		metadata: {
			name:      parameters.webappName
			namespace: parameters.k8sNamespace
			annotations: "vs.axis-dev.io/dependsOn": "external-secrets.io:ExternalSecret::\(parameters.k8sNamespace)/\(parameters.dbName)-\(_hash)"
		}
		spec: {
			replicas: 1
			selector: {
				matchLabels: {
					app: "phpmyadmin"
				}
			}
			template: {
				metadata: {
					labels: {
						app: "phpmyadmin"
					}
				}
				spec: {
					containers: [{
						name:  "phpmyadmin"
						image: "phpmyadmin"
						env: [{
							name: "MYSQL_ROOT_PASSWORD"
							valueFrom: secretKeyRef: {
								name: "\(parameters.dbName)-\(_hash)"
								key:  parameters.dbrootpass.key
							}
						}, {
							name:  "PMA_HOST"
							value: "mysql"
						}, {
							name:  "PMA_PORT"
							value: "3306"
						}]
						ports: [{
							containerPort: 80
						}]
					}]
				}
			}
		}
	}

	_service2: types.#Service & {
		metadata: {
			name:      parameters.webappName
			namespace: parameters.k8sNamespace
		}
		spec: {
			selector: {
				app: "phpmyadmin"
			}
			ports: [{
				port:       80
				protocol:   "TCP"
				targetPort: 80
			}]
			type: "LoadBalancer"
		}

	}
	_secret: #ExternalSecret & {
		metadata: {
			name:      "\(parameters.dbName)-\(_hash)"
			namespace: parameters.k8sNamespace
		}
		spec: {
			refreshInterval: "0"
			secretStoreRef: {
				name: "gcp-secret-manager"
				kind: "ClusterSecretStore"
			}
			target: {
				name:           "\(parameters.dbName)-\(_hash)"
				creationPolicy: "Owner"
			}
			data: [{
				secretKey: "dbuser"
				remoteRef: {
					key:     parameters.dbuser.key
					version: parameters.dbuser.version
				}
			}, {
				secretKey: "dbpass"
				remoteRef: {
					key:     parameters.dbpass.key
					version: parameters.dbpass.version
				}
			}, {
				secretKey: "dbrootpass"
				remoteRef: {
					key:     parameters.dbrootpass.key
					version: parameters.dbrootpass.version
				}
			}]
		}
	}
}

Info

ここではDeployment SecretでのSecretのバージョン変更に対するDBパスワード等の更新が有効になるように、Secret リソース名にデータのハッシュ値を含めること、およびExternal Secretとの依存関係を示すAnnotationを付与するように実装しています。詳細を確認されたい場合は、SecretとConfigMapの更新に追従したDeploymentリソースの定義を参照してください。

6. QVS Configの作成 (CLI)

Qmonus Value Streamの設定ファイルであるQVS Configを作成します。 vimでqvs.yamlという名前のファイルを.valuestreamディレクトリ直下に作成します。

bash
vim qvs.yaml

qvs.yamlへ記載する内容は下記の通りです。

yaml
params:
  - name: k8sNamespace
    type: string
  - name: dbName
    type: string
  - name: webappName
    type: string
  - name: dbuser
    type: secret
  - name: dbpass
    type: secret
  - name: dbrootpass
    type: secret

modules:
  - name: qmonus.net/adapter/official
    local: 
      path: .

designPatterns:
  - pattern: github.com/qmonus/sample/local
    params:
      k8sNamespace: $(params.k8sNamespace)
      dbName:  	    $(params.dbName)
      webappName:   $(params.webappName)
      dbuser:       $(params.dbuser)
      dbpass:       $(params.dbpass)
      dbrootpass:   $(params.dbrootpass)

  - pattern: qmonus.net/adapter/official/pipeline/deploy:simple

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

手順6で作成したQVS Configを用いてPipeline Manifestを生成し、Qmonus Value Streamへ登録します。 projectName はQmonus Value StreamのProject Nameです。Project Nameは、qvsctl project list で取得したPROJECT NAMEを指定してください。

WARNING

以降、qvsctl pipeline applyコマンドを使って、Tekton Task、Pipeline、さらにはAssemblyLineを登録していきます。ここではQmonus Value StreamのKubernetesクラスタにアクセスしています。このクラスタは、手順1でKubeconfigを作成した、これからアプリケーションをデプロイしようとしているクラスタとは別のものです。

bash
# コンパイルする
qvsctl pipeline compile -m . -c qvs.yaml --prefix deployment-secret

# Project Nameを取得する
qvsctl project list

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

applyを実行した際に1つのpipelinedeployment-secret-deployと3つのtaskdeployment-secret-git-checkout,deployment-secret-compile-design-pattern,deployment-secret-deployment-workerが登録されることを確認してください。

8. AssemblyLineの登録(CLI)

作成したPipelineを実行するためのAssemblyLineを作成し、Qmonus Value Streamへ登録します。

bash
# AssemblyLineを作成する
vim deployment-secret-deploy-assemblyline.yaml
yaml
apiVersion: vs.axis-dev.io/v1
kind: AssemblyLine
metadata:
  name: deploymentsecret-deploy
spec:
  params:
    - name: gitRevision
      type: string
  stages:
    - name: deploy
      spec:
        pipeline: deployment-secret-deploy
        deployment:
          app: deployment-secret
          name: staging
        params:
          - name: gitRevision
            value: $(inputs.gitRevision)

作成したAssemblyLineを登録します。

bash
# AssemblyLineを登録する
qvsctl pipeline apply -p ${projectName} -f deployment-secret-deploy-assemblyline.yaml

リモートリポジトリに変更を反映します。

bash
git add --all
git commit -m "Deployment Secret QVS Tutorial"
git push origin deployment_secret

9. Deployment Secretの登録 (GUI)

Qmonus Value StreamからDeployment Secret機能を用いて、GCP Secret Managerに保存されている機密情報をユーザの権限に応じて、追加・取得・削除が可能です。今回はDeployment Secret機能から新規登録した機密情報を扱います。こちらにSecretの登録については、ガイドを参照してください。

GCPへの認証

  1. 左メニューより、AssemblyLineを選択します。
  2. 画面中央に表示される deploymentsecret-deploy を選択します。
  3. Pipeline Stagesに表示されている deploy カードを選択します。
  4. 画面下側にSecretsの項目があることを確認します。
  5. 画面右側のEdit Deployment Secretボタンを押下します。
  6. ポップアップしてきたページのフォームにそれぞれ下記のような項目を記入し、SYNC SECRET BACKENDボタンを押下します
    • Provider Type: gcp
    • Backend ID: 利用するGCPのプロジェクトID
  7. GCPによる認証画面がポップアップされるのでそれぞれログイン(アカウントの選択)とプライバシーポリシーへの許可を押下します。

GCP Secret Managerへの機密情報の登録

今回はデプロイするDBのユーザ名とパスワードの2つをSecretとして登録します。

  1. 先ほどのGCP Secret Managerへアクセスする手順の5.までを行いEdit Deployment Secret画面まで進みます。
  2. CREATE SECRETボタンを押下します。
  3. ログインのユーザ名を作成します。フォームに下記の値を入力しSAVEボタンを押下します。この手順を繰り返し3つのSecretを作成します。
    • DBユーザ名 手順12の動作確認でユーザ名として入力します
      • Key: dbuser
      • Secret Value: user1
    • DBパスワード 手順12の動作確認でパスワードとして入力します
      • Key: dbpass
      • Secret Value: p@ssw0rd1
    • DBルートパスワード phpMyAdminとMySQLを接続するために利用するパスワードとなっています
      • Key: dbrootpass
      • Secret Value: rootp@ssw0rd1

上記の手順で登録された機密情報は、実際にGCPコンソール上からもSecretとして確認できます。

アプリケーションにバインディングするSecretとそのVersionを設定

  1. Edit Deployment Secret画面まで進みます
  2. Secret Bindingのフォームにそれぞれ下記のように値を入力します。プルダウンメニューより値を選択してください。
    • Name: dbuser
      • Key: dbuser
      • version: 1
    • Name: dbpass
      • Key: dbpass
      • version: 1
    • Name: dbrootpass
      • Key: dbrootpass
      • version: 1
  3. SAVEボタンを押下します

ここでKeyの値を選択したものは先ほどDeployment Secretから作成したSecretとなっています。

10. Deployment Configの登録 (GUI)

AssemblyLine詳細画面より、不足しているパラメータを設定します。

  1. 左メニューより、AssemblyLineを選択します。
  2. 画面中央に表示される deploymentsecret-deploy を選択します。
  3. AssemblyLinePipeline Stagesdeploy パネルを選択します。
  4. AssemblyLine Stage 画面右に表示される Edit Deployment Config リンクを選択します。
  5. YAMLモードのEditorが表示されますので、以下の通りInfrastructure Adapterに必要なパラメータをYAML形式で入力します。
    • ${k8sNamespace}: 手順1で作成した${k8sNamespace}
    yaml
      k8sNamespace:       ${k8sNamespace}
      dbName:             mysql
      webappName:         phpmyadmin
  6. Saveボタンを押下し、Input Parameters 一覧全てが緑で表示されていることを確認します。

11. AssemblyLineの実行 (GUI)

登録したAssemblyLineを実行します。 以下のInput Parameterを入力し、AssemblylineのRUN ボタンを押下してCI/CDを開始します。

  • gitRevision: deployment_secret

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

Kubernetesにアクセスして、デプロイに成功したアプリケーションを確認します。

bash
# kubectl の現在のコンテキストを確認する
kubectl config current-context
(アプリケーションをデプロイしたクラスタ名)

## Deploymentリソースを確認する
kubectl get deployment -n ${k8sNamespace}

NAME                                        READY   UP-TO-DATE   AVAILABLE   AGE
mysql                                       1/1     1            1           
phpmyadmin                                  1/1     1            1           

# serviceがデプロイされていることを確認する
kubectl get service -n ${k8sNamespace}

NAME          TYPE           CLUSTER-IP       EXTERNAL-IP       PORT(S)        AGE
mysql         ClusterIP      xxx.xxx.xxx.xxx  <none>            3306/TCP            
phpmyadmin    LoadBalancer   xxx.xxx.xxx.xxx  xxx.xxx.xxx.xxx   80:30080/TCP

ブラウザから下記のようにphpmyadminのEXTERNAL-IPへアクセスします。 http://${EXTERNAL-IP} 下記の画面へアクセスできることを確認し、手順9で決めたユーザ名/パスワードを入力します。 アプリケーションの確認 ログインが成功すると下記のような画面へ遷移します。 アプリケーションの確認 Qmonus Value StreamのDeployment Secretで設定した値通りにユーザ名パスワードが設定されSecretとして値をアプリケーションに受け渡していることが確認できました。

解説

QVS Configで記載しているSecret型について

今回作成するQVS Configの8~13行目のparamsではtype: secretと定義しています。

yaml
params:
  - name: k8sNamespace
    type: string
  - name: dbName
    type: string
  - name: webappName
    type: string
  - name: dbuser
    type: secret
  - name: dbpass
    type: secret
  - name: dbrootpass
    type: secret

modules:
...

Cloud Native Adapter機密情報のオブジェクトとして取り扱うためには、Secret型を指定してください。 より詳しい説明については、QVS ConfigのSecret型、および、Cloud Native AdapterのSecret型をご確認ください。

Deployment SecretにおけるVersion指定

本チュートリアルでは紹介しませんでしたが、Deployment Secret画面では、SecretのVersionを更新できます。 Edit Deployment Secretの画面からadd versionボタンを押下することで下記画像のポップアップが表示されます。 Deployment SecretにおけるVersion指定

1つ目の欄でプルダウンメニューを開きversionを更新したSecret名を選択し、Secret Value欄へ値を入力することで、Secretに新しいVersionを追加できます。

また、追加したVersionは、Secret Bindingでプルダウンメニューから選択でき、モーダルでの更新後にAssemblyLineを流し直すことで、アプリケーションの機密情報を更新できます。