Setting up Opendesk on Kubernetes

Since i had little prior experience with K8S, this document serves as a sort of self-documentation of setting up opendesk, a sovereign office suite. While it might be nice to use ArgoCD for such a setup in production, I have no experience with that *yet*.

This is not a full tutorial. Just a reference for your own install and it may have some things out of order!

Ensure you have enough CPU cores and RAM or opendesk will fail to install

First of all, you want to set up DNS:

I am just going to link to the opendesk guys here:
https://gitlab.opencode.de/bmi/opendesk/deployment/opendesk/-/blob/develop/docs/getting-started.md#dns

When setting up opendesk on a second level domain, I ran into issues with ruby limits and K8S’s ndots settings: https://gitlab.opencode.de/bmi/opendesk/deployment/opendesk/-/issues/252
If you still run into this issue you may want to use https://github.com/maxlaverse/ndots-admission-controller. This is more performant anyways.

Kubernetes using KubeAdm

I chose kubeadm as it seems to be a bit more production-oriented than minikube. Interestingly, i found the cri-o site to be the best source for information regarding K8S installation. Cri-o is a container runtime for Kubernetes similar to containerd. I will be using ubuntu 24.04 as the base.

KUBERNETES_VERSION=v1.34
CRIO_VERSION=v1.34

apt-get update
apt-get install -y software-properties-common curl unzip

curl -fsSL https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/deb/Release.key |
    gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/deb/ /" |
    tee /etc/apt/sources.list.d/kubernetes.list

curl -fsSL https://download.opensuse.org/repositories/isv:/cri-o:/stable:/$CRIO_VERSION/deb/Release.key |
    gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg

echo "deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://download.opensuse.org/repositories/isv:/cri-o:/stable:/$CRIO_VERSION/deb/ /" |
    tee /etc/apt/sources.list.d/cri-o.list

apt-get update
apt-get install -y cri-o kubelet kubeadm kubectl

systemctl start crio.service

swapoff -a
modprobe br_netfilter
echo 'br_netfilter' > /etc/modules-load.d/br_netfilter.conf
sysctl -w net.ipv4.ip_forward=1

kubeadm init --pod-network-cidr=10.244.0.0/16
kubectl get pods --all-namespaces
# Setup kubectl
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
sudo sysctl -w fs.inotify.max_user_watches=2099999999
sudo sysctl -w fs.inotify.max_user_instances=2099999999
sudo sysctl -w fs.inotify.max_queued_events=2099999999

You might get very close to the pod limit which gives very cryptic errors (110), so up it:

#nano /var/lib/kubelet/config.yaml
maxPods: 1024
sudo systemctl restart kubelet

Since i’m running a single node cluster for now, I need to tell k8s that it can use the current machine:

kubectl taint nodes --all node-role.kubernetes.io/control-plane-
kubectl label nodes --all node.kubernetes.io/exclude-from-external-load-balancers-

Cri-o since kube 1.34 does some stupid name deduplication that breaks longhorn. Disable it by creating a /etc/crio/crio.conf.d/20-shortname.conf

#/etc/crio/crio.conf.d/20-shortname.conf
[crio.image]
short_name_mode = "disabled"

service crio restart

Since the normal kubectl CLI get quite limiting, I like to use the k9s TUI for easier cluster management:

wget https://github.com/derailed/k9s/releases/download/v0.50.16/k9s_linux_amd64.deb
dpkg -i k9s_linux_amd64.deb

Kubernetes needs a Container Network Interface (CNI) for networking. I chose to use Flannel just because it is a popular choice:

kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml

You also want to install Helm, Helmdiff, Helmfile to manage opendesk:

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm plugin install https://github.com/databus23/helm-diff
wget https://github.com/helmfile/helmfile/releases/download/v1.1.3/helmfile_1.1.3_linux_amd64.tar.gz
tar -zxvf helmfile_1.1.3_linux_amd64.tar.gz
mv helmfile /usr/local/bin

We install ingress-nginx, which i believe is going out of support but is the only currently supported ingress controller for opendesk

# This may be outdated. Take care!
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install quickstart ingress-nginx/ingress-nginx --set controller.config.annotations-risk-level=Critical   --set controller.config.strict-validate-path-type=false --set controller.allowSnippetAnnotations=true
 --set controller.admissionWebhooks.allowSnippetAnnotations=true 
 # -f values.yaml
# You may or may not need this values.yaml:
controller:
#  service:
#    type: "NodePort"
  hostPort:
    enabled: true
  service:
    type: "ClusterIP"
  config:
    annotations-risk-level: "Critical"
    strict-validate-path-type: "false"
  allowSnippetAnnotations: true
  admissionWebhooks:
    allowSnippetAnnotations: true

Storage

The local path provisioner does not work for opendesk as it needs the sticky bit. Use Longhorn!

helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn --namespace longhorn-system --create-namespace --version 1.10.0
USER=luc; PASSWORD=<snip>; echo "${USER}:$(openssl passwd -stdin -apr1 <<< ${PASSWORD})" >> auth
kubectl -n longhorn-system create secret generic basic-auth --from-file=auth
nano longhorn-ingress.yml # See below
kubectl -n longhorn-system apply -f longhorn-ingress.yml
kubectl -n longhorn-system get ingress
# longhorn-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: longhorn-ingress
  namespace: longhorn-system
  annotations:
    # type of authentication
    nginx.ingress.kubernetes.io/auth-type: basic
    # prevent the controller from redirecting (308) to HTTPS
    nginx.ingress.kubernetes.io/ssl-redirect: 'false'
    # name of the secret that contains the user/password definitions
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    # message to display with an appropriate context why the authentication is required
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required '
    # custom max body size for file uploading like backing image uploading
    nginx.ingress.kubernetes.io/proxy-body-size: 10000m
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: longhorn-frontend
            port:
              number: 80

Now go to the longhorn web UI to check! Under settings, set the default replica count to 1 and minimum number of backingimage copies to 1 aswell if running single-node. You might have to do this after the opendesk install in the UI aswell since it doesnt seem to follow the defaults.

Certificates

Use cert-manager with Nginx

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.19.1/cert-manager.yaml
kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/production-issuer.yaml # Change Issuer to ClusterIssuer and remove the namespace

Loadbalancer

I chose metalLB, though this has caused some issues. Can’t flannel help here?

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.15.2/config/manifests/metallb-native.yaml
kubectl apply -f pool.yml # you need the file below
kubectl apply -f adv.yml # See below
# pool.yml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 45.136.141.166-45.136.141.168
  - 45.136.141.154-45.136.141.156 # I don't think you need this many IPs
#adv.yml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system

Install Opendesk

# Update with the newest version
wget https://gitlab.opencode.de/bmi/opendesk/deployment/opendesk/-/archive/v1.10.0/opendesk-v1.10.0.zip
unzip opendesk-v1.10.0.zip
kubectl create namespace opendesk
kubectl config set-context --current --namespace opendesk

Now, configure and start!

First of all, have you setup all required DNS settings?

Then, configure the domain:

root@Kubernetes:~/opendesk-v1.10.0# cat helmfile/environments/dev/global.yaml.gotmpl
global:
  domain: "rabevcqhguoovcu.xyz"
export MASTER_PASSWORD="<snip>"
root@Kubernetes:~/opendesk-v1.10.0# helmfile apply -e dev -n opendesk

https://gitlab.opencode.de/bmi/opendesk/deployment/opendesk/-/blob/develop/docs/getting-started.md

Bash history

Since it may help in debugging, my bash history can be found here: https://gist.github.com/Wqrld/8b9a1b6569f215d2807764f1e717aa53

Warning: It is a mess and i had no clue what i was doing. Don’t read too much into it.

Negotiation 101

This post intends to be a quick introduction to some negotiation basics. It was mostly written for myself, but may be of use to others

Most importantly, negotiation is often not just about getting something for the lowest price. You will have the most success if you are able to find value for both parties.

1. Agenda

In order to get what you want, you either want to be the one leading the negotiation, or at least an equal. This does absolutely not mean that you should talk too much!

You should try to match the counterparty’s people count. If they come with two then you come with two. There should be at least one person taking notes. Some people prefer to have two sets of notes: one general to be sent to the counterparty and one private.

If viable in your situation, prepare a short agenda:
– Opening (who, what)
– Getting everyone up to date if necessary
– Negotiation
– Closing, hand shaking

2. Preparation

Preparation is 80% of the negotiation.

It is EXTREMELY important to have as much information as possible about the thing you are negotiating, and who your opponent is. This is triple as important when you have multiple people on your ‘side’, as disagreement during the meeting can be fatal. Some questions that may help:

What do i want from this negotiation:
What exactly do I want

How do my interests rank in importance:
Very important. Many people have the tendency to want either a lot of things, or too little. Have some interests, but it may be worth to have some interests that you would easily give away. Be very clear on this ranking with your team.

What are my strengths?
What can i offer that others can’t. Why does the other person need me specifically.

What are my weaknesses
What can be used against me. Where am i less good of a choice than others, what am i missing

What do i have that the other party needs
If not, don’t worry yet if you are able to realize it on a short term. If you have no chance of offering what the other party needs, do not lead them on and ruin your reputation. Keep in mind that what they say they want and what they need may differ.

What is each party’s BATNA
Best Alternative To a Negotiated Agreement. If you can’t reach a conclusion, what backup do you have?. This is a huge factor in how hard you can push and what terms you can set.
You should not be scared to walk away from a negotiation if you get a bad offer. A bad deal will hurt you more in the long run. You may be able to get called back after walking away once, but don’t expect this to happen.

Do not reveal your batna right from the start, but for example when you see that you have reached an impasse. BATNA can also be a great form of leverage. Do not use it badly, but instead frame it as helping find a good outcome. DO NOT BLUFF, this will almost always end up hurting you. Position it as an oppertunity for a win-win outcome.

Stay flexible and open to negotiation. Be willing to slightly adjust. Prepare for counteroffers or to explain/explore the gap. Evaluate the response to your BATNA

How high do i want to anchor
Anchoring is a magic trick that any good negotiator will use. Keep in mind the counterparty will also likely use this against you. Anchoring means taking the Highest price the counterparty would likely accept, and add 30% to that. Humans have the tendency to make things relative, and now you pushed the average in your negotiation discussion up to a more preferred range for you.

In what way can i make a good deal for both parties? (increase the pie)
You usually have more to negotiate on than just money. What can you do to offer more value to the other party? You can often increase the value of a deal by more than it’s monetary cost.

What’s your ZOPA
Zone of possible agreement. Both parties have a rough minimum and maximum price range. The ZOPA is the range where this overlaps. ZOPA’s can move a bit depending on what additional value is offered, but be aware that you shouldn’t move it too much for it to become a bad deal. While you may give rough hints about your ZOPA when you are not moving in your negotiation, you should never give clear limits.

You might also see the term Reservation Point, which is the point in the negotiation where a deal is no longer profitable to you.

What’s your information
It’s very important to have as much information as possible: What are we talking about, how’s the market. If your counterparty sees you do not know what you are talking about, this will cost you dearly. Make sure that information is equal between team members.

Who are you up against
Hierarchy of goals. The person who you are talking to may have different KPI’s than the company, or than the final person deciding.

Recognize if your countertparty is a hard negotiator. You can usually already see this from their title and years of experience, but be ready to adjust this during the negotiation.

3. During

If the counterparty comes to your ‘home base’, see if you can have some smalltalk beforehand. Offer them a coffee. Reciprocity is persuasion 101.

Some tips: Watch your and the counterparty’s emotions. Become very aware of nonverbal communication of your counterparty, but also your own. Do not fiddle, sit upright take active notes if possible.

Prefer in-person meetings over online, as a lot of nonverbal information gets lost.

Look dependable. Humans hate one thing the most: risk. If you seem like a person they can depend on, you already take away half of their problems.

Ask questions! Every question makes your counterparty give away more information that you can use against them or to create more value!

You are *hopefully* well prepared. Make the best of it!

4. Ending

Independent of if you come to an agreement, stay friendly. You may have to talk to this counterparty again, and a friendly party will get way better deals in the future.

Importantly: talk about next steps and rough timeframes. This will make a clear path forward to round off the deal.

5. After

Todo: Finish this post

Send the counterparty notes, plan a new meeting to look at progress. Look proactive, friendly. You will hopefully deal with them more often!

Want to know more? https://www.karrass.com/blog/batna looks like quite an interesting read.

Some interesting books are:
– Getting to Yes: Negotiating Agreement without Giving in
– The psychology of persuasion