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 {
|
func (b *BaseCommand) MetadataOnly() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseCommand) RequiresUnpackedFS() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ type DockerCommand interface {
|
||||||
FilesUsedFromContext(*v1.Config, *dockerfile.BuildArgs) ([]string, error)
|
FilesUsedFromContext(*v1.Config, *dockerfile.BuildArgs) ([]string, error)
|
||||||
|
|
||||||
MetadataOnly() bool
|
MetadataOnly() bool
|
||||||
|
|
||||||
|
RequiresUnpackedFS() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCommand(cmd instructions.Command, buildcontext string) (DockerCommand, error) {
|
func GetCommand(cmd instructions.Command, buildcontext string) (DockerCommand, error) {
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,10 @@ func (r *RunCommand) MetadataOnly() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *RunCommand) RequiresUnpackedFS() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
type CachingRunCommand struct {
|
type CachingRunCommand struct {
|
||||||
BaseCommand
|
BaseCommand
|
||||||
img v1.Image
|
img v1.Image
|
||||||
|
|
|
||||||
|
|
@ -86,14 +86,11 @@ func newStageBuilder(opts *config.KanikoOptions, stage config.KanikoStage) (*sta
|
||||||
}, nil
|
}, 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 {
|
if !s.opts.Cache {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
args := dockerfile.NewBuildArgs(s.opts.BuildArgs)
|
|
||||||
args.AddMetaArgs(s.stage.MetaArgs)
|
|
||||||
|
|
||||||
layerCache := &cache.RegistryCache{
|
layerCache := &cache.RegistryCache{
|
||||||
Opts: s.opts,
|
Opts: s.opts,
|
||||||
}
|
}
|
||||||
|
|
@ -144,15 +141,6 @@ func (s *stageBuilder) optimize(compositeKey CompositeCache, cfg v1.Config, cmds
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stageBuilder) build() error {
|
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.
|
// Set the initial cache key to be the base image digest, the build args and the SrcContext.
|
||||||
compositeKey := NewCompositeCache(s.baseImageDigest)
|
compositeKey := NewCompositeCache(s.baseImageDigest)
|
||||||
compositeKey.AddKey(s.opts.BuildArgs...)
|
compositeKey.AddKey(s.opts.BuildArgs...)
|
||||||
|
|
@ -170,9 +158,32 @@ func (s *stageBuilder) build() error {
|
||||||
args.AddMetaArgs(s.stage.MetaArgs)
|
args.AddMetaArgs(s.stage.MetaArgs)
|
||||||
|
|
||||||
// Apply optimizations to the instructions.
|
// 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
|
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 {
|
for index, command := range cmds {
|
||||||
if command == nil {
|
if command == nil {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,7 @@ var whitelist = []WhitelistEntry{
|
||||||
// GetFSFromImage extracts the layers of img to root
|
// GetFSFromImage extracts the layers of img to root
|
||||||
// It returns a list of all files extracted
|
// It returns a list of all files extracted
|
||||||
func GetFSFromImage(root string, img v1.Image) ([]string, error) {
|
func GetFSFromImage(root string, img v1.Image) ([]string, error) {
|
||||||
whitelist, err := fileSystemWhitelist(constants.WhitelistPath)
|
if err := DetectFilesystemWhitelist(constants.WhitelistPath); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
logrus.Debugf("Mounted directories: %v", whitelist)
|
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)
|
// (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
|
||||||
// Where (5) is the mount point relative to the process's root
|
// Where (5) is the mount point relative to the process's root
|
||||||
// From: https://www.kernel.org/doc/Documentation/filesystems/proc.txt
|
// 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)
|
f, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
reader := bufio.NewReader(f)
|
reader := bufio.NewReader(f)
|
||||||
|
|
@ -314,7 +313,7 @@ func fileSystemWhitelist(path string) ([]WhitelistEntry, error) {
|
||||||
line, err := reader.ReadString('\n')
|
line, err := reader.ReadString('\n')
|
||||||
logrus.Debugf("Read the following line from %s: %s", path, line)
|
logrus.Debugf("Read the following line from %s: %s", path, line)
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
lineArr := strings.Split(line, " ")
|
lineArr := strings.Split(line, " ")
|
||||||
if len(lineArr) < 5 {
|
if len(lineArr) < 5 {
|
||||||
|
|
@ -336,7 +335,7 @@ func fileSystemWhitelist(path string) ([]WhitelistEntry, error) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return whitelist, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RelativeFiles returns a list of all files at the filepath relative to root
|
// RelativeFiles returns a list of all files at the filepath relative to root
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import (
|
||||||
"github.com/GoogleContainerTools/kaniko/testutil"
|
"github.com/GoogleContainerTools/kaniko/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_fileSystemWhitelist(t *testing.T) {
|
func Test_DetectFilesystemWhitelist(t *testing.T) {
|
||||||
testDir, err := ioutil.TempDir("", "")
|
testDir, err := ioutil.TempDir("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating tempdir: %s", err)
|
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)
|
t.Fatalf("Error writing file contents to %s: %s", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
actualWhitelist, err := fileSystemWhitelist(path)
|
err = DetectFilesystemWhitelist(path)
|
||||||
expectedWhitelist := []WhitelistEntry{
|
expectedWhitelist := []WhitelistEntry{
|
||||||
{"/kaniko", false},
|
{"/kaniko", false},
|
||||||
{"/proc", false},
|
{"/proc", false},
|
||||||
|
|
@ -59,6 +59,7 @@ func Test_fileSystemWhitelist(t *testing.T) {
|
||||||
{"/var/run", false},
|
{"/var/run", false},
|
||||||
{"/etc/mtab", false},
|
{"/etc/mtab", false},
|
||||||
}
|
}
|
||||||
|
actualWhitelist := whitelist
|
||||||
sort.Slice(actualWhitelist, func(i, j int) bool {
|
sort.Slice(actualWhitelist, func(i, j int) bool {
|
||||||
return actualWhitelist[i].Path < actualWhitelist[j].Path
|
return actualWhitelist[i].Path < actualWhitelist[j].Path
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue