Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support transient store mode #16371

Merged
merged 6 commits into from
Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/podman/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,8 @@ func rootFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) {
pFlags.StringVar(&podmanConfig.Runroot, runrootFlagName, "", "Path to the 'run directory' where all state information is stored")
_ = cmd.RegisterFlagCompletionFunc(runrootFlagName, completion.AutocompleteDefault)

pFlags.BoolVar(&podmanConfig.TransientStore, "transient-store", false, "Enable transient container storage")
vrothberg marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems to override the setting in storage.conf.

Shouldn't transient_store = true in storage.conf always set the transient flag?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to be able to pass this in the conmon cleanup commands to get the exact same setup as the initial podman command was run as, similar to the --rundir=... args, etc. That can't rely on the global setting.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed it makes sense to have the CLI option, but when I create a new container and the global setting is set I'd expect it to affect the newly created container unless I specify --transient=false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. right!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, this was a bit busted before, but should work now.

However, if you specify transient_store=true in the config file that currently only affects the default for rootful containers. It seems that there is a bunch of special code that defines the default for rootless containers. This might make sense, but I'm not sure, opinions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel strongly about either way. I think it's a fairly niche feature (for now) but it's worth pointing users to storage.conf.


runtimeFlagName := "runtime"
pFlags.StringVar(&podmanConfig.RuntimePath, runtimeFlagName, podmanConfig.ContainersConfDefaultsRO.Engine.OCIRuntime, "Path to the OCI-compatible binary used to run containers.")
_ = cmd.RegisterFlagCompletionFunc(runtimeFlagName, completion.AutocompleteDefault)
Expand Down
9 changes: 6 additions & 3 deletions cmd/podman/system/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func init() {
flags := pruneCommand.Flags()
flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation. The default is false")
flags.BoolVarP(&pruneOptions.All, "all", "a", false, "Remove all unused data")
flags.BoolVar(&pruneOptions.External, "external", false, "Remove container data in storage not controlled by podman")
flags.BoolVar(&pruneOptions.Volume, "volumes", false, "Prune volumes")
filterFlagName := "filter"
flags.StringArrayVar(&filters, filterFlagName, []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
Expand All @@ -55,8 +56,8 @@ func init() {

func prune(cmd *cobra.Command, args []string) error {
var err error
// Prompt for confirmation if --force is not set
if !force {
// Prompt for confirmation if --force is not set, unless --external
if !force && !pruneOptions.External {
reader := bufio.NewReader(os.Stdin)
volumeString := ""
if pruneOptions.Volume {
Expand Down Expand Up @@ -113,7 +114,9 @@ func prune(cmd *cobra.Command, args []string) error {
return err
}

fmt.Printf("Total reclaimed space: %s\n", units.HumanSize((float64)(response.ReclaimedSpace)))
if !pruneOptions.External {
fmt.Printf("Total reclaimed space: %s\n", units.HumanSize((float64)(response.ReclaimedSpace)))
}
return nil
}

Expand Down
4 changes: 3 additions & 1 deletion docs/source/markdown/podman-info.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ store:
imageStore:
number: 5
runRoot: /run/user/3267/containers
transientStore: false
volumePath: /home/dwalsh/.local/share/containers/storage/volumes
version:
APIVersion: 4.0.0
Expand Down Expand Up @@ -263,7 +264,8 @@ $ podman info --format json
"number": 5
},
"runRoot": "/run/user/3267/containers",
"volumePath": "/home/dwalsh/.local/share/containers/storage/volumes"
"volumePath": "/home/dwalsh/.local/share/containers/storage/volumes",
"transientStore": false
},
"registries": {
"search": [
Expand Down
9 changes: 9 additions & 0 deletions docs/source/markdown/podman-system-prune.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ By default, volumes are not removed to prevent important data from being deleted

Recursively remove all unused pods, containers, images, networks, and volume data. (Maximum 50 iterations.)

#### **--external**

Removes all leftover container storage files from local storage that are not managed by podman. In normal circumstances no such data should exist, but in case of an unclean shutdown the podman database may be corrupted and cause his.

However, when using transient storage mode, the podman database does not persist. This means containers can will leave the writable layers on disk after a reboot. If you use transient store
it it recommended that you run **podman system prune --external** once some time after each boot.

This option is incompatible with **--all** and **--filter** and drops the default behaviour of removing unused resources.

#### **--filter**=*filters*

Provide filter values.
Expand Down
22 changes: 15 additions & 7 deletions docs/source/markdown/podman.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,14 @@ environment variable is set, the **--remote** option defaults to true.
#### **--root**=*value*

Storage root dir in which data, including images, is stored (default: "/var/lib/containers/storage" for UID 0, "$HOME/.local/share/containers/storage" for other users).
Default root dir configured in `/etc/containers/storage.conf`.
Default root dir configured in `containers-storage.conf(5)`.

Overriding this option will cause the *storage-opt* settings in /etc/containers/storage.conf to be ignored. The user must specify additional options via the `--storage-opt` flag.
Overriding this option will cause the *storage-opt* settings in `containers-storage.conf(5)` to be ignored. The user must specify additional options via the `--storage-opt` flag.

#### **--runroot**=*value*

Storage state directory where all state information is stored (default: "/run/containers/storage" for UID 0, "/run/user/$UID/run" for other users).
Default state dir configured in `/etc/containers/storage.conf`.
Default state dir configured in `containers-storage.conf(5)`.

#### **--runtime**=*value*

Expand All @@ -141,14 +141,14 @@ to use the installed ssh binary and config file declared in containers.conf.

#### **--storage-driver**=*value*

Storage driver. The default storage driver for UID 0 is configured in /etc/containers/storage.conf (`$HOME/.config/containers/storage.conf` in rootless mode), and is *vfs* for non-root users when *fuse-overlayfs* is not available. The `STORAGE_DRIVER` environment variable overrides the default. The --storage-driver specified driver overrides all.
Storage driver. The default storage driver for UID 0 is configured in `containers-storage.conf(5)` in rootless mode), and is *vfs* for non-root users when *fuse-overlayfs* is not available. The `STORAGE_DRIVER` environment variable overrides the default. The --storage-driver specified driver overrides all.

Overriding this option will cause the *storage-opt* settings in /etc/containers/storage.conf to be ignored. The user must
Overriding this option will cause the *storage-opt* settings in `containers-storage.conf(5)` to be ignored. The user must
specify additional options via the `--storage-opt` flag.

#### **--storage-opt**=*value*

Storage driver option, Default storage driver options are configured in /etc/containers/storage.conf (`$HOME/.config/containers/storage.conf` in rootless mode). The `STORAGE_OPTS` environment variable overrides the default. The --storage-opt specified options overrides all. If you specify --storage-opt="", no storage options will be used.
Storage driver option, Default storage driver options are configured in `containers-storage.conf(5)`. The `STORAGE_OPTS` environment variable overrides the default. The --storage-opt specified options overrides all. If you specify --storage-opt="", no storage options will be used.

#### **--syslog**

Expand All @@ -162,6 +162,14 @@ Path to the tmp directory, for libpod runtime content. Defaults to `$XDG_RUNTIME

NOTE --tmpdir is not used for the temporary storage of downloaded images. Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`.

#### **--transient-store**

Enables a global transient storaga mode where all container metadata is stored on non-persistant media (i.e. in the location specified by `--runroot`).
This mode allows starting containers faster, as well as guaranteeing a fresh state on boot in case of unclean shutdowns or other problems. However
it is not compabible with a traditional model where containers persist across reboots.

Default value for this is configured in `containers-storage.conf(5)`.

#### **--url**=*value*
URL to access Podman service (default from `containers.conf`, rootless `unix://run/user/$UID/podman/podman.sock` or as root `unix://run/podman/podman.sock`).
Setting this option will switch the **--remote** option to true.
Expand Down Expand Up @@ -426,7 +434,7 @@ Currently slirp4netns or pasta is required to be installed to create a network
device, otherwise rootless containers need to run in the network namespace of
the host.

In certain environments like HPC (High Performance Computing), users cannot take advantage of the additional UIDs and GIDs from the /etc/subuid and /etc/subgid systems. However, in this environment, rootless Podman can operate with a single UID. To make this work, set the `ignore_chown_errors` option in the /etc/containers/storage.conf or in ~/.config/containers/storage.conf files. This option tells Podman when pulling an image to ignore chown errors when attempting to change a file in a container image to match the non-root UID in the image. This means all files get saved as the user's UID. Note this could cause issues when running the container.
In certain environments like HPC (High Performance Computing), users cannot take advantage of the additional UIDs and GIDs from the /etc/subuid and /etc/subgid systems. However, in this environment, rootless Podman can operate with a single UID. To make this work, set the `ignore_chown_errors` option in the `containers-storage.conf(5)` file. This option tells Podman when pulling an image to ignore chown errors when attempting to change a file in a container image to match the non-root UID in the image. This means all files get saved as the user's UID. Note this could cause issues when running the container.

### **NOTE:** Unsupported file systems in rootless mode

Expand Down
1 change: 1 addition & 0 deletions libpod/define/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ type StoreInfo struct {
ImageStore ImageStore `json:"imageStore"`
RunRoot string `json:"runRoot"`
VolumePath string `json:"volumePath"`
TransientStore bool `json:"transientStore"`
}

// ImageStore describes the image store. Right now only the number
Expand Down
1 change: 1 addition & 0 deletions libpod/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ func (r *Runtime) storeInfo() (*define.StoreInfo, error) {
GraphOptions: nil,
VolumePath: r.config.Engine.VolumePath,
ConfigFile: configFile,
TransientStore: r.store.TransientStore(),
}

graphOptions := map[string]interface{}{}
Expand Down
12 changes: 12 additions & 0 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ func WithStorageConfig(config storage.StoreOptions) RuntimeOption {
}
}

func WithTransientStore(transientStore bool) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return define.ErrRuntimeFinalized
}

rt.storageConfig.TransientStore = transientStore

return nil
}
}

// WithSignaturePolicy specifies the path of a file which decides how trust is
// managed for images we've pulled.
// If this is not specified, the system default configuration will be used
Expand Down
32 changes: 15 additions & 17 deletions libpod/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,11 @@ func makeRuntime(runtime *Runtime) (retErr error) {
}
}

// Create the TmpDir if needed
if err := os.MkdirAll(runtime.config.Engine.TmpDir, 0751); err != nil {
Copy link
Collaborator

@flouthoc flouthoc Nov 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be 755 or if 751 is intentional maybe a comment just above could help understand why only execute is given without read to others.

Suggested change
if err := os.MkdirAll(runtime.config.Engine.TmpDir, 0751); err != nil {
if err := os.MkdirAll(runtime.config.Engine.TmpDir, 0755); err != nil {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0751 would give others the ability to reach files (search) under the tree, but not able to list it. I do not know which is correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have an opinion on this, I just moved the existing mkdir code earlier in the function because I need to reference TmpDir. The existing code actually had both 751 and 755, so I deleted both, but the 751 is the one that would have "won" before, so I kept that to make it not change behaviour.

I'm fine with changing the behavior to 755`, but maybe that should be a separate PR with separate arguments.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0751 is what's being used at the moment, so I think we should keep it that way in this PR. Changing the default can be done in a separate change if needed.

return fmt.Errorf("creating runtime temporary files directory: %w", err)
}

// Set up the state.
//
// TODO - if we further break out the state implementation into
Expand All @@ -334,7 +339,11 @@ func makeRuntime(runtime *Runtime) (retErr error) {
case config.SQLiteStateStore:
return fmt.Errorf("SQLite state is currently disabled: %w", define.ErrInvalidArg)
case config.BoltDBStateStore:
dbPath := filepath.Join(runtime.config.Engine.StaticDir, "bolt_state.db")
baseDir := runtime.config.Engine.StaticDir
if runtime.storageConfig.TransientStore {
baseDir = runtime.config.Engine.TmpDir
}
dbPath := filepath.Join(baseDir, "bolt_state.db")

state, err := NewBoltState(dbPath, runtime)
if err != nil {
Expand Down Expand Up @@ -391,6 +400,7 @@ func makeRuntime(runtime *Runtime) (retErr error) {
logrus.Debugf("Using static dir %s", runtime.config.Engine.StaticDir)
logrus.Debugf("Using tmp dir %s", runtime.config.Engine.TmpDir)
logrus.Debugf("Using volume path %s", runtime.config.Engine.VolumePath)
logrus.Debugf("Using transient store: %v", runtime.storageConfig.TransientStore)

// Validate our config against the database, now that we've set our
// final storage configuration
Expand Down Expand Up @@ -458,14 +468,6 @@ func makeRuntime(runtime *Runtime) (retErr error) {
}
runtime.imageContext.SignaturePolicyPath = runtime.config.Engine.SignaturePolicyPath

// Create the tmpDir
if err := os.MkdirAll(runtime.config.Engine.TmpDir, 0751); err != nil {
// The directory is allowed to exist
if !errors.Is(err, os.ErrExist) {
return fmt.Errorf("creating tmpdir: %w", err)
}
}

// Get us at least one working OCI runtime.
runtime.ociRuntimes = make(map[string]OCIRuntime)

Expand Down Expand Up @@ -516,14 +518,6 @@ func makeRuntime(runtime *Runtime) (retErr error) {
return fmt.Errorf("no default OCI runtime was configured: %w", define.ErrInvalidArg)
}

// Make the per-boot files directory if it does not exist
if err := os.MkdirAll(runtime.config.Engine.TmpDir, 0755); err != nil {
// The directory is allowed to exist
if !errors.Is(err, os.ErrExist) {
return fmt.Errorf("creating runtime temporary files directory: %w", err)
}
}

// the store is only set up when we are in the userns so we do the same for the network interface
if !needsUserns {
netBackend, netInterface, err := network.NetworkBackend(runtime.store, runtime.config, runtime.syslog)
Expand Down Expand Up @@ -965,6 +959,10 @@ func (r *Runtime) StorageConfig() storage.StoreOptions {
return r.storageConfig
}

func (r *Runtime) GarbageCollect() error {
return r.store.GarbageCollect()
}

// RunRoot retrieves the current c/storage temporary directory in use by Libpod.
func (r *Runtime) RunRoot() string {
if r.store == nil {
Expand Down
12 changes: 7 additions & 5 deletions pkg/api/handlers/libpod/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ func SystemPrune(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)

query := struct {
All bool `schema:"all"`
Volumes bool `schema:"volumes"`
All bool `schema:"all"`
Volumes bool `schema:"volumes"`
External bool `schema:"external"`
}{}

if err := decoder.Decode(&query, r.URL.Query()); err != nil {
Expand All @@ -38,9 +39,10 @@ func SystemPrune(w http.ResponseWriter, r *http.Request) {
containerEngine := abi.ContainerEngine{Libpod: runtime}

pruneOptions := entities.SystemPruneOptions{
All: query.All,
Volume: query.Volumes,
Filters: *filterMap,
All: query.All,
Volume: query.Volumes,
Filters: *filterMap,
External: query.External,
}
report, err := containerEngine.SystemPrune(r.Context(), pruneOptions)
if err != nil {
Expand Down
7 changes: 4 additions & 3 deletions pkg/bindings/system/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ type EventsOptions struct {
//
//go:generate go run ../generator/generator.go PruneOptions
type PruneOptions struct {
All *bool
Filters map[string][]string
Volumes *bool
All *bool
Filters map[string][]string
Volumes *bool
External *bool
}

// VersionOptions are optional options for getting version info
Expand Down
15 changes: 15 additions & 0 deletions pkg/bindings/system/types_prune_options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions pkg/domain/entities/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ type PodmanConfig struct {
Trace bool // Hidden: Trace execution
URI string // URI to RESTful API Service

Runroot string
StorageDriver string
StorageOpts []string
SSHMode string
MachineMode bool
Runroot string
StorageDriver string
StorageOpts []string
SSHMode string
MachineMode bool
TransientStore bool
}
7 changes: 4 additions & 3 deletions pkg/domain/entities/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ type ServiceOptions struct {

// SystemPruneOptions provides options to prune system.
type SystemPruneOptions struct {
All bool
Volume bool
Filters map[string][]string `json:"filters" schema:"filters"`
All bool
Volume bool
Filters map[string][]string `json:"filters" schema:"filters"`
External bool
}

// SystemPruneReport provides report after system prune is executed.
Expand Down
12 changes: 12 additions & 0 deletions pkg/domain/infra/abi/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,18 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, noMoveProcess bool)
// SystemPrune removes unused data from the system. Pruning pods, containers, networks, volumes and images.
func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) {
var systemPruneReport = new(entities.SystemPruneReport)

if options.External {
if options.All || options.Volume || len(options.Filters) > 0 {
return nil, fmt.Errorf("system prune --external cannot be combined with other options")
}
err := ic.Libpod.GarbageCollect()
if err != nil {
return nil, err
}
return systemPruneReport, nil
}

filters := []string{}
for k, v := range options.Filters {
filters = append(filters, fmt.Sprintf("%s=%s", k, v[0]))
Expand Down
3 changes: 3 additions & 0 deletions pkg/domain/infra/runtime_libpod.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo
storageOpts.GraphDriverOptions = cfg.StorageOpts
}
}
if fs.Changed("transient-store") {
options = append(options, libpod.WithTransientStore(cfg.TransientStore))
}
if opts.migrate {
options = append(options, libpod.WithMigrate())
if opts.name != "" {
Expand Down
2 changes: 1 addition & 1 deletion pkg/domain/infra/tunnel/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, noMoveProcess bool)

// SystemPrune prunes unused data from the system.
func (ic *ContainerEngine) SystemPrune(ctx context.Context, opts entities.SystemPruneOptions) (*entities.SystemPruneReport, error) {
options := new(system.PruneOptions).WithAll(opts.All).WithVolumes(opts.Volume).WithFilters(opts.Filters)
options := new(system.PruneOptions).WithAll(opts.All).WithVolumes(opts.Volume).WithFilters(opts.Filters).WithExternal(opts.External)
return system.Prune(ic.ClientCtx, options)
}

Expand Down
1 change: 1 addition & 0 deletions pkg/specgenutil/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ func CreateExitCommandArgs(storageConfig storageTypes.StoreOptions, config *conf
"--network-config-dir", config.Network.NetworkConfigDir,
"--network-backend", config.Network.NetworkBackend,
"--volumepath", config.Engine.VolumePath,
fmt.Sprintf("--transient-store=%t", storageConfig.TransientStore),
}
if config.Engine.OCIRuntime != "" {
command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
Expand Down
Loading