Yêu cầu để phát triển ứng dụng trong Kubernetes

Hôm nay tôi dự định nói về cách viết ứng dụng và những yêu cầu để ứng dụng của bạn hoạt động tốt trong Kubernetes. Để không phải đau đầu với ứng dụng, để bạn không phải phát minh và xây dựng bất kỳ “khung” nào xung quanh nó - và mọi thứ đều hoạt động theo cách mà Kubernetes dự định.

Bài giảng này là một phần của "Trường học đêm Slurm trên Kubernetes" Bạn có thể xem các bài giảng lý thuyết mở của Trường buổi tối trên Youtube, được nhóm thành danh sách phát. Đối với những người thích văn bản hơn video, chúng tôi đã chuẩn bị bài viết này.

Tên tôi là Pavel Selivanov, hiện tại tôi là kỹ sư DevOps hàng đầu tại Mail.ru Cloud Solutions, chúng tôi tạo ra các đám mây, chúng tôi tạo ra các kubernetes quản lý, v.v. Nhiệm vụ của tôi bây giờ bao gồm hỗ trợ phát triển, triển khai các đám mây này, triển khai các ứng dụng mà chúng tôi viết và trực tiếp phát triển các công cụ mà chúng tôi cung cấp cho người dùng.

Yêu cầu để phát triển ứng dụng trong Kubernetes

Tôi nghĩ tôi đã làm DevOps được khoảng ba năm rồi. Nhưng về nguyên tắc, tôi đã làm những gì DevOps làm được khoảng 5 năm rồi. Trước đó, tôi chủ yếu tham gia vào công việc quản trị. Tôi đã bắt đầu làm việc với Kubernetes từ lâu - có lẽ đã khoảng bốn năm trôi qua kể từ khi tôi bắt đầu làm việc với nó.

Nói chung, tôi đã bắt đầu khi Kubernetes có thể là phiên bản 1.3 và có thể là 1.2 - khi nó vẫn còn ở giai đoạn sơ khai. Bây giờ nó không còn ở giai đoạn sơ khai nữa - và rõ ràng là thị trường đang có nhu cầu rất lớn đối với các kỹ sư muốn có khả năng làm Kubernetes. Và các công ty có nhu cầu rất cao đối với những người như vậy. Vì vậy, trên thực tế, bài giảng này đã xuất hiện.

Nếu chúng ta nói theo kế hoạch của những gì tôi sẽ nói, nó trông như thế này, trong ngoặc nó được viết (TL;DR) - “quá dài; không đọc”. Bài thuyết trình của tôi hôm nay sẽ bao gồm những danh sách vô tận.

Yêu cầu để phát triển ứng dụng trong Kubernetes

Trên thực tế, bản thân tôi không thích những bài thuyết trình như vậy khi chúng được thực hiện, nhưng đây là một chủ đề mà khi chuẩn bị bài thuyết trình này, tôi chỉ đơn giản là không thực sự tìm ra cách sắp xếp thông tin này một cách khác biệt.

Bởi vì nhìn chung, thông tin này là “ctrl+c, ctrl+v”, từ Wiki của chúng tôi trong phần DevOps, nơi chúng tôi có các yêu cầu bằng văn bản dành cho nhà phát triển: “các bạn, để chúng tôi khởi chạy ứng dụng của bạn trong Kubernetes, nó phải như thế này."

Đó là lý do tại sao bài thuyết trình hóa ra lại là một danh sách lớn như vậy. Lấy làm tiếc. Mình sẽ cố gắng kể nhiều nhất có thể để không bị nhàm chán nếu có thể.

Những gì chúng ta sẽ xem xét bây giờ:

  • trước hết đây là nhật ký (nhật ký ứng dụng?), phải làm gì với chúng trong Kubernetes, phải làm gì với chúng, chúng nên là gì;
  • phải làm gì với cấu hình trong Kubernetes, cách tốt nhất và tệ nhất để định cấu hình ứng dụng cho Kubernetes là gì;
  • Hãy nói về việc kiểm tra khả năng truy cập nói chung là gì, chúng trông như thế nào;
  • hãy nói về việc tắt máy một cách duyên dáng là như thế nào;
  • hãy nói lại về tài nguyên;
  • Hãy chạm lại chủ đề lưu trữ dữ liệu;
  • và cuối cùng tôi sẽ cho bạn biết thuật ngữ ứng dụng gốc đám mây bí ẩn này là gì. Cloudnativeness, như một tính từ của thuật ngữ này.

Nhật ký

Tôi khuyên bạn nên bắt đầu với nhật ký - nơi cần đưa những nhật ký này vào Kubernetes. Bây giờ bạn đã khởi chạy một ứng dụng trong Kubernetes. Theo kinh điển, các ứng dụng trước đây luôn ghi nhật ký ở đâu đó trong một tệp. Các ứng dụng xấu đã ghi nhật ký vào một tệp trong thư mục chính của nhà phát triển đã khởi chạy ứng dụng. Các ứng dụng tốt đã ghi nhật ký vào một tệp ở đâu đó trong /var/log.

Yêu cầu để phát triển ứng dụng trong Kubernetes

Theo đó, hơn nữa, các quản trị viên giỏi đã định cấu hình một số thứ trong cơ sở hạ tầng của họ để các nhật ký này có thể xoay - cùng một rsyslog, xem xét các nhật ký này và khi có điều gì đó xảy ra với chúng, có rất nhiều nhật ký, nó tạo ra các bản sao lưu, đặt nhật ký ở đó , xóa các tệp cũ hơn một tuần, sáu tháng và một số tệp khác. Về lý thuyết, chúng ta nên có những điều khoản để đơn giản vì ứng dụng ghi nhật ký nên dung lượng trên các máy chủ sản xuất (máy chủ chiến đấu?) không bị hết. Và theo đó, toàn bộ quá trình sản xuất không dừng lại vì khúc gỗ.

Khi chúng ta chuyển sang thế giới Kubernetes và chạy điều tương tự ở đó, điều đầu tiên bạn có thể chú ý là thực tế là mọi người, khi họ viết nhật ký vào một tệp, vẫn tiếp tục viết chúng.

Hóa ra là nếu chúng ta nói về Kubernetes, nơi thích hợp để ghi nhật ký ở đâu đó từ vùng chứa docker chỉ đơn giản là ghi chúng từ ứng dụng vào cái gọi là Stdout/Stderr, tức là các luồng đầu ra tiêu chuẩn của hệ điều hành, đầu ra lỗi tiêu chuẩn. Đây là cách chính xác nhất, đơn giản nhất và hợp lý nhất để đưa nhật ký về nguyên tắc vào Docker và cụ thể là trong Kubernetis. Bởi vì nếu ứng dụng của bạn ghi nhật ký vào Stdout/Stderr, thì Docker và tiện ích bổ sung Kubernetes sẽ quyết định phải làm gì với những nhật ký này. Docker theo mặc định sẽ xây dựng các tệp đặc biệt của nó ở định dạng JSON.

Ở đây câu hỏi được đặt ra là bạn sẽ làm gì tiếp theo với những nhật ký này? Cách dễ nhất là rõ ràng, chúng tôi có khả năng làm kubectl logs và nhìn vào nhật ký của những “nhóm” này. Tuy nhiên, có lẽ đây không phải là một lựa chọn tốt - cần phải làm một việc khác với nhật ký.

Bây giờ, chúng ta hãy cùng nói chuyện, vì chúng ta đã đề cập đến chủ đề nhật ký, về một thứ như nhật ký sẽ trông như thế nào. Nghĩa là, điều này không áp dụng trực tiếp cho Kubernetes, nhưng khi chúng ta bắt đầu nghĩ về những việc cần làm với nhật ký, thì cũng nên nghĩ về điều này.

Chúng tôi cần một số loại công cụ, theo cách thân thiện, sẽ lấy những nhật ký này mà docker của chúng tôi đặt vào các tệp của nó và gửi chúng đi đâu đó. Nhìn chung, chúng tôi thường khởi chạy một số loại tác nhân bên trong Kubernetes dưới dạng DaemonSet - một trình thu thập nhật ký, được cho biết đơn giản về vị trí của các nhật ký mà Docker thu thập. Và tác nhân thu thập này chỉ đơn giản lấy chúng, thậm chí có thể bằng cách nào đó phân tích chúng trong quá trình thực hiện, có thể làm phong phú chúng bằng một số thông tin meta bổ sung và cuối cùng, gửi chúng để lưu trữ ở đâu đó. Các biến thể đã có thể xảy ra ở đó. Phổ biến nhất có lẽ là Elaticsearch, nơi bạn có thể lưu trữ nhật ký và có thể truy xuất chúng từ đó một cách thuận tiện. Sau đó, bằng cách sử dụng một yêu cầu, chẳng hạn như sử dụng Kibana, xây dựng biểu đồ dựa trên chúng, xây dựng cảnh báo dựa trên chúng, v.v.

Ý tưởng quan trọng nhất, tôi muốn nhắc lại một lần nữa, đó là bên trong Docker, đặc biệt là bên trong Kubernetes, việc lưu trữ nhật ký của bạn vào một tệp là một ý tưởng rất tồi.

Bởi vì thứ nhất, rất khó để lấy nhật ký bên trong vùng chứa trong một tệp. Trước tiên, bạn phải vào vùng chứa, thực thi ở đó và sau đó xem nhật ký. Điểm tiếp theo là nếu bạn có nhật ký trong một tệp thì các thùng chứa thường có môi trường tối giản và không có tiện ích nào thường cần cho công việc bình thường với nhật ký. Hãy chôn chúng, nhìn chúng, mở chúng trong trình soạn thảo văn bản. Khoảnh khắc tiếp theo là khi chúng ta có nhật ký trong một tệp bên trong vùng chứa, nếu vùng chứa này bị xóa, bạn biết đấy, nhật ký sẽ chết cùng với nó. Theo đó, bất kỳ sự khởi động lại nào của vùng chứa đều có nghĩa là không còn nhật ký nào nữa. Một lần nữa, lựa chọn tồi.

Và điểm cuối cùng là bên trong các thùng chứa, bạn thường có ứng dụng của mình và thế là xong - đó thường là quá trình duy nhất đang chạy. Không có cuộc thảo luận nào về bất kỳ quy trình nào có thể xoay tệp bằng nhật ký của bạn. Ngay sau khi nhật ký bắt đầu được ghi vào một tệp, điều này có nghĩa là, xin lỗi, chúng tôi sẽ bắt đầu mất máy chủ sản xuất. Bởi vì, thứ nhất, chúng rất khó tìm, không ai theo dõi chúng, cộng thêm không ai kiểm soát chúng - theo đó, tệp sẽ phát triển vô tận cho đến khi hết dung lượng trên máy chủ. Do đó, tôi xin nhắc lại rằng việc đăng nhập Docker, đặc biệt là trong Kubernetes, vào một tệp là một ý tưởng tồi.

Điểm tiếp theo, ở đây tôi muốn nói lại về vấn đề này - vì chúng ta đang đề cập đến chủ đề nhật ký, nên sẽ rất tốt nếu nói về giao diện của nhật ký để thuận tiện khi làm việc với chúng. Như mình đã nói, chủ đề này không liên quan trực tiếp đến Kubernetes nhưng nó liên quan rất tốt đến chủ đề DevOps. Về chủ đề văn hóa phát triển và tình bạn giữa hai bộ phận khác nhau - Dev và Ops, để mọi người đều thoải mái.

Điều này có nghĩa là lý tưởng nhất là ngày nay nhật ký phải được viết ở định dạng JSON. Nếu bạn có một số ứng dụng khó hiểu của riêng mình, ghi nhật ký ở các định dạng khó hiểu vì bạn chèn một số loại bản in hoặc thứ gì đó tương tự, thì đã đến lúc tìm kiếm trên Google một loại khung, một loại trình bao bọc nào đó cho phép bạn triển khai ghi nhật ký thông thường; bật tham số ghi nhật ký trong JSON ở đó, vì JSON là định dạng đơn giản nên việc phân tích cú pháp cũng đơn giản.

Nếu JSON của bạn không hoạt động theo một số tiêu chí, không ai biết đó là gì, thì ít nhất hãy viết nhật ký ở định dạng có thể phân tích cú pháp được. Thay vào đó, ở đây đáng để suy nghĩ về thực tế là, chẳng hạn, nếu bạn đang chạy một loạt các thùng chứa hoặc chỉ xử lý bằng nginx và mỗi thùng chứa có cài đặt ghi nhật ký riêng, thì có vẻ như điều đó sẽ rất bất tiện cho bạn. phân tích chúng. Bởi vì đối với mỗi phiên bản nginx mới, bạn cần phải viết trình phân tích cú pháp của riêng mình, vì chúng viết nhật ký khác nhau. Một lần nữa, có lẽ đáng để suy nghĩ về việc đảm bảo rằng tất cả các phiên bản nginx này có cùng cấu hình ghi nhật ký và ghi tất cả nhật ký của chúng một cách hoàn toàn thống nhất. Điều tương tự cũng áp dụng cho tất cả các ứng dụng.

Cuối cùng, tôi cũng muốn đổ thêm dầu vào lửa, lý tưởng nhất là nên tránh ghi nhật ký có định dạng nhiều dòng. Vấn đề là ở đây, nếu bạn đã từng làm việc với những người thu thập nhật ký, thì rất có thể bạn đã thấy những gì họ hứa với bạn rằng họ có thể làm việc với nhật ký nhiều dòng, biết cách thu thập chúng, v.v. Trên thực tế, theo tôi, không một nhà sưu tập nào ngày nay có thể thu thập nhật ký nhiều dòng một cách bình thường, đầy đủ và không có sai sót. Theo cách của con người, sao cho thuận tiện và không có lỗi.

Yêu cầu để phát triển ứng dụng trong Kubernetes

Nhưng dấu vết ngăn xếp luôn là nhật ký nhiều dòng và cách tránh chúng. Câu hỏi ở đây là nhật ký là bản ghi của một sự kiện và stactrace không thực sự là nhật ký. Nếu chúng tôi thu thập nhật ký và đặt chúng ở đâu đó trong Elaticsearch rồi vẽ biểu đồ từ chúng, xây dựng một số báo cáo về hoạt động của người dùng trên trang web của bạn, thì khi bạn nhận được dấu vết ngăn xếp, điều đó có nghĩa là có điều gì đó không mong muốn đang xảy ra, một tình huống chưa được xử lý trong ứng dụng của bạn. Và thật hợp lý khi tự động tải dấu vết ngăn xếp lên đâu đó vào hệ thống có thể theo dõi chúng.

Đây là phần mềm (cùng Sentry) được thiết kế đặc biệt để hoạt động với dấu vết ngăn xếp. Nó có thể ngay lập tức tạo các nhiệm vụ tự động, giao chúng cho ai đó, cảnh báo khi xảy ra dấu vết, nhóm các dấu vết này theo một loại, v.v. Về nguyên tắc, sẽ không có nhiều ý nghĩa khi nói về stactrace khi chúng ta nói về nhật ký, bởi vì xét cho cùng thì đây là những thứ khác nhau với những mục đích khác nhau.

Cấu hình

Tiếp theo chúng ta nói về cấu hình trong Kubernetes: phải làm gì với nó và cách cấu hình các ứng dụng bên trong Kubernetes. Nói chung, tôi thường nói rằng Docker không phải về container. Mọi người đều biết rằng Docker là về container, ngay cả những người chưa từng làm việc với Docker nhiều. Tôi nhắc lại, Docker không phải về container.

Docker, theo tôi, là về tiêu chuẩn. Và thực tế có những tiêu chuẩn cho mọi thứ: tiêu chuẩn để xây dựng ứng dụng của bạn, tiêu chuẩn để cài đặt ứng dụng của bạn.

Yêu cầu để phát triển ứng dụng trong Kubernetes

Và thứ này - chúng tôi đã sử dụng nó trước đây, nó trở nên đặc biệt phổ biến với sự ra đời của vùng chứa - thứ này được gọi là biến ENV (môi trường), tức là các biến môi trường có trong hệ điều hành của bạn. Đây thường là một cách lý tưởng để định cấu hình ứng dụng của bạn, bởi vì nếu bạn có các ứng dụng bằng JAVA, Python, Go, Perl, Chúa cấm và tất cả chúng đều có thể đọc máy chủ cơ sở dữ liệu, người dùng cơ sở dữ liệu, các biến mật khẩu cơ sở dữ liệu thì đó là điều lý tưởng. Bạn có các ứng dụng bằng bốn ngôn ngữ khác nhau được định cấu hình trong gói cơ sở dữ liệu theo cùng một cách. Không có nhiều cấu hình khác nhau.

Mọi thứ đều có thể được cấu hình bằng các biến ENV. Khi nói về Kubernetes, có một cách tuyệt vời để khai báo các biến ENV ngay trong Deployment. Theo đó, nếu đang nói về dữ liệu bí mật thì chúng ta có thể đẩy ngay dữ liệu bí mật từ các biến ENV (mật khẩu đến cơ sở dữ liệu, v.v.) vào một bí mật, tạo một cụm bí mật và cho biết trong mô tả ENV trong Triển khai rằng chúng ta không khai báo trực tiếp giá trị của biến này và giá trị của biến mật khẩu cơ sở dữ liệu này sẽ được đọc từ bí mật. Đây là hành vi Kubernetes tiêu chuẩn. Và đây là lựa chọn lý tưởng nhất để cấu hình ứng dụng của bạn. Chỉ ở cấp độ mã, một lần nữa điều này áp dụng cho các nhà phát triển. Nếu bạn là DevOps, bạn có thể hỏi: “Các bạn, hãy dạy ứng dụng của bạn đọc các biến môi trường. Và tất cả chúng ta sẽ hạnh phúc.”

Nếu mọi người trong công ty đều đọc các biến môi trường được đặt tên giống nhau thì điều đó thật tuyệt. Vì vậy, điều đó không xảy ra khi một số đang đợi cơ sở dữ liệu postgres, những người khác đang đợi tên cơ sở dữ liệu, những người khác đang đợi cái gì khác, những người khác đang đợi một loại dbn nào đó, do đó, theo đó, có sự đồng nhất.

Vấn đề xảy ra khi bạn có quá nhiều biến môi trường đến mức bạn vừa mở Triển khai - và có năm trăm dòng biến môi trường. Trong trường hợp này, bạn chỉ đơn giản là đã vượt xa các biến môi trường - và bạn không cần phải hành hạ bản thân nữa. Trong trường hợp này, sẽ hợp lý hơn nếu bạn bắt đầu sử dụng configs. Tức là huấn luyện ứng dụng của bạn sử dụng cấu hình.

Câu hỏi duy nhất là cấu hình không như bạn nghĩ. Config.pi không phải là cấu hình thuận tiện để sử dụng. Hoặc một số cấu hình ở định dạng của riêng bạn, hoặc có năng khiếu - đây cũng không phải là cấu hình mà tôi muốn nói.

Điều tôi đang nói đến là cấu hình ở các định dạng có thể chấp nhận được, tức là cho đến nay, tiêu chuẩn phổ biến nhất là tiêu chuẩn .yaml. Cách đọc nó rõ ràng, con người có thể đọc được, cách đọc nó từ ứng dụng cũng rõ ràng.

Theo đó, ngoài YAML, chẳng hạn, bạn cũng có thể sử dụng JSON, việc phân tích cú pháp cũng thuận tiện như YAML về mặt đọc cấu hình ứng dụng từ đó. Nó đáng chú ý là bất tiện hơn cho mọi người để đọc. Bạn có thể thử định dạng này. Theo quan điểm của con người, nó khá thuận tiện để đọc, nhưng có thể bất tiện khi xử lý nó một cách tự động, theo nghĩa là nếu bạn muốn tạo cấu hình của riêng mình, định dạng ini có thể đã bất tiện khi tạo.

Nhưng trong mọi trường hợp, dù bạn chọn định dạng nào, vấn đề là theo quan điểm của Kubernetes thì nó rất thuận tiện. Bạn có thể đặt toàn bộ cấu hình của mình vào Kubernetes, trong ConfigMap. Sau đó, lấy sơ đồ cấu hình này và yêu cầu gắn nó bên trong nhóm của bạn vào một thư mục cụ thể nào đó, nơi ứng dụng của bạn sẽ đọc cấu hình từ sơ đồ cấu hình này như thể nó chỉ là một tệp. Trên thực tế, đây là điều nên làm khi bạn có nhiều tùy chọn cấu hình trong ứng dụng của mình. Hoặc nó chỉ là một loại cấu trúc phức tạp nào đó, có sự lồng ghép.

Nếu bạn có một sơ đồ cấu hình, thì bạn có thể hướng dẫn rất tốt cho ứng dụng của mình, chẳng hạn như cách tự động theo dõi các thay đổi trong tệp nơi gắn sơ đồ cấu hình và cũng tự động tải lại ứng dụng của bạn khi cấu hình thay đổi. Đây thường sẽ là một lựa chọn lý tưởng.

Một lần nữa, tôi đã nói về điều này - thông tin bí mật không có trong sơ đồ cấu hình, thông tin bí mật không có trong các biến, thông tin bí mật không có trong bí mật. Từ đó, kết nối thông tin bí mật này với ngoại giao. Thông thường chúng tôi lưu trữ tất cả các mô tả về đối tượng Kubernetes, quá trình triển khai, sơ đồ cấu hình, dịch vụ trong git. Theo đó, việc đặt mật khẩu vào cơ sở dữ liệu trong git, ngay cả khi đó là git của bạn, mà bạn có trong nội bộ công ty, là một ý tưởng tồi. Bởi vì, ở mức tối thiểu, git ghi nhớ mọi thứ và việc xóa mật khẩu khỏi đó không phải là điều dễ dàng như vậy.

Kiểm tra sức khỏe

Điểm tiếp theo là thứ được gọi là Kiểm tra sức khỏe. Nói chung, Kiểm tra tình trạng chỉ đơn giản là kiểm tra xem ứng dụng của bạn có hoạt động hay không. Đồng thời, chúng ta thường nói về một số ứng dụng web nhất định, theo đó, từ quan điểm kiểm tra tình trạng (tốt hơn là không dịch ở đây và xa hơn), đây sẽ là một số URL đặc biệt mà chúng xử lý như một tiêu chuẩn, họ thường làm /health.

Theo đó, khi truy cập URL này, ứng dụng của chúng tôi sẽ thông báo “có, được rồi, mọi thứ đều ổn với tôi, 200” hoặc “không, mọi thứ đều không ổn với tôi, khoảng 500”. Theo đó, nếu ứng dụng của chúng ta không phải là http, không phải là ứng dụng web, thì chúng ta đang nói về một loại daemon nào đó, chúng ta có thể tìm ra cách thực hiện kiểm tra tình trạng. Nghĩa là, không cần thiết, nếu ứng dụng không phải là http, thì mọi thứ sẽ hoạt động mà không cần kiểm tra tình trạng và điều này không thể thực hiện được bằng bất kỳ cách nào. Bạn có thể cập nhật định kỳ một số thông tin trong tệp, bạn có thể đưa ra một số lệnh đặc biệt cho trình nền của mình, như, daemon status, sẽ nói "vâng, mọi thứ đều ổn, daemon đang hoạt động, nó vẫn hoạt động."

Nó dùng để làm gì? Điều đầu tiên và rõ ràng nhất có lẽ là tại sao cần phải kiểm tra tình trạng - để hiểu rằng ứng dụng đang hoạt động. Ý tôi là, thật ngu ngốc, khi nó hoạt động bây giờ, có vẻ như nó đang hoạt động, nên bạn có thể chắc chắn rằng nó đang hoạt động. Và hóa ra là ứng dụng đang chạy, vùng chứa đang chạy, phiên bản đang hoạt động, mọi thứ đều ổn - và sau đó người dùng đã cắt tất cả các số điện thoại khỏi bộ phận hỗ trợ kỹ thuật và nói “bạn là gì..., bạn ngủ quên, chẳng có tác dụng gì cả.”

Kiểm tra tình trạng chỉ là một cách để xem nó có hoạt động theo quan điểm của người dùng hay không. Một trong những phương pháp. Hãy đặt nó theo cách này. Theo quan điểm của Kubernetes, đây cũng là một cách để hiểu thời điểm ứng dụng khởi động, bởi vì chúng tôi hiểu rằng có sự khác biệt giữa thời điểm container được khởi chạy, tạo và khởi động và khi ứng dụng được khởi chạy trực tiếp trong container này. Bởi vì nếu chúng ta lấy một số ứng dụng java trung bình và cố gắng khởi chạy nó trong dock, thì trong bốn mươi giây, thậm chí một phút, hoặc thậm chí mười phút, nó có thể khởi động bình thường. Trong trường hợp này, ít nhất bạn có thể gõ vào các cổng của nó, nó sẽ không trả lời ở đó, tức là nó chưa sẵn sàng nhận lưu lượng truy cập.

Một lần nữa, với sự trợ giúp của kiểm tra tình trạng và với sự trợ giúp của thực tế là chúng ta đang chuyển sang đây, chúng ta có thể hiểu trong Kubernetes rằng không chỉ vùng chứa đã tăng lên trong ứng dụng mà bản thân ứng dụng cũng đã khởi động, nó đã phản hồi với kiểm tra tình trạng, có nghĩa là chúng tôi có thể gửi lưu lượng truy cập đến đó.

Yêu cầu để phát triển ứng dụng trong Kubernetes

Điều tôi đang nói đến bây giờ được gọi là các bài kiểm tra Mức độ sẵn sàng/Tính sống động trong Kubernetes; theo đó, các bài kiểm tra mức độ sẵn sàng của chúng tôi chịu trách nhiệm về tính khả dụng của ứng dụng trong việc cân bằng. Nghĩa là, nếu các bài kiểm tra mức độ sẵn sàng được thực hiện trong ứng dụng thì mọi thứ đều ổn, lưu lượng truy cập của máy khách sẽ đến ứng dụng. Nếu các bài kiểm tra mức độ sẵn sàng không được thực hiện thì ứng dụng sẽ không tham gia, phiên bản cụ thể này không tham gia vào quá trình cân bằng, nó sẽ bị loại khỏi quá trình cân bằng và lưu lượng máy khách không được lưu chuyển. Theo đó, các bài kiểm tra Liveness trong Kubernetes là cần thiết để nếu ứng dụng bị kẹt thì có thể khởi động lại. Nếu kiểm tra mức độ hoạt động không hoạt động đối với một ứng dụng được khai báo trong Kubernetes thì ứng dụng đó không chỉ bị xóa khỏi trạng thái cân bằng mà còn được khởi động lại.

Và đây là một điểm quan trọng mà tôi muốn đề cập: từ góc độ thực tế, bài kiểm tra mức độ sẵn sàng thường được sử dụng thường xuyên hơn và thường xuyên cần thiết hơn bài kiểm tra mức sống. Nghĩa là, chỉ đơn giản là tuyên bố một cách thiếu suy nghĩ cả các bài kiểm tra mức độ sẵn sàng và mức độ hoạt động, bởi vì Kubernetes có thể làm điều đó và hãy sử dụng mọi thứ nó có thể làm, không phải là một ý tưởng hay. Tôi sẽ giải thích tại sao. Bởi vì điểm thứ hai trong quá trình kiểm tra là bạn nên kiểm tra dịch vụ cơ bản trong quá trình kiểm tra sức khỏe của mình. Điều này có nghĩa là nếu bạn có một ứng dụng web cung cấp một số thông tin thì thông tin đó đương nhiên phải được lấy từ đâu đó. Trong cơ sở dữ liệu chẳng hạn. Chà, nó lưu thông tin có trong API REST này vào cùng một cơ sở dữ liệu. Sau đó, theo đó, nếu kiểm tra tình trạng của bạn phản hồi giống như đã liên hệ vớilashhealth, ứng dụng sẽ thông báo “200, được rồi, mọi thứ đều ổn” và đồng thời cơ sở dữ liệu của ứng dụng của bạn không thể truy cập được và ứng dụng kiểm tra sức khỏe cho biết “200, được rồi, mọi thứ đều ổn ” - Đây là một cuộc kiểm tra sức khỏe tồi tệ. Đây không phải là cách nó nên hoạt động.

Tức là, ứng dụng của bạn, khi có yêu cầu đến với nó /health, nó không chỉ phản hồi, “200, ok”, chẳng hạn, trước tiên nó đi đến cơ sở dữ liệu, cố gắng kết nối với nó, thực hiện một số việc rất cơ bản ở đó, chẳng hạn như chọn một, chỉ kiểm tra xem có kết nối nào trong cơ sở dữ liệu và bạn có thể truy vấn cơ sở dữ liệu. Nếu tất cả điều này thành công thì câu trả lời là “200, được thôi”. Nếu không thành công thì báo lỗi, cơ sở dữ liệu không khả dụng.

Do đó, về vấn đề này, tôi một lần nữa quay trở lại các bài kiểm tra Mức độ sẵn sàng/Tính sống động - tại sao rất có thể bạn cần một bài kiểm tra mức độ sẵn sàng, nhưng lại có vấn đề về bài kiểm tra mức độ sẵn sàng. Bởi vì nếu bạn mô tả các bài kiểm tra sức khỏe chính xác như tôi vừa nói thì hóa ra nó không có trong phần instanceв или со всех instancetrong cơ sở dữ liệu chẳng hạn. Khi bạn tuyên bố kiểm tra mức độ sẵn sàng, các cuộc kiểm tra tình trạng của chúng tôi bắt đầu thất bại và theo đó, tất cả các ứng dụng mà cơ sở dữ liệu không thể truy cập được, chúng chỉ đơn giản là bị tắt để cân bằng và trên thực tế, chỉ “treo” ở trạng thái bị bỏ quên và chờ cơ sở dữ liệu của chúng hoạt động trở lại. công việc.

Nếu chúng ta đã khai báo kiểm tra mức độ hoạt động, thì hãy tưởng tượng, cơ sở dữ liệu của chúng ta đã bị hỏng và trong Kubernetes của bạn, một nửa mọi thứ bắt đầu khởi động lại do kiểm tra mức độ hoạt động không thành công. Điều này có nghĩa là bạn cần phải khởi động lại. Đây hoàn toàn không phải là điều bạn muốn, tôi thậm chí còn có kinh nghiệm cá nhân trong thực tế. Chúng tôi có một ứng dụng trò chuyện được viết bằng JS và được đưa vào cơ sở dữ liệu Mongo. Và vấn đề là khi bắt đầu làm việc với Kubernetes, chúng tôi đã mô tả mức độ sẵn sàng, tính sống động của các thử nghiệm theo nguyên tắc Kubernetes có thể làm được nên chúng tôi sẽ sử dụng nó. Theo đó, đến một lúc nào đó Mongo trở nên hơi “buồn tẻ” và mẫu bắt đầu thất bại. Theo đó, theo thử nghiệm độ mưa, vỏ quả bắt đầu “tiêu diệt”.

Như bạn hiểu, khi họ “bị giết”, đây là một cuộc trò chuyện, tức là có rất nhiều kết nối từ khách hàng đang treo trên đó. Họ cũng bị "giết" - không, không phải khách hàng, chỉ các kết nối - không phải tất cả cùng một lúc, và do thực tế là họ không bị giết cùng lúc, một số sớm hơn, một số muộn hơn, nên họ không bắt đầu giống nhau thời gian. Cộng với tiêu chuẩn ngẫu nhiên, chúng tôi không thể dự đoán với độ chính xác đến mili giây về thời gian bắt đầu của ứng dụng mỗi lần, vì vậy họ thực hiện từng trường hợp một. Một infospot tăng lên, được thêm vào để cân bằng, tất cả khách hàng đến đó, nó không thể chịu được tải trọng như vậy, bởi vì nó chỉ có một mình, và nói một cách đại khái, có hàng tá người trong số họ đang làm việc ở đó, và nó rơi xuống. Người tiếp theo đứng dậy, toàn bộ gánh nặng đổ lên người anh ta, anh ta cũng ngã. Chà, những thác nước này cứ tiếp tục đổ xuống. Cuối cùng, vấn đề này đã được giải quyết như thế nào - chúng tôi chỉ cần dừng nghiêm ngặt lưu lượng truy cập của người dùng vào ứng dụng này, để tất cả các phiên bản tăng lên và sau đó bắt đầu tất cả lưu lượng truy cập của người dùng cùng một lúc để nó được phân bổ cho tất cả mười phiên bản.

Nếu không có bài kiểm tra độ sống này được công bố, điều này sẽ buộc tất cả phải khởi động lại, thì ứng dụng đã có thể xử lý nó tốt. Nhưng mọi thứ từ việc cân bằng đều bị vô hiệu hóa đối với chúng tôi, vì cơ sở dữ liệu không thể truy cập được và tất cả người dùng đều đã “thất vọng”. Sau đó, khi cơ sở dữ liệu này có sẵn, mọi thứ sẽ được đưa vào cân bằng, nhưng các ứng dụng không cần phải khởi động lại và không cần lãng phí thời gian và tài nguyên cho việc này. Tất cả họ đều đã ở đây, họ đã sẵn sàng cho giao thông, vì vậy giao thông vẫn mở, mọi thứ đều ổn - ứng dụng đã sẵn sàng, mọi thứ vẫn tiếp tục hoạt động.

Do đó, các bài kiểm tra mức độ sẵn sàng và khả năng hoạt động là khác nhau, thậm chí hơn nữa, về mặt lý thuyết, bạn có thể thực hiện các bài kiểm tra sức khỏe khác nhau, chẳng hạn như một loại bán kính, một loại liv và kiểm tra những thứ khác nhau. Trong quá trình kiểm tra mức độ sẵn sàng, hãy kiểm tra phần phụ trợ của bạn. Và chẳng hạn, trong bài kiểm tra mức độ hoạt động, bạn không kiểm tra theo quan điểm rằng bài kiểm tra mức độ hoạt động nói chung chỉ là một ứng dụng phản hồi, liệu nó có thể phản hồi hay không.

Bởi vì nhìn chung, bài kiểm tra về mức độ sống động là khi chúng ta “mắc kẹt”. Một vòng lặp vô tận đã bắt đầu hoặc điều gì đó khác - và không có yêu cầu nào được xử lý nữa. Do đó, việc tách chúng ra cũng có ý nghĩa - và triển khai logic khác nhau trong chúng.

Về những gì bạn cần trả lời khi đi khám, khi khám sức khỏe. Đó thực sự chỉ là một nỗi đau. Những người quen thuộc với điều này có thể sẽ cười - nhưng nghiêm túc mà nói, tôi đã từng thấy các dịch vụ trong đời trả lời “200” trong XNUMX% trường hợp. Tức là ai thành công. Nhưng đồng thời, trong phần nội dung phản hồi, họ viết “lỗi như vậy và lỗi như vậy”.

Tức là trạng thái phản hồi sẽ đến với bạn - mọi thứ đều thành công. Nhưng đồng thời, bạn phải phân tích nội dung, vì nội dung nói "xin lỗi, yêu cầu đã kết thúc có lỗi" và đây chỉ là thực tế. Tôi đã nhìn thấy điều này trong cuộc sống thực.

Và để một số người không thấy buồn cười và những người khác lại thấy rất đau đớn, bạn vẫn nên tuân thủ một quy tắc đơn giản. Trong kiểm tra tình trạng và về nguyên tắc khi làm việc với các ứng dụng web.

Nếu mọi việc suôn sẻ thì hãy trả lời bằng câu trả lời thứ hai trăm. Về nguyên tắc, bất kỳ câu trả lời thứ hai trăm nào cũng phù hợp với bạn. Nếu bạn đọc rất tốt và biết rằng một số trạng thái phản hồi khác với các trạng thái khác, hãy trả lời bằng những trạng thái thích hợp: 204, 5, 10, 15, bất cứ điều gì. Nếu không tốt lắm thì chỉ là “hai không không”. Nếu mọi thứ trở nên tồi tệ và kiểm tra sức khỏe không phản hồi, thì hãy trả lời bằng năm phần trăm bất kỳ. Một lần nữa, nếu bạn hiểu cách phản hồi, các trạng thái phản hồi khác nhau như thế nào. Nếu bạn không hiểu thì 502 là lựa chọn để bạn phản hồi các cuộc kiểm tra tình trạng nếu có sự cố xảy ra.

Đây là một điểm khác, tôi muốn quay lại một chút về việc kiểm tra các dịch vụ cơ bản. Ví dụ: nếu bạn bắt đầu kiểm tra tất cả các dịch vụ cơ bản đằng sau ứng dụng của mình - mọi thứ nói chung. Những gì chúng tôi nhận được từ quan điểm của kiến ​​​​trúc microservice, chúng tôi có một khái niệm như “khớp nối thấp” - nghĩa là khi các dịch vụ của bạn phụ thuộc tối thiểu vào nhau. Nếu một trong số chúng bị lỗi, tất cả những cái khác không có chức năng này sẽ tiếp tục hoạt động. Một số chức năng không hoạt động. Theo đó, nếu bạn liên kết tất cả các cuộc kiểm tra tình trạng với nhau, thì bạn sẽ kết thúc với một thứ rơi vào cơ sở hạ tầng và vì nó bị rơi, tất cả các cuộc kiểm tra tình trạng của tất cả các dịch vụ cũng bắt đầu thất bại - và nói chung có nhiều cơ sở hạ tầng hơn cho toàn bộ kiến ​​trúc microservice Mọi thứ trở nên tối tăm ở đó.

Do đó, tôi muốn nhắc lại điều này một lần nữa rằng bạn cần kiểm tra các dịch vụ cơ bản, những dịch vụ mà không có ứng dụng của bạn trong một trăm phần trăm trường hợp không thể thực hiện được công việc của nó. Nghĩa là, điều hợp lý là nếu bạn có API REST mà qua đó người dùng lưu vào cơ sở dữ liệu hoặc truy xuất từ ​​cơ sở dữ liệu, thì khi không có cơ sở dữ liệu, bạn không thể đảm bảo công việc với người dùng của mình.

Nhưng nếu người dùng của bạn, khi bạn đưa họ ra khỏi cơ sở dữ liệu, được bổ sung thêm một số siêu dữ liệu khác, từ một chương trình phụ trợ khác mà bạn nhập trước khi gửi phản hồi đến giao diện người dùng - và chương trình phụ trợ này không khả dụng, điều này có nghĩa là bạn cung cấp cho câu trả lời mà không có bất kỳ phần nào của siêu dữ liệu.

Tiếp theo, chúng ta cũng gặp phải một trong những vấn đề nhức nhối khi khởi chạy ứng dụng.

Trên thực tế, điều này không chỉ áp dụng cho Kubernetes nói chung; nó còn xảy ra khi văn hóa của một số loại hình phát triển đại chúng và DevOps nói riêng bắt đầu lan rộng cùng thời với Kubernetes. Do đó, nhìn chung, hóa ra là bạn cần phải tắt ứng dụng của mình một cách nhẹ nhàng mà không cần Kubernetes. Ngay cả trước Kubernetes, mọi người đã làm điều này, nhưng với sự ra đời của Kubernetes, chúng tôi đã bắt đầu nói về nó hàng loạt.

Tắt máy duyên dáng

Nói chung, Graceful Shutdown là gì và tại sao lại cần thiết? Đây là khi ứng dụng của bạn gặp sự cố vì lý do nào đó, bạn cần phải làm app stop - hoặc bạn nhận được, chẳng hạn như tín hiệu từ hệ điều hành, ứng dụng của bạn phải hiểu tín hiệu đó và thực hiện điều gì đó với tín hiệu đó. Tất nhiên, trường hợp xấu nhất là khi ứng dụng của bạn nhận được SIGTERM và giống như “SIGTERM, hãy tiếp tục, làm việc, không làm gì cả”. Đây là một lựa chọn hết sức tồi tệ.

Yêu cầu để phát triển ứng dụng trong Kubernetes

Một lựa chọn gần như tệ không kém là khi ứng dụng của bạn nhận được SIGTERM và kiểu như “họ nói segterm, nghĩa là chúng ta sắp kết thúc, tôi chưa thấy, tôi không biết bất kỳ yêu cầu nào của người dùng, tôi không biết loại nào những yêu cầu tôi đang thực hiện, họ nói SIGTERM, điều đó có nghĩa là chúng tôi sắp kết thúc " Đây cũng là một lựa chọn tồi.

Lựa chọn nào là tốt? Điểm đầu tiên là phải tính đến việc hoàn thành các hoạt động. Một lựa chọn tốt là máy chủ của bạn vẫn tính đến những gì nó sẽ làm nếu nhận được SIGTERM.

SIGTERM là một tắt máy mềm, nó được thiết kế đặc biệt, nó có thể bị chặn ở cấp độ mã, nó có thể được xử lý, nói rằng bây giờ, chờ đã, trước tiên chúng ta sẽ hoàn thành công việc mà chúng ta có, sau đó chúng ta sẽ thoát ra.

Từ góc độ Kubernetes, nó trông như thế này. Khi chúng tôi nói với một nhóm đang chạy trong cụm Kubernetes, “vui lòng dừng lại, biến đi” hoặc chúng tôi được khởi động lại hoặc có bản cập nhật xảy ra khi Kubernetes tạo lại các nhóm, Kubernetes chỉ gửi cùng một thông báo SIGTERM đến nhóm đó, đợi đôi khi, và đây là lúc anh ấy chờ đợi, nó cũng được cấu hình, có một tham số đặc biệt như vậy trong các bằng cấp và nó được gọi là Graceful ShutdownTimeout. Như bạn hiểu, nó được gọi như vậy không phải là vô ích, và bây giờ chúng ta đang nói về nó không phải là vô ích.

Ở đó, chúng tôi có thể cho biết cụ thể chúng tôi cần đợi bao lâu kể từ thời điểm chúng tôi gửi SIGTERM đến ứng dụng và khi chúng tôi hiểu rằng ứng dụng dường như đã phát điên vì điều gì đó hoặc bị “kẹt” và sẽ không kết thúc - và chúng tôi cần phải gửi nó SIGKILL, tức là khó hoàn thành công việc của nó. Nghĩa là, theo đó, chúng ta có một số loại daemon đang chạy, nó xử lý các hoạt động. Chúng tôi hiểu rằng trung bình các hoạt động của chúng tôi mà daemon hoạt động không kéo dài quá 30 giây mỗi lần. Theo đó, khi SIGTERM đến, chúng tôi hiểu rằng daemon của chúng tôi có thể hoàn thành tối đa 30 giây sau SIGTERM. Ví dụ: chúng tôi viết nó là 45 giây để đề phòng và nói rằng SIGTERM. Sau đó chúng tôi đợi 45 giây. Về lý thuyết, trong thời gian này con quỷ lẽ ra đã hoàn thành công việc của mình và tự kết thúc. Nhưng nếu đột nhiên nó không thể, điều đó có nghĩa là rất có thể nó đã bị kẹt—nó không còn xử lý các yêu cầu của chúng tôi một cách bình thường nữa. Và trên thực tế, trong 45 giây, bạn có thể hạ gục anh ta một cách an toàn.

Và ở đây, trên thực tế, thậm chí có thể tính đến 2 khía cạnh. Đầu tiên, hãy hiểu rằng nếu bạn nhận được một yêu cầu, bạn đã bắt đầu làm việc với nó bằng cách nào đó và không đưa ra phản hồi cho người dùng, nhưng bạn đã nhận được SIGTERM chẳng hạn. Việc tinh chỉnh nó và đưa ra câu trả lời cho người dùng là điều hợp lý. Đây là điểm số một trong vấn đề này. Điểm thứ hai ở đây là nếu bạn viết ứng dụng của riêng mình, thường xây dựng kiến ​​trúc theo cách mà bạn nhận được yêu cầu cho ứng dụng của mình, sau đó bạn bắt đầu một số công việc, bắt đầu tải xuống các tệp từ đâu đó, tải xuống cơ sở dữ liệu, v.v. Cái đó. Nói chung, người dùng của bạn, yêu cầu của bạn bị treo trong nửa giờ và chờ bạn trả lời anh ta - khi đó, rất có thể, bạn cần phải làm việc trên kiến ​​​​trúc. Nghĩa là, chỉ cần tính đến cảm giác thông thường rằng nếu thao tác của bạn ngắn thì việc bỏ qua SIGTERM và sửa đổi nó là điều hợp lý. Nếu hoạt động của bạn kéo dài thì việc bỏ qua SIGTERM trong trường hợp này sẽ không có ý nghĩa gì. Việc thiết kế lại kiến ​​trúc để tránh những hoạt động kéo dài như vậy là điều hợp lý. Để người dùng không chỉ loanh quanh và chờ đợi. Tôi không biết, hãy tạo một số loại websocket ở đó, tạo các móc nối ngược mà máy chủ của bạn sẽ gửi cho khách hàng, bất cứ điều gì khác, nhưng đừng buộc người dùng treo trong nửa giờ và chỉ đợi một phiên cho đến khi bạn trả lời anh ấy. Bởi vì không thể đoán trước được nó có thể bị hỏng ở đâu.

Khi ứng dụng của bạn chấm dứt, bạn nên cung cấp một số mã thoát thích hợp. Nghĩa là, nếu ứng dụng của bạn được yêu cầu đóng, dừng và nó có thể tự dừng bình thường, thì bạn không cần phải trả lại một số loại mã thoát 1,5,255, v.v. Tôi chắc chắn rằng bất cứ điều gì không phải là mã 0, ít nhất là trong hệ thống Linux, đều bị coi là không thành công. Nghĩa là, ứng dụng của bạn trong trường hợp này được coi là đã kết thúc có lỗi. Theo đó, một cách thân thiện, nếu ứng dụng của bạn hoàn thành mà không có lỗi, bạn sẽ nói 0 ở đầu ra. Nếu ứng dụng của bạn bị lỗi vì lý do nào đó, bạn sẽ nói khác XNUMX ở đầu ra. Và bạn có thể làm việc với thông tin này.

Và lựa chọn cuối cùng. Thật tệ khi người dùng của bạn gửi yêu cầu và bị treo trong nửa giờ trong khi bạn xử lý nó. Nhưng nói chung, tôi cũng muốn nói về những gì nhìn chung có giá trị từ phía khách hàng. Sẽ không thành vấn đề nếu bạn có ứng dụng di động, giao diện người dùng, v.v. Cần phải tính đến việc nhìn chung phiên của người dùng có thể bị chấm dứt, bất cứ điều gì cũng có thể xảy ra. Ví dụ: một yêu cầu có thể được gửi đi nhưng chưa được xử lý và không có phản hồi nào được trả lại. Giao diện người dùng hoặc ứng dụng di động của bạn - bất kỳ giao diện người dùng nào nói chung, hãy nói theo cách đó - nên tính đến điều này. Nếu bạn làm việc với websockets, đây thường là nỗi đau tồi tệ nhất mà tôi từng gặp phải.

Khi các nhà phát triển của một số cuộc trò chuyện thông thường không biết điều đó, hóa ra websocket có thể bị hỏng. Đối với họ, khi có điều gì đó xảy ra ở proxy, chúng tôi chỉ cần thay đổi cấu hình và nó sẽ tải lại. Đương nhiên, tất cả các phiên kéo dài đều bị xé nát trong trường hợp này. Các nhà phát triển chạy đến chỗ chúng tôi và nói: "Các bạn đang làm gì vậy, cuộc trò chuyện với tất cả khách hàng của chúng tôi đã bị hỏng!" Chúng tôi nói với họ: “Bạn đang làm gì vậy? Khách hàng của bạn không thể kết nối lại? Họ nói: “Không, chúng tôi cần các phiên họp không bị rách nát.” Nói tóm lại, điều này thực sự là vô nghĩa. Phía khách hàng cần phải được tính đến. Đặc biệt, như tôi đã nói, với các phiên tồn tại lâu dài như websockets, nó có thể bị hỏng và người dùng không nhận thấy, bạn cần có khả năng cài đặt lại các phiên như vậy. Và sau đó mọi thứ đều hoàn hảo.

Tài nguyên

Thật ra ở đây tôi chỉ kể cho bạn một câu chuyện thẳng thắn. Một lần nữa từ cuộc sống thực. Điều bệnh hoạn nhất tôi từng nghe về tài nguyên.

Ý tôi là, tài nguyên trong trường hợp này là một số loại yêu cầu, giới hạn mà bạn có thể đặt trên các nhóm trong cụm Kubernetes của mình. Điều buồn cười nhất mà tôi nghe được từ một nhà phát triển... Một trong những nhà phát triển đồng nghiệp của tôi ở nơi làm việc trước đây đã từng nói: “Ứng dụng của tôi không khởi động được trong cụm.” Tôi nhìn thì thấy nó chưa bắt đầu, nhưng hoặc là nó không phù hợp với nguồn lực hoặc họ đã đặt ra những giới hạn rất nhỏ. Nói tóm lại, ứng dụng không thể khởi động do tài nguyên. Tôi nói: “Nó sẽ không bắt đầu do nguồn lực, bạn quyết định số tiền bạn cần và đặt ra một giá trị phù hợp.” Anh ấy nói: "Loại tài nguyên nào?" Tôi bắt đầu giải thích với anh ấy rằng Kubernetes, các giới hạn về yêu cầu và blah, blah, blah cần phải được thiết lập. Người đàn ông lắng nghe năm phút, gật đầu và nói: “Tôi đến đây với tư cách là nhà phát triển, tôi không muốn biết gì về bất kỳ tài nguyên nào. Tôi đến đây để viết mã và thế là xong.” Thật là buồn. Đây là một khái niệm rất đáng buồn theo quan điểm của nhà phát triển. Có thể nói, đặc biệt là trong thế giới hiện đại của những người sùng đạo tiến bộ.

Tại sao lại cần nguồn lực? Có 2 loại tài nguyên trong Kubernetes. Một số được gọi là yêu cầu, một số khác được gọi là giới hạn. Bằng nguồn lực, chúng ta sẽ hiểu rằng về cơ bản luôn chỉ có hai hạn chế cơ bản. Tức là giới hạn thời gian CPU và giới hạn RAM cho một container chạy trong Kubernetes.

Giới hạn đặt giới hạn trên về cách sử dụng tài nguyên trong ứng dụng của bạn. Theo đó, nếu bạn nói giới hạn là 1GB RAM thì ứng dụng của bạn sẽ không thể sử dụng nhiều hơn 1GB RAM. Và nếu anh ta đột nhiên muốn và cố gắng làm điều này, thì một quá trình được gọi là oom Killer, hết bộ nhớ, tức là sẽ đến và giết ứng dụng của bạn - tức là nó sẽ chỉ khởi động lại. Các ứng dụng sẽ không khởi động lại dựa trên CPU. Về mặt CPU, nếu một ứng dụng cố gắng sử dụng nhiều, nhiều hơn mức quy định trong giới hạn, CPU sẽ đơn giản được lựa chọn nghiêm ngặt. Điều này không dẫn đến khởi động lại. Đây là giới hạn - đây là giới hạn trên.

Và có một yêu cầu. Yêu cầu là cách Kubernetes hiểu cách các nút trong cụm Kubernetes của bạn được điền vào các ứng dụng. Nghĩa là, yêu cầu là một loại cam kết của ứng dụng của bạn. Nó nói những gì tôi muốn sử dụng: "Tôi muốn bạn dành nhiều CPU và nhiều bộ nhớ này cho tôi." Một sự tương tự đơn giản như vậy. Điều gì sẽ xảy ra nếu chúng ta có một nút có tổng cộng 8 CPU, tôi không biết. Và một nhóm đến đó, yêu cầu của họ là 1 CPU, nghĩa là nút còn lại 7 CPU. Theo đó, ngay sau khi 8 nhóm đến nút này, mỗi nhóm có 1 CPU trong yêu cầu của chúng, nút đó, như thể theo quan điểm của Kubernetes, đã hết CPU và không thể có thêm nhóm có yêu cầu nữa. được khởi chạy trên nút này. Nếu tất cả các nút hết CPU thì Kubernetes sẽ bắt đầu thông báo rằng không có nút nào phù hợp trong cụm để chạy nhóm của bạn vì CPU đã hết.

Tại sao lại cần có yêu cầu và tại sao không có yêu cầu, tôi nghĩ không cần khởi chạy bất cứ thứ gì trong Kubernetes? Hãy tưởng tượng một tình huống giả định. Bạn khởi chạy ứng dụng của mình mà không có yêu cầu, Kubernetes không biết bạn có bao nhiêu thứ, bạn có thể đẩy nó tới những nút nào. Chà, anh ta đẩy, đẩy, đẩy vào các nút. Tại một thời điểm nào đó, bạn sẽ bắt đầu nhận được lưu lượng truy cập vào ứng dụng của mình. Và một trong những ứng dụng đột nhiên bắt đầu sử dụng tài nguyên đến giới hạn mà nó có theo giới hạn. Hóa ra có một ứng dụng khác ở gần đó và nó cũng cần tài nguyên. Nút thực sự bắt đầu cạn kiệt tài nguyên, chẳng hạn như OP. Nút thực sự bắt đầu cạn kiệt tài nguyên về mặt vật lý, chẳng hạn như bộ nhớ truy cập ngẫu nhiên (RAM). Khi một nút hết điện, trước hết docker sẽ ngừng phản hồi, sau đó là khối lập phương, sau đó là HĐH. Đơn giản là họ sẽ bất tỉnh và MỌI THỨ chắc chắn sẽ ngừng hoạt động đối với bạn. Nghĩa là, điều này sẽ dẫn đến nút của bạn bị kẹt và bạn sẽ cần phải khởi động lại nó. Tóm lại tình hình không ổn lắm.

Và khi bạn có các yêu cầu, các giới hạn không khác nhau nhiều, ít nhất là không gấp nhiều lần so với các giới hạn hoặc yêu cầu, thì bạn có thể lấp đầy các ứng dụng một cách bình thường, hợp lý trên các nút của cụm Kubernetes. Đồng thời, Kubernetes gần như nhận thức được bao nhiêu thứ được đặt ở đâu, bao nhiêu thứ được sử dụng ở đâu. Đó chỉ là một khoảnh khắc như vậy. Điều quan trọng là phải hiểu nó. Và điều quan trọng là phải kiểm soát rằng điều này được chỉ định.

Lưu trữ dữ liệu

Điểm tiếp theo của chúng tôi là về lưu trữ dữ liệu. Phải làm gì với chúng và nói chung, phải làm gì với sự kiên trì trong Kubernetes?

Tôi nghĩ, một lần nữa, trong vòng của chúng tôi Trường học buổi tối, có một chủ đề về cơ sở dữ liệu trong Kubernetes. Và đối với tôi, có vẻ như tôi thậm chí còn biết đại khái những gì đồng nghiệp của bạn đã nói với bạn khi được hỏi: “Có thể chạy cơ sở dữ liệu trong Kubernetes không?” Vì lý do nào đó, đối với tôi, có vẻ như đồng nghiệp của bạn lẽ ra phải nói với bạn rằng nếu bạn đặt câu hỏi liệu có thể chạy cơ sở dữ liệu trong Kubernetes hay không thì điều đó là không thể.

Logic ở đây rất đơn giản. Để đề phòng, tôi sẽ giải thích một lần nữa, nếu bạn là một người thực sự tuyệt vời, người có thể xây dựng một hệ thống lưu trữ mạng phân tán khá có khả năng chịu lỗi, hãy hiểu cách đưa cơ sở dữ liệu vào trường hợp này, cách thức hoạt động của đám mây gốc trong vùng chứa trong cơ sở dữ liệu nói chung. Rất có thể, bạn không có câu hỏi nào về cách chạy nó. Nếu bạn có một câu hỏi như vậy và bạn muốn chắc chắn rằng tất cả sẽ diễn ra và đứng vững trong quá trình sản xuất và không bao giờ gục ngã, thì điều này sẽ không xảy ra. Bạn chắc chắn sẽ tự bắn vào chân mình bằng cách tiếp cận này. Vì vậy tốt hơn là không nên làm vậy.

Chúng ta nên làm gì với dữ liệu mà ứng dụng của chúng ta muốn lưu trữ, một số hình ảnh mà người dùng tải lên, một số thứ mà ứng dụng của chúng ta tạo ra trong quá trình hoạt động, chẳng hạn như khi khởi động? Phải làm gì với chúng trong Kubernetes?

Nói chung, tất nhiên, lý tưởng nhất là Kubernetes được thiết kế rất tốt và ban đầu thường được hình thành cho các ứng dụng không trạng thái. Tức là đối với những ứng dụng hoàn toàn không lưu trữ thông tin. Điều này thật lý tưởng.

Nhưng tất nhiên, lựa chọn lý tưởng không phải lúc nào cũng tồn tại. Vậy thì sao? Điểm đầu tiên và đơn giản nhất là lấy một loại S3 nào đó, không phải loại tự sản xuất, cũng không rõ hoạt động như thế nào mà là của một số nhà cung cấp. Một nhà cung cấp tốt, bình thường - và dạy ứng dụng của bạn sử dụng S3. Nghĩa là, khi người dùng của bạn muốn tải tệp lên, hãy nói "ở đây, vui lòng tải tệp đó lên S3." Khi anh ấy muốn nhận, hãy nói: “Đây là liên kết tới S3 và lấy nó từ đây”. Điều này thật lý tưởng.

Nếu đột nhiên vì lý do nào đó, tùy chọn lý tưởng này không phù hợp, bạn có một ứng dụng mà bạn không viết, bạn không phát triển hoặc nó là một loại di sản khủng khiếp nào đó, nó không thể sử dụng giao thức S3 mà phải hoạt động với các thư mục cục bộ trong các thư mục cục bộ. Hãy làm một điều gì đó đơn giản hơn hoặc ít hơn, triển khai Kubernetes. Có nghĩa là, đối với tôi, có vẻ như ngay lập tức bảo vệ Ceph cho một số nhiệm vụ tối thiểu là một ý tưởng tồi. Bởi vì Ceph tất nhiên là tốt và thời trang. Nhưng nếu bạn không thực sự hiểu mình đang làm gì thì khi bạn đặt thứ gì đó lên Ceph, bạn có thể rất dễ dàng và đơn giản là không bao giờ lấy nó ra khỏi đó nữa. Bởi vì, như bạn đã biết, Ceph lưu trữ dữ liệu trong cụm của nó ở dạng nhị phân chứ không phải ở dạng tệp đơn giản. Do đó, nếu đột nhiên cụm Ceph bị hỏng, thì khả năng cao là bạn sẽ không bao giờ lấy được dữ liệu của mình từ đó nữa.

Chúng tôi sẽ có một khóa học về Ceph, bạn có thể làm quen với chương trình và nộp đơn.

Vì vậy, tốt hơn hết bạn nên làm điều gì đó đơn giản như máy chủ NFS. Kubernetes có thể hoạt động với chúng, bạn có thể gắn một thư mục vào máy chủ NFS - ứng dụng của bạn cũng giống như một thư mục cục bộ. Đồng thời, tất nhiên, bạn cần phải hiểu rằng, một lần nữa, bạn cần phải làm điều gì đó với NFS của mình, bạn cần hiểu rằng đôi khi nó có thể không truy cập được và xem xét câu hỏi bạn sẽ làm gì trong trường hợp này. Có lẽ nó nên được sao lưu ở đâu đó trên một máy riêng.

Điểm tiếp theo tôi đã nói đến là phải làm gì nếu ứng dụng của bạn tạo ra một số tệp trong quá trình hoạt động. Ví dụ: khi khởi động, nó tạo ra một số tệp tĩnh dựa trên một số thông tin mà ứng dụng chỉ nhận được tại thời điểm khởi chạy. Thật là một khoảnh khắc. Nếu không có nhiều dữ liệu như vậy thì bạn không cần phải bận tâm chút nào, chỉ cần cài đặt ứng dụng này cho mình và làm việc. Câu hỏi duy nhất ở đây là cái gì, nhìn này. Rất thường xuyên, tất cả các loại hệ thống cũ, chẳng hạn như WordPress, v.v., đặc biệt là với một số loại plugin xảo quyệt đã được sửa đổi, các nhà phát triển PHP xảo quyệt, họ thường biết cách tạo ra một loại tệp nào đó cho riêng mình. Theo đó, một tệp tạo một tệp, tệp thứ hai tạo tệp thứ hai. Họ khác nhau. Cân bằng xảy ra trong cụm Kubernetes của khách hàng một cách tình cờ. Theo đó, hóa ra là họ không biết cách làm việc cùng nhau. Một cái cung cấp một thông tin, cái kia cung cấp cho người dùng một thông tin khác. Đây là điều bạn nên tránh. Nghĩa là, trong Kubernetes, mọi thứ bạn khởi chạy đều được đảm bảo có thể hoạt động trong nhiều phiên bản. Bởi vì Kubernetes là một thứ chuyển động. Theo đó, anh ta có thể di chuyển bất cứ thứ gì, bất cứ khi nào anh ta muốn mà không cần hỏi ý kiến ​​ai cả. Vì vậy, bạn cần phải tính đến điều này. Mọi thứ được khởi chạy trong một phiên bản sớm hay muộn sẽ thất bại. Bạn càng có nhiều đặt chỗ thì càng tốt. Nhưng một lần nữa, tôi nói lại, nếu bạn có một vài tập tin như vậy, thì bạn có thể đặt chúng ngay bên dưới mình, chúng nặng một lượng nhỏ. Nếu có nhiều hơn một chút, có lẽ bạn không nên đẩy chúng vào trong thùng chứa.

Tôi khuyên rằng có một điều tuyệt vời như vậy ở Kubernetes, bạn có thể sử dụng âm lượng. Đặc biệt, có một ổ đĩa kiểu trống dir. Nghĩa là, Kubernetes sẽ tự động tạo một thư mục trong các thư mục dịch vụ của nó trên máy chủ nơi bạn đã bắt đầu. Và anh ấy sẽ đưa nó cho bạn để bạn có thể sử dụng nó. Chỉ có một điểm quan trọng. Nghĩa là, dữ liệu của bạn sẽ không được lưu trữ bên trong vùng chứa mà trên máy chủ mà bạn đang chạy. Hơn nữa, Kubernetes có thể kiểm soát các thư mục trống như vậy trong cấu hình bình thường và có thể kiểm soát kích thước tối đa của chúng và không cho phép vượt quá. Điểm duy nhất là những gì bạn đã viết trong thư mục trống sẽ không bị mất trong quá trình khởi động lại nhóm. Nghĩa là, nếu nhóm của bạn vô tình rơi và tăng trở lại, thông tin trong thư mục trống sẽ không đi đâu cả. Anh ấy có thể sử dụng lại nó khi bắt đầu lại - và điều đó thật tốt. Nếu nhóm của bạn rời đi đâu đó, thì đương nhiên anh ta sẽ rời đi mà không có dữ liệu. Tức là, ngay khi nhóm từ nút nơi nó được khởi chạy với thư mục trống biến mất, thư mục trống sẽ bị xóa.

Còn điều gì tốt về thư mục trống? Ví dụ: nó có thể được sử dụng làm bộ đệm. Hãy tưởng tượng rằng ứng dụng của chúng ta tạo ra thứ gì đó một cách nhanh chóng, cung cấp nó cho người dùng và thực hiện nó trong một thời gian dài. Do đó, ví dụ, ứng dụng sẽ tạo và cung cấp nó cho người dùng, đồng thời lưu trữ nó ở đâu đó, để lần sau khi người dùng đến làm điều tương tự, việc cung cấp nó ngay lập tức sẽ nhanh hơn. Thư mục trống có thể được yêu cầu Kubernetes tạo trong bộ nhớ. Và do đó, bộ nhớ đệm của bạn thường có thể hoạt động với tốc độ cực nhanh - xét về tốc độ truy cập ổ đĩa. Nghĩa là, bạn có một thư mục trống trong bộ nhớ, trong hệ điều hành, nó được lưu trong bộ nhớ, nhưng đối với bạn, đối với người dùng bên trong nhóm, nó trông giống như một thư mục cục bộ. Bạn không cần ứng dụng này để dạy cụ thể bất kỳ phép thuật nào. Bạn chỉ cần trực tiếp lấy và đặt tệp của mình vào một thư mục, nhưng trên thực tế, vào bộ nhớ trên HĐH. Đây cũng là một tính năng rất tiện lợi về mặt Kubernetes.

Minio có vấn đề gì? Vấn đề chính với Minio là để thứ này hoạt động, nó cần phải chạy ở đâu đó và phải có một loại hệ thống tệp nào đó, tức là bộ lưu trữ. Và ở đây chúng ta gặp phải những vấn đề tương tự như Ceph. Tức là Minio phải lưu trữ các tập tin của nó ở đâu đó. Nó chỉ đơn giản là một giao diện HTTP cho các tập tin của bạn. Hơn nữa, chức năng rõ ràng là kém hơn so với S3 của Amazon. Trước đây, nó không thể ủy quyền đúng cách cho người dùng. Bây giờ, theo như tôi biết, nó đã có thể tạo các nhóm với các quyền khác nhau, nhưng một lần nữa, đối với tôi, có vẻ như vấn đề chính ở mức tối thiểu là hệ thống lưu trữ cơ bản.

Thư mục trống trong bộ nhớ ảnh hưởng đến giới hạn như thế nào? Không ảnh hưởng đến giới hạn dưới bất kỳ hình thức nào. Nó nằm trong bộ nhớ của máy chủ chứ không phải trong bộ nhớ của vùng chứa của bạn. Nghĩa là, vùng chứa của bạn không xem thư mục trống trong bộ nhớ như một phần bộ nhớ bị chiếm dụng. Chủ nhà nhìn thấy điều này. Theo đó, vâng, theo quan điểm của kubernetes, khi bạn bắt đầu sử dụng cái này, sẽ rất tốt nếu hiểu rằng bạn đang dành một phần bộ nhớ của mình cho thư mục trống. Và theo đó, hãy hiểu rằng bộ nhớ có thể hết không chỉ do ứng dụng mà còn do ai đó ghi vào các thư mục trống này.

Bản chất của đám mây

Và chủ đề phụ cuối cùng là Cloudnative là gì. Tại sao nó lại cần thiết? Cloudnativeness và như vậy.

Đó là những ứng dụng có khả năng và được viết để hoạt động trong cơ sở hạ tầng đám mây hiện đại. Nhưng trên thực tế, Cloudnative còn có một khía cạnh khác như vậy. Rằng đây không chỉ là một ứng dụng tính đến tất cả các yêu cầu của cơ sở hạ tầng đám mây hiện đại mà còn biết cách làm việc với cơ sở hạ tầng đám mây hiện đại này, tận dụng những ưu điểm và nhược điểm của việc nó hoạt động trong những đám mây này. Đừng chỉ làm việc trên đám mây mà hãy tận dụng những lợi ích của việc làm việc trên đám mây.

Yêu cầu để phát triển ứng dụng trong Kubernetes

Hãy lấy Kubernetes làm ví dụ. Ứng dụng của bạn đang chạy trong Kubernetes. Ứng dụng của bạn luôn có thể, hay đúng hơn là quản trị viên cho ứng dụng của bạn, luôn có thể tạo tài khoản dịch vụ. Tức là một tài khoản được ủy quyền trong chính Kubernetes trên máy chủ của nó. Thêm một số quyền mà chúng tôi cần ở đó. Và bạn có thể truy cập Kubernetes từ trong ứng dụng của mình. Bạn có thể làm gì theo cách này? Ví dụ: từ ứng dụng, nhận dữ liệu về vị trí đặt các ứng dụng khác của bạn, các phiên bản tương tự khác và bằng cách nào đó tập hợp lại trên Kubernetes, nếu có nhu cầu như vậy.

Một lần nữa, chúng tôi thực sự đã có một trường hợp gần đây. Chúng tôi có một bộ điều khiển giám sát hàng đợi. Và khi một số tác vụ mới xuất hiện trong hàng đợi này, nó sẽ chuyển đến Kubernetes - và bên trong Kubernetes, nó sẽ tạo một nhóm mới. Cung cấp cho nhóm này một số nhiệm vụ mới và trong khuôn khổ của nhóm này, nhóm sẽ thực hiện nhiệm vụ, gửi phản hồi đến chính bộ điều khiển và sau đó bộ điều khiển sẽ thực hiện điều gì đó với thông tin này. Ví dụ, nó thêm một cơ sở dữ liệu. Một lần nữa, đây là một điểm cộng của việc ứng dụng của chúng tôi chạy trong Kubernetes. Chúng ta có thể sử dụng chính chức năng Kubernetes tích hợp sẵn để bằng cách nào đó mở rộng và làm cho chức năng của ứng dụng của chúng ta thuận tiện hơn. Nghĩa là, không che giấu một loại phép thuật nào đó về cách khởi chạy một ứng dụng, cách khởi chạy một công nhân. Trong Kubernetes, bạn chỉ cần gửi yêu cầu trong ứng dụng nếu ứng dụng được viết bằng Python.

Điều tương tự cũng áp dụng nếu chúng ta vượt ra ngoài Kubernetes. Chúng tôi có Kubernetes đang chạy ở đâu đó - thật tốt nếu nó ở trên một loại đám mây nào đó. Một lần nữa, chúng ta có thể sử dụng và thậm chí tôi tin rằng nên sử dụng các khả năng của chính đám mây nơi chúng ta đang chạy. Từ những điều cơ bản mà đám mây cung cấp cho chúng ta. Cân bằng, nghĩa là chúng ta có thể tạo bộ cân bằng đám mây và sử dụng chúng. Đây là một lợi thế trực tiếp của những gì chúng ta có thể sử dụng. Bởi vì cân bằng đám mây, trước hết, chỉ đơn giản là loại bỏ trách nhiệm của chúng tôi về cách thức hoạt động, cách cấu hình của nó một cách ngu ngốc. Thêm vào đó, nó rất tiện lợi vì Kubernetes thông thường có thể tích hợp với đám mây.

Điều tương tự cũng xảy ra với việc mở rộng quy mô. Kubernetes thông thường có thể tích hợp với các nhà cung cấp đám mây. Biết cách hiểu rằng nếu cụm hết nút, tức là đã hết dung lượng nút thì bạn cần thêm - Kubernetes chính nó sẽ thêm các nút mới vào cụm của bạn và bắt đầu khởi chạy các nhóm trên chúng. Tức là khi tải của bạn đến, số lượng lò sưởi bắt đầu tăng lên. Khi các nút trong cụm dùng hết các nhóm này, Kubernetes sẽ khởi chạy các nút mới và theo đó, số lượng nhóm vẫn có thể tăng lên. Và nó rất thuận tiện. Đây là cơ hội trực tiếp để mở rộng quy mô cụm một cách nhanh chóng. Không nhanh lắm, theo nghĩa là không phải một giây, giống như một phút để thêm các nút mới.

Nhưng theo kinh nghiệm của tôi, một lần nữa, đó là điều tuyệt vời nhất mà tôi từng thấy. Khi cụm Cloudnative mở rộng quy mô dựa trên thời gian trong ngày. Đó là một dịch vụ phụ trợ được sử dụng bởi những người ở văn phòng hỗ trợ. Nghĩa là, họ đến làm việc lúc 9 giờ sáng, bắt đầu đăng nhập vào hệ thống và theo đó, cụm Cloudnative, nơi tất cả đang chạy, bắt đầu hoạt động, khởi chạy các nhóm mới để mọi người đến làm việc đều có thể làm việc với ứng dụng. Khi họ tan làm lúc 8 giờ tối hoặc 6 giờ tối, các cụm Kubernetes nhận thấy không còn ai sử dụng ứng dụng nữa và bắt đầu thu hẹp lại. Tiết kiệm lên đến 30 phần trăm được đảm bảo. Nó hoạt động hiệu quả ở Amazon vào thời điểm đó, vào thời điểm đó không có ai ở Nga có thể làm tốt như vậy.

Tôi sẽ nói thẳng với bạn rằng mức tiết kiệm được là 30% chỉ vì chúng tôi sử dụng Kubernetes và tận dụng các khả năng của đám mây. Bây giờ điều này có thể được thực hiện ở Nga. Tất nhiên, tôi sẽ không quảng cáo cho bất kỳ ai, nhưng hãy nói rằng có những nhà cung cấp có thể làm điều này, cung cấp nó ngay lập tức chỉ bằng một nút bấm.

Có một điểm cuối cùng mà tôi cũng muốn bạn chú ý. Để ứng dụng, cơ sở hạ tầng của bạn trở thành Cloudnative, cuối cùng bạn nên bắt đầu điều chỉnh cách tiếp cận có tên Cơ sở hạ tầng dưới dạng Mã. Nghĩa là, điều này có nghĩa là ứng dụng của bạn, hay đúng hơn là cơ sở hạ tầng của bạn, cần giống hệt như mã Mô tả của bạn. ứng dụng, logic nghiệp vụ của bạn dưới dạng mã. Và làm việc với nó dưới dạng mã, nghĩa là kiểm tra nó, triển khai nó, lưu trữ trong git, áp dụng CICD cho nó.

Và đây chính xác là điều cho phép bạn, trước hết, luôn có quyền kiểm soát cơ sở hạ tầng của mình, luôn hiểu được trạng thái của nó. Thứ hai, tránh những thao tác thủ công gây ra sai sót. Thứ ba, hãy tránh những gì được gọi là doanh thu khi bạn liên tục phải thực hiện các công việc thủ công giống nhau. Thứ tư, nó cho phép bạn phục hồi nhanh hơn nhiều trong trường hợp thất bại. Ở Nga, mỗi lần tôi nói về vấn đề này, luôn có rất nhiều người nói: “Ừ, rõ ràng rồi, nhưng bạn có cách tiếp cận, tóm lại là không cần phải sửa gì cả”. Nhưng đó là sự thật. Nếu có điều gì đó bị hỏng trong cơ sở hạ tầng của bạn, thì theo quan điểm của cách tiếp cận Cloudnative và từ quan điểm Cơ sở hạ tầng như một Mã, thay vì sửa nó, hãy đến máy chủ, tìm ra cái gì bị hỏng và sửa nó, điều đó sẽ dễ dàng hơn để xóa máy chủ và tạo lại. Và tôi sẽ khôi phục lại tất cả những thứ này.

Tất cả những vấn đề này sẽ được thảo luận chi tiết hơn tại Các khóa học video về Kubernetes: Junior, Basic, Mega. Bằng cách theo liên kết, bạn có thể làm quen với chương trình và các điều kiện. Điều thuận tiện là bạn có thể thành thạo Kubernetes bằng cách học ở nhà hoặc làm việc 1-2 giờ mỗi ngày.

Nguồn: www.habr.com

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