+10

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

image.png

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

image.png 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:

  1. 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.

  1. 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:

  1. Khởi tạo namespace trên Kubernetes cho Dragonfly
$ kubectl create namespace dragonfly

namespace/dragonfly created
  1. Thêm Helm Repository của Dragonfly
$ helm repo add dragonfly https://dragonflyoss.github.io/helm-charts/
  1. 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õ image.png

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 image.png

So sánh giữa 2 lần pull trên 2 node image.png

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

Viblo
Let's register a Viblo Account to get more interesting posts.