Skip unpacking the base FS if there are no run commands (or only cached ones). (#440)
This is the final part of an optimization that I've been refactoring towards for awhile. If the Dockerfile consists of no RUN commands, or cached RUN commands, followed by metadata-only operations, we can skip downloading and unpacking the base image.
This commit is contained in:
parent
58b607b4d0
commit
063663e17b
|
|
@ -39,3 +39,7 @@ func (b *BaseCommand) FilesUsedFromContext(_ *v1.Config, _ *dockerfile.BuildArgs
|
|||
func (b *BaseCommand) MetadataOnly() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *BaseCommand) RequiresUnpackedFS() bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ type DockerCommand interface {
|
|||
FilesUsedFromContext(*v1.Config, *dockerfile.BuildArgs) ([]string, error)
|
||||
|
||||
MetadataOnly() bool
|
||||
|
||||
RequiresUnpackedFS() bool
|
||||
}
|
||||
|
||||
func GetCommand(cmd instructions.Command, buildcontext string) (DockerCommand, error) {
|
||||
|
|
|
|||
|
|
@ -165,6 +165,10 @@ func (r *RunCommand) MetadataOnly() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (r *RunCommand) RequiresUnpackedFS() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type CachingRunCommand struct {
|
||||
BaseCommand
|
||||
img v1.Image
|
||||
|
|
|
|||
|
|
@ -86,14 +86,11 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage) (*sta
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *stageBuilder) optimize(compositeKey CompositeCache, cfg v1.Config, cmds []commands.DockerCommand) error {
|
||||
func (s *stageBuilder) optimize(compositeKey CompositeCache, cfg v1.Config, cmds []commands.DockerCommand, args *dockerfile.BuildArgs) error {
|
||||
if !s.opts.Cache {
|
||||
return nil
|
||||
}
|
||||
|
||||
args := dockerfile.NewBuildArgs(s.opts.BuildArgs)
|
||||
args.AddMetaArgs(s.stage.MetaArgs)
|
||||
|
||||
layerCache := &cache.RegistryCache{
|
||||
Opts: s.opts,
|
||||
}
|
||||
|
|
@ -144,15 +141,6 @@ func (s *stageBuilder) optimize(compositeKey CompositeCache, cfg v1.Config, cmds
|
|||
}
|
||||
|
||||
func (s *stageBuilder) build() error {
|
||||
// Unpack file system to root
|
||||
if _, err := util.GetFSFromImage(constants.RootDir, s.image); err != nil {
|
||||
return err
|
||||
}
|
||||
// Take initial snapshot
|
||||
if err := s.snapshotter.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the initial cache key to be the base image digest, the build args and the SrcContext.
|
||||
compositeKey := NewCompositeCache(s.baseImageDigest)
|
||||
compositeKey.AddKey(s.opts.BuildArgs...)
|
||||
|
|
@ -170,9 +158,32 @@ func (s *stageBuilder) build() error {
|
|||
args.AddMetaArgs(s.stage.MetaArgs)
|
||||
|
||||
// Apply optimizations to the instructions.
|
||||
if err := s.optimize(*compositeKey, s.cf.Config, cmds); err != nil {
|
||||
if err := s.optimize(*compositeKey, s.cf.Config, cmds, args); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unpack file system to root if we need to.
|
||||
shouldUnpack := false
|
||||
for _, cmd := range cmds {
|
||||
if cmd.RequiresUnpackedFS() {
|
||||
logrus.Infof("Unpacking rootfs as cmd %s requires it.", cmd.String())
|
||||
shouldUnpack = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if shouldUnpack {
|
||||
if _, err := util.GetFSFromImage(constants.RootDir, s.image); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := util.DetectFilesystemWhitelist(constants.WhitelistPath); err != nil {
|
||||
return err
|
||||
}
|
||||
// Take initial snapshot
|
||||
if err := s.snapshotter.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for index, command := range cmds {
|
||||
if command == nil {
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -62,8 +62,7 @@ var whitelist = []WhitelistEntry{
|
|||
// GetFSFromImage extracts the layers of img to root
|
||||
// It returns a list of all files extracted
|
||||
func GetFSFromImage(root string, img v1.Image) ([]string, error) {
|
||||
whitelist, err := fileSystemWhitelist(constants.WhitelistPath)
|
||||
if err != nil {
|
||||
if err := DetectFilesystemWhitelist(constants.WhitelistPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logrus.Debugf("Mounted directories: %v", whitelist)
|
||||
|
|
@ -303,10 +302,10 @@ func checkWhitelistRoot(root string) bool {
|
|||
// (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
|
||||
// Where (5) is the mount point relative to the process's root
|
||||
// From: https://www.kernel.org/doc/Documentation/filesystems/proc.txt
|
||||
func fileSystemWhitelist(path string) ([]WhitelistEntry, error) {
|
||||
func DetectFilesystemWhitelist(path string) error {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
reader := bufio.NewReader(f)
|
||||
|
|
@ -314,7 +313,7 @@ func fileSystemWhitelist(path string) ([]WhitelistEntry, error) {
|
|||
line, err := reader.ReadString('\n')
|
||||
logrus.Debugf("Read the following line from %s: %s", path, line)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
lineArr := strings.Split(line, " ")
|
||||
if len(lineArr) < 5 {
|
||||
|
|
@ -336,7 +335,7 @@ func fileSystemWhitelist(path string) ([]WhitelistEntry, error) {
|
|||
break
|
||||
}
|
||||
}
|
||||
return whitelist, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// RelativeFiles returns a list of all files at the filepath relative to root
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import (
|
|||
"github.com/GoogleContainerTools/kaniko/testutil"
|
||||
)
|
||||
|
||||
func Test_fileSystemWhitelist(t *testing.T) {
|
||||
func Test_DetectFilesystemWhitelist(t *testing.T) {
|
||||
testDir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating tempdir: %s", err)
|
||||
|
|
@ -49,7 +49,7 @@ func Test_fileSystemWhitelist(t *testing.T) {
|
|||
t.Fatalf("Error writing file contents to %s: %s", path, err)
|
||||
}
|
||||
|
||||
actualWhitelist, err := fileSystemWhitelist(path)
|
||||
err = DetectFilesystemWhitelist(path)
|
||||
expectedWhitelist := []WhitelistEntry{
|
||||
{"/kaniko", false},
|
||||
{"/proc", false},
|
||||
|
|
@ -59,6 +59,7 @@ func Test_fileSystemWhitelist(t *testing.T) {
|
|||
{"/var/run", false},
|
||||
{"/etc/mtab", false},
|
||||
}
|
||||
actualWhitelist := whitelist
|
||||
sort.Slice(actualWhitelist, func(i, j int) bool {
|
||||
return actualWhitelist[i].Path < actualWhitelist[j].Path
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue