Tutorial : Traefik 2.0 - Baremetal - RancherOS - Hetzner - OVH

Hello,

Last time i’ve check, Traefik wasn’t working out of the box on the Rancher Products.
This is following my another post about RancherOS/Rancher.

  • Using Helm package didn’t get our Ingress directives as ready on the UI
  • Using the guide on the traefik wasn’t working as it is not up to date of the 2.0 Rancher release.
  • We are going to use the lastest release of Traefik 2.0

So what can we do ? Try, try and try until we figure it out

But first a reminder about what is Traefik:

Traefik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy. Traefik integrates with your existing infrastructure components (Docker, Swarm mode, Kubernetes, Marathon, Consul, Etcd, Rancher, Amazon ECS, …) and configures itself automatically and dynamically. Pointing Traefik at your orchestrator should be the only configuration step you need

Disclaimer

We well not talk about how to set or use Rancher products and associate tools. You need to be aware of how to use Rancher Products and kubectl tool.
This topic could be upgraded at any times regarding feedback so don’t hesitate to comment.

Objectives

Running Rancher on top of a single RancherOS server i needed to have a simple solution in order to expose my services to the WWW. Traefik have a single feature that i didn’t found on any other (yet) Ingress controller (Ambassador, Istio, Haproxy, External-DNS etc…) which is to be able to use Letsencrypt DNS Challenge with a support of OVH as a provider.

Let’s do it :wink::

  1. Create a dedicated ns ( or not it’s up to you but i recommend it ) without a restricting policy in place as we are going to bind to restricted port ( 80/443/22 )
kubectl create ns traefik
  1. Create the Traefik Custom Ressources Definitions
    This is a new configuration required regarding Traefik 1.7.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutes.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRoute
    plural: ingressroutes
    singular: ingressroute
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutetcps.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRouteTCP
    plural: ingressroutetcps
    singular: ingressroutetcp
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: middlewares.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: Middleware
    plural: middlewares
    singular: middleware
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsoptions.traefik.containo.us

spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSOption
    plural: tlsoptions
    singular: tlsoption
  scope: Namespaced

  1. RBAC Settings
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller

rules:
  - apiGroups:
      - ""
    resources:
      - services
      - endpoints
      - secrets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses/status
    verbs:
      - update
  - apiGroups:
      - traefik.containo.us
    resources:
      - middlewares
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - traefik.containo.us
    resources:
      - ingressroutes
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - traefik.containo.us
    resources:
      - ingressroutetcps
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - traefik.containo.us
    resources:
      - tlsoptions
    verbs:
      - get
      - list
      - watch

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller

roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
  - kind: ServiceAccount
    name: traefik-ingress-controller
    namespace: traefik
    
  1. Traefik Configuration file

Traefik 2.0 can use static file & dynamic configuration through labels and annotations. In our case we are going an traefik.yml file.

## Static configuration
entryPoints:
  web:
    address: ":8000"

  web-secure:
    address: ":4443"
  
  ssh:
    address: ":2222"

certificatesResolvers:
  default:
    acme:
      email: YOUREMAIL@acme.com
      storage: /data/acme.json
      dnsChallenge:
        provider: ovh
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"

providers:
  kubernetesCRD:
    namespaces:
      - "default"
      - "production"
      - "traefik"
      - "x"
      - "y"

api:
  dashboard: true

accessLog: {}


http:
  routers:
    api:
      rule: PathPrefix(`/api`) || PathPrefix(`/dashboard`)
      service: api@internal
      middlewares:
        - auth
  middlewares:
    auth:
      basicAuth:
        users:
          - "test:YYYYYYYYYYYYYYYYYYYYYYYYYYY" 
          - "test2:XXXXXXXXXXXXXXXXXXXXXXXXXX"
kubectl create configmap traefik-config-yml --from-file=./config-maps/traefik -n traefik
  1. Environment variables

To be able to reach OVH Api you need to have some api crendential in order to the Trafik Controller to reach and interact with your OVH Account.
Have a look on thoses links:

Let’s create the secret file which will have my api keys.

apiVersion: v1
kind: Secret
metadata:
  name: ovh-credentials
type: Opaque
data:
  ovh_endpoint: BASE64
  ovh_application_key: BASE64
  ovh_application_secret: BASE64
  ovh_consumer_key: BASE64

Now that we have our file let’s import it on the Rancher

kubectl create -n traefik -f traefik-ovh-secret.yml
  1. Deployment of Traefik

Check using kubectl get pods -n traefik to see how your’re pods are going on and to check the logs if any goes wrong.

I’m using the same logic as the official Traefik documentation.

apiVersion: v1
kind: Service
metadata:
  name: traefik

spec:
  ports:
    - protocol: TCP
      name: web
      port: 8000
    - protocol: TCP
      name: admin
      port: 8080
    - protocol: TCP
      name: websecure
      port: 4443
    - protocol: TCP
      name: ssh
      port: 2222
  selector:
    app: traefik

---
apiVersion: v1
kind: Service
metadata:
  name: whoami

spec:
  ports:
    - protocol: TCP
      name: web
      port: 80
  selector:
    app: whoami
    
---

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: traefik
  name: traefik-ingress-controller

---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: traefik
  labels:
    app: traefik

spec:
  replicas: 1
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      serviceAccountName: traefik-ingress-controller
      containers:
        - name: traefik
          env:
          - name: TZ
            value: 'Europe/Brussels'
          - name: OVH_ENDPOINT
            valueFrom:
              secretKeyRef:
                name: ovh-credentials
                key: ovh_endpoint
          - name: OVH_APPLICATION_KEY
            valueFrom:
              secretKeyRef:
                name: ovh-credentials
                key: ovh_application_key
          - name: OVH_APPLICATION_SECRET
            valueFrom:
              secretKeyRef:
                name: ovh-credentials
                key: ovh_application_secret
          - name: OVH_CONSUMER_KEY
            valueFrom:
              secretKeyRef:
                name: ovh-credentials
                key: ovh_consumer_key
          image: traefik:v2.0
          args:
            - --configFile=/local/traefik/traefik.yml
          ports:
            - name: web
              containerPort: 8000
              hostPort: 80
            - name: websecure
              containerPort: 4443
              hostPort: 443
              protocol: TCP
            - name: ssh
              containerPort: 2222
              hostPort: 22
              protocol: TCP
            - name: admin
              containerPort: 8080
          volumeMounts:
          - mountPath: /local/traefik
            name: traefik-config
          - mountPath: /data
            name: traefik-ssl-storage
      volumes:
      - configMap:
          defaultMode: 256
          name: traefik-config-yml
          optional: false
        name: traefik-config
      - hostPath:
          path: /mnt/system/traefik-data
          type: DirectoryOrCreate
        name: traefik-ssl-storage
              

---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: whoami
  labels:
    app: whoami

spec:
  replicas: 2
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
        - name: whoami
          image: containous/whoami
          ports:
            - name: web
              containerPort: 80

  1. IngressRoute

Since the 2.0 i didn’t find a way through the documentation of the use of acme certificate generation using classic ingress definition. I’ve been force to move my ingress to an IngressRoute which is not available through the UI of Rancher.

As the documentation say, let’s deploy an IngressRoute for the whoami container.


apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: simpleingressroute
spec:
  entryPoints:
    - web
  routes:
  - match: Host(`whoami.YOURDOMAIN.com`) && PathPrefix(`/notls`)
    kind: Rule
    services:
    - name: whoami
      port: 80

---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: ingressroutetls
spec:
  entryPoints:
    - web-secure
  routes:
  - match: Host(`whoami.YOURDOMAIN.com`) && PathPrefix(`/tls`)
    kind: Rule
    services:
    - name: whoami
      port: 80
  tls:
    certResolver: default

I can now use Traefik through IngressRoute declaration and other kind of operations available to distribute my applications :slight_smile: .

Please comment if you have any question or remarks…

RancherOS SSH Port Settings

Open a console into your rancheros server then use a file like :

rancher:
  ssh:
    port: 2022

Then use ros config merge -i cloud-ssh.yml in order to change the ssh port of your server.

Warning

This is working fine when you deploy your application on the same namespace that Traefik but i have an issue when i’m deploying apps on dedicated namespaces. I’ve open a thread on Traefik and will update this post with another example.

Bibliography

https://docs.traefik.io/
https://docs.traefik.io/user-guides/crd-acme


1 Like