+17

Bảo mật Nginx Ingress với Cert Manager trên Kubernetes

Hello các bạn, lại là mình đây 👋

Hi vọng các bạn vẫn hóng series của mình, và hi vọng rằng các bạn không nghĩ rằng mình sẽ bỏ rơi series này 🤣🤣

Vậy thì hôm nay ta hâm nóng lại series này với setup Nginx Ingress Controller, cert-manager để tự động việc lấy HTTPS trên K8S nhé 😉

Lên tàu thôi ⛴️⛴️

Những thứ ta sẽ làm

Ở bài này ta sẽ deploy một Todo list app giống như của mình như sau:

Screenshot 2024-03-16 at 12.12.16 PM.png

Các bạn có thể truy cập ở đây: https://todo.jamesisme.com

App khá đơn giản với các thành phần như sau: 1 frontend, 1 backend, 1 DB MySQL

Ta sẽ deploy app trên K8S, setup Nginx Ingress, và lấy HTTPS bằng cert-manager nhé

Test thử ở local

Trước khi deploy thì luôn luôn phải chạy thử coi xem nó như thế nào đã nhỉ 😎😎

Các bạn tạo mới folder bất kì, trong đó tạo docker-compose.yml:

version: '3.7'

services:
  frontend:
    image: maitrungduc1410/k8s-ingress-demo-frontend
    restart: always
    volumes:
      - ./nginx.app.conf:/etc/nginx/conf.d/default.conf
    ports:
      - 3002:80

  backend:
    image: maitrungduc1410/k8s-ingress-demo-backend
    restart: always
    env_file: .env

  db:
    image: mysql:8
    restart: always
    environment:
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: rootpass
    volumes:
      - ./data:/var/lib/mysql/

Tạo tiếp cho mình file nginx.app.conf:

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    location /api {
        proxy_pass http://backend:3000;
        proxy_redirect     off;
        proxy_http_version 1.1;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header Host $host;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header X-Real-Ip $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

Tiếp theo ta tạo .env:

DB_HOST=db
DB_PORT=3306
DB_USER=myuser
DB_PASSWORD=myuserpass
DB_NAME=k8s_ingress
DATABASE_URL=mysql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME

Sau đó ta tạo folder data để mount volume cho mysql nha:

mkdir data

Cuối cùng là ta start app:

docker compose up -d

Và mở trình duyệt ở http://localhost:3002, các bạn tự kiểm tra xem app có lên ngon nghẻ không nhé 😂

Local oke rồi thì ta vào lý thuyết thôi nào 🥸🥸

Lý thuyết vỡ lòng

Ingress là gì?

Về cơ bản thì Ingress trong Kubernetes là một tài nguyên cho phép quản lý luồng lưu lượng truy cập đến các Service trong một Kubernetes clusler từ bên ngoài. Nó hoạt động như một cái traffic router, cho phép request từ bên ngoài được định tuyến đến các Service cụ thể trong cluster dựa trên cấu hình do ta định nghĩa.

Ingress làm cho việc quản lý traffic truy cập từ bên ngoài vào các ứng dụng trở nên dễ dàng hơn bằng cách giúp xác định các routing rules và cung cấp một cách thức để quản lý các routes.

Ta cùng xem ảnh dưới (mình lấy luôn trên trang chủ k8s vì nó khá dễ hiểu)

Screenshot 2024-03-16 at 12.39.31 PM.png

Ở trên các bạn thấy, traffic đi từ Client (bên ngoài) -> Load Balancer được managed (bởi cloud provider) -> Ingress (do ta định nghĩa), ở đó ta sẽ định nghĩa các routing rules -> vào Service -> cuối cùng là vào Pod

Ingress theo mình thấy thường dùng để expose Service của chúng ta ra ngoài bằng URL (domain) để friendly hơn, hoặc làm load balance traffic,...

Nhưng chú ý rằng Ingress nó không expose port nào cả, không giống như Service (type=NodePort) đâu nhé các bạn 😃

Thường thì mỗi app sẽ có 1 Ingress riêng, dành cho 1 domain riêng, dùng 2 domain cho 1 app cũng được nhưng mà tuỳ trường hợp

Ingress Controller

Để mà Ingress mà chúng ta viết chạy được thì ta cần có Ingress Controller

Ingress Controller là một thành phần trong Kubernetes chịu trách nhiệm cho việc triển khai và quản lý các rules của Ingress. Ingress Controller làm nhiệm vụ xử lý request tới Ingress và thực hiện các hành động phù hợp, chẳng hạn như route request đến các Service cụ thể trong cluster. Mỗi loại Ingress Controller có thể có triển khai khác nhau, nhưng chức năng chính là nhận và xử lý các request tới Ingress, sau đó cấu hình các bản ghi tương ứng trong cơ sở dữ liệu của Cluster để đảm bảo việc routing traffic đến đúng nơi.

Ingress Controller thì có rất nhiều loại, ở đây mình sẽ dùng loại phổ biến nhất là Nginx Ingress Controller

Nãy giờ nói gì hơi khó hiểu à nha 🤔🤔🤔

Oke, Ví dụ, một Nginx Ingress Controller để triển khai các quy tắc Ingress do ta viết. Khi một yêu cầu HTTP đến, Ingress Controller sẽ đọc cấu hình Ingress, quyết định nơi mà yêu cầu đó cần được chuyển tới, và sau đó cấu hình máy chủ proxy (như Nginx) để đảm bảo request được định tuyến đúng cách.

Vẫn là bức ảnh bên trên thì Ingress Controller nó như thế này:

Screenshot 2024-03-16 at 12.39.31 PM.png

Một cluster có thể có 1 Ingress Controller dùng chung cho cả cluster, nhưng cũng có thể có nhiều Ingress Controller (ví dụ mỗi namespace 1 controller). Ở bài này thì ta sẽ dùng 1 controller cho cả cluster

Nếu các bạn vẫn thấy khó hiểu, Ingress Controller nó là cái gì thì ta tạm thời công nhận nó là 1 cái "gì đó" làm nhiệm vụ đọc cấu hình Ingress do ta viết và thực thi nhé 😉

Và luôn ghi nhớ là Ingress khác Ingress Controller, ta cần dùng cho đúng khái niệm nhé

Giờ thì zô phần chính thôi nào 🚀🚀

Setup Ingress và Ingress Controller

Ingress Controller

Như mình đã nói ở phần trước, ta cần phải có Ingress Controller, tối thiểu 1 cluster 1 controller (như bài này mình làm). Ở bài này ta chọn Nginx Ingress Controller: https://kubernetes.github.io/ingress-nginx

Nhưng vì Ingress Controller setup rất phức tạp, và thường 1 cluster chỉ cần 1 cái controller thôi, nên mình đã làm luôn cho các bạn bước này, các bạn chỉ cần xem thôi 😃.

Việc mình làm thì rất đơn giản thôi, lên trang chủ của Nginx Ingress và tuỳ vào cloud provider của mình là gì mà chọn cho đúng:

Screenshot 2024-03-16 at 4.09.01 PM.png

Và ta chỉ cần kubectl apply là được, phần còn lại đã có hết ở file YML của họ rồi

Đây là tất cả những gì có của nginx ingress controller sau khi ta apply:

Screenshot 2024-03-16 at 4.13.10 PM.png

Như ta thấy thì toàn bộ những thứ liên quan tới Nginx Ingress Controller được deploy ở 1 namespace riêng, cơ bản những thứ nó làm như sau:

  • Có 1 Deployment chạy 1 con nginx
  • có 1 service expose ra bên ngoài với type=LoadBalancer
  • mọi request đi vào cluster đầu tiên sẽ qua con nginx này
  • Controller này sẽ watch tất cả các namespace, và khi nào ta tạo/sửa Ingress nó sẽ tự update và reload.

Flow kiểu như sau:

K8S Ingress Flow (1).jpg

Lấy session

Tiếp theo trước khi cầu hình Ingress thì ta cần phải deploy app demo của chúng ta trước đã nhé. Ta đầu tiên tạo 1 folder để chứa toàn bộ file manifest cho bài này nhé.

Sau đó vẫn như các bài trước ta cần phải lấy K8S Session để truy cập cluster của mình và thực hành. Các bạn nhớ check Require Domain nhé, vì bài này ta làm với Ingress mà, ta cần có domain.

Sau khi lấy được file session về thì ta check thử xem domain của ta là gì nhé:

kubectl get pod --kubeconfig=./kubernetes-config

Ta sẽ thấy in ra là No resources found in lk8s-75b79a namespace.

Và ở đây domain của ta sẽ là 75b79a.learnk8s.jamesisme.com (bỏ đi cái tiền tố lk8s-)

jamesisme.com là domain của mình 😃

Bước này các bạn phải chú ý thật kĩ domain của các bạn là gì và dùng nó xuyên suốt bài này nhé

Ở bài này ta sẽ có: frontend, backend và database (mysql). Nên tương ứng ta sẽ tạo file deployment và service. Và bởi vì ta chỉ cần frontend được expose ra bên ngoài, giao tiếp giữa frontend -> backend và backend -> DB, là internal trong cluster thôi.

Flow mà tí nữa ta deploy sẽ như sau:

Bản sao của K8S Ingress Flow.jpg

Deploy app

Giờ ta tiến hành deploy app nhé.

Đầu tiên các bạn tạo cho mình file secret.yml:

apiVersion: v1
kind: Secret
metadata:
  name: todo
type: Opaque
stringData:
  MYSQL_HOST: db
  MYSQL_PORT: "80"
  MYSQL_USER: myuser
  MYSQL_PASSWORD: myuserpass
  MYSQL_DATABASE: todo_app
  DATABASE_URL: mysql://myuser:myuserpass@db:80/todo_app
  MYSQL_ROOT_PASSWORD: rootpass

  # Note: Cách bên dưới chạy trong Docker nhưng không chạy trên k8s
  # DATABASE_URL: mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}

File này sẽ chứa secret dùng chung cho cả backend và db (mysql)

Sau đó ta apply để tạo secret nhé:

kubectl apply -f secret.yml --kubeconfig=./kubernetes-config

Tiếp theo ta tạo file deployment.yml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  labels:
    app.kubernetes.io/name: k8s-ingress-demo-backend
spec:
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: maitrungduc1410/k8s-ingress-demo-backend:latest
        ports:
        - containerPort: 3000
          name: pod-http
        resources:
          requests:
            memory: "128Mi"
            cpu: "64m"
          limits:
            memory: "750Mi"
            cpu: "500m"
        envFrom:
        - secretRef:
            name: todo

---

apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  type: ClusterIP
  ports:
    - name: svc-http
      protocol: TCP
      port: 80
      targetPort: pod-http
  selector:
    app: backend

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  labels:
    app.kubernetes.io/name: k8s-ingress-demo-frontend
spec:
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: maitrungduc1410/k8s-ingress-demo-frontend:latest
        ports:
        - containerPort: 80
          name: pod-http
        resources:
          requests:
            memory: "128Mi"
            cpu: "64m"
          limits:
            memory: "512Mi"
            cpu: "200m"

---

apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: LoadBalancer
  ports:
    - name: svc-http
      protocol: TCP
      port: 80
      targetPort: pod-http
  selector:
    app: frontend

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: db
  labels:
    app.kubernetes.io/name: k8s-ingress-demo-db
spec:
  selector:
    matchLabels:
      app: db
  template:
    metadata:
      labels:
        app: db
    spec:
      containers:
      - name: db
        image: mysql:8
        ports:
        - containerPort: 3306
          name: pod-http
        resources:
          requests:
            memory: "128Mi"
            cpu: "64m"
          limits:
            memory: "750Mi"
            cpu: "500m"
        envFrom:
        - secretRef:
            name: todo

---

apiVersion: v1
kind: Service
metadata:
  name: db
spec:
  type: ClusterIP
  ports:
    - name: svc-http
      protocol: TCP
      port: 80
      targetPort: pod-http
  selector:
    app: db

Ở trên mình dùng 1 file manifest và khai báo tất cả frontend, backend và db vào đó, mỗi cái gồm 1 deployment và 1 service. Cho tiện demo thôi chứ khi làm thật ta nên tách ra cho dễ quản lý nhé các bạn 😃

Sau đó ta apply deploymet nhé:

kubectl apply -f deployment.yml --kubeconfig=./kubernetes-config

Tiếp theo ta check xem pod chạy ok không nhé:

kubectl get po --kubeconfig=./kubernetes-config

-->>>
NAME                        READY   STATUS    RESTARTS   AGE
backend-785774cdcc-7pzb2    1/1     Running   0          1m47s
db-cd7458d4b-j86tl          1/1     Running   0          1m47s
frontend-7c8d646c5d-t7fp6   1/1     Running   0          1m47s

Check logs backend tí xem nó thế nào nào (thay tên pod của các bạn vào cho đúng nha):

kubectl logs backend-785774cdcc-7pzb2 --kubeconfig=./kubernetes-config

Thấy như sau là oke rồi 😎 Screenshot 2024-03-16 at 6.15.59 PM.png

chú ý rằng đoạn đầu backend start thì nó connect ngay đến DB, nhưng DB có thể mất chút thời gian để up, nên lần connect đầu backend có thể failed, nó sẽ tự restart và connect lại nhé

Ở file deployment.yml, ta đang để service của frontendLoadBalancer nên ta sẽ có thể truy cập trực tiếp từ tình duyệt để test thông qua IP của LB nha

Ta check xem IP của LB là gì trước nha:

kubectl get svc --kubeconfig=./kubernetes-config

--->>
NAME       TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
backend    ClusterIP      10.245.32.202    <none>        80/TCP         9m33s
db         ClusterIP      10.245.49.191    <none>        80/TCP         9m33s
frontend   LoadBalancer   10.245.190.193   <pending>     80:31420/TCP   9m33s

Như trên EXTERNAL-IP của frontend service vẫn là <pending> tức là nó đang được tạo trên cloud, ta chờ chút nhé

Chờ tẹo get service lại ta sẽ thấy như sau:

NAME       TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)        AGE
backend    ClusterIP      10.245.32.202    <none>           80/TCP         10m
db         ClusterIP      10.245.49.191    <none>           80/TCP         10m
frontend   LoadBalancer   10.245.190.193   139.59.222.113   80:31420/TCP   10m

Ở trên địa chỉ service của mình là 139.59.222.113:80, ta mở ở trình duyệt nhé:

Screenshot 2024-03-16 at 6.24.57 PM.png

Ta sẽ thấy UI đã lên, nhưng vẫn đang quay quay vì không gọi được tới backend, check console thì thấy là request tới /api đang failed. Vì ta chưa có cấu hình gì để route request /api vào backend, các bạn cứ bình tĩnh nha 😃

Cấu hình Ingress

Quay lại tấm hình mô tả mình đã nói ở trên về flow request tới app của chúng ta:

Bản sao của K8S Ingress Flow.jpg

Giờ ta sẽ cần tạo 1 ingress để điều phối tất cả request có path là /api vào backend, / vào frontend

Các bạn tạo cho mình file ingress.yml nhé (sửa tên domain cho đúng với của các bạn nhé):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: todo
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  # ingressClassName: nginx # Nếu bạn không muốn dùng annotations
  rules:
  - host: 75b79a.learnk8s.jamesisme.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: frontend
            port:
              name: svc-http
      - path: /api
        pathType: Prefix
        backend:
          service: 
            name: backend
            port:
              name: svc-http

Ở trên các bạn thấy rằng:

  • ta đang tạo resource với kind: Ingress
  • ta annotate resource này với kubernetes.io/ingress.class: "nginx" ý bảo là dùng nginx ingress nhé
  • bên dưới là ta định nghĩa các rules cho ingress này, rules là 1 Array, ở đây array chỉ có 1 item
  • item này ta khai báo host là 75b79a.learnk8s.jamesisme.com chính là cái domain mình nói ở phần lấy Session ý nhé, các bạn sửa tên domain cho đúng với của các bạn nha
  • và bên dưới ta có cấu hình http gồm 2 paths, / -> frontend service, port svc-http của service đó. Tương tự /api cho backend

Tiếp theo ta apply ingress nhé:

kubectl apply -f ingress.yml --kubeconfig=./kubernetes-config

Sau đó ta get ingress xem thế nào nha:

kubectl get ing --kubeconfig=./kubernetes-config

--->>
NAME   CLASS    HOSTS                           ADDRESS          PORTS   AGE
todo   <none>   75b79a.learnk8s.jamesisme.com   k8s.jamesisme.com   80      12m

Ở trên các bạn thấy rằng ingress của ta đã được tạo thành công, ADDRESS trỏ về domain k8s.jamesisme.com, domain này trỏ về IP của Ingress Nginx Controller.

Thực tế với các cloud khác như AWS hay GCP thì sẽ hơi khác chút là nó sẽ show luôn IP của Nginx Ingress Controller ở đây, nhưng vì trên Digital Ocean nó hơi dị một chút 😂. Bạn nào tò mò có thể xem thêm ở đây

cái ADDRESS lúc mới tạo có thể chưa lên, các bạn chờ tí nhé

Giờ là ta có thể truy cập được từ tình duyệt với domain 75b79a.learnk8s.jamesisme.com rồi đó:

Screenshot 2024-03-22 at 2.09.15 PM.png

Ủa nhưng mà bị sao vậy nhỉ???? 🙄 Mở console trình duyệt thì thấy báo 500 (Internal Server Error)

Oke thử check logs backend coi nào (thay tên backend của các bạn vào nha):

kubectl logs backend-785774cdcc-jnm8z --kubeconfig=./kubernetes-config

Screenshot 2024-03-22 at 2.11.39 PM.png

Úi quên chưa tạo bảng trong database 😁😁

Ta exec vào container và chạy migrate nhé:

kubectl exec -it backend-785774cdcc-jnm8z --kubeconfig=./kubernetes-config -- sh

yarn prisma migrate deploy

Sau đó ta quay trở lại trình duyệt F5 và có thể làm các thao tác CRUD rồi 😎

Screenshot 2024-03-16 at 6.52.23 PM.png

Pòm pòm chíu chíu lên rồi 😎😎😎🥳🥳🥳. Các bạn thử tạo vài cái todo, update delete các thứ coi sao nha

Chú ý rằng nếu các bạn bị lỗi HTTPS như bên dưới, thì khả năng là trình duyệt của các bạn đang tự redirect HTTP -> HTTPS, trường hợp này các bạn mở tab ẩn danh, trực tiếp gõ vào http://75b79a.learnk8s.jamesisme.com là được nhé: Screenshot 2024-03-16 at 6.51.52 PM.png

Vì giờ traffic đi vào sẽ tới domain và qua Nginx Ingress Controller, nên ta không cần expose frontend service với type=LoadBalancer nữa, ta đổi nó thành ClusterIP để bảo mật hơn nhé (và cũng tiết kiệm hơn nữa vì cloud provider sẽ charge thêm phí với LB)

Ở file deployment.yml ta sửa lại cấu hình service của frontend nhé:

apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: ClusterIP # <<-- sửa thành ClusterIP
  ports:
    - name: svc-http
      protocol: TCP
      port: 80
      targetPort: pod-http
  selector:
    app: frontend

Sau đó ta apply lại:

kubectl apply -f deployment.yml --kubeconfig=./kubernetes-config

Âu cây vậy là bước đầu tiên đã xong rồi đó. Tiếp theo ta cùng xem lấy HTTPS cho domain của chúng ta như thế nào nhé 😉

Tự động lấy HTTPS với cert-manager

Cài Cert-manager

Nhớ lại khi ta deploy app trên VPS, thường ta sẽ cần dùng tới certbot sau đó chạy command sau để lấy HTTPS:

certbot --nginx -d example.com

Giờ trên K8S không có certbot thì ta lại có cert-manager để giúp ta làm điều đó.

Về cơ bản cert-manager sẽ tạo TLS cert cho các workload (resource) ta tạo trên K8S cluster, và tự renew cert trước khi mà nó hết hạn. Cert-manager thì có thể giúp chúng ta lấy HTTPS từ nhiều bên khác nhau: Let's Encrypt, HashiCorp Vault, Venafi and private PKI. Ở bài này ta sẽ dùng cái phổ biến nhất là Let's Encrypt nhé (giống certbot)

Và với certmanager thì cả cluster ta cũng thường chỉ cần 1 cert-manager là đủ, trừ khi ta có những yêu cầu cụ thể hơn. Do vậy ở trên cluster của mình thì mình cũng đã cài sẵn cho các bạn rồi và các bạn không cần làm gì nữa. Các cài thì cũng rất đơn giản, mình chỉ apply file manifest của cert-manager thôi

Mình không có làm gì đặc biệt, "giấu bài" đâu 😄, nếu các bạn có cluster của riêng các bạn thì các bạn cũng làm 1 bước cài đặt như vậy thôi 😄

Lấy HTTPS

Trước khi ta lấy HTTPS thì ta cần tạo manifest để cấu hình Issuer, mục đích là để cert-manager hiểu cần phải lấy HTTPS như thế nào (Vì như bên trên mình nói cert-manager có thể lấy cert từ nhiều bên khác nhau, mỗi bên lại có cấu hình khác nhau)

Các bạn tạo cho mình file issuer.yml nhé:

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    # The ACME server URL
    server: https://acme-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: test@gmail.com
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-prod
    # Enable the HTTP-01 challenge provider
    solvers:
    - http01:
        ingress:
          class: nginx

Ở trên các bạn đổi email cho phù hợp với của các bạn nhé.

Như bên trên các bạn thấy rằng ta tạo Issuer với tên letsencrypt-prod, issuer này sẽ được ăn vào namespace của các bạn, ngoài Issuer thì ta cũng có ClusterIssuer được dùng cho toàn bộ cluster, nhưng mình khuyên nên chỉ dùng Issuer nếu có thể

Bên trên ta dùng server letsencrypt và nói với cert-manager rằng ta đang dùng nginx ingress. Bên cạnh đó ta cũng để privateKeySecretRef để lưu private key cho account ACME

1 Issuer có thể được dùng cho nhiều Ingress trên cùng namespace (đây cũng là cách mình hay dùng để đỡ phải quản lý nhiều issuer)

Bây giờ ta apply để tạo Issuer nhé:

kubectl apply -f issuer.yml --kubeconfig=./kubernetes-config

Sau đó ta check xem trạng thái Issuer như thế nào nhé:

kubectl get issuer --kubeconfig=./kubernetes-config

--->>>
NAME               READY   AGE
letsencrypt-prod   True    58s

Ở trên ta thấy READY=true rồi 👍️

Nếu các bạn muốn xem detail về issuer thì ta có thể describe nhé:

kubectl describe issuer letsencrypt-prod --kubeconfig=./kubernetes-config

Giờ ta quay lại ingress.yml và lấy HTTPS thôiiiii (nhớ thay domain của các bạn vào cho đúng):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: todo
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/issuer: "letsencrypt-prod"
spec:
  tls:
  - hosts:
    - 75b79a.learnk8s.jamesisme.com
    secretName: todo-tls
  rules:
  - host: 75b79a.learnk8s.jamesisme.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: frontend
            port:
              name: svc-http
      - path: /api
        pathType: Prefix
        backend:
          service: 
            name: backend
            port:
              name: svc-http

Ở trên các bạn thấy rằng ta vừa thêm vào cert-manager.io/issuer: "letsencrypt-prod"tls để chỉ định ta sẽ lấy HTTPS cho những host nào

Nếu ta muốn lấy HTTPS cho nhiều hơn 1 host thì ta làm như sau:

tls:
  - hosts:
    - jamesisme.com
    - www.jamesisme.com
    - www2.jamesisme.com
    ...
    secretName: todo-tls

Ổn rồi đó giờ ta apply lại ingress nha:

kubectl apply -f ingress.yml --kubeconfig=./kubernetes-config

Sau đó ta check trạng thái ingress xem thế nào nhé:

kubectl get ing --kubeconfig=./kubernetes-config

---->>>
NAME                        CLASS    HOSTS                           ADDRESS          PORTS     AGE
cm-acme-http-solver-54t4f   <none>   75b79a.learnk8s.jamesisme.com                    80        26s
todo                        <none>   75b79a.learnk8s.jamesisme.com   k8s.jamesisme.com   80, 443   3h20m

Ta thấy rằng cert-manager đang tiến hành lấy HTTPS cho chúng ta, nếu trong lúc này ta quay lại trình duyệt và F5 sẽ thấy lỗi như sau:

Screenshot 2024-03-16 at 9.59.01 PM.png

Chờ thêm một chút và ta get ingress lại sẽ thấy như sau:

NAME   CLASS    HOSTS                           ADDRESS             PORTS     AGE
todo   <none>   75b79a.learnk8s.jamesisme.com   k8s.jamesisme.com   80, 443   29s

Giờ ta quay trở lại trình duyệt và F5:

Screenshot 2024-03-16 at 10.30.20 PM.png

Pòm, HTTPS đã lên rồi 💪💪💪💪💪🥳🥳🥳

Vọc vạch chút

Ta có thể check trạng thái certificate được tạo cho ingress của chúng ta như sau:

kubectl get cert --kubeconfig=./kubernetes-config

--->>>
NAME       READY   SECRET     AGE
todo-tls   True    todo-tls   4m31s

Các bạn có thể decsribe hoặc get -o yaml để xem cụ thể bên trong cái cert kia như thế nào nhé

Nội dung thực tế của HTTPS cert của chúng ta thì được lưu ở secret như ta đã định nghĩa ở file ingress.yml (todo-tls), ta có thể xem như sau:

kubectl get secret todo-tls --kubeconfig=./kubernetes-config -o yaml

Giờ nếu ta thử update ingress để bỏ rule của route /api đi:

  - host: 75b79a.learnk8s.jamesisme.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service: 
            name: frontend
            port:
              name: svc-http
      # - path: /api
      #   pathType: Prefix
      #   backend:
      #     service: 
      #       name: backend
      #       port:
      #         name: svc-http

Sau đó ta apply lại và quay lại trình duyệt thì ta thấy lại quay đều quay đều, tức là với mọi thay đổi của chúng ta thì nginx ingress controller đã ngay lập tức reload lại

Các câu hỏi liên quan

Ta có thể deploy nhiều Ingress Controller được không?

Hoàn toàn được nhé.

Nếu các bạn để ý, hiện tại cả cluster của chúng ta có 1 cái Controller, mọi request đi vào đều qua đó, nên nếu ví dụ khi ta check log thì ta sẽ thấy log toàn bộ các request vào cluster, giả sử ta có 10 apps traffic cao thì khi đó lượng log sẽ nhiều như thế nào

Hơn nữa trong trường hợp số request khủng quá thì còn có thể làm controller CPU/RAM tăng cao ảnh hưởng hiệu suất

Ta hoàn toàn có thể deploy mỗi namespace một cái ingress controller nhé, tuỳ vào nhu cầu 😃

Search Google thấy có 2 Nginx Ingress Controller

Đúng vậy 😄.

Hiện tại ta có 2 bản: của Nginx Inc. và của cộng đồng.

Mình thì thường dùng bản của cộng đồng phát triển, vì cũng chưa có nhu cầu gì khó, và tutorial toàn dùng cái đó 😄 😄

Muốn custom nginx thì như thế nào?

Ta có thể custom nginx cho Ingress của ta viết bằng Annotation, các bạn xem tất cả thuộc tính ở đây. Ta thêm nó vào annotationsingress.yml rồi apply lại là được

Kết bài

Phùuuuu.... Bài nào trong series viết xong cũng vỡ mặt thớt dù đã cố tinh gọn rồi 😂

Hi vọng qua bài này các bạn đã biết cách setup Ingress để điều phối traffic và lấy HTTPS cho app chạy Kubernetes.

Chúc các bạn ngủ ngon, hẹn gặp lại ở bài sau 😇


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í