Appearance
Infrastructure Adapter Specification
アプリケーションを動かすクラウドアーキテクチャを構成する「インフラストラクチャ構成」を定義するInfrastructure Adapterの仕様について以下に示します。
以下の定義のように、Infrastructure Adapterは、大きく分けて、初期化時に利用するparameters、出力するResource Manifest(クラウド、およびアプリケーションの設定ファイル)を宣言するresources、および組み合わせるInfrastructure Adapterのリストcompositesから構成されます。
Qmonus Value Streamは、このInfrastructure Adapterを解決し、Resource Manifestを生成します。この処理をコンパイル、と呼びます。
cue
#DesignPattern: {
name: string
description: string | *""
// input parameters
parameters: {[string]: _}
// enumerates design pattern
composites?: [...#CompositeDef]
// whether to group resources after composite
group: string | *""
// resource declaration
resources?: #ResourceOutput
// lazy evaluation
defer?: {
#ResourceOutput
}
}Infrastructure Adapterの基本要素
nameおよびdescriptionフィールドは、Infrastructure Adapterについての説明を記述します。現在、これらのフィールドがコンパイル時に利用されることはありません。 groupフィールドは、出力するResource Manifestをグループ化する際に宣言します。本フィールドはDeprecatedであり、後述するcomposites内で指定するgroupフィールドを利用ください。
ここで、nameは必須フィールドです。
例
cue
name: "pattern01"
description: "a sample infrastructure adapter"
group: "group01"Parameters
parametersフィールドは、Infrastructure Adapterの初期化時に、外から受け取るパラメータのリストを宣言します。コンパイル時に渡されたパラメータの値は、ここで宣言したparametersにバインドされます。 具体的には、QVS ConfigでInfrastructure Adapterを指定した際に一緒に宣言したパラメータparamsが、ここで宣言したparametersにバインドされます。また、後述するComposite処理においても、Infrastructure Adapterとそれに渡すパラメータを宣言でき、QVS Configと同様に、ここで宣言したparametersにバインドします。
ここで、parametersは必須フィールドです。
例
string型のパラメータであるnameおよびnamespace、int型のパラメータであるreplicas、#Secret型のパラメータであるapiKeyを受け取ります。
cue
parameters: {
name: string
namespace: string
replicas: int
apiKey: #Secret
}#Secret
parametersフィールドでは一般的な型に加えて、独自に#Secret型を定義することで、CLIによりSecretオブジェクトがBindingされるフィールドを宣言できます。 #Secret型は以下のようなkeyとversionのフィールドを含む型である必要があります。
cue
#Secret: {
key: string
version: string
}#Secretの各フィールドはそれぞれ以下の内容を意味しています。
| フィールド名 | 説明 |
|---|---|
| key | 機密情報の管理サービスにおいて登録されている機密情報の名前 |
| version | 機密情報の管理サービスにおいて登録されている機密情報のバージョン |
Resources
resourcesフィールドは、Infrastructure Adapterが出力するResource Manifestについて、リソースIDをkeyとしたObjectの形で宣言します。 以下の#ResourceBaseに示すように、Resource Manifestは、プロバイダ定義、APIスキーマおよびメタデータの固定フィールドを持ちます。さらに、#ResourceSpecに示すように、APIスキーマごとの固有なフィールドを宣言したものがResource Manifestであり、#ResourceOutputで示すように、appまたはappSetupをkeyにまとめた形で、resourcesフィールドに定義します。
providerフィールドは、kubernetesやgcpといった、プロバイダ定義を宣言します。apiVersionおよびkindフィールドは、リソースのAPIスキーマを宣言します。metadataフィールドは、リソースのメタ情報を宣言します。ここで、provider、apiVersion、kind、metadata.nameは必須フィールドです(outputは現在利用されません)。
appおよびappSetupは、それぞれリソースをデプロイするStageを宣言します。app stageは、アプリケーションを実際にインストール・アップデートするstageであるのに対し、appSetupは事前作業としてアプリケーションが動作するために必要な周辺のリソースをデプロイするstageを表します。
cue
// Infrastructure Adapterの抜粋
#DesignPattern: {
// resource declaration
resources?: #ResourceOutput
...
}
#ResourceBase: {
// cloud provider
provider: string
// API schema
apiVersion: string
kind: string
// metadata
metadata: {
name: string
{[string]: _}
}
// output to save
output: [...string]
}
#ResourceSpec: {
#ResourceBase
// any resource specific configuration
{[string]: _}
}
#ResourceOutput: {
// application setup stage
appSetup?: {[string]: #ResourceSpec}
// application deployment stage
app?: {[string]: #ResourceSpec}
}例
appSetup stageでServiceとIngressを、app stageでDeploymentリソースのManifestを出力します。
cue
resources: {
app: deployment: {
provider: "kubernetes"
apiVersion: "apps/v1"
kind: "Deployment"
metadata: name: "app01"
spec: ...
}
appSetup: service: {
provider: "kubernetes"
apiVersion: "v1"
kind: "Service"
metadata: name: "service01"
spec: ...
}
appSetup: ingress: {
provider: "kubernetes"
apiVersion: "networking.k8s.io/v1beta1"
kind: "Ingress"
metadata: name: "ingress01"
spec: ...
}
}Composite
compositesフィールドは、他のInfrastructure Adapterを組み合わせて新しいInfrastructure Adapterを定義するために、組み合わせるInfrastructure Adapterとそれに渡すパラメータを宣言します。Infrastructure Adapterのコンパイル時に、ここで宣言されたInfrastructure Adapterを読み出し、各Infrastructure Adapterが出力するResource Manifestを解決します。その後、解決したManifestを、resources フィールドに全て結合します。この一連の処理をComposite処理と呼びます。
以下の#CompositeDefで示すように、compositesフィールドは、組み合わせたInfrastructure Adapterについてのルールをリストで宣言します。具体的には、CompositeするInfrastructure Adapterを指定するpattern、バインドするパラメータを指定するparams、およびComposite時に指定したInfrastructure Adapterが出力するResource Manifestのグループ化を指定するgroupを宣言します。
ここで、patternおよびparamsは必須フィールドです。またgroupフィールドを指定することで、Composite処理で解決したリソース定義をグループ化し、resourcesフィールドに結合する際に、他のリソースと結合しないよう、分離できます。
cue
// Infrastructure Adapterの抜粋
#DesignPattern: {
// resource declaration
composites?: [...#CompositeDef]
...
}
#CompositeDef: {
// design pattern to composite
pattern: #DesignPattern
// parameters to bind on composite
params: {[string]: _}
// group composited resource to isolate from others
group?: string
}例
app Infrastructure AdapterをCompositeしたリソース定義に対して、独自のannotationをさらに追加します。
cue
import (
app "module-to-load"
)
composites: [
{
pattern: app.DesignPattern
params: {
name: "app01"
namespace: "test-namespace"
replicas: 3
}
}
]
resources: {
app: deployment: {
metadata: annotations: "my-original": "label"
}
}Defer
deferフィールドは、Infrastructure Adapterのコンパイル処理のなかで最後に評価するデータを宣言します。Composite処理後に解決したResource Manifestに追加のデータを加える、などといった遅延評価を実現できます。
例
app Infrastructure AdapterをCompositeしたリソース定義全てに対して、独自のannotationを追加します。
cue
import (
app "module-to-load"
)
composites: [
{
pattern: app.DesignPattern
params: {
name: "app01"
namespace: "test-namespace"
replicas: 3
}
}
]
resources: {}
defer: {
app: {
for key, res in resources.app {
"\(key)": metadata: annotations: "my-original": "label"
}
}
}Annotation付与によるデプロイ動作制御
Infrastructure Adapterを実装する際、特定のAnnotationを付与することで、リソースのデプロイ挙動を制御できます。以下の三種類のAnnotationが設定できます。
vs.axis-dev.io/dependsOn:リソース間の依存関係を指定できます。vs.axis-dev.io/ignore-changes:リソースのパラメータを更新しないように制御できます。vs.axis-dev.io/keepOnDelete: リソースを削除しないよう制御できます。
dependsOn
dependsOnによるリソース間の依存関係を示すことで、依存先のリソースから先に作成・更新されるようになります。逆に削除のときは依存元のリソースが消えてから、依存先のリソースが削除されます。 依存先のリソースを更新するケースとして、依存元リソース名をsource、依存先の旧リソース名をtarget-old、新リソース名をtarget-newとすると、
target-newが作成されるsourceが更新されるtarget-oldが削除される
という順序でデプロイが行われます。デプロイ失敗時には、後続の更新や削除が行われません。
Annotationとして指定する際の命名規則は以下のようになります。
"vs.axis-dev.io/dependsOn": ${apiGroup}:${kind}::${namespace}/${name}
${apiGroup}は、apiVersionの中でversionを除いたもの(例:apiVersion: networking.k8s.io/v1の場合、networking.k8s.io)を指定します。version以外の指定がない場合、coreを指定します (例:apiVersion:v1のみの場合、coreになります)。${kind}は、manifestのkindをそのまま指定します。${namespace}が指定されていない場合、${name}のみ指定します。 (/を除きます)
例
sample-appDeployment にsample-externalsecret-\(_hash)External Secret への依存関係を持たせることで、External Secretの更新とDeploymentの更新順序を制御する例を挙げます。
cue
_deployment: {
apiVersion: "apps/v1"
kind: "Deployment"
metadata: {
name: "sample-app"
namespace: "test-namespace"
annotations: "vs.axis-dev.io/dependsOn": "external-secrets.io:ExternalSecret::test-namespace/sample-externalsecret-\(_hash)"
}
[...]
}External Secretのリソース名は、そのSpecによって決定するハッシュ値 _hash を含んでいると仮定した場合、External SecretのSpecを更新する際に、リソース名が変わります。Qmonus Value Streamは、デプロイ時に新しい名前でExternal Secretリソースを作成し、古いリソースは削除します。 上記のサンプルコードのように、DeploymentリソースからExternal Secretリソースへの依存関係を宣言することで、Qmonus Value Streamは、新しいExternal Secretリソースが作成された後にDeploymentリソースを更新するため、Deploymentリソースから新しいSecret情報を確実に参照できます。また、Deploymentリソースの更新が成功しない限り古いExternal Secretリソースを削除しないため、Deploymentリソースの更新失敗時には古いExternal Secretを参照したままの状態、すなわち失敗時の状態を保つことができます。
ignore-changes
ignore-changesにリソースのプロパティ名を指定することで、該当プロパティの値の変更を無視します。Qmonus Value Streamは、ignore-changesで指定されたプロパティについては、実際にリソースに適用されている値を尊重し、値を上書きすることはありません。本機能は、特定のプロパティをCI/CDのサイクルの外で変更し、その値を維持したい場合に有効です。 例外として、初回リソース作成時のみ、ignore-changesは効きません。
Annotationとして指定する際の命名規則は以下のようになります。
- ドット区切りで指定します。 (例:
spec: { selector: { app: x } }は、spec.selector.app) - リストの場合、[]で指定します。(例:
containers: [ { image: x }]は、containers[0].image) - カンマ "," 区切りで指定することで複数指定できます。
例
HorizontalPodAutoscalerがCI/CDの外で動作し、Deploymentのreplica数をコントロールしている中で、Qmonus Value Streamにsample-deployment Deployment のspec.replicasの値の更新を無視させます。
cue
_deployment: {
apiVersion: "apps/v1"
kind: "Deployment"
metadata: {
name: "sample-deployment"
annotations: "vs.axis-dev.io/ignore-changes": "spec.replicas"
}
spec: {
replicas: 2 # 初回以外の値の更新を無視する
[...]
}
}このようにすることで、Horizontal Pod Autoscalerを使って、以下のようにDeploymentのreplica数を動的に変更できます。
- Qmonus Value Streamを使って、Deploymentのreplica数を指定してデプロイします。
- Horizontal Pod Autoscalerを使って、CI/CDの外でDeploymentのreplica数を動的に変更します。
ignore-changesでreplicasプロパティを指定することで以降のデプロイ時にreplicasの変更を無視します。
keepOnDelete
keepOnDeleteのannotationを指定してデプロイされたリソースはデプロイ処理中の削除処理から保護されるようになります。また、annotationの値(以下、keepOnDeleteキー)には同じ目的でデプロイするリソースに同じ名前のキーを指定してください。そうすることでkeepOnDelete キーの単位でリソースをまとめてバージョン管理をし、将来は古いバージョンのリソースを自動で削除する機能を提供予定です。
本機能を利用することで、Blue/Green デプロイメントでConfigMap/Secretリソース等を使用するアプリケーションをデプロイする際に、Blue/Greenが切り替わる前のコンテナが参照している古いConfigMap/Secretを削除せずに維持できます。
ただし以下の注意点があります。
- ConfigMap/Secretリソースを適切にrevision管理するため下記例のようにリソース名がユニークなものになるように検討してください。
keepOnDelete設定をせずにデプロイされているリソースは本機能の対象にならないため、既にデプロイされているリソースを削除したくない場合は手動でkeepOnDeleteannotationをリソースに付与してください。暗黙的にrevisionの値を"0"が指定されているものとして扱います。(*1)- 手動でrevisionのannotationを追加・編集・削除をすることはしないでください。
(*1) CLI (kubectl)を利用して手動でannotationを付与する場合は以下のようなコマンドで実施できます。
e.g. sample-namespace にある sample-config-abcd ConfigMapリソースを削除処理から保護したい。keepOnDeleteキーはsample-configとする。
# kubectl annotate コマンドを利用します
kubectl -n sample-namespace annotate configmaps sample-config vs.axis-dev.io/keepOnDelete=sample-config例
ConfigMapリソースをapp-configというキーを持っているリソース群でrevision管理する場合の例を下記に記載します。また、下記の例ではConfigMapリソースが保持するデータが異なれば別リソースであるという考えを基に、dataフィールドの内容をhash化したものをリソース名のsuffixとして付与するという方法をとっています。
cue
import (
"crypto/sha256"
"encoding/hex"
"encoding/yaml"
"strings"
)
// ConfigMap の data 部分だけYaml化する
let _data = yaml.Marshal(_configMap.data)
// Yaml化したデータをハッシュ化し、最初の10文字だけ取り出す
let _hash = strings.SliceRunes(hex.Encode(sha256.Sum256(_data)), 0, 10)
_configMap: {
apiVersion: "apps/v1"
kind: "ConfigMap"
metadata: {
name: "sample-configmap-\(_hash)"
annotations: "vs.axis-dev.io/keepOnDelete": "app-config" // app-config キーでrevisionを管理
}
data: {
host: "example.co.jp"
log_level: "verbose"
}
}上記サンプルをコンパイルし生成されたConfigMapリソース名がsample-configmap-308a0d0c4cだとし、このリソースを環境にデプロイするとします。環境には既に以下のようなConfigMapリソースがデプロイ済みであった場合、
yaml
apiVersion: apps/v1
kind: ConfigMap
metadata:
name: sample-configmap-2ed2af4518
annotations:
vs.axis-dev.io/keepOnDelete: app-config
vs.axis-dev.io/revision: "1"
data:
host: example.co.jp
log_level: info最終的にデプロイされるリソースの状態は以下のようになります。
- 既にデプロイされていた
sample-configmap-2ed2af4518リソースは従来では削除されます。しかし上記のようにkeepOnDeleteを指定することで削除処理から保護されて残り続けます。 - 新規にデプロイされる
sample-configmap-308a0d0c4cリソースはrevisionが"2"として設定された状態でデプロイされます。