Kustomize là một trong những công cụ hữu ích nhất trong hệ sinh thái Kubernetes để đơn giản hóa việc triển khai, cho phép bạn tạo toàn bộ ứng dụng Kubernetes từ các manifest có sẵn mà không cần chỉnh sửa vào các file cấu hình YAML gốc cho nhiều mục đích khác nhau.
Trong hướng dẫn này, chúng ta sẽ thiết lập Kustomize và khám phá cách nó hoạt động bằng cách triển khai WordPress và sau đó, chúng ta sẽ xem việc sử dụng Kustomize với kubectl để xem lợi ích của Kustomize đối với việc triển khai trên quy mô lớn nó có lợi như thế nào.
Lưu ý : Nếu bạn đang sử dụng phiên bản mới nhất của kubectl, thì bạn đã tích hợp sẵn Kustomize. Nếu bạn đang sử dụng phiên bản kubectl 1.14 trở về trước hoặc chỉ muốn sử dụng công cụ Kustomize độc lập, bạn có thể làm theo hướng dẫn bên dưới. Trong một số trường hợp, các lệnh sẽ khác nhau giữa phiên bản độc lập và phiên bản tích hợp — mình chắc chắn sẽ chỉ ra những khác biệt đó nếu nó phát sinh.
1. Cài đặt Kustomize.
opsys=linux # hoặc darwin hoặc windows
curl -s https://api.github.com/repos/kubernetes-sigs/kustomize/releases/latest |\
grep browser_download |\
grep $opsys |\
cut -d '"' -f 4 |\
xargs curl -O -L
mv kustomize_*_${opsys}_amd64 kustomize
chmod u+x kustomize
Hoặc nếu bạn sử dụng snap, hãy chạy lệnh.
snap install kustomize
2. Triển khai môi trường ứng dụng.
Một trong những cách sử dụng phổ biến nhất của Kustomize là lấy nhiều đối tượng và kết hợp chúng thành một tài nguyên duy nhất dựa vào các label chung, đầu tiên hãy tạo thư mục làm việc cho WordPress trước.
KUSTOM_HOME=$(mktemp -d)
BASE=$KUSTOM_HOME/base
mkdir $BASE
WORDPRESS_HOME=$BASE/wordpress
mkdir $WORDPRESS_HOME
cd $WORDPRESS_HOME
Bây giờ chúng ta hãy tạo manifest deployment.yaml theo đường dẫn $WORDPRESS_HOME/deployment.yaml .
cat > $WORDPRESS_HOME/deployment.yaml << 'OEF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
emptyDir: {}
OEF
Tiếp theo hãy tạo manifest của services vào đường dẫn $WORDPRESS_HOME/service.yaml
cat > $WORDPRESS_HOME/service.yaml << 'OEF'
apiVersion: v1
kind: Service
metadata:
name: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
selector:
app: wordpress
type: LoadBalancer
OEF
Theo cách bình thường thì bạn sẽ sử dụng lệnh dưới để tạo ứng dụng.
$ kubectl apply -f $WORDPRESS_HOME
deployment.apps/wordpress created
service/wordpress created
$ kubectl get pod,svc
NAME READY STATUS RESTARTS AGE
pod/wordpress-5d9f64987b-98l7b 1/1 Running 0 70s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/wordpress LoadBalancer 10.97.120.115 192.168.13.224 80:30709/TCP 71s
Bây giờ nếu bạn xem các định nghĩa, bạn sẽ nhận thấy rằng cả hai tài nguyên đều hiển thị label ứng dụng là wordpress . Nếu bạn muốn triển khai chúng và sử dụng label để phân biệt giữa các ứng dụng, chẳng hạn như app:my-wordpress1 hoặc app:my-wordpress2,… bạn sẽ phải vào thêm tham số label trong cả 2 file manifest deployment.yaml và service.yaml. Nếu như vậy rất mất thời gian và không tiện trong việc triển khai nhiều dự án lớn.
Thay vào đó, chúng ta có thể sử dụng Kustomize để kết hợp chúng thành một manifest duy nhất — bao gồm label ứng dụng mong muốn — mà không thay đổi manifest gốc. Do lúc nãy mình đã triển khai thử nên bây giờ trước khi chúng ta tiếp tục, bạn cần dọn dẹp nó trước.
$ kubectl delete -f $WORDPRESS_HOME
deployment.apps "wordpress" deleted
service "wordpress" deleted
Sau khi dọn dẹp nó xong chúng ta sẽ bắt đầu bằng cách tạo file $WORDPRESS_HOME/kustomization.yaml và thêm file này nội dung sau:
cat > $WORDPRESS_HOME/kustomization.yaml << 'OEF'
commonLabels:
app: my-wordpress
resources:
- deployment.yaml
- service.yaml
OEF
Đây là một file rất đơn giản chỉ đơn giản là thêm 1 label chung — app: my-wordpress — vào các tài nguyên được xác định trong 2 file manifest deployment.yaml và service.yaml . Bây giờ chúng ta có thể sử dụng lệnh kustomize build để build 1 YAML mới. Nếu bạn đang sử dụng Kustomize độc lập, bạn sẽ chạy:
kustomize build $WORDPRESS_HOME
Nếu bạn đang sử dụng kubectl kustomize bạn hãy chạy:
kubectl kustomize $WORDPRESS_HOME
Đầu ra là sự kết hợp của 2 file manifest deployment.yaml và nó chỉ thay đổi label mà chúng ta đã định nghĩa trong file kustomization.yaml
$ kubectl kustomize $WORDPRESS_HOME
apiVersion: v1
kind: Service
metadata:
labels:
app: my-wordpress
name: wordpress
spec:
ports:
- port: 80
selector:
app: my-wordpress
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-wordpress
name: wordpress
spec:
selector:
matchLabels:
app: my-wordpress
strategy:
type: Recreate
template:
metadata:
labels:
app: my-wordpress
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- mountPath: /var/www/html
name: wordpress-persistent-storage
volumes:
- emptyDir: {}
name: wordpress-persistent-storage
Bạn có thể export nội dung này thành file hoặc chuyển file trực tiếp vào kubectl — mình sẽ đề cập đến cả hai phương pháp trong giây lát.
Quản lý nhiều thư mục
Để WordPress chạy bạn cần có thêm cơ sở dữ liệu như MySQL hoặc MariaDB. Đề làm được điều này mình sẽ tạo một tập hợp các file tương tự để thiết lập MySQL nhưng thật không may là chúng sẽ trùng tên với các file của WordPress và bạn hãy nhớ rằng mình không muốn thay đổi bất kỳ file nào của WordPress vì vậy để làm được điều này mình sẽ tạo thêm 1 thư mục mới để lưu các file manifest cho MySQL.
MYSQL_HOME=$BASE/mysql
mkdir $MYSQL_HOME
cd $MYSQL_HOME
File đầu tiên mình cần thêm là $MYSQL_HOME/deployment.yaml
cat > $MYSQL_HOME/deployment.yaml << 'OEF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
labels:
app: mysql
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
emptyDir: {}
OEF
Tiếp theo thêm file $MYSQL_HOME/service.yaml
cat > $MYSQL_HOME/service.yaml << 'OEF'
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
OEF
Và cuối cùng $MYSQL_HOME/secret.yaml để giữ tên người dùng và mật khẩu cơ sở dữ liệu.
cat > $MYSQL_HOME/secret.yaml << 'OEF'
apiVersion: v1
kind: Secret
metadata:
name: mysql-pass
type: Opaque
data:
# Default password is "admin".
password: YWRtaW4=
OEF
Và giống như WordPress chúng ta sẽ thêm một file kustomization, $MYSQL_HOME/kustomization.yaml để custom resource manifest cho MySQL.
cat > $MYSQL_HOME/kustomization.yaml << 'OEF'
resources:
- deployment.yaml
- service.yaml
- secret.yaml
OEF
Bây giờ chúng ta cần thêm một file kustomization mới vào thư mục base tại $BASE/kustomization.yaml, chúng ta sẽ khai báo thông tin label tại file kustomization.yaml này luôn để thông tin label có sẽ được áp dụng cho cả 2 resource WordPress và MySQL.
cat > $BASE/kustomization.yaml << 'OEF'
commonLabels:
app: my-wordpress
bases:
- ./wordpress
- ./mysql
OEF
Ở bước trên lúc này chúng ta có thử triển khai Kustomize cho WordPress nên đã thêm nội dung label vào file $WORDPRESS_HOME/kustomization.yaml rồi nên giờ bạn hãy xoá label trong file này nhé.
commonLabels: -> xoá phần này
app: my-wordpress -> xoá phần này
resources: -> giữ nguyên
- deployment.yaml -> giữ nguyên
- service.yaml -> giữ nguyên
Tất cả đã sẵn sàng, hãy thử build nó bằng lệnh kubectl kustomize $BASE hoặc kustomize build $BASE. Chúng ta có thể thấy cả 2 thành phần là WordPress và MySQL đã được cập nhật label.
$ kubectl kustomize $BASE
apiVersion: v1
data:
password: YWRtaW4=
kind: Secret
metadata:
labels:
app: my-wordpress
name: mysql-pass
type: Opaque
---
apiVersion: v1
kind: Service
metadata:
labels:
app: my-wordpress
name: mysql
spec:
ports:
- port: 3306
selector:
app: my-wordpress
---
apiVersion: v1
kind: Service
metadata:
labels:
app: my-wordpress
name: wordpress
spec:
ports:
- port: 80
selector:
app: my-wordpress
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-wordpress
name: wordpress
spec:
selector:
matchLabels:
app: my-wordpress
strategy:
type: Recreate
template:
metadata:
labels:
app: my-wordpress
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- mountPath: /var/www/html
name: wordpress-persistent-storage
volumes:
- emptyDir: {}
name: wordpress-persistent-storage
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
labels:
app: my-wordpress
name: mysql
spec:
selector:
matchLabels:
app: my-wordpress
strategy:
type: Recreate
template:
metadata:
labels:
app: my-wordpress
spec:
containers:
- env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: mysql-pass
image: mysql:5.6
name: mysql
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- mountPath: /var/lib/mysql
name: mysql-persistent-storage
volumes:
- emptyDir: {}
name: mysql-persistent-storage
OK, vậy bây giờ chúng ta đã thay đổi được nhiều thành phần… nhưng điều gì sẽ xảy ra nếu chúng ta cần thay đổi điều gì đó?
Thay đổi tham số cho một thành phần bằng cách sử dụng Kustomize Overlays.
Bây giờ, chúng ta gần như đã sẵn sàng triển khai được ứng dụng WordPress, nhưng chúng ta có một vấn đề là nếu các manifest trên chúng ta triển khai cho hệ thống có tích hợp sẵn LoadBalancer ví dụ như các cloud provider thì nó sẽ hoạt động tốt nhưng nếu chúng ta triển khai demo trên máy tính cá nhân của mình chẳng hạn mà không có sẵn tính năng loadbalancer thì dịch vụ sẽ không chạy. Vì vậy mình đã nghĩ ra nếu rơi vào trường hợp demo thì sẽ chạy service là NodePort. Chúng ta có thể giải quyết vấn đề này bằng Kustomize.
Kustomize cho phép chúng ta lấy YAML base và thay đổi một số nội dung mong muốn. Ví dụ: chúng ta sẽ tạo một bản vá để thay đổi service từ các manifest base thành các service loại NodePort.
Creating a kustomize patch
Mình sẽ tạo 1 thư mục có tên là overlay và điều quan trọng là thư mục overlay không được nằm trong cùng thư mục với các file base, vì vậy chúng ta sẽ tạo thư mục overlay song song với thư mục base, sau đó thêm thư mục con dev vào thư mục overlay.
OVERLAY_HOME=$BASE/../overlays
mkdir $OVERLAY_HOME
DEV_HOME=$OVERLAY_HOME/dev
mkdir $DEV_HOME
cd $DEV_HOME
Tiếp theo, chúng ta sẽ tạo 1 manifest như là 1 bản vá cho service $DEV_HOME/localserv.yaml
cat > $DEV_HOME/localserv.yaml << 'OEF'
apiVersion: v1
kind: Service
metadata:
name: wordpress
spec:
type: NodePort
---
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
type: NodePort
OEF
Lưu ý rằng chúng ta đã có các thông tin tối thiểu ở đây; vừa đủ để xác định từng dịch vụ mà chúng ta muốn thay đổi, sau đó chỉ định thay đổi mà chúng ta muốn thực hiện — trong trường hợp này là service type. Bây giờ chúng ta cần tạo file $DEV_HOME/kustomization.yaml để liên kết tất cả những thứ này lại với nhau:
cat > $DEV_HOME/kustomization.yaml << 'OEF'
bases:
- ../../base
patchesStrategicMerge:
- localserv.yaml
OEF
Lưu ý rằng điều này thực sự rất đơn giản; chúng ta đang chỉ vào thư mục base khởi tạo lúc ban đầu có chứa 2 thư mục của 2 thành phần đó là WordPress bà MySQL và chỉ định (các) bản vá mà chúng ta muốn thêm.
Bây giờ chúng ta có thể tiếp tục và xây dựng $BASE ban đầu và đảm bảo rằng nó không bị ảnh hưởng.
Sau khi tạo xong các thành phần, bạn sẽ có cây thư mục như thế này.
.
├── overlays
│ └── dev
│ ├── kustomization.yaml
│ └── localserv.yaml
└── base
├── kustomization.yaml
├── mysql
│ ├── deployment.yaml
│ ├── kustomization.yaml
│ ├── secret.yaml
│ └── service.yaml
└── wordpress
├── deployment.yaml
├── kustomization.yaml
└── service.yaml
Chúng ta vẫn có các dịch vụ LoadBalancer nếu build chúng tại thư mục base:
$ kustomize build $BASE
apiVersion: v1
data:
password: YWRtaW4=
kind: Secret
metadata:
labels:
app: my-wordpress
name: mysql-pass
type: Opaque
---
apiVersion: v1
kind: Service
metadata:
labels:
app: my-wordpress
name: mysql
spec:
ports:
- port: 3306
selector:
app: my-wordpress
---
apiVersion: v1
kind: Service
metadata:
labels:
app: my-wordpress
name: wordpress
spec:
ports:
- port: 80
selector:
app: my-wordpress
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-wordpress
name: wordpress
spec:
selector:
matchLabels:
app: my-wordpress
strategy:
type: Recreate
template:
metadata:
labels:
app: my-wordpress
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- mountPath: /var/www/html
name: wordpress-persistent-storage
volumes:
- emptyDir: {}
name: wordpress-persistent-storage
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
labels:
app: my-wordpress
name: mysql
spec:
selector:
matchLabels:
app: my-wordpress
strategy:
type: Recreate
template:
metadata:
labels:
app: my-wordpress
spec:
containers:
- env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: mysql-pass
image: mysql:5.6
name: mysql
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- mountPath: /var/lib/mysql
name: mysql-persistent-storage
volumes:
- emptyDir: {}
name: mysql-persistent-storage
Nhưng nếu chúng ta build các manifest từ thư mục dev, bạn sẽ có kết qu service type loadbalancer đã được thay thế bằng NodePort. Hãy sử dụng kubectl kustomize $DEV_HOME hoặc kustomize build $DEV_HOME. Bạn sẽ thấy mọi thứ không thay đổi ngoại trừ service type:
$ kustomize build $DEV_HOME
apiVersion: v1
data:
password: YWRtaW4=
kind: Secret
metadata:
labels:
app: my-wordpress
name: mysql-pass
type: Opaque
---
apiVersion: v1
kind: Service
metadata:
labels:
app: my-wordpress
name: mysql
spec:
ports:
- port: 3306
selector:
app: my-wordpress
type: NodePort
---
apiVersion: v1
kind: Service
metadata:
labels:
app: my-wordpress
name: wordpress
spec:
ports:
- port: 80
selector:
app: my-wordpress
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-wordpress
name: wordpress
spec:
selector:
matchLabels:
app: my-wordpress
strategy:
type: Recreate
template:
metadata:
labels:
app: my-wordpress
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- mountPath: /var/www/html
name: wordpress-persistent-storage
volumes:
- emptyDir: {}
name: wordpress-persistent-storage
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
labels:
app: my-wordpress
name: mysql
spec:
selector:
matchLabels:
app: my-wordpress
strategy:
type: Recreate
template:
metadata:
labels:
app: my-wordpress
spec:
containers:
- env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: mysql-pass
image: mysql:5.6
name: mysql
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- mountPath: /var/lib/mysql
name: mysql-persistent-storage
volumes:
- emptyDir: {}
name: mysql-persistent-storage
Sử dụng Kustomize với Kubectl
Tất cả những điều này thật tuyệt vời, nhưng việc lưu nó vào một file rồi chạy file đó có vẻ hơi quá mức cần thiết. May mắn cho chúng ta là có hai cách để chúng ta có thể cung cấp trực tiếp thông tin này.
$ kustomize build . | kubectl apply -f -
secret/mysql-pass created
service/mysql created
service/wordpress created
deployment.apps/mysql created
deployment.apps/wordpress created
Hoặc nếu đang sử dụng Kubernetes 1.14 trở lên, bạn chỉ cần sử dụng tham số -k:
$ kubectl apply -k $DEV_HOME
secret/mysql-pass created
service/mysql created
service/wordpress created
deployment.apps/mysql created
deployment.apps/wordpress created
Như đã khai báo dịch vụ của bạn sau khi triển khai tại thư mục $DEV_HOME đang ở loại NodePort.
$ kubectl get svc -A | grep 'wordpress'
default wordpress NodePort 10.96.65.38 <none> 80:32680/TCP 8m30s
Một ví dụ Secret Generator với Kustomize
với cách làm cũ bạn sẽ phải chạy lệnh như dưới.
$ kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
secret/myregistrykey created.
Nhưng nếu sử dụng Kustomize bạn chỉ cần tạo ta file như thế này.
secretGenerator:
- name: myregistrykey
type: docker-registry
literals:
- docker-server=DOCKER_REGISTRY_SERVER
- docker-username=DOCKER_USER
- docker-password=DOCKER_PASSWORD
- docker-email=DOCKER_EMAIL
Sau đó, chỉ cần tham chiếu nó bằng tham số -k:
$ kubectl apply -k .
secret/myregistrykey-66h7d4d986 created
Kết luận.
Khi bạn đang làm việc trên nhiều môi trường với các yêu cầu riêng biệt, Kustomize có thể giúp nhóm của bạn chia sẻ cùng một file cơ sở chỉ với những thay đổi cần thiết được đặt chồng lên nhau một cách đơn giản và có chọn lọc. Chúng ta có thể tiếp tục công việc của mình trên ứng dụng đó với sự tự tin rằng các cấu hình của chúng ta vẫn giống nhau trên các môi trường .
Kết hợp điều này với sự tích hợp của Kustomize vào kubectl tất cả đều hoàn toàn phù hợp với bộ công cụ K8s — và rõ ràng là Kustomize có thể đóng vai trò là một công cụ mạnh mẽ để triển khai cơ sở hạ tầng Kubernetes của chúng ta nhanh chóng và gọn gàng hơn.