Skip to content

Commit

Permalink
Add supports for k0s
Browse files Browse the repository at this point in the history
Signed-off-by: Hung-Han (Henry) Chen <[email protected]>
  • Loading branch information
chenhunghan committed Dec 21, 2024
1 parent 7482867 commit 61b42bb
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 48 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ To enable Kubernetes, start Colima with `--kubernetes` flag.
colima start --kubernetes
```

By default, colima use [k3s](https://github.com/k3s-io/k3s/).

Use [k0s](https://github.com/k0sproject/k0s).

```
colima start --kubernetes --k0s
```

#### Interacting with Image Registry

For Docker runtime, images built or pulled with Docker are accessible to Kubernetes.
Expand Down
6 changes: 5 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ func (c colimaApp) startWithRuntime(conf config.Config) ([]environment.Container
{
runtime := conf.Runtime
if kubernetesEnabled {
runtime += "+k3s"
if conf.Kubernetes.UseK0s {
runtime += "+k0s"
} else {
runtime += "+k3s"
}
}
log.Println("runtime:", runtime)
}
Expand Down
19 changes: 13 additions & 6 deletions cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ Run 'colima template' to set the default configurations or 'colima start --edit'
" colima start --arch aarch64\n" +
" colima start --dns 1.1.1.1 --dns 8.8.8.8\n" +
" colima start --dns-host example.com=1.2.3.4\n" +
" colima start --kubernetes --k3s-arg=--disable=coredns,servicelb,traefik,local-storage,metrics-server",
" colima start --kubernetes --k3s-arg=--disable=coredns,servicelb,traefik,local-storage,metrics-server" +
" colima start --kubernetes --k0s" +
" colima start --kubernetes --k0s --kubernetes-version=v1.28.15+k0s.0",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
app := newApp()
Expand Down Expand Up @@ -109,10 +111,11 @@ Run 'colima template' to set the default configurations or 'colima start --edit'
}

const (
defaultCPU = 2
defaultMemory = 2
defaultDisk = 100
defaultKubernetesVersion = kubernetes.DefaultVersion
defaultCPU = 2
defaultMemory = 2
defaultDisk = 100
defaultK3sVersion = kubernetes.DefaultK3sVersion
defaultK0sVersion = kubernetes.DefaultK0sVersion

defaultMountTypeQEMU = "sshfs"
defaultMountTypeVZ = "virtiofs"
Expand Down Expand Up @@ -204,7 +207,8 @@ func init() {
// k8s
startCmd.Flags().BoolVarP(&startCmdArgs.Kubernetes.Enabled, "kubernetes", "k", false, "start with Kubernetes")
startCmd.Flags().BoolVar(&startCmdArgs.Flags.LegacyKubernetes, "with-kubernetes", false, "start with Kubernetes")
startCmd.Flags().StringVar(&startCmdArgs.Kubernetes.Version, "kubernetes-version", defaultKubernetesVersion, "must match a k3s version https://github.com/k3s-io/k3s/releases")
startCmd.Flags().StringVar(&startCmdArgs.Kubernetes.Version, "kubernetes-version", "", "Kubernetes version to use")
startCmd.Flags().BoolVarP(&startCmdArgs.Kubernetes.UseK0s, "k0s", "0", false, "use k0s instead of k3s")
startCmd.Flags().StringSliceVar(&startCmdArgs.Flags.LegacyKubernetesDisable, "kubernetes-disable", nil, "components to disable for k3s e.g. traefik,servicelb")
startCmd.Flags().StringSliceVar(&startCmdArgs.Kubernetes.K3sArgs, "k3s-arg", defaultK3sArgs, "additional args to pass to k3s")
startCmd.Flag("with-kubernetes").Hidden = true
Expand Down Expand Up @@ -417,6 +421,9 @@ func prepareConfig(cmd *cobra.Command) {
if !cmd.Flag("kubernetes-version").Changed {
startCmdArgs.Kubernetes.Version = current.Kubernetes.Version
}
if !cmd.Flag("k0s").Changed {
startCmdArgs.Kubernetes.UseK0s = current.Kubernetes.UseK0s
}
if !cmd.Flag("k3s-arg").Changed {
startCmdArgs.Kubernetes.K3sArgs = current.Kubernetes.K3sArgs
}
Expand Down
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type Kubernetes struct {
Enabled bool `yaml:"enabled"`
Version string `yaml:"version"`
K3sArgs []string `yaml:"k3sArgs"`
UseK0s bool `yaml:"usek0s"`
}

// Network is VM network configuration
Expand Down
42 changes: 42 additions & 0 deletions environment/container/kubernetes/k0s.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package kubernetes

import (
"fmt"

"github.com/abiosoft/colima/cli"
"github.com/abiosoft/colima/environment"
)

func installK0s(
guest environment.GuestActions,
a *cli.ActiveCommandChain,
k0sVersion string,
) {
installK0sBinary(guest, a, k0sVersion)
installK0sCluster(guest, a)
}

func installK0sBinary(
guest environment.GuestActions,
a *cli.ActiveCommandChain,
k0sVersion string,
) {
a.Add(func() error {
k0senv := fmt.Sprintf("K0S_VERSION=%s", k0sVersion)
cmd := fmt.Sprintf("curl --tlsv1.2 -sSf https://get.k0s.sh | sudo %s sh", k0senv)
if err := guest.Run("sh", "-c", cmd); err != nil {
return fmt.Errorf("failed to install k0s %w", err)
}
return nil
})
}

func installK0sCluster(
guest environment.GuestActions,
a *cli.ActiveCommandChain,
) {
// Initialize k0s with default configuration
a.Add(func() error {
return guest.Run("sudo", "k0s", "install", "controller", "--single")
})
}
34 changes: 25 additions & 9 deletions environment/container/kubernetes/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"path/filepath"
"regexp"
"strings"
"time"

Expand Down Expand Up @@ -46,16 +47,31 @@ func (c kubernetesRuntime) provisionKubeconfig(ctx context.Context) error {

// manipulate in VM and save to host
a.Add(func() error {
kubeconfig, err := c.guest.Read("/etc/rancher/k3s/k3s.yaml")
if err != nil {
return fmt.Errorf("error fetching kubeconfig on guest: %w", err)
}
// replace name
kubeconfig = strings.ReplaceAll(kubeconfig, ": default", ": "+profile)
var err error
kubeconfig := ""
if c.config().UseK0s {
kubeconfig, err = c.guest.Read("/var/lib/k0s/pki/admin.conf")
if err != nil {
return fmt.Errorf("error fetching kubeconfig on guest: %w", err)
}
kubeconfig = strings.ReplaceAll(kubeconfig, ": Default", ": "+profile)

// replace IP
if ip != "" && ip != "127.0.0.1" {
kubeconfig = strings.ReplaceAll(kubeconfig, "https://127.0.0.1:", "https://"+ip+":")
if ip != "" && ip != "127.0.0.1" {
re := regexp.MustCompile(`https://[^:]+:`)
kubeconfig = re.ReplaceAllString(kubeconfig, "https://"+ip+":")
}
} else {
kubeconfig, err = c.guest.Read("/etc/rancher/k3s/k3s.yaml")
if err != nil {
return fmt.Errorf("error fetching kubeconfig on guest: %w", err)
}
// replace name
kubeconfig = strings.ReplaceAll(kubeconfig, ": default", ": "+profile)

// replace IP
if ip != "" && ip != "127.0.0.1" {
kubeconfig = strings.ReplaceAll(kubeconfig, "https://127.0.0.1:", "https://"+ip+":")
}
}

// save on the host
Expand Down
113 changes: 81 additions & 32 deletions environment/container/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import (
// Name is container runtime name

const (
Name = "kubernetes"
DefaultVersion = "v1.31.2+k3s1"

ConfigKey = "kubernetes_config"
Name = "kubernetes"
DefaultK3sVersion = "v1.31.2+k3s1"
DefaultK0sVersion = "v1.31.3+k0s.0"
ConfigKey = "kubernetes_config"
)

func newRuntime(host environment.HostActions, guest environment.GuestActions) environment.Container {
Expand All @@ -47,21 +47,35 @@ func (c kubernetesRuntime) Name() string {
return Name
}

func (c kubernetesRuntime) isInstalled() bool {
func (c kubernetesRuntime) isInstalled(useK0s bool) bool {
if useK0s {
return c.guest.RunQuiet("command", "-v", "k0s") == nil
}
// it is installed if uninstall script is present.
return c.guest.RunQuiet("command", "-v", "k3s-uninstall.sh") == nil
}

func (c kubernetesRuntime) isVersionInstalled(version string) bool {
// validate version change via cli flag/config.
out, err := c.guest.RunOutput("k3s", "--version")
if err != nil {
return false
func (c kubernetesRuntime) isVersionInstalled(version string, useK0s bool) bool {
if useK0s {
out, err := c.guest.RunOutput("k0s", "version")
if err != nil {
return false
}
return strings.Contains(out, version)
} else {
// validate version change via cli flag/config.
out, err := c.guest.RunOutput("k3s", "--version")
if err != nil {
return false
}
return strings.Contains(out, version)
}
return strings.Contains(out, version)
}

func (c kubernetesRuntime) Running(context.Context) bool {
if c.config().UseK0s {
return c.guest.RunQuiet("sudo", "service", "k0scontroller", "status") == nil
}
return c.guest.RunQuiet("sudo", "service", "k3s", "status") == nil
}

Expand All @@ -70,10 +84,20 @@ func (c kubernetesRuntime) runtime() string {
}

func (c kubernetesRuntime) config() config.Kubernetes {
conf := config.Kubernetes{Version: DefaultVersion}
conf := config.Kubernetes{}
if b := c.guest.Get(ConfigKey); b != "" {
_ = json.Unmarshal([]byte(b), &conf)
}

// Set default version based on UseK0s flag
if conf.Version == "" {
if conf.UseK0s {
conf.Version = DefaultK0sVersion
} else {
conf.Version = DefaultK3sVersion
}
}

return conf
}

Expand Down Expand Up @@ -104,16 +128,17 @@ func (c *kubernetesRuntime) Provision(ctx context.Context) error {
conf = c.config()
}

if c.isVersionInstalled(conf.Version) {
if c.isVersionInstalled(conf.Version, conf.UseK0s) {
// runtime has changed, ensure the required images are in the registry
if currentRuntime := c.runtime(); currentRuntime != "" && currentRuntime != runtime {
a.Stagef("changing runtime to %s", runtime)
installK3sCache(c.host, c.guest, a, log, runtime, conf.Version)
if !conf.UseK0s {
a.Stagef("changing runtime to %s", runtime)
// other settings may have changed e.g. ingress
installK3sCache(c.host, c.guest, a, log, runtime, conf.Version)
}
}
// other settings may have changed e.g. ingress
installK3sCluster(c.host, c.guest, a, runtime, conf.Version, conf.K3sArgs)
} else {
if c.isInstalled() {
if c.isInstalled(conf.UseK0s) {
a.Stagef("version changed to %s, downloading and installing", conf.Version)
} else {
if ok {
Expand All @@ -122,13 +147,19 @@ func (c *kubernetesRuntime) Provision(ctx context.Context) error {
a.Stage("installing")
}
}
installK3s(c.host, c.guest, a, log, runtime, conf.Version, conf.K3sArgs)
if conf.UseK0s {
installK0s(c.guest, a, conf.Version)
} else {
installK3s(c.host, c.guest, a, log, runtime, conf.Version, conf.K3sArgs)
}
}

// this needs to happen on each startup
{
// cni is used by both cri-dockerd and containerd
installCniConfig(c.guest, a)
if !conf.UseK0s {
// cni is used by both cri-dockerd and containerd
installCniConfig(c.guest, a)
}
}

// provision successful, now we can persist the version
Expand All @@ -145,12 +176,21 @@ func (c kubernetesRuntime) Start(ctx context.Context) error {
return nil
}

a.Add(func() error {
return c.guest.Run("sudo", "service", "k3s", "start")
})
a.Retry("", time.Second*2, 10, func(int) error {
return c.guest.RunQuiet("kubectl", "cluster-info")
})
if c.config().UseK0s {
a.Add(func() error {
return c.guest.Run("sudo", "systemctl", "start", "k0scontroller")
})
a.Retry("", time.Second*2, 10, func(int) error {
return c.guest.RunQuiet("sudo", "k0s", "kubectl", "cluster-info")
})
} else {
a.Add(func() error {
return c.guest.Run("sudo", "service", "k3s", "start")
})
a.Retry("", time.Second*2, 10, func(int) error {
return c.guest.RunQuiet("kubectl", "cluster-info")
})
}

if err := a.Exec(); err != nil {
return err
Expand Down Expand Up @@ -238,15 +278,24 @@ func (c kubernetesRuntime) runningContainerIDs() string {
func (c kubernetesRuntime) Teardown(ctx context.Context) error {
a := c.Init(ctx)

if c.isInstalled() {
if c.isInstalled(c.config().UseK0s) {
a.Add(func() error {
return c.guest.Run("k3s-uninstall.sh")
if c.config().UseK0s {
if err := c.guest.Run("sudo", "systemctl", "stop", "k0scontroller"); err != nil {
return fmt.Errorf("error stopping k0scontroller services: %w", err)
}
return c.guest.Run("sudo", "k0s", "reset")
} else {
return c.guest.Run("k3s-uninstall.sh")
}
})
}

// k3s is buggy with external containerd for now
// cleanup is manual
a.Add(c.deleteAllContainers)
if !c.config().UseK0s {
// k3s is buggy with external containerd for now
// cleanup is manual
a.Add(c.deleteAllContainers)
}

c.teardownKubeconfig(a)

Expand Down

0 comments on commit 61b42bb

Please sign in to comment.