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_password và api_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.