Kubernetes ConfigMap ve Secret Nedir?

Kubernetes ConfigMap ve Secret, uygulamalarınızın yapılandırma bilgilerini ve hassas verilerini güvenli bir şekilde saklamak ve uygulamalarınıza iletmek için kullanılan Kubernetes nesneleridir.

ConfigMap, uygulama yapılandırma bilgilerini depolamak ve paylaşmak için kullanılırken, Secret'ler hassas verileri (parolalar, API anahtarları, sertifikalar vb.) korumak için kullanılır. Bu yazıda, Kubernetes ConfigMap ve Secret kavramlarını daha ayrıntılı olarak ele alacak ve nasıl kullanıldıklarını anlatacağız.

1. ConfigMap Nedir?

ConfigMap, bir uygulamanın yapılandırma bilgilerini key-value çifti şeklinde depolamak için kullanılan bir Kubernetes kaynağıdır. ConfigMap'ler genellikle metin tabanlı verileri içerir. Uygulama pod'larına bu yapılandırma bilgilerini enjekte edebilirsiniz.

ConfigMap içinde oluşturduğunuz key-value configler, bir Pod'a environment variable veya özel bir volume olarak tanımlanabilir. Her birini detaylıca inceleyeceğiz.

Bir uygulamanın bir ConfigMap'i nasıl kullandığından bağımsız olarak, yapılandırmaların ayrı bir bağımsız nesnede olması, Pod manifestinizi değiştirmeden her biri farklı bir ortam için (development, test, QA, production vb.) aynı ada sahip ConfigMaps kullanmanıza olanak tanır. Pod'lar, ConfigMap'e adıyla başvurdukları için, hepsinde aynı Pod manifestini kullanırken her ortamda farklı bir yapılandırma kullanabilirsiniz.

1.1. ConfigMap Nasıl Oluşturulur?

Kubernetes'te ConfigMap oluşturmak için hem YAML hem de kubectl komutunu kullanabilirsiniz. Her iki yönteme de bakalım.

1.1.1 YAML Manifest Dosyasıyla ConfigMap Oluşturmak

YAML manifest dosyasıyla ConfigMap oluşturmak için aşağıdaki örneği kullanabilirsiniz.

apiVersion: v1
kind: ConfigMap
metadata:
  name: my-configmap
data:
  key1: value1
  key-2: value2
  counter: "5"
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5
binaryData:
  key3: L3Jvb3QvMTAw 
  • data parametresi alanında tüm key-value config çiftlerinizi oluşturabilirsiniz. key1 ve key2 property-like key'lerdir.
  • | işareti ile multi-line configler oluşturabilirsiniz. Bu config tipine file-like key'ler denir.
  • Eğer değerleriniz UTF-8 olmayan karakterler içeriyorsa binaryData parametresi alanında değerlerinizi base64 formatında ekleyebilirsiniz.
💡
String değerleri direkt olarak yazabilirken, numeric değerleri yazarken " içine almanız gerekir.

Bu YAML dosyasını kullanarak ConfigMap'i oluşturmak için aşağıdaki komutu kullanabilirsiniz:

kubectl apply -f configmap.yaml

1.1.2 Kubectl Komutuyla ConfigMap Oluşturmak

Imperative methodla kubectl komutunu kullanarak ConfigMap oluştururken birçok senaryomuz mevcut. Tüm bu seçeneklere tek tek bakalım.

  • --from-literal argümanını kullanarak key-value config oluşturabiliriz. Tek bir komutta birden fazla config oluşturabilirsiniz.
kubectl create configmap my-configmap --from-literal=key1=value1 --from-literal=blog-name="Kerteriz Blog" --from-literal=blog-schema="https://"
  • --from-file argümanını kullanarak bir dosya içeriğinden config oluşturabilirsiniz. Eğer key belirtmezseniz, key dosya adı olarak belirlenir.
kubectl create configmap my-configmap --from-file=config-file.conf --from-file=file-config=config-file2.txt
  • --from-file argümanını kullanarak sadece dosya değil bir dizinde belirtebilirsiniz. Böylece o dizin altında yer alan tüm dosyalar için configler oluşturulmuş olur.
kubectl create configmap my-configmap --from-file=/var/config-dir/

Son olarak tüm bu yöntemleri tek bir komut içerisinde aynı anda kullanabilirsiniz. Örneğin aşağıdaki komut tüm yöntemleri aynı anda kullanmanıza olanak sağlar.

kubectl create configmap my-config
➥ --from-file=foo.json
➥ --from-file=bar=foobar.conf
➥ --from-file=config-opts/
➥ --from-literal=some=thing

Bu komutu aşağıdaki gibi bir şemaya dökebiliriz.

1.2. ConfigMap Nasıl Kullanılır?

Kubernetes ConfigMap'leri kullanarak bir Pod'un yapılandırma bilgilerini belirtmek için birkaç farklı yöntem vardır. Tüm bu methodları tek tek inceleyelim

1.2.1 Environment Variable Olarak Kullanmak

ConfigMap configlerini kullanmanın en basit yolu, onları environment variable olarak container'lara geçmektir. Pod konfigürasyonunda env bölümünü kullanarak valueFrom parametresi ile nasıl environment variable tanımlayabileceğimizi aşağıdaki örnekte görebilirsiniz.

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: my-container
      image: my-image
      env:
        - name: KEY1
          valueFrom:
            configMapKeyRef:
              name: my-configmap
              key: key1
        - name: KEY2
          valueFrom:
            configMapKeyRef:
              name: my-configmap
              key: key2

configMapKeyRef parametresindeki name tanımı ile hangi ConfigMap nesnesindeki hangi key'i kullanacağımızı belirtiyoruz.

💡
Eğer kullanmaya çalıştığınız key ConfigMap içinde mevcut değilse ilgili container hata verir ve başlamaz. Fakat Pod içindeki diğer containerlar çalışmaya devam eder. Key mevcut olmadığında bile container'ın hata vermeden başlamasını istiyorsanız configMapKeyRef altına optional: true eklemelisiniz.

Eğer ConfigMap içinde çok fazla config mevcutsa bu şekilde tek tek eklemek zor olacaktır. Bunu çözmek için ConfigMap içindeki tüm configleri envFrom ile tek seferde environment olarak ayarlayabilirsiniz.

spec:
  containers:
    - name: my-container
      image: my-image
      envFrom:
        - prefix: CONFIG_
          configMapKeyRef:
            name: my-configmap              
💡
prefix parametresi opsiyoneldir. Kullandığınızda environment variable eklerken tüm configlerin başına prefix'teki değeri ekler. 

Eğer ConfigMap içindeki configlerde geçersiz bir environment variable key varsa, örneğin my-key, bu geçersiz keyler eklenmez ve atlanır. Bu örnekte, environment variable'lar - karakterini kabul etmediği için eklenmez.

Ayrıca environment variable olarak geçtiğiniz configleri Pod args parametresinde de kullanabilirsiniz.

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: my-container
      image: my-image
      env:
        - name: KEY1
          valueFrom:
            configMapKeyRef:
              name: my-configmap
              key: key1
      args: ["${KEY1}"]

Aşağıdaki şemada akışı görebilirsiniz.

🚧
Container çalışırken environment variable'ları güncelleyemezsiniz. Bu nedenle değişkenlik gösterecek configleriniz varsa config volume methodunu kullanmanızı tavsiye ederiz.

1.2.2 Config Dosyası Olarak Volume İçinde Kullanmak

Configleri environment variables olarak Pod'lara dahil etmek genelde kısa configler için mantıklıdır. Fakat uzun bir değere sahip (application.properties vb.) configleriniz olduğunda environment variables yerine configMap kullanmak daha doğru bir yaklaşımdır.

configMap , ConfigMap içindeki herbir config'i bir dosya olarak belirtilen dizine kaydeder. Örneğin my-nginx-config.conf ve sleep-interval isimli dosyalarda config değerleri saklayan configmap-files isimli bir config dizinimiz mevcut olsun.

my-nginx-config.conf isimli dosya içerği ise basit bir nginx.conf yapılandırması tutsun.

server {
  listen 80;
  server_name www.kubia-example.com;
  gzip on;
  gzip_types text/plain application/xml;
  location / {
    root /usr/share/nginx/html;
    index index.html index.htm;
  }
}

Bu dizindeki dosyalardan kubectl create configmap fortune-config --from-file=configmap-files komutu ile kolaylıkla bir ConfigMap oluşturabiliriz.

Ardından bir Pod ile bu configleri kullanabilmek için göreceğiniz üzere environment variable kullanmak mantıklı değildir. Çünkü dosya içeriğimiz çok uzun. Bu nedenle bir configMap tipinde Config Volume oluşturarak tüm bu configleri ilgili dizine mount edebiliriz.

apiVersion: v1
kind: Pod
metadata:
  name: fortune-configmap-volume
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: config
      mountPath: /etc/nginx/conf.d
      readOnly: true
  volumes:
  - name: config
    configMap:
      name: fortune-config

Artık Pod oluşturulduğunda fortune-config isimli ConfigMap içindeki tüm configler birer dosya olarak /etc/nginx/conf.d dizinine mount edilecektir.

Mount ettiğimiz dizinde ls /etc/nginx/conf.d komutunu çalıştırırsak ConfigMap içinde yer alan iki configi de görebiliriz.

$ ls /etc/nginx/conf.d
my-nginx-config.conf
sleep-interval

Fakat ConfigMap içinde mount ettiğiniz dizine koymak istemeyeceğiniz configler olabilir. Mesela yukarıdaki örnekte sleep-interval isimli config dosyasının nginx için hiçbir önemi yoktur. Bunun önüne geçebilmek için sadece eklemek istediğiniz configleri aşağıdaki gibi seçebilirsiniz.

 volumes:
 - name: config
   configMap:
     name: fortune-config
     items:
     - key: my-nginx-config.conf
       path: nginx.conf

Böylece ConfigMap içinde yer alan sadece my-nginx-config.conf isimli dosya nginx.conf dosya ismiyle mount edilen dizine eklenecektir.

🛑
Bir volume'u bir dizine mount ettiğinizde, eğer mount ettiğiniz dizin boş değilse, dizinde bulunan dosyalar erişilemez hale gelir.

Yukarıdaki uyarıdaki davranış Volume'lerin normal davranışıdır. Mount ettiğiniz dizin doluysa, bu dosyalar artık erişilimez hale gelir ve sadece Volume'un içindeki dosyalar gözükür.

Kubernetes ile bunu engellemenin bir yolu mevcuttur. Mount ettiğiniz dizindeki dosyaları erişilimez hale getirmeden subPath parametresi ile sadece kendi config dosyalarınızı ekleyebilirsiniz.

spec:
  containers:
  - image: some/image
    volumeMounts:
    - name: myvolume
      mountPath: /etc/someconfig.conf
      subPath: myconfig.conf

Bu örnekte, mount edilen dizindeki dosyalar hem erişilebilir halde kalacak, hemde ConfigMap içindeki configinizi ek bir dosya olarak ilgili dizine ekleyebileceksiniz.

Ayrıca config dosyalarınızı mount ederken isterseniz dosya izinlerini de ayarlayabilirsiniz.

volumes:
 - name: config
   configMap:
     name: my-configmap
     defaultMode: "6600"

Son olarak tüm volume'u mount ettikten sonra, kubectl edit configmap komutuyla bir ConfigMap içindeki config değerini güncellediğinizde, mount ettiğiniz dizindeki config dosyasının da içeriği değişir.

Fakat tüm volume yerine subPath ile sadece belirli dosyaları mount ederseniz, ConfigMap değerlerini güncelleseniz bile mount ettiğiniz dizindeki config dosyaları güncellenmeyecektir.

💡
ConfigMap içinde güncellediğiniz değerler her ne kadar mount ettiğiniz dizindeki dosyalara yansısa da, uygulamanızın bu değişiklikleri farkediyor olması gerekir. Aksi halde değişiklikleri uygulamada göremezsiniz. Örneğin nginx.conf içeriğini değiştirdikten sonra nginx uygulamasının bu conf değişikliğini algılaması için nginx -s reload komutunu çalıştırmalısınız.

2. Secret Nedir?

Secret, hassas bilgilerin (örneğin, parolalar, API anahtarları, sertifikalar vb.) depolanması ve yönetimi için kullanılan bir Kubernetes nesnesidir. Secret'lar ConfigMap'lerle benzer şekilde çalışır, ancak veriler şifrelenir ve daha güvenli bir şekilde saklanır. Secret'lar da YAML formatında tanımlanır ve genellikle Base64 kodlaması kullanılarak veriler depolanır. Uygulama pod'larına Secret'ları enjekte ederek, bu hassas bilgilere güvenli bir şekilde erişebilirsiniz.

💡
Secret'lar, daima bellekte depolanır ve asla fiziksel diske yazılmaz. 

ConfigMap ile Secret arasındaki önemli farklardan birisi Secret'ların bazı tiplere sahip olmasıdır. Bu tiplere şimdi bakalım.

Secret oluştururken type değeri aşağıdaki değerlerden birini alabilir:

  1. Opaque: Opaque (generic), genel amaçlı bir Secret tipidir ve varsayılan değeridir. Bu tip, herhangi bir metin veya binary veriyi şifrelemeksizin saklamak için kullanılır. Örneğin, parolalar, sertifikalar, özel anahtarlar gibi hassas bilgiler bu Secret tipiyle saklanabilir.
  2. kubernetes.io/basic-auth: basic-auth, kullanıcı adı ve parolayı saklamak için kullanılan bir Secret tipidir. Bu tip, bir kullanıcının kimlik doğrulama bilgilerini depolamak için kullanılabilir.
  3. kubernetes.io/ssh-auth: ssh-auth, SSH kimlik doğrulama bilgilerini saklamak için kullanılan bir Secret tipidir. Bu tip, SSH sunucusuna erişim sağlamak için kullanılan kullanıcı adı ve private key bilgilerini içerebilir.
  4. kubernetes.io/tls: tls, TLS sertifikalarını ve private key'i saklamak için kullanılan bir Secret tipidir. Bu tip, HTTPS trafiğini şifrelemek veya uygulamalar arasında güvenli iletişim sağlamak için kullanılan sertifikaları içerebilir.
  5. kubernetes.io/service-account-token: service-account-token, Kubernetes Service hesabına ait kimlik bilgilerini içeren bir Secret tipidir. Bu Secret tipi, Kubernetes tarafından otomatik olarak oluşturulur ve Service hesaplarına ilişkin JWT (JSON Web Token) ve diğer yetkilendirme bilgilerini içerir.
  6. kubernetes.io/dockercfg: dockercfg, Docker Registry'lerine kimlik doğrulama yapmak için kullanılan bir Secret tipidir. Bu tip, Docker imajlarını private registry'den çekmek için gereken kimlik bilgilerini içerebilir.
  7. kubernetes.io/dockerconfigjson: dockerconfigjson, Docker imajını private registry'den çekmek için kullanılan bir Secret tipidir. Bu tip, Docker Hub veya başka bir özel Docker Registry'sindeki kimlik doğrulama bilgilerini içerir.

Bu type değerleri, Secret'lerin kullanım amaçlarına göre farklılaşmasını sağlar ve Kubernetes tarafından doğru şekilde yorumlanmalarını sağlar. Secret oluştururken doğru type değerini belirtmek, Secret'in amacına uygun olarak kullanılmasını sağlar.

2.1. Secret Nasıl Oluşturulur?

Kubernetes'te Secret oluşturmak için hem YAML hem de kubectl komutunu kullanabilirsiniz. Her iki yönteme de bakalım.

2.1.1 YAML Manifest Dosyasıyla Secret Oluşturmak

YAML manifest dosyasıyla Secret oluşturmak için aşağıdaki örneği kullanabilirsiniz.

apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  username: dXNlcm5hbWU=
  password: cGFzc3dvcmQ=
stringData:
  blog: Kerteriz Blog
  • data parametresi alanında tüm key-value config çiftlerinizi oluşturabilirsiniz. Config değerleri Base64 ile encode edilerek eklenmelidir. Çünkü eklemek istediğiniz değerler binary data içeriyor olabilir
  • stringData parametresi altında ise Base64 yerine plain text değerlerinizi ekleyebilirsiniz.
🚧
Her bir Secret için 1MB dosya limiti vardır. Bu nedenle ekleyeceğiniz değerlerin uzunluğunu da hesaba katmalısınız.

Bu YAML dosyasını kullanarak Secret'ı oluşturmak için aşağıdaki komutu kullanabilirsiniz:

kubectl apply -f secret.yaml

Diğer Secret tipleri için kullanabileceğiniz manifest örneklerini https://kubernetes.io/docs/concepts/configuration/secret/#secret-types adresinden inceleyebilirsiniz.

2.1.2 Kubectl Komutuyla Secret Oluşturmak

Imperative methodla kubectl komutunu kullanarak Secret oluştururken birçok seçeneğimiz mevcut. Tüm bu seçeneklere tek tek bakalım.

  • --from-literal argümanını kullanarak key-value config oluşturabiliriz. Tek bir komutta birden fazla config oluşturabilirsiniz.
kubectl create secret generic my-secret --from-literal=user=poweruser --from-literal=password='f0ob@r'
💡
Opaque tipi kubectl komutu ile kullanırken generic adını alır.
  • --from-file argümanını kullanarak bir dosya içeriğinden config oluşturabilirsiniz. Eğer key belirtmezseniz, key dosya adı olarak belirlenir.
kubectl create configmap my-configmap --from-file=config-file.conf --from-file=file-config=config-file2.txt
  • --from-file argümanını kullanarak sadece dosya değil bir dizinde belirtebilirsiniz. Böylece o dizin altında yer alan tüm dosyalar için configler oluşturulmuş olur.
kubectl create secret generic my-secret --from-file=/var/secret-config-dir/

Son olarak tüm bu yöntemleri tek bir komut içerisinde aynı anda kullanabilirsiniz. Örneğin aşağıdaki komut tüm yöntemleri aynı anda kullanmanıza olanak sağlar.

kubectl create secret generic my-secret
➥ --from-file=foo.json
➥ --from-file=bar=foobar.conf
➥ --from-file=config-opts/
➥ --from-literal=some=thing

2.2. Secret Nasıl Kullanılır?

Kubernetes Secret'ları kullanarak bir Pod'un yapılandırma bilgilerini belirtmek için birkaç farklı yöntem vardır. Tüm bu methodları tek tek inceleyelim.

💡
Secret configleri secret volume aracılığıyla bir pod'a çıkardığınızda, Secret kaydının base64 değeri decode edilir ve gerçek değeri yazılır. Aynı şey, bir environment variable aracılığıyla expose ederken de geçerlidir. Her iki durumda da uygulamanın base64 decode etmesi gerekmez.

2.2.1 Environment Variable Olarak Kullanmak

Secret configlerini kullanmanın en basit yolu, onları environment variable olarak container'lara geçmektir. Pod konfigürasyonunda env bölümünü kullanarak valueFrom parametresi ile nasıl environment variable tanımlayabileceğimizi aşağıdaki örnekte görebilirsiniz.

apiVersion: v1
kind: Pod
metadata:
  name: my-web
spec:
  containers:
  - image: nginx
    name: web
    env:
    - name: password
      valueFrom:
        secretKeyRef:
          name: my-secret
          key: password

2.2.2 Config Dosyası Olarak Volume İçinde Kullanmak

Configleri environment variables yerine secret kullanarak bir volume içine de çıkarabilirsiniz.

apiVersion: v1
kind: Pod
metadata:
  name: my-web
spec:
  containers:
  - image: nginx:alpine
    name: web-server
    volumeMounts:
    - name: certs
      mountPath: /etc/nginx/certs/
      readOnly: true
  volumes:
  - name: certs
    secret:
      secretName: my-secret

2.3. Docker Hub Private Repository için Secret Kullanımı

Bu konu ile çok kez karşılaşacağınız için özel bir başlık oluşturduk. Eğer Docker Hub üzerinde yer alan private bir image repository (registry) üzerinden image pull etmeniz gerekirse öncelikle imagePullSecrets ile gerekli authentication işlemini ayarlamanız gerekir.

Docker registry için Secret tiplerindeki özel docker-registry tipini kullanacağız. Öncelikle bu tipi kullanarak credentials bilgilerimizi içeren bir secret oluşturalım.

kubectl create secret docker-registry mydockerhubsecret \
 --docker-username=myusername --docker-password=mypassword \
 --docker-email=my.email@provider.com

generic tipte bir secret oluşturmak yerine, mydockerhubsecret adında bir docker-registry tipinde secret oluşturduk ve Docker Hub kullanıcı adını, şifreni ve e-postasını belirttik. Yeni oluşturulan Secret'ın içeriğini kubectl define ile incelerseniz, .dockercfg adında tek bir girdi içerdiğini görürsünüz. Bu, ana dizininizdeki docker login komutunu çalıştırdığınızda Docker tarafından oluşturulan .dockercfg dosyasına eşdeğerdir.

Artık private repository'den pull edeceğiniz pod'u kullanacağınız manifest dosyasında bu secret'ı imagePullSecrets değişkeni ile ekleyebilirsiniz:

apiVersion: v1
kind: Pod
metadata:
  name: private-pod
spec:
  imagePullSecrets:
  - name: mydockerhubsecret
  containers:
  - image: username/private:tag
    name: main

Sıradaki yazı ile eğitim serisine devam edebilirsiniz.

Kubernetes Downward API ve Metadata
Kubernetes Downward API, bir Pod’un metadata bilgilerini container içine inject eden mekanizmadır.