Cert Manager

Делаю:
2026.02.03


https://www.youtube.com/watch?v=hoLUigg4V18

https://github.com/marcel-dempers/docker-development-youtube-series/tree/master/kubernetes/cert-manager


Поехали

// Запуск кластера kind
$ kind create cluster --name certmanager --image kindest/node:v1.35.0


https://github.com/jetstack/cert-manager/releases/


$ cd ~/tmp
$ curl -LO https://github.com/jetstack/cert-manager/releases/download/v1.19.3/cert-manager.yaml

$ mv cert-manager.yaml cert-manager-1.19.3.yaml


// install cert-manager
$ kubectl apply --validate=false -f cert-manager-1.19.3.yaml


$ kubectl get pods -n cert-manager
NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-845844dd8-xn7d2               1/1     Running   0          107s
cert-manager-cainjector-7b5d65fbcb-sb4zn   1/1     Running   0          107s
cert-manager-webhook-6fcf4cb6c-cqwmc       1/1     Running   0          107s


Test Certificate Issuing

Let’s create some test certificates

$ kubectl create ns cert-manager-test


$ cat << EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: test-selfsigned
  namespace: cert-manager-test
spec:
  selfSigned: {}
EOF


$ cat << EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: selfsigned-cert
  namespace: cert-manager-test
spec:
  dnsNames:
    - example.com
  secretName: selfsigned-cert-tls
  issuerRef:
    name: test-selfsigned
EOF


$ kubectl describe certificate -n cert-manager-test
$ kubectl get secrets -n cert-manager-test
$ kubectl delete ns cert-manager-test


Ingress Controller

$ kubectl create ns ingress-nginx


https://github.com/kubernetes/ingress-nginx


$ kubectl -n ingress-nginx apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.14.3/deploy/static/provider/cloud/deploy.yaml


$ kubectl get pods -n ingress-nginx
NAME                                        READY   STATUS    RESTARTS   AGE
ingress-nginx-controller-7fdf8d9764-thnjw   1/1     Running   0          49s


$ kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.96.6.115    <pending>     80:31983/TCP,443:30265/TCP   2m15s
ingress-nginx-controller-admission   ClusterIP      10.96.196.39   <none>        443/TCP                      2m15s


$ kubectl -n ingress-nginx --address 0.0.0.0 port-forward svc/ingress-nginx-controller 8080:80
$ kubectl -n ingress-nginx --address 0.0.0.0 port-forward svc/ingress-nginx-controller 8081:443


// Ok!
http://localhost:8080
https://localhost:8081


Setup my DNS

In my container, I can get the public IP address of my computer by running a simple command:

curl ifconfig.co

I can log into my DNS provider and point my DNS A record to my IP.
Also setup my router to allow 80 and 443 to come to my PC

If you are running in the cloud, your Ingress controller and Cloud provider will give you a public IP and you can point your DNS to that accordingly.


Create Let’s Encrypt Issuer for our cluster

We create a ClusterIssuer that allows us to issue certs in any namespace


$ cat << EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-cluster-issuer
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your-email@email.com
    privateKeySecretRef:
      name: letsencrypt-cluster-issuer-key
    solvers:
    - http01:
       ingress:
         class: nginx
EOF


// check the issuer
$ kubectl describe clusterissuer letsencrypt-cluster-issuer


Deploy a pod that uses SSL


$ cat << EOF | kubectl apply -f -
# kind create cluster --name deployments --image kindest/node:v1.31.1
# kubectl apply -f deployment.yaml

# deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-deploy
  labels:
    app: example-app
    test: test
  annotations:
    fluxcd.io/tag.example-app: semver:~1.0
    fluxcd.io/automated: 'true'
spec:
  selector:
    matchLabels:
      app: example-app
  replicas: 2
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: example-app
    spec:
      containers:
      - name: example-app
        image: aimvector/python:1.0.4
        imagePullPolicy: Always
        ports:
        - containerPort: 5000
        # livenessProbe:
        #   httpGet:
        #     path: /status
        #     port: 5000
        #   initialDelaySeconds: 3
        #   periodSeconds: 3
        resources:
          requests:
            memory: "64Mi"
            cpu: "50m"
          limits:
            memory: "256Mi"
            cpu: "500m"
      tolerations:
      - key: "cattle.io/os"
        value: "linux"
        effect: "NoSchedule"
#NOTE: comment out `volumeMounts` section for configmap and\or secret guide
        # volumeMounts:
        # - name: secret-volume
        #   mountPath: /secrets/
        # - name: config-volume
        #   mountPath: /configs/
#NOTE: comment out `volumes` section for configmap and\or secret guide
      # volumes:
      # - name: secret-volume
      #   secret:
      #     secretName: mysecret
      # - name: config-volume
      #   configMap:
      #     name: example-config #name of our configmap object
EOF


$ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
example-deploy-6489756d87-gcx9z   1/1     Running   0          2m8s
example-deploy-6489756d87-zd86s   1/1     Running   0          2m8s


$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: example-service
  labels:
    app: example-app
spec:
  type: ClusterIP
  selector:
    app: example-app
  ports:
    - protocol: TCP
      name: http
      port: 80
      targetPort: 5000
EOF


$ cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: "nginx"
  name: example-app
spec:
  tls:
  - hosts:
    - marcel.guru
    secretName: example-app-tls
  rules:
  - host: marcel.guru
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: example-service
            port:
              number: 80
EOF


Issue Certificate


$ cat << EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: example-app
  namespace: default
spec:
  dnsNames:
    - marcel.guru
  secretName: example-app-tls
  issuerRef:
    name: letsencrypt-cluster-issuer
    kind: ClusterIssuer
EOF


// check the cert has been issued
$ kubectl describe certificate example-app


$ kubectl get secrets
NAME TYPE DATA AGE
example-app-q8cxl Opaque 1 64s