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:
dlorenc 2018-11-12 12:51:45 -06:00 committed by GitHub
parent 58b607b4d0
commit 063663e17b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 43 additions and 22 deletions

View File

@ -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
}

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
})