Appearance
機密情報を取り扱うアプリケーションのデプロイ 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)
今回利用するサンプルアプリケーションを登録します。
- 左メニューより、Applicationを選択します。
- 画面右上の
NEW APPLICATION
ボタンを押下します。 - 各フォームに以下の値を入力し、画面右下の
NEXT
ボタンを押下します。- Display Name: Deployment Secret
- Description: (任意の文章または空白)
- QVS Config Repository:
+ Create New Repository
を選択し、手順2-aに従いRepositoryを作成 - QVS Config File Path: .valuestream/qvs.yaml
以下は、必要な値を入力した状態の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の登録画面になります。
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を特定できるエイリアス名を付与してください。
- Kind:
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への認証
- 左メニューより、AssemblyLineを選択します。
- 画面中央に表示される
deploymentsecret-deploy
を選択します。 - Pipeline Stagesに表示されている
deploy
カードを選択します。 - 画面下側にSecretsの項目があることを確認します。
- 画面右側の
Edit Deployment Secret
ボタンを押下します。 - ポップアップしてきたページのフォームにそれぞれ下記のような項目を記入し、
SYNC SECRET BACKEND
ボタンを押下します- Provider Type: gcp
- Backend ID: 利用するGCPのプロジェクトID
- GCPによる認証画面がポップアップされるのでそれぞれログイン(アカウントの選択)とプライバシーポリシーへの許可を押下します。
GCP Secret Managerへの機密情報の登録
今回はデプロイするDBのユーザ名とパスワードの2つをSecretとして登録します。
- 先ほどのGCP Secret Managerへアクセスする手順の5.までを行い
Edit Deployment Secret
画面まで進みます。 CREATE SECRET
ボタンを押下します。- ログインのユーザ名を作成します。フォームに下記の値を入力し
SAVE
ボタンを押下します。この手順を繰り返し3つのSecretを作成します。
上記の手順で登録された機密情報は、実際にGCPコンソール上からもSecretとして確認できます。
アプリケーションにバインディングするSecretとそのVersionを設定
Edit Deployment Secret
画面まで進みますSecret Binding
のフォームにそれぞれ下記のように値を入力します。プルダウンメニューより値を選択してください。- Name: dbuser
- Key: dbuser
- version: 1
- Name: dbpass
- Key: dbpass
- version: 1
- Name: dbrootpass
- Key: dbrootpass
- version: 1
- Name: dbuser
SAVE
ボタンを押下します
ここでKeyの値を選択したものは先ほどDeployment Secretから作成したSecretとなっています。
10. Deployment Configの登録 (GUI)
AssemblyLine詳細画面より、不足しているパラメータを設定します。
- 左メニューより、AssemblyLineを選択します。
- 画面中央に表示される
deploymentsecret-deploy
を選択します。 AssemblyLine
のPipeline Stages
のdeploy
パネルを選択します。AssemblyLine Stage
画面右に表示されるEdit Deployment Config
リンクを選択します。- YAMLモードのEditorが表示されますので、以下の通りInfrastructure Adapterに必要なパラメータをYAML形式で入力します。
${k8sNamespace}
: 手順1で作成した${k8sNamespace}
yamlk8sNamespace: ${k8sNamespace} dbName: mysql webappName: phpmyadmin
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
ボタンを押下することで下記画像のポップアップが表示されます。
1つ目の欄でプルダウンメニューを開きversionを更新したSecret名を選択し、Secret Value
欄へ値を入力することで、Secretに新しいVersionを追加できます。
また、追加したVersionは、Secret Binding
でプルダウンメニューから選択でき、モーダルでの更新後にAssemblyLineを流し直すことで、アプリケーションの機密情報を更新できます。