<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>key-value store on Digi Hunch</title><link>https://static.digihunch.com/tag/key-value-store/</link><description>Recent content in key-value store on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Wed, 02 Apr 2025 14:06:01 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/key-value-store/index.xml" rel="self" type="application/rss+xml"/><item><title>Etcd – the key-value store for Kubernetes</title><link>https://static.digihunch.com/2022/06/etcd-the-key-value-store-for-kubernetes/</link><pubDate>Tue, 14 Jun 2022 00:10:00 -0400</pubDate><guid>https://static.digihunch.com/2022/06/etcd-the-key-value-store-for-kubernetes/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-etcd.webp" alt="Featured image of post Etcd – the key-value store for Kubernetes" /&gt;&lt;h2 class="wp-block-heading"&gt;Etcd in Kubernetes&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Kubernetes &lt;a href="https://static.digihunch.com/2021/04/preparing-certified-kubernetes-administrator-exam/"&gt;architecture&lt;/a&gt;, &lt;a href="https://etcd.io/"&gt;etcd&lt;/a&gt; is the data store. It stores the desired state of Kubernetes object. API server is the only client that connects to etcd (via &lt;a href="https://grpc.io/"&gt;gRPC&lt;/a&gt; protocol). Cluster builder specifies the endpoint of etcd as a parameter to the kube-api-server process. Other Kubernetes components, whether in the control plane or from the nodes, connect to API server. API server translates their request into etcd query, and then translates etcd query result into what its clients ask for. For this reason, communication with etcd accounts for a lot of network traffic in a Kubernetes cluster.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The etcd store is a CNCF project for &amp;#8220;a distributed, reliable key-value store for critical data in a distributed system&amp;#8221;, developed by CoreOS team. So it is essentially a distributed key-value store for any distributed application. If an application runs on Kubernetes, it can leverage etcd store, by keeping their configurations in ConfigMap and Secret objects. One key feature is to watch for specific keys or directories for changes, and react to the changes. Voila! This is the underlying mechanism for &lt;a href="https://kubernetes.io/docs/concepts/architecture/controller/"&gt;controller&lt;/a&gt;!&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A Kubernetes cluster may have stacked etcd deployment or connect to an external etcd store.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="848" height="560" src="https://static.digihunch.com/wp-content/uploads/2022/05/stacked.png" alt="" class="wp-image-5250"/&gt;&lt;figcaption class="wp-element-caption"&gt;stacked etcd architecture&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="856" height="601" src="https://static.digihunch.com/wp-content/uploads/2022/05/external.png" alt="" class="wp-image-5251"/&gt;&lt;figcaption class="wp-element-caption"&gt;external etcd architecture&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In managed Kubernetes services such as EKS in AWS and AKS in Azure, users usually do not directly access etcd store. However, it is still a very important component to understand. Its use case includes:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Configuration sharing&lt;/li&gt;&#10;&lt;li&gt;Service discovery&lt;/li&gt;&#10;&lt;li&gt;Consistency&lt;/li&gt;&#10;&lt;li&gt;Watching mechanism&lt;/li&gt;&#10;&lt;li&gt;Expiry and extension of key &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The consistency use case is based on Raft protocol for distributed consensus.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Raft protocol&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I am not an expert in distributed consensus protocols and nor do I intent to cover it in depth. At a high level, I have heard of three of them so far:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Etcd uses Raft protocol&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://static.digihunch.com/2020/08/zookeeper/"&gt;Zookeeper&lt;/a&gt; uses ZAB protocol&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://static.digihunch.com/2018/03/cassandra-architecture-summary/"&gt;Cassandra&lt;/a&gt; uses paxos protocol&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.alibabacloud.com/blog/a-brief-analysis-of-consensus-protocol-from-logical-clock-to-raft_594675"&gt;Here&lt;/a&gt; is a good intro to the three protocols. Instead of getting into the fine details, I would like to discuss why we need such a consensus protocol (or consensus mechanism) in distributed systems, which are also decentralized systems.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="686" src="https://static.digihunch.com/wp-content/uploads/2025/04/etcd-topology-1024x686.webp" alt="" class="wp-image-13111" srcset="https://static.digihunch.com/wp-content/uploads/2025/04/etcd-topology-1024x686.webp 1024w, https://static.digihunch.com/wp-content/uploads/2025/04/etcd-topology-300x201.webp 300w, https://static.digihunch.com/wp-content/uploads/2025/04/etcd-topology-768x514.webp 768w, https://static.digihunch.com/wp-content/uploads/2025/04/etcd-topology-410x275.webp 410w, https://static.digihunch.com/wp-content/uploads/2025/04/etcd-topology.webp 1138w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Centralized, Decentralized, Distributed systems&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The reason a distributed system needs consensus protocol, is that a distributed system lacks a single source of truth as centralized systems do. Different parts of the distributed system may receive different signals but they must come to agreement of a single plan to act. Lamport studies this with an analogy of &lt;a href="https://en.wikipedia.org/wiki/Byzantine_fault"&gt;Byzantine Generals&lt;/a&gt; problem, and first proposed Paxos protocol. &lt;a href="https://en.wikipedia.org/wiki/Paxos_(computer_science)"&gt;Paxos&lt;/a&gt; has been an important foundation to modern distributed systems. In Paxos, consensus is achieved in &lt;a href="https://martinfowler.com/articles/patterns-of-distributed-systems/paxos.html"&gt;two phases&lt;/a&gt;, which creates the problem of livelocks. Raft is an alternative to Paxos, and is widely adopted today. &lt;a href="http://thesecretlivesofdata.com/raft/"&gt;Here&lt;/a&gt; is a link to an animated illustration for Raft protocol. The Raft protocol is also used in Redis. It has three roles: Leader, Candidate, and follower. ZAB protocol is similar to Raft, where it needs to select a leader.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Etcd Lab&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In troubleshooting, if we suspect that the response from API server is inconsistent with etcd store, we want to directly connect to it.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Managed Kubernetes services do not expose their etcd store. We can use KinD or Minikube. There are two types of jump box to access etcd store: using etcd Pod, or SSH to a Node. To connect to etcd, we also need the X509 key, certificate and CA&amp;#8217;s certificate, in addition to the endpoint, usually an IP with port 2389. When I connect to Pod shell, I find the command shell not easy to use. They might miss basic command such as ls, or do not support auto completion.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Take KinD for example, we first create a secret, then we can connect to the node with docker CLI command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl create ns myns&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n myns create secret generic mysecret --from-literal key1&lt;span style="color:#f92672"&gt;=&lt;/span&gt;value1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n myns get secret mysecret -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.data.key1}&amp;#39;&lt;/span&gt; | base64 -d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker exec -it control /bin/bash&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;From the node, &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;apt update &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt install etcd-client&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl version&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nc -vz localhost &lt;span style="color:#ae81ff"&gt;2379&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep etcd&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export ETCDCTL_API&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export ETCDCTL_CERT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/kubernetes/pki/apiserver-etcd-client.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export ETCDCTL_KEY&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/kubernetes/pki/apiserver-etcd-client.key&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export ETCDCTL_CACERT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/ca.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export ETCDCTL_ENDPOINTS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;https://127.0.0.1:2379&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl member list write out&lt;span style="color:#f92672"&gt;=&lt;/span&gt;table&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now we can see the secret object directly with etcd store:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl get /registry/secrets/myns/mysecret&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;With get query, when using &amp;#8211;prefix, we can use &amp;#8211;keys-only switch to list keys without values:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl get --prefix /registry/api --keys-only&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl get --prefix /registry/namespace -wjson&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We can write key-value with put command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl put myloc &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl get myloc -wjson&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In Kubernetes, all the key names start with / which makes the key looks like a POSIX path. Every Kubernetes object is stored in etcd with a unique key following a self-explanatory naming pattern. To display the path, we can also use debug log that records the call to API server:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get ns myns -v9&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Look for curl command such as:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;I0523 22:51:43.517728 32347 round_trippers.go:466] curl -v -XGET -H &amp;#34;Accept: application/json;as=Table;v=v1;g=meta.k8s.io,application/json;as=Table;v=v1beta1;g=meta.k8s.io,application/json&amp;#34; -H &amp;#34;User-Agent: kubectl/v1.23.6 (darwin/amd64) kubernetes/ad33385&amp;#34; &amp;#39;https://127.0.0.1:64081/api/v1/namespaces/myns&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;From there we can see the etcd query as the URI is namespaces/myns, which we use in etcdctl query path:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl get /registry/namespaces/myns&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Every type of Kubernetes object has a storage.go file in their implementation that defines how api server should write object. &lt;a href="https://github.com/kubernetes/kubernetes/blob/master/pkg/registry/core/pod/storage/storage.go"&gt;Here&lt;/a&gt; is an example for Pod object.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Etcd also supports watch command to watch for changes. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl watch --prefix /registry/namespace &lt;span style="color:#75715e"&gt;# watch output k create ns newns&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now we create a namespace with kubectl:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl create ns myns&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The output from etcdctl will reflect the change. The communication between etcdctl and etcd is gRPC protocol. The output is based on stream, as we can see from the watch result.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-etcd-maintenance"&gt;Etcd Maintenance&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Like any distributed store, etcd needs &lt;a href="https://etcd.io/docs/v3.5/op-guide/maintenance/"&gt;maintenance&lt;/a&gt; and operation work. For example, we can check endpoint status with endpoint command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl endpoint status&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We can also backup and restore etcd store with etcdctl command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;etcdctl snapshot save /tmp/backup.db&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This was an question in &lt;a href="https://static.digihunch.com/2021/04/preparing-certified-kubernetes-administrator-exam/"&gt;CKA exam&lt;/a&gt;. In real life, when the workload scales up, the etcd store may come across many pitfalls, such as degraded performance, unresponsiveness, some etcd member going down, network partition on etcd store causing split brain. It is important to ensure efficient communication between API server and etcd store. The etcdctl provides defrag and compact commands for common maintenance activities.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/05/hosting-database-on-kubernetes/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Hosting database on Kubernetes&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/06/chaos-mesh-cloud-native-chaos-engineering/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Chaos Mesh – Cloud Native Chaos Engineering&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>