One of the issues I've often seen that my team mates send "right command" to wrong cluster and context. We have a bunch of clusters and it's always surprising to see some laptop deployments on ... production cluster.
I'm lazy and I don't like having to remember "the right way" to run something, so my solution is directories and wrappers. I keep a directory for every environment (dev, stage, prod, etc) for every account I manage.
I keep config files in each directory. I call a wrapper script, cicd.sh, to run certain commands for me. When I want to deploy to stage in account-b, I just do:
~ $ cd env/account-b/stage/
~/env/account-b/stage $ cicd.sh deploy
cicd.sh: Deploying to account-b/stage ...
The script runs ../../../modules/deploy/main.sh and passes in configs from the current directory ("stage") and the previous directory ("account-b"). Those configs are hard-coded with all the correct variables. It's impossible for me to deploy the wrong thing to the wrong place, as long as I'm in the right directory.
I use this model to manage everything (infrastructure, services, builds, etc). This has saved my bacon a couple times; I might have my AWS credentials set up for one account (export AWS_PROFILE=prod) but trying to deploy nonprod, and the deploy immediately fails because the configs had hard-coded values that didn't match my environment.
Very interesting solution to the problem. Pretty much everyone has their $PS1 set to show the current working directory, because the desire to know the implicit context of our commands ($PWD) has existed since the dawn of computing. Since then, we've added a lot of commands that have an implicit context, but we haven't updated our tooling to support them. That's a big problem, but I like your solution -- make the kubernetes context depend on the working directory, which your shell already prints out for you before every command.
(If I were redoing this all from scratch, I would just have my interactive terminal show some status-information above the command after I typed "kubectl "; the context, etc. That way, you know at a glance, and you don't have to tie yourself to the filesystem. And, this could all be recorded in the history, perhaps with a versioned snapshot of the full configuration, so that when this shows up in your history 6 weeks later, you know exactly what you were doing.)
With that in mind, I do feel like the concept of an "environment" has been neglected by UI designers. I never know if I'm on production, staging, private preview, or what; either for my own software, or for other people's software. (For my own, I use "dark reader" and put staging in dark mode and production in unmodified mode. Sure confuses people when I share my screen or file bug reports, though. And, this only works if you have exactly two environments, which is fewer than I actually have. Sigh!)
Agree, this is a huge pain point when dealing with multiple clusters. I wrote a wrapper for `kubectl` that displays the current context for `apply` & `delete` and prompts me to confirm the command. It's not perfect, but it's saved me a lot of trouble already — but encouraging other members of the team to have a similar setup is another story.
My approach was to have a default kubeconfig for dev/QA environments, and a separate for production. I had a quick wrapper script to use the prod config file - it would set the KUBECONFIG env car to use the prod file, and update my PS1 to be red, a clear differentiator that reminds me I'm pointed at prod.
Not a perfect solution but I add a prompt signaling both my current namespace and cluster, along with some safeguards for any changes on our production environment. In practice I haven't deployed something wrongfully in production ever.
I use a custom written script but I've used this one in the past - its pretty nice.
I have a prompt display as well, but to my own dismay, earlier that year, I applied some QA config to a prod system. (It did not cause substantial harm, thankfully.) After that, I changed my prompt display so that names of productive regions are highlighted with red background. That seems to really help in situations of diminished attentiveness from what I can tell.
We partially resolve this by having different namespaces in each of our environments. Nothing is ever run in the 'default' namespace.
So if we think we're targeting the dev cluster and run 'kubectl -n dev-namespace delete deployment service-deployment' but our current context is actually pointing to prod then we trigger an error as there is no 'dev-namespace' in prod.
Obviously we can associate specific namespaces to contexts to traverse this safety net but it can help in some situations.
direnv is our magic sauce for this.
We enforce that all devs store the current context in an environment variable (KUBECTL_CONTEXT), and define the appropriate kubectl alias to always use that variable as the current context. To do stuff in a cluster, cd into that cluster’s directory, and direnv will automatically set the correct context. I also change prompt colors based on the current context.
(This way, the worst you can do is re-apply some yaml that should’ve already been applied in that cluster anyway)
We also have a Makefile in every directory, where the default pseudo-target is the thing you want 99% of the time anyway: kustomize build | kubectl apply -f -
This approach allows the convenience of short, context-free commands without compromising safety, because the context info in the shell prompt can be relied on, due to the isolation.
There are some things which don't work well inside a docker container (port-forwarding for example), but it does make it simple to have isolated shell history, specific kubectl versions, etc.
When I was running the internal k8s clusters at a previous workplace, I simply got into the habit of compulsively running `kubectl config current-context` to check which one of the 50+ clusters I was currently connected to (designated test clusters for *playing with cluster infra", designated clusters for "devs playing around", designated prod clusters, with segregation between "batch-like" and "interactive" workloads, as we needed to treat the nodes differently in those, designated "run the CI/CD pipelines" clusters, as they needed different RBAC, ... and then duplicate between multiple data centres).
thanks for starting that thread, context is a major hurdle for beginners.
I myself am quite happy with the basics, but have an alias on k=kubectl and set-context that without argument displays the current-context. Before doing anything I rename or edit contexts in .kube/config to have a minimal amount of characters to type for the target ("proj-prod"). Using -l name= is another help in filtering, jsonpath and jq too.. as years ago with using the cli prompts with database products, building up muscle memory also gave me opportunity to grok the concepts at the same time.
After some attempts with different tooling, I came to like kubernetes for what it can do.
I used k9s before and that's an awesome tool. Tho it doesn't help when I want to send a command to my team mate and he just executes them on wrong cluster. It's the problem I want to solve
I like the spirit of this but for dealing with multiple clusters, kubectx is pretty standard, always returns highlighting where you are and we don't have to type in the cluster name in every command. Also avoiding "kubectl delete" seems such a narrow case, I can still delete with "k scale --replicas=0" and possibly many other ways; at this point you are better of with a real RBAC implementation.
isn't kubectx the problem, not the solution? You think you are in one context but you are actually in another. You wanted to tear down the dev deployments but you nuked the production ones instead.
Nice list. Learned a couple neat things. Thank you!
Would like to add that my favorite under-appreciated can't-live-without kubectl tool is `kubectl port-forward`. So nice being able to easily open a port on localhost to any port in any container without manipulating ingress and potentially compromising security.
Something this guide misses that is helpful about explain is that it can explain down to primaries types. “K explain po” is great, but “k explain po.spec” will give more details about the spec and its fields. This dot field pattern can go as deep as needed, like pod.spec.volumes.secret.items
This command describes the fields associated with each supported API resource. Fields are identified via a simple
JSONPath identifier:
<type>.<fieldName>[.<fieldName>]
Add the --recursive flag to display all of the fields at once without descriptions. Information about each field is
retrieved from the server in OpenAPI format.
Use "kubectl api-resources" for a complete list of supported resources.
Examples:
# Get the documentation of the resource and its fields
kubectl explain pods
# Get the documentation of a specific field of a resource
kubectl explain pods.spec.containers
Options:
--api-version='': Get different explanations for particular API version (API group/version)
--recursive=false: Print the fields of fields (Currently only 1 level deep)
Usage:
kubectl explain RESOURCE [options]
Use "kubectl options" for a list of global command-line options (applies to all commands).
In case you work a lot with k8s, you can take a look as well at k9s, hightly reccomend it. It can save a lot of time with typings, especially to quickly check what pods/deployments are running, execute command in pod, describe to understand why did it fail, change cluster / namespace and so on
Along those lines, this was an interesting statement:
"You should learn how to use these commands, but they shouldn't be a regular part of your prod workflows. That will lead to a flaky system."
It seems like there's some theory vs. practice tension here. In theory, you shouldn't need to use these commands often, but in practice, you should be able to do them quickly.
How often is it the case in reality that a team of Kubernetes superheroes, well versed in these commands, is necessary to make Continuous Integration and/or Continuous Deployment work?
For the read-only commands, you can obv use them as much as you can, the issue is with the write commands. I see them as a tool for troubleshooting (eg, you are adding a debugging pod, not changing the running system) and emergency work that would be faster on command line than running the CI/CD pipeline but the final state needs to be in sync with the code (tools like ArgoCD help with this), otherwise it's a mess.
So I wrote this https://github.com/icy/gk8s#seriously-why-dont-just-use-kube... It doesn't come with any autocompletion by default, but it's a robust way to deal with multiple clusters. Hope this helps.
Edit: Fix typo err0rs
I use this model to manage everything (infrastructure, services, builds, etc). This has saved my bacon a couple times; I might have my AWS credentials set up for one account (export AWS_PROFILE=prod) but trying to deploy nonprod, and the deploy immediately fails because the configs had hard-coded values that didn't match my environment.
(If I were redoing this all from scratch, I would just have my interactive terminal show some status-information above the command after I typed "kubectl "; the context, etc. That way, you know at a glance, and you don't have to tie yourself to the filesystem. And, this could all be recorded in the history, perhaps with a versioned snapshot of the full configuration, so that when this shows up in your history 6 weeks later, you know exactly what you were doing.)
With that in mind, I do feel like the concept of an "environment" has been neglected by UI designers. I never know if I'm on production, staging, private preview, or what; either for my own software, or for other people's software. (For my own, I use "dark reader" and put staging in dark mode and production in unmodified mode. Sure confuses people when I share my screen or file bug reports, though. And, this only works if you have exactly two environments, which is fewer than I actually have. Sigh!)
Here's the script (along with a bunch of extra utils): https://github.com/pch/dotfiles/blob/master/kubernetes/utils...
For the several dozen clusters that I manage, I have separate kubeconfig files for each and I use the --kubeconfig flag.
It's explicit and I have visual feedback in the command I run for the cluster I'm running against, by short name. No stupidly long contexts.
I use a custom written script but I've used this one in the past - its pretty nice.
https://github.com/jonmosco/kube-ps1/blob/master/kube-ps1.sh
So if we think we're targeting the dev cluster and run 'kubectl -n dev-namespace delete deployment service-deployment' but our current context is actually pointing to prod then we trigger an error as there is no 'dev-namespace' in prod.
Obviously we can associate specific namespaces to contexts to traverse this safety net but it can help in some situations.
(This way, the worst you can do is re-apply some yaml that should’ve already been applied in that cluster anyway)
We also have a Makefile in every directory, where the default pseudo-target is the thing you want 99% of the time anyway: kustomize build | kubectl apply -f -
This approach allows the convenience of short, context-free commands without compromising safety, because the context info in the shell prompt can be relied on, due to the isolation.
There are some things which don't work well inside a docker container (port-forwarding for example), but it does make it simple to have isolated shell history, specific kubectl versions, etc.
I myself am quite happy with the basics, but have an alias on k=kubectl and set-context that without argument displays the current-context. Before doing anything I rename or edit contexts in .kube/config to have a minimal amount of characters to type for the target ("proj-prod"). Using -l name= is another help in filtering, jsonpath and jq too.. as years ago with using the cli prompts with database products, building up muscle memory also gave me opportunity to grok the concepts at the same time.
After some attempts with different tooling, I came to like kubernetes for what it can do.
e.g.,
(and comment out any liveness or readiness probes)Very useful to then `exec` with a shell in the pod debug things or test out different configs quickly, check the environment etc.
Deleted Comment
Would like to add that my favorite under-appreciated can't-live-without kubectl tool is `kubectl port-forward`. So nice being able to easily open a port on localhost to any port in any container without manipulating ingress and potentially compromising security.
Deleted Comment
List the fields for supported resources
This command describes the fields associated with each supported API resource. Fields are identified via a simple JSONPath identifier:
retrieved from the server in OpenAPI format.Use "kubectl api-resources" for a complete list of supported resources.
Examples: # Get the documentation of the resource and its fields kubectl explain pods
Options: --api-version='': Get different explanations for particular API version (API group/version) --recursive=false: Print the fields of fields (Currently only 1 level deep)Usage: kubectl explain RESOURCE [options]
Use "kubectl options" for a list of global command-line options (applies to all commands).
$
I don't think it validates the Kubernetes resources.
Here's an example:
$ helm create foo
$ cd foo
Then change "apiVersion" in deployment.yaml to "apiVersion: nonsense"
In the linting, I got
$ helm lint ==> Linting . [INFO] Chart.yaml: icon is recommended
1 chart(s) linted, 0 chart(s) failed
$ helm template . | kubeval -
ERR - foo/templates/deployment.yaml: Failed initializing schema https://kubernetesjsonschema.dev/master-standalone/deploymen...: Could not read schema from HTTP, response status is 404 Not Found
kubectl rollout undo deployment <deployment-name>
"You should learn how to use these commands, but they shouldn't be a regular part of your prod workflows. That will lead to a flaky system."
It seems like there's some theory vs. practice tension here. In theory, you shouldn't need to use these commands often, but in practice, you should be able to do them quickly.
How often is it the case in reality that a team of Kubernetes superheroes, well versed in these commands, is necessary to make Continuous Integration and/or Continuous Deployment work?