Kubernetes (k8s) là gì?

Kubernetes được cho là còn sống, trong khi docker swarm coi như đã chết

Kubernetes là một nền tảng nguồn mở, khả chuyển, có thể mở rộng để quản lý các ứng dụng được đóng gói và các service, giúp thuận lợi trong việc cấu hình và tự động hoá việc triển khai ứng dụng. Kubernetes là một hệ sinh thái lớn và phát triển nhanh chóng. Các dịch vụ, sự hỗ trợ và công cụ có sẵn rộng rãi.

Tên gọi Kubernetes có nguồn gốc từ tiếng Hy Lạp, có ý nghĩa là người lái tàu hoặc hoa tiêu. Google mở mã nguồn Kubernetes từ năm 2014. Kubernetes xây dựng dựa trên một thập kỷ rưỡi kinh nghiệm mà Google có được với việc vận hành một khối lượng lớn workload trong thực tế, kết hợp với các ý tưởng và thực tiễn tốt nhất từ cộng đồng.

Lịch sử phát triển cấu trúc app

Chúng ta hãy xem tại sao Kubernetes rất hữu ích bằng cách quay ngược thời gian.

Container_Evolution

Thời đại triển khai theo cách truyền thống: Ban đầu, các ứng dụng được chạy trên các máy chủ vật lý. Không có cách nào để xác định ranh giới tài nguyên cho các ứng dụng trong máy chủ vật lý và điều này gây ra sự cố phân bổ tài nguyên. Ví dụ, nếu nhiều ứng dụng cùng chạy trên một máy chủ vật lý, có thể có những trường hợp một ứng dụng sẽ chiếm phần lớn tài nguyên hơn và kết quả là các ứng dụng khác sẽ hoạt động kém đi. Một giải pháp cho điều này sẽ là chạy từng ứng dụng trên một máy chủ vật lý khác nhau. Nhưng giải pháp này không tối ưu vì tài nguyên không được sử dụng đúng mức và rất tốn kém cho các tổ chức để có thể duy trì nhiều máy chủ vật lý như vậy.

Thời đại triển khai ảo hóa: Như một giải pháp, ảo hóa đã được giới thiệu. Nó cho phép bạn chạy nhiều Máy ảo (VM) trên CPU của một máy chủ vật lý. Ảo hóa cho phép các ứng dụng được cô lập giữa các VM và cung cấp mức độ bảo mật vì thông tin của một ứng dụng không thể được truy cập tự do bởi một ứng dụng khác.

Ảo hóa cho phép sử dụng tốt hơn các tài nguyên trong một máy chủ vật lý và cho phép khả năng mở rộng tốt hơn vì một ứng dụng có thể được thêm hoặc cập nhật dễ dàng, giảm chi phí phần cứng và hơn thế nữa. Với ảo hóa, bạn có thể có một tập hợp các tài nguyên vật lý dưới dạng một cụm các máy ảo sẵn dùng.

Mỗi VM là một máy tính chạy tất cả các thành phần, bao gồm cả hệ điều hành riêng của nó, bên trên phần cứng được ảo hóa.

Thời đại triển khai Container: Các container tương tự như VM, nhưng chúng có tính cô lập để chia sẻ Hệ điều hành (HĐH) giữa các ứng dụng. Do đó, container được coi là nhẹ (lightweight). Tương tự như VM, một container có hệ thống tệp (filesystem), CPU, bộ nhớ, process space, v.v. Khi chúng được tách rời khỏi cơ sở hạ tầng bên dưới, chúng có thể khả chuyển (portable) trên cloud hoặc các bản phân phối Hệ điều hành.

Các container đã trở nên phổ biến vì chúng có thêm nhiều lợi ích, chẳng hạn như:

  • Tạo mới và triển khai ứng dụng Agile: gia tăng tính dễ dàng và hiệu quả của việc tạo các container image so với việc sử dụng VM image.

  • Phát triển, tích hợp và triển khai liên tục: cung cấp khả năng build và triển khai container image thường xuyên và đáng tin cậy với việc rollbacks dễ dàng, nhanh chóng.

  • Phân biệt giữa Dev và Ops: tạo các images của các application container tại thời điểm build/release thay vì thời gian triển khai, do đó phân tách các ứng dụng khỏi hạ tầng.

  • Khả năng quan sát không chỉ hiển thị thông tin và các metric ở mức Hệ điều hành, mà còn cả application health và các tín hiệu khác.

  • Tính nhất quán về môi trường trong suốt quá trình phát triển, testing và trong production: Chạy tương tự trên laptop như trên cloud.

  • Tính khả chuyển trên cloud và các bản phân phối HĐH: Chạy trên Ubuntu, RHEL, CoreOS, on-premises, Google Kubernetes Engine và bất kì nơi nào khác.

  • Quản lý tập trung ứng dụng: Tăng mức độ trừu tượng từ việc chạy một Hệ điều hành trên phần cứng ảo hóa sang chạy một ứng dụng trên một HĐH bằng logical resources.

  • Các micro-services phân tán, elastic: ứng dụng được phân tách thành các phần nhỏ hơn, độc lập và thể được triển khai và quản lý một cách linh hoạt - chứ không phải một app nguyên khối (monolithic).

  • Cô lập các tài nguyên: dự đoán hiệu năng ứng dụng

  • Sử dụng tài nguyên: hiệu quả

Tại sao bạn cần Kubernetes và nó có thể làm những gì?

Các container là một cách tốt để đóng gói và chạy các ứng dụng của bạn. Trong môi trường production, bạn cần quản lý các container chạy các ứng dụng và đảm bảo rằng không có khoảng thời gian downtime. Ví dụ, nếu một container bị tắt đi, một container khác cần phải khởi động lên. Điều này sẽ dễ dàng hơn nếu được xử lý bởi một hệ thống.

Đó là cách Kubernetes đến với chúng ta. Kubernetes cung cấp cho bạn một framework để chạy các hệ phân tán một cách mạnh mẽ. Nó đảm nhiệm việc nhân rộng và chuyển đổi dự phòng cho ứng dụng của bạn, cung cấp các mẫu deployment và hơn thế nữa. Ví dụ, Kubernetes có thể dễ dàng quản lý một triển khai canary cho hệ thống của bạn.

Kubernetes cung cấp cho bạn:

  • Service discovery và cân bằng tải
    Kubernetes có thể expose một container sử dụng DNS hoặc địa chỉ IP của riêng nó. Nếu lượng traffic truy cập đến một container cao, Kubernetes có thể cân bằng tải và phân phối lưu lượng mạng (network traffic) để việc triển khai được ổn định.

  • Điều phối bộ nhớ
    Kubernetes cho phép bạn tự động mount một hệ thống lưu trữ mà bạn chọn, như local storages, public cloud providers, v.v.

  • Tự động rollouts và rollbacks
    Bạn có thể mô tả trạng thái mong muốn cho các container được triển khai dùng Kubernetes và nó có thể thay đổi trạng thái thực tế sang trạng thái mong muốn với tần suất được kiểm soát. Ví dụ, bạn có thể tự động hoá Kubernetes để tạo mới các container cho việc triển khai của bạn, xoá các container hiện có và áp dụng tất cả các resource của chúng vào container mới.

  • Đóng gói tự động
    Bạn cung cấp cho Kubernetes một cluster gồm các node mà nó có thể sử dụng để chạy các tác vụ được đóng gói (containerized task). Bạn cho Kubernetes biết mỗi container cần bao nhiêu CPU và bộ nhớ (RAM). Kubernetes có thể điều phối các container đến các node để tận dụng tốt nhất các resource của bạn.

  • Tự phục hồi
    Kubernetes khởi động lại các containers bị lỗi, thay thế các container, xoá các container không phản hồi lại cấu hình health check do người dùng xác định và không cho các client biết đến chúng cho đến khi chúng sẵn sàng hoạt động.

  • Quản lý cấu hình và bảo mật
    Kubernetes cho phép bạn lưu trữ và quản lý các thông tin nhạy cảm như: password, OAuth token và SSH key. Bạn có thể triển khai và cập nhật lại secret và cấu hình ứng dụng mà không cần build lại các container image và không để lộ secret trong cấu hình stack của bạn.

Kubernetes không phải là gì?

Kubernetes không phải là một hệ thống PaaS (Nền tảng như một Dịch vụ) truyền thống, toàn diện. Do Kubernetes hoạt động ở tầng container chứ không phải ở tầng phần cứng, nó cung cấp một số tính năng thường áp dụng chung cho các dịch vụ PaaS, như triển khai, nhân rộng, cân bằng tải, ghi nhật ký và giám sát. Tuy nhiên, Kubernetes không phải là cấu trúc nguyên khối và các giải pháp mặc định này là tùy chọn và có thể cắm được (pluggable).

Kubernetes:

  • Không giới hạn các loại ứng dụng được hỗ trợ. Kubernetes nhằm mục đích hỗ trợ một khối lượng công việc cực kỳ đa dạng, bao gồm cả stateless, stateful và xử lý dữ liệu. Nếu một ứng dụng có thể chạy trong một container, nó sẽ chạy rất tốt trên Kubernetes.

  • Không triển khai mã nguồn và không build ứng dụng của bạn. Quy trình CI/CD được xác định bởi tổ chức cũng như các yêu cầu kỹ thuật.

  • Không cung cấp các service ở mức ứng dụng, như middleware (ví dụ, các message buses), các framework xử lý dữ liệu (ví dụ, Spark), cơ sở dữ liệu (ví dụ, MySQL), bộ nhớ cache, cũng như hệ thống lưu trữ của cluster (ví dụ, Ceph). Các thành phần như vậy có thể chạy trên Kubernetes và/hoặc có thể được truy cập bởi các ứng dụng chạy trên Kubernetes thông qua các cơ chế di động, chẳng hạn như Open Service Broker.

  • Không bắt buộc các giải pháp ghi lại nhật ký (logging), giám sát (monitoring) hoặc cảnh báo (alerting). Nó cung cấp một số sự tích hợp như proof-of-concept, và cơ chế để thu thập và xuất các số liệu.

  • Không cung cấp, không bắt buộc một cấu hình ngôn ngữ/hệ thống (ví dụ: Jsonnet). Nó cung cấp một API khai báo có thể được targeted bởi các hình thức khai báo tùy ý.

  • Không cung cấp cũng như áp dụng bất kỳ cấu hình toàn diện, bảo trì, quản lý hoặc hệ thống tự phục hồi.

  • Ngoài ra, Kubernetes không phải là một hệ thống điều phối đơn thuần. Trong thực tế, nó loại bỏ sự cần thiết của việc điều phối. Định nghĩa kỹ thuật của điều phối là việc thực thi một quy trình công việc được xác định: đầu tiên làm việc A, sau đó là B rồi sau chót là C. Ngược lại, Kubernetes bao gồm một tập các quy trình kiểm soát độc lập, có thể kết hợp, liên tục điều khiển trạng thái hiện tại theo trạng thái mong muốn đã cho. Nó không phải là vấn đề làm thế nào bạn có thể đi được từ A đến C. Kiểm soát tập trung cũng không bắt buộc. Điều này dẫn đến một hệ thống dễ sử dụng hơn, mạnh mẽ hơn, linh hoạt hơn và có thể mở rộng.

Một số khái niệm quan trọng trong Kubernetes

Volumes

Volumes

521acfb9-e193-4f66-bcd0-128c4b6a77c6

Chúng ta cùng đến một khái niệm quan trọng khác đó là volumes. Volumes là nơi lưu trữ dữ liệu của của K8s. Giả sử container db hoặc logging sinh ra dữ liệu trong quá trình chạy và pod đó bị khởi động lại (restart) thì toàn bộ dữ liệu sinh ra trong quá trình chạy pod sẽ bị mất. Vì vậy để dữ liệu của database hoặc log data không bị mất để ta sẽ cần sử dụng đến volumes để gán cho pod. Volumes còn giải quyết được vấn đề chia sẻ dữ liệu giữa những container chạy trong cùng một pod. Nó có thể là bộ nhớ vật lý của server nơi mà pod đang chạy hoặc những remote storage nằm ngoài Kubernetes cluster (server hiện tại). Có một lưu ý ở đây là những bộ nhớ lưu trữ này được coi như một bộ nhớ bên ngoài được gắn vào Kubernetes cluster vì vậy K8s không quản lý tính bền vững của dữ liệu mà admin phải tự tiến hành có những cơ chế backup cho chúng như tạo một bản sao của dữ liệu ra một bộ nhớ vật lý khác, … Kubernetes hỗ trợ rất nhiều kiểu volumes có thể kể đến một số như awsElasticBlockStore, azureDisk, cephfs, …

Để sử dụng thành thạo Kubernetes Storage ngoài khái niệm volumes ta cũng cần biết một số component quan trọng khác đó là Persistent Volume(PV), Persistent Volume Claim(PVC)Storage Class.

Persistent Volume (PV)

Persistent Volume (PV) là một phần không gian lưu trữ dữ liệu trong cluster và nó được cung cấp bởi admin hoặc cung cấp động bằng cách sử dụng Storage Class mà ta sẽ nói ở phần dưới. Những volumes này tồn tại độc lập với lifecycle của pod (pod bị xóa hoặc restart thì persistent volume vẫn tồn tại). Kubernetes hỗ trợ rất nhiều loại persistent volume khác nhau như NFS, iSCSI, … Ta có thể hiểu đơn giản persistent volume là storage được lấy từ ổ cứng của cluster node, NFS server ngoài cluster hoặc cloud storage như AWS, Google Cloud Storage, … Storage có kiểu như nào, được lấy như nào, ở đâu hoàn toàn do người quản trị quyết định.

Persistent Volume cũng như những component khác được tạo bằng cách sử dụng YAML file như bên dưới:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-volume-name
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  storageClassName: slow
  nfs:
    path: /var/www/html/data
    server: server-ip-address
  • kind: chúng ta để là PersistentVolume
  • spec: để chúng ta định nghĩa ra một số tham số khác ví dụ như dung lượng của volume này là bao nhiêu?, chế độ quyền truy cập, storage backend … đầy đủ các bạn có thể tham khảo ở đây

Tùy theo loại storage mà ta sẽ có những tham số khác nhau.

Trước khi bắt đầu tìm hiểu về Persistent Volume Claim (PVC) ở phần tiếp theo ta sẽ so sánh một chút về K8s AdministratorK8s User:

  • K8s Administrator : là người tạo và quản trị cluster đồng thời đảm bảo cluster có đủ resource để sử dụng.
  • K8s User (Developer): là những người deploy(triển khai) ứng dụng này lên cluster.

=> Vì resource của cluster là do K8s Administrator thiết lập nên người admin cần chắc chắn rằng storage được sử dụng cho cluster còn hoạt động (nfs sever storage, cloud storage server) và tạo persistent volume từ storage này. Tùy theo yêu cầu từ developer cần storage nào thì người admin sẽ tạo loại storage đó. Developer cũng cần cấu hình và tạo YAML file để sử dụng những Persistent Volume đã được tạo do đó ta sẽ sử dụng đến một component khác của Kubernetes đó là Persistent Volume Claim (PVC).

Persistent Volume Claim (PVC)

Persistent Volume Claim (PVC) là yêu cầu sử dụng không gian lưu trữ của persistent volume đã được tạo. Developer sẽ tạo một YAML file cho PVC chỉ định dung lượng, loại lớp lưu trữ (storage class), các mức tài nguyên, quyền truy cập, … Dưới dây là một ví dụ về YAML file.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-volume-name
spec:
  storageClassName: manual
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

Để có thể sử dụng PVC được tạo thì developer cũng phải cấu hính nó vào trong pod như ví dụ dưới đây:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: nginx-container
      image: nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: myvol
  volumes:
    - name: myvol
      persistentVolumeClaim:
        claimName: pvc-volume-name

Như các bạn có thể thấy ở file cấu hình pod mình có sử dụng thuộc tiunhs volumes với tên là pvc-volume-name mà mình đã định nghĩa ở trên. Lưu ý là PVC cần ở cùng trong namespace với pod . Tổng thể quá trình bạn có thể hiểu là pod sẽ yêu cầu volume thông qua PV claimPVC sẽ tìm volume trong cluster để cho podPod sẽ mount volume đó cho container . Để ý trong file config bạn sẽ thấy khi pod yêu cầu được volume thì nó sẽ được mount vào container nginx ở đường dẫn var/www/html .

Storage Class

Giả sử trong cluster của của mình có rất nhiều application được deploy hàng ngày và cần storage cho nó thì developer sẽ cần hỏi admin để tạo persistent volume cho ứng dụng trước khi deploy. Admin cần phải xử lý rất nhiều yêu cầu từ developer để tạo PV cho ứng dụng. Điều này sẽ gây khó khăn, mất nhiều thời gian cho admin. Để làm cho việc này trở nên đơn giản và tiện lợi ta sẽ dùng đến một component khác của Kubernetes đó là Storage Class.

Storage Class là một cách để admin mô tả “class”(lớp) của hệ thống storage được cung cấp. Mỗi class khác nhau sẽ được ánh xạ với những chính sách tùy ý được xác định bởi cluster admin. Hay bạn có thể hiểu đơn giản là Storage Class giúp cũng cấp những PV một cách tự động khi nhận yêu cầu từ PVC. Dưới đây là một ví dụ về cấu hình của Storage Class.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: storage-class-name
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  iopsPerGB: "10"
  fsType: ext4
  • kind: chúng ta để là StorageClass
  • provisioner: đây là thuộc tính quan trọng của storage class vì nó cho K8s biết là provisioner (storage platform, cloud provider) nào được sử dụng để tạo persistent volume. Mỗi một server storage sẽ có provisioner của nó. Ở đây mình sử dụng internal provisioner của K8s với prefix là kubernetes.io. Bạn có thể sử dụng một số external provisioner khác.
  • parameters: những tham số của storage đó.

Bởi vì PV được yêu cầu bởi PVC nên trong cấu hình của PVC mình sẽ thêm thuộc tính cho storage class như bên dưới.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-volume-name
spec:
  storageClassName: manual
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
  storageClassName: storage-class-name

Sau khi đã có Storage Class thì ta sẽ có quá trình như sau: Pod lấy storage từ PVCPVC yêu cầu storage từ Storage ClassStorage Class tạo ra PV cho yêu cầu của Claim đó.

Deployment and Stateful Set

Deployment and Stateful Set

Khi mọi thứ được chạy ổn định trong K8s. Chuyện gì sẽ xảy ra khi ứng dụng của ta bị crash và ta cần tạo mới một container image. Lúc này sẽ sinh ra down time cho ứng dụng. Để không sinh ra down time cho ứng dụng ta cần replicate node mà ứng dụng ta chạy. Những replicate cùng cấu hình với chung service (IP không đổi, có load balancer). Ta sẽ đĩnh nghĩa một bản vẽ (blueprint) cho pod và định nghĩa số lượng replicate chạy tùy theo ý muốn. Những bản vẽ này được gọi là deployment trong K8s. Khi làm việc với K8s hều hết ta sẽ không tạo pod mà ta sẽ tiến hành tạo deployment vì chúng ta có thể định nghĩa số lượng replicate tùy theo ý muốn. Deployment giúp ta dễ dàng quản lý và cấu hình pod. Khi đã replicate node một trong những replicate bị crash thì sever sẽ điều hướng requets đến những replicate khác.

Ứng dụng của mình được replicate thì database cũng nên được replicate vì khi một trong những replicate database bị crash ta có thể dùng những replicate khác. Nhưng chúng ta không thể replicate database với Deployment vì database có state (dữ liệu được thay đổi, cập nhật liên tục). Giả sử ta có 2 replicate đang cần cùng truy cập vào cùng data storage thì ta sẽ cần có một cơ chế để quản lý xem replicate nào đang tiến hành sửa hoặc đọc dữ liệu của storage đó để tránh dữ liệu không được nhất quán giữa các replicate. Cơ chế này có trong một component khác của K8s đó là StatefulSet.

StatefulSet để quản lý các ứng dụng stateful như mysql, mongoDB, elastic search, … Những ứng dụng này nên được tạo bằng StatefulSet chứ không phải Deployment. Cũng giống như Deployment, StatefulSet cũng replicating pod, scale up nhưng nó đảm bảo rằng database được đọc và viết một cách đồng bộ (synchronize). Tuy nhiên việc deploy StatefulSet trong K8s không hề dễ dàng nên database thường được host ngoài K8s cluster.

Với setup 2 replicate của ứng dụng và database thì khi một node bị crash hay restart thì người dùng vẫn có thể truy cập vào ứng dụng của ta bình thường vì service load balancer sẽ điều hướng ta tới node còn lại nên ta có thể tránh down time.