Search code examples
kuberneteskubernetes-ingress

Configure Ingress traffic for Kubernetes image


I have a standalone Kubernetes cluster:

plane node - hostname kubernetes1 - 192.168.1.126
work node - hostname kubernetes2 - 192.168.1.138

I deployed this private repository:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  capacity:
    storage: 5Gi # specify your own size
  volumeMode: Filesystem
  persistentVolumeReclaimPolicy: Retain
  local:
    path: /opt/registry # can be any path
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - kubernetes2
  accessModes:
    - ReadWriteMany # only 1 node will read/write on the path.
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pv1-claim
spec: # should match specs added in the PersistenVolume
  accessModes:
    - ReadWriteMany
  volumeMode: Filesystem
  resources:
    requests:
      storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: private-repository-k8s
  labels:
    app: private-repository-k8s
spec:
  replicas: 1
  selector:
    matchLabels:
      app: private-repository-k8s
  template:
    metadata:
      labels:
        app: private-repository-k8s
    spec:
      volumes:
       - name: certs-vol
         hostPath:
          path: /opt/certs
          type: Directory
       - name: task-pv-storage
         persistentVolumeClaim:
           claimName: pv1-claim # specify the PVC that you've created. PVC and Deployment must be in same namespace.
      containers:
        - image: registry:2
          name: private-repository-k8s
          imagePullPolicy: IfNotPresent
          env:
          - name: REGISTRY_HTTP_TLS_CERTIFICATE
            value: "/opt/certs/registry.crt"
          - name: REGISTRY_HTTP_TLS_KEY
            value: "/opt/certs/registry.key"
          ports:
            - containerPort: 5000
          volumeMounts:
          - name: certs-vol
            mountPath: /opt/certs
          - name: task-pv-storage
            mountPath: /opt/registry

Service is deployed on the work node:

kubernetes@kubernetes1:/opt/registry$ kubectl get pods
NAME                                      READY   STATUS    RESTARTS   AGE
private-repository-k8s-6d5d954b4f-ldwd6   1/1     Running   0          153m
kubernetes@kubernetes1:/opt/registry$

I tried to create a ingress access because I want to access this pod from the outside:

kubectl create namespace test

service controller:

apiVersion: networking.k8s.io/v1   
kind: Ingress   
metadata:
  namespace: test   
  name: private-repository-service-ingress   
  annotations:   
    nginx.ingress.kubernetes.io/rewrite-target: /$1   
spec:   
  rules:   
    - host: kubernetes2   
      http:   
        paths:   
          - path: /   
            pathType: Prefix   
            backend:   
              service:   
                name: private-repository-service   
                port:   
                  number: 5000

service-load-balancer:

apiVersion: v1
kind: Service
metadata:
  namespace: test
  name: private-repository-service
spec:
  #type: NodePort
  selector:
    app: private-repository-k8s
  ports:
      # By default and for convenience, the `targetPort` is set to the same value as the `port` field.
    - port: 5000
      targetPort: 5000
      # Optional field
      # By default and for convenience, the Kubernetes control plane will allocate a port from a range (default: 30000-32767)
      #nodePort: 30007

When I run curl 192.168.1.138:5000 there is no response. Do you know where I might be wrong?

EDIT:

kubernetes@kubernetes1:~$ kubectl get svc
NAME                       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kubernetes                 ClusterIP   10.96.0.1        <none>        443/TCP    3d20h
private-registry-service   ClusterIP   10.103.148.234   <none>        5000/TCP   6h34m
kubernetes@kubernetes1:~$

Solution

  • Looks like you don't have an ingress-controller of any kind and just want to access the registry directly. In that case you need to create a service of type NodePort.

    apiVersion: v1
    kind: Service
    metadata:
      namespace: test
      name: private-repository-service
    spec:
      type: NodePort # Added
      selector:
        app: private-repository-k8s
      ports:
        - port: 5000
          targetPort: 5000
          nodePort: 30123 # Added
    

    This will bind the service port 5000 to the host's port 30123. If you run kubectl get svc this will than give you a slightly different output.

    kubernetes@kubernetes1:~$ kubectl get svc
    NAME                       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
    kubernetes                 ClusterIP   10.96.0.1        <none>        443/TCP    3d20h
    private-registry-service   ClusterIP   10.103.148.234   <none>        5000:30123/TCP   6h34m
    

    Notice the mapping 30312:5000. Now you can send a request to the registry on that port: curl 192.168.1.138:30312. You can also omit the nodePort field, kubernetes will then choose a random one in the range between 3000 and 32767 for you. It will be displayed in the kubectl get svc command as shown above. The Ingress is not needed and can be removed.

    If you want to use an Ingress as you provided you need to use an ingress-controller, like nginx or traefik, see also kubernetes docs on that topic.

    [...] An Ingress controller is responsible for fulfilling the Ingress, usually with a load balancer, though it may also configure your edge router or additional frontends to help handle the traffic.

    EDIT

    There are a lot of ingress-controllers out there, they all have their advantages and disadvantages. For a beginner, nginx might be a good choise, see docs.

    To install it, run these commands (from the install docs page)

    $ helm repo add nginx-stable https://helm.nginx.com/stable
    $ helm repo update
    $ helm install my-release nginx-stable/nginx-ingress
    

    where my-release is a random name, you can choose what ever you want. This will create an nginx pod in the namespace you installed the chart. It will also create an nginx-ingress service of type LoadBalancer, like this:

    kubectl get svc
    NAME                       TYPE           CLUSTER-IP     EXTERNAL-IP          
    my-release-nginx-ingress   LoadBalancer   10.109.27.49   <pending>     80:30536/TCP,443:31694/TCP   111s
    

    As you can see the EXTERNAL-IP is in <pending> state. In a public cloud environment like AWS a load-balancer resource like ELB is created and its public ip will be assigned to the service as EXTERNAL-IP. In your on-premise setup it will stay in <pending> status. But as you can see, two random node ports are mapped to the http/https ports, like with the nodePort setup above. Here it's 80 -> 30536 and 433 -> 31694, for you it will be something similar.

    Now you can apply your manifests as above. You'll get a service of type ClusterIP. Also create an Ingress like this:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: private-repository-service 
    spec:
      ingressClassName: nginx
      rules:
        - host: example.com
          http:
            paths:
            - path: /
              pathType: Prefix
              backend:
                service:
                  name: private-repository-service 
                  port:
                    number: 5000
    

    Now you can run curl against that host curl -HHost:example.com "http://192.168.1.138:30536/" (for me it's port 30536, will be a different one for you) and you will get an answer from the registry. Works the same with https using the other port.

    Note that I installed everything in the same namespace. In reality you should have a dedicated ingress namespace.

    I would also highly recommand to learn the basics of kubernetes, e.g. by a Udemy Course or a youtube tutorail series. If you want to know more about ingress-controller in an on-premise setup, check out my other answer on that topic.