LFS258 [4/15]: Kubernetes APIs and Access
Sawit M.
Posted on February 3, 2020
จากบทที่แล้วเราได้รู้แล้วว่า APIs เป็นหัวใจสำคัญของ Kubernetes การทำงานทุกอย่างต้องถูกสั่งด้วยการเรียก API ผ่าน kube-apiserver
ที่อยู่บน master node ในบทนี้เราจะมาเจาะลึกเรื่อง APIs และ การเรียกใช้มันดูกัน
TL;DR
-
API Access APIs ของ Kubernetes เป็น REST-based API การเข้าถึงสามารถใช้
curl
หรือ tools อื่นๆ ก็ได้ แต่ต้องมี cert และ key ที่ถูกต้อง และ สิทธิ์ ที่เหมาะสม -
Checking Access Privilege สามารถทำได้โดยใช้ command
kubectl auth can-i ...
-
Optimistic Concurrency การ update resources ของ kubernetes จะไม่ใช้การ locking แต่ใช้การอ้างอิง version ของ resource และ retry หาก ได้รับ error
409 CONFLICT
เนื่องจาก version ของ resource ไม่ตรงกับ version ปัจจุบัน - Using Annotations annotation เป็นที่เก็บ metadata ให้กับ object ไม่เหมือนกับ labels ที่ใช้สำหรับการ identify resources ที่ต้องการสั่งงาน
- Simple Pod อาจมีเพียง 1 container หรือมากว่านั้นก็ได้ แต่โดยปกติแล้วจะมี 1 container ที่ทำ logic หลัก นอกนั้นจะทำหน้าที่เป็นตัวคอยช่วยเหลือ เรียกว่า sidecar
-
What kubectl really do
kubectl
จะทำหน้าที่แปลงคำสั่ง ที่เราสั่งผ่าน kubectl ให้กลายเป็น REST-based API call พร้อมทั้งแปลง configuration ที่เป็น YAML ให้เป็น JSON format เพื่อส่งไปยัง kube-apiserver แทนเรา -
Access from Outside the Cluster ในการเข้าถึง API นั้น cert และ key ต้องถูกต้อง ในกรณีข้อง
kubectl
จะอ่าน cert และ key จาก~/.kube/config
- Namespace ของ kubenetes จะหมายถึงการแบ่ง (segmentation) object ภายใน cluster ออกเป็นกลุ่มๆ ไม่ใช่ kernel เหมือนของ Linux
- Working with Namespaces เราสามารถสร้าง namespace ขึ้นมาใช้งานเองได้ และ assign resources ไปยัง namespace ที่เราสร้างขึ้นมาใหม่ได้เลย
-
API Resources of Kubernetes สามารถเข้าถึงได้ทั้ง
kubectl
และ REST API call -
API Maturity Kubernetes มีการแบ่ง API ออกเป็นกลุ่มๆ และภายในกลุ่มยังแบ่งออกเป็น version ทำให้แต่ละ team สามารถแยกพัฒนา API กันได้อย่างอิสระ ในการ release version มี 3 stages คือ
alpha
,beta
และstable
API Access
APIs ของ Kubernetes เป็น REST-based API และด้วยความที่ kubernetes เติมโตขึ้นเรื่อยๆ ตลอดเวลา ดังนั้น ความเข้าใจเรื่อง endpoint และ การเปลี่ยนแปลง API specification ในแต่ละ version เป็นเรื่องที่สำคัญและต้องติดตาม โดยตั้งแต่ v1.16 เป็นต้นมา object ที่ไม่ได้ไปต่อ (deprecated) จะเรียกใช้งานไม่ได้แล้ว
เนื่องจาก Kubernetes ใช้ REST-based API ดังนั้นเราสามารถ curl
เข้าไปเรียกใช้งาน APIs ได้เลย โดย API ของ Kubernetes จะแบ่งออกเป็นกลุ่มๆ ในแต่ละกลุ่มจะมีการพัฒนาแยกกับกลุ่มอื่นโดยอิสระ ภายในกลุ่มก็ยังแบ่งเป็น version ในการเรียกใช้ ต้องระบุกลุ่ม และ version ให้ชัดเจน
ในการเรียกใช้งาน API เราสามารถใช้ HTTP verbs เช่น GET
, POST
หรือ DELETE
ได้ตามปกติ โดยสามารถใช้ curl
หรือ tool อื่นๆ ก็ได้ แต่สิ่งสำคัญที่สุดที่ต้องมี คือ certificates และ keys ที่ถูกต้องเหมาะสม เราสามารถระบุรายละเอียดของคำสั่งใน JSON file แล้วส่งมันไปพร้อมกับ HTTP request เพื่อสั่งงาน kubernetes ได้ด้วย
$ curl \
--cert userbob.pem \
--key userbob-key.pem \
--cacert /path/to/ca.pem \
--http://k8sMaster:6443/api/v1/pods
ปกติแล้วเราจะไม่ได้ curl เข้าไปตรงๆ แต่จะใช้ command kubectl
ในการ call ไปยัง kube-apiserver
โดยเราสามารถเขียน configuration file ใน YAML format ส่งให้ kubectl
และ kubectl
จะทำหน้าที่แปลง YAML ให้เป็น JSON จากนั้นจึงส่งให้ kube-apiserver
อีกที
นอกจากนั้น เรายังสามารถสั่งงานในนามของ user หรือ group อื่นได้ (impersonate) แต่เราต้อง configure RBAC ก่อน
Checking Access Privilege
ก่อนจะเข้าไปงงด้วยกันในเรื่อง security ใน chapter หลังๆ kubernetes มี sub-command ที่จะช่วยให้เราตรวจสอบ authorization ทั้งของเราเองในฐานะ admin และ user อื่นๆ ได้ด้วย นั่นก็คือ kubectl auth can-i ...
ดังตัวอย่างดังต่อไปนี้
# Can I, as an administrator, create deployment in default namespace?
$ kubectl auth can-i create deployments
yes
# Can I as, a bob, create deployment in default namespace?
$ kubectl auth can-i create deployments --as bob
no
# Can I as, a bob, create deployment in developer namespace?
$ kubectl auth can-i create deployments --as bob --namespace developer
yes
โดย sub-command นี้ไปเรียกใช้ kind
ดังต่อไปนี้
-
SubjectAccessReview
: เป็นการตรวจสอบความสามารถของ user ใดๆ ก็ได้ -
LocalSubjectAccessReview
: เหมือนSubjectAccessReview
แต่เป็นการตรวจสอบเฉพาะ namespace หนึ่งๆ เท่านั้น -
SelfSubjectRulesReview
: เป็นการตรวจสอบว่า user นั้นๆ สามารถทำอะไรบ้างใน namespace นั้นๆ
ของ API Group authorization.k8s.io/v1
นอกจากนี้ยังมี kubectl auth reconcile
ที่ช่วย reconcile rules ของ RBAC Role, RoleBinding, ClusterRole และ ClusterRole binding objects ให้เป็นไปตาม configure ของเรา ถ้ามีอะไรหายไปมันก็จะแก้ไขให้ถูกต้อง
Optimistic Concurrency
โดย default data ที่จะส่งให้ API จะถูก serialization ให้ในรูป JSON ต่อมา Google ได้เสนออีกวิธีในการ serialization เรียกว่า protobuf ซึ่งยังอยู่ในขั้นทดลอง ในทางปฏิบัติ เราจะใช้ YAML format ซึ่ง kubectl
จะเป็นคนแปลงให้เป็น JSON เพื่อส่งไปยัง kube-apiserver
ต่อไป
ถ้าใครเคยผ่านเรื่อง database มาแล้ว น่าจะเข้าใจ concept ของการ locking คือ ถ้า นาย A กำลัง update row ที่ 3 อยู่ แล้วนาย B ต้องการ update row 3 เหมือนกัน นาย B ต้องรอให้ นาย A update เสร็จก่อน ถึงจะ update ได้
แต่ kubernetes ไม่ได้เป็นแบบนั้น มันจะใช้ versionResource
บวกกับ optimistic concurrency
มาจัดการเรื่องนี้แทน โดย ทุกการ update ต้อง ระบุ version ของ resource ที่จะ update ด้วยเสมอ ถ้า version ไม่ตรง (อาจมีคนอื่นมาชิง update ก่อน) จะได้รับ error 409 CONFLICT
คนนั้นต้องกลับไป check version ของ resource นั้นก่อน แล้วค่อยมา update ใหม่เองอีกครั้ง ทั้งนี้ operation ที่ไม่ทำให้ resource เกินการเปลี่ยนแปลง เช่น WATCH
หรือ GET
จะไม่ทำให้ versionResource
เปลี่ยนแปลง
kubernetes ได้ versionResource
มาจาก modifiedIndex
ของ etcd database ซึ่งมันจะไม่ซ้ำกันใน namespace
, kind
และ server
เดียวกัน
Using Annotations
Annotations คล้ายกับ labels ตรงที่ใช้เพื่อกำหนด metadata ให้กับ object แต่ annotations ไม่ได้มีไว้ใช้ในการระบุ หรือ เลือก resources เพื่อมาทำงานอย่างใดอย่างหนึ่งเหมือน labels มันทำหน้าที่แค่เก็บ metadata เพื่อเป็นข้อมูลให้คนอื่นมาอ่าน มันจึงมีความยืดหยุ่นในการเก็บข้อมูลมากกว่า
ตัวอย่างของ data ที่เก็บใน metadata เช่น
- Build, release หรือ image information
- Pointers ของ logging, monitoring, analytics หรือ audit repositories
- Client library หรือ tool information
- Contact Point
เป็นต้น
annotations สามารถเก็บไว้ใน external database ได้ แต่จะทำให้ความยืดหยุ่นลดลงไม่ควรทำ
ตัวอย่างการกำหนด annotation
# define annotation
$ kubectl annotate pods --all description='prod' -n prod
# overwrite annotation
$ kubectl annotate --overwrite pods description='old-prod' -n prod
# remove annotation
$ kubectl annotate pods foo description- -n prod
Simple Pod
จาก chapter ที่แล้ว Pod คือ หน่วยที่เล็กที่สุดที่ Kubernetes สามารถควบคุมได้ ใน pod อาจมีเพียง 1 container หรือมากว่านั้นก็ได้ แต่โดยปกติแล้วจะมี 1 container ที่ทำ logic ของ app นอกนั้นจะทำหน้าที่เป็นตัวคอยช่วยเหลือ เรียกว่า sidecar
ในการสร้าง pod สามารถ define ด้วย YAML file ดังนี้
apiVersion: v1
kind: Pod
metadata:
name: firstpod
spec:
containers:
- image: nginx
name: firstpod
จากนั้นทำการ create ด้วย kubectl create
เราสามารถตรวจสอบ status ของ pod ด้วย kubectl get pods
ดังตัวอย่าง
# Create pod which define in simple.yaml
$ kubectl crate -f simple.yaml
# Get all pods status in default namespace
$ kubectl get pods
# Get all pods status name firstpod in default namespace in YAML format
$ kubeclt get pod firstpod -o yaml
# Get all pods status name firstpod in default namespace in JSON format
$ kubeclt get pod firstpod -o json
What kubectl really do
kube-apiserver เป็นศูนย์กลางในการรับ REST-based API เพื่อให้เราสามารถสั่งงาน cluster ของเราได้ โดย เราสามารถ curl ไปยัง URL ที่เป็น endpoint ด้วย HTTP verbs มาตรฐาน (GET, PUT, POST, DELETE, ...) และอาจมี data ในรูปแบบ JSON ส่งไปด้วย เพื่อระบุรายละเอียด
kubectl ก็ทำงานเช่นเดียว curl โดย kubectl จะทำหน้าที่แปลงคำสั่ง ที่เราสั่งผ่าน kubectl ให้กลายเป็น REST-based API call เพื่อส่งไปยัง kube-apiserver แทนเราอีกที
เราสามารถ debug การทำงานของ kubectl ได้โดยการใส่ option --v=n
โดยที่ n คือ 0-9 ไปให้มัน ดังนี้
$ kubectl --v=7 get pods
I0201 08:11:43.476150 22800 loader.go:375] Config loaded from file: /root/.kube/config
I0201 08:11:43.486369 22800 round_trippers.go:420] GET https://k8smaster:6443/api/v1/namespaces/default/pods?limit=500
I0201 08:11:43.486646 22800 round_trippers.go:427] Request Headers:
I0201 08:11:43.486835 22800 round_trippers.go:431] Accept: application/json;as=Table;v=v1beta1;g=meta.k8s.io, application/json
I0201 08:11:43.487011 22800 round_trippers.go:431] User-Agent: kubectl/v1.16.1 (linux/amd64) kubernetes/d647ddb
I0201 08:11:43.502660 22800 round_trippers.go:446] Response Status: 200 OK in 15 milliseconds
NAME READY STATUS RESTARTS AGE
secondapp-5cf87c9f48-q8fxj 1/1 Running 0 5d1h
thirdpage-579947d95-mx2tp 1/1 Running 0 5d
$ kubectl --v=7 delete pod thirdpage-579947d95-mx2tp
I0201 08:12:32.998052 23824 loader.go:375] Config loaded from file: /root/.kube/config
I0201 08:12:33.008824 23824 round_trippers.go:420] DELETE https://k8smaster:6443/api/v1/namespaces/default/pods/thirdpage-579947d95-mx2tp
I0201 08:12:33.008858 23824 round_trippers.go:427] Request Headers:
I0201 08:12:33.008868 23824 round_trippers.go:431] Accept: application/json
I0201 08:12:33.008877 23824 round_trippers.go:431] Content-Type: application/json
I0201 08:12:33.008907 23824 round_trippers.go:431] User-Agent: kubectl/v1.16.1 (linux/amd64) kubernetes/d647ddb
I0201 08:12:33.043031 23824 round_trippers.go:446] Response Status: 200 OK in 34 milliseconds
pod "thirdpage-579947d95-mx2tp" deleted
I0201 08:12:33.045222 23824 round_trippers.go:420] GET https://k8smaster:6443/api/v1/namespaces/default/pods?fieldSelector=metadata.name%3Dthirdpage-579947d95-mx2tp
I0201 08:12:33.045239 23824 round_trippers.go:427] Request Headers:
I0201 08:12:33.045247 23824 round_trippers.go:431] Accept: application/json
I0201 08:12:33.045257 23824 round_trippers.go:431] User-Agent: kubectl/v1.16.1 (linux/amd64) kubernetes/d647ddb
I0201 08:12:33.053650 23824 round_trippers.go:446] Response Status: 200 OK in 8 milliseconds
I0201 08:12:33.054186 23824 round_trippers.go:420] GET https://k8smaster:6443/api/v1/namespaces/default/pods?fieldSelector=metadata.name%3Dthirdpage-579947d95-mx2tp&resourceVersion=2236351&watch=true
I0201 08:12:33.054201 23824 round_trippers.go:427] Request Headers:
I0201 08:12:33.054208 23824 round_trippers.go:431] User-Agent: kubectl/v1.16.1 (linux/amd64) kubernetes/d647ddb
I0201 08:12:33.054216 23824 round_trippers.go:431] Accept: application/json
I0201 08:12:33.056778 23824 round_trippers.go:446] Response Status: 200 OK in 2 milliseconds
Access from Outside the Cluster
เรารู้แล้วว่า kubectl
จะช่วยให้ชีวิตของเราง่ายขึ้นด้วยการแปลง command เป็น HTTP request แล้วส่งไปยัง kube-apiserver ให้เรา และก่อนหน้านี้เราก็รู้แล้วด้วยว่า การ request เข้าไปยัง kube-apiserver นั้น จะทำได้ก็ต้องมี certification และ key ที่ถูกต้องด้วย
คำถามคือ kubectl
ได้อย่างๆรว่า certificate และ key ที่ถูกต้องคืออะไร ?
ถ้าเราย้อนกลับไปดูหัวข้อก่อนหน้านี้ ที่กล่าวถึงเรื่องการ debug kubectl
จะเห็นว่าบรรทัดแรกของการ debug มีการอ่านค่าจาก file ชื่อ /root/.kube/config
นั่นแหละครับ มันคือ configuration file ของ kubectl
ที่เก็บ certificate และ key ที่ถูกต้องไว้ให้
โดย default, kubectl
จะอ่าน configuration file จาก home directory ของ user นั้นๆ (~/.kube/config
)
ตัวอย่าง ~/.kube/config
เป็นดังนี้
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTi...
server: https://k8smaster:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTi...
client-key-data: LS0tLS1CRUdJTi...
จาก file ด้านบน แต่ละตัวแปรมีความหมายดังนี้
-
apiVersion:
ใช้กำหนด version ของ API ของ kube-apiserver ที่เราจะคุยด้วย
-
clusters:
ใช้กำหนด list ของ cluster ซึ่งประกอบด้วย name: ที่ใช้อ้างอิง และ
-
certificate-authority-data:
CA ที่ใช้ในการ authentication request
-
server:
URL ของ kube-apiserver
-
certificate-authority-data:
-
users:
ใช้กำหนด list ของ user ซึ่งประกอบด้วย name: ที่ใช้อ้างอิง และ
-
client-certificate-data:
certificate
-
client-key-data:
key
-
client-certificate-data:
-
contexts:
ใช้กำหนด list ของ context ซึ่งประกอบด้วย name: ที่ใช้อ้างอิง และ
-
user:
อ้างอิงมากจาก name: ของ users:
-
cluster:
อ้างอิงมากจาก name: ของ clusters:
-
user:
-
current-context:
เป็นการระบุว่า command ที่เราส่งไปนั่น จะถูกส่งไปด้วย context ไหน โดยอ้างอิงจาก contexts: อีกที
-
kind:
ใช้กำหนดชนิดของ object นี้ นั้นก็คือ Config เสมอ
-
preference:
เป็น optional ยังไม่ใช้ในตอนนี้
Namespace
คำนี้ถ้าใครเคยอ่าน บนความก่อนๆ ของผมเรื่อง Containers Fundametals น่าจะรู้จัก namespace ในระดับ kernel เป็นอย่างดี และ kubernetes ก็มี namespace เช่นกัน
โดยทั้งสองมีจุดมุ่งหมายที่เหมือนกันคือ เพื่อแบ่งแยก resource แต่ kubenetes จะหมายถึงการแบ่ง (segmentation) object ภายใน cluster ออกเป็นกลุ่มๆ ไม่ใช่ kernel
ในการ call API ของ kubernetes ต้องมีการระบุ namespace ด้วยเสมอ แต่ถ้าเราสั่งงานด้วย kubectl
แล้วไม่ระบุ namespace kubectl
จะทำงานกับ namespace ที่ชื่อว่า default โดยอัตโนมัติ
namespace ที่มาพร้อมกับการติดตั้ง cluster มีดังนี้
-
default:
เป็นที่อยู่ของ resources ที่ไม่ได้ระบุ namespace
-
kube-node-lease:
เป็น namespace ที่เก็บ lease information
-
kube-public:
เป็น namespace ที่เก็บ information ทั่วๆ ไปที่ทุกคนอ่านได้ แม้แต่คนที่ไม่ได้ authenticate ก็ตาม
-
kube-system:
เป็นที่เก็บ infrastructure pods เช่น kube-apiserver, CoreDNS, kube-controller-manager, kube-scheduler, etc
เราสามารถตรวจสอบสถานะของ resources ที่อยู่ในทุก namespace ด้วยการใส่ option --all-namespaces
ให้กับ kubectl
Working with Namespaces
# List all namespaces in this cluster
$ kubectl get ns
# Create namespace called my_namespace
$ kubectl crete ns my_namespace
# Show detail of namespace name my_namespace
$ kubectl describe ns my_namespace
# Get status of namespace name my_namespace in YAML format
$ kubectl get ns/my_namespace -o yaml
# Delete namespace name my_namespace
$ kubectl delete ns/my_namespace
เมื่อ create namespace เรียบร้อยแล้ว ก็จะสามารถ assign pod ไปยัง namespace ได้ด้วยการระบุใน YAML file ดังนี้
apiVersion: v1
kind: pod
metadata:
name: redis
namespace: my_namespace #<-- Here
: :
API Resources of Kubernetes
kubectl
syntax ในการใช้ command kubectl
เป็นดังนี้
kubectl [command] [type] [name] [flag]
โดย
-
command:
verbs ที่เราจะทำกับ resource
(list of commands
) -
type:
type ของ resources ที่เราจะจัดการกับมัน
(list of resource types
) -
name:
ชื่อของ resources นั้นๆ
-
flag:
option เสริมระบุรายละเอียดของของคำสั่ง
REST API
หากต้องการดู log ของ pods ปกติจะใช้ kubectl logs firstpod
แต่ถ้าต้องการส่งเป็น curl
ไปแทนต้องส่งไปดังนี้
$ curl \
--cert /tmp/client.pem \
--key /tmp/client-key.pem \
--cacert /tmp/ca.pem \
-v \
-XGET \
https://k8sMaster:6443/api/v1/namespaces/default/pods/firstpod/log
โดยยังมี endpoint อื่นๆ ที่สามารถ call ได้ดังตัวอย่างดังต่อไปนี้
GET /api/v1/namespaces/{namespace}/pods/{name}/exec
GET /api/v1/namespaces/{namespace}/pods/{name}/log
GET /api/v1/watch/namespaces/{namespace}/pods/{name}
API ทั้งหมดของ Kubernetes ออกแบบโดยใช้ Swagger ซึ่งเป็นไปตาม OpenAPI
API Maturity
API ของ Kubernetes มีการแบ่งออกเป็นกลุ่มๆ และภายในกลุ่มยังแบ่งออกเป็น version ทำให้แต่ละ team สามารถแยกพัฒนา API กันได้อย่างอิสระ โดย release ของ API version มี 3 แบบ
-
Alpha Release:
โดย default จะ disable ไว้เพราะยังมี bug อยู่ และอาจมีการเปลี่ยนแปลงได้ตลอดเวลา รวมทั้งสามารถยกเลิกการใช้งานได้ตลอด ตัวอย่างเช่น v1alpha1
-
Beta Release:
โดย default จะ enable ไว้ release นี้ ได้รับการ Test มาดีในระดับหนึ่ง รองรับ backward compatibility อาจยังมี bugs และ issues อยู่ ตัวอย่างเช่น v2beta3
-
Stable Release:
stable แล้ว ได้รับการ test มาเป็นอย่างดี มีโอกาสพบ bug บ้าง แต่ไม่มาก ตัวอย่างเช่น v1
Posted on February 3, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.