Học cách viết và hiểu một Kubernetes YAML files


Học cách viết và hiểu một Kubernetes YAML files

Tổng quan

Ở bài viết trước mình đã nói sơ qua về kubenetes, trong bài viết này chúng ta sẽ tập trung vào cách viết và tạo một kubenetes pod, service,…

Ở đây mình sử dụng minikube để demo, các bạn có thể cài đặt theo hướng dẫn ở đây.

Pod

Pod là đơn vị nhỏ nhất trong kubenetes, bên trong nó sẽ chứa những container. Các container bên trong sẽ dùng chung một địa chỉ ip của pod và không gian port. Địa chỉ ip của pod chỉ là một địa chỉ ip nội bộ, không phải ip của cluster.

image

Thông thường, người ta sẽ chỉ chạy duy nhất một container bên trong một Pod, và chỉ có một vài trường hợp số ít, khi cần một số helper như ghi log hay một helper gì đó người ta mới chạy nhiều container bên trong một Pod.

image

Về miệc chạy multi container có thể xem thêm ở đây.

Demo Pod

test-pod.yaml

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.24.0
ports:
- containerPort: 80

Một file yaml với kind Pod sẽ có dạng như trên, ngoài ra ta có thể quản lý các pod bằng cách thêm các labels cho Pod:

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
test: test
spec:
containers:
- name: nginx
image: nginx:1.24.0
ports:
- containerPort: 80

Chạy lệnh kubectl apply -f test-pod.yaml:

1
2
3
4
5
> kubectl apply -f test-pod.yaml
pod/nginx created
> kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 29m

Ta đã tạo thành công một Pod, tuy nhiên ta không thể truy cập vào nginx mặc dù đã có thuộc tính containerPort: 80:

1
2
3
4
> minikube ip
192.168.59.101
> curl 192.168.59.101:80
curl: (7) Failed to connect to 192.168.59.101 port 80 after 1 ms: Couldn't connect to server

Tại sao lại vậy? Lý do là vì Pod chỉ là một đơn vị trong một cluster, do đó nó chỉ có thể truy cập bên trong cluster đó.

image

Để đưa port đó ra ngoài, ta cần sử dụng đến Service.

Service

image

Service được dùng để public port của Pod ra ngoài, để xác định Pod nào cần public port ta cần chỉ định Selector trỏ đến label của Pod đó.

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: service-web
spec:
selector:
app: nginx
ports:
- name: tcp
port: 80
nodePort: 30007
type: NodePort

Một file yml của Service sẽ có dạng như trên.

  • name: tcp: chỉ định giao thức trên port.

Có 4 loại type của Service:

  • type: ClusterIP: Nếu ta chỉ định là ClusterIP thì service của chúng ta chỉ có thể truy cập bên trong cluster và không thể truy cập từ bên ngoài ví dụ như truy cập từ browser.

    image

    Ta sử dụng ClusterIP nếu service đó chỉ là một internal service. Ví dụ như một microservices, ta chỉ muốn nó chạy nội bộ và chỉ có microservices khác giao tiếp với nó.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    apiVersion: v1
    kind: Service
    metadata:
    name: clusterip-service

    spec:
    selector:
    app: nginx

    ports:
    - name: tcp
    port: 80

    type: ClusterIP
    1
    2
    3
    4
    5
    6
    > kubectl get all
    NAME READY STATUS RESTARTS AGE
    pod/nginx 1/1 Running 0 103m

    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    service/clusterip-service ClusterIP 10.111.124.5 <none> 80/TCP 12s

    Ta thấy nó được cấp một địa chỉ ip, bây giờ các service khác ở trong cùng cluster có thể tương tác với nó bằng ip trên hoặc http://clusterip-service

  • type: NodePort

    image

    NodePort cho phép chúng ta expose port để có thể truy cập bên ngoài cụm, port expose ra sẽ được truy cập từ bằng nodePort. Ta chỉ có thể chỉ định NodePort nằm tròng khoảng 30000-32768.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    apiVersion: v1
    kind: Service
    metadata:
    name: service-web

    spec:
    selector:
    app: nginx

    ports:
    - name: tcp
    port: 80
    nodePort: 30007

    type: NodePort
    1
    2
    3
    4
    5
    6
    7
    8
    9
    NAME                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
    service/clusterip-service ClusterIP 10.111.124.5 <none> 80/TCP 60m
    service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3h16m
    service/service-web NodePort 10.100.217.211 <none> 80:30007/TCP 63m
    > minikube ip
    192.168.59.101
    > curl 192.168.59.101:30007
    StatusCode : 200
    StatusDescription : OK

ReplicaSets

ReplicaSet được dùng để duy trì tính ổn định của Pod khi tạo ra nhiều bản sao của Pod đó. Nếu một Pod trong số Replicas đó crash thì server vẫn có thể duy trì nhờ vào các bản sao.

Viết một file Replicas type khá đơn giản, ta chỉ cần wrap Pod vào trong spec như sau:

  • Pod được quản lý bởi ReplicaSet nên ta có thể bỏ name của Pod trong phần metadata của Pod.
  • Tương tự như service, ta cầng dùng đến selector để chỉ định Pod nào cần Replica.
1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginxweb
template:
metadata:
labels:
app: nginxweb
spec:
containers:
- name: nginx
image: nginx:1.24.0
ports:
- containerPort: 80

Apply file trên:

1
2
3
4
5
6
7
8
9
> kubectl get all
NAME READY STATUS RESTARTS AGE
pod/nginx-drb62 0/1 ContainerCreating 0 45s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 92m

NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx 1 1 0 45s

Pod đã được cấp tên ở trong replicaset nên đó là lý do ta không cần metadata name.

Deployment

Deployment là một khái niệm nâng cấp hơn của ReplicaSet, nó tương tự như ReplicaSet nhưng điểm khác biệt là giúp ứng dụng “rolling updates”. Rolling updates cho phép cập nhật của Deployments diễn ra mà không gây gián đoạn bằng cách cập nhật từng đợt Pod một cách tăng dần với các phiên bản mới.

Cách deployment rolling updates:

  • Giả sử ban đầu ta có một tập ReplicaSet từ deployment như sau:image
  • Bây giờ ta muốn update Pod từ v1 thành v2, ta sửa file yaml và appy nó trên kube. Sau khi apply, kube sẽ tiến hành tạo một ReplicaSet mới với Pod v2:
    image
  • Sau khi ReplicaSet mới được hoàn thiện, và sẵn sàng cho request. Kube sẽ tiến hành xóa các Pod trong ReplicaSet cũ, tuy nhiên ReplicaSet vẫn ở đấy với 0 Pod:
    image

Demo

xyz-deployment.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginxweb
template:
metadata:
labels:
app: nginxweb
spec:
containers:
- name: nginx
image: nginx:1.24
ports:
- containerPort: 80
1
2
3
4
5
6
7
8
9
10
11
12
13
kubectl get all
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-5c5c46c579-db5dj 1/1 Running 0 9s
pod/nginx-deployment-5c5c46c579-djcsk 1/1 Running 0 9s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 118m

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 2/2 2 2 9s

NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-5c5c46c579 2 2 2 9s

Bây giờ đổi version của nginx để xem cách deployment rolling update:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginxweb
template:
metadata:
labels:
app: nginxweb
spec:
containers:
- name: nginx
image: nginx:1.25.2
ports:
- containerPort: 80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
> kubectl get all
pod/nginx-deployment-5c5c46c579-db5dj 1/1 Running 0 2m28s
pod/nginx-deployment-5c5c46c579-djcsk 1/1 Running 0 2m28s
pod/nginx-deployment-77dff7f778-5mqkn 0/1 Pending 0 2s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 120m

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 0/2 1 0 2m28s

NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-5c5c46c579 2 2 0 2m28s
replicaset.apps/nginx-deployment-77dff7f778 1 1 0 2s
> kubectl get all
pod/nginx-deployment-5c5c46c579-db5dj 1/1 Running 0 2m30s
pod/nginx-deployment-5c5c46c579-djcsk 1/1 Running 0 2m30s
pod/nginx-deployment-77dff7f778-5mqkn 0/1 Pending 0 4s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 120m

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 0/2 1 0 2m30s

NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-5c5c46c579 2 2 0 2m30s
replicaset.apps/nginx-deployment-77dff7f778 1 1 0 4s
> kubectl get all
pod/nginx-deployment-5c5c46c579-db5dj 1/1 Running 0 2m34s
pod/nginx-deployment-5c5c46c579-djcsk 1/1 Running 0 2m34s
pod/nginx-deployment-77dff7f778-5mqkn 0/1 Pending 0 8s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 120m

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 0/2 1 0 2m35s

NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-5c5c46c579 2 2 0 2m35s
replicaset.apps/nginx-deployment-77dff7f778 1 1 0 9s
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-5c5c46c579-db5dj 1/1 Running 0 2m38s
pod/nginx-deployment-5c5c46c579-djcsk 1/1 Running 0 2m38s
pod/nginx-deployment-77dff7f778-5mqkn 0/1 ContainerCreating 0 12s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 121m

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 2/2 1 2 2m39s

NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-5c5c46c579 2 2 2 2m39s
replicaset.apps/nginx-deployment-77dff7f778 1 1 0 13s
> kubectl get all
NAME READY STATUS RESTARTS AGE
pod/nginx-deployment-77dff7f778-5mqkn 1/1 Running 0 19s
pod/nginx-deployment-77dff7f778-5vvtq 1/1 Running 0 6s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 121m

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-deployment 2/2 2 2 2m45s

NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-deployment-5c5c46c579 0 0 0 2m45s
replicaset.apps/nginx-deployment-77dff7f778 2 2 2 19s

Ngoài ra ta có thể xem quá trình rollout đơn giản hơn bằng lệnh rollout:

1
2
3
4
5
6
7
8
9
> kubectl apply -f .\deployment.yaml
deployment.apps/nginx-deployment configured
> kubectl rollout status deployment nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 2 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
> kubectl rollout history deploy nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
3 <none>
4 <none>
> kubectl rollout history deploy nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
3 <none>
4 <none>

> kubectl rollout undo deploy nginx-deployment --to-revision=3
deployment.apps/nginx-deployment rolled back
> kubectl rollout history deploy nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
4 <none>
5 <none>