mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-10 13:37:08 +08:00
new driver: kubernetes
Tested with `kind` and GKE. Note: "nodes" shown in `docker buildx ls` are unrelated to Kubernetes "nodes". Probably buildx should come up with an alternative term. Usage: $ kind create cluster $ export KUBECONFIG="$(kind get kubeconfig-path --name="kind")" $ docker buildx create --driver kubernetes --driver-opt replicas=3 --use $ docker buildx build -t foo --load . `--load` loads the image into the local Docker. Driver opts: - `image=IMAGE` - Sets the container image to be used for running buildkit. - `namespace=NS` - Sets the Kubernetes namespace. Defaults to the current namespace. - `replicas=N` - Sets the number of `Pod` replicas. Defaults to 1. - `rootless=(true|false)` - Run the container as a non-root user without `securityContext.privileged`. Defaults to false. - `loadbalance=(sticky|random)` - Load-balancing strategy. If set to "sticky", the pod is chosen using the hash of the context path. Defaults to "sticky" Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
This commit is contained in:
146
driver/kubernetes/factory.go
Normal file
146
driver/kubernetes/factory.go
Normal file
@ -0,0 +1,146 @@
|
||||
package kubernetes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/buildx/driver"
|
||||
"github.com/docker/buildx/driver/bkimage"
|
||||
"github.com/docker/buildx/driver/kubernetes/manifest"
|
||||
"github.com/docker/buildx/driver/kubernetes/podchooser"
|
||||
dockerclient "github.com/docker/docker/client"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
const prioritySupported = 30
|
||||
const priorityUnsupported = 70
|
||||
|
||||
func init() {
|
||||
driver.Register(&factory{})
|
||||
}
|
||||
|
||||
type factory struct {
|
||||
}
|
||||
|
||||
func (*factory) Name() string {
|
||||
return DriverName
|
||||
}
|
||||
|
||||
func (*factory) Usage() string {
|
||||
return DriverName
|
||||
}
|
||||
|
||||
func (*factory) Priority(ctx context.Context, api dockerclient.APIClient) int {
|
||||
if api == nil {
|
||||
return priorityUnsupported
|
||||
}
|
||||
return prioritySupported
|
||||
}
|
||||
|
||||
func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver, error) {
|
||||
if cfg.KubeClientConfig == nil {
|
||||
return nil, errors.Errorf("%s driver requires kubernetes API access", DriverName)
|
||||
}
|
||||
deploymentName, err := buildxNameToDeploymentName(cfg.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
namespace, _, err := cfg.KubeClientConfig.Namespace()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot determine Kubernetes namespace, specify manually")
|
||||
}
|
||||
restClientConfig, err := cfg.KubeClientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientset, err := kubernetes.NewForConfig(restClientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := &Driver{
|
||||
factory: f,
|
||||
InitConfig: cfg,
|
||||
clientset: clientset,
|
||||
}
|
||||
deploymentOpt := &manifest.DeploymentOpt{
|
||||
Name: deploymentName,
|
||||
Image: bkimage.DefaultImage,
|
||||
Replicas: 1,
|
||||
BuildkitFlags: cfg.BuildkitFlags,
|
||||
Rootless: false,
|
||||
}
|
||||
loadbalance := LoadbalanceSticky
|
||||
imageOverride := ""
|
||||
for k, v := range cfg.DriverOpts {
|
||||
switch k {
|
||||
case "image":
|
||||
imageOverride = v
|
||||
case "namespace":
|
||||
namespace = v
|
||||
case "replicas":
|
||||
deploymentOpt.Replicas, err = strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "rootless":
|
||||
deploymentOpt.Rootless, err = strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deploymentOpt.Image = bkimage.DefaultRootlessImage
|
||||
case "loadbalance":
|
||||
switch v {
|
||||
case LoadbalanceSticky:
|
||||
case LoadbalanceRandom:
|
||||
default:
|
||||
return nil, errors.Errorf("invalid loadbalance %q", v)
|
||||
}
|
||||
loadbalance = v
|
||||
default:
|
||||
return nil, errors.Errorf("invalid driver option %s for driver %s", k, DriverName)
|
||||
}
|
||||
}
|
||||
if imageOverride != "" {
|
||||
deploymentOpt.Image = imageOverride
|
||||
}
|
||||
d.deployment, err = manifest.NewDeployment(deploymentOpt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.minReplicas = deploymentOpt.Replicas
|
||||
d.deploymentClient = clientset.AppsV1().Deployments(namespace)
|
||||
d.podClient = clientset.CoreV1().Pods(namespace)
|
||||
switch loadbalance {
|
||||
case LoadbalanceSticky:
|
||||
d.podChooser = &podchooser.StickyPodChooser{
|
||||
Key: cfg.ContextPathHash,
|
||||
PodClient: d.podClient,
|
||||
Deployment: d.deployment,
|
||||
}
|
||||
case LoadbalanceRandom:
|
||||
d.podChooser = &podchooser.RandomPodChooser{
|
||||
PodClient: d.podClient,
|
||||
Deployment: d.deployment,
|
||||
}
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (f *factory) AllowsInstances() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// buildxNameToDeploymentName converts buildx name to Kubernetes Deployment name.
|
||||
//
|
||||
// eg. "buildx_buildkit_loving_mendeleev0" -> "loving-mendeleev0"
|
||||
func buildxNameToDeploymentName(bx string) (string, error) {
|
||||
// TODO: commands.util.go should not pass "buildx_buildkit_" prefix to drivers
|
||||
if !strings.HasPrefix(bx, "buildx_buildkit_") {
|
||||
return "", errors.Errorf("expected a string with \"buildx_buildkit_\", got %q", bx)
|
||||
}
|
||||
s := strings.TrimPrefix(bx, "buildx_buildkit_")
|
||||
s = strings.ReplaceAll(s, "_", "-")
|
||||
return s, nil
|
||||
}
|
Reference in New Issue
Block a user