Cache images for localkube
This PR introduces caching of localkube images. It makes a best effort to cache the essential images localkube needs as minikube starts up. Currently, the list of cached images is hardcoded, but future work might entail 1. Cached images as a property of the cluster bootstrapper - to allow localkube and kubeadm to cache their respective images. 2. Addons contain image information. Then, we can selectively cache and preload only the addon images that are enabled.pull/1881/head
parent
201e5f9eff
commit
55b41415ed
4
Makefile
4
Makefile
|
@ -67,11 +67,7 @@ KUBE_CROSS_DOCKER_CMD := docker run -w /go/src/$(REPOPATH) --user $(shell id -u)
|
|||
|
||||
# $(call MINIKUBE_GO_BUILD_CMD, output file, OS)
|
||||
define MINIKUBE_GO_BUILD_CMD
|
||||
<<<<<<< HEAD
|
||||
$(MINIKUBE_ENV_$(2)) go build --installsuffix cgo -ldflags="$(MINIKUBE_LDFLAGS) $(K8S_VERSION_LDFLAGS)" -a -o $(1) k8s.io/minikube/cmd/minikube
|
||||
=======
|
||||
$(MINIKUBE_ENV_$(2)) go build -tags "container_image_ostree_stub containers_image_openpgp" --installsuffix cgo -ldflags="$(MINIKUBE_LDFLAGS) $(K8S_VERSION_LDFLAGS)" -a -o $(1) k8s.io/minikube/cmd/minikube
|
||||
>>>>>>> a98f9553f... Vendor changes
|
||||
endef
|
||||
|
||||
ifeq ($(BUILD_IN_DOCKER),y)
|
||||
|
|
|
@ -86,6 +86,7 @@ assumes you have already installed one of the VM drivers: virtualbox/vmwarefusio
|
|||
}
|
||||
|
||||
func runStart(cmd *cobra.Command, args []string) {
|
||||
go machine.CacheImagesForBootstrapper(viper.GetString(cmdcfg.Bootstrapper))
|
||||
api, err := machine.NewAPIClient()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error getting client: %s\n", err)
|
||||
|
|
|
@ -16,7 +16,10 @@ limitations under the License.
|
|||
|
||||
package bootstrapper
|
||||
|
||||
import "k8s.io/minikube/pkg/util"
|
||||
import (
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
// Bootstrapper contains all the methods needed to bootstrap a kubernetes cluster
|
||||
type Bootstrapper interface {
|
||||
|
@ -44,3 +47,7 @@ type KubernetesConfig struct {
|
|||
const (
|
||||
BootstrapperTypeLocalkube = "localkube"
|
||||
)
|
||||
|
||||
var CachedImagesForBootstrapper = map[string][]string{
|
||||
BootstrapperTypeLocalkube: constants.LocalkubeCachedImages,
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"k8s.io/minikube/pkg/minikube/bootstrapper"
|
||||
"k8s.io/minikube/pkg/minikube/config"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
"k8s.io/minikube/pkg/minikube/machine"
|
||||
"k8s.io/minikube/pkg/minikube/sshutil"
|
||||
|
||||
"github.com/docker/machine/libmachine"
|
||||
|
@ -105,6 +106,9 @@ func (lk *LocalkubeBootstrapper) RestartCluster(kubernetesConfig bootstrapper.Ku
|
|||
}
|
||||
|
||||
func (lk *LocalkubeBootstrapper) UpdateCluster(config bootstrapper.KubernetesConfig) error {
|
||||
// Make best effort to load any cached images
|
||||
go machine.LoadImages(lk.cmd, constants.LocalkubeCachedImages, constants.ImageCacheDir)
|
||||
|
||||
copyableFiles := []assets.CopyableFile{}
|
||||
var localkubeFile assets.CopyableFile
|
||||
var err error
|
||||
|
|
|
@ -41,6 +41,7 @@ import (
|
|||
|
||||
cfg "k8s.io/minikube/pkg/minikube/config"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
|
||||
"k8s.io/minikube/pkg/util"
|
||||
)
|
||||
|
||||
|
|
|
@ -146,3 +146,21 @@ const (
|
|||
const IsMinikubeChildProcess = "IS_MINIKUBE_CHILD_PROCESS"
|
||||
const DriverNone = "none"
|
||||
const FileScheme = "file"
|
||||
|
||||
var LocalkubeCachedImages = []string{
|
||||
// Dashboard
|
||||
"gcr.io/google_containers/kubernetes-dashboard-amd64:v1.6.3",
|
||||
|
||||
// DNS
|
||||
"gcr.io/google_containers/k8s-dns-kube-dns-amd64:1.14.4",
|
||||
"gcr.io/google_containers/k8s-dns-dnsmasq-nanny-amd64:1.14.4",
|
||||
"gcr.io/google_containers/k8s-dns-sidecar-amd64:1.14.4",
|
||||
|
||||
// Addon Manager
|
||||
"gcr.io/google-containers/kube-addon-manager:v6.4-beta.2",
|
||||
|
||||
// Pause
|
||||
"gcr.io/google_containers/pause-amd64:3.0",
|
||||
}
|
||||
|
||||
var ImageCacheDir = MakeMiniPath("cache", "images")
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 machine
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"k8s.io/minikube/pkg/minikube/assets"
|
||||
"k8s.io/minikube/pkg/minikube/bootstrapper"
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
|
||||
"github.com/containers/image/copy"
|
||||
"github.com/containers/image/docker"
|
||||
"github.com/containers/image/docker/archive"
|
||||
"github.com/containers/image/signature"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/golang/glog"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const tempLoadDir = "/tmp"
|
||||
|
||||
func CacheImagesForBootstrapper(clusterBootstrapper string) error {
|
||||
images, ok := bootstrapper.CachedImagesForBootstrapper[clusterBootstrapper]
|
||||
if !ok {
|
||||
glog.Infoln("Could not find list of images to cache for bootstrapper %s", clusterBootstrapper)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := CacheImages(images, constants.ImageCacheDir); err != nil {
|
||||
return errors.Wrapf(err, "Caching images for %s", clusterBootstrapper)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CacheImages(images []string, cacheDir string) error {
|
||||
var g errgroup.Group
|
||||
for _, image := range images {
|
||||
image := image
|
||||
g.Go(func() error {
|
||||
dst := filepath.Join(cacheDir, image)
|
||||
dst = sanitizeCacheDir(dst)
|
||||
if err := CacheImage(image, dst); err != nil {
|
||||
return errors.Wrapf(err, "caching image %s", dst)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if err := g.Wait(); err != nil {
|
||||
return errors.Wrap(err, "caching images")
|
||||
}
|
||||
glog.Infoln("Successfully cached all images.")
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadImages(cmd bootstrapper.CommandRunner, images []string, cacheDir string) error {
|
||||
var g errgroup.Group
|
||||
for _, image := range images {
|
||||
image := image
|
||||
g.Go(func() error {
|
||||
src := filepath.Join(cacheDir, image)
|
||||
src = sanitizeCacheDir(src)
|
||||
if err := LoadFromCacheBlocking(cmd, src); err != nil {
|
||||
return errors.Wrapf(err, "loading image %s", src)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if err := g.Wait(); err != nil {
|
||||
return errors.Wrap(err, "loading cached images")
|
||||
}
|
||||
glog.Infoln("Successfully loaded all cached images.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// # ParseReference cannot have a : in the directory path
|
||||
func sanitizeCacheDir(image string) string {
|
||||
return strings.Replace(image, ":", "_", -1)
|
||||
}
|
||||
|
||||
func LoadFromCacheBlocking(cmd bootstrapper.CommandRunner, src string) error {
|
||||
glog.Infoln("Loading image from cache at ", src)
|
||||
filename := filepath.Base(src)
|
||||
for {
|
||||
if _, err := os.Stat(src); err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
dst := filepath.Join(tempLoadDir, filename)
|
||||
f, err := assets.NewFileAsset(src, tempLoadDir, filename, "0777")
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "creating copyable file asset: %s", filename)
|
||||
}
|
||||
if err := cmd.Copy(f); err != nil {
|
||||
return errors.Wrap(err, "transferring cached image")
|
||||
}
|
||||
|
||||
dockerLoadCmd := "docker load -i " + dst
|
||||
|
||||
if err := cmd.Run(dockerLoadCmd); err != nil {
|
||||
return errors.Wrapf(err, "loading docker image: %s", dst)
|
||||
}
|
||||
|
||||
if err := cmd.Run("rm -rf " + dst); err != nil {
|
||||
return errors.Wrap(err, "deleting temp docker image location")
|
||||
}
|
||||
|
||||
glog.Infof("Successfully loaded image %s from cache", src)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSrcRef(image string) (types.ImageReference, error) {
|
||||
srcRef, err := docker.ParseReference("//" + image)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "parsing docker image src ref")
|
||||
}
|
||||
return srcRef, nil
|
||||
}
|
||||
|
||||
func getDstRef(image, dst string) (types.ImageReference, error) {
|
||||
dstRef, err := archive.ParseReference(dst + ":" + image)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "parsing docker archive dst ref")
|
||||
}
|
||||
return dstRef, nil
|
||||
}
|
||||
|
||||
func CacheImage(image, dst string) error {
|
||||
glog.Infof("Attempting to cache image: %s at %s\n", image, dst)
|
||||
if _, err := os.Stat(dst); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
|
||||
return errors.Wrapf(err, "making cache image directory: %s", dst)
|
||||
}
|
||||
|
||||
srcRef, err := getSrcRef(image)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "creating docker image src ref")
|
||||
}
|
||||
|
||||
dstRef, err := getDstRef(image, dst)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "creating docker archive dst ref")
|
||||
}
|
||||
|
||||
policy := &signature.Policy{Default: []signature.PolicyRequirement{signature.NewPRInsecureAcceptAnything()}}
|
||||
policyContext, err := signature.NewPolicyContext(policy)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting policy context")
|
||||
}
|
||||
|
||||
tmp, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "making temp dir")
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
sourceCtx := &types.SystemContext{
|
||||
// By default, the image library will try to look at /etc/docker/certs.d
|
||||
// As a non-root user, this would result in a permissions error,
|
||||
// so, we skip this step by just looking in a newly created tmpdir.
|
||||
DockerCertPath: tmp,
|
||||
}
|
||||
|
||||
err = copy.Image(policyContext, dstRef, srcRef, ©.Options{
|
||||
SourceCtx: sourceCtx,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "copying image")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 machine
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
)
|
||||
|
||||
func TestGetSrcRef(t *testing.T) {
|
||||
for _, image := range constants.LocalkubeCachedImages {
|
||||
if _, err := getSrcRef(image); err != nil {
|
||||
t.Errorf("Error getting src ref for %s: %s", image, err)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue