0

Quản trị Kubernetes on-premise (for newbie)

Lưu ý: Bài viết thuần tuý được viết góc nhìn kinh nghiệm của 1 middle đang 1 mình 1 ngựa quản trị 1 cụm k8s Dev, có thể có những điều còn sai sót, mong anh chị em nhẹ tay. Tiện đây em cũng đang kiếm job remote, làm đêm càng tốt, quý công ty có nhu cầu liên hệ em dưới comment ạ 😁

Mở đầu:

Với yêu cầu của công ty là dựng 1 cụm K8s để thay thế cho cụm production hiện tại đã quá lỗi thời do poorly maintained, kiến trúc ban đầu thiết kế rất khó để scale và fault tolerance rất kém, 1 devops (chưa đến middle) lúc đó (là em) đã phải ra tay để vẽ ra cho công ty một hệ thống tầm trung, mà đại để là anh chị em có thể nhìn vào diagram vẽ vội bên dưới

Sơ đồ hệ thống

Cho anh chị em nào mắt kém thì nói chung là thiết kế tổng thể sẽ gồm:

  1. 3 cụm k8s và vài thành phần khác, bao gồm 1.1. Cụm Admin: bao gồm Rancher để manage các cụm con và các thành phần monitor/ logging 1.2. Cụm CICD: bao gồm Gitlab và các stack khác hỗ trợ việc lưu trữ code và triển khai ứng dụng 1.3. Các cụm App: như tên
  2. Các cụm nằm trên các dải mạng riêng biệt
  3. Các DB nằm trên các máy chủ riêng ở dải mạng nội bộ
  4. Sử dụng Longhorn làm block storage system

Với thiết kế trên thì các tính chất của nó đã giải quyết các yêu cầu:

  1. Quản lí tập trung: Các cụm được quản lí chung qua cụm Admin giúp việc quản lí và debug dễ thở hơn
  2. Triển khai phân tán: Các cụm phục vụ các mục đích/ môi trường khác nhau được tách riêng tăng fault tolerance và dễ dàng để scale, nhất là nếu doanh nghiệp có nhu cầu phục vụ multi tenant

Công ty cũng nói rõ hệ thống trên mang tinh thần thử nghiệm, nhưng với quyết tâm học để kiếm lương ngàn đô thì em đã cố làm mọi thứ theo cách khó nhất có thể. Bài viết cũng bao gồm vài kinh nghiệm chung chung đã đúc kết và cả các lỗi em đã và đang gặp với stack trên

Kinh nghiệm chung

Với 1 đứa trình độ junior lúc đó, việc triển khai 1 hệ thống như trên không phải là bất khả thi, nhưng cũng gặp khá nhiều khó khăn. Kinh nghiệm đầu tiên có lẽ là

Nghiên cứu kĩ và phân tích các options lúc cài đặt

Kiếp nạn đầu tiên mà em gặp phải đến từ Ingress của RKE2. Như mọi người đã biết là từ phiên bản 1.36, RKE2 cùng các distro k8s sẽ kết thúc vòng đời hỗ trợ cho Nginx Ingress. Nhưng ngặt nỗi lúc cài đặt cụm Rancher bằng terminal, có ai nói cho điều đó đâu, và default vẫn là Nginx Ingress. Chính lựa chọn này dẫn đến 1 quá trình update và revert vài lần mới xong để đổi qua Traefik Ingress (mặc định mới của RKE2). Chính việc không đọc kĩ các options cũng dẫn đến 1 sự cố đau đầu khác liên quan đến Longhorn (mà e có thể sẽ nói ở phần sau :v )

Với các bạn newbie, hãy tập đọc docs, đôi khi còn nhanh hơn là hỏi AI

Không dưới 3 lần việc đọc kĩ docs đã giúp em tìm ra giải pháp nhanh hơn là hỏi AI, vì

  • AI chưa cập nhật kịp thay đổi trong phiên bản mới
  • Tài liệu wording hơi tệ, AI hiểu sai Dùng AI không phải xấu, nhưng đừng quá phụ thuộc

Tools sẽ luôn thay đổi, hãy học tư duy

Mỗi công ty sẽ luôn có các stack khác nhau, cái quan trọng cần học ở thời điểm đầu là tư duy tự động hoá vốn quan trọng nhất với devops. Cái này thì chỉ có làm mới nghiệm ra, sẽ chẳng mấy khoá học dạy được

Các thử thách và sự cố đã và đang gặp phải

Gitlab runner với Kubernetes executor

Với tài nguyên có hạn (và lười xin thêm VM, em quyết định dùng Kubernetes cho gitlab runner. Ưu điểm của phương pháp này là tiết kiệm tài nguyên, kiểm tra logs nhanh chóng, dễ dàng chạy nhiều concurrent jobs, cũng như không cần sử dụng docker daemon ở node, giữ cho môi trường node sạch nhất có thể. Dưới đây là helm chart cho runner, được e viết lại theo docs (link). Nhược điểm tổng thời gian sẽ lâu hơn 1 chút do phải sinh thêm pod mới

gitlabUrl: https://gitlab.example.com

runnerRegistrationToken: "glrt-xxxxxxxx"
nodeSelector:
  node-role: runner
rbac:
  create: true
concurrent: 10
runners:
  requestConcurrency: 10
  config: |
    listen_address = ":9252"
    concurrent = 3
    check_interval = 1
    log_level = "debug"
    log_format = "runner"
    connection_max_age = "15m0s"
    shutdown_timeout = 0

    [session_server]
      session_timeout = 1800
    [[runners]]
      name = "podman"
      limit = 50
      url = "https://gitlab.example.com/"
      executor = "kubernetes"
      builds_dir = "/my_custom_dir"
      shell = "bash"
      namespace= "gitlab-runner"
      [runners.kubernetes]
        host = ""
        image_pull_secrets = ["image_pull_secrets"]
        bearer_token_overwrite_allowed = false
        image = ""
        namespace = "gitlab-runner"
        namespace_overwrite_allowed = ""
        namespace_per_job = false
        privileged = true
        cache_dir = "/cache"
        node_selector_overwrite_allowed = ""
        node_tolerations_overwrite_allowed = ""
        pod_labels_overwrite_allowed = ""
        service_account_overwrite_allowed = ""
        pod_annotations_overwrite_allowed = ""
        [runners.kubernetes.node_selector]
          "node-role" = "runner"
        [runners.kubernetes.volumes]
          [[runners.kubernetes.volumes.empty_dir]]
            name = "repo"
            mount_path = "/my_custom_dir"
          [[runners.kubernetes.volumes.secret]]
            name = "gitlab-domain-cert"
            mount_path = "/custom-certs"
            read_only = true
          [[runners.kubernetes.volumes.pvc]]
            name = "cache-pvc"
            mount_path = "/cache"
          [[runners.kubernetes.volumes.config_map]]
            name = "custom-dockerfile"
            mount_path = "/custom-dockerfile"
        [runners.kubernetes.pod_security_context]
          run_as_user = 1000
          fs_group = 1000
        [runners.kubernetes.build_container_security_context]
          run_as_user = 1000
certsSecretName: gitlab-domain-cert

Như ở file trên có 1 đoạn mount 1 config map chưa make file và template cho các stack thường dùng ( [[runners.kubernetes.volumes.config_map]]), do đó dockerfile dùng cho production sẽ không đẩy thẳng vào code nếu muốn mà được gen ra trong lúc chạy CI

Gitlab báo random 5xx chập chờn dù vẫn thừa tài nguyên

Nếu cài đặt GItlab Helm bundle, hoặc trên server Postgres mới dựng/ không được dùng nhiều ( hoặc gitlab helm version 10. trở xuống), có thể ace sẽ gặp lỗi này (link).

Check logs trước ở postgres, khả năng cao sẽ báo lỗi

FATAL:  remaining connection slots are reserved for non-replication superuser connections

Default MAX_CONNECTIONS của Postgres helm installation rất thấp, đơn giản là hãy nâng lên

postgresql:
  primary:
    extraEnvVars:
      - name: POSTGRESQL_MAX_CONNECTIONS
        value: "1024"

Làm tương tự nếu server Postgres đặt ở ngoài

Pod không thể mount lại vào PVC của longhorn

Nhất là với những PVC RWO mode, sau khi cập nhật pod có thể xảy ra tình trạng MountVolume.SetUp failed for volume, hoặc đơn giản logs pod có thể báo là đang có 1 pod khác đang occupy Check logs như các bước trong link, nếu đúng là do multipathd thì có thể disable luôn

sudo systemctl stop multipathd && sudo systemctl disable multipathd

Lưu ý khi update Kubernetes với longhorn

Trên các node có chứa các pvc chỉ 1 replica, lúc rolling update k8s, sẽ thể drain hoàn toàn node, bởi policy mặc định của longhorn block-if-contains-last-replica. Để xử lí hay đổi sang block-for-eviction-if-contains-last-replica, lưu ý luôn phải có 1 node đủ dung lượng để build lại replica đó trên node khác. Để an toàn hãy luôn đảm bảo rằng mọi pvc có tối thiểu 2 replicas trên 2 node khác nhau.

Tips and tricks: trong trường hợp chỉ có duy nhất 1 node đủ storage cho pvc đó (pvc quá lớn), cta có thể đổi policy về allow-if-replica-is-stopped, nhưng pod gắn vào pvc đó phải ở trên cùng node với pvc đó, mục đích là khi evict pod đó thì replicas cũng sẽ được stop -> quá trình drain cũng sẽ được tiếp tục. Để chắn chắn cả 2 có thể cùng schedule trên cùng pod, tạm thời disable scheduling trên các pod longhorn lúc cài đặt, và giảm pvc replicas về 1

Traefik ingress stuck Progressing

Do Traefik còn khá mới, vẫn có vài bug nhỏ nhỏ như này. Lỗi này xảy ra do argo đọc field này trên resource để xem ingress đã heathy chưa status: loadBalancer: {}. Như set up trên của e, TLS terminated ở Traefik, và tất cả traefik sẽ đi qua port 443 trên các node, không set LB status làm field trên bị trống -> Argo báo là progressing. Phần lớn mọi người có thể fix theo cách này (update traefik helm chart):

providers:
  kubernetesIngress:
    publishedService:
      enabled: true
      # Format: "namespace/traefik-service-name"
      pathOverride: "traefik/traefik" 

Hoặc đơn giản hơn cả là override để argo luôn báo heathy

data:
  resource.customizations: |
    networking.k8s.io/Ingress:
      health.lua: |
        hs = {}
        hs.status = "Healthy"
        hs.message = "Skip health check for Ingress"
        return hs

Một vài quy trình tự động hoá và trò mèo để giảm thiểu workload

Chiến lược quản lí cert

Với cụm Admin, mặc định là sẽ không thể truy cập từ Internet, và cũng thường ít có nhu cầu truy cập từ xa, cũng như ace quản trị cũng sẽ không quan tâm đến dấu đỏ untrusted CA trên browser lắm, thì như em sẽ dùng selfsigned cert rồi config cho cert manager tự rotate luôn. Điều kiện là phải trust CA này trên tất cả các node mà cụm Admin quản trị, bao gồm node ở các cụm khác. Helm chart installation sẽ phải thêm privateCA: true để các node có thể trust CA khi join cụm Ví dụ:

hostname: rancher.example.com
replicas: 1
ingress:
  enabled: true
  servicePort: 80
  ingressClassName: "traefik"
  tls:
    source: secret
    secretName: tls-rancher-ingress
  extraAnnotations:
    traefik.ingress.kubernetes.io/router.entrypoints: "websecure"
    traefik.ingress.kubernetes.io/router.tls: "true"
privateCA: true

Với cert cho cụm App, hầu hết các cty phải mua cert wildcard từ các CA public, thì em dùng reflector để chỉ phải update 1 lần, thay đổi tất cả cert trên từng namesapce cho từng ứng dụng, đỡ phải mò từng namespace để thay

Argo instance cho mỗi cụm

Mỗi cụm sẽ có 1 argo instace riêng để quản lí các tools cài đặt trên cụm đó, trỏ đến repo chưa values file trên gitlab, set server side apply và manual sync, việc cập nhật chỉ bao gồm thay đổi param trên gitlab.

Kết

Hy vọng những kinh nghiệm e đúc kết được sẽ giúp đỡ phần nào cho anh chị em, nhất là các anh em mới chân ướt chân ráo vào nghề. Bài viết dài, còn ngô nghê do mới vào nghề chưa biết gì, mong ace lượng thứ và góp ý nhẹ tay


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí