Monday, January 20, 2025

[Kubernetes 8] Sử dụng Service và Secret Tls trong Kubernetes

-

Khai báo và sử dụng service, các kiểu service như NodePort, ClusterIP. Định nghĩa endpoint cho Service, sử dụng Secret Tls

Service trong Kubernetes

Các POD được quản lý trong Kubernetes, trong vòng đời của nó chỉ diễn ra theo hướng – được tạo ra, chạy và khi nó kết thúc thì bị xóa và khởi tạo POD mới thay thế. ! Có nghĩa ta không thể có tạm dừng POD, chạy lại POD đang dừng …

Mặc dù mỗi POD khi tạo ra nó có một IP để liên lạc, tuy nhiên vấn đề là mỗi khi POD thay thế thì là một IP khác, nên các dịch vụ truy cập không biết IP mới nếu ta cấu hình nó truy cập đến POD nào đó cố định. Để giải quết vấn đề này sẽ cần đến Service.

Service (micro-service) là một đối tượng trừu tượng nó xác định ra một nhóm các POD và chính sách để truy cập đến POD đó. Nhóm cá POD mà Service xác định thường dùng kỹ thuật Selector (chọn các POD thuộc về Service theo label của POD).

Cũng có thể hiểu Service là một dịch vụ mạng, tạo cơ chế cân bằng tải (load balancing) truy cập đến các điểm cuối (thường là các Pod) mà Service đó phục vụ.

1. Tạo Service kiểu ClusterIP không Selector

1.1. Đầu tiên ta tại file yaml với nội dung sau

cat > ./prometheus-svc.yaml << OEF
apiVersion: v1
kind: Service
metadata:
  name: svc-prometheus
spec:
  type: ClusterIP
  ports:
    - name: http
      port: 80
      targetPort: 80
OEF

File trên khai báo một service đặt tên svc1, kiểu của service là ClusterIP, đây là kiểu mặc định (ngoài ra còn có kiểu NodePort, LoadBalancer, ExternalName), phần khai báo cổng gồm có cổng của service (port) tương ứng ánh xạ vào cổng của endpoint (targetPort – thường là cổng Pod).

Triển khai file trên

root@k8s-standalone:/home# kubectl apply -f prometheus-svc.yaml 
service/svc-prometheus created

Để lấy các service

root@k8s-standalone:/home# kubectl get svc -o wide
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE     SELECTOR
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP   34h     <none>
svc-prometheus   ClusterIP   10.103.21.142   <none>        80/TCP    6m33s   <none>

Để xem thông tin của service svc-prometheus

root@k8s-standalone:/home# kubectl describe svc/svc-prometheus
Name:              svc-prometheus
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.103.21.142
IPs:               10.103.21.142
Port:              http  80/TCP
TargetPort:        80/TCP
Endpoints:         <none>
Session Affinity:  None
Events:            <none>

Hệ thống đã tạo ra service có tên là svc-prometheus với địa chỉ IP là 10.97.217.42, khi Pod truy cập địa chỉ IP này với cổng 80 thì nó truy cập đến các endpoint định nghĩa trong dịch vụ. Tuy nhiên thông tin service cho biết phần endpoints là không có gì, có nghĩa là truy cập thì không có phản hồi nào.

1.2. Tiếp theo bạn tạo EndPoint cho Service

cat > ./prometheus-endpoints.yaml << OEF
apiVersion: v1
kind: Endpoints
metadata:
  name: svc-prometheus
subsets:
  - addresses:
      - ip: 10.244.20.186
    ports:
      - name: http
        port: 80
OEF

Triển khai với lệnh

root@k8s-standalone:/home# kubectl apply -f prometheus-endpoints.yaml 
endpoints/svc-prometheus created

Xem lại thông tin ta đã thấy Endpoints cho service svc-prometheus

root@k8s-standalone:/home# kubectl describe svc/svc-prometheus
Name:              svc-prometheus
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.103.21.142
IPs:               10.103.21.142
Port:              http  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.20.186:80
Session Affinity:  None
Events:            <none>

Như vậy svc-prometheus đã có endpoints, khi truy cập svc-prometheus:80 hoặc svc-prometheus.default:80 hoặc 10.103.21.142:80 có nghĩa là đang truy truy cập 10.103.21.142:80

Do có dịch vụ CoreDns nên có thể truy cập thông qua phân giải tên, truy cập đến service theo cấu trúc namespace.servicename nếu cùng namespace chỉ cần servicenname

Ví dụ trên sử dụng Service (không có Selector), cần tạo Endpoint có tên cùng tên Service: dùng loại này khi cần tạo ra một điểm truy cập dịch vụ tương tự như proxy đến một địa chỉ khác (một server khác, một dịch vụ khác ở namespace khác …)

2. Thực hành tạo Service có Selector, chọn các Pod là Endpoint của Service

2.1. Trước tiên triển khai trên Cluster 2 POD chạy độc lập, các POD đó đều có nhãn app=monitor

cat > ./prometheus-deployment.yaml << OEF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus
spec:
  selector:
    matchLabels:
      app: monitor
  replicas: 3
  template:
    metadata:
      labels:
        app: monitor
    spec:
      containers:
        - name: prometheus
          image: prom/prometheus
          ports:
            - containerPort: 9090
          resources:
            limits:
              memory: "150M"
              cpu: "100m"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pushgateway
spec:
  selector:
    matchLabels:
      app: monitor
  replicas: 3
  template:
    metadata:
      labels:
        app: monitor
    spec:
      containers:
        - name: pushgateway
          image: prom/pushgateway
          ports:
            - containerPort: 9091
          resources:
            limits:
              memory: "150M"
              cpu: "100m"

OEF

Triển khai file trên

kubectl apply -f ./prometheus-deployment.yaml

Kết quả ta có 3 pod chạy prometheus và 3 pod chạy pushgateway và chúng dùng chung 1 nhãn là app=monitor như dưới

root@k8s-standalone:/home# kubectl get po -o wide -l 'app=monitor'
NAME                           READY   STATUS    RESTARTS   AGE     IP              NODE             NOMINATED NODE   READINESS GATES
prometheus-5d7d45c474-h9f8n    1/1     Running   0          2m30s   10.244.20.133   k8s-standalone   <none>           <none>
prometheus-5d7d45c474-tbxw6    1/1     Running   0          2m30s   10.244.20.138   k8s-standalone   <none>           <none>
prometheus-5d7d45c474-x2wfq    1/1     Running   0          2m30s   10.244.20.137   k8s-standalone   <none>           <none>
pushgateway-6f77d5b477-kqrhb   1/1     Running   0          2m30s   10.244.20.135   k8s-standalone   <none>           <none>
pushgateway-6f77d5b477-krnm4   1/1     Running   0          2m30s   10.244.20.134   k8s-standalone   <none>           <none>
pushgateway-6f77d5b477-w9l47   1/1     Running   0          2m30s   10.244.20.136   k8s-standalone   <none>           <none>

2.2. Thực hành tạo Service kiểu ClusterIP

Ta tạo ra service có tên svc-monitor có thêm thiết lập selector chọn nhãn app=monitor

cat > ./prometheus-svc.yaml << OEF
apiVersion: v1
kind: Service
metadata:
  name: svc-monitor
spec:
  selector:
     app: monitor
  type: ClusterIP
  ports:
    - name: http
      port: 80
      targetPort: 80
OEF

Triển khai

root@k8s-standalone:/home# kubectl apply -f ./prometheus-svc.yaml 
service/svc-monitor created

Kiểm tra lại service svc-monitor

root@k8s-standalone:/home# kubectl describe svc/svc-monitor
Name:              svc-monitor
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=monitor
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.102.135.30
IPs:               10.102.135.30
Port:              http  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.20.133:80,10.244.20.134:80,10.244.20.135:80 + 3 more...
Session Affinity:  None
Events:            <none>

Thông tin trên ta có, endpoint của svc-monitor là 10.244.20.133:80,10.244.20.135:80,… các IP này tương ứng là của 2 POD trên. Khi truy cập địa chỉ svc-monitor:80 hoặc 10.102.135.30:80 thì cân bằng tải hoạt động sẽ là truy cập đến 10.244.20.133:80 (prometheus) hoặc 10.244.20.135:80 (pushgateway)

2.3. Thực hành tạo Service kiểu NodePort

Kiểu NodePort này tạo ra có thể truy cập từ ngoài internet bằng IP của các Node, ví dụ sửa dịch vụ svc-monitor trên thành dịch vụ svc-monitor-NodePort kiểu NodePort

cat > ./prometheus-svc.yaml << OEF
apiVersion: v1
kind: Service
metadata:
  name: svc-forwardport
spec:
  selector:
     app: monitor
  type: NodePort
  ports:
    - name: prometheus
      port: 80
      targetPort: 9090
      nodePort: 31080
    - name: pushgateway
      port: 80
      targetPort: 9091
      nodePort: 31080
OEF

Trong file trên, thiết lập kiểu với type: NodePort, lúc này Service tạo ra có thể truy cập từ các IP của Node với một cổng nó ngẫu nhiên sinh ra trong khoảng 30000-32767. Nếu muốn ấn định một cổng của Service mà không để ngẫu nhiên thì dùng tham số nodePort như trên.

Triển khai file trên

root@k8s-standalone:/home# kubectl apply -f ./prometheus-svc-forwardport.yaml
service/svc-forwardport created

Kiểm tra lại service

cat > ./prometheus-svc.yaml << OEF
apiVersion: v1
kind: Service
metadata:
  name: svc-forwardport
spec:
  selector:
     app: monitor
  type: NodePort
  ports:
    - name: prometheus
      port: 9090
      targetPort: 9090
      nodePort: 31080
    - name: pushgateway
      port: 9091
      targetPort: 9091
      nodePort: 31081
OEF

Sau khi triển khai có thể truy cập với IP là địa chỉ IP của các Node và cổng là 31080, ví dụ 192.168.13.238:31080 (prometheus) hoặc 192.168.13.238:31081 (pushgateway)

root@k8s-standalone:/home# kubectl get svc
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
kubernetes        ClusterIP   10.96.0.1       <none>        443/TCP                         34h
svc-forwardport   NodePort    10.97.238.88    <none>        9090:31080/TCP,9091:31081/TCP   45s
svc-monitor       ClusterIP   10.102.135.30   <none>        80/TCP

Ví dụ với 192.168.13.238:31080 (prometheus)

Ví dụ với 192.168.13.238:31081 (pushgateway)

3. Ứng dụng Service, Deployment, Secret

Đầu tiên mình tạo namespace monitor mới

root@k8s-standalone:/home# kubectl create ns monitor
namespace/monitor created

Verify namespace

root@k8s-standalone:/home# kubectl get ns monitor
NAME      STATUS   AGE
monitor   Active   8s

Triển khai nó

root@k8s-standalone:/home# kubectl apply -f prometheus-deployment.yaml 
deployment.apps/prometheus created

Verify các pod của namespace monitor vừa được tạo

root@k8s-standalone:/home# kubectl get po -n monitor
NAME                          READY   STATUS    RESTARTS   AGE
prometheus-77dddff598-2pdzs   1/1     Running   0          7s

Tiếp theo mình tạo thư mục chứa các file certs

root@k8s-standalone:/home# mkdir -p /home/certs

Do không có cert nên mình sẽ tự sinh xác thực với openssl, xác thực SSL gồm có server certificate và private key, đối với nginx cấu hình qua hai thiết lập ssl_certificate và ssl_certificate_key tương ứng ta đã cấu hình là hai file tls.crt, tls.key. Ta để tên này vì theo cách đặt tên của letsencrypt.org, sau này bạn có thể thận tiện hơn nếu xin xác thực miễn phí từ đây.

Thực hiện lệnh sau để sinh file tự xác thực

root@k8s-standalone:/home# openssl req -nodes -newkey rsa:2048 -keyout certs/tls.key -out certs/ca.csr -subj "/CN=monitor.hoanghd.com"
Can't load /root/.rnd into RNG
139900962353600:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/root/.rnd
Generating a RSA private key
...............................+++++
.............................................+++++
writing new private key to 'certs/tls.key'
-----

Và lệnh

root@k8s-standalone:/home# openssl x509 -req -sha256 -days 365 -in certs/ca.csr -signkey certs/tls.key -out certs/tls.crt
Signature ok
subject=CN = monitor.hoanghd.com
Getting Private key

Đến đây có 2 file tls.key và tls.crt

root@k8s-standalone:/home# ls certs/
ca.csr  tls.crt  tls.key

Thi hành lệnh sau để tạo ra một Secret kiểu tls, tức là chứa xác thực SSL

root@k8s-standalone:/home# kubectl create secret tls secret-prometheus-cert --cert=certs/tls.crt  --key=certs/tls.key -n monitor
secret/secret-prometheus-cert created

Secret này tạo ra thì mặc định nó đặt tên file là tls.crt và tls.key có thể xem với lệnh

root@k8s-standalone:/home# kubectl describe secret/secret-prometheus-cert -n monitor
Name:         secret-prometheus-cert
Namespace:    monitor
Labels:       <none>
Annotations:  <none>

Type:  kubernetes.io/tls

Data
====
tls.key:  1708 bytes
tls.crt:  1017 bytes

Tạo thư mục lưu file config của nginx

root@k8s-standalone:/home# mkdir -p /home/nginx/

Thêm vào nội dung sau

echo '''
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
  worker_connections  4096;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen 80;
        server_name localhost;                    # my-site.com
        location / {
            proxy_pass http://svc-prometheus:9090/;
        }
    }
    server {
        listen  443 ssl;
        server_name  localhost;                   # my-site.com;
        ssl_certificate /certs/tls.crt;           # fullchain.pem
        ssl_certificate_key /certs/tls.key;       # privkey.pem
        location / {
            proxy_pass http://svc-prometheus:9090/;
        }
    }
}
''' > /home/nginx/nginx.conf

Tạo 1 Deployment với nội dung như sau

cat > ./prometheus-deployment.yaml << OEF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus
  namespace: monitor
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prometheus
  template:
    metadata:
      labels:
        app: prometheus
    spec: 
      volumes:
        - name: cert-volume
          secret:
             secretName: "secret-prometheus-cert" 
        - name: "config-nginx-volume"
          hostPath:
            path: "/home/nginx/nginx.conf"
      containers:
      - name: prometheus
        image: prom/prometheus
        imagePullPolicy: "Always"
        resources:
          limits:
            memory: "128Mi"
            cpu: "100m"
        ports:
        - containerPort: 9090 
      - name: nginx-prometheus
        image: nginx
        imagePullPolicy: "Always"
        resources:
          limits:
            memory: "128Mi"
            cpu: "100m"
        ports:
        - containerPort: 80 
        - containerPort: 443 
        volumeMounts:
          - mountPath: "/certs"
            name: cert-volume
          - mountPath: "/etc/nginx/nginx.conf"
            name: config-nginx-volume
OEF

Sử dụng Secret cho Pod ta tạo 1 file service yaml như dưới

cat > ./prometheus-svc.yaml << OEF
apiVersion: v1
kind: Service
metadata:
  name: svc-prometheus
  namespace: monitor
spec:
  type: ClusterIP
  ports:
  - port: 9090        # cổng dịch vụ ánh xạ vào cổng POD
    targetPort: 9090    # cổng POD ánh xạ vào container
    protocol: TCP
    name: port-prometheus

  # Chú ý đúng với Label của POD tại Deployment
  selector:
    app: prometheus
OEF

Triển khai file này

kubectl apply -f ./prometheus-svc.yaml

Tạo file nginx-svc.yaml

cat > ./nginx-svc.yaml << OEF
apiVersion: v1
kind: Service
metadata:
  name: svc-nginx
  namespace: monitor
spec:
  type: NodePort
  ports:
  - port: 80        # cổng dịch vụ ánh xạ vào cổng POD
    targetPort: 80    # cổng POD ánh xạ vào container
    protocol: TCP
    name: http
    nodePort: 31480   # cổng NODE ánh xạ vào cổng dịch vụ (chỉ chọn 30000-32767)

  - port: 443        # cổng dịch vụ ánh xạ vào cổng POD
    targetPort: 443    # cổng POD ánh xạ vào container
    protocol: TCP
    name: https
    nodePort: 31443   # cổng NODE ánh xạ vào cổng dịch vụ (chỉ chọn 30000-32767)

  # Chú ý đúng với Label của POD tại Deployment
  selector:
    app: prometheus
OEF

Triển khai file nginx-svc.yaml

root@k8s-standalone:/home# kubectl apply -f nginx-svc.yaml 
service/svc-nginx created

Verify lại service svc-prometheus vaf svc-nginx

root@k8s-standalone:/home# kubectl get svc -n monitor
NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
svc-nginx        NodePort    10.98.58.48      <none>        80:31480/TCP,443:31443/TCP   52s
svc-prometheus   ClusterIP   10.109.221.121   <none>        9090/TCP                     105s

Hoặc có thể sử dụng lệnh describe để xem chi tiết hơn, đầu tiên là Verify service prometheus

root@k8s-standalone:/home# kubectl describe svc/svc-prometheus -n monitor
Name:              svc-prometheus
Namespace:         monitor
Labels:            <none>
Annotations:       <none>
Selector:          app=prometheus
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.109.221.121
IPs:               10.109.221.121
Port:              port-prometheus  9090/TCP
TargetPort:        9090/TCP
Endpoints:         10.244.20.178:9090
Session Affinity:  None
Events:            <none>

Verify service nginx

root@k8s-standalone:/home# kubectl describe svc/svc-nginx -n monitor
Name:                     svc-nginx
Namespace:                monitor
Labels:                   <none>
Annotations:              <none>
Selector:                 app=prometheus
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.98.58.48
IPs:                      10.98.58.48
Port:                     http  80/TCP
TargetPort:               80/TCP
NodePort:                 http  31480/TCP
Endpoints:                10.244.20.178:80
Port:                     https  443/TCP
TargetPort:               443/TCP
NodePort:                 https  31443/TCP
Endpoints:                10.244.20.178:443
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

Giờ có thể truy cập website của prometheus từ địa chỉ IP của Node với cổng tương ứng https://192.168.13.238:31443

Và đây là kết quả

Chúc các bạn thành công.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

4,956FansLike
256FollowersFollow
223SubscribersSubscribe
spot_img

Related Stories