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.