Skip to content

Tips

ここでは、Cloud Native Adapterを実装する際のTipsを記載します。

SecretとConfigMapの更新に追従したDeploymentリソースの定義

Infrastructure AdapterでSecret(External Secret)やConfigMapリソースを用いる際は、それらのリソースのデプロイと連動したリソース名の変更が行われるように、リソース名にハッシュ値を含める実装を推奨しています。

対象となる課題

PodやDeploymentでSecretやConfigMapを環境変数やボリュームとして利用する場合、それらのデータのみの変更に留まるデプロイを行っても、Podが保持する情報に変更が加わりません。

このような場合に当てはまる、Infrastructure Adapterの記述例が以下になります。External Secretリソースを作成して、Deploymentの環境変数としてSecretを指定しています。

go
package sample

#Secret: {
	key:     string
	version: string
}

DesignPattern: {
	name: "sample-app"

	parameters: sampleSecret: #Secret

    resources: app: {
        externalSecret: _externalSecret
        deployment: _deployment
	}

    _externalSecret: {
	    apiVersion: "external-secrets.io/v1beta1"
	    kind:       "ExternalSecret"
        metadata: { 
			name:      "sample-externalsecret" 
			namespace: "test-namespace"
		}
        spec: {
            refreshInterval: "0"
			secretStoreRef: {
				name: "gcp-secret-manager"
				kind: "ClusterSecretStore"
			}
			target: {
				name:           "sample-secret"
				creationPolicy: "Owner"
			}
			data: [{
				secretKey: "sample_secret_key"
				remoteRef: {
					key:     parameters.sampleSecret.key
					version: parameters.sampleSecret.version
				}
            }]
			}
        }

	_deployment: {
        apiVersion: "apps/v1"
        kind: "Deployment"
		metadata: {
			name:      "sample-app"
			namespace: "test-namespace"
		}
		spec: {
			replicas: 3
			selector: matchLabels: app: "nginx"
			template: {
				metadata: labels: app: "nginx"
				spec: containers: [{
						name:  "nginx"
						image: "nginx"
						envFrom: [{secretRef: name: "sample-secret"}] 
					}]
			}
		}
	}
}

この場合、パラメータとして注入する#Secretkeyversionの値を変えてデプロイしても、spec.template.spec.containers[].envFromからPodに渡される環境変数は更新されません。

原因

Kubernetesの特性上、リソース名を除いた Secret や ConfigMapリソースの変更は Deployment リソースには影響を与えず、Deployment のアップデートが起きない構造となっているためにこのような問題が発生します。

例えば、直接マニフェスト上でSecretのデータを変更してApplyしても次のように出力されます。

bash
kubectl apply -f output/manifests.yml

externalsecret.external-secrets.io/sample-externalsecret configured
deployment.apps/sample-app unchanged ## マニフェスト上で変更のないDeployment側に反映されない

解決方法

この問題を解決する方法の1つとして、SecretやConfigMapのデータの更新に対してそのSecretやConfigMap自体のリソース名が更新される、すなわちDeployment側が追従して変更が走るように、Infrastructure Adapterを定義する方法があります。 本来YAMLマニフェストベースでの管理においてこの問題を解決するには工夫が必要になりますが、Cloud Native AdapterはプログラマブルなCUE言語を用いている特性上、比較的容易にDeploymentを追従させるように構成できます。

解決方法の具体例として、対象となる課題の例で示したInfrastructure Adapterの記述の中で、 External SecretのデータのみをYAMLとして取得・ハッシュ化し、接尾辞としてExternal SecretとSecretのリソース名に付与することで、Secret のデータの更新に追従する形で新しい名前でSecretが作成されるようにします。

さらに、DeploymentとExternal Secretの依存関係を示すAnnotationを付与することで、Secretの更新を安全に行うことができます。詳しくは、dependsOnを確認してください。

以下に実装を抜粋します。

diff
// 実装に必要なパッケージを追加する
+ import (
+	"crypto/sha256"
+	"encoding/hex"
+ 	"encoding/yaml"
+ 	"strings"
+ )
...

DesignPattern: {
...

// External Secret の spec.data 部分だけyaml化する
+ let _esData = yaml.Marshal(_externalSecret.spec.data)

// Yaml化したデータをハッシュ化し、最初の10文字だけ取り出す
+ let _hash = strings.SliceRunes(hex.Encode(sha256.Sum256(_esData)), 0, 10)
...

// ハッシュ化したデータを,External Secretリソース名およびExternal Secretで指定しているSecretリソース名の接尾辞として付与する
_externalSecret: {
[...]
metadata: { 
-	name:      "sample-externalsecret" 
+	name:      "sample-externalsecret-\(_hash)" 
[...]
target: {
-			 name:           "sample-secret"
+			 name:           "sample-secret-\(_hash)"

// External Secretとの依存関係を明記するannotationを追加し、Deploymentで指定しているSecretリソース名にも接尾辞を付与する
_deployment: {
[...]
metadata: { 
	name:      "sample-app"
	namespace: "test-namespace"
+	annotations: "vs.axis-dev.io/dependsOn": "external-secrets.io:ExternalSecret::test-namespace/sample-externalsecret-\(_hash)"
}
[...]
- envFrom: [{secretRef: name: "sample-secret"}]
+ envFrom: [{secretRef: name: "sample-secret-\(_hash)"}]

このように実装することで、Secretの更新に対して、常にDeploymentが追従してPodの環境変数が更新されるようになります。