Sổ tay Kubernetes: làm việc với Ingress Controller (p2)

Sổ tay Kubernetes: làm việc với Ingress Controller (p2)

Loạt bài viết sổ tay Kubernetes:

  • Sổ tay Kubernetes: Giới thiệu về Kubernetes.
  • Sổ tay Kubernetes: Các khái niệm cơ bản trong Kubernetes.
  • Sổ tay Kubernetes: Phương pháp triển khai bằng khai báo.
  • Sổ tay Kubernetes: Triển khai ứng dụng đa container (p1).
  • Sổ tay Kubernetes: Triển khai ứng dụng đa container (p2).
  • Sổ tay Kubernetes: Triển khai ứng dụng đa container (p3).
  • Sổ tay Kubernetes: làm việc với Ingress Controller (p1).
  • Sổ tay Kubernetes: làm việc với Ingress Controller (p2) (bài viết này).

Ở phần 1, bạn đã tìm hiểu về Ingress Controller và cách sử dụng NGINX Ingress Controller để cấu hình định tuyến trong Kubernetes.

Trong phần 2 này, bạn sẽ tìm hiểu về Secret để lưu trữ các thông tin nhạy cảm, ConfigMap để lưu trữ thông tin cấu hình, phát hành cập nhật và xử lý sự cố trong Kubernetes.

Secret và ConfigMap trong Kubernetes

Cho đến lúc này trong các lần triển khai, bạn đã lưu trữ thông tin nhạy cảm như POSTGRES_PASSWORD ở dạng văn bản thuần túy, đây không phải là một ý kiến ​​hay.

Để lưu trữ các giá trị như vậy trong cụm của bạn, bạn có thể sử dụng một cách an toàn hơn nhiều là Secret để lưu trữ mật khẩu, mã thông báo, v.v.

Bước tiếp theo có thể không hoạt động tương tự trong dòng lệnh Windows. Bạn có thể sử dụng git bash hoặc cmder cho tác vụ này.

Để lưu trữ thông tin trong Secret trước tiên bạn phải chuyển dữ liệu của mình qua base64. Nếu mật khẩu văn bản thuần túy 63eaQB9wtLqmNBpg thì thực thi lệnh sau để nhận phiên bản được mã hóa base64:

echo -n "63eaQB9wtLqmNBpg" | base64

# NjNlYVFCOXd0THFtTkJwZw==

Bước này không phải là tùy chọn, bạn phải chạy chuỗi văn bản thuần túy thông qua base64. Bây giờ, hãy tạo một tệp có tên postgres-secret.yaml bên trong thư mục k8s và đặt nội dung sau vào đó:

apiVersion: v1
kind: Secret
metadata:
  name: postgres-secret
data:
  password: NjNlYVFCOXd0THFtTkJwZw==

Các trường apiVersion, kindmetadata khá dễ hiểu. Trường data giữ bí mật thực tế.

Như bạn có thể thấy, tôi đã tạo một cặp khóa-giá trị trong đó khóa là password và giá trị NjNlYVFCOXd0THFtTkJwZw==. Bạn sẽ sử dụng giá trị metadata.name để định danh Secret trong các tệp cấu hình khác và khóa để truy cập giá trị mật khẩu.

Bây giờ để sử dụng bí mật này bên trong cấu hình cơ sở dữ liệu của bạn, hãy cập nhật tệp postgres-deployment.yaml như sau:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      component: postgres
  template:
    metadata:
      labels:
        component: postgres
    spec:
      volumes:
        - name: postgres-storage
          persistentVolumeClaim:
            claimName: database-persistent-volume-claim
      containers:
        - name: postgres
          image: fhsinchy/notes-postgres
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql/data
              subPath: postgres
          env:
          	# not putting the password directly anymore
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: password
            - name: POSTGRES_DB
              value: notesdb

Như bạn có thể thấy, toàn bộ tệp đều giống nhau ngoại trừ trường spec.template.spec.continers.env.

Trước đây, biến môi trường name được sử dụng để lưu trữ giá trị mật khẩu ở dạng văn bản thuần túy. Nhưng bây giờ có một trường valueFrom.secretKeyRef mới.

Trường name ở đây đề cập đến tên của Secret mà bạn đã tạo ở phần trước, và giá trị key đề cập đến khóa từ cặp khóa-giá trị ở chỗ tập tin cấu hình Secret. Giá trị được mã hóa sẽ được Kubernetes giải mã thành văn bản thuần túy trong nội bộ.

Ngoài cấu hình cơ sở dữ liệu, bạn cũng sẽ phải cập nhật tệp api-deployment.yaml như sau:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      component: api
  template:
    metadata:
      labels:
        component: api
    spec:
      containers:
        - name: api
          image: fhsinchy/notes-api
          ports:
            - containerPort: 3000
          env:
            - name: DB_CONNECTION
              value: pg
            - name: DB_HOST
              value: postgres-cluster-ip-service
            - name: DB_PORT
              value: '5432'
            - name: DB_USER
              value: postgres
            - name: DB_DATABASE
              value: notesdb
              # not putting the password directly anymore
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: password

Bây giờ áp dụng tất cả các cấu hình mới này bằng cách thực hiện lệnh sau:

kubectl apply -f k8s

# service/api-cluster-ip-service created
# deployment.apps/api-deployment created
# service/client-cluster-ip-service created
# deployment.apps/client-deployment created
# persistentvolumeclaim/database-persistent-volume-claim created
# secret/postgres-secret created
# ingress.extensions/ingress-service created
# service/postgres-cluster-ip-service created
# deployment.apps/postgres-deployment created

Tùy thuộc vào trạng thái của cụm của bạn, bạn có thể thấy một tập hợp đầu ra khác nhau.

Trong trường hợp bạn đang gặp bất kỳ sự cố nào, hãy xóa tất cả tài nguyên Kubernetes và tạo lại chúng bằng cách áp dụng lại các cấu hình.

Sử dụng lệnh get để kiểm tra và đảm bảo rằng tất cả các nhóm đều đang hoạt động.

Bây giờ để kiểm tra cấu hình mới, hãy truy cập ứng dụng ghi chú bằng lệnh minikube IP và thử tạo ghi chú mới. Để lấy IP, bạn có thể thực hiện lệnh sau:

minikube ip

# 172.17.0.2

Bằng cách truy cập 127.17.0.2:80, bạn sẽ truy cập trực tiếp vào ứng dụng ghi chú.

Giao diện ứng dụng

Có một cách khác để tạo secret mà không cần bất kỳ tệp cấu hình nào. Để tạo  Secret tương tự bằng cách sử dụng lệnh kubectl, hãy thực hiện lệnh sau:

kubectl create secret generic postgres-secret --from-literal=password=63eaQB9wtLqmNBpg

# secret/postgres-secret created

Đây là một cách tiếp cận thuận tiện hơn vì bạn có thể bỏ qua toàn bộ bước mã hóa base64. Secret trong trường hợp này sẽ được mã hóa tự động.

ConfigMap tương tự như Secret nhưng được sử dụng với thông tin không nhạy cảm.

Để đặt tất cả các biến môi trường khác trong triển khai API bên trong một ConfigMap, hãy tạo một tệp mới có tên api-config-map.yaml bên trong thư mục k8s và đưa nội dung sau vào đó:

apiVersion: v1 
kind: ConfigMap 
metadata:
  name: api-config-map 
data:
  DB_CONNECTION: pg
  DB_HOST: postgres-cluster-ip-service
  DB_PORT: '5432'
  DB_USER: postgres
  DB_DATABASE: notesdb

Các trường apiVersion, kindmetadata đã quá dễ hiểu. Trường data có thể chứa các biến môi trường dưới dạng các cặp khóa-giá trị.

Không giống như Secret, các khóa ở đây phải khớp với khóa chính xác mà API yêu cầu. Do đó, tôi đã sao chép các biến từ tệp api-deployment.yaml và dán chúng vào đây với một chút sửa đổi trong cú pháp.

Để sử dụng cấu hình này trong triển khai API, hãy mở tệp api-deployment.yaml và cập nhật nội dung của nó như sau:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      component: api
  template:
    metadata:
      labels:
        component: api
    spec:
      containers:
        - name: api
          image: fhsinchy/notes-api
          ports:
            - containerPort: 3000
          # not putting environment variables directly
          envFrom:
            - configMapRef:
                name: api-config-map
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: password
          

Toàn bộ tệp hầu như không thay đổi ngoại trừ trường spec.template.spec.containers.env.

Tôi đã chuyển các biến môi trường sang ConfigMap. Trường spec.template.spec.containers.envFrom được sử dụng để lấy dữ liệu từ ConfigMap. Trường configMapRef.name ở đây chỉ ra ConfigMap nơi mà các biến môi trường sẽ được kéo.

Bây giờ áp dụng tất cả các cấu hình mới này bằng cách thực hiện lệnh sau:

kubectl apply -f k8s

# service/api-cluster-ip-service created
# configmap/api-config-map created
# deployment.apps/api-deployment created
# service/client-cluster-ip-service created
# deployment.apps/client-deployment created
# persistentvolumeclaim/database-persistent-volume-claim created
# ingress.extensions/ingress-service configured
# service/postgres-cluster-ip-service created
# deployment.apps/postgres-deployment created
# secret/postgres-secret created

Tùy thuộc vào trạng thái của cụm của bạn, bạn có thể thấy một tập hợp đầu ra khác nhau.

Trong trường hợp bạn đang gặp bất kỳ sự cố nào, hãy xóa tất cả tài nguyên Kubernetes và tạo lại chúng bằng cách áp dụng lại các cấu hình.

Sau khi đảm bảo rằng các pod đã được thiết lập và đang chạy bằng lệnh get, hãy truy cập ứng dụng ghi chú bằng minikube IP và thử tạo ghi chú mới.

Để lấy IP, bạn có thể thực hiện lệnh sau:

minikube ip

# 172.17.0.2

Bằng cách truy cập 127.17.0.2:80, bạn sẽ truy cập trực tiếp vào ứng dụng ghi chú.

Giao diện ứng dụng ghi chú

SecretConfigMap có thêm một vài thủ thuật mà tôi sẽ không trình bày ở đây. Nhưng nếu bạn tò mò, bạn có thể xem các tài liệu chính thức.

Thực hiện phát hành cập nhật trong Kubernetes

Bây giờ bạn đã triển khai thành công một ứng dụng bao gồm nhiều container trên Kubernetes, đã đến lúc tìm hiểu về việc thực hiện cập nhật.

Có vẻ như Kubernetes kỳ diệu đối với bạn, việc cập nhật container lên phiên bản image mới hơn là một chút khó khăn. Có nhiều cách tiếp cận mà mọi người thường thực hiện để cập nhật container, nhưng tôi sẽ không đi hết tất cả chúng.

Thay vào đó, tôi sẽ chuyển ngay sang cách tiếp cận mà tôi chủ yếu thực hiện khi cập nhật container của mình. Nếu bạn mở tệp client-deployment.yaml và nhìn vào trường spec.template.spec.containers, bạn sẽ thấy một cái gì đó giống như sau:

containers:
    - name: client
      image: fhsinchy/notes-client

Như bạn có thể thấy, trong trường image này, tôi chưa sử dụng bất kỳ image tag nào. Bây giờ nếu bạn nghĩ rằng việc thêm :latest vào cuối tên image sẽ đảm bảo rằng việc triển khai luôn kéo image mới nhất có sẵn, bạn đã nhầm.

Cách tiếp cận mà tôi thường áp dụng là cách tiếp cận bắt buộc. Tôi đã đề cập trong phần trước rằng, trong một vài trường hợp, sử dụng cách tiếp cận mệnh lệnh thay vì cách tiếp cận khai báo là một ý tưởng hay. Việc tạo Secret hoặc cập nhật container là một trường hợp như vậy.

Lệnh bạn có thể sử dụng để thực hiện cập nhật là lệnh set và cú pháp chung như sau:

kubectl set image <resource type>/<resource name> <container name>=<image name with tag>

Loại tài nguyên là deployment và tên tài nguyên client-deployment. Tên container có thể được tìm thấy dưới trường containers bên trong tệp client-deployment.yaml, nó là client trong trường hợp này.

Tôi đã tạo một phiên bản của image fhsinchy/notes-client với thẻ edge mà tôi sẽ sử dụng để cập nhật triển khai này.

Vì vậy, lệnh cuối cùng sẽ như sau:

kubectl set image deployment/client-deployment client=fhsinchy/notes-client:edge

# deployment.apps/client-deployment image updated

Quá trình cập nhật có thể mất một lúc vì Kubernetes sẽ tạo lại tất cả các pod. Bạn có thể chạy lệnh get để biết liệu tất cả các pod đã hoạt động hay chưa và đang chạy lại.

Sau khi tất cả chúng đã được tạo lại, hãy truy cập ứng dụng ghi chú bằng minikube IP và thử tạo ghi chú mới. Để lấy IP, bạn có thể thực hiện lệnh sau:

minikube ip

# 172.17.0.2

Bằng cách truy cập 127.17.0.2:80, bạn sẽ truy cập trực tiếp vào ứng dụng ghi chú.

Giao diện ứng dụng Notes

Bạn có thể cho rằng tôi đã không thực hiện bất kỳ thay đổi thực tế nào đối với mã ứng dụng, mọi thứ sẽ vẫn như cũ. Bạn có thể đảm bảo rằng các pod đang sử dụng image mới bằng lệnh describe.

kubectl describe pod client-deployment-849bc58bcc-gz26b | grep 'Image'

# Image:          fhsinchy/notes-client:edge
# Image ID:       docker-pullable://fhsinchy/notes-client@sha256:58bce38c16376df0f6d1320554a56df772e30a568d251b007506fd3b5eb8d7c2

Lệnh grep có sẵn trên máy Mac và Linux. Nếu bạn đang sử dụng Windows, hãy sử dụng git bash thay vì dòng lệnh windows.

Mặc dù quy trình cập nhật bắt buộc hơi tẻ nhạt, nhưng nó có thể được thực hiện dễ dàng hơn nhiều bằng cách sử dụng quy trình làm việc CI/CD tốt.

Kết hợp các cấu hình

Như bạn đã thấy, số lượng tệp cấu hình trong dự án này là khá lớn mặc dù chỉ có ba container trong đó.

Bạn thực sự có thể kết hợp các tệp cấu hình như sau:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: client-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      component: client
  template:
    metadata:
      labels:
        component: client
    spec:
      containers:
        - name: client
          image: fhsinchy/notes-client
          ports:
            - containerPort: 8080
          env:
            - name: VUE_APP_API_URL
              value: /api
              
---

apiVersion: v1
kind: Service
metadata:
  name: client-cluster-ip-service
spec:
  type: ClusterIP
  selector:
    component: client
  ports:
    - port: 8080
      targetPort: 8080

Như bạn có thể thấy, tôi đã kết hợp nội dung của tệp client-deployment.yaml và tệp client-cluster-ip-service.yaml bằng dấu phân cách (---). Mặc dù có thể và có thể hữu ích trong các dự án có số lượng container rất cao, tôi khuyên bạn nên giữ chúng riêng biệt, sạch sẽ và ngắn gọn.

Xử lý sự cố

Trong phần này, tôi sẽ liệt kê một số vấn đề phổ biến mà bạn có thể gặp phải trong thời gian làm việc với Kubernetes.

  • Nếu bạn đang sử dụng Windows hoặc Mac và sử dụng trình điều khiển Docker cho minikube, plugin Ingress sẽ không hoạt động.
  • Nếu bạn có Laravel Valet đang chạy trên Mac và đang sử dụng trình điều khiển HyperKit minikube, nó sẽ không kết nối được với Internet. Tắt dịch vụ dnsmasq sẽ giải quyết được sự cố.
  • Nếu bạn có PC Ryzen (của tôi là R5 1600) và đang chạy Windows 10, trình điều khiển VirtualBox có thể không khởi động được do thiếu hỗ trợ ảo hóa lồng nhau. Bạn sẽ phải sử dụng trình điều khiển Hyper-V trên Windows 10 (Pro, Enterprise và Education). Đối với người dùng phiên bản Home, thật đáng buồn là không có tùy chọn an toàn nào trên phần cứng đó.
  • Nếu bạn đang chạy Windows 10 (Pro, Enterprise và Education) với trình điều khiển Hyper-V cho minikube, máy ảo có thể không khởi động được với thông báo về việc không đủ bộ nhớ. Đừng hoảng sợ và thực hiện lệnh minikube start một lần nữa để khởi động máy ảo đúng cách.
  • Nếu bạn thấy một số lệnh được thực thi trong bài viết này bị thiếu hoặc hoạt động sai trong dòng lệnh Windows, hãy sử dụng git bash hoặc cmder để thay thế.

Tôi khuyên bạn nên cài đặt một bản phân phối Linux tốt trên hệ thống của bạn và sử dụng trình điều khiển Docker cho minikube. Đây là cách thiết lập nhanh nhất và đáng tin cậy nhất.

Phần kết luận

Tôi xin cảm ơn từ tận đáy lòng vì thời gian bạn đã dành để đọc loạt bài viết này. Tôi hy vọng bạn đã tận hưởng thời gian của mình và đã học được tất cả những điều cần thiết về Kubernetes.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *