mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
podman: add support for splitting imagestore
Add support for `--imagestore` in podman which allows users to split the filesystem of containers vs image store, imagestore if configured will pull images in image storage instead of the graphRoot while keeping the other parts still in the originally configured graphRoot. This is an implementation of https://github.com/containers/storage/pull/1549 in podman. Signed-off-by: Aditya R <arajan@redhat.com>
This commit is contained in:
@ -507,6 +507,10 @@ func rootFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) {
|
|||||||
pFlags.StringVar(&podmanConfig.Runroot, runrootFlagName, "", "Path to the 'run directory' where all state information is stored")
|
pFlags.StringVar(&podmanConfig.Runroot, runrootFlagName, "", "Path to the 'run directory' where all state information is stored")
|
||||||
_ = cmd.RegisterFlagCompletionFunc(runrootFlagName, completion.AutocompleteDefault)
|
_ = cmd.RegisterFlagCompletionFunc(runrootFlagName, completion.AutocompleteDefault)
|
||||||
|
|
||||||
|
imageStoreFlagName := "imagestore"
|
||||||
|
pFlags.StringVar(&podmanConfig.ImageStore, imageStoreFlagName, "", "Path to the `image store`, different from `graph root`, use this to split storing the image into a separate `image store`, see `man containers-storage.conf` for details")
|
||||||
|
_ = cmd.RegisterFlagCompletionFunc(imageStoreFlagName, completion.AutocompleteDefault)
|
||||||
|
|
||||||
pFlags.BoolVar(&podmanConfig.TransientStore, "transient-store", false, "Enable transient container storage")
|
pFlags.BoolVar(&podmanConfig.TransientStore, "transient-store", false, "Enable transient container storage")
|
||||||
|
|
||||||
runtimeFlagName := "runtime"
|
runtimeFlagName := "runtime"
|
||||||
|
@ -78,6 +78,12 @@ Identity value resolution precedence:
|
|||||||
- `containers.conf`
|
- `containers.conf`
|
||||||
Remote connections use local containers.conf for default.
|
Remote connections use local containers.conf for default.
|
||||||
|
|
||||||
|
#### **--imagestore**=*path*
|
||||||
|
|
||||||
|
Path of the imagestore where images are stored. By default, the storage library stores all the images in the graphroot but if an imagestore is provided, then the storage library will store newly pulled images in the provided imagestore and keep using the graphroot for everything else. If the user is using the overlay driver, then the images which were already part of the graphroot will still be accessible.
|
||||||
|
|
||||||
|
This will override *imagestore* option in `containers-storage.conf(5)`, refer to `containers-storage.conf(5)` for more details.
|
||||||
|
|
||||||
#### **--log-level**=*level*
|
#### **--log-level**=*level*
|
||||||
|
|
||||||
Log messages at and above specified level: debug, info, warn, error, fatal or panic (default: "warn")
|
Log messages at and above specified level: debug, info, warn, error, fatal or panic (default: "warn")
|
||||||
|
@ -126,6 +126,18 @@ func WithTransientStore(transientStore bool) RuntimeOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithImageStore(imageStore string) RuntimeOption {
|
||||||
|
return func(rt *Runtime) error {
|
||||||
|
if rt.valid {
|
||||||
|
return define.ErrRuntimeFinalized
|
||||||
|
}
|
||||||
|
|
||||||
|
rt.storageConfig.ImageStore = imageStore
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithSignaturePolicy specifies the path of a file which decides how trust is
|
// WithSignaturePolicy specifies the path of a file which decides how trust is
|
||||||
// managed for images we've pulled.
|
// managed for images we've pulled.
|
||||||
// If this is not specified, the system default configuration will be used
|
// If this is not specified, the system default configuration will be used
|
||||||
|
@ -52,6 +52,7 @@ type PodmanConfig struct {
|
|||||||
URI string // URI to RESTful API Service
|
URI string // URI to RESTful API Service
|
||||||
|
|
||||||
Runroot string
|
Runroot string
|
||||||
|
ImageStore string
|
||||||
StorageDriver string
|
StorageDriver string
|
||||||
StorageOpts []string
|
StorageOpts []string
|
||||||
SSHMode string
|
SSHMode string
|
||||||
|
@ -153,6 +153,10 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo
|
|||||||
storageSet = true
|
storageSet = true
|
||||||
storageOpts.RunRoot = cfg.Runroot
|
storageOpts.RunRoot = cfg.Runroot
|
||||||
}
|
}
|
||||||
|
if fs.Changed("imagestore") {
|
||||||
|
storageOpts.ImageStore = cfg.ImageStore
|
||||||
|
options = append(options, libpod.WithImageStore(cfg.ImageStore))
|
||||||
|
}
|
||||||
if len(storageOpts.RunRoot) > 50 {
|
if len(storageOpts.RunRoot) > 50 {
|
||||||
return nil, errors.New("the specified runroot is longer than 50 characters")
|
return nil, errors.New("the specified runroot is longer than 50 characters")
|
||||||
}
|
}
|
||||||
|
@ -285,6 +285,9 @@ func CreateExitCommandArgs(storageConfig storageTypes.StoreOptions, config *conf
|
|||||||
"--db-backend", config.Engine.DBBackend,
|
"--db-backend", config.Engine.DBBackend,
|
||||||
fmt.Sprintf("--transient-store=%t", storageConfig.TransientStore),
|
fmt.Sprintf("--transient-store=%t", storageConfig.TransientStore),
|
||||||
}
|
}
|
||||||
|
if storageConfig.ImageStore != "" {
|
||||||
|
command = append(command, []string{"--imagestore", storageConfig.ImageStore}...)
|
||||||
|
}
|
||||||
if config.Engine.OCIRuntime != "" {
|
if config.Engine.OCIRuntime != "" {
|
||||||
command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
|
command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,73 @@ var _ = Describe("Podman pull", func() {
|
|||||||
Expect(session).Should(Exit(0))
|
Expect(session).Should(Exit(0))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("podman pull and run on split imagestore", func() {
|
||||||
|
SkipIfRemote("podman-remote does not support setting external imagestore")
|
||||||
|
imgName := "splitstoretest"
|
||||||
|
|
||||||
|
// Make alpine write-able
|
||||||
|
session := podmanTest.Podman([]string{"build", "--pull=never", "--tag", imgName, "build/basicalpine"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(Exit(0))
|
||||||
|
|
||||||
|
tmpDir := filepath.Join(podmanTest.TempDir, "splitstore")
|
||||||
|
outfile := filepath.Join(podmanTest.TempDir, "image.tar")
|
||||||
|
|
||||||
|
save := podmanTest.Podman([]string{"save", "-o", outfile, "--format", "oci-archive", imgName})
|
||||||
|
save.WaitWithDefaultTimeout()
|
||||||
|
Expect(save).Should(Exit(0))
|
||||||
|
|
||||||
|
rmi := podmanTest.Podman([]string{"rmi", imgName})
|
||||||
|
rmi.WaitWithDefaultTimeout()
|
||||||
|
Expect(rmi).Should(Exit(0))
|
||||||
|
|
||||||
|
// load to splitstore
|
||||||
|
result := podmanTest.Podman([]string{"load", "--imagestore", tmpDir, "-q", "-i", outfile})
|
||||||
|
result.WaitWithDefaultTimeout()
|
||||||
|
Expect(result).Should(Exit(0))
|
||||||
|
|
||||||
|
// tag busybox to busybox-test in graphroot since we can delete readonly busybox
|
||||||
|
session = podmanTest.Podman([]string{"tag", "quay.io/libpod/busybox:latest", "busybox-test"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(Exit(0))
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"images", "--imagestore", tmpDir})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(Exit(0))
|
||||||
|
Expect(session.OutputToString()).To(ContainSubstring(imgName))
|
||||||
|
Expect(session.OutputToString()).To(ContainSubstring("busybox-test"))
|
||||||
|
|
||||||
|
// Test deleting image in graphroot even when `--imagestore` is set
|
||||||
|
session = podmanTest.Podman([]string{"rmi", "--imagestore", tmpDir, "busybox-test"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(Exit(0))
|
||||||
|
|
||||||
|
// Images without --imagestore should not contain alpine
|
||||||
|
session = podmanTest.Podman([]string{"images"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(Exit(0))
|
||||||
|
Expect(session.OutputToString()).To(Not(ContainSubstring(imgName)))
|
||||||
|
|
||||||
|
// Set `imagestore` in `storage.conf` and container should run.
|
||||||
|
configPath := filepath.Join(podmanTest.TempDir, ".config", "containers", "storage.conf")
|
||||||
|
os.Setenv("CONTAINERS_STORAGE_CONF", configPath)
|
||||||
|
defer func() {
|
||||||
|
os.Unsetenv("CONTAINERS_STORAGE_CONF")
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = os.MkdirAll(filepath.Dir(configPath), os.ModePerm)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
storageConf := []byte(fmt.Sprintf("[storage]\nimagestore=\"%s\"", tmpDir))
|
||||||
|
err = os.WriteFile(configPath, storageConf, os.ModePerm)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"run", "--name", "test", "--rm",
|
||||||
|
imgName, "echo", "helloworld"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(Exit(0))
|
||||||
|
Expect(session.OutputToString()).To(ContainSubstring("helloworld"))
|
||||||
|
})
|
||||||
|
|
||||||
It("podman pull by digest", func() {
|
It("podman pull by digest", func() {
|
||||||
session := podmanTest.Podman([]string{"pull", "quay.io/libpod/testdigest_v2s2@sha256:755f4d90b3716e2bf57060d249e2cd61c9ac089b1233465c5c2cb2d7ee550fdb"})
|
session := podmanTest.Podman([]string{"pull", "quay.io/libpod/testdigest_v2s2@sha256:755f4d90b3716e2bf57060d249e2cd61c9ac089b1233465c5c2cb2d7ee550fdb"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
|
Reference in New Issue
Block a user