`docker network prune` prunes unused networks, including overlay ones.
`docker system prune` also prunes unused networks.
Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
| ... | ... |
@@ -17,4 +17,5 @@ type Backend interface {
|
| 17 | 17 |
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error |
| 18 | 18 |
DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error |
| 19 | 19 |
DeleteNetwork(name string) error |
| 20 |
+ NetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) |
|
| 20 | 21 |
} |
| ... | ... |
@@ -37,6 +37,7 @@ func (r *networkRouter) initRoutes() {
|
| 37 | 37 |
router.NewPostRoute("/networks/create", r.postNetworkCreate),
|
| 38 | 38 |
router.NewPostRoute("/networks/{id:.*}/connect", r.postNetworkConnect),
|
| 39 | 39 |
router.NewPostRoute("/networks/{id:.*}/disconnect", r.postNetworkDisconnect),
|
| 40 |
+ router.NewPostRoute("/networks/prune", r.postNetworksPrune),
|
|
| 40 | 41 |
// DELETE |
| 41 | 42 |
router.NewDeleteRoute("/networks/{id:.*}", r.deleteNetwork),
|
| 42 | 43 |
} |
| ... | ... |
@@ -274,3 +274,24 @@ func buildEndpointResource(id string, name string, info libnetwork.EndpointInfo) |
| 274 | 274 |
} |
| 275 | 275 |
return er |
| 276 | 276 |
} |
| 277 |
+ |
|
| 278 |
+func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
|
| 279 |
+ if err := httputils.ParseForm(r); err != nil {
|
|
| 280 |
+ return err |
|
| 281 |
+ } |
|
| 282 |
+ |
|
| 283 |
+ if err := httputils.CheckForJSON(r); err != nil {
|
|
| 284 |
+ return err |
|
| 285 |
+ } |
|
| 286 |
+ |
|
| 287 |
+ var cfg types.NetworksPruneConfig |
|
| 288 |
+ if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
|
|
| 289 |
+ return err |
|
| 290 |
+ } |
|
| 291 |
+ |
|
| 292 |
+ pruneReport, err := n.backend.NetworksPrune(&cfg) |
|
| 293 |
+ if err != nil {
|
|
| 294 |
+ return err |
|
| 295 |
+ } |
|
| 296 |
+ return httputils.WriteJSON(w, http.StatusOK, pruneReport) |
|
| 297 |
+} |
| ... | ... |
@@ -522,6 +522,11 @@ type ContainersPruneConfig struct {
|
| 522 | 522 |
type VolumesPruneConfig struct {
|
| 523 | 523 |
} |
| 524 | 524 |
|
| 525 |
+// NetworksPruneConfig contains the configuration for Remote API: |
|
| 526 |
+// POST "/networks/prune" |
|
| 527 |
+type NetworksPruneConfig struct {
|
|
| 528 |
+} |
|
| 529 |
+ |
|
| 525 | 530 |
// ContainersPruneReport contains the response for Remote API: |
| 526 | 531 |
// POST "/containers/prune" |
| 527 | 532 |
type ContainersPruneReport struct {
|
| ... | ... |
@@ -542,3 +547,9 @@ type ImagesPruneReport struct {
|
| 542 | 542 |
ImagesDeleted []ImageDelete |
| 543 | 543 |
SpaceReclaimed uint64 |
| 544 | 544 |
} |
| 545 |
+ |
|
| 546 |
+// NetworksPruneReport contains the response for Remote API: |
|
| 547 |
+// POST "/networks/prune" |
|
| 548 |
+type NetworksPruneReport struct {
|
|
| 549 |
+ NetworksDeleted []string |
|
| 550 |
+} |
| 32 | 33 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,72 @@ |
| 0 |
+package network |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ |
|
| 5 |
+ "golang.org/x/net/context" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/docker/docker/api/types" |
|
| 8 |
+ "github.com/docker/docker/cli" |
|
| 9 |
+ "github.com/docker/docker/cli/command" |
|
| 10 |
+ "github.com/spf13/cobra" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+type pruneOptions struct {
|
|
| 14 |
+ force bool |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+// NewPruneCommand returns a new cobra prune command for networks |
|
| 18 |
+func NewPruneCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|
| 19 |
+ var opts pruneOptions |
|
| 20 |
+ |
|
| 21 |
+ cmd := &cobra.Command{
|
|
| 22 |
+ Use: "prune [OPTIONS]", |
|
| 23 |
+ Short: "Remove all unused networks", |
|
| 24 |
+ Args: cli.NoArgs, |
|
| 25 |
+ RunE: func(cmd *cobra.Command, args []string) error {
|
|
| 26 |
+ output, err := runPrune(dockerCli, opts) |
|
| 27 |
+ if err != nil {
|
|
| 28 |
+ return err |
|
| 29 |
+ } |
|
| 30 |
+ if output != "" {
|
|
| 31 |
+ fmt.Fprintln(dockerCli.Out(), output) |
|
| 32 |
+ } |
|
| 33 |
+ return nil |
|
| 34 |
+ }, |
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ flags := cmd.Flags() |
|
| 38 |
+ flags.BoolVarP(&opts.force, "force", "f", false, "Do not prompt for confirmation") |
|
| 39 |
+ |
|
| 40 |
+ return cmd |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+const warning = `WARNING! This will remove all networks not used by at least one container. |
|
| 44 |
+Are you sure you want to continue?` |
|
| 45 |
+ |
|
| 46 |
+func runPrune(dockerCli *command.DockerCli, opts pruneOptions) (output string, err error) {
|
|
| 47 |
+ if !opts.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
|
|
| 48 |
+ return |
|
| 49 |
+ } |
|
| 50 |
+ |
|
| 51 |
+ report, err := dockerCli.Client().NetworksPrune(context.Background(), types.NetworksPruneConfig{})
|
|
| 52 |
+ if err != nil {
|
|
| 53 |
+ return |
|
| 54 |
+ } |
|
| 55 |
+ |
|
| 56 |
+ if len(report.NetworksDeleted) > 0 {
|
|
| 57 |
+ output = "Deleted Networks:\n" |
|
| 58 |
+ for _, id := range report.NetworksDeleted {
|
|
| 59 |
+ output += id + "\n" |
|
| 60 |
+ } |
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ return |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+// RunPrune calls the Network Prune API |
|
| 67 |
+// This returns the amount of space reclaimed and a detailed output string |
|
| 68 |
+func RunPrune(dockerCli *command.DockerCli) (uint64, string, error) {
|
|
| 69 |
+ output, err := runPrune(dockerCli, pruneOptions{force: true})
|
|
| 70 |
+ return 0, output, err |
|
| 71 |
+} |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"github.com/docker/docker/cli/command" |
| 5 | 5 |
"github.com/docker/docker/cli/command/container" |
| 6 | 6 |
"github.com/docker/docker/cli/command/image" |
| 7 |
+ "github.com/docker/docker/cli/command/network" |
|
| 7 | 8 |
"github.com/docker/docker/cli/command/volume" |
| 8 | 9 |
"github.com/spf13/cobra" |
| 9 | 10 |
) |
| ... | ... |
@@ -23,6 +24,11 @@ func NewImagePruneCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| 23 | 23 |
return image.NewPruneCommand(dockerCli) |
| 24 | 24 |
} |
| 25 | 25 |
|
| 26 |
+// NewNetworkPruneCommand returns a cobra prune command for Networks |
|
| 27 |
+func NewNetworkPruneCommand(dockerCli *command.DockerCli) *cobra.Command {
|
|
| 28 |
+ return network.NewPruneCommand(dockerCli) |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 26 | 31 |
// RunContainerPrune executes a prune command for containers |
| 27 | 32 |
func RunContainerPrune(dockerCli *command.DockerCli) (uint64, string, error) {
|
| 28 | 33 |
return container.RunPrune(dockerCli) |
| ... | ... |
@@ -37,3 +43,8 @@ func RunVolumePrune(dockerCli *command.DockerCli) (uint64, string, error) {
|
| 37 | 37 |
func RunImagePrune(dockerCli *command.DockerCli, all bool) (uint64, string, error) {
|
| 38 | 38 |
return image.RunPrune(dockerCli, all) |
| 39 | 39 |
} |
| 40 |
+ |
|
| 41 |
+// RunNetworkPrune executes a prune command for networks |
|
| 42 |
+func RunNetworkPrune(dockerCli *command.DockerCli) (uint64, string, error) {
|
|
| 43 |
+ return network.RunPrune(dockerCli) |
|
| 44 |
+} |
| ... | ... |
@@ -39,6 +39,7 @@ const ( |
| 39 | 39 |
warning = `WARNING! This will remove: |
| 40 | 40 |
- all stopped containers |
| 41 | 41 |
- all volumes not used by at least one container |
| 42 |
+ - all networks not used by at least one container |
|
| 42 | 43 |
%s |
| 43 | 44 |
Are you sure you want to continue?` |
| 44 | 45 |
|
| ... | ... |
@@ -64,13 +65,14 @@ func runPrune(dockerCli *command.DockerCli, opts pruneOptions) error {
|
| 64 | 64 |
for _, pruneFn := range []func(dockerCli *command.DockerCli) (uint64, string, error){
|
| 65 | 65 |
prune.RunContainerPrune, |
| 66 | 66 |
prune.RunVolumePrune, |
| 67 |
+ prune.RunNetworkPrune, |
|
| 67 | 68 |
} {
|
| 68 | 69 |
spc, output, err := pruneFn(dockerCli) |
| 69 | 70 |
if err != nil {
|
| 70 | 71 |
return err |
| 71 | 72 |
} |
| 72 |
- if spc > 0 {
|
|
| 73 |
- spaceReclaimed += spc |
|
| 73 |
+ spaceReclaimed += spc |
|
| 74 |
+ if output != "" {
|
|
| 74 | 75 |
fmt.Fprintln(dockerCli.Out(), output) |
| 75 | 76 |
} |
| 76 | 77 |
} |
| ... | ... |
@@ -91,6 +91,7 @@ type NetworkAPIClient interface {
|
| 91 | 91 |
NetworkInspectWithRaw(ctx context.Context, networkID string) (types.NetworkResource, []byte, error) |
| 92 | 92 |
NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) |
| 93 | 93 |
NetworkRemove(ctx context.Context, networkID string) error |
| 94 |
+ NetworksPrune(ctx context.Context, cfg types.NetworksPruneConfig) (types.NetworksPruneReport, error) |
|
| 94 | 95 |
} |
| 95 | 96 |
|
| 96 | 97 |
// NodeAPIClient defines API client methods for the nodes |
| 97 | 98 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,26 @@ |
| 0 |
+package client |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/docker/api/types" |
|
| 7 |
+ "golang.org/x/net/context" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// NetworksPrune requests the daemon to delete unused networks |
|
| 11 |
+func (cli *Client) NetworksPrune(ctx context.Context, cfg types.NetworksPruneConfig) (types.NetworksPruneReport, error) {
|
|
| 12 |
+ var report types.NetworksPruneReport |
|
| 13 |
+ |
|
| 14 |
+ serverResp, err := cli.post(ctx, "/networks/prune", nil, cfg, nil) |
|
| 15 |
+ if err != nil {
|
|
| 16 |
+ return report, err |
|
| 17 |
+ } |
|
| 18 |
+ defer ensureReaderClosed(serverResp) |
|
| 19 |
+ |
|
| 20 |
+ if err := json.NewDecoder(serverResp.body).Decode(&report); err != nil {
|
|
| 21 |
+ return report, fmt.Errorf("Error retrieving network prune report: %v", err)
|
|
| 22 |
+ } |
|
| 23 |
+ |
|
| 24 |
+ return report, nil |
|
| 25 |
+} |
| ... | ... |
@@ -279,6 +279,7 @@ func (cli *DaemonCli) start(opts daemonOptions) (err error) {
|
| 279 | 279 |
|
| 280 | 280 |
// initMiddlewares needs cli.d to be populated. Dont change this init order. |
| 281 | 281 |
cli.initMiddlewares(api, serverConfig) |
| 282 |
+ d.SetCluster(c) |
|
| 282 | 283 |
initRouter(api, d, c) |
| 283 | 284 |
|
| 284 | 285 |
cli.setupConfigReloadTrap() |
| 285 | 286 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,12 @@ |
| 0 |
+package daemon |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ apitypes "github.com/docker/docker/api/types" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+// Cluster is the interface for github.com/docker/docker/daemon/cluster.(*Cluster). |
|
| 7 |
+type Cluster interface {
|
|
| 8 |
+ GetNetwork(input string) (apitypes.NetworkResource, error) |
|
| 9 |
+ GetNetworks() ([]apitypes.NetworkResource, error) |
|
| 10 |
+ RemoveNetwork(input string) error |
|
| 11 |
+} |
| ... | ... |
@@ -102,6 +102,7 @@ type Daemon struct {
|
| 102 | 102 |
containerdRemote libcontainerd.Remote |
| 103 | 103 |
defaultIsolation containertypes.Isolation // Default isolation mode on Windows |
| 104 | 104 |
clusterProvider cluster.Provider |
| 105 |
+ cluster Cluster |
|
| 105 | 106 |
} |
| 106 | 107 |
|
| 107 | 108 |
// HasExperimental returns whether the experimental features of the daemon are enabled or not |
| ... | ... |
@@ -1234,3 +1235,13 @@ func copyBlkioEntry(entries []*containerd.BlkioStatsEntry) []types.BlkioStatEntr |
| 1234 | 1234 |
} |
| 1235 | 1235 |
return out |
| 1236 | 1236 |
} |
| 1237 |
+ |
|
| 1238 |
+// GetCluster returns the cluster |
|
| 1239 |
+func (daemon *Daemon) GetCluster() Cluster {
|
|
| 1240 |
+ return daemon.cluster |
|
| 1241 |
+} |
|
| 1242 |
+ |
|
| 1243 |
+// SetCluster sets the cluster |
|
| 1244 |
+func (daemon *Daemon) SetCluster(cluster Cluster) {
|
|
| 1245 |
+ daemon.cluster = cluster |
|
| 1246 |
+} |
| ... | ... |
@@ -1,6 +1,8 @@ |
| 1 | 1 |
package daemon |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "regexp" |
|
| 5 |
+ |
|
| 4 | 6 |
"github.com/Sirupsen/logrus" |
| 5 | 7 |
"github.com/docker/distribution/digest" |
| 6 | 8 |
"github.com/docker/docker/api/types" |
| ... | ... |
@@ -8,7 +10,9 @@ import ( |
| 8 | 8 |
"github.com/docker/docker/layer" |
| 9 | 9 |
"github.com/docker/docker/pkg/directory" |
| 10 | 10 |
"github.com/docker/docker/reference" |
| 11 |
+ "github.com/docker/docker/runconfig" |
|
| 11 | 12 |
"github.com/docker/docker/volume" |
| 13 |
+ "github.com/docker/libnetwork" |
|
| 12 | 14 |
) |
| 13 | 15 |
|
| 14 | 16 |
// ContainersPrune removes unused containers |
| ... | ... |
@@ -150,3 +154,72 @@ func (daemon *Daemon) ImagesPrune(config *types.ImagesPruneConfig) (*types.Image |
| 150 | 150 |
|
| 151 | 151 |
return rep, nil |
| 152 | 152 |
} |
| 153 |
+ |
|
| 154 |
+// localNetworksPrune removes unused local networks |
|
| 155 |
+func (daemon *Daemon) localNetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) {
|
|
| 156 |
+ rep := &types.NetworksPruneReport{}
|
|
| 157 |
+ var err error |
|
| 158 |
+ // When the function returns true, the walk will stop. |
|
| 159 |
+ l := func(nw libnetwork.Network) bool {
|
|
| 160 |
+ nwName := nw.Name() |
|
| 161 |
+ predefined := runconfig.IsPreDefinedNetwork(nwName) |
|
| 162 |
+ if !predefined && len(nw.Endpoints()) == 0 {
|
|
| 163 |
+ if err = daemon.DeleteNetwork(nw.ID()); err != nil {
|
|
| 164 |
+ logrus.Warnf("could not remove network %s: %v", nwName, err)
|
|
| 165 |
+ return false |
|
| 166 |
+ } |
|
| 167 |
+ rep.NetworksDeleted = append(rep.NetworksDeleted, nwName) |
|
| 168 |
+ } |
|
| 169 |
+ return false |
|
| 170 |
+ } |
|
| 171 |
+ daemon.netController.WalkNetworks(l) |
|
| 172 |
+ return rep, err |
|
| 173 |
+} |
|
| 174 |
+ |
|
| 175 |
+// clusterNetworksPrune removes unused cluster networks |
|
| 176 |
+func (daemon *Daemon) clusterNetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) {
|
|
| 177 |
+ rep := &types.NetworksPruneReport{}
|
|
| 178 |
+ cluster := daemon.GetCluster() |
|
| 179 |
+ networks, err := cluster.GetNetworks() |
|
| 180 |
+ if err != nil {
|
|
| 181 |
+ return rep, err |
|
| 182 |
+ } |
|
| 183 |
+ networkIsInUse := regexp.MustCompile(`network ([[:alnum:]]+) is in use`) |
|
| 184 |
+ for _, nw := range networks {
|
|
| 185 |
+ if nw.Name == "ingress" {
|
|
| 186 |
+ continue |
|
| 187 |
+ } |
|
| 188 |
+ // https://github.com/docker/docker/issues/24186 |
|
| 189 |
+ // `docker network inspect` unfortunately displays ONLY those containers that are local to that node. |
|
| 190 |
+ // So we try to remove it anyway and check the error |
|
| 191 |
+ err = cluster.RemoveNetwork(nw.ID) |
|
| 192 |
+ if err != nil {
|
|
| 193 |
+ // we can safely ignore the "network .. is in use" error |
|
| 194 |
+ match := networkIsInUse.FindStringSubmatch(err.Error()) |
|
| 195 |
+ if len(match) != 2 || match[1] != nw.ID {
|
|
| 196 |
+ logrus.Warnf("could not remove network %s: %v", nw.Name, err)
|
|
| 197 |
+ } |
|
| 198 |
+ continue |
|
| 199 |
+ } |
|
| 200 |
+ rep.NetworksDeleted = append(rep.NetworksDeleted, nw.Name) |
|
| 201 |
+ } |
|
| 202 |
+ return rep, nil |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 205 |
+// NetworksPrune removes unused networks |
|
| 206 |
+func (daemon *Daemon) NetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) {
|
|
| 207 |
+ rep := &types.NetworksPruneReport{}
|
|
| 208 |
+ clusterRep, err := daemon.clusterNetworksPrune(config) |
|
| 209 |
+ if err != nil {
|
|
| 210 |
+ logrus.Warnf("could not remove cluster networks: %v", err)
|
|
| 211 |
+ } else {
|
|
| 212 |
+ rep.NetworksDeleted = append(rep.NetworksDeleted, clusterRep.NetworksDeleted...) |
|
| 213 |
+ } |
|
| 214 |
+ localRep, err := daemon.localNetworksPrune(config) |
|
| 215 |
+ if err != nil {
|
|
| 216 |
+ logrus.Warnf("could not remove local networks: %v", err)
|
|
| 217 |
+ } else {
|
|
| 218 |
+ rep.NetworksDeleted = append(rep.NetworksDeleted, localRep.NetworksDeleted...) |
|
| 219 |
+ } |
|
| 220 |
+ return rep, err |
|
| 221 |
+} |
| ... | ... |
@@ -157,8 +157,10 @@ This section lists each version from latest to oldest. Each listing includes a |
| 157 | 157 |
* `POST /containers/prune` prunes stopped containers. |
| 158 | 158 |
* `POST /images/prune` prunes unused images. |
| 159 | 159 |
* `POST /volumes/prune` prunes unused volumes. |
| 160 |
+* `POST /networks/prune` prunes unused networks. |
|
| 160 | 161 |
* Every API response now includes a `Docker-Experimental` header specifying if experimental features are enabled (value can be `true` or `false`). |
| 161 | 162 |
|
| 163 |
+ |
|
| 162 | 164 |
### v1.24 API changes |
| 163 | 165 |
|
| 164 | 166 |
[Docker Remote API v1.24](docker_remote_api_v1.24.md) documentation |
| ... | ... |
@@ -3881,6 +3881,36 @@ Instruct the driver to remove the network (`id`). |
| 3881 | 3881 |
- **404** - no such network |
| 3882 | 3882 |
- **500** - server error |
| 3883 | 3883 |
|
| 3884 |
+### Prune unused networks |
|
| 3885 |
+ |
|
| 3886 |
+`POST /networks/prune` |
|
| 3887 |
+ |
|
| 3888 |
+Delete unused networks |
|
| 3889 |
+ |
|
| 3890 |
+**Example request**: |
|
| 3891 |
+ |
|
| 3892 |
+ POST /networks/prune HTTP/1.1 |
|
| 3893 |
+ Content-Type: application/json |
|
| 3894 |
+ |
|
| 3895 |
+ {
|
|
| 3896 |
+ } |
|
| 3897 |
+ |
|
| 3898 |
+**Example response**: |
|
| 3899 |
+ |
|
| 3900 |
+ HTTP/1.1 200 OK |
|
| 3901 |
+ Content-Type: application/json |
|
| 3902 |
+ |
|
| 3903 |
+ {
|
|
| 3904 |
+ "NetworksDeleted": [ |
|
| 3905 |
+ "n1" |
|
| 3906 |
+ ], |
|
| 3907 |
+ } |
|
| 3908 |
+ |
|
| 3909 |
+**Status codes**: |
|
| 3910 |
+ |
|
| 3911 |
+- **200** – no error |
|
| 3912 |
+- **500** – server error |
|
| 3913 |
+ |
|
| 3884 | 3914 |
## 3.6 Plugins |
| 3885 | 3915 |
|
| 3886 | 3916 |
### List plugins |
| ... | ... |
@@ -98,5 +98,6 @@ You can connect a container to one or more networks. The networks need not be th |
| 98 | 98 |
* [network disconnect](network_disconnect.md) |
| 99 | 99 |
* [network ls](network_ls.md) |
| 100 | 100 |
* [network rm](network_rm.md) |
| 101 |
+* [network prune](network_prune.md) |
|
| 101 | 102 |
* [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) |
| 102 | 103 |
* [Work with networks](https://docs.docker.com/engine/userguide/networking/work-with-networks/) |
| ... | ... |
@@ -197,4 +197,5 @@ to create an externally isolated `overlay` network, you can specify the |
| 197 | 197 |
* [network disconnect](network_disconnect.md) |
| 198 | 198 |
* [network ls](network_ls.md) |
| 199 | 199 |
* [network rm](network_rm.md) |
| 200 |
+* [network prune](network_prune.md) |
|
| 200 | 201 |
* [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) |
| ... | ... |
@@ -39,4 +39,5 @@ Disconnects a container from a network. The container must be running to disconn |
| 39 | 39 |
* [network create](network_create.md) |
| 40 | 40 |
* [network ls](network_ls.md) |
| 41 | 41 |
* [network rm](network_rm.md) |
| 42 |
+* [network prune](network_prune.md) |
|
| 42 | 43 |
* [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) |
| ... | ... |
@@ -128,4 +128,5 @@ $ docker network inspect simple-network |
| 128 | 128 |
* [network create](network_create.md) |
| 129 | 129 |
* [network ls](network_ls.md) |
| 130 | 130 |
* [network rm](network_rm.md) |
| 131 |
+* [network prune](network_prune.md) |
|
| 131 | 132 |
* [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) |
| ... | ... |
@@ -214,4 +214,5 @@ d1584f8dc718: host |
| 214 | 214 |
* [network create](network_create.md) |
| 215 | 215 |
* [network inspect](network_inspect.md) |
| 216 | 216 |
* [network rm](network_rm.md) |
| 217 |
+* [network prune](network_prune.md) |
|
| 217 | 218 |
* [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) |
| 218 | 219 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,45 @@ |
| 0 |
+--- |
|
| 1 |
+title: "network prune" |
|
| 2 |
+description: "Remove unused networks" |
|
| 3 |
+keywords: [network, prune, delete] |
|
| 4 |
+--- |
|
| 5 |
+ |
|
| 6 |
+# network prune |
|
| 7 |
+ |
|
| 8 |
+```markdown |
|
| 9 |
+Usage: docker network prune [OPTIONS] |
|
| 10 |
+ |
|
| 11 |
+Remove all unused networks |
|
| 12 |
+ |
|
| 13 |
+Options: |
|
| 14 |
+ -f, --force Do not prompt for confirmation |
|
| 15 |
+ --help Print usage |
|
| 16 |
+``` |
|
| 17 |
+ |
|
| 18 |
+Remove all unused networks. Unused networks are those which are not referenced by any containers. |
|
| 19 |
+ |
|
| 20 |
+Example output: |
|
| 21 |
+ |
|
| 22 |
+```bash |
|
| 23 |
+$ docker network prune |
|
| 24 |
+WARNING! This will remove all networks not used by at least one container. |
|
| 25 |
+Are you sure you want to continue? [y/N] y |
|
| 26 |
+Deleted Networks: |
|
| 27 |
+n1 |
|
| 28 |
+n2 |
|
| 29 |
+``` |
|
| 30 |
+ |
|
| 31 |
+## Related information |
|
| 32 |
+ |
|
| 33 |
+* [network disconnect ](network_disconnect.md) |
|
| 34 |
+* [network connect](network_connect.md) |
|
| 35 |
+* [network create](network_create.md) |
|
| 36 |
+* [network ls](network_ls.md) |
|
| 37 |
+* [network inspect](network_inspect.md) |
|
| 38 |
+* [network rm](network_rm.md) |
|
| 39 |
+* [Understand Docker container networks](../../userguide/networking/index.md) |
|
| 40 |
+* [system df](system_df.md) |
|
| 41 |
+* [container prune](container_prune.md) |
|
| 42 |
+* [image prune](image_prune.md) |
|
| 43 |
+* [volume prune](volume_prune.md) |
|
| 44 |
+* [system prune](system_prune.md) |
| ... | ... |
@@ -55,4 +55,5 @@ deletion. |
| 55 | 55 |
* [network create](network_create.md) |
| 56 | 56 |
* [network ls](network_ls.md) |
| 57 | 57 |
* [network inspect](network_inspect.md) |
| 58 |
+* [network prune](network_prune.md) |
|
| 58 | 59 |
* [Understand Docker container networks](https://docs.docker.com/engine/userguide/networking/) |
| ... | ... |
@@ -66,8 +66,11 @@ my-named-vol 0 |
| 66 | 66 |
* `UNIQUE SIZE` is the amount of space that is only used by a given image |
| 67 | 67 |
* `SIZE` is the virtual size of the image, it is the sum of `SHARED SIZE` and `UNIQUE SIZE` |
| 68 | 68 |
|
| 69 |
+Note that network information is not shown because it doesn't consume the disk space. |
|
| 70 |
+ |
|
| 69 | 71 |
## Related Information |
| 70 | 72 |
* [system prune](system_prune.md) |
| 71 | 73 |
* [container prune](container_prune.md) |
| 72 | 74 |
* [volume prune](volume_prune.md) |
| 73 | 75 |
* [image prune](image_prune.md) |
| 76 |
+* [network prune](network_prune.md) |
| ... | ... |
@@ -26,7 +26,7 @@ Options: |
| 26 | 26 |
--help Print usage |
| 27 | 27 |
``` |
| 28 | 28 |
|
| 29 |
-Remove all unused containers, volumes and images (both dangling and unreferenced). |
|
| 29 |
+Remove all unused containers, volumes, networks and images (both dangling and unreferenced). |
|
| 30 | 30 |
|
| 31 | 31 |
Example output: |
| 32 | 32 |
|
| ... | ... |
@@ -35,6 +35,7 @@ $ docker system prune -a |
| 35 | 35 |
WARNING! This will remove: |
| 36 | 36 |
- all stopped containers |
| 37 | 37 |
- all volumes not used by at least one container |
| 38 |
+ - all networks not used by at least one container |
|
| 38 | 39 |
- all images without at least one container associated to them |
| 39 | 40 |
Are you sure you want to continue? [y/N] y |
| 40 | 41 |
Deleted Containers: |
| ... | ... |
@@ -74,4 +75,5 @@ Total reclaimed space: 13.5 MB |
| 74 | 74 |
* [system df](system_df.md) |
| 75 | 75 |
* [container prune](container_prune.md) |
| 76 | 76 |
* [image prune](image_prune.md) |
| 77 |
+* [network prune](network_prune.md) |
|
| 77 | 78 |
* [system prune](system_prune.md) |
| 54 | 55 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,61 @@ |
| 0 |
+// +build !windows |
|
| 1 |
+ |
|
| 2 |
+package main |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "strconv" |
|
| 6 |
+ "strings" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/docker/docker/pkg/integration/checker" |
|
| 9 |
+ "github.com/go-check/check" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+func pruneNetworkAndVerify(c *check.C, d *SwarmDaemon, kept, pruned []string) {
|
|
| 13 |
+ _, err := d.Cmd("network", "prune", "--force")
|
|
| 14 |
+ c.Assert(err, checker.IsNil) |
|
| 15 |
+ out, err := d.Cmd("network", "ls", "--format", "{{.Name}}")
|
|
| 16 |
+ c.Assert(err, checker.IsNil) |
|
| 17 |
+ for _, s := range kept {
|
|
| 18 |
+ c.Assert(out, checker.Contains, s) |
|
| 19 |
+ } |
|
| 20 |
+ for _, s := range pruned {
|
|
| 21 |
+ c.Assert(out, checker.Not(checker.Contains), s) |
|
| 22 |
+ } |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+func (s *DockerSwarmSuite) TestPruneNetwork(c *check.C) {
|
|
| 26 |
+ d := s.AddDaemon(c, true, true) |
|
| 27 |
+ _, err := d.Cmd("network", "create", "n1") // used by container (testprune)
|
|
| 28 |
+ c.Assert(err, checker.IsNil) |
|
| 29 |
+ _, err = d.Cmd("network", "create", "n2")
|
|
| 30 |
+ c.Assert(err, checker.IsNil) |
|
| 31 |
+ _, err = d.Cmd("network", "create", "n3", "--driver", "overlay") // used by service (testprunesvc)
|
|
| 32 |
+ c.Assert(err, checker.IsNil) |
|
| 33 |
+ _, err = d.Cmd("network", "create", "n4", "--driver", "overlay")
|
|
| 34 |
+ c.Assert(err, checker.IsNil) |
|
| 35 |
+ |
|
| 36 |
+ cName := "testprune" |
|
| 37 |
+ _, err = d.Cmd("run", "-d", "--name", cName, "--net", "n1", "busybox", "top")
|
|
| 38 |
+ c.Assert(err, checker.IsNil) |
|
| 39 |
+ |
|
| 40 |
+ serviceName := "testprunesvc" |
|
| 41 |
+ replicas := 1 |
|
| 42 |
+ out, err := d.Cmd("service", "create", "--name", serviceName,
|
|
| 43 |
+ "--replicas", strconv.Itoa(replicas), |
|
| 44 |
+ "--network", "n3", |
|
| 45 |
+ "busybox", "top") |
|
| 46 |
+ c.Assert(err, checker.IsNil) |
|
| 47 |
+ c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "") |
|
| 48 |
+ waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, replicas+1) |
|
| 49 |
+ |
|
| 50 |
+ // prune and verify |
|
| 51 |
+ pruneNetworkAndVerify(c, d, []string{"n1", "n3"}, []string{"n2", "n4"})
|
|
| 52 |
+ |
|
| 53 |
+ // remove containers, then prune and verify again |
|
| 54 |
+ _, err = d.Cmd("rm", "-f", cName)
|
|
| 55 |
+ c.Assert(err, checker.IsNil) |
|
| 56 |
+ _, err = d.Cmd("service", "rm", serviceName)
|
|
| 57 |
+ c.Assert(err, checker.IsNil) |
|
| 58 |
+ waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 0) |
|
| 59 |
+ pruneNetworkAndVerify(c, d, []string{}, []string{"n1", "n3"})
|
|
| 60 |
+} |