Bài toán
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 🤣🤣🤣
Công cụ Reloader
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:
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
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.
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
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
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.
Ta nhận thấy pod đã được tạo lại mới ngay sau khi secret được thay đổi:
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
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 😸😸😸
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 Upvote và Follow mình để đọc thêm nhiều bài viết khác về chủ đề DevOps, SRE nữa nhé !!!
Have a nice day!