Calico dành cho kết nối mạng trong Kubernetes: giới thiệu và một chút kinh nghiệm

Calico dành cho kết nối mạng trong Kubernetes: giới thiệu và một chút kinh nghiệm

Mục đích của bài viết là giới thiệu cho người đọc những kiến ​​thức cơ bản về kết nối mạng và quản lý các chính sách mạng trong Kubernetes, cũng như plugin Calico của bên thứ ba giúp mở rộng các khả năng tiêu chuẩn. Trong quá trình này, tính dễ cấu hình và một số tính năng sẽ được thể hiện bằng các ví dụ thực tế từ trải nghiệm vận hành của chúng tôi.

Giới thiệu nhanh về thiết bị mạng Kubernetes

Không thể tưởng tượng được cụm Kubernetes nếu không có mạng. Chúng tôi đã xuất bản các tài liệu về những điều cơ bản của họ: “Hướng dẫn minh họa về kết nối mạng trong Kubernetes"Và"Giới thiệu về Chính sách mạng Kubernetes dành cho chuyên gia bảo mật'.

Trong bối cảnh của bài viết này, điều quan trọng cần lưu ý là bản thân K8 không chịu trách nhiệm về kết nối mạng giữa các container và nút: đối với điều này, nhiều plugin CNI (Giao diện mạng container). Tìm hiểu thêm về khái niệm này, chúng tôi họ cũng nói với tôi.

Ví dụ: plugin phổ biến nhất trong số này là Flannel — cung cấp kết nối mạng đầy đủ giữa tất cả các nút cụm bằng cách nâng cao các cầu nối trên mỗi nút, gán mạng con cho nó. Tuy nhiên, khả năng tiếp cận đầy đủ và không được kiểm soát không phải lúc nào cũng có lợi. Để cung cấp một số loại cách ly tối thiểu trong cụm, cần phải can thiệp vào cấu hình tường lửa. Trong trường hợp chung, nó được đặt dưới sự kiểm soát của cùng một CNI, đó là lý do tại sao mọi sự can thiệp của bên thứ ba vào iptables đều có thể bị hiểu sai hoặc bị bỏ qua hoàn toàn.

Và được cung cấp tính năng vượt trội để tổ chức quản lý chính sách mạng trong cụm Kubernetes API chính sách mạng. Tài nguyên này, được phân phối trên các không gian tên đã chọn, có thể chứa các quy tắc để phân biệt quyền truy cập từ ứng dụng này sang ứng dụng khác. Nó cũng cho phép bạn định cấu hình khả năng truy cập giữa các nhóm, môi trường (không gian tên) hoặc khối địa chỉ IP cụ thể:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

Đây không phải là ví dụ nguyên thủy nhất về tài liệu chính thức có thể một lần và mãi mãi ngăn cản mong muốn hiểu logic về cách thức hoạt động của các chính sách mạng. Tuy nhiên, chúng ta vẫn sẽ cố gắng tìm hiểu các nguyên tắc và phương pháp cơ bản xử lý luồng lưu lượng bằng chính sách mạng...

Điều hợp lý là có 2 loại lưu lượng truy cập: đi vào nhóm (Ingress) và đi ra từ nó (Egress).

Calico dành cho kết nối mạng trong Kubernetes: giới thiệu và một chút kinh nghiệm

Trên thực tế, chính trị được chia thành 2 loại này dựa trên hướng vận động.

Thuộc tính bắt buộc tiếp theo là bộ chọn; người mà quy tắc được áp dụng. Đây có thể là một nhóm (hoặc một nhóm nhóm) hoặc một môi trường (tức là một không gian tên). Một chi tiết quan trọng: cả hai loại đối tượng này phải chứa nhãn (nhãn theo thuật ngữ Kubernetes) - đây là những người mà các chính trị gia làm việc cùng.

Ngoài số lượng bộ chọn hữu hạn, được thống nhất bởi một số loại nhãn, có thể viết các quy tắc như “Cho phép/từ chối mọi thứ/mọi người” theo các biến thể khác nhau. Với mục đích này, các công trình có dạng sau được sử dụng:

  podSelector: {}
  ingress: []
  policyTypes:
  - Ingress

— trong ví dụ này, tất cả các nhóm trong môi trường đều bị chặn lưu lượng truy cập đến. Hành vi ngược lại có thể đạt được với việc xây dựng sau:

  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

Tương tự cho việc gửi đi:

  podSelector: {}
  policyTypes:
  - Egress

- để tắt nó đi. Và đây là những gì cần bao gồm:

  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

Quay trở lại việc lựa chọn plugin CNI cho một cụm, điều đáng chú ý là không phải mọi plugin mạng đều hỗ trợ NetworkPolicy. Ví dụ: Flannel đã được đề cập không biết cách định cấu hình các chính sách mạng, điều này nó được nói trực tiếp trong kho lưu trữ chính thức. Một giải pháp thay thế cũng được đề cập ở đó - một dự án Nguồn mở một thứ vải trắng, mở rộng đáng kể bộ API Kubernetes tiêu chuẩn về mặt chính sách mạng.

Calico dành cho kết nối mạng trong Kubernetes: giới thiệu và một chút kinh nghiệm

Làm quen với Calico: lý thuyết

Plugin Calico có thể được sử dụng tích hợp với Flannel (tiểu dự án Kinh) hoặc độc lập, bao gồm cả khả năng kết nối mạng và quản lý tính khả dụng.

Việc sử dụng giải pháp “đóng hộp” K8s và bộ API từ Calico mang lại những cơ hội gì?

Đây là những gì được tích hợp trong NetworkPolicy:

  • các chính trị gia bị giới hạn bởi môi trường;
  • chính sách được áp dụng cho các nhóm được đánh dấu bằng nhãn;
  • các quy tắc có thể được áp dụng cho nhóm, môi trường hoặc mạng con;
  • quy tắc có thể chứa các giao thức, thông số kỹ thuật cổng được đặt tên hoặc tượng trưng.

Đây là cách Calico mở rộng các chức năng này:

  • các chính sách có thể được áp dụng cho bất kỳ đối tượng nào: pod, container, máy ảo hoặc giao diện;
  • quy tắc có thể chứa một hành động cụ thể (cấm, cho phép, ghi nhật ký);
  • mục tiêu hoặc nguồn của quy tắc có thể là một cổng, một loạt cổng, giao thức, thuộc tính HTTP hoặc ICMP, IP hoặc mạng con (thế hệ thứ 4 hoặc thứ 6), bất kỳ bộ chọn nào (nút, máy chủ, môi trường);
  • Ngoài ra, bạn có thể điều chỉnh lưu lượng truy cập bằng cách sử dụng cài đặt DNAT và chính sách chuyển tiếp lưu lượng.

Cam kết đầu tiên về GitHub trong kho lưu trữ Calico bắt đầu từ tháng 2016 năm XNUMX và một năm sau, dự án đã dẫn đầu trong việc tổ chức kết nối mạng Kubernetes - ví dụ, điều này được chứng minh bằng kết quả khảo sát, được thực hiện bởi The New Stack:

Calico dành cho kết nối mạng trong Kubernetes: giới thiệu và một chút kinh nghiệm

Nhiều giải pháp được quản lý lớn với K8, chẳng hạn như Amazon EKS, Azure AKS, Google GKE và những người khác bắt đầu giới thiệu nó để sử dụng.

Về hiệu suất, mọi thứ ở đây đều tuyệt vời. Khi thử nghiệm sản phẩm của mình, nhóm phát triển Calico đã chứng minh hiệu suất vượt trội, chạy hơn 50000 container trên 500 nút vật lý với tốc độ tạo 20 container mỗi giây. Không có vấn đề nào được xác định với việc mở rộng quy mô. Kết quả như vậy đã được công bố đã có thông báo về phiên bản đầu tiên. Các nghiên cứu độc lập tập trung vào thông lượng và mức tiêu thụ tài nguyên cũng xác nhận hiệu suất của Calico gần bằng Flannel. Ví dụ:

Calico dành cho kết nối mạng trong Kubernetes: giới thiệu và một chút kinh nghiệm

Dự án đang phát triển rất nhanh, nó hỗ trợ làm việc trong các giải pháp phổ biến được quản lý K8s, OpenShift, OpenStack, có thể sử dụng Calico khi triển khai cụm bằng cách sử dụng đá, có tài liệu tham khảo về việc xây dựng mạng lưới dịch vụ (đây là một ví dụ được sử dụng cùng với Istio).

Luyện tập với Calico

Trong trường hợp chung của việc sử dụng vanilla Kubernetes, việc cài đặt CNI phụ thuộc vào việc sử dụng tệp calico.yaml, được tải xuống từ trang web chính thức, bằng cách sử dụng kubectl apply -f.

Theo quy định, phiên bản hiện tại của plugin tương thích với 2-3 phiên bản Kubernetes mới nhất: hoạt động trong các phiên bản cũ hơn không được kiểm tra và không được đảm bảo. Theo các nhà phát triển, Calico chạy trên nhân Linux trên 3.10 chạy CentOS 7, Ubuntu 16 hoặc Debian 8, trên iptables hoặc IPVS.

Cách ly trong môi trường

Để hiểu chung, chúng ta hãy xem xét một trường hợp đơn giản để hiểu các chính sách mạng trong ký hiệu Calico khác với các chính sách tiêu chuẩn như thế nào và cách tiếp cận tạo quy tắc đơn giản hóa khả năng đọc và tính linh hoạt trong cấu hình của chúng:

Calico dành cho kết nối mạng trong Kubernetes: giới thiệu và một chút kinh nghiệm

Có 2 ứng dụng web được triển khai trong cụm: trong Node.js và PHP, một trong số đó sử dụng Redis. Để chặn quyền truy cập vào Redis từ PHP, trong khi vẫn duy trì kết nối với Node.js, chỉ cần áp dụng chính sách sau:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: allow-redis-nodejs
spec:
  podSelector:
    matchLabels:
      service: redis
  ingress:
  - from:
    - podSelector:
        matchLabels:
          service: nodejs
    ports:
    - protocol: TCP
      port: 6379

Về cơ bản, chúng tôi đã cho phép lưu lượng truy cập đến cổng Redis từ Node.js. Và rõ ràng là họ không cấm bất cứ điều gì khác. Ngay khi NetworkPolicy xuất hiện, tất cả các bộ chọn được đề cập trong đó sẽ bắt đầu bị cô lập, trừ khi có quy định khác. Tuy nhiên, các quy tắc cách ly không áp dụng cho các đối tượng khác không nằm trong bộ chọn.

Ví dụ sử dụng apiVersion Kubernetes có sẵn nhưng không có gì ngăn cản bạn sử dụng nó tài nguyên cùng tên từ Calico Delivery. Cú pháp ở đó chi tiết hơn nên bạn sẽ cần viết lại quy tắc cho trường hợp trên theo mẫu sau:

apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
  name: allow-redis-nodejs
spec:
  selector: service == 'redis'
  ingress:
  - action: Allow
    protocol: TCP
    source:
      selector: service == 'nodejs'
    destination:
      ports:
      - 6379

Các cấu trúc được đề cập ở trên để cho phép hoặc từ chối tất cả lưu lượng truy cập thông qua API NetworkPolicy thông thường chứa các cấu trúc có dấu ngoặc đơn khó hiểu và khó nhớ. Trong trường hợp của Calico, để thay đổi logic của quy tắc tường lửa sang ngược lại, chỉ cần thay đổi action: Allow trên action: Deny.

Cách ly theo môi trường

Bây giờ hãy tưởng tượng một tình huống trong đó một ứng dụng tạo ra các số liệu kinh doanh để thu thập trong Prometheus và phân tích sâu hơn bằng Grafana. Nội dung tải lên có thể chứa dữ liệu nhạy cảm và có thể xem công khai theo mặc định. Hãy ẩn dữ liệu này khỏi con mắt tò mò:

Calico dành cho kết nối mạng trong Kubernetes: giới thiệu và một chút kinh nghiệm

Prometheus, theo quy định, được đặt trong một môi trường dịch vụ riêng biệt - trong ví dụ, nó sẽ là một không gian tên như thế này:

apiVersion: v1
kind: Namespace
metadata:
  labels:
    module: prometheus
  name: kube-prometheus

Lĩnh vực metadata.labels điều này hóa ra không phải là ngẫu nhiên. Như đã đề cập ở trên, namespaceSelector (cũng như podSelector) hoạt động với nhãn. Do đó, để cho phép lấy số liệu từ tất cả các nhóm trên một cổng cụ thể, bạn sẽ phải thêm một số loại nhãn (hoặc lấy từ các nhãn hiện có), sau đó áp dụng cấu hình như:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-metrics-prom
spec:
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          module: prometheus
    ports:
    - protocol: TCP
      port: 9100

Và nếu bạn sử dụng chính sách Calico thì cú pháp sẽ như sau:

apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
  name: allow-metrics-prom
spec:
  ingress:
  - action: Allow
    protocol: TCP
    source:
      namespaceSelector: module == 'prometheus'
    destination:
      ports:
      - 9100

Nói chung, bằng cách thêm các loại chính sách này cho các nhu cầu cụ thể, bạn có thể bảo vệ khỏi sự can thiệp cố ý hoặc vô tình vào hoạt động của các ứng dụng trong cụm.

Theo những người tạo ra Calico, cách thực hành tốt nhất là phương pháp “Chặn mọi thứ và mở rõ ràng những gì bạn cần”, được ghi lại trong tài liệu chính thức (những người khác theo cách tiếp cận tương tự - đặc biệt, trong bài viết đã đề cập rồi).

Sử dụng các đối tượng Calico bổ sung

Hãy để tôi nhắc bạn rằng thông qua bộ API Calico mở rộng, bạn có thể điều chỉnh tính khả dụng của các nút, không giới hạn ở các nhóm. Trong ví dụ sau sử dụng GlobalNetworkPolicy khả năng chuyển các yêu cầu ICMP trong cụm bị đóng (ví dụ: ping từ nhóm đến nút, giữa các nhóm hoặc từ nút đến nhóm IP):

apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
  name: block-icmp
spec:
  order: 200
  selector: all()
  types:
  - Ingress
  - Egress
  ingress:
  - action: Deny
    protocol: ICMP
  egress:
  - action: Deny
    protocol: ICMP

Trong trường hợp trên, các nút cụm vẫn có thể “tiếp cận” với nhau thông qua ICMP. Và vấn đề này được giải quyết bằng cách GlobalNetworkPolicy, áp dụng cho một thực thể HostEndpoint:

apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
  name: deny-icmp-kube-02
spec:
  selector: "role == 'k8s-node'"
  order: 0
  ingress:
  - action: Allow
    protocol: ICMP
  egress:
  - action: Allow
    protocol: ICMP
---
apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
  name: kube-02-eth0
  labels:
    role: k8s-node
spec:
  interfaceName: eth0
  node: kube-02
  expectedIPs: ["192.168.2.2"]

Trường hợp VPN

Cuối cùng, tôi sẽ đưa ra một ví dụ rất thực tế về việc sử dụng hàm Calico cho trường hợp tương tác gần cụm, khi một bộ chính sách tiêu chuẩn là không đủ. Để truy cập ứng dụng web, khách hàng sử dụng đường hầm VPN và quyền truy cập này được kiểm soát chặt chẽ và giới hạn trong một danh sách dịch vụ cụ thể được phép sử dụng:

Calico dành cho kết nối mạng trong Kubernetes: giới thiệu và một chút kinh nghiệm

Khách hàng kết nối với VPN thông qua cổng UDP tiêu chuẩn 1194 và khi được kết nối, sẽ nhận các tuyến đường đến các mạng con cụm của nhóm và dịch vụ. Toàn bộ mạng con được đẩy để không bị mất dịch vụ trong quá trình khởi động lại và thay đổi địa chỉ.

Cổng trong cấu hình là cổng tiêu chuẩn, áp đặt một số sắc thái trong quá trình định cấu hình ứng dụng và chuyển nó sang cụm Kubernetes. Ví dụ: trong cùng một AWS LoadBalancer dành cho UDP đã xuất hiện theo đúng nghĩa đen vào cuối năm ngoái trong một danh sách khu vực hạn chế và không thể sử dụng NodePort do tính năng chuyển tiếp của nó trên tất cả các nút cụm và không thể mở rộng số lượng phiên bản máy chủ cho mục đích chịu lỗi. Ngoài ra, bạn sẽ phải thay đổi phạm vi cổng mặc định...

Kết quả của việc tìm kiếm thông qua các giải pháp khả thi, những giải pháp sau đã được chọn:

  1. Các nhóm có VPN được lên lịch cho mỗi nút trong hostNetwork, nghĩa là với IP thực tế.
  2. Dịch vụ được đăng tải bên ngoài thông qua ClusterIP. Một cổng được cài đặt vật lý trên nút, có thể truy cập được từ bên ngoài với một số đặt trước nhỏ (sự hiện diện có điều kiện của địa chỉ IP thực).
  3. Việc xác định nút mà nhóm mọc lên nằm ngoài phạm vi câu chuyện của chúng ta. Tôi chỉ nói rằng bạn có thể “đóng đinh” dịch vụ chặt chẽ vào một nút hoặc viết một dịch vụ phụ nhỏ sẽ giám sát địa chỉ IP hiện tại của dịch vụ VPN và chỉnh sửa các bản ghi DNS đã đăng ký với khách hàng - bất kỳ ai có đủ trí tưởng tượng.

Từ góc độ định tuyến, chúng tôi có thể xác định duy nhất một máy khách VPN bằng địa chỉ IP do máy chủ VPN cấp. Dưới đây là một ví dụ cơ bản về việc hạn chế quyền truy cập vào các dịch vụ của khách hàng như vậy, được minh họa trên Redis đã đề cập ở trên:

apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
  name: vpnclient-eth0
  labels:
    role: vpnclient
    environment: production
spec:
  interfaceName: "*"
  node: kube-02
  expectedIPs: ["172.176.176.2"]
---
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
  name: vpn-rules
spec:
  selector: "role == 'vpnclient'"
  order: 0
  applyOnForward: true
  preDNAT: true
  ingress:
  - action: Deny
    protocol: TCP
    destination:
      ports: [6379]
  - action: Allow
    protocol: UDP
    destination:
      ports: [53, 67]

Ở đây, việc kết nối với cổng 6379 bị nghiêm cấm, nhưng đồng thời hoạt động của dịch vụ DNS vẫn được bảo toàn, chức năng của dịch vụ này thường bị ảnh hưởng khi thiết lập các quy tắc. Bởi vì, như đã đề cập trước đó, khi một bộ chọn xuất hiện, chính sách từ chối mặc định sẽ được áp dụng cho nó trừ khi có quy định khác.

Kết quả

Do đó, bằng cách sử dụng API nâng cao của Calico, bạn có thể định cấu hình linh hoạt và thay đổi linh hoạt định tuyến trong và xung quanh cụm. Nhìn chung, việc sử dụng nó có thể giống như bắn chim sẻ bằng đại bác và việc triển khai mạng L3 với các đường hầm BGP và IP-IP trông thật quái dị khi cài đặt Kubernetes đơn giản trên mạng phẳng... Tuy nhiên, nếu không thì công cụ này trông khá khả thi và hữu ích .

Việc cô lập một cụm để đáp ứng các yêu cầu bảo mật có thể không phải lúc nào cũng khả thi và đây là lúc Calico (hoặc một giải pháp tương tự) ra tay giải cứu. Các ví dụ đưa ra trong bài viết này (với những sửa đổi nhỏ) được sử dụng trong một số bản cài đặt của máy khách của chúng tôi trong AWS.

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