Sổ tay Kubernetes: Triển khai ứng dụng đa container (p1)
Loạt bài viết sổ tay Kubernetes:
- Sổ tay Kubernetes: Giới thiệu về Kubernetes.
- Sổ tay Kubernetes: Các khái niệm cơ bản trong Kubernetes.
- Sổ tay Kubernetes: Phương pháp triển khai bằng khai báo.
- Sổ tay Kubernetes: Triển khai ứng dụng đa container (p1) (bài viết này).
- Sổ tay Kubernetes: Triển khai ứng dụng đa container (p2).
- Sổ tay Kubernetes: Triển khai ứng dụng đa container (p3).
- Sổ tay Kubernetes: làm việc với Ingress Controller (p1).
- Sổ tay Kubernetes: làm việc với Ingress Controller (p2).
Ở phần trước, bạn đã triển khai cùng một ứng dụng hello-kube
theo cách tiếp cận khai báo.
Cho đến lúc này bạn đã làm việc với các ứng dụng chạy trong một container.
Trong phần này, bạn sẽ làm việc với một ứng dụng bao gồm hai container. Bạn cũng sẽ làm quen với kỹ thuật Deployment
, ClusterIP
, PersistentVolume
, PersistentVolumeClaim
và một số lỗi.
Ứng dụng của bạn sẽ làm việc là một API ghi chú nhanh đơn giản với đầy đủ chức năng CRUD. Ứng dụng sử dụng PostgreSQL làm hệ thống cơ sở dữ liệu của nó. Vì vậy, bạn không chỉ triển khai ứng dụng mà còn thiết lập mạng nội bộ giữa ứng dụng và cơ sở dữ liệu.
Mã cho ứng dụng nằm trong thư mục notes-api
bên trong repository dự án.
.
├── api
├── docker-compose.yaml
└── postgres
2 directories, 1 file
Mã nguồn ứng dụng nằm bên trong thư mục api
và thư mục postgres
chứa một Dockerfile
để tạo image postgres
tùy chỉnh. Tập tin docker-compose.yaml
chứa các cấu hình cần thiết để chạy các ứng dụng sử dụng lệnh docker-compose
.
Cũng giống như với dự án trước, bạn có thể xem tập tin Dockerfile
của từng dịch vụ riêng lẻ để biết cách ứng dụng chạy bên trong container. Hoặc bạn cũng có thể xem tập tin docker-compose.yaml
.
version: "3.8"
services:
db:
build:
context: ./postgres
dockerfile: Dockerfile.dev
volumes:
- db-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: 63eaQB9wtLqmNBpg
POSTGRES_DB: notesdb
api:
build:
context: ./api
dockerfile: Dockerfile.dev
ports:
- 3000:3000
volumes:
- /home/node/app/node_modules
- ./api:/home/node/app
environment:
DB_CONNECTION: pg
DB_HOST: db
DB_PORT: 5432
DB_USER: postgres
DB_DATABASE: notesdb
DB_PASSWORD: 63eaQB9wtLqmNBpg
volumes:
db-data:
name: notes-db-dev-data
Nhìn vào định nghĩa dịch vụ api
, bạn có thể thấy rằng ứng dụng chạy trên cổng 3000 bên trong container. Nó cũng yêu cầu một loạt các biến môi trường để hoạt động bình thường.
Có thể bỏ qua các volumne vì chúng chỉ cần thiết cho mục đích phát triển và cấu hình xây dựng dành riêng cho Docker. Vì vậy, hai bộ thông tin mà bạn có thể chuyển sang tệp cấu hình Kubernetes của mình hầu như không thay đổi như sau:
- Ánh xạ cổng – bởi vì bạn sẽ phải hiển thị cùng một cổng từ vùng chứa.
- Các biến môi trường – bởi vì các biến này sẽ giống nhau trên tất cả các môi trường (mặc dù vậy, các giá trị sẽ thay đổi).
Dịch vụ db
thậm chí còn đơn giản hơn. Tất cả những gì nó có là một loạt các biến môi trường. Bạn thậm chí có thể sử dụng image postgres
chính thức thay vì image tùy chỉnh.
Nhưng lý do duy nhất cho một image tùy chỉnh là nếu bạn muốn instance cơ sở dữ liệu đi kèm với bảng notes
được tạo trước.
Bảng này là cần thiết cho ứng dụng. Nếu bạn nhìn vào bên trong thư mục postgres/docker-entrypoint-initdb.d
, bạn sẽ thấy một tệp có tên notes.sql
được sử dụng để tạo cơ sở dữ liệu trong quá trình khởi tạo.
Kế hoạch triển khai
Không giống như dự án trước mà bạn đã triển khai, dự án này sẽ phức tạp hơn một chút.
Trong dự án này, bạn sẽ không tạo một mà là ba instance của Notes API. Ba instance này sẽ được hiển thị bên ngoài cụm bằng cách sử dụng dịch vụ LoadBalancer
.
Ngoài ba instance này, sẽ có một instance khác của hệ thống cơ sở dữ liệu PostgreSQL. Cả ba instance của ứng dụng Notes API sẽ giao tiếp với instance cơ sở dữ liệu này bằng dịch vụ ClusterIP
.
Dịch vụ ClusterIP
là một loại khác của dịch vụ Kubernetes hiển thị một ứng dụng trong cụm của bạn. Điều đó có nghĩa là không có lưu lượng truy cập bên ngoài nào có thể tiếp cận ứng dụng bằng dịch vụ ClusterIP
.
Trong dự án này, cơ sở dữ liệu chỉ được truy cập bởi Notes API, vì vậy việc hiển thị dịch vụ cơ sở dữ liệu trong cụm là một lựa chọn lý tưởng.
Tôi đã đề cập trong phần trước rằng bạn không nên tạo pod trực tiếp. Vì vậy, trong dự án này, bạn sẽ sử dụng Deployment
thay vì Pod
.
Replication Controllers, Replica Sets và Deployments
Theo tài liệu Kubernetes –
“Trong Kubernetes, controller là các vòng lặp điều khiển theo dõi trạng thái của cụm của bạn, sau đó thực hiện hoặc yêu cầu thay đổi nếu cần. Mỗi controller cố gắng di chuyển trạng thái cụm hiện tại gần với trạng thái mong muốn. Vòng lặp điều khiển là một vòng lặp không kết thúc điều chỉnh trạng thái của một hệ thống. “
ReplicationController
cho phép bạn dễ dàng tạo nhiều bản sao rất dễ dàng. Khi số lượng bản sao mong muốn được tạo, controller sẽ đảm bảo rằng trạng thái vẫn như vậy.
Nếu sau một thời gian, bạn quyết định giảm số lượng bản sao, thì ReplicationController
sẽ thực hiện các hành động ngay lập tức và loại bỏ các pod thừa.
Ngược lại, nếu số lượng bản sao thấp hơn những gì bạn muốn (có thể một số pod bị lỗi) thì ReplicationController
sẽ tạo ra những bản sao mới để phù hợp với trạng thái mong muốn.
Nghe có vẻ hữu ích đối với bạn, nhưng ReplicationController
hiện nay không phải là cách được khuyến nghị để tạo bản sao. Một API mới hơn được gọi là ReplicaSet
đã thay thế nó.
Ngoài thực tế là ReplicaSet
có thể cung cấp cho bạn nhiều tùy chọn hơn, cả ReplicationController
và ReplicaSet
ít nhiều đều giống nhau.
Có nhiều tùy chọn hơn là tốt nhưng điều tốt hơn nữa là có sự linh hoạt hơn trong việc triển khai và quay lại các bản cập nhật. Đây là lúc một API Kubernetes khác có tên là Deployment
xuất hiện.
Deployment
giống như một phần mở rộng cho ReplicaSet
vốn đã rất tuyệt. Deployment
không chỉ cho phép bạn tạo bản sao ngay lập tức mà còn cho phép bạn phát hành bản cập nhật hoặc quay lại chức năng trước đó chỉ bằng một hoặc hai lệnh kubectl
.
ReplicationController | ReplicaSet | Deployment |
---|---|---|
Cho phép tạo nhiều pod một cách dễ dàng | Cho phép tạo nhiều pod một cách dễ dàng | Cho phép tạo nhiều pod một cách dễ dàng |
Phương pháp sao chép ban đầu trong Kubernetes | Có nhiều tùy chọn linh hoạt hơn | Mở rộng ReplicaSet với việc triển khai và khôi phục bản cập nhật dễ dàng |
Trong dự án này, bạn sẽ sử dụng Deployment
để duy trì các phiên bản ứng dụng.
Tạo Deployment đầu tiên của bạn
Hãy bắt đầu bằng cách viết tệp cấu hình để triển khai Notes API. Tạo thư mục k8s
bên trong thư mục dự án notes-api
.
Bên trong thư mục đó, hãy tạo một tập tin có tên api-deployment.yaml
và đặt nội dung sau vào đó:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
replicas: 3
selector:
matchLabels:
component: api
template:
metadata:
labels:
component: api
spec:
containers:
- name: api
image: fhsinchy/notes-api
ports:
- containerPort: 3000
Trong tập này, các trường apiVersion
, kind
, metadata
và spec
phục vụ mục đích tương tự như các dự án trước đó. Những thay đổi đáng chú ý trong tệp này so với tệp cuối cùng như sau:
- Để tạo một Pod, bắt buộc
apiVersion
làv1
. Nhưng để tạo Deployment, phiên bản bắt buộc làapps/v1
. Các phiên bản API Kubernetes đôi khi có thể hơi khó hiểu, nhưng khi bạn tiếp tục làm việc với Kubernetes, bạn sẽ hiểu được chúng. Ngoài ra, bạn có thể tham khảo các tài liệu chính thức ví dụ các tệp YAML để tham khảo. Đại loại làDeployment
khá dễ hiểu. spec.replicas
xác định số lượng bản sao đang chạy. Đặt giá trị này thành3
nghĩa là bạn cho Kubernetes biết rằng bạn muốn ba phiên bản ứng dụng của mình luôn chạy.spec.selector
là nơi bạn choDeployment
biết cần kiểm soát nhóm nào. Tôi đã đề cập rằngDeployment
là một phần mở rộng choReplicaSet
và có thể kiểm soát một tập hợp các đối tượng Kubernetes. Đặtselector.matchLabels
thànhcomponent: api
có nghĩa làDeployment
sẽ kiểm soát các nhóm có nhãncomponent: api
. Dòng này cho Kubernetes biết rằng bạn muốnDeployment
kiểm soát tất cả các nhóm có nhãncomponent: api
.spec.template
là mẫu để cấu hình nhóm. Nó gần giống với tệp cấu hình trước đó.
Bây giờ hãy chạy lệnh sau:
kubectl apply -f api-deployment.yaml
# deployment.apps/api-deployment created
Để đảm bảo rằng Deployment
đã được tạo, hãy thực hiện lệnh sau:
kubectl get deployment
# NAME READY UP-TO-DATE AVAILABLE AGE
# api-deployment 0/3 3 0 2m7s
Nếu bạn nhìn vào cột READY
, bạn sẽ thấy 0/3
. Điều này có nghĩa là các pod chưa được tạo. Chờ một vài phút và thử lại một lần nữa.
kubectl get deployment
# NAME READY UP-TO-DATE AVAILABLE AGE
# api-deployment 0/3 3 0 28m
Như bạn có thể thấy, tôi đã đợi gần nửa giờ mà vẫn chưa có pod nào sẵn sàng. Bản thân API chỉ là vài trăm kilobyte. Việc triển khai ở kích thước này sẽ không mất nhiều thời gian. Có nghĩa là có một vấn đề và chúng ta phải khắc phục điều đó.
Kiểm tra Kubernetes Resources
Trước khi bạn có thể giải quyết một vấn đề, trước tiên bạn phải tìm ra nguồn gốc. Một điểm khởi đầu tốt là lệnh get
.
Bạn đã biết lệnh get
in một bảng chứa thông tin quan trọng về một hoặc nhiều tài nguyên Kubernetes. Cú pháp chung của lệnh như sau:
kubectl get <resource type> <resource name>
Để chạy lệnh get
trên api-deployment
của bạn, hãy thực thi dòng mã sau trong thiết bị đầu cuối của bạn:
kubectl get deployment api-deployment
# NAME READY UP-TO-DATE AVAILABLE AGE
# api-deployment 0/3 3 0 15m
Bạn có thể bỏ qua tên api-deployment
để nhận danh sách tất cả các deployment có sẵn. Bạn cũng có thể chạy lệnh get
trên tệp cấu hình.
Nếu bạn muốn nhận thông tin về các deployment được mô tả trong tệp api-deployment.yaml
, lệnh sẽ như sau:
kubectl get -f api-deployment
# NAME READY UP-TO-DATE AVAILABLE AGE
# api-deployment 0/3 3 0 18m
Theo mặc định, lệnh get
hiển thị một lượng rất nhỏ thông tin. Bạn có thể tận dụng nó nhiều hơn bằng cách sử dụng tùy chọn -o
.
Tùy chọn -o
thiết lập định dạng đầu ra cho các lệnh get
. Bạn có thể sử dụng định dạng đầu ra wide
để xem thêm chi tiết.
kubectl get -f api-deployment.yaml
# NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
# api-deployment 0/3 3 0 19m api fhsinchy/notes-api component=api
Như bạn có thể thấy, bây giờ danh sách chứa nhiều thông tin hơn trước. Bạn có thể tìm hiểu về các tùy chọn cho lệnh get
từ các tài liệu chính thức .
Thành thật mà nói, việc chạy get
trên Deployment
không tạo ra bất cứ điều gì thú vị. Trong những trường hợp như vậy, bạn phải xuống các tài nguyên cấp thấp hơn.
Hãy xem danh sách pod và xem liệu bạn có thể tìm thấy điều gì đó thú vị ở đó không:
kubectl get pod
# NAME READY STATUS RESTARTS AGE
# api-deployment-d59f9c884-88j45 0/1 CrashLoopBackOff 10 30m
# api-deployment-d59f9c884-96hfr 0/1 CrashLoopBackOff 10 30m
# api-deployment-d59f9c884-pzdxg 0/1 CrashLoopBackOff 10 30m
Tất cả các pod có thông tin cột STATUS
là CrashLoopBackOff
. Trước đây bạn chỉ thấy trạng thái ContainerCreating
và Running
. Bạn cũng có thể thấy Error
ở vị trí của CrashLoopBackOff
.
Nhìn vào cột RESTARTS
, bạn có thể thấy rằng các pod đã được khởi động lại 10 lần. Điều này có nghĩa là vì một số lý do mà các pod không khởi động được.
Bây giờ để có cái nhìn chi tiết hơn về một trong các pod, bạn có thể sử dụng lệnh khác được gọi là describe
. Nó rất giống lệnh get
. Cú pháp chung của lệnh như sau:
kubectl describe <resource type> <resource name>
Để biết chi tiết về pod api-deployment-d59f9c884-88j45
, bạn có thể thực hiện lệnh sau:
kubectl describe pod api-deployment-d59f9c884-88j45
# Name: api-deployment-d59f9c884-88j45
# Namespace: default
# Priority: 0
# Node: minikube/172.28.80.217
# Start Time: Sun, 09 Aug 2020 16:01:28 +0600
# Labels: component=api
# pod-template-hash=d59f9c884
# Annotations: <none>
# Status: Running
# IP: 172.17.0.4
# IPs:
# IP: 172.17.0.4
# Controlled By: ReplicaSet/api-deployment-d59f9c884
# Containers:
# api:
# Container ID: docker://d2bc15bda9bf4e6d08f7ca8ff5d3c8593655f5f398cf8bdd18b71da8807930c1
# Image: fhsinchy/notes-api
# Image ID: docker-pullable://fhsinchy/notes-api@sha256:4c715c7ce3ad3693c002fad5e7e7b70d5c20794a15dbfa27945376af3f3bb78c
# Port: 3000/TCP
# Host Port: 0/TCP
# State: Waiting
# Reason: CrashLoopBackOff
# Last State: Terminated
# Reason: Error
# Exit Code: 1
# Started: Sun, 09 Aug 2020 16:13:12 +0600
# Finished: Sun, 09 Aug 2020 16:13:12 +0600
# Ready: False
# Restart Count: 10
# Environment: <none>
# Mounts:
# /var/run/secrets/kubernetes.io/serviceaccount from default-token-gqfr4 (ro)
# Conditions:
# Type Status
# Initialized True
# Ready False
# ContainersReady False
# PodScheduled True
# Volumes:
# default-token-gqfr4:
# Type: Secret (a volume populated by a Secret)
# SecretName: default-token-gqfr4
# Optional: false
# QoS Class: BestEffort
# Node-Selectors: <none>
# Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
# node.kubernetes.io/unreachable:NoExecute for 300s
# Events:
# Type Reason Age From Message
# ---- ------ ---- ---- -------
# Normal Scheduled <unknown> default-scheduler Successfully assigned default/api-deployment-d59f9c884-88j45 to minikube
# Normal Pulled 2m40s (x4 over 3m47s) kubelet, minikube Successfully pulled image "fhsinchy/notes-api"
# Normal Created 2m40s (x4 over 3m47s) kubelet, minikube Created container api
# Normal Started 2m40s (x4 over 3m47s) kubelet, minikube Started container api
# Normal Pulling 107s (x5 over 3m56s) kubelet, minikube Pulling image "fhsinchy/notes-api"
# Warning BackOff <invalid> (x44 over 3m32s) kubelet, minikube Back-off restarting failed container
Phần thú vị nhất trong toàn bộ kết quả trả về là phần Events
. Hãy xem kỹ hơn:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/api-deployment-d59f9c884-88j45 to minikube
Normal Pulled 2m40s (x4 over 3m47s) kubelet, minikube Successfully pulled image "fhsinchy/notes-api"
Normal Created 2m40s (x4 over 3m47s) kubelet, minikube Created container api
Normal Started 2m40s (x4 over 3m47s) kubelet, minikube Started container api
Normal Pulling 107s (x5 over 3m56s) kubelet, minikube Pulling image "fhsinchy/notes-api"
Warning BackOff <invalid> (x44 over 3m32s) kubelet, minikube Back-off restarting failed container
Từ những sự kiện này, bạn có thể thấy rằng image đã được pull thành công. Container cũng được tạo, nhưng rõ ràng thông điệp Back-off restarting failed container
có nghĩa là container không khởi động được.
Lệnh describe
rất giống với lệnh get
và có cùng một loại tùy chọn.
Bạn có thể bỏ qua tên api-deployment-d59f9c884-88j45
để nhận thông tin về tất cả các pod có sẵn. Hoặc bạn cũng có thể sử dụng tùy chọn -f
để truyền tệp cấu hình vào lệnh. Truy cập tài liệu chính thức để tìm hiểu thêm.
Bây giờ bạn biết rằng có điều gì đó không ổn với vùng chứa, bạn phải đi xuống cấp độ vùng chứa và xem điều gì đang xảy ra ở đó.

Bài viết gốc.