Skip to content

Commit afa12ac

Browse files
authored
First Blog Posts (#391)
* Add new blog section to website * Add a blog on simple HA Signed-off-by: Derek Nola <derek.nola@suse.com>
1 parent 07a6ed1 commit afa12ac

File tree

10 files changed

+266
-52
lines changed

10 files changed

+266
-52
lines changed

blog/2025-03-09-hello-blog.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
title: "Hello Blog"
3+
description: This is the first blog post on k3s.io
4+
authors: dereknola # Fill out your info in the authors.yml file
5+
# tags: ["example-tag"]
6+
---
7+
8+
9+
This is the first post on blog.k3s.io
10+
11+
<!-- everything above is seen in the snippet on the main blog page. The truncate tag below hides the rest -->
12+
13+
<!-- truncate -->
14+
15+
We will explore aspects of K3s, Kubernetes, and other related topics. These long form posts will be written by the K3s team and help illuminate aspects of the project that are not easily covered in the documentation.
16+
17+
Stay tuned for more posts in the future.

blog/2025-03-10-simple-ha.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
---
2+
title: "The Basic HA Cluster"
3+
description: Creating the simplest High Availability cluster with LB and upgrading
4+
authors: dereknola # Fill out your info in the authors.yml file
5+
hide_table_of_contents: true
6+
---
7+
8+
While we have more [detailed docs](/datastore/ha-embedded/) on setting up a High Availability (HA) cluster, this post will cover the simplest HA cluster you can create.
9+
10+
<!-- truncate -->
11+
## Baseline HA Cluster 💻🖥️💻
12+
13+
Whenever we get a question around HA, this is the cluster configuration I start with. It provides a solid foundation when deploying beyond a single server.
14+
15+
Our cluster will have:
16+
- 4 nodes or VMs:
17+
- 1 load balancer
18+
- 3 servers
19+
- A k3s-upgrade plan that will automatically update the cluster to the latest patch version of a given minor.
20+
21+
## Cluster Setup 🌐🔧
22+
23+
I'm using `vagrant` to provision 4 Ubuntu 24.04 VMs for this setup, all on a flat network. Setup of nodes is left as an exercise for the reader 😅.
24+
25+
My nodes are configured with the following names and IPs:
26+
| Name | IP |
27+
|------|----|
28+
| lb-0 | 10.10.10.100 |
29+
| server-0 | 10.10.10.50 |
30+
| server-1 | 10.10.10.51 |
31+
| server-2 | 10.10.10.52 |
32+
33+
### Load Balancer
34+
35+
I'm using [haproxy](https://www.haproxy.org/) as it supports later expansion to multiple LB nodes (via keepalived).
36+
37+
SSH into the load balancer and install haproxy:
38+
39+
```bash
40+
sudo apt install haproxy
41+
```
42+
43+
The haproxy config is simple, just forward traffic to the servers:
44+
45+
```
46+
#/etc/haproxy/haproxy.cfg
47+
frontend k3s
48+
bind *:6443
49+
mode tcp
50+
default_backend k3s
51+
52+
backend k3s
53+
mode tcp
54+
option tcp-check
55+
balance roundrobin
56+
server server-0 10.10.10.50:6443 check
57+
server server-1 10.10.10.51:6443 check
58+
server server-2 10.10.10.52:6443 check
59+
```
60+
61+
Restart haproxy to apply the config:
62+
63+
```bash
64+
systemctl restart haproxy
65+
```
66+
67+
### Install K3s on first server
68+
69+
On the first server, install K3s with embedded etcd and a known token:
70+
71+
```bash
72+
curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=v1.31 sh -s - \
73+
--cluster-init --token k3sblog --tls-san 10.10.10.100
74+
```
75+
76+
We pass the `--tls-san` flag adds the load balancer IP as a Subject Alternative Name (SAN) for the certificate.
77+
78+
### Join the other servers
79+
80+
On the other servers, join the cluster via the load balancer:
81+
82+
```bash
83+
curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=v1.31 sh -s - \
84+
--server https://10.10.10.100:6443 --token k3sblog
85+
```
86+
87+
### Grab the kubeconfig
88+
89+
Now that the cluster is up, we can grab the kubeconfig from the first server:
90+
91+
```bash
92+
scp server-0:/etc/rancher/k3s/k3s.yaml k3s.yaml
93+
```
94+
95+
Modify it to access the cluster via the load balancer:
96+
97+
```bash
98+
sed -i 's/127.0.0.1/10.10.10.100/' k3s.yaml
99+
```
100+
101+
No we can manage the cluster from our local machine:
102+
103+
```bash
104+
export KUBECONFIG=$(pwd)/k3s.yaml
105+
kubectl get nodes
106+
```
107+
108+
## Upgrade Plan 🏗️📝📐
109+
110+
The plan I'm using will keep k3s updated to the latest patch version of the channel we give. In this case I'm using the `v1.31` channel, the same channel used above. Kubernetes v1.31.4 just released at time of writing this post, so with this plan we have stable upgrades handled for the next 10-12 months (depending on how many patch releases this minor gets).
111+
112+
### Install the system-upgrade-controller
113+
114+
The upgrade plan is managed by the system-upgrade-controller. Install it:
115+
116+
```bash
117+
kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/latest/download/system-upgrade-controller.yaml
118+
kubectl apply -f https://github.com/rancher/system-upgrade-controller/releases/latest/download/crd.yaml
119+
```
120+
121+
### Create the upgrade plan
122+
```yaml
123+
#server-plan.yaml
124+
apiVersion: upgrade.cattle.io/v1
125+
kind: Plan
126+
metadata:
127+
name: server-plan
128+
namespace: system-upgrade
129+
spec:
130+
concurrency: 1
131+
cordon: true
132+
nodeSelector:
133+
matchExpressions:
134+
- key: node-role.kubernetes.io/control-plane
135+
operator: In
136+
values:
137+
- "true"
138+
serviceAccountName: system-upgrade
139+
upgrade:
140+
image: rancher/k3s-upgrade
141+
channel: https://update.k3s.io/v1-release/channels/v1.31
142+
```
143+
144+
```bash
145+
kubectl apply -f server-plan.yaml
146+
```
147+
148+
See the [automated upgrade docs](/upgrades/automated) for more details.
149+
150+
151+
## Conclusion 🚀
152+
153+
![kubectl summary](kubectl.png)
154+
155+
We now have a high-availability cluster, accessible via a single IP. Upgrades are handled for the next year. This is a great starting point to:
156+
- Add agent nodes to expand our workload capacity
157+
- Add another load-balancer for additional redundancy

blog/authors.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# No names, just github icons and links
2+
dereknola:
3+
title: K3s maintainer
4+
image_url: https://github.com/dereknola.png
5+
url: https://github.com/dereknola
6+
name: Derek Nola

blog/kubectl.png

124 KB
Loading

docusaurus.config.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module.exports = {
2323
docsRouteBasePath: "/",
2424
hashed: true,
2525
highlightSearchTermsOnTargetPage: true,
26-
indexBlog: false,
26+
indexBlog: true,
2727
ignoreFiles: [/release-notes\/.*/],
2828
}),
2929
],
@@ -73,11 +73,17 @@ module.exports = {
7373
type: "localeDropdown",
7474
position: "right",
7575
},
76+
{
77+
to: 'blog',
78+
label: 'Blog',
79+
position: 'right',
80+
className: 'navbar__icon navbar__blog',
81+
},
7682
{
7783
to: 'https://github.com/k3s-io/k3s/',
7884
label: 'GitHub',
7985
position: 'right',
80-
className: 'navbar__github btn',
86+
className: 'navbar__icon navbar__github',
8187
},
8288
],
8389
},
@@ -105,7 +111,12 @@ module.exports = {
105111
showLastUpdateTime: true,
106112
editUrl: 'https://github.com/k3s-io/docs/edit/main/',
107113
},
108-
blog: false, // Optional: disable the blog plugin
114+
blog: {
115+
blogTitle: 'k3s blog',
116+
showReadingTime: false,
117+
blogSidebarTitle: 'All our posts',
118+
blogSidebarCount: 'ALL',
119+
},
109120
// ...
110121
theme: {
111122
customCss: [require.resolve("./src/css/custom.css")],

src/css/custom.css

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* bundles Infima by default. Infima is a CSS framework designed to
55
* work well for content-centric websites.
66
*/
7+
@import "icon_dropdown.css";
78
/* Import fonts. */
89

910
/* poppins */
@@ -102,58 +103,10 @@ hr {
102103
height: 40px;
103104
}
104105

105-
.btn.navbar__github {
106-
font-weight: 400;
107-
color: #fff!important;
108-
text-align: center;
109-
user-select: none;
110-
background-color: #384745;
111-
border: 2px solid transparent;
112-
padding: 8px 20px 7px 47px;
113-
font-size: 1rem;
114-
line-height: 1.66;
115-
border-radius: 3px;
116-
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
117-
border-color: #384745;
118-
box-shadow: inset 0 1px rgb(255 255 255 / 15%), 0 1px 1px rgb(0 0 0 / 8%);
119-
position: relative;
120-
font-family: poppins,sans-serif;
121-
text-decoration: none;
122-
}
123-
124106
.clear-btn {
125107
padding: 100px;
126108
}
127109

128-
a.btn.navbar__github:hover {
129-
color: #fff;
130-
background-color: #273230;
131-
border-color: #222a29;
132-
}
133-
134-
a.btn.navbar__github::before {
135-
content: "";
136-
background-image: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 20.5 20%22%3E%3Cdefs/%3E%3Cpath fill=%22%23fff%22 d=%22M10.3.0C4.6.0.0 4.6.0 10.3c0 4.4 2.8 8.3 7 9.7.5.1.7-.2.7-.5v-1.9c-2.6.5-3.2-.6-3.4-1.2-.2-.6-.6-1.1-1-1.5-.4-.2-.9-.7.0-.7.7.1 1.3.5 1.6 1 .6 1.1 1.9 1.4 3 .8.0-.5.3-1 .7-1.4-2.3-.3-4.7-1.1-4.7-5.1.0-1 .4-2 1.1-2.8-.5-.6-.5-1.6-.1-2.5.0.0.9-.3 2.8 1.1 1.7-.5 3.4-.5 5.1.0 2-1.3 2.8-1.1 2.8-1.1.4.9.5 1.9.2 2.8.7.7 1.1 1.7 1.1 2.8.0 3.9-2.4 4.8-4.7 5.1.5.5.7 1.2.7 1.9v2.8c0 .3.2.6.7.5 5.4-1.8 8.3-7.6 6.5-13C18.6 2.8 14.7.0 10.3.0z%22/%3E%3C/svg%3E");
137-
height: 20px;
138-
width: 20px;
139-
position: absolute;
140-
left: 15px;
141-
top: 10px;
142-
}
143-
144-
.header-github-link::before {
145-
content: '';
146-
width: 24px;
147-
height: 24px;
148-
display: flex;
149-
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
150-
no-repeat;
151-
}
152-
[data-theme='dark'] .header-github-link::before {
153-
background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E")
154-
no-repeat;
155-
}
156-
157110
.docusaurus-highlight-code-line {
158111
background-color: rgb(72, 77, 91);
159112
display: block;

src/css/icon_dropdown.css

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
2+
.navbar__icon {
3+
font-family: poppins,sans-serif;
4+
font-size: 16px;
5+
display: flex;
6+
}
7+
8+
.navbar__icon:before {
9+
content: "";
10+
display: inline-flex;
11+
height: 20px;
12+
width: 35px;
13+
margin-right: 4px;
14+
padding-bottom: 7px;
15+
background-color: var(--ifm-navbar-link-color);
16+
}
17+
.navbar__icon:hover:before {
18+
background-color: var(--ifm-link-color);
19+
}
20+
21+
.navbar__github{
22+
font-size: 16px;
23+
}
24+
25+
.navbar__github:before {
26+
mask: url(/static/img/icon-github.png) no-repeat 100% 100%;
27+
mask-size: cover;
28+
height: 19px;
29+
width: 22px;
30+
padding-right: 1px;
31+
padding-bottom: 4px;
32+
}
33+
34+
35+
.navbar__blog:before {
36+
mask: url(/static/img/icon-notebook.png) no-repeat 100% 100%;
37+
mask-size: cover;
38+
height: 19px;
39+
width: 22px;
40+
padding-right: 1px;
41+
padding-bottom: 4px;
42+
}
43+
44+
/* Used on mobile for icon only links */
45+
.header__link:before {
46+
content: '';
47+
width: 24px;
48+
height: 24px;
49+
display: flex;
50+
background-color: var(--ifm-navbar-link-color);
51+
}
52+
.header__link:hover:before {
53+
background-color: var(--ifm-link-color);
54+
}
55+
56+
.header__github:before {
57+
mask: url(/static/img/icon-github.png) no-repeat 100% 100%;
58+
mask-size: cover;
59+
}
60+
61+
.header__blog:before {
62+
mask: url(/static/img/icon-notebook.png) no-repeat 100% 100%;
63+
mask-size: cover;
64+
}

src/theme/Navbar/MobileSidebar/Header/index.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,17 @@ export default function NavbarMobileSidebarHeader() {
2424
return (
2525
<div className="navbar-sidebar__brand">
2626
<NavbarLogo />
27+
<a
28+
href="blog"
29+
target="_blank"
30+
rel="noopener noreferrer"
31+
className="margin-right--md header__link header__blog"
32+
/>
2733
<a
2834
href="https://github.com/k3s-io/k3s"
2935
target="_blank"
3036
rel="noopener noreferrer"
31-
className="margin-right--md header-github-link"
37+
className="margin-right--md header__link header__github"
3238
/>
3339
<NavbarColorModeToggle className="margin-right--md" />
3440
<CloseButton />

static/img/icon-github.png

6.24 KB
Loading

static/img/icon-notebook.png

4.03 KB
Loading

0 commit comments

Comments
 (0)