Ansible + Kubernetes: Deploy App ไป K8s Cluster ด้วย Ansible

kubernetes.core collection (เดิมชื่อ community.kubernetes) เป็น official collection สำหรับ manage Kubernetes resources จาก Ansible — deploy Deployments, Services, ConfigMaps และ namespaces ด้วย playbooks แทนการรัน kubectl ด้วยตนเอง

บทความนี้อธิบายการติดตั้ง kubernetes.core collection, เชื่อมต่อ cluster ผ่าน kubeconfig, deploy applications ด้วย k8s module, manage Helm charts ผ่าน helm module และ pattern สำหรับ multi-environment Kubernetes deployments

ติดตั้งและ Prerequisites

kubernetes.core collection ต้องการ Python packages สำหรับ connect กับ Kubernetes API server — ติดตั้งทั้งบน control node และตรวจสอบ cluster access

# ติดตั้ง kubernetes.core collection
ansible-galaxy collection install kubernetes.core:2.4.0

# ติดตั้ง Python dependencies บน control node
pip install kubernetes

# requirements.yml
---
collections:
  - name: kubernetes.core
    version: "2.4.0"

# ตรวจสอบ connection ก่อนเริ่ม
- name: Get cluster info
  kubernetes.core.k8s_info:
    kind: Node
  register: nodes

- name: Show node count
  ansible.builtin.debug:
    msg: "Cluster has {{ nodes.resources | length }} nodes"

k8s Module — Deploy Resources

kubernetes.core.k8s คือ module หลักสำหรับ manage Kubernetes resources — ใช้ definition inline หรืออ่านจากไฟล์ YAML รองรับทุก resource type (Deployment, Service, ConfigMap, Secret, ฯลฯ)

---
- name: Deploy application to Kubernetes
  hosts: localhost
  gather_facts: false

  vars:
    namespace: myapp
    app_version: "1.2.0"
    replicas: 3

  tasks:
    # สร้าง namespace
    - name: Create namespace
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: v1
          kind: Namespace
          metadata:
            name: "{{ namespace }}"

    # สร้าง ConfigMap
    - name: Create app config
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: v1
          kind: ConfigMap
          metadata:
            name: myapp-config
            namespace: "{{ namespace }}"
          data:
            APP_ENV: production
            LOG_LEVEL: info
            MAX_CONNECTIONS: "100"

    # Deploy application
    - name: Deploy application
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: myapp
            namespace: "{{ namespace }}"
          spec:
            replicas: "{{ replicas }}"
            selector:
              matchLabels:
                app: myapp
            template:
              metadata:
                labels:
                  app: myapp
                  version: "{{ app_version }}"
              spec:
                containers:
                  - name: myapp
                    image: "myregistry.example.com/myapp:{{ app_version }}"
                    ports:
                      - containerPort: 8000
                    envFrom:
                      - configMapRef:
                          name: myapp-config
                    resources:
                      requests:
                        memory: "256Mi"
                        cpu: "250m"
                      limits:
                        memory: "512Mi"
                        cpu: "500m"

    # สร้าง Service
    - name: Create service
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: v1
          kind: Service
          metadata:
            name: myapp-svc
            namespace: "{{ namespace }}"
          spec:
            selector:
              app: myapp
            ports:
              - port: 80
                targetPort: 8000
            type: ClusterIP

Apply จากไฟล์ YAML

แทนที่จะเขียน resource definition ใน playbook โดยตรง สามารถอ่านจากไฟล์ YAML หรือ folder — เหมาะสำหรับ project ที่มี manifests อยู่แล้ว

# Apply จากไฟล์ YAML เดี่ยว
- name: Apply deployment manifest
  kubernetes.core.k8s:
    state: present
    src: /path/to/deployment.yaml

# Apply ทุก manifests ใน folder
- name: Apply all manifests in directory
  kubernetes.core.k8s:
    state: present
    src: "{{ item }}"
  with_fileglob:
    - /opt/k8s-manifests/*.yaml

# Apply พร้อม template (Jinja2) ก่อน apply
- name: Template and apply manifest
  kubernetes.core.k8s:
    state: present
    template: templates/deployment.yaml.j2

# ลบ resource
- name: Remove old deployment
  kubernetes.core.k8s:
    state: absent
    kind: Deployment
    name: myapp-old
    namespace: "{{ namespace }}"

k8s_info — ดึงข้อมูล Resources

k8s_info module ใช้ query resources จาก cluster — เทียบเท่า kubectl get แต่ return Python dict ที่ใช้ใน playbook ต่อได้

# ดึง Pods ทั้งหมดใน namespace
- name: Get all pods
  kubernetes.core.k8s_info:
    kind: Pod
    namespace: myapp
  register: pods

- name: Show pod names
  ansible.builtin.debug:
    msg: "{{ pods.resources | map(attribute='metadata.name') | list }}"

# ดึงเฉพาะ pod ที่ label ตรงกัน
- name: Get app pods
  kubernetes.core.k8s_info:
    kind: Pod
    namespace: myapp
    label_selectors:
      - app=myapp
  register: app_pods

# รอ deployment พร้อมใช้งาน
- name: Wait for deployment to be ready
  kubernetes.core.k8s_info:
    kind: Deployment
    name: myapp
    namespace: myapp
    wait: true
    wait_condition:
      type: Available
      status: "True"
    wait_timeout: 120

helm Module — จัดการ Helm Charts

kubernetes.core.helm module manage Helm releases — install, upgrade, rollback และ uninstall charts ด้วย idempotency เทียบเท่า helm install/upgrade –install

# ติดตั้ง Python dependency สำหรับ helm module
pip install pyhelm3

# Install Nginx Ingress Controller
- name: Add Nginx Ingress repo
  kubernetes.core.helm_repository:
    name: ingress-nginx
    repo_url: https://kubernetes.github.io/ingress-nginx

- name: Install Nginx Ingress
  kubernetes.core.helm:
    name: ingress-nginx
    chart_ref: ingress-nginx/ingress-nginx
    chart_version: "4.9.0"
    release_namespace: ingress-nginx
    create_namespace: true
    values:
      controller:
        replicaCount: 2
        service:
          type: LoadBalancer

# Install/Upgrade application chart
- name: Deploy application via Helm
  kubernetes.core.helm:
    name: myapp
    chart_ref: ./charts/myapp
    release_namespace: "{{ namespace }}"
    create_namespace: true
    values:
      image:
        tag: "{{ app_version }}"
      replicas: "{{ replicas }}"
      env:
        DATABASE_URL: "{{ vault_db_url }}"
    wait: true
    wait_timeout: 120

# Uninstall chart
- name: Remove application
  kubernetes.core.helm:
    name: myapp
    release_namespace: "{{ namespace }}"
    state: absent

Multi-Environment Deployment Pattern

ใช้ Ansible inventory variables แยก config ต่าง environment (dev, staging, production) ด้วย playbook เดียวกัน

# inventory/group_vars/production/vars.yml
k8s_context: production-cluster
namespace: myapp-prod
replicas: 3
app_version: "1.2.0"
resources:
  requests:
    memory: "512Mi"
    cpu: "500m"
  limits:
    memory: "1Gi"
    cpu: "1000m"

# inventory/group_vars/staging/vars.yml
k8s_context: staging-cluster
namespace: myapp-staging
replicas: 1
app_version: "1.3.0-rc1"
resources:
  requests:
    memory: "128Mi"
    cpu: "100m"
  limits:
    memory: "256Mi"
    cpu: "200m"

# deploy.yml — playbook เดียวใช้ได้กับทุก environment
---
- name: Deploy application
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Set Kubernetes context
      ansible.builtin.command:
        cmd: "kubectl config use-context {{ k8s_context }}"

    - name: Deploy application
      kubernetes.core.k8s:
        state: present
        template: templates/deployment.yaml.j2

    - name: Wait for rollout
      kubernetes.core.k8s_rollout_status:
        kind: Deployment
        name: myapp
        namespace: "{{ namespace }}"

# รัน production: ansible-playbook deploy.yml -l production
# รัน staging: ansible-playbook deploy.yml -l staging

Rolling Update และ Rollback

Update image ของ Deployment และรอให้ rollout เสร็จสมบูรณ์ พร้อม rollback กลับ version เดิมถ้าเกิด error

---
- name: Rolling update Kubernetes deployment
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Update image version
      kubernetes.core.k8s:
        state: present
        definition:
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: myapp
            namespace: "{{ namespace }}"
          spec:
            template:
              spec:
                containers:
                  - name: myapp
                    image: "myregistry.example.com/myapp:{{ new_version }}"
        merge_type: strategic-merge

    - name: Wait for rollout to complete
      kubernetes.core.k8s_rollout_status:
        kind: Deployment
        name: myapp
        namespace: "{{ namespace }}"
        timeout: 300
      register: rollout_result

    - name: Rollback if rollout failed
      kubernetes.core.k8s_rollout:
        kind: Deployment
        name: myapp
        namespace: "{{ namespace }}"
        action: undo
      when: rollout_result.failed is defined and rollout_result.failed

สรุป

kubernetes.core collection ทำให้ Kubernetes resource management เป็นส่วนหนึ่งของ Ansible automation pipeline — ใช้ k8s module สำหรับ apply manifests, k8s_info สำหรับ query cluster state, helm module สำหรับ manage charts และ k8s_rollout_status สำหรับรอให้ deployment พร้อม

ข้อได้เปรียบหลักคือสามารถ orchestrate Kubernetes deployment ร่วมกับงาน infrastructure อื่น × เช่น provision VPS, configure DNS, update database schema ได้ในหนึ่ง playbook เดียว แทนที่จะสลับระหว่าง kubectl และ script อื่น ×