В данном туториале максимально просто расскажу и покажу на практике как настроить автоматический выпуск сертификатов в локальном kubernetes так, что бы ваша локальная машина доверяла им. Я постарался написать его так, чтобы даже новичкам можно было настроить свой куб просто следуя данной инструкции.

Итак, наш план примерно такой:

  1. Развернем локально кластер локально. Установим MetalLB, cert-manager, ingress и поднимем тестовые приложения grafana и argocd.

  2. Настроим нашу локальную машину так, чтобы она доверяла выпущенным в кубе сертификатам.

  3. Задействуем nip.io, чтобы не прописывать локально в hosts хостнеймы разворачиваемых приложений.

  4. Поднимем grafana и argocd двумя разными способами (yaml и helm chart).

Поднимаем кластер

Тут совершенно нет никаких проблем, я буду использовать стандартный Docker Desktop для запуска. Ставим галочку в настройках Docker Desktop, что нам нужен куб и погнали дальше.

image.png
Включаем локальный куб

Установка и настройка MetalLB

Теперь давайте установим MetalLB и сконфигурируем просто для нашего удобства. MetalLB выдаст ingress Load Balancer наш локальный IP-адрес.

MetalLB — это балансировщик нагрузки для Kubernetes, предназначенный для работы в средах, где нет встроенного облачного балансировщика, например, в bare-metal кластерах.

# Для начала проверим что мы точно в нужном кластере
$ kubectl config get-contexts                                                                                                                                                     
CURRENT   NAME             CLUSTER          AUTHINFO         NAMESPACE
*         docker-desktop   docker-desktop   docker-desktop   
          orbstack         orbstack         orbstack    
# Устанавливаем
$ kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/main/config/manifests/metallb-native.yaml
  namespace/metallb-system created
  .......
  serviceaccount/speaker created
  .......
  validatingwebhookconfiguration.admissionregistration.k8s.io/metallb-webhook-configuration created

# Проверяем что подики поднялись
$ kubectl get pods -n metallb-system -w
  NAME                         READY   STATUS    RESTARTS   AGE
  controller-8b7b6bf6b-4phg6   0/1     Running   0          5s
	speaker-h75cw                0/1     Running   0          5s
  controller-8b7b6bf6b-bc2jw   1/1     Running   0          36s
  speaker-xd8xt                1/1     Running   0          36s

Теперь сразу же настроим, чтобы выдавался только IP 127.0.0.1. Если у вас есть необходимость пошарить ваши приложения из куба в локальную сеть, то вы можете указать ваш локальный IP адрес в качестве диапазона адресов для пула metallb.

$ kubectl apply -f - <<EOF
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: ingress-ip-pool
  namespace: metallb-system
spec:
  addresses:
    - "127.0.0.1-127.0.0.1"
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: advert
  namespace: metallb-system
EOF

Домены, сертификаты и Ingress

Для начала нам надо выпустить корневой сертификат. Для этих целей будем использовать mkcert. Это утилита, которая позволяет легко создавать локальные SSL/TLS-сертификаты без необходимости подписывать их у внешнего удостоверяющего центра (CA). Основное преимущество mkcert — автоматическая генерация доверенного корневого сертификата и выпуск локальных сертификатов, которые сразу же распознаются браузерами и системами без дополнительных настроек. Я не буду описывать процесс установки. Это есть в документации.

# Запустим утилиту. Это надо сделать один раз, она сгенерит CA и пропишет в нашу ОС.
$ mkcert --install

Далее установим наш cert-manager в kubernetes и добавим наш CA в кластер, чтобы мы могли выпускать сертификаты.

# Устанавливаем cert-manager
$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.1/cert-manager.crds.yaml
$ helm repo add jetstack https://charts.jetstack.io --force-update
$ helm install cert-manager --namespace cert-manager --version v1.17.1 jetstack/cert-manager --create-namespace
 
# cert-manager сможет использовать этот CA для автоматической выдачи сертификатов в кластере.
$ kubectl create secret tls mkcert-ca-key-pair --key "$(mkcert -CAROOT)"/rootCA-key.pem --cert "$(mkcert -CAROOT)"/rootCA.pem -n cert-manager

# Создаем объект ClusterIssuer в Kubernetes, который будет использовать сертификаты из секрета mkcert-ca-key-pair для подписывания локальных TLS-сертификатов.
$ kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: mkcert-issuer
  namespace: cert-manager
spec:
  ca:
    secretName: mkcert-ca-key-pair
EOF

Поскольку мы хотим в ingress задавать нужный домен и чтобы он сразу был доступен на локальной машине, то нет ничего проще чем использовать nip.io.

nip.io — это бесплатный сервис для динамического DNS, который позволяет использовать валидные доменные имена, привязанные к IP-адресу. Сервис автоматически подставляет IP-адрес при запросе. Ты просто используешь домен в формате: <IP-адрес>.nip.io. Например:

  • grafana.127.0.0.1.nip.io → резолвится в 127.0.0.1

  • 192.168.1.52.nip.io → разолвится в 192.168.1.52

  • demo.203.0.113.20.nip.io → резолвится в 203.0.113.20

Таким образом наши приложения будут иметь домены grafana.127.0.0.1.nip.io и argocd.127.0.0.1.nip.io. И они оба будут резолвиться в 127.0.0.1. В файл hosts ничего прописывать не нужно.

Теперь давайте создадим wildcard сертификат, который будет использован по умолчанию в нашем будущем ingress.

$ kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: default-ingress-cert
  namespace: cert-manager
spec:
  secretName: default-ingress-cert
  issuerRef:
    name: mkcert-issuer
    kind: ClusterIssuer
  dnsNames:
  - "*.127.0.0.1.nip.io"
EOF

Устанавливаем ingress и делаем так, чтобы он использовал этот сертификат по умолчанию. Как вы уже поняли это позволит нам не выпускать каждый раз новые сертификаты для наших приложений. Хотя в целом вам ничего не мешает использовать аннотацию в ingress для выпуска сертификата под любой другой домен.

$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx --force-update
$ helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.extraArgs.default-ssl-certificate="cert-manager/default-ingress-cert"
image.png
External IP выдан

Запускаем приложения и тестируем

Применяем подготовленные манифесты с Deployment, Service и Ingress для запуска grafana. Данный пост не преследует цель показать best way запуска grafana . Поэтому мы опустим все нюансы pv, сессий и тд. Пример ниже мы просто используем для демонстрации.

kubectl create namespace grafana

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grafana-deployment
  namespace: grafana
  labels:
    app: grafana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: grafana
  template:
    metadata:
      labels:
        app: grafana
    spec:
      containers:
      - image: grafana/grafana:11.5.1
        name: grafana
        ports:
        - containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
  name: grafana-service
  namespace: grafana
  labels:
    app: grafana
spec:
  selector:
    app: grafana
  ports:
    - protocol: TCP
      port: 3000
      targetPort: 3000
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: grafana-ingress
  namespace: grafana
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - grafana.127.0.0.1.nip.io
  rules:
  - host: grafana.127.0.0.1.nip.io
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: grafana-service
            port:
              number: 3000
EOF
image.png
ingress для grafana

Хочу обратить внимание, что я специально не задавал аннотаций. В нашем случае будет использован уже выпущенный сертификат ранее default-ingress-cert. Но если необходимо использовать для своего приложения отдельный, то вам ничего не мешает задействовать аннотации к ingress.

Пример ingress.yml с выпуском сертификата для grafana.127.0.0.1.nip.io
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: grafana-ingress
  annotations:
    cert-manager.io/cluster-issuer: mkcert-issuer
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - grafana.127.0.0.1.nip.io
    secretName: grafana-ingress-cert
  rules:
  - host: grafana.127.0.0.1.nip.io
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: grafana-service
            port:
              number: 3000

В результате получаем приложение с валидным на локальной машине самоподписным сертификатом по адресу https://grafana.127.0.0.1.nip.io.

image.png
Все выглядит валидно

Давайте для примера еще установим argocd и проверим что все работает как надо.

$ helm repo add bitnami https://charts.bitnami.com/bitnami --force-update

$ helm install argocd bitnami/argo-cd \
  --namespace argocd --create-namespace \
  --set server.ingress.enabled=true \
  --set server.ingress.hostname="argocd.127.0.0.1.nip.io" \
  --set server.ingress.ingressClassName="nginx" \
  --set server.ingress.path="/" \
  --set server.ingress.pathType="Prefix" \
  --set server.ingress.tls=true \
  --set server.insecure=true

В результате получаем agocd, прикрытый выпущенным сертификатом, валидный в рамках нашей машины.

image.png
Ну какова красота

А на этом всё — спасибо за внимание. При желании заглядывайте в тележку.

Комментарии (0)