Sử dụng Dragonfly để tối ưu hoá việc phân tán Docker Image giữa các node trên Kubernetes cluster
Giới thiệu Dragonfly
Dragonfly là một dự án mã nguồn mở quan trọng và đột phá được phát triển bởi Alibaba Group, nhằm giải quyết các thách thức về việc quản lý và phân tán Docker image hiệu quả trên môi trường Kubernetes (k8s). Với mục tiêu tối ưu hóa hiệu suất tải và chia sẻ Docker image trong các môi trường mạng phức tạp, Dragonfly đã mang đến những giải pháp đột phá, giúp giảm bớt tải trên nguồn chính và tăng tốc quá trình tải xuống. Bài viết này sẽ giới thiệu về Dragonfly, từ mục tiêu, nguyên tắc hoạt động đến cách tích hợp và tận dụng trong môi trường Kubernetes.
Mục tiêu của Dragonfly
Một trong những thách thức chính của việc quản lý Docker image trong môi trường Kubernetes là vấn đề tải xuống và chia sẻ hiệu quả. Khi có nhiều container đang chạy trên cùng một node hoặc trên các node khác nhau, việc tải xuống lại và lưu trữ lặp đi lặp lại các Docker image có thể gây lãng phí băng thông mạng và tài nguyên hệ thống. Mục tiêu của Dragonfly là giảm tải trên nguồn gốc và tối ưu hóa việc chia sẻ Docker image giữa các container trên cùng một node hoặc giữa các node khác nhau.
Nguyên tắc hoạt động của Dragonfly
Dragonfly hoạt động dựa trên mô hình peer-to-peer (P2P). Nó tận dụng sức mạnh cộng đồng để chia sẻ và tải xuống Docker image một cách hiệu quả. Khi một Docker image được yêu cầu, Dragonfly sẽ kiểm tra xem có bản cache của image đó trên các nút khác trong mạng không. Nếu có, nó sẽ tải image từ cache đó thay vì từ nguồn chính, giúp giảm tải trên nguồn chính và tăng tốc độ tải xuống.
Đặc biệt, Dragonfly cung cấp tính năng P2P Caching, cho phép lưu trữ cache của các Docker image trên các nút trong mạng. Điều này giúp tối ưu hóa việc chia sẻ và lưu trữ image trên các nút, từ đó cải thiện hiệu suất và giảm áp lực lên nguồn gốc.
Một số tính năng quan trọng của Dragonfly khi được sử dụng để phân tán Docker image trên một Kubernetes cluster có thể kể tới như:
-
Tăng tốc tải Docker image: Dragonfly tận dụng việc chia sẻ Docker image đã tải trước đó giữa các nút trong mạng, giúp giảm tải trên nguồn gốc và giảm thời gian tải xuống.
-
P2P Caching: Dragonfly sử dụng mô hình peer-to-peer (P2P) để lưu trữ cache của các Docker image trên các nút trong mạng, giúp tăng tốc quá trình tải và chia sẻ image.
-
Sự linh hoạt và mở rộng: Dragonfly có thể linh hoạt tích hợp vào môi trường Kubernetes và mở rộng theo nhu cầu, giúp quản lý tài nguyên hiệu quả trong môi trường phức tạp.
-
Quản lý băng thông: Dragonfly giúp kiểm soát băng thông và giới hạn lưu lượng mạng được sử dụng trong quá trình tải và chia sẻ image.
Thực hành triển khai Dragonfly lên Kubernetes cluster
Để tiến hành triển khai Dragonfly cho Kubernetes cluster chúng ta cần chuẩn bị trước các công cụ cần thiết, trong bài viết này mình sẽ demo cách cấu hình trên 1 cụm được dựng từ Kind. Trước tiên bạn cần cài đặt các công cụ dưới đây:
- Kind (Kubernetes IN Docker): một dự án mã nguồn mở giúp tạo và quản lý các cluster Kubernetes sử dụng Docker container như các node
- Docker
- Helm: Helm là một công cụ quản lý gói và triển khai ứng dụng trên Kubernetes
Việc cài đặt các công cụ trên khá đơn giản chúng ta chỉ cần follow theo docs để setup
Dựng Kubernetes cluster bằng Kind
Để tạo một cluster Kubernetes sử dụng Kind (Kubernetes IN Docker), bạn chỉ cần làm theo các bước sau:
- Tạo file
kind-config.yaml
chứa cấu hình của cụm cần tạo
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
mình sẽ sử dụng cụm có 3 nodes, trong đó có 2 worker node.
- Sau cho chạy command để Kind dựng Kubernetes cluster
$ kind create cluster --config kind-config.yaml
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.27.3) 🖼
✓ Preparing nodes 📦 📦 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
✓ Joining worker nodes 🚜
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
kết quả như trên là thành công, ở bước này bạn đã có cho mình 1 K8s cluster dưới local
$ kubectl get node
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready control-plane 77s v1.27.3
kind-worker Ready <none> 55s v1.27.3
kind-worker2 Ready <none> 56s v1.27.3
Cài đặt Dragonfly bằng Helm
Helm là một công cụ quản lý gói và triển khai ứng dụng trên Kubernetes. Bằng việc sử dụng Helm, ta sẽ dễ dàng cài đặt Dragonfly qua các bước sau:
- Khởi tạo namespace trên Kubernetes cho Dragonfly
$ kubectl create namespace dragonfly
namespace/dragonfly created
- Thêm Helm Repository của Dragonfly
$ helm repo add dragonfly https://dragonflyoss.github.io/helm-charts/
- Cài đặt Dragonfly
Ở bước này, thay vì sử dụng Helm values mặc định của Dragonfly chart, mình sẽ cấu hình lại 1 chút để giải quyết vấn đề
- Kubernetes phiên bản mới hiện không còn sử dụng Docker làm container runtime nữa, thay vào đó sẽ dùng Containerd nên chúng ta cần cập nhật config của Containerd daemon.
- Ngoài ra, để cache được các Docker Image từ Docker Hub thì mình cần chỉnh sửa lại 1 chút config để khi Kubelet pull 1 image từ Docker Hub, khi không tìm thấy cache ở trong Dragonfly, nó sẽ pull tiếp từ Registry
https://index.docker.io
thay vìhttps://docker.io
.
Mọi người có thể tham khảo file dragonfly-config.yaml
của mình:
containerRuntime:
extraInitContainers:
- name: setup-mirror
image: dragonflyoss/openssl
command:
- /bin/sh
- -cx
- |-
echo "inject config_path into /etc/containerd/config.toml"
if grep -q '\[plugins."io.containerd.grpc.v1.cri".registry\]' /etc/containerd/config.toml}; then
sed -i 's|\[plugins."io.containerd.grpc.v1.cri".registry\]|\[plugins."io.containerd.grpc.v1.cri".registry\]\nconfig_path = "/etc/containerd/certs.d"|g' /etc/containerd/config.toml
else
cat << EOF >> /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
EOF
fi
echo "Registry config_path $config_path added"
mkdir -p /etc/containerd/certs.d
mkdir -p /etc/containerd/certs.d/docker.io
cat << EOF >> /etc/containerd/certs.d/docker.io/hosts.toml
server = "https://docker.io"
[host."http://127.0.0.1:65001"]
capabilities = ["pull", "resolve"]
[host."http://127.0.0.1:65001".header]
X-Dragonfly-Registry = ["https://index.docker.io"]
[host."https://index.docker.io"]
capabilities = ["pull", "resolve"]
EOF
mkdir -p /etc/containerd/certs.d/ghcr.io
cat << EOF >> /etc/containerd/certs.d/ghcr.io/hosts.toml
server = "https://ghcr.io"
[host."http://127.0.0.1:65001"]
capabilities = ["pull", "resolve"]
[host."http://127.0.0.1:65001".header]
X-Dragonfly-Registry = ["https://ghcr.io"]
[host."https://ghcr.io"]
capabilities = ["pull", "resolve"]
EOF
echo "Restart containerd\n"
nsenter -t 1 -m -- systemctl restart containerd.service
volumeMounts:
- name: containerd-conf
mountPath: /etc/containerd/
securityContext:
privileged: true
containerd:
enable: true
injectConfigPath: true
scheduler:
tag: latest
replicas: 1
metrics:
enable: true
config:
verbose: true
pprofPort: 18066
redis:
replicas: 1
seedPeer:
replicas: 1
metrics:
enable: true
config:
verbose: true
pprofPort: 18066
dfdaemon:
tag: latest
metrics:
enable: true
config:
verbose: true
console: true
pprofPort: 18066
manager:
tag: latest
replicas: 1
metrics:
enable: true
config:
verbose: true
pprofPort: 18066
jaeger:
enable: true
Khi đã có file chứa Helm values cho Dragonfly thì chúng ta chỉ cần install chart lên Kubernetes cluster thôi
$ helm upgrade --install -n dragonfly dragonfly dragonfly/dragonfly -f dragonfly-config.yaml
Release "dragonfly" does not exist. Installing it now.
NAME: dragonfly
LAST DEPLOYED: Fri Sep 29 15:56:41 2023
NAMESPACE: dragonfly
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the scheduler address by running these commands:
export SCHEDULER_POD_NAME=$(kubectl get pods --namespace dragonfly -l "app=dragonfly,release=dragonfly,component=scheduler" -o jsonpath={.items[0].metadata.name})
export SCHEDULER_CONTAINER_PORT=$(kubectl get pod --namespace dragonfly $SCHEDULER_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
kubectl --namespace dragonfly port-forward $SCHEDULER_POD_NAME 8002:$SCHEDULER_CONTAINER_PORT
echo "Visit http://127.0.0.1:8002 to use your scheduler"
2. Get the dfdaemon port by running these commands:
export DFDAEMON_POD_NAME=$(kubectl get pods --namespace dragonfly -l "app=dragonfly,release=dragonfly,component=dfdaemon" -o jsonpath={.items[0].metadata.name})
export DFDAEMON_CONTAINER_PORT=$(kubectl get pod --namespace dragonfly $DFDAEMON_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
You can use $DFDAEMON_CONTAINER_PORT as a proxy port in Node.
3. Configure runtime to use dragonfly:
https://d7y.io/docs/getting-started/quick-start/kubernetes/
4. Get Jaeger query URL by running these commands:
export JAEGER_QUERY_PORT=$(kubectl --namespace dragonfly get services dragonfly-jaeger-query -o jsonpath="{.spec.ports[0].port}")
kubectl --namespace dragonfly port-forward service/dragonfly-jaeger-query 16686:$JAEGER_QUERY_PORT
echo "Visit http://127.0.0.1:16686/search?limit=20&lookback=1h&maxDuration&minDuration&service=dragonfly to query download events"
Giờ chúng ta chỉ cần chờ cho việc deploy hoàn thành và test thôi. Kết quả như dưới đây là chúng ta đã setup thành công
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dragonfly-dfdaemon-h42jz 1/1 Running 0 5m34s 10.244.2.5 kind-worker <none> <none>
dragonfly-dfdaemon-pz8sl 1/1 Running 0 5m34s 10.244.1.2 kind-worker2 <none> <none>
dragonfly-jaeger-c7947b579-gxq92 1/1 Running 0 5m34s 10.244.2.3 kind-worker <none> <none>
dragonfly-manager-86d4874c55-pv5pl 1/1 Running 0 5m34s 10.244.2.4 kind-worker <none> <none>
dragonfly-mysql-0 1/1 Running 0 5m34s 10.244.1.8 kind-worker2 <none> <none>
dragonfly-redis-master-0 1/1 Running 0 5m34s 10.244.1.9 kind-worker2 <none> <none>
dragonfly-redis-replicas-0 1/1 Running 2 (3m24s ago) 5m34s 10.244.1.7 kind-worker2 <none> <none>
dragonfly-redis-replicas-1 1/1 Running 0 3m4s 10.244.2.8 kind-worker <none> <none>
dragonfly-redis-replicas-2 1/1 Running 0 99s 10.244.2.10 kind-worker <none> <none>
dragonfly-scheduler-0 1/1 Running 0 5m34s 10.244.1.3 kind-worker2 <none> <none>
dragonfly-seed-peer-0 1/1 Running 1 (2m ago) 5m34s 10.244.2.6 kind-worker <none> <none>
Kiểm tra tốc độ pull image trên các Node sau khi triển khai Dragonfly
Trên Dragonfly đã tích hợp công cụ Jaeger để tracing, chúng ta có thể vào đó để theo dõi thời gian pull image. Để sử dụng Jaeger, chúng ta forward-port từ trong Kubernetes ra máy host thông qua command
$ kubectl port-forward svc/dragonfly-jaeger-query 16686:16686
sau đó truy cập localhost:16686
để sử dụng Jaeger.
Pull image trên node kind-worker
Để thực hiện pull image, chúng ta sẽ exec vào docker container của Node và pull image về. Trong lần đầu tiên pull image trên Node, Dragonfly sẽ chưa có ảnh hưởng, nó sẽ chỉ hỗ trợ lưu image vào cache.
$ docker exec -it kind-worker bash
# Sau khi exec vào container worker, chúng ta sẽ tiến hành pull image thông qua command dưới
$ root@kind-worker:/# time (crictl pull node:16)
Image is up to date for sha256:1ddc7e4055fdb6f6bf31063b593befda814294f9f904b6ddfc21ab1513bafa8e
real 2m19.398s
user 0m0.307s
sys 0m0.042s
như bạn thấy, image node:16
được pull về node kind-worker tốn 2m19s
.
Trong Jaeger cũng thể hiển rõ
Pull image trên node kind-worker2
để xem hiệu quả cải thiện sau khi có Dragonfly
Chúng ta tiếp tục exec vào docker container của node kind-worker2
và tiến hành pull image. Tuy nhiên, trong lần pull image này, thời gian pull sẽ giảm rõ rệt do Dragonfly đã caching lại các layer.
$ docker exec -it kind-worker2 bash
# Sau khi exec vào container worker 2, chúng ta sẽ tiến hành pull image thông qua command dưới
$ root@kind-worker2:/# time (crictl pull node:16)
Image is up to date for sha256:1ddc7e4055fdb6f6bf31063b593befda814294f9f904b6ddfc21ab1513bafa8e
real 0m20.095s
user 0m0.035s
sys 0m0.031s
lần pull ở node worker 2 này, chúng ta chỉ mất 20s
. Một cải thiện đáng kể.
Bạn có thể xem lại trace trên Jaeger
So sánh giữa 2 lần pull trên 2 node
Kết luận
Dragonfly đóng vai trò quan trọng trong việc giải quyết vấn đề tải xuống và quản lý Docker image trong môi trường Kubernetes. Bằng cách tận dụng mô hình P2P và khả năng lưu trữ cache, Dragonfly mang lại lợi ích lớn cho việc tối ưu hóa hiệu suất và giảm tải trên nguồn gốc, đồng thời tăng tốc quá trình tải xuống và chia sẻ Docker image. Sự tích hợp linh hoạt và mở rộng của Dragonfly giúp tối ưu hóa môi trường Kubernetes, mang lại hiệu suất và hiệu quả lớn cho quản lý Docker image.
All rights reserved