[lxc-devel] [lxd/master] Tweak to automated snapshots
stgraber on Github
lxc-bot at linuxcontainers.org
Wed Nov 21 21:46:37 UTC 2018
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20181121/59c28089/attachment.bin>
-------------- next part --------------
From 7f3fac7d14e94a834ce652df514443ceea326dcb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 21 Nov 2018 15:53:56 -0500
Subject: [PATCH 1/4] lxd/snapshots: Always use snap%d as pattern
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
doc/containers.md | 2 +-
lxd/container.go | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/containers.md b/doc/containers.md
index a9d0014b32..1ffa09448d 100644
--- a/doc/containers.md
+++ b/doc/containers.md
@@ -79,7 +79,7 @@ security.syscalls.blacklist\_default | boolean | true | no
security.syscalls.whitelist | string | - | no | container\_syscall\_filtering | A '\n' separated list of syscalls to whitelist (mutually exclusive with security.syscalls.blacklist\*)
snapshots.schedule | string | - | no | snapshot\_scheduling | Cron expression (`<minute> <hour> <dom> <month> <dow>`)
snapshots.schedule.stopped | bool | false | no | snapshot\_scheduling | Controls whether or not stopped containers are to be snapshoted automatically
-snapshots.pattern | string | auto-snapshot | no | snapshot\_scheduling | Pongo2 template string which represents the snapshot name
+snapshots.pattern | string | snap%d | no | snapshot\_scheduling | Pongo2 template string which represents the snapshot name (used for scheduled snapshots and unnamed snapshots)
user.\* | string | - | n/a | - | Free form user key/value storage (can be used in search)
The following volatile keys are currently internally used by LXD:
diff --git a/lxd/container.go b/lxd/container.go
index efab279ddc..5a3a9b48ca 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -1639,7 +1639,7 @@ func autoCreateContainerSnapshots(ctx context.Context, d *Daemon) error {
ch := make(chan struct{})
go func() {
- snapshotName, err := containerDetermineNextSnapshotName(d, c, "auto-snapshot")
+ snapshotName, err := containerDetermineNextSnapshotName(d, c, "snap%d")
if err != nil {
logger.Error("Error retrieving next snapshot name", log.Ctx{"err": err,
"container": c})
From 0b47a151216f5b3d9899fbf5149bde8edeaea97c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 21 Nov 2018 15:55:36 -0500
Subject: [PATCH 2/4] lxd/snapshots: Validate pattern
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
shared/container.go | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/shared/container.go b/shared/container.go
index 47f2f75a88..cbcd2497cd 100644
--- a/shared/container.go
+++ b/shared/container.go
@@ -5,6 +5,9 @@ import (
"regexp"
"strconv"
"strings"
+
+ "github.com/pkg/errors"
+ "gopkg.in/robfig/cron.v2"
)
type ContainerAction string
@@ -263,7 +266,22 @@ var KnownContainerConfigKeys = map[string]func(value string) error{
"security.syscalls.blacklist": IsAny,
"security.syscalls.whitelist": IsAny,
- "snapshots.schedule": IsAny,
+ "snapshots.schedule": func(value string) error {
+ if value == "" {
+ return nil
+ }
+
+ if len(strings.Split(value, " ")) != 5 {
+ return fmt.Errorf("Schedule must be of the form: <minute> <hour> <day-of-month> <month> <day-of-week>")
+ }
+
+ _, err := cron.Parse(fmt.Sprintf("* %s", value))
+ if err != nil {
+ return errors.Wrap(err, "Error parsing schedule")
+ }
+
+ return nil
+ },
"snapshots.schedule.stopped": IsBool,
"snapshots.pattern": IsAny,
From 58ec0b53bb94be59aa72ae2e39416771f2fe199f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 21 Nov 2018 15:56:31 -0500
Subject: [PATCH 3/4] lxd/snapshots: Don't run on startup
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/container.go | 2 --
1 file changed, 2 deletions(-)
diff --git a/lxd/container.go b/lxd/container.go
index 5a3a9b48ca..9eb91043a4 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -1558,8 +1558,6 @@ func autoCreateContainerSnapshotsTask(d *Daemon) (task.Func, task.Schedule) {
logger.Info("Done creating scheduled container snapshots")
}
- f(context.Background())
-
first := true
schedule := func() (time.Duration, error) {
interval := time.Minute
From d14578dd06122c09b051cbafe8de816a94a1a5f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 21 Nov 2018 15:56:48 -0500
Subject: [PATCH 4/4] lxd/snapshots: Only spawn operation when needed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxd/container.go | 127 +++++++++++++++++++++--------------------------
1 file changed, 56 insertions(+), 71 deletions(-)
diff --git a/lxd/container.go b/lxd/container.go
index 9eb91043a4..272adf4068 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -1539,8 +1539,54 @@ func containerCompareSnapshots(source container, target container) ([]container,
func autoCreateContainerSnapshotsTask(d *Daemon) (task.Func, task.Schedule) {
f := func(ctx context.Context) {
+ // Load all local containers
+ allContainers, err := containerLoadNodeAll(d.State())
+ if err != nil {
+ logger.Error("Failed to load containers for scheduled snapshots", log.Ctx{"err": err})
+ }
+
+ // Figure out which need snapshotting (if any)
+ containers := []container{}
+ for _, c := range allContainers {
+ logger.Debugf("considering: %v", c.Name())
+ schedule := c.LocalConfig()["snapshots.schedule"]
+
+ if schedule == "" {
+ logger.Debugf("skipping, empty schedule")
+ continue
+ }
+
+ // Extend our schedule to one that is accepted by the used cron parser
+ sched, err := cron.Parse(fmt.Sprintf("* %s", schedule))
+ if err != nil {
+ logger.Debugf("skipping, parsing error: %v", err)
+ continue
+ }
+
+ // Check if it's time to snapshot
+ now := time.Now()
+ next := sched.Next(now)
+
+ if now.Add(time.Minute).Before(next) {
+ logger.Debugf("skipping, %v is after %v", now.Add(time.Minute), next)
+ continue
+ }
+
+ // Check if the container is running
+ if !shared.IsTrue(c.LocalConfig()["snapshots.schedule.stopped"]) && !c.IsRunning() {
+ logger.Debugf("skipping, container is stopped")
+ continue
+ }
+
+ containers = append(containers, c)
+ }
+
+ if len(containers) == 0 {
+ return
+ }
+
opRun := func(op *operation) error {
- return autoCreateContainerSnapshots(ctx, d)
+ return autoCreateContainerSnapshots(ctx, d, containers)
}
op, err := operationCreate(d.cluster, "", operationClassTask, db.OperationSnapshotCreate, nil, nil, opRun, nil, nil)
@@ -1573,82 +1619,21 @@ func autoCreateContainerSnapshotsTask(d *Daemon) (task.Func, task.Schedule) {
return f, schedule
}
-func autoCreateContainerSnapshots(ctx context.Context, d *Daemon) error {
- containers, err := d.cluster.ContainersNodeList(db.CTypeRegular)
- if err != nil {
- return errors.Wrap(err, "Unable to retrieve the list of containers")
- }
-
- for _, container := range containers {
- cId, err := d.cluster.ContainerID(container)
- if err != nil {
- return errors.Wrap(err, "Error retrieving container ID")
- }
-
- c, err := containerLoadById(d.State(), cId)
- if err != nil {
- return errors.Wrap(err, "Error loading container")
- }
-
- schedule := c.LocalConfig()["snapshots.schedule"]
-
- if schedule == "" || (!shared.IsTrue(c.LocalConfig()["snapshots.schedule.stopped"]) && c.IsRunning()) {
- continue
- }
-
- if len(strings.Split(schedule, " ")) != 5 {
- logger.Error("Schedule must be of the form: <minute> <hour> <day-of-month> <month> <day-of-week>")
- continue
- }
-
- // Extend our schedule to one that is accepted by the used cron parser
- sched, err := cron.Parse(fmt.Sprintf("* %s", schedule))
- if err != nil {
- logger.Error("Error parsing schedule", log.Ctx{"err": err,
- "container": container, "schedule": schedule})
- continue
- }
-
- now := time.Now()
- next := sched.Next(now).Format("2006-01-02T15:04")
- skip := false
-
- snapshots, err := c.Snapshots()
- if err != nil {
- logger.Error("Error retrieving snapshots", log.Ctx{"err": err,
- "container": container})
- continue
- }
-
- for _, snap := range snapshots {
- // Check whether the container has been snapshotted within (at least)
- // the last minute.
- if snap.CreationDate().Format("2006-01-02T15:04") == next {
- skip = true
- break
- }
- }
-
- // Skip snapshotting if the container has been snapshotted within (at
- // least) the last minute, or it's not time yet.
- if skip || now.Format("2006-01-02T15:04") != next {
- continue
- }
-
- ch := make(chan struct{})
+func autoCreateContainerSnapshots(ctx context.Context, d *Daemon, containers []container) error {
+ // Make the snapshots
+ for _, c := range containers {
+ ch := make(chan error)
go func() {
snapshotName, err := containerDetermineNextSnapshotName(d, c, "snap%d")
if err != nil {
- logger.Error("Error retrieving next snapshot name", log.Ctx{"err": err,
- "container": c})
- ch <- struct{}{}
+ logger.Error("Error retrieving next snapshot name", log.Ctx{"err": err, "container": c})
+ ch <- nil
return
}
snapshotName = fmt.Sprintf("%s%s%s", c.Name(), shared.SnapshotDelimiter, snapshotName)
args := db.ContainerArgs{
- Project: c.Project(),
Architecture: c.Architecture(),
Config: c.LocalConfig(),
Ctype: db.CTypeSnapshot,
@@ -1656,16 +1641,16 @@ func autoCreateContainerSnapshots(ctx context.Context, d *Daemon) error {
Ephemeral: c.IsEphemeral(),
Name: snapshotName,
Profiles: c.Profiles(),
+ Project: c.Project(),
Stateful: false,
}
_, err = containerCreateAsSnapshot(d.State(), args, c)
if err != nil {
- logger.Error("Error creating snapshots", log.Ctx{"err": err,
- "container": c})
+ logger.Error("Error creating snapshots", log.Ctx{"err": err, "container": c})
}
- ch <- struct{}{}
+ ch <- nil
}()
select {
case <-ctx.Done():
More information about the lxc-devel
mailing list