บทความนี้มีวัตถุประสงค์เพื่อแนะนำผู้อ่านเกี่ยวกับพื้นฐานของเครือข่ายและการจัดการนโยบายเครือข่ายใน Kubernetes รวมถึงปลั๊กอิน Calico บุคคลที่สามที่ขยายความสามารถมาตรฐาน ในระหว่างนี้ ความง่ายในการกำหนดค่าและคุณสมบัติบางอย่างจะแสดงให้เห็นโดยใช้ตัวอย่างจริงจากประสบการณ์การดำเนินงานของเรา
ข้อมูลเบื้องต้นเกี่ยวกับอุปกรณ์เครือข่าย Kubernetes
ไม่สามารถจินตนาการถึงคลัสเตอร์ Kubernetes ได้หากไม่มีเครือข่าย เราได้เผยแพร่เนื้อหาเกี่ยวกับพื้นฐานแล้ว: “
ในบริบทของบทความนี้ สิ่งสำคัญคือต้องทราบว่า K8 เองจะไม่รับผิดชอบต่อการเชื่อมต่อเครือข่ายระหว่างคอนเทนเนอร์และโหนด: สำหรับสิ่งนี้ ต่างๆ ปลั๊กอิน CNI (อินเตอร์เฟสเครือข่ายคอนเทนเนอร์) เพิ่มเติมเกี่ยวกับแนวคิดนี้เรา
ตัวอย่างเช่น ปลั๊กอินเหล่านี้ที่พบบ่อยที่สุดคือ
และมีการจัดเตรียม "นอกกรอบ" สำหรับการจัดระเบียบการจัดการนโยบายเครือข่ายในคลัสเตอร์ Kubernetes
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
นี่ไม่ใช่ตัวอย่างดั้งเดิมที่สุดของ
มีเหตุผลที่มีการรับส่งข้อมูล 2 ประเภท: เข้าสู่พ็อด (ทางเข้า) และขาออก (ทางออก)
จริงๆ แล้วการเมืองแบ่งออกเป็น 2 หมวดนี้ตามทิศทางการเคลื่อนไหว
แอตทริบิวต์ที่จำเป็นถัดไปคือตัวเลือก ผู้ที่ใช้กฎนี้ ซึ่งอาจเป็นพ็อด (หรือกลุ่มของพ็อด) หรือสภาพแวดล้อม (เช่น เนมสเปซ) รายละเอียดที่สำคัญ: ออบเจ็กต์ทั้งสองประเภทต้องมีป้ายกำกับ (ฉลาก ในคำศัพท์ Kubernetes) - สิ่งเหล่านี้คือคำศัพท์ที่นักการเมืองดำเนินการด้วย
นอกจากตัวเลือกจำนวนจำกัดที่รวมเข้าด้วยกันโดยป้ายกำกับบางประเภทแล้ว ยังสามารถเขียนกฎ เช่น "อนุญาต/ปฏิเสธทุกอย่าง/ทุกคน" ในรูปแบบที่แตกต่างกันได้ เพื่อจุดประสงค์นี้มีการใช้โครงสร้างแบบฟอร์ม:
podSelector: {}
ingress: []
policyTypes:
- Ingress
— ในตัวอย่างนี้ พ็อดทั้งหมดในสภาพแวดล้อมจะถูกบล็อกจากการรับส่งข้อมูลขาเข้า พฤติกรรมตรงกันข้ามสามารถทำได้ด้วยโครงสร้างต่อไปนี้:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
ในทำนองเดียวกันสำหรับขาออก:
podSelector: {}
policyTypes:
- Egress
- เพื่อปิด และนี่คือสิ่งที่ควรรวม:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
เมื่อกลับมาที่ตัวเลือกปลั๊กอิน CNI สำหรับคลัสเตอร์ ก็เป็นที่น่าสังเกตว่า ไม่ใช่ทุกปลั๊กอินเครือข่ายที่รองรับ NetworkPolicy. ตัวอย่างเช่นผ้าสักหลาดที่กล่าวไปแล้วไม่ทราบวิธีกำหนดค่านโยบายเครือข่ายซึ่ง
ทำความรู้จักกับผ้าดิบ: ทฤษฎี
ปลั๊กอิน Calico สามารถใช้ร่วมกับ Flannel (โครงการย่อย
การใช้โซลูชัน “แบบบรรจุกล่อง” ของ K8 และชุด API จาก Calico มอบโอกาสอะไรบ้าง
นี่คือสิ่งที่สร้างไว้ใน NetworkPolicy:
- นักการเมืองถูกจำกัดโดยสภาพแวดล้อม
- นโยบายถูกนำไปใช้กับพ็อดที่มีป้ายกำกับ
- กฎสามารถนำไปใช้กับพ็อด สภาพแวดล้อม หรือซับเน็ตได้
- กฎสามารถประกอบด้วยโปรโตคอล ข้อกำหนดพอร์ตที่มีชื่อหรือสัญลักษณ์
นี่คือวิธีที่ Calico ขยายฟังก์ชันเหล่านี้:
- นโยบายสามารถนำไปใช้กับออบเจ็กต์ใดก็ได้: พ็อด คอนเทนเนอร์ เครื่องเสมือน หรืออินเทอร์เฟซ
- กฎอาจมีการดำเนินการเฉพาะ (ข้อห้าม การอนุญาต การบันทึก)
- เป้าหมายหรือแหล่งที่มาของกฎอาจเป็นพอร์ต ช่วงของพอร์ต โปรโตคอล คุณลักษณะ HTTP หรือ ICMP IP หรือเครือข่ายย่อย (รุ่นที่ 4 หรือ 6) ตัวเลือกใดๆ (โหนด โฮสต์ สภาพแวดล้อม)
- นอกจากนี้ คุณยังสามารถควบคุมเส้นทางการรับส่งข้อมูลได้โดยใช้การตั้งค่า DNAT และนโยบายการส่งต่อการรับส่งข้อมูล
การดำเนินการครั้งแรกกับ GitHub ในพื้นที่เก็บข้อมูล Calico ย้อนกลับไปในเดือนกรกฎาคม 2016 และอีกหนึ่งปีต่อมาโครงการก็เป็นผู้นำในการจัดการการเชื่อมต่อเครือข่าย Kubernetes - นี่เป็นหลักฐานเช่นจากผลการสำรวจ
โซลูชันการจัดการขนาดใหญ่จำนวนมากที่มี K8 เช่น
ในส่วนของประสิทธิภาพที่นี่ทุกอย่างยอดเยี่ยมมาก ในการทดสอบผลิตภัณฑ์ ทีมพัฒนา Calico แสดงให้เห็นถึงประสิทธิภาพทางดาราศาสตร์ โดยใช้งานคอนเทนเนอร์มากกว่า 50000 ตู้บนโหนดทางกายภาพ 500 โหนด ด้วยอัตราการสร้างคอนเทนเนอร์ 20 ตู้ต่อวินาที ไม่พบปัญหาใดๆ กับการปรับขนาด ผลลัพธ์ดังกล่าว
โครงการกำลังพัฒนาอย่างรวดเร็ว รองรับการทำงานในโซลูชันยอดนิยมที่มีการจัดการ K8s, OpenShift, OpenStack คุณสามารถใช้ Calico เมื่อปรับใช้คลัสเตอร์โดยใช้
ฝึกกับผ้าดิบ
ในกรณีทั่วไปของการใช้ vanilla Kubernetes การติดตั้ง CNI จะขึ้นอยู่กับการใช้ไฟล์ calico.yaml
, kubectl apply -f
.
ตามกฎแล้ว เวอร์ชันปัจจุบันของปลั๊กอินเข้ากันได้กับ Kubernetes เวอร์ชัน 2-3 ล่าสุด: การดำเนินการในเวอร์ชันเก่าไม่ได้รับการทดสอบและไม่รับประกัน ตามที่นักพัฒนาระบุว่า Calico ทำงานบนเคอร์เนล Linux ที่สูงกว่า 3.10 ที่ใช้ CentOS 7, Ubuntu 16 หรือ Debian 8 ที่ด้านบนของ iptables หรือ IPVS
การแยกตัวภายในสภาพแวดล้อม
เพื่อความเข้าใจทั่วไป มาดูกรณีง่ายๆ เพื่อทำความเข้าใจว่านโยบายเครือข่ายในรูปแบบ Calico แตกต่างจากมาตรฐานอย่างไร และวิธีการสร้างกฎทำให้ความสามารถในการอ่านและความยืดหยุ่นในการกำหนดค่าง่ายขึ้น:
มีเว็บแอปพลิเคชัน 2 ตัวที่ใช้งานอยู่ในคลัสเตอร์: ใน Node.js และ PHP ซึ่งหนึ่งในนั้นใช้ Redis หากต้องการบล็อกการเข้าถึง Redis จาก PHP ในขณะที่ยังคงการเชื่อมต่อกับ Node.js เพียงใช้นโยบายต่อไปนี้:
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
โดยพื้นฐานแล้วเราอนุญาตให้รับส่งข้อมูลขาเข้าไปยังพอร์ต Redis จาก Node.js และเห็นได้ชัดว่าพวกเขาไม่ได้ห้ามสิ่งอื่นใด ทันทีที่ NetworkPolicy ปรากฏขึ้น ตัวเลือกทั้งหมดที่กล่าวถึงในนั้นจะเริ่มถูกแยกออก เว้นแต่จะระบุไว้เป็นอย่างอื่น อย่างไรก็ตาม กฎการแยกจะไม่ใช้กับออบเจ็กต์อื่นที่ตัวเลือกไม่ครอบคลุม
ตัวอย่างการใช้งาน apiVersion
Kubernetes พร้อมใช้งานทันที แต่ไม่มีอะไรขัดขวางคุณจากการใช้งาน
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
โครงสร้างที่กล่าวมาข้างต้นสำหรับการอนุญาตหรือปฏิเสธการรับส่งข้อมูลทั้งหมดผ่าน NetworkPolicy API ปกติมีโครงสร้างที่มีวงเล็บซึ่งยากต่อการทำความเข้าใจและจดจำ ในกรณีของ Calico หากต้องการเปลี่ยนตรรกะของกฎไฟร์วอลล์ไปในทางตรงกันข้าม เพียงแค่เปลี่ยน action: Allow
บน action: Deny
.
การแยกตัวตามสภาพแวดล้อม
ตอนนี้ลองจินตนาการถึงสถานการณ์ที่แอปพลิเคชันสร้างหน่วยวัดทางธุรกิจสำหรับการรวบรวมใน Prometheus และการวิเคราะห์เพิ่มเติมโดยใช้ Grafana การอัปโหลดอาจมีข้อมูลที่ละเอียดอ่อน ซึ่งสามารถดูได้แบบสาธารณะอีกครั้งตามค่าเริ่มต้น มาซ่อนข้อมูลนี้จากการสอดรู้สอดเห็น:
ตามกฎแล้ว Prometheus จะถูกวางไว้ในสภาพแวดล้อมการบริการที่แยกต่างหาก - ในตัวอย่างมันจะเป็นเนมสเปซดังนี้:
apiVersion: v1
kind: Namespace
metadata:
labels:
module: prometheus
name: kube-prometheus
สนาม metadata.labels
นี่กลายเป็นว่าไม่ใช่เรื่องบังเอิญ ตามที่กล่าวไว้ข้างต้น namespaceSelector
(เช่นเดียวกับ podSelector
) ดำเนินการโดยมีป้ายกำกับ ดังนั้น หากต้องการอนุญาตให้ดึงตัววัดจากพ็อดทั้งหมดบนพอร์ตเฉพาะ คุณจะต้องเพิ่มป้ายกำกับบางประเภท (หรือนำมาจากที่มีอยู่) จากนั้นจึงใช้การกำหนดค่า เช่น:
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
และหากคุณใช้นโยบาย Calico ไวยากรณ์จะเป็นดังนี้:
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
โดยทั่วไป การเพิ่มนโยบายประเภทนี้ตามความต้องการเฉพาะจะทำให้คุณสามารถป้องกันการรบกวนที่เป็นอันตรายหรือโดยไม่ได้ตั้งใจในการทำงานของแอปพลิเคชันในคลัสเตอร์ได้
แนวทางปฏิบัติที่ดีที่สุดตามที่ผู้สร้าง Calico กล่าวคือแนวทาง "บล็อกทุกสิ่งและเปิดสิ่งที่คุณต้องการอย่างชัดเจน" ซึ่งบันทึกไว้ใน
การใช้วัตถุผ้าดิบเพิ่มเติม
ฉันขอเตือนคุณว่าด้วยชุดส่วนขยายของ Calico API คุณสามารถควบคุมความพร้อมใช้งานของโหนดได้ โดยไม่จำกัดเพียงพ็อด ในตัวอย่างต่อไปนี้โดยใช้ GlobalNetworkPolicy
ความสามารถในการส่งคำขอ ICMP ในคลัสเตอร์ถูกปิด (เช่น การส่ง Ping จากพ็อดไปยังโหนด ระหว่างพ็อด หรือจากโหนดไปยังพ็อด 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
ในกรณีข้างต้น ยังคงเป็นไปได้ที่โหนดคลัสเตอร์จะ "เข้าถึง" ถึงกันผ่าน ICMP และปัญหานี้ได้รับการแก้ไขด้วยวิธี GlobalNetworkPolicy
ใช้กับเอนทิตี 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"]
กรณี VPN
สุดท้ายนี้ ฉันจะยกตัวอย่างที่แท้จริงของการใช้ฟังก์ชัน Calico ในกรณีของการโต้ตอบแบบคลัสเตอร์ใกล้เคียง เมื่อชุดนโยบายมาตรฐานยังไม่เพียงพอ ในการเข้าถึงเว็บแอปพลิเคชัน ไคลเอนต์จะใช้อุโมงค์ VPN และการเข้าถึงนี้ได้รับการควบคุมอย่างเข้มงวดและจำกัดเฉพาะรายการบริการเฉพาะที่อนุญาตให้ใช้:
ไคลเอนต์เชื่อมต่อกับ VPN ผ่านพอร์ต UDP มาตรฐาน 1194 และเมื่อเชื่อมต่อแล้ว จะได้รับเส้นทางไปยังซับเน็ตคลัสเตอร์ของพ็อดและบริการ เครือข่ายย่อยทั้งหมดถูกผลักเพื่อไม่ให้บริการสูญหายระหว่างการรีสตาร์ทและการเปลี่ยนแปลงที่อยู่
พอร์ตในการกำหนดค่าเป็นมาตรฐานซึ่งกำหนดความแตกต่างบางประการเกี่ยวกับกระบวนการกำหนดค่าแอปพลิเคชันและถ่ายโอนไปยังคลัสเตอร์ Kubernetes ตัวอย่างเช่น ใน AWS LoadBalancer เดียวกันสำหรับ UDP ปรากฏอย่างแท้จริงเมื่อปลายปีที่แล้วในรายการภูมิภาคที่จำกัด และ NodePort ไม่สามารถใช้งานได้เนื่องจากการส่งต่อบนโหนดคลัสเตอร์ทั้งหมด และเป็นไปไม่ได้ที่จะปรับขนาดจำนวนอินสแตนซ์เซิร์ฟเวอร์สำหรับ วัตถุประสงค์ในการทนต่อข้อผิดพลาด นอกจากนี้ คุณจะต้องเปลี่ยนช่วงเริ่มต้นของพอร์ต...
จากการค้นหาวิธีแก้ปัญหาที่เป็นไปได้ จึงมีการเลือกสิ่งต่อไปนี้:
- พ็อดที่มี VPN ได้รับการกำหนดเวลาต่อโหนดใน
hostNetwork
นั่นคือไปยัง IP จริง - โดยจะโพสต์บริการภายนอกผ่าน
ClusterIP
. พอร์ตได้รับการติดตั้งทางกายภาพบนโหนด ซึ่งสามารถเข้าถึงได้จากภายนอกโดยต้องมีการจองเล็กน้อย (การมีที่อยู่ IP จริงตามเงื่อนไข) - การกำหนดโหนดที่พ็อดเพิ่มขึ้นนั้นอยู่นอกเหนือขอบเขตของเรื่องราวของเรา ฉันจะบอกว่าคุณสามารถเดินสายบริการไปยังโหนดหรือเขียนบริการช่วยเหลือขนาดเล็กที่จะตรวจสอบที่อยู่ IP ปัจจุบันของบริการ VPN และแก้ไขบันทึก DNS ที่ลงทะเบียนกับไคลเอนต์ - ใครก็ตามที่มีจินตนาการเพียงพอ
จากมุมมองของการกำหนดเส้นทาง เราสามารถระบุไคลเอนต์ VPN โดยไม่ซ้ำกันด้วยที่อยู่ IP ที่ออกโดยเซิร์ฟเวอร์ VPN ด้านล่างนี้เป็นตัวอย่างเบื้องต้นในการจำกัดการเข้าถึงบริการของลูกค้าดังกล่าว ดังที่แสดงใน Redis ที่กล่าวถึงข้างต้น:
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]
ที่นี่ห้ามเชื่อมต่อกับพอร์ต 6379 โดยเด็ดขาด แต่ในขณะเดียวกันการทำงานของบริการ DNS จะยังคงอยู่ซึ่งการทำงานค่อนข้างบ่อยเมื่อจัดทำกฎ เนื่องจาก ตามที่กล่าวไว้ก่อนหน้านี้ เมื่อตัวเลือกปรากฏขึ้น นโยบายการปฏิเสธเริ่มต้นจะถูกนำไปใช้กับตัวเลือกนั้น เว้นแต่จะระบุไว้เป็นอย่างอื่น
ผลของการ
ดังนั้น เมื่อใช้ API ขั้นสูงของ Calico คุณสามารถกำหนดค่าและเปลี่ยนเส้นทางในและรอบๆ คลัสเตอร์ได้อย่างยืดหยุ่น โดยทั่วไปการใช้งานอาจดูเหมือนการยิงนกกระจอกด้วยปืนใหญ่และการใช้เครือข่าย L3 พร้อมอุโมงค์ BGP และ IP-IP นั้นดูน่ากลัวในการติดตั้ง Kubernetes แบบง่าย ๆ บนเครือข่ายแบบเรียบ... อย่างไรก็ตาม ไม่เช่นนั้นเครื่องมือจะดูใช้งานได้และมีประโยชน์ทีเดียว .
การแยกคลัสเตอร์เพื่อให้เป็นไปตามข้อกำหนดด้านความปลอดภัยอาจไม่สามารถทำได้เสมอไป และนี่คือจุดที่ Calico (หรือโซลูชันที่คล้ายกัน) เข้ามาช่วยเหลือ ตัวอย่างที่ให้ไว้ในบทความนี้ (พร้อมการแก้ไขเล็กน้อย) ใช้ในการติดตั้งลูกค้าของเราใน AWS หลายรายการ
PS
อ่านเพิ่มเติมในบล็อกของเรา:
- «
ข้อมูลเบื้องต้นเกี่ยวกับนโยบายเครือข่าย Kubernetes สำหรับผู้เชี่ยวชาญด้านความปลอดภัย "; - "ภาพประกอบคู่มือการสร้างเครือข่ายใน Kubernetes":
ส่วนที่ 1 และ 2 (โมเดลเครือข่าย, เครือข่ายโอเวอร์เลย์) ,ส่วนที่ 3 (บริการและการประมวลผลการรับส่งข้อมูล) ; - «
Container Networking Interface (CNI) - อินเทอร์เฟซเครือข่ายและมาตรฐานสำหรับคอนเทนเนอร์ Linux '
ที่มา: will.com