ConfigMaps & Secrets - Configuration Management

Tổng quan

ConfigMaps và Secrets quản lý configuration data và sensitive information trong Kubernetes.

ConfigMaps

Creating ConfigMaps

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  database_url: "mongodb://mongo:27017"
  debug_mode: "true"
  app_name: "my-application"

Using ConfigMaps

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    image: myapp:latest
    env:
    - name: DATABASE_URL
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: database_url

Accessing ConfigMaps in Java

Bạn có thể truy cập các giá trị từ ConfigMap trong ứng dụng Java thông qua biến môi trường hoặc mount chúng dưới dạng file.

1. Truy cập qua biến môi trường:

public class ConfigMapEnvExample {
    public static void main(String[] args) {
        String databaseUrl = System.getenv("DATABASE_URL");
        String debugMode = System.getenv("DEBUG_MODE");
        String appName = System.getenv("APP_NAME");

        System.out.println("Database URL: " + databaseUrl);
        System.out.println("Debug Mode: " + debugMode);
        System.out.println("App Name: " + appName);
    }
}

2. Truy cập qua file mount: Nếu ConfigMap được mount vào /etc/config/:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class ConfigMapFileExample {
    public static void main(String[] args) {
        try {
            String databaseUrl = new String(Files.readAllBytes(Paths.get("/etc/config/database_url")));
            String debugMode = new String(Files.readAllBytes(Paths.get("/etc/config/debug_mode")));
            String appName = new String(Files.readAllBytes(Paths.get("/etc/config/app_name")));

            System.out.println("Database URL: " + databaseUrl.trim());
            System.out.println("Debug Mode: " + debugMode.trim());
            System.out.println("App Name: " + appName.trim());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Secrets

Creating Secrets

apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  password: cGFzc3dvcmQxMjM=  # base64 encoded
  api_key: YWJjZGVmZ2hpams=

Secret Types

  • Opaque
  • kubernetes.io/service-account-token
  • kubernetes.io/dockercfg
  • kubernetes.io/tls

Accessing Secrets in Java

Secrets cũng có thể được truy cập tương tự như ConfigMaps, thông qua biến môi trường hoặc file mount.

1. Truy cập qua biến môi trường:

public class SecretEnvExample {
    public static void main(String[] args) {
        String password = System.getenv("DB_PASSWORD");
        String apiKey = System.getenv("API_KEY");

        System.out.println("Database Password: " + password);
        System.out.println("API Key: " + apiKey);
    }
}

2. Truy cập qua file mount: Nếu Secret được mount vào /etc/secrets/:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class SecretFileExample {
    public static void main(String[] args) {
        try {
            String password = new String(Files.readAllBytes(Paths.get("/etc/secrets/password")));
            String apiKey = new String(Files.readAllBytes(Paths.get("/etc/secrets/api_key")));

            System.out.println("Database Password: " + password.trim());
            System.out.println("API Key: " + apiKey.trim());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Volume Mounts

Mount ConfigMaps và Secrets as files.

Best Practices

  • Never hardcode sensitive data
  • Use Secrets cho passwords, tokens
  • Use ConfigMaps cho non-sensitive config
  • Rotate secrets regularly
  • Limit secret access với RBAC

Security Considerations

  • Secrets stored in etcd (encrypted at rest)
  • Secrets transmitted over TLS
  • Pods can only access secrets trong same namespace

External Secret Management

Để quản lý secrets an toàn hơn và tích hợp với các hệ thống quản lý secrets bên ngoài (như HashiCorp Vault, AWS Secrets Manager, Azure Key Vault), bạn có thể sử dụng External Secrets Operator.

HashiCorp Vault Integration

1. Cài đặt External Secrets Operator: Theo hướng dẫn trên GitHub của External Secrets Operator.

2. Cấu hình SecretStore cho Vault:

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: default
spec:
  provider:
    vault:
      server: "http://vault.example.com:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "my-app-role"

3. Tạo ExternalSecret:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-external-secret
  namespace: default
spec:
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: my-app-secret
    creationPolicy: Owner
  data:
    - secretKey: db_password
      remoteRef:
        key: my-app/config
        property: db_password
    - secretKey: api_key
      remoteRef:
        key: my-app/config
        property: api_key

External Secrets Operator sẽ tự động tạo một Kubernetes Secret có tên my-app-secret với các khóa db_passwordapi_key được lấy từ Vault. Sau đó, ứng dụng Java của bạn có thể truy cập my-app-secret như một Secret thông thường.

Advanced Patterns

Dynamic Configuration Reload

Trong một số trường hợp, bạn muốn ứng dụng tự động tải lại cấu hình khi ConfigMap hoặc Secret thay đổi mà không cần khởi động lại Pod. Bạn có thể implement logic này trong ứng dụng Java bằng cách theo dõi các file mount.

import java.io.IOException;
import java.nio.file.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class DynamicConfigReloader {

    private static final String CONFIG_PATH = "/etc/config"; // Path where ConfigMap is mounted
    private static final Logger logger = Logger.getLogger(DynamicConfigReloader.class.getName());

    public void startWatching() {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(this::checkConfigChanges, 0, 5, TimeUnit.SECONDS);
    }

    private void checkConfigChanges() {
        try {
            Path configDir = Paths.get(CONFIG_PATH);
            if (Files.exists(configDir)) {
                Files.list(configDir).forEach(file -> {
                    try {
                        long lastModified = Files.getLastModifiedTime(file).toMillis();
                        // Compare with previous last modified time
                        // If changed, reload specific config and notify application
                        logger.info("Config file " + file.getFileName() + " last modified: " + lastModified);
                        // Example: Reload a specific property
                        // String newDatabaseUrl = new String(Files.readAllBytes(file));
                        // if (file.getFileName().toString().equals("database_url")) {
                        //     ApplicationConfig.setDatabaseUrl(newDatabaseUrl.trim());
                        //     logger.info("Database URL reloaded.");
                        // }
                    } catch (IOException e) {
                        logger.log(Level.SEVERE, "Error reading config file: " + file.getFileName(), e);
                    }
                });
            }
        } catch (IOException e) {
            logger.log(Level.SEVERE, "Error listing config directory: " + CONFIG_PATH, e);
        }
    }

    public static void main(String[] args) {
        new DynamicConfigReloader().startWatching();
        // Keep application running
        try { Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException e) { /* ignore */ }
    }
}

Nội dung đã được mở rộng với external secret integration và advanced patterns, cùng các ví dụ Java.