Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add supports for k0s #1227

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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