Kubesafe: Never run Kubernetes commands on the wrong cluster again
github.com
github.com
- Each environment gets its own directory. We use kustomize to share config between environments.
- direnv “sets” the current context when you cd under a cluster’s directory (it sets an environment variable that a kubectl alias uses. Nobody calls kubectl directly; it wouldn’t work because we’ve banned the current context from the yaml files). You switch clusters by changing to that cluster’s directory.
- most of the time, the only command you run is ‘make’ which just does kubectl kustomize apply (or whatever). 100% of cluster config is checked into git (with git-crypt for secrets), so the worst that can happen is that you reapply something that’s already there.
I’ve also colored the command prompt according to the current cluster.
But anyway it’s essentially impossible to apply a config change to the wrong cluster. I haven’t worried about this in years.
Active one is in $PS1 somewhere.
If I’m doing something more involved, I’ve got a k9s window open in another pane, making sure the command is having the intended effect.
I guess the riskiest commands would be things like deleting persistent volumes. But our storage class doesn’t automatically clean up the disk in the cloud provider, so we could recover from that too.
In practice, there are always slight differences between cluster config between test and prod (using different S3 buckets, for example) so this is needed anyway.
I also tried to avoid current context initially, but it just slowed me down. Switching between clusters is so much easier with the current context and kubectx.
That’s why I built kubesafe. In this way I can keep using the current context without worrying about screwing up. If I accidentally target the wrong context, at least I get a warning before executing the command.
The only hassle now is remembering to add new prod contexts to the safe list, but that’s about to change with regex support coming soon :)
k8x() {
export env=$1;
# exit if no param
if [ -z $1 ]; then
if [ -z ${KUBECONFIG+x} ]; then
echo "Need param of a k8s environment";
return 1
else
echo "Removing KUBECONFIG variable";
PS1="$(echo "$PS1" | sed -e 's;^([^)]*) ;;')";
unset KUBECONFIG;
return 0
fi
fi;
# exit if no file for param
cfgPath="$HOME/.kube/config.${env}";
if [ ! -f $cfgPath ]; then
echo "A config does not exist";
return 1
fi;
PS1="$(echo "$PS1" | sed -e 's;^([^)]*) ;;' -e 's;^;('$env') ;')";
export KUBECONFIG="$cfgPath";
}I've worked in lots of places since and the world's matured from scripts and rsync to ansible and puppet and similar.
Have we regressed to the point where we've turned big clusters of systems back into "oops I ran a command as superuser in the wrong directory" ?
#!/usr/bin/env bash
set -e
context=$(kubectl config get-contexts | awk '{print $2;}' | grep -v NAME | fzf --preview 'kubectl config use-context {} && kubectl get namespaces')
kubectl config use-context $context
You get a two-pane window with the context on the left and the namespaces on the right. That's all I need to find what I'm looking at. It's destructive, though.Would highly recommend kubie, it allows you to switch and shows you the name of the cluster in the prompt. It's probably a more visual way of solving the same problem.
Instead I went with kubeswitch which still gives you a different kubeconfig per terminal but allows you to re-use existing sessions.
With kubie entering a context copies the configuration to a new file and sets KUBECONFIG appropriately, so it is not affected by changes in another shell.
It's still possible I could mess something up with kubectl, but it provides constant reminders of what I'm working with.
i added the following to my bashrc a few days ago for similar reasons; this forces me to be explicit about the cluster; now i mess up the wrong namespace instead :)
if [[ -e "/opt/homebrew/bin/kubectl" ]]; then
/opt/homebrew/bin/kubectl config unset current-context >/dev/null
ficontext=$(grep "current-context:" ~/.kube/config | grep "*prod*")
if [[ -z ${context} ]]
then # do the command
else # do a y/n prompt
fi
Am I missing something?
I developed kubesafe because (1) I was tired of tinkering with shell aliases and scripts (especially when I wanted to define protected commands) and (2) I needed something that worked smoothly with all Kubernetes tools like kubectl, helm, kubecolor, etc.
Kubesafe is just a convenient way to manage protected commands and contexts. Nothing too fancy!
Btw - I also found a kubectl plugin written in Bash that’s similar to what you mentioned, in case you're interested: https://github.com/jordanwilson230/kubectl-plugins/blob/krew...
Incidentally: I have no idea why something like kubecolor isn't built in to kubectl itself.
Example with kubecolor:
`kubesafe kubecolor get pods --all-namespaces`
Kubesafe is just an extra safety net to prevent those kind of accidents :)