Docker & DevOps

    Kubernetes ConfigMap ve Secret Kullanımı

    Yapılandırmayı imajdan ayırmanın ve sırları Kubernetes'te doğru saklamanın pratik rehberi.

    10 dk okuma Güncellendi: 25 Ağustos 2026

    Aynı uygulamayı test ve canlı ortamda çalıştırmak istiyorsun. Veritabanı adresi farklı, log seviyesi farklı, API anahtarı farklı. Bu değerleri imajın içine gömersen her ortam için ayrı imaj derlemen gerekir ve "test ettiğin imaj ile canlıya çıkan imaj aynı" güvencesini kaybedersin. Kubernetes ConfigMap ve Secret nesneleri tam olarak bu ayrımı yapmak için var: tek imaj, ortama göre değişen yapılandırma.

    Bu rehberde ConfigMap ve Secret'ı oluşturmanın farklı yollarını, Pod'a ortam değişkeni ya da dosya olarak bağlamanın hangi durumda tercih edileceğini, değişiklik yaptığında neden bazen uygulamanın yeniden başlatılması gerektiğini ve en önemlisi Secret'ın gerçekte ne kadar "gizli" olduğunu anlatacağım. Son bölümde de üretimde sır yönetimini ciddiye almak istediğinde atacağın adımları özetleyeceğim. Temel nesneleri henüz oturtmadıysan Kubernetes Pod, Deployment ve Service yazısı iyi bir ön okuma.

    ConfigMap: Yapılandırmayı İmajdan Ayırmak#

    ConfigMap, anahtar-değer çiftlerinden oluşan basit bir nesnedir. Değerler kısa metinler (LOG_LEVEL=info) olabileceği gibi tam bir yapılandırma dosyasının içeriği de (nginx.conf) olabilir. Toplam boyut sınırı yaklaşık 1 MiB'dır; bu, bir yapılandırma dosyası için fazlasıyla yeterli ama bir veri deposu için değildir — ConfigMap'i asla dosya deposu gibi kullanma.

    Oluşturmanın üç yolu var:

    # 1. Tek tek değerlerle
    kubectl create configmap api-config \
      --from-literal=LOG_LEVEL=info \
      --from-literal=TZ=Europe/Istanbul \
      --from-literal=MAX_UPLOAD_MB=32
    
    # 2. Bir dosyanın içeriğinden (anahtar = dosya adı olur)
    kubectl create configmap nginx-config --from-file=nginx.conf
    
    # 3. Bir dizindeki tüm dosyalardan
    kubectl create configmap site-config --from-file=./conf.d/
    

    Üretimde ise nesneyi YAML olarak yazıp sürüm kontrolünde tutmak doğru yaklaşımdır:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: api-config
    data:
      LOG_LEVEL: "info"
      TZ: "Europe/Istanbul"
      MAX_UPLOAD_MB: "32"
      # Çok satırlı bir dosya içeriği:
      uygulama.properties: |
        onbellek.sure=300
        kuyruk.isci.sayisi=4
        ozellik.yeni-panel=acik
    

    Buradaki | işareti YAML'da çok satırlı blok başlatır ve girintiyi koruyarak dosya içeriğini olduğu gibi saklar. Bir uyarı: ConfigMap'teki bütün değerler dize olmak zorundadır. MAX_UPLOAD_MB: 32 yazarsan (tırnaksız) API sunucusu tip hatası verir; sayıları ve true/false değerlerini mutlaka tırnak içine al.

    Pod'a Bağlamanın Üç Yolu#

    Aynı ConfigMap'i üç farklı şekilde Pod'a verebilirsin ve seçimin sonuçları vardır.

    spec:
      containers:
        - name: api
          image: firmaniz/api:1.4
    
          # 1. Tek tek seçerek ortam değişkeni
          env:
            - name: LOG_LEVEL
              valueFrom:
                configMapKeyRef:
                  name: api-config
                  key: LOG_LEVEL
    
          # 2. Tamamını ortam değişkeni olarak içe aktar
          envFrom:
            - configMapRef:
                name: api-config
    
          # 3. Dosya olarak bağla
          volumeMounts:
            - name: config-vol
              mountPath: /etc/uygulama
              readOnly: true
    
      volumes:
        - name: config-vol
          configMap:
            name: api-config
    

    Üçüncü yöntemde her anahtar, bağlanan dizinde ayrı bir dosya olarak görünür: /etc/uygulama/LOG_LEVEL, /etc/uygulama/uygulama.properties gibi.

    YöntemDeğişiklik canlı yansır mıNe zaman tercih edilir
    env + configMapKeyRefHayır, yeniden başlatma gerekirAz sayıda, seçili değer
    envFromHayır, yeniden başlatma gerekirTüm anahtarlar ortam değişkeni olacaksa
    Volume bağlamaEvet, kısa gecikmeyleYapılandırma dosyası okuyan uygulamalar
    Volume + subPathHayırTek dosyayı mevcut dizine koymak

    Son satır çok kişiyi yakalar: subPath ile tek bir dosyayı /etc/nginx/nginx.conf gibi mevcut bir dizinin içine yerleştirebilirsin, ama bu durumda dosya otomatik güncellenmez. Kubernetes yalnızca tam bir dizini bağladığında sembolik bağ mekanizması sayesinde canlı güncelleme yapabilir.

    Bir ConfigMap'in bir daha değişmeyeceğini biliyorsan immutable: true alanını ekle: hem kazara değişiklikleri engeller hem de kubelet'in sürekli izleme yükünü kaldırdığı için büyük kümelerde belirgin performans kazancı sağlar.

    Secret: Neyi Gerçekten Gizler, Neyi Gizlemez#

    Secret nesnesi, ConfigMap ile neredeyse birebir aynı arayüze sahiptir; farkı değerlerin base64 ile kodlanmış olarak saklanmasıdır. Buradaki en kritik yanlış anlamayı hemen kapatalım: base64 şifreleme değildir, kodlamadır. Herkes tek komutla çözebilir:

    kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d
    # ornek-guclu-parola
    

    Yani Secret'ın sağladığı şey, değerin kubectl get -o yaml çıktısında ve loglarda düz metin olarak görünmemesidir. Gerçek koruma iki yerden gelir: etcd'de bekleyen verinin şifrelenmesi ve RBAC ile kimin okuyabileceğinin kısıtlanması. Bu ikisi yapılandırılmadıysa Secret, ConfigMap'ten yalnızca kozmetik olarak farklıdır.

    Secret oluştururken data yerine stringData kullanmak hayatı kolaylaştırır; base64'e çevirme işini API sunucusu yapar:

    apiVersion: v1
    kind: Secret
    metadata:
      name: db-secret
    type: Opaque
    stringData:
      DB_USER: uygulama
      DB_PASSWORD: ornek-guclu-parola
      DB_URL: postgres://uygulama@postgres:5432/uretim
    

    Komut satırından oluşturmak istersen:

    kubectl create secret generic db-secret \
      --from-literal=DB_USER=uygulama \
      --from-literal=DB_PASSWORD='ornek-guclu-parola'
    

    Güçlü ve rastgele parolalar üretmek için şifre üretici aracımızı kullanabilirsin; kabuk geçmişine parola yazmamak içinse değeri bir dosyadan okutmak (--from-file=DB_PASSWORD=./parola.txt) daha temiz bir yöntemdir.

    Secret Tipleri ve Özel Kullanımlar#

    Secret'ın type alanı yalnızca etiket değildir; bazı tipler Kubernetes tarafından özel olarak işlenir.

    TipNe içinNasıl oluşturulur
    OpaqueGenel amaçlı anahtar-değerkubectl create secret generic
    kubernetes.io/dockerconfigjsonÖzel registry kimlik bilgisikubectl create secret docker-registry
    kubernetes.io/tlsTLS sertifikası ve özel anahtarkubectl create secret tls
    kubernetes.io/basic-authKullanıcı adı ve parola çiftistringData ile elle

    Özel bir registry'den imaj çekiyorsan dockerconfigjson tipini kullanır ve Pod'a imagePullSecrets ile bağlarsın:

    kubectl create secret docker-registry regcred \
      --docker-server=registry.firmaniz.com \
      --docker-username=ci-kullanici \
      --docker-password='ornek-token'
    
    spec:
      imagePullSecrets:
        - name: regcred
      containers:
        - name: api
          image: registry.firmaniz.com/firmaniz/api:1.4
    

    Bu yapılandırma yoksa Pod ImagePullBackOff durumunda takılır ve kubectl describe pod çıktısında yetkilendirme hatası görürsün. Docker Hub'dan çekerken oran sınırına takılıyorsan sebep farklı olabilir; Docker Hub pull limiti hatası yazısı o durumu ayrıca ele alıyor.

    TLS sertifikalarını da Secret olarak saklarsın; Ingress kaynağı tls.secretName alanıyla bunu okur:

    kubectl create secret tls firmaniz-com-tls \
      --cert=firmaniz.com.crt --key=firmaniz.com.key
    

    Değişiklik Yaptım, Uygulama Neden Görmüyor#

    Bu, en sık sorulan sorulardan biri ve cevabı bağlama yöntemine bağlı. Ortam değişkeni olarak verilen değerler konteyner başlarken okunur; ConfigMap'i güncellemen çalışan bir sürecin ortamını değiştirmez. Bu Linux'un temel davranışıdır, Kubernetes'in eksiği değil.

    Volume olarak bağlanan dosyalar ise kubelet'in eşitleme döngüsü içinde (genelde bir dakikayı aşmayan bir gecikmeyle) güncellenir. Ancak uygulamanın bu değişikliği fark etmesi için dosyayı yeniden okuması gerekir; çoğu uygulama yapılandırmayı yalnızca açılışta okur.

    Pratikte kullanılan üç çözüm var:

    # 1. Deployment'ı yeniden başlat (en yaygın ve en basit yöntem)
    kubectl rollout restart deployment/api
    kubectl rollout status deployment/api
    
    # 2. Uygulama destekliyorsa yapılandırmayı yeniden yükleme sinyali gönder
    kubectl exec deploy/api -- kill -HUP 1
    

    Üçüncü ve en sağlam yöntem, Pod şablonuna ConfigMap içeriğinin özetini (checksum) bir açıklama olarak eklemektir. İçerik değiştiğinde özet değişir, Pod şablonu değişmiş sayılır ve Deployment kendiliğinden kademeli yeniden başlatma yapar. Helm şablonlarında bu desen standarttır; nasıl kurulduğunu Helm ile uygulama kurulumu yazısında bulabilirsin.

    Bir başka sık tuzak: silinen bir anahtar ortam değişkeni olarak bağlıysa Pod hiç başlamaz. configMapKeyRef ile referans verdiğin anahtarı ConfigMap'ten kaldırırsan, yeni Pod CreateContainerConfigError durumunda takılır. optional: true alanı bu durumu tolere etmeni sağlar.

    Üretimde Sır Yönetimini Ciddiye Almak#

    Küçük bir kümede stringData ile yazılmış Secret'lar iş görür. Ama şu üç soruyu sorduğun anda daha fazlası gerekir: Bu YAML dosyasını Git'e koyabilir miyim? Kim bu Secret'ı okuyabilir? Sızdığında nasıl fark ederim?

    Git sorunu. Düz metin Secret YAML'ını depoya koymak, parolayı depoya koymakla aynı şeydir; üstelik geçmişten silmek çok zordur. Bunun ne kadar pahalı bir hata olduğunu .git ve .env dosyası ifşası yazısında anlattım. Çözüm olarak yaygın üç yaklaşım var:

    1. Sealed Secrets — Secret'ı kümenin genel anahtarıyla şifrelersin; şifreli hâli Git'e güvenle konur, yalnızca hedef küme çözebilir.
    2. Harici sır yöneticisi — Vault ya da bulut sağlayıcısının sır servisi asıl kaynaktır; bir operatör bunları kümeye Secret olarak yansıtır.
    3. SOPS — Dosyaları dağıtım anında çözen bir şifreleme katmanı; küçük ekiplerde kurulumu en hafif seçenek.

    Erişim sorunu. Varsayılan RBAC ayarlarında, bir ad alanında Pod oluşturabilen herkes o ad alanındaki bütün Secret'ları okuyabilir — çünkü Pod'una istediği Secret'ı bağlayabilir. Bu yüzden ortamları ad alanlarıyla ayırmak ve rolleri gerçekten daraltmak, Secret'ı base64'le saklamaktan çok daha etkili bir korumadır.

    Bekleyen veri sorunu. Secret'lar etcd'de saklanır ve varsayılan yapılandırmada şifrelenmez. Kontrol düzlemi sunucularına erişebilen biri etcd yedeğinden bütün sırları okuyabilir. Yönetilen bir küme kullanmıyorsan API sunucusunda bekleyen veri şifrelemesini açmak ve etcd yedeklerini şifreli tutmak, atlanmaması gereken iki adımdır. Yedeklerin şifreli ve düzenli olması ayrıca bir dayanıklılık meselesidir; yedekleme hizmetimiz bu tarafta işi kolaylaştırır.

    Sık Yapılan Hatalar ve Tuzaklar#

    1. Base64'ü şifreleme sanmak. data alanındaki değerler yalnızca kodlanmıştır. YAML dosyasını paylaşmak, parolayı paylaşmakla aynıdır.
    2. data alanına düz metin yazmak. data base64 bekler; düz metin yazarsan API sunucusu kabul etmez ya da uygulama anlamsız bir değer okur. Düz metin için stringData kullan.
    3. Sayı ve boolean değerleri tırnaksız yazmak. ConfigMap data alanı yalnızca dize kabul eder; replicas: 3 gibi bir satır kaynağın reddedilmesine yol açar.
    4. Ortam değişkeninin canlı güncelleneceğini varsaymak. ConfigMap'i güncelleyip "neden değişmedi" diye saatlerce aramak yaygındır. Ortam değişkenleri için mutlaka yeniden başlatma gerekir.
    5. subPath ile bağlayıp otomatik güncelleme beklemek. subPath kullanıldığında dosya güncellenmez; tam dizin bağlaması gerekir.
    6. Sırları log'a yazdırmak. Uygulama açılışta ortam değişkenlerini loglayan bir "debug" satırı içeriyorsa, sırlar log toplama sistemine ve yedeklere kopyalanır. Konteyner loglarının nereye gittiğini Docker log driver yapılandırması yazısındaki mantıkla düşün.
    7. Tüm ortamlar için tek ad alanı kullanmak. Test ve canlı sırları aynı ad alanındaysa, test için verdiğin erişim canlı sırları da kapsar.

    Sıkça Sorulan Sorular#

    ConfigMap ile Secret arasındaki fark nedir#

    Arayüzleri neredeyse aynıdır; ikisi de anahtar-değer saklar ve Pod'a ortam değişkeni ya da dosya olarak bağlanır. Fark, Secret değerlerinin base64 ile kodlanmış saklanması, kubectl çıktılarında düz metin görünmemesi ve Kubernetes'in bazı Secret tiplerini (registry kimlik bilgisi, TLS sertifikası) özel olarak işlemesidir. Gerçek güvenlik farkı ise RBAC ve etcd şifrelemesiyle sağlanır.

    Kubernetes Secret gerçekten güvenli mi#

    Kutudan çıktığı hâliyle Secret, gizlemekten çok "kazara ifşayı önleme" aracıdır; base64 kodlaması hiçbir koruma sağlamaz. Güvenli hâle getirmek için üç şey gerekir: etcd'de bekleyen veri şifrelemesini açmak, RBAC ile Secret okuma yetkisini gerçekten daraltmak ve mümkünse harici bir sır yöneticisini asıl kaynak olarak kullanmak. Bu üçü yapılmadan Secret, ConfigMap'ten anlamlı biçimde güvenli değildir.

    ConfigMap değişikliği Pod'a otomatik yansır mı#

    Volume olarak bağlanmış ConfigMap dosyaları kısa bir gecikmeyle güncellenir, ancak subPath ile bağlanan tek dosyalar güncellenmez. Ortam değişkeni olarak verilen değerler ise hiçbir zaman canlı güncellenmez; konteyner başlarken bir kez okunur. Ayrıca uygulamanın dosyayı yeniden okuyor olması gerekir; çoğu uygulama yapılandırmayı yalnızca açılışta okuduğu için pratikte kubectl rollout restart en güvenilir yöntemdir.

    Secret değerini nasıl okurum#

    kubectl get secret adi -o jsonpath='{.data.anahtar}' | base64 -d komutu değeri çözerek gösterir. Bu komutu çalıştırabiliyor olman, aynı zamanda kümede o Secret'ı okuma yetkisine sahip olduğunu gösterir; üretim ortamında bu yetkinin kimlerde olduğunu gözden geçirmek iyi bir alışkanlıktır. Değeri terminal geçmişine düşürmemek için çıktıyı ekrana basmak yerine doğrudan kullanacağın komuta yönlendirmeyi tercih et.

    Secret'ı Git deposunda tutabilir miyim#

    Düz hâliyle tutmamalısın; bu, parolayı depoya yazmakla aynıdır ve geçmişten temizlemek çok zordur. Sürüm kontrolünde tutmak istiyorsan Sealed Secrets ya da SOPS gibi bir şifreleme katmanı kullan; bu araçlarla depoya yalnızca şifreli hâli girer ve çözme yetkisi yalnızca hedef kümede olur. Alternatif olarak sırları Vault gibi harici bir sistemde tutup kümeye bir operatörle yansıtabilirsin.

    Aynı ConfigMap'i birden fazla ad alanında kullanabilir miyim#

    Hayır, ConfigMap ve Secret ad alanına özeldir; başka bir ad alanındaki Pod bunlara doğrudan erişemez. Aynı yapılandırmayı birden fazla ad alanında kullanman gerekiyorsa nesneyi her ad alanında ayrı ayrı oluşturursun. Bu tekrarı yönetmek için Helm şablonları ya da bir eşitleme operatörü kullanmak yaygın çözümlerdir.

    Kapanış#

    ConfigMap ve Secret, "tek imaj her ortamda çalışsın" ilkesini uygulanabilir kılan iki basit nesne. Aklında kalması gereken dört şey: değerleri her zaman dize olarak yaz, ortam değişkeni olarak bağladığın hiçbir şeyin canlı güncellenmeyeceğini bil, base64'ün şifreleme olmadığını asla unutma ve Secret'ları Git'e koymadan önce mutlaka bir şifreleme katmanı ekle. Bir yapılandırma değişikliği sonrası uygulamanın davranmadığını görürsen ilk refleksin kubectl rollout restart olsun.

    Kubernetes altyapını kurarken ya da işletirken destek istersen Clou.TR tarafında birkaç seçenek var. Küme düğümleri için bulut sunucu ve VDS paketlerimizi inceleyebilir, kurulum ve bakımı bize devretmek istersen sunucu yönetimi hizmetimize göz atabilir, etcd ve uygulama verisi için düzenli kopyalar almak üzere yedekleme çözümümüzü değerlendirebilirsin.

    KubernetesConfigMapSecret

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.