Kubernetes và Container Orchestration - Quản lý containers
Kubernetes là gì?
Định nghĩa
Kubernetes (K8s) là open-source container orchestration platform tự động hóa deployment, scaling, và management của containerized applications.
Tại sao cần Kubernetes?
- Service Discovery: Tự động phát hiện và load balance traffic
- Storage Orchestration: Mount storage systems automatically
- Automated Rollouts: Deploy changes mà không downtime
- Self-healing: Restart failed containers automatically
- Secret Management: Store và manage sensitive information
Core Concepts
- Pod: Đơn vị nhỏ nhất deployable trong K8s
- Service: Expose pods thông qua stable network endpoint
- Deployment: Quản lý replica sets và rolling updates
- ConfigMap: Store configuration data
- Secret: Store sensitive data như passwords, tokens
Java Application trên Kubernetes
Basic Deployment
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
labels:
app: user-service
version: v1.0.0
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
version: v1.0.0
spec:
containers:
- name: user-service
image: mycompany/user-service:1.0.0
ports:
- containerPort: 8080
name: http
env:
- name: SPRING_PROFILES_ACTIVE
value: "kubernetes"
- name: JAVA_OPTS
value: "-Xms512m -Xmx1g -XX:+UseContainerSupport"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
Service Configuration
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: user-service
labels:
app: user-service
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: user-service
---
# Ingress for external access
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: user-service-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- api.company.com
secretName: tls-secret
rules:
- host: api.company.com
http:
paths:
- path: /api/users
pathType: Prefix
backend:
service:
name: user-service
port:
number: 80
ConfigMap cho Application Configuration
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: user-service-config
data:
application.yml: |
server:
port: 8080
spring:
application:
name: user-service
datasource:
url: jdbc:mysql://mysql-service:3306/userdb
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: validate
show-sql: false
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
redis:
host: redis-service
port: 6379
logging:
level:
com.company: INFO
org.springframework: WARN
pattern:
console: "%d{ISO8601} [%thread] %-5level [%logger{36}] - %msg%n"
management:
endpoints:
web:
exposure:
include: health,metrics,prometheus,info
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
Secrets Management
# secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: user-service-secrets
type: Opaque
data:
# Base64 encoded values
DB_USERNAME: dXNlcg== # user
DB_PASSWORD: cGFzc3dvcmQ= # password
JWT_SECRET: bXktand0LXNlY3JldA== # my-jwt-secret
---
# Database Secret
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
type: Opaque
data:
MYSQL_ROOT_PASSWORD: cm9vdHBhc3N3b3Jk # rootpassword
MYSQL_DATABASE: dXNlcmRi # userdb
MYSQL_USER: dXNlcg== # user
MYSQL_PASSWORD: cGFzc3dvcmQ= # password
Updated Deployment với ConfigMap và Secrets
# deployment-complete.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
labels:
app: user-service
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/actuator/prometheus"
spec:
containers:
- name: user-service
image: mycompany/user-service:1.0.0
ports:
- containerPort: 8080
name: http
env:
- name: SPRING_PROFILES_ACTIVE
value: "kubernetes"
- name: JAVA_OPTS
value: "-Xms512m -Xmx1g -XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
envFrom:
- secretRef:
name: user-service-secrets
volumeMounts:
- name: config
mountPath: /config
readOnly: true
- name: logs
mountPath: /logs
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 20
periodSeconds: 10
failureThreshold: 30
volumes:
- name: config
configMap:
name: user-service-config
- name: logs
emptyDir: {}
imagePullSecrets:
- name: registry-secret
Database trên Kubernetes
MySQL StatefulSet
# mysql-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-service
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_ROOT_PASSWORD
- name: MYSQL_DATABASE
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_DATABASE
- name: MYSQL_USER
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_USER
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_PASSWORD
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
- name: mysql-config
mountPath: /etc/mysql/conf.d
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
exec:
command:
- mysqladmin
- ping
- -h
- localhost
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
command:
- mysql
- -h
- localhost
- -e
- "SELECT 1"
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 1
volumes:
- name: mysql-config
configMap:
name: mysql-config
volumeClaimTemplates:
- metadata:
name: mysql-storage
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "fast-ssd"
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
name: mysql
MySQL Configuration
# mysql-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
my.cnf: |
[mysqld]
# Basic Settings
user = mysql
default-storage-engine = innodb
socket = /var/lib/mysql/mysql.sock
pid-file = /var/lib/mysql/mysql.pid
# Connection Settings
max_connections = 200
max_connect_errors = 1000000
# Buffer Settings
innodb_buffer_pool_size = 512M
innodb_log_file_size = 64M
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
# Charset
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# Logging
slow_query_log = 1
slow_query_log_file = /var/lib/mysql/slow.log
long_query_time = 2
Monitoring trên Kubernetes
Prometheus Configuration
# prometheus-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
data:
prometheus.yml: |
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
- job_name: 'user-service'
static_configs:
- targets: ['user-service:80']
metrics_path: /actuator/prometheus
Grafana Dashboard ConfigMap
# grafana-dashboard.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: java-app-dashboard
data:
java-app.json: |
{
"dashboard": {
"id": null,
"title": "Java Application Metrics",
"tags": ["java", "spring-boot"],
"style": "dark",
"timezone": "browser",
"panels": [
{
"id": 1,
"title": "HTTP Requests Rate",
"type": "graph",
"targets": [
{
"expr": "rate(http_server_requests_seconds_count[5m])",
"legendFormat": "{{method}} {{uri}}"
}
]
},
{
"id": 2,
"title": "JVM Memory Usage",
"type": "graph",
"targets": [
{
"expr": "jvm_memory_used_bytes{area=\"heap\"}",
"legendFormat": "Heap Used"
},
{
"expr": "jvm_memory_max_bytes{area=\"heap\"}",
"legendFormat": "Heap Max"
}
]
}
],
"time": {
"from": "now-1h",
"to": "now"
},
"refresh": "5s"
}
}
Helm Charts cho Java Applications
Chart.yaml
# Chart.yaml
apiVersion: v2
name: user-service
description: A Helm chart for User Service Java Application
type: application
version: 0.1.0
appVersion: "1.0.0"
dependencies:
- name: mysql
version: 9.4.0
repository: https://charts.bitnami.com/bitnami
condition: mysql.enabled
- name: redis
version: 17.3.0
repository: https://charts.bitnami.com/bitnami
condition: redis.enabled
values.yaml
# values.yaml
replicaCount: 3
image:
repository: mycompany/user-service
pullPolicy: IfNotPresent
tag: "1.0.0"
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: ""
podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/actuator/prometheus"
podSecurityContext:
fsGroup: 1001
securityContext:
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1001
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: true
className: "nginx"
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
hosts:
- host: api.company.com
paths:
- path: /api/users
pathType: Prefix
tls:
- secretName: tls-secret
hosts:
- api.company.com
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 250m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
# Application configuration
config:
springProfiles: "kubernetes"
javaOpts: "-Xms512m -Xmx1g -XX:+UseContainerSupport"
# Database configuration
mysql:
enabled: true
auth:
rootPassword: "rootpassword"
database: "userdb"
username: "user"
password: "password"
redis:
enabled: true
auth:
enabled: false
Deployment Template
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "user-service.fullname" . }}
labels:
{{- include "user-service.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "user-service.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "user-service.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "user-service.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
- name: SPRING_PROFILES_ACTIVE
value: {{ .Values.config.springProfiles }}
- name: JAVA_OPTS
value: {{ .Values.config.javaOpts }}
{{- if .Values.mysql.enabled }}
- name: SPRING_DATASOURCE_URL
value: "jdbc:mysql://{{ include "user-service.fullname" . }}-mysql:3306/{{ .Values.mysql.auth.database }}"
- name: SPRING_DATASOURCE_USERNAME
value: {{ .Values.mysql.auth.username }}
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "user-service.fullname" . }}-mysql
key: mysql-password
{{- end }}
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: http
initialDelaySeconds: 60
periodSeconds: 30
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: http
initialDelaySeconds: 30
periodSeconds: 10
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
Kubernetes Commands
Basic Commands
# Apply configurations
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f configmap.yaml
# Get resources
kubectl get pods
kubectl get services
kubectl get deployments
kubectl get configmaps
# Describe resources
kubectl describe pod user-service-xxx
kubectl describe service user-service
# View logs
kubectl logs user-service-xxx
kubectl logs -f user-service-xxx # Follow logs
# Execute commands in pod
kubectl exec -it user-service-xxx -- /bin/bash
kubectl exec user-service-xxx -- curl localhost:8080/actuator/health
# Port forwarding
kubectl port-forward service/user-service 8080:80
# Scale deployment
kubectl scale deployment user-service --replicas=5
# Rolling update
kubectl set image deployment/user-service user-service=mycompany/user-service:1.1.0
# Rollback
kubectl rollout undo deployment/user-service
kubectl rollout history deployment/user-service
Helm Commands
# Install chart
helm install user-service ./user-service-chart
# Upgrade release
helm upgrade user-service ./user-service-chart
# List releases
helm list
# Uninstall release
helm uninstall user-service
# Template rendering
helm template user-service ./user-service-chart
# Lint chart
helm lint ./user-service-chart
Best Practices
1. Resource Management
- Set appropriate resource requests và limits
- Use HPA (Horizontal Pod Autoscaler) cho scaling
- Monitor resource usage
2. Health Checks
- Implement proper liveness, readiness, và startup probes
- Use Spring Boot Actuator endpoints
- Configure appropriate timeouts
3. Configuration Management
- Use ConfigMaps cho configuration
- Store secrets trong Kubernetes Secrets
- Separate configuration theo environments
4. Security
- Run containers với non-root users
- Use Pod Security Standards
- Implement network policies
- Regular vulnerability scanning
5. Monitoring và Logging
- Implement centralized logging
- Use Prometheus cho metrics
- Set up proper alerting
- Monitor cluster health
Kubernetes provides powerful orchestration capabilities cho Java applications, enabling scalable, resilient, và manageable containerized deployments.