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

feature: k0s init, import some dependencies form k0sproject. #1619

Merged
merged 3 commits into from
Aug 14, 2022
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
27 changes: 20 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ require (
github.com/docker/go-connections v0.4.0
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/go-git/go-git/v5 v5.4.2
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/google/uuid v1.2.0
github.com/imdario/mergo v0.3.12
github.com/k0sproject/dig v0.2.0
github.com/k0sproject/k0sctl v0.13.1
github.com/k0sproject/rig v0.6.4
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
github.com/lestrrat-go/strftime v1.0.6 // indirect
github.com/mitchellh/go-homedir v1.1.0
Expand All @@ -40,23 +44,32 @@ require (
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.etcd.io/etcd/client/v3 v3.5.0
go.uber.org/zap v1.17.0
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
golang.org/x/net v0.0.0-20210510120150-4163338589ed
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
golang.org/x/sys v0.0.0-20220209214540-3681064d5158
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gotest.tools v2.2.0+incompatible
helm.sh/helm/v3 v3.6.2
k8s.io/api v0.21.0
k8s.io/apimachinery v0.21.0
k8s.io/apimachinery v0.22.1
k8s.io/cli-runtime v0.21.0
k8s.io/client-go v0.21.0
k8s.io/client-go v0.22.1
k8s.io/kube-proxy v0.21.0
k8s.io/kubelet v0.21.0
k8s.io/utils v0.0.0-20210111153108-fddb29f9d009
k8s.io/utils v0.0.0-20210820185131-d34e5cb4466e
sigs.k8s.io/controller-runtime v0.8.1
sigs.k8s.io/yaml v1.2.0
)

replace github.com/docker/docker => github.com/docker/docker v20.10.3-0.20211208011758-87521affb077+incompatible
replace (
github.com/docker/docker => github.com/docker/docker v20.10.3-0.20211208011758-87521affb077+incompatible
golang.org/x/crypto => golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
golang.org/x/net => golang.org/x/net v0.0.0-20210510120150-4163338589ed
golang.org/x/sys => golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
k8s.io/api => k8s.io/api v0.21.0
k8s.io/apimachinery => k8s.io/apimachinery v0.21.0
k8s.io/client-go => k8s.io/client-go v0.21.0
k8s.io/utils => k8s.io/utils v0.0.0-20210111153108-fddb29f9d009
)
375 changes: 139 additions & 236 deletions go.sum

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions pkg/runtime/k0s/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# k0s Runtime Design
## basefs

```shell
.
├── amd64
│   ├── bin
│   │   ├── k0s
│   │   ├── k0sctl
│   │   ├── kubectl
│   │   ├── nerdctl
│   │   └── seautil
│   ├── images
│   │   └── registry.tar.gz
│   └── Metadata
├── imageList
├── Kubefile
└── rootfs
├── etc
│   ├── dump-config.toml
│   └── registry.yml
└── scripts
├── containerd.sh
├── init-registry.sh
└── init.sh
```

## introduce
We define the k0s runtime have 5 phases to install/scale/reset the cluster.

basefs contain binary、shell script、config file and image. see more about [sealerio/basefs](https://github.com/sealerio/basefs)

Runtime before filesystem lead cluster install through execute [k0sctl](https://github.com/k0sproject/k0sctl) command.

+ Init
+ When sealer lead to cluster install first, init phase copy rootfs/bin to /usr/bin in init.sh script, make host ssh environment with no password, convert cluster file to k0sctl config, install the private registry.
+ Join
+ Join phase prepare the registry certs, and use `k0sctl apply` to reconcile the cluster.
+ Delete
+ Delete is same as join, but it recycles anything installed by join phase.
+ Reset
+ Reset through k0sctl to remove this cluster and remove anything about this cluster generated by sealer.
+ Upgrade
+ Upgrade can upgrade the k0s cluster.
22 changes: 22 additions & 0 deletions pkg/runtime/k0s/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright © 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package k0s

const (
GetK0sVersionCMD = "k0s version"

SSHKeyGenCMD = "ssh-keygen -f /root/.ssh/id_rsa -P \"\""
SSHCopyIDPrefix = "ssh-copy-id -f "
)
81 changes: 81 additions & 0 deletions pkg/runtime/k0s/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright © 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package k0s

import (
"fmt"
)

func (k *Runtime) init() error {
pipeline := []func() error{
k.MergeConfigOnMaster0,
k.SSHKeyGenAndCopyIDToHosts,
Copy link
Member

@kakaZhou719 kakaZhou719 Aug 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not very familiar with k0s, I have a question, is this necessary to configure ssh key login?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we don't ssh-keygen id_rsa config: when k0sctl lead to install cluster, it will report:ERRO [ssh] 172.16.161.63:22: attempt 1 of 60.. failed to connect: stat /root/.ssh/id_rsa: no such file or directory
if we don't ssh-copy-id: k0sctl will report :failed to connect: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain

// TODO: move all these registry operation to the specific registry packages.
k.GenerateCert,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of configuring private image repositories will be removed from the runtime pkg after we refactor run process, and i think k8s runtime also need those logic. see PR #1621

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it! will adapt it when new code merged.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest that we do this before the code merging. Or at least you add a TODO comment

// TODO: move all these registry operation to the specific registry packages.

k.ApplyRegistryOnMaster0,
}

for _, f := range pipeline {
if err := f(); err != nil {
return fmt.Errorf("failed to prepare Master0 env: %v", err)
}
}

return nil
}

// MergeConfigOnMaster0 convert the cluster file spec to kubectl.yaml (k0sctl config file) to lead cluster run
func (k *Runtime) MergeConfigOnMaster0() error {
if err := k.K0sConfig.ConvertTok0sConfig(k.cluster); err != nil {
return fmt.Errorf("failed to convert to k0s config from clusterfile: %v", err)
}
ssh, err := k.getHostSSHClient(k.cluster.GetMaster0IP())
if err != nil {
return fmt.Errorf("failed to get ssh client: %v", err)
}
output, err := ssh.Cmd(k.cluster.GetMaster0IP(), GetK0sVersionCMD)
if err != nil {
return err
}

k.K0sConfig.DefineConfigFork0s(string(output), k.RegConfig.Domain, k.RegConfig.Port, k.cluster.Name)

//write k0sctl.yaml to master0 rootfs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the name of this func explicitly illustrates its ability. Maybe we don't have to annotate it. (It's ok to keep it.)

if err := k.K0sConfig.WriteConfigToMaster0(k.getRootfs()); err != nil {
return err
}
return nil
}

// SSHKeyGenAndCopyIDToHosts use ssh-copy-id to prepare a no password ssh-client for k0sctl install environment.
func (k *Runtime) SSHKeyGenAndCopyIDToHosts() error {
return k.sshKeyGenAndCopyIDToHosts()
}

// GenerateCert generate the containerd CA for registry TLS.
func (k *Runtime) GenerateCert() error {
if err := k.GenerateRegistryCert(); err != nil {
return err
}
return k.SendRegistryCert(k.cluster.GetMasterIPList()[:1])
}

// sshKeyGenAndCopyIDToHosts use ssh-key-gen and ssh-copy-id to prepare an env without password for k0sctl.
func (k *Runtime) sshKeyGenAndCopyIDToHosts() error {
if err := k.sshKeyGen(); err != nil {
return err
}
return k.sshCopyIDToEveryHost()
}
28 changes: 28 additions & 0 deletions pkg/runtime/k0s/k0sctl/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright © 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package k0sctl

const (
K0sUploadBinaryPath = "/usr/bin/k0s"

ClusterFileRoleMaster = "master"
ClusterFileRoleWorker = "worker"

K0sController = "controller"
K0sWorker = "worker"
K0sControllerAndWorker = "controller+worker"

K0sDefaultUser = "root"
)
138 changes: 138 additions & 0 deletions pkg/runtime/k0s/k0sctl/k0sctl_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Copyright © 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package k0sctl

import (
"fmt"
"strconv"

"github.com/sealerio/sealer/pkg/runtime/k0s/k0sctl/v1beta1"
v1 "github.com/sealerio/sealer/types/api/v1"
v2 "github.com/sealerio/sealer/types/api/v2"
utilsnet "github.com/sealerio/sealer/utils/net"
osi "github.com/sealerio/sealer/utils/os"

"github.com/k0sproject/dig"
"github.com/k0sproject/k0sctl/pkg/apis/k0sctl.k0sproject.io/v1beta1/cluster"
"github.com/k0sproject/rig"
yaml2 "gopkg.in/yaml.v2"
)

type K0sConfig struct {
*v1beta1.Cluster
}

// ConvertTok0sConfig convert cluster file spec host to k0sctl spec hosts.
func (c *K0sConfig) ConvertTok0sConfig(clusterFile *v2.Cluster) error {
return c.convertIPVSToAddress(clusterFile)
}

func (c *K0sConfig) convertIPVSToAddress(clusterFile *v2.Cluster) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the logic here seems to be parsing the host configuration information in the *v2.Cluster structure. Do we need to rename this function? convertClusterHosts ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This func mainly use sealer cluster file spec Hosts:IPS as input and out put k0s cluster spec Hosts:SSH:Address, so i named this. If there is a better way to rename it? I think though convertClusterHosts seems like convert some thing in Hosts field, but it don't represent we convert Hosts every field under Hosts.

masterIPList := utilsnet.IPsToIPStrs(clusterFile.GetIPSByRole(ClusterFileRoleMaster))
if err := c.joinK0sHosts(masterIPList, clusterFile.Spec.Hosts, clusterFile.Spec.SSH, K0sController); err != nil {
return err
}

nodeIPList := utilsnet.IPsToIPStrs(clusterFile.GetIPSByRole(ClusterFileRoleWorker))
if err := c.joinK0sHosts(nodeIPList, clusterFile.Spec.Hosts, clusterFile.Spec.SSH, K0sWorker); err != nil {
return err
}
// TODO: Get the controller+worker role node.
// because sealer's cluster file do not support master and worker roles,
// so we can't generate a controller and worker role in k0s config.
return nil
}

func (c *K0sConfig) joinK0sHosts(ipList []string, hosts []v2.Host, ssh v1.SSH, role string) error {
for _, ip := range ipList {
port, err := c.parseSSHPortByIP(ip, hosts, ssh)
if err != nil {
return err
}
//Now sealer do not support rootless, so default user is always 'root'.
c.addHostField(ip, port, role, K0sDefaultUser)
}
return nil
}

// addHostField generate k0s config spec Host and append it to config file.
func (c *K0sConfig) addHostField(ipAddr string, port int, role, user string) {
host := cluster.Host{
Connection: rig.Connection{
SSH: &rig.SSH{
Address: ipAddr,
Port: port,
User: user,
},
},
Role: role,
UploadBinary: true,
K0sBinaryPath: K0sUploadBinaryPath,
}
c.Spec.Hosts = append(c.Spec.Hosts, &host)
}

// parseSSHPortByIP parse cluster file ssh port to k0s ssh port.
func (c *K0sConfig) parseSSHPortByIP(ipAddr string, hosts []v2.Host, ssh v1.SSH) (int, error) {
hostsMap := c.getHostsMap(hosts)
host := hostsMap[ipAddr]
if host.SSH.Port != "" {
return strconv.Atoi(host.SSH.Port)
}
return strconv.Atoi(ssh.Port)
}

// getHostsMap convert hosts to map[string]v1.SSH with ssh arg
func (c *K0sConfig) getHostsMap(hosts []v2.Host) map[string]v2.Host {
hostsMap := make(map[string]v2.Host)
for _, host := range hosts {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic here should be able to simplify。
1.convert hosts to map[string]v1.SSH with ssh arg.
2. get map value from ipAddr.

ips := utilsnet.IPsToIPStrs(host.IPS)
for _, ip := range ips {
hostsMap[ip] = host
}
}
return hostsMap
}

// DefineConfigFork0s define the private registry for image repo such as: sea.hub:5000
func (c *K0sConfig) DefineConfigFork0s(version, domain, port, name string) {
c.Spec.K0s = &cluster.K0s{
Version: version,
Config: dig.Mapping{
"apiVersion": "k0s.k0sproject.io/v1beta1",
"metadata": dig.Mapping{
"name": name,
},
"spec": dig.Mapping{
"images": dig.Mapping{
"repository": "\"" + domain + ":" + port + "\"",
},
},
},
}
}

// WriteConfigToMaster0 write k0sctl config to rootfs
func (c *K0sConfig) WriteConfigToMaster0(rootfs string) error {
k0sConfig := c.Cluster
marshal, err := yaml2.Marshal(k0sConfig)
if err != nil {
return err
}
if err := osi.NewAtomicWriter(rootfs + "/k0sctl.yaml").WriteFile(marshal); err != nil {
return fmt.Errorf("failed to write k0sctl config: %v ", err)
}
return nil
}
Loading