Các tệp cục bộ khi di chuyển ứng dụng sang Kubernetes

Các tệp cục bộ khi di chuyển ứng dụng sang Kubernetes

Khi xây dựng quy trình CI/CD bằng Kubernetes, đôi khi phát sinh vấn đề không tương thích giữa các yêu cầu của cơ sở hạ tầng mới và ứng dụng được chuyển sang nó. Đặc biệt, ở giai đoạn xây dựng ứng dụng, điều quan trọng là phải có được một hình ảnh sẽ được sử dụng trong của tất cả môi trường dự án và các cụm. Nguyên tắc này làm cơ sở cho sự đúng đắn theo Google quản lý vùng chứa (nhiều lần về điều này đã nói và bộ phận kỹ thuật của chúng tôi).

Tuy nhiên, bạn sẽ không gặp bất kỳ ai trong các tình huống mà mã của trang web sử dụng khung làm sẵn, việc sử dụng khung này sẽ đặt ra các hạn chế đối với việc sử dụng tiếp theo. Và trong khi ở “môi trường bình thường”, điều này rất dễ giải quyết thì ở Kubernetes, hành vi này có thể trở thành một vấn đề, đặc biệt là khi bạn gặp phải nó lần đầu tiên. Mặc dù một bộ óc sáng tạo có thể đưa ra các giải pháp cơ sở hạ tầng thoạt nhìn có vẻ hiển nhiên hoặc thậm chí tốt... điều quan trọng cần nhớ là hầu hết các tình huống đều có thể và nên được giải quyết về mặt kiến ​​trúc.

Chúng tôi sẽ phân tích các giải pháp khắc phục phổ biến để lưu trữ tệp có thể dẫn đến hậu quả khó chịu khi vận hành một cụm, đồng thời chỉ ra đường dẫn chính xác hơn.

Lưu trữ tĩnh

Để minh họa, hãy xem xét một ứng dụng web sử dụng một số loại trình tạo tĩnh để thu được một tập hợp hình ảnh, kiểu dáng và những thứ khác. Ví dụ: khung Yii PHP có trình quản lý nội dung tích hợp để tạo các tên thư mục duy nhất. Theo đó, đầu ra là một tập hợp các đường dẫn cho trang tĩnh rõ ràng không giao nhau với nhau (điều này được thực hiện vì một số lý do - ví dụ: để loại bỏ trùng lặp khi nhiều thành phần sử dụng cùng một tài nguyên). Vì vậy, ngay từ lần đầu tiên bạn truy cập một mô-đun tài nguyên web, các tệp tĩnh (trên thực tế, thường là các liên kết tượng trưng, ​​nhưng sẽ nói thêm về điều đó sau) được hình thành và bố trí với một thư mục gốc chung duy nhất cho việc triển khai này:

  • webroot/assets/2072c2df/css/…
  • webroot/assets/2072c2df/images/…
  • webroot/assets/2072c2df/js/…

Điều này có ý nghĩa gì đối với một cụm?

Ví dụ đơn giản nhất

Hãy lấy một trường hợp khá phổ biến, khi PHP đứng trước nginx để phân phối dữ liệu tĩnh và xử lý các yêu cầu đơn giản. Cách dễ nhất - Triển khai với hai thùng chứa:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: site
spec:
  selector:
    matchLabels:
      component: backend
  template:
    metadata:
      labels:
        component: backend
    spec:
      volumes:
        - name: nginx-config
          configMap:
            name: nginx-configmap
      containers:
      - name: php
        image: own-image-with-php-backend:v1.0
        command: ["/usr/local/sbin/php-fpm","-F"]
        workingDir: /var/www
      - name: nginx
        image: nginx:1.16.0
        command: ["/usr/sbin/nginx", "-g", "daemon off;"]
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/conf.d/default.conf
          subPath: nginx.conf

Ở dạng đơn giản hóa, cấu hình nginx có nội dung như sau:

apiVersion: v1
kind: ConfigMap
metadata:
  name: "nginx-configmap"
data:
  nginx.conf: |
    server {
        listen 80;
        server_name _;
        charset utf-8;
        root  /var/www;

        access_log /dev/stdout;
        error_log /dev/stderr;

        location / {
            index index.php;
            try_files $uri $uri/ /index.php?$args;
        }

        location ~ .php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            include fastcgi_params;
        }
    }

Khi bạn truy cập trang web lần đầu tiên, nội dung sẽ xuất hiện trong vùng chứa PHP. Nhưng trong trường hợp có hai vùng chứa trong một nhóm, nginx không biết gì về các tệp tĩnh này, những tệp này (theo cấu hình) sẽ được cung cấp cho chúng. Kết quả là máy khách sẽ gặp lỗi 404 đối với tất cả các yêu cầu tới tệp CSS và JS. Giải pháp đơn giản nhất ở đây là tổ chức một thư mục chung cho các thùng chứa. Tùy chọn nguyên thủy - chung emptyDir:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: site
spec:
  selector:
    matchLabels:
      component: backend
  template:
    metadata:
      labels:
        component: backend
    spec:
      volumes:
        - name: assets
          emptyDir: {}
        - name: nginx-config
          configMap:
            name: nginx-configmap
      containers:
      - name: php
        image: own-image-with-php-backend:v1.0
        command: ["/usr/local/sbin/php-fpm","-F"]
        workingDir: /var/www
        volumeMounts:
        - name: assets
          mountPath: /var/www/assets
      - name: nginx
        image: nginx:1.16.0
        command: ["/usr/sbin/nginx", "-g", "daemon off;"]
        volumeMounts:
        - name: assets
          mountPath: /var/www/assets
        - name: nginx-config
          mountPath: /etc/nginx/conf.d/default.conf
          subPath: nginx.conf

Bây giờ các tệp tĩnh được tạo trong vùng chứa được nginx phân phát chính xác. Nhưng hãy để tôi nhắc bạn rằng đây là một giải pháp sơ khai, có nghĩa là nó còn xa lý tưởng và có những sắc thái cũng như khuyết điểm riêng, sẽ được thảo luận dưới đây.

Lưu trữ nâng cao hơn

Bây giờ hãy tưởng tượng tình huống trong đó người dùng truy cập trang web, tải một trang có các kiểu có sẵn trong vùng chứa và trong khi anh ta đang đọc trang này, chúng tôi đã triển khai lại vùng chứa. Danh mục nội dung đã trống và cần có yêu cầu tới PHP để bắt đầu tạo danh mục mới. Tuy nhiên, ngay cả sau đó, các liên kết đến số liệu thống kê cũ sẽ không liên quan, điều này sẽ dẫn đến lỗi hiển thị số liệu thống kê.

Ngoài ra, rất có thể chúng tôi có một dự án được tải ít nhiều, điều đó có nghĩa là một bản sao của ứng dụng sẽ không đủ:

  • Hãy mở rộng quy mô Triển khai tối đa hai bản sao.
  • Khi trang web được truy cập lần đầu tiên, nội dung được tạo thành một bản sao.
  • Tại một thời điểm nào đó, ingress đã quyết định (vì mục đích cân bằng tải) gửi yêu cầu đến bản sao thứ hai và những nội dung này vẫn chưa có ở đó. Hoặc có thể chúng không còn ở đó nữa vì chúng ta sử dụng RollingUpdate và hiện tại chúng tôi đang triển khai.

Nói chung, kết quả lại là sai lầm.

Để tránh mất tài sản cũ, bạn có thể thay đổi emptyDir trên hostPath, thêm tĩnh vật lý vào nút cụm. Cách tiếp cận này không tốt vì chúng ta thực sự phải liên kết với một nút cụm cụ thể ứng dụng của bạn, bởi vì - trong trường hợp di chuyển sang các nút khác - thư mục sẽ không chứa các tệp cần thiết. Hoặc một số loại đồng bộ hóa thư mục nền giữa các nút là bắt buộc.

Các giải pháp là gì?

  1. Nếu phần cứng và tài nguyên cho phép, bạn có thể sử dụng cephfs để tổ chức một thư mục có thể truy cập như nhau cho các nhu cầu tĩnh. tài liệu chính thức khuyến nghị sử dụng ổ SSD, sao chép ít nhất ba lần và kết nối “dày” ổn định giữa các nút cụm.
  2. Một lựa chọn ít đòi hỏi hơn là tổ chức một máy chủ NFS. Tuy nhiên, sau đó bạn cần tính đến khả năng tăng thời gian phản hồi để xử lý các yêu cầu của máy chủ web và khả năng chịu lỗi sẽ còn nhiều điều chưa được mong đợi. Hậu quả của sự thất bại là rất thảm khốc: việc mất giá đỡ khiến cụm này chết dưới áp lực của tải LA lao lên trời.

Trong số những thứ khác, tất cả các tùy chọn để tạo bộ lưu trữ liên tục sẽ yêu cầu làm sạch nền tập hợp các tập tin lỗi thời được tích lũy trong một khoảng thời gian nhất định. Trước các thùng chứa có PHP, bạn có thể đặt DaemonSet từ bộ nhớ đệm nginx, sẽ lưu trữ các bản sao của nội dung trong một thời gian giới hạn. Hành vi này có thể dễ dàng cấu hình bằng cách sử dụng proxy_cache với độ sâu lưu trữ tính bằng ngày hoặc gigabyte dung lượng ổ đĩa.

Việc kết hợp phương pháp này với các hệ thống tệp phân tán được đề cập ở trên mang lại một lĩnh vực rộng lớn cho trí tưởng tượng, chỉ bị giới hạn bởi ngân sách và tiềm năng kỹ thuật của những người sẽ triển khai và hỗ trợ nó. Từ kinh nghiệm, chúng ta có thể nói rằng hệ thống càng đơn giản thì hoạt động càng ổn định. Khi các lớp như vậy được thêm vào, việc duy trì cơ sở hạ tầng trở nên khó khăn hơn nhiều, đồng thời thời gian dành cho việc chẩn đoán và phục hồi sau mọi lỗi sẽ tăng lên.

Khuyến nghị

Nếu việc triển khai các tùy chọn lưu trữ được đề xuất cũng có vẻ không hợp lý đối với bạn (phức tạp, tốn kém...), thì bạn nên xem xét tình hình từ phía bên kia. Cụ thể là đi sâu vào kiến ​​trúc dự án và khắc phục sự cố trong mã, được gắn với một số cấu trúc dữ liệu tĩnh trong hình ảnh, một định nghĩa rõ ràng về nội dung hoặc quy trình để “khởi động” và/hoặc biên dịch trước nội dung ở giai đoạn lắp ráp hình ảnh. Bằng cách này, chúng tôi có được hành vi hoàn toàn có thể dự đoán được và cùng một bộ tệp cho tất cả các môi trường và bản sao của ứng dụng đang chạy.

Nếu chúng ta quay lại ví dụ cụ thể với Yii framework và không đi sâu vào cấu trúc của nó (vốn không phải mục đích của bài viết), thì chỉ cần chỉ ra hai cách tiếp cận phổ biến là đủ:

  1. Thay đổi quy trình xây dựng hình ảnh để đặt nội dung ở vị trí có thể dự đoán được. Điều này được đề xuất/triển khai trong các tiện ích mở rộng như yii2-tĩnh-tài sản.
  2. Xác định các giá trị băm cụ thể cho các thư mục nội dung, như đã thảo luận trong ví dụ: Bài trình bày này (bắt đầu từ slide số 35). Nhân tiện, tác giả của báo cáo cuối cùng (và không phải không có lý do!) khuyên rằng sau khi tập hợp nội dung trên máy chủ xây dựng, hãy tải chúng lên bộ lưu trữ trung tâm (như S3), phía trước đặt CDN.

Tải xuống

Một trường hợp khác chắc chắn sẽ phát huy tác dụng khi di chuyển ứng dụng sang cụm Kubernetes là lưu trữ tệp người dùng trong hệ thống tệp. Ví dụ: chúng ta lại có một ứng dụng PHP chấp nhận các tệp thông qua biểu mẫu tải lên, thực hiện điều gì đó với chúng trong quá trình hoạt động và gửi chúng trở lại.

Trong Kubernetes, vị trí đặt các tệp này phải chung cho tất cả các bản sao của ứng dụng. Tùy thuộc vào mức độ phức tạp của ứng dụng và nhu cầu sắp xếp tính bền vững của các tệp này, các tùy chọn thiết bị dùng chung nêu trên có thể là một nơi như vậy, nhưng, như chúng ta thấy, chúng có những nhược điểm.

Khuyến nghị

Một giải pháp là sử dụng bộ lưu trữ tương thích với S3 (ngay cả khi đó là một loại danh mục tự lưu trữ như minio). Chuyển sang S3 sẽ yêu cầu thay đổi ở cấp độ mãvà cách thức phân phối nội dung ở giao diện người dùng, chúng tôi đã писали.

Phiên người dùng

Riêng biệt, điều đáng chú ý là việc tổ chức lưu trữ các phiên của người dùng. Thông thường, đây cũng là các tệp trên đĩa, trong bối cảnh Kubernetes sẽ dẫn đến các yêu cầu ủy quyền liên tục từ người dùng nếu yêu cầu của anh ta kết thúc ở một vùng chứa khác.

Vấn đề được giải quyết một phần bằng cách bật stickySessions khi xâm nhập (tính năng này được hỗ trợ trong tất cả các bộ điều khiển xâm nhập phổ biến - để biết thêm chi tiết, hãy xem đánh giá của chúng tôi)để liên kết người dùng với một nhóm cụ thể với ứng dụng:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: nginx-test
  annotations:
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "route"
    nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
    nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"

spec:
  rules:
  - host: stickyingress.example.com
    http:
      paths:
      - backend:
          serviceName: http-svc
          servicePort: 80
        path: /

Nhưng điều này sẽ không loại bỏ được vấn đề khi triển khai nhiều lần.

Khuyến nghị

Một cách chính xác hơn là chuyển ứng dụng sang lưu trữ phiên trong memcached, Redis và các giải pháp tương tự - nói chung, từ bỏ hoàn toàn các tùy chọn tập tin.

Kết luận

Các giải pháp cơ sở hạ tầng được thảo luận trong văn bản chỉ đáng được sử dụng ở dạng “nạng” tạm thời (nghe có vẻ hay hơn trong tiếng Anh như một cách giải quyết). Chúng có thể có liên quan trong giai đoạn đầu tiên di chuyển ứng dụng sang Kubernetes, nhưng không nên root.

Con đường chung được khuyến nghị là loại bỏ chúng để sửa đổi kiến ​​​​trúc của ứng dụng phù hợp với những gì đã được nhiều người biết đến Ứng dụng 12 yếu tố. Tuy nhiên, điều này - đưa ứng dụng sang dạng không trạng thái - chắc chắn có nghĩa là sẽ cần phải thay đổi mã và ở đây điều quan trọng là phải tìm được sự cân bằng giữa khả năng/yêu cầu của doanh nghiệp và triển vọng triển khai và duy trì con đường đã chọn .

PS

Đọc thêm trên blog của chúng tôi:

Nguồn: www.habr.com

Thêm một lời nhận xét