+3

Tự động cập nhật ứng dụng khi thay đổi Secret, Configmap trên Kubernetes với Reloader

Bài toán

image.png

Nếu anh em đã làm việc với Kubernetes thì chắc sẽ biết được 1 điều, đối với một số loại ứng dụng khi ta thay đổi cấu hình được lưu trữ trong Configmap hay Secret thì application sẽ không tự động cập nhật mà đòi hỏi chúng ta sẽ phải có hành động bằng tay thực hiện xóa pod đi tạo lại hay thực hiện Rollout restart thì application mới lấy lại được cấu hình mới. Đây mình nghĩ rằng là một tính năng để giảm thiểu sự thay đổi hạ tầng một cách vô ý, tuy nhiên việc này đôi khi lại gây sự bất tiện cho một số anh em DevOps ,SRE khi muốn ứng dụng sẽ tự động cập nhật cấu hình mới mà không cần phải thêm 1 bước Rollout trong CI/CD hay phải làm bằng tay.

Ngoại lệ có 1 số ứng dụng như NGINX có thể tự động nhận diện được sự thay đổi và load vào cấu hình mới mà không cần restart

Tự động cập nhật ứng dụng khi có thay đổi về cấu hình trong Configmap, Secret sẽ hữu ích trong trường hợp:

  • Trong các môi trường không yêu cầu độ ổn định cao (Dev, Staging)
  • Trong các môi trường thay đổi cấu hình thường xuyên.
  • Trong các môi trường mà Dev quản lý cấu hình ứng dụng, DevOps/SRE quản lý hạ tầng => Tự động reload sẽ giảm gánh nặng công việc tay chân cho DevOps/SRE.
  • Đối với các ứng dụng có đã cấu hình Readiness, Liveness,... chuẩn chỉ, không gặp downtime khi Rollout update.
  • ...

Khi có nhu cầu thì sẽ có giải pháp! Vô tình được ông anh chia sẻ cho một công cụ khá hay có thể tự động hóa việc cập nhật ứng dụng khi Configmap hay Secret thay đổi, thấy rằng công cụ khá hay nhưng chưa nhiều anh em biết nên lên ngay 1 bài chia sẻ cho anh em cho nóng 🤣🤣🤣

Pod LifeCycle

Công cụ Reloader

image.png

https://github.com/stakater/Reloader

Công cụ Reloader là một công cụ Open Source được phát triển bởi stakater.

Reloader là một Kubernetes Controller, nó sẽ liên tục giám sát sự thay đổi các Configmaps và Secrets được chỉ định. Nếu có sự thay đổi diễn ra, Controller sẽ thực hiện cập nhật lại Deployment/StatefulSet/Deamonset/Rollout đang sử dụng Configmap/Secret đó để ứng dụng chạy với cấu hình mới nhất.

Cài đặt Reloader

Vì là Kubernetes Controller, Reloader sẽ được cài đặt trên chính cụm Kubernetes mà bạn muốn áp dụng công cụ này. Reloader cài đặt khá dễ dàng, bạn có thể cài bằng cách apply trực tiếp file manifest

kubectl apply -f https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml

Hoặc ngoài ra bạn có thể cài đặt sử dụng Kustomize hay Helm Charts với nhiều cấu hình tùy chọn hơn.

Tham khảo thêm tại document chính thức của công cụ nhé :

https://github.com/stakater/Reloader?tab=readme-ov-file#deploying-to-kubernetes

Trong bài này để đơn giản mình sẽ cài đặt bằng cách apply trực tiếp file manifest:

image.png

Từ trên hình bạn có thể thấy có 1 Deployment trên reloader-reloader được tạo và cùng với đó là serviceAccount - ClusterRole - ClusterRoleBinding phục vụ cho việc cấp quyền cho công cụ trên toàn bộ Cluster.

Mặc định công cụ này sẽ giám sát sự thay đổi configmap và secret trên toàn bộ cluster nha. Chính vì thế nó cần tạo ClusterRole.

Thử nghiệm

Trong phần này mình cùng các bạn sẽ thử nghiệm liệu công cụ này sẽ hoạt động thế nào với một demo đơn giản.

Mình sẽ thử nghiệm thay đổi cấu hình của một ứng dụng web Python echo ra biến môi trường có tên RANDOM_ENV. Biến môi trường này sẽ được mount vào trong deployment thông qua 1 secret. Các bước chúng ta sẽ phải làm là:

1, Code python webserver (Flask) 2, Dockerize 3, Push Image lên Dockerhub 4, Tạo các resource k8s cần thiết (Secret, Deployment, Service) 5, Thử nghiệm thay đổi secret khi chưa có Reloader 6, Cài đặt Reloader và cấu hình reloader cho deployment 7, Thử nghiệm thay đổi secret khi đã cài đặt Reloader.

1, Code Python webserver

from flask import Flask
import os

app = Flask(__name__)

@app.route('/')
def echo_random_env():
    random_env_value = os.environ.get('RANDOM_ENV', 'Environment variable RANDOM_ENV is not set.')
    return f'The value of RANDOM_ENV is: {random_env_value}'

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')


Đoạn code trên sẽ chạy 1 web server và hiển thị ra giá trị của biến môi trường RANDOM_ENV được lưu trong pod. Bạn có thể dùng câu lệnh export RANDOM_ENV = "foo" rồi chạy webserver bằng python main.py sẽ thấy nội dung được hiển thị có dạng

image.png

2, Dockerize

Dockerfile dùng để dockerize ứng dụng này sẽ như sau:

# Use an official Python runtime as a parent image
FROM python:3.9-slim

# Set the working directory in the container
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY . /app

# Install any needed packages specified in requirements.txt
RUN pip install flask

# Make port 5000 available to the world outside this container
EXPOSE 5000

# Run app.py when the container launches
CMD ["python", "main.py"]

3, Build và push image lên Registry (Dockerhub)

Build và đẩy image ứng dụng python lên Dockerhub để có thể pull về được trong Kubernetes.

docker build -t docker.io/nguyenhoangviet3/reloader-test:latest .

docker push docker.io/nguyenhoangviet3/reloader-test:latest

Image của thử nghiệm này là docker.io/nguyenhoangviet3/reloader-test:latest bạn có thể cấu hình luôn để chạy nếu không muốn build lại.

4, Tạo resource trên K8s

Để có thể thực hiện thử nghiệm mình sẽ cần tạo 3 resource bao gồm:

  • Secret (lưu trữ giá trị RANDOM_ENV)
  • Deployment (Resource chạy ứng dụng python)
  • Service (dùng để expose dịch vụ ra ngoài để test)

Manifest Secret

apiVersion: v1
kind: Secret
metadata:
  name: random-env-secret
type: Opaque
data:
  RANDOM_ENV: "Zm9vCg==" # giá trị Zm9vCg== là mã hóa base64 của từ foo

Deployment manifest

apiVersion: apps/v1
kind: Deployment
metadata:
  name: flask-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: flask-app
  template:
    metadata:
      labels:
        app: flask-app
    spec:
      containers:
      - name: flask-app
        image: nguyenhoangviet3/reloader-test:latest
        ports:
        - containerPort: 5000
        env:
        - name: RANDOM_ENV
          valueFrom:
            secretKeyRef:                                # Định nghĩa lấy giá trị từ secret phía trên
              name: random-env-secret
              key: RANDOM_ENV
        volumeMounts:
        - name: secret-volume
          mountPath: /var/run/secrets/random-env-secret
          readOnly: true
      volumes:
      - name: secret-volume
        secret:
          secretName: random-env-secret

Service Manifest

apiVersion: v1
kind: Service
metadata:
  name: flask-app-service
spec:
  selector:
    app: flask-app
  ports:
    - protocol: TCP
      port: 5000
      targetPort: 5000
  type: NodePort

Khi tạo xong các resource trên ta có pod và service như trong hình.

image.png

5, Thử nghiệm thay đổi secret khi chưa có Reloader

Để thay đổi secret ta dùng câu lệnh sau để sửa trực tiếp secret đã tạo trước đó

kubectl edit secret random-env-secret

image.png

Ta se thay đổi phần giá trị trong mục data từ "Zm9vCg==" (foo) thành "YmFyCg==" (bar) và truy cập ứng dụng thông qua NodePort ở địa chỉ NodeIP:NodePort để xem có thay đổi không.

Trong môi trường của mình, địa chỉ để truy cập vào ứng dụng thông qua NodePort là http://192.168.49.2:32496/ với 192.168.49.2 là Node IP, 32496 là NodePort của service đã tạo.

Mặc dù đã thay đổi secret thành bar nhưng giá trị hiện thị bạn thấy sẽ vẫn là foo

image.png

6, Cài đặt Reloader và cấu hình reloader cho deployment

Phần cài đặt mình đã hướng dẫn bên trên, bạn lựa chọn cách cài đặt phù hợp nhé

Ngoài ra để kích hoạt tính năng tự động cập nhật khi có thay đổi secret hay configmap cho deployment/Statefulset/Deamonset/Rollout thì bạn cần thêm annotation cho resource đó. Cụ thể bạn sẽ cần thêm annoation sau cho deployment:

  annotations:
    reloader.stakater.com/auto: "true"

Bạn có thể edit trực tiếp deployment hoặc sử dụng câu lệnh sau để gắn annoation cho deployment

kubectl annotate deployment flask-app reloader.stakater.com/auto="true"

Đơn giản như vậy thôi là ta đã yêu cầu controller Reloader giám sát tự động thay đổi cấu hình cho deployment flask-app này.

7, Thử nghiệm thay đổi cấu hình khi đã cấu hình Reloader

Tương tự như bước 5, giờ ta sẽ thay đổi từ "bar" thành "foo-bar". Base64 của 'foo-bar' là "Zm9vLWJhcgo="

kubectl edit secret random-env-secret

Manifest secret sau khi thay đổi. Tiến hành lưu lại thay đổi.

image.png

Ta nhận thấy pod đã được tạo lại mới ngay sau khi secret được thay đổi:

image.png

Tiến hành get log của pod Reloader Controller ta cũng thấy sự kiện Controller bắt được sự thay đổi của secret

image.png

Và khi truy cập lại ứng dụng thông qua broswer, nội dung hiển thị cũng đã được tự động thay đổi thành foo-bar 😸😸😸

image.png

Như vậy ta đã thử nghiệm thành công việc tự động cập nhật ứng dụng khi có sự thay đổi về cấu hình nằm trong secret mà ứng dụng đang sử dụng.

Kết

Ngoài ra Reloader còn hỗ trợ bạn cấu hình theo dõi ứng dụng, cấu hình theo namespace, label,... Các bạn tìm hiểu và đọc thêm trên document ở Github nhé.

Hy vọng bài viết này đã giúp ích cho bạn một chút gì đó trong công việc. Nếu thấy bài viết hữu ích hãy UpvoteFollow mình để đọc thêm nhiều bài viết khác về chủ đề DevOps, SRE nữa nhé 😄 !!!

Have a nice day!


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.