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?

  1. Service Discovery: Tự động phát hiện và load balance traffic
  2. Storage Orchestration: Mount storage systems automatically
  3. Automated Rollouts: Deploy changes mà không downtime
  4. Self-healing: Restart failed containers automatically
  5. 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.