Setup RKE2 cluster with cert-manager

หลังติดตั้ง RKE2 cluster ก็ได้ kubernetes node ขึ้นมาได้ง่ายๆ ทีนี้ปัญหามาอยู่ที่จะ host service โดยให้ cert-manager ทำการสร้าง certificate ผ่าน Letsencrypt ได้โดยอัตโนมัติ

การติดตั้ง cert-manager ทำได้ผ่าน helm ได้ตาม URL ต้นทางเลย https://cert-manager.io/docs/installation/

ในตอนที่ติดตั้งจริงๆ แล้วทำผ่าน Rancher UI ก็กดจาก Apps ได้ตรงๆ เพียงแต่เราจะไม่มีตัวเลือกให้สร้าง CRD (Custom Resource Definition) จึงแอบไปทำก่อนผ่าน helm CLI (ก็ตามในหน้าติดตั้ง อย่ารีบอ่านจนข้ามขั้นตอนนี้ไป)

หลังติดตั้ง cert-manager แล้ว การจะให้ cert-manager ทำการสร้าง certificate ให้กับ ingress ใดๆ จะต้องมีการทำ matching กันก่อน โดยการลงทะเบียน (ใช้ ClusterIssuer เลย ทำทีเดียวสะดวกดี) ให้ apply yml file ดังนี้

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-production
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    preferredChain: "ISRG Root X1"
    email: your@email.com
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-prd
    solvers:
      - http01:
          ingress:
            class: nginx

จากเงื่อนไขส่วนของ solver ด้วย http-01 challenge ตัว ingress ที่ต้องการให้ clusterIssuer ตัวนี้ทำขั้นตอนการขอ certificate ให้ จะต้องทำการ annotation ให้ใช้ cluster-issuer “letsencrypt-production” ตามชื่อที่สร้างไว้ ดังตัวอย่างดังนี้

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-rancher
  namespace: dashcloud
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-production"
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  ingressClassName: nginx
  tls:
    - hosts:
      - yourservice.yourdomain.com
      secretName: yourservice-tls
  rules:
    - host: yourservice.yourdomain.com
      http:
        paths:
          - path: /(.+)
            pathType: Prefix
            backend:
              service:
                name: yourservice
                port:
                  number: 3000

หลังสร้างไปแล้ว พบปัญหา cert-manager ไม่ยอมสร้าง certificate ให้ (Request Approve แต่ไม่ Ready) ต้องไปดู logs ของ cert-manager pod ใน ns cert-manager พบปัญหา error ในขั้นตอน self-check ราวๆ นี้ (mask host นิดหน่อย)

E0615 13:42:25.519370       1 sync.go:190] "cert-manager/challenges: propagation check failed" err="failed to perform self check GET request 'http://yourservice.yourdomain.com/.well-known/acme-challenge/ryuGypRgK7iF2HmpQYD9m7GuPz3dkXn_bq3P7G1m4vY': Get \"http://yourservice.yourdomain.com/.well-known/acme-challenge/ryuGypRgK7iF2HmpQYD9m7GuPz3dkXn_bq3P7G1m4vY\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)" resource_name="yourservice-tls-ptk8q-2512130651-1182728672" resource_namespace="dashcloud" resource_kind="Challenge" resource_version="v1" dnsName="yourservice.yourdomain.com" type="HTTP-01" 

ซึ่งสาเหตุของปัญหามี 2 ส่วนคือ

name resolution

การ resolve dns จะเห็น hostname ที่เราสร้าง ingress แบบปกติ ทำให้ได้ public IP มา และเครื่องในวง k8s ไม่สามารถเรียก http/https ด้วย IP นั้นได้ ส่วนใหญ่จะเป็นปัญหาทั่วไปที่ใช้กับ NAT network คือเครื่องในวงไม่สามารถเรียกเครื่องอื่นที่อยู่ในวงเดียวกัน แก้ไขแบบลวกๆ ได้โดยการเพิ่ม dns rewrite ใน configmap ของ coredns เช่น

HTTP-01 handshake

ตอนทำ self check และตอนที่ Letsencrypt server จะตรวจสอบ HTTP-01 challenge (เปิดเว็บมาที่ /.well-known/xxxxxx) นั้นจะเรียกมาที่ port http ไม่ใช่ https ซึ่ง RKE2 ทีใช้ปัจจุบัน มีการทำ port mapping เพียงแค่ https (443) port เดียว เคสนี้จึงต้องทำการเพิ่ม port http ไปที่ตัว service ด้วย ดังนี้