prune.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package daemon
  2. import (
  3. "regexp"
  4. "github.com/Sirupsen/logrus"
  5. "github.com/docker/distribution/digest"
  6. "github.com/docker/docker/api/types"
  7. "github.com/docker/docker/image"
  8. "github.com/docker/docker/layer"
  9. "github.com/docker/docker/pkg/directory"
  10. "github.com/docker/docker/reference"
  11. "github.com/docker/docker/runconfig"
  12. "github.com/docker/docker/volume"
  13. "github.com/docker/libnetwork"
  14. )
  15. // ContainersPrune removes unused containers
  16. func (daemon *Daemon) ContainersPrune(config *types.ContainersPruneConfig) (*types.ContainersPruneReport, error) {
  17. rep := &types.ContainersPruneReport{}
  18. allContainers := daemon.List()
  19. for _, c := range allContainers {
  20. if !c.IsRunning() {
  21. cSize, _ := daemon.getSize(c)
  22. // TODO: sets RmLink to true?
  23. err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{})
  24. if err != nil {
  25. logrus.Warnf("failed to prune container %s: %v", c.ID, err)
  26. continue
  27. }
  28. if cSize > 0 {
  29. rep.SpaceReclaimed += uint64(cSize)
  30. }
  31. rep.ContainersDeleted = append(rep.ContainersDeleted, c.ID)
  32. }
  33. }
  34. return rep, nil
  35. }
  36. // VolumesPrune removes unused local volumes
  37. func (daemon *Daemon) VolumesPrune(config *types.VolumesPruneConfig) (*types.VolumesPruneReport, error) {
  38. rep := &types.VolumesPruneReport{}
  39. pruneVols := func(v volume.Volume) error {
  40. name := v.Name()
  41. refs := daemon.volumes.Refs(v)
  42. if len(refs) == 0 {
  43. vSize, err := directory.Size(v.Path())
  44. if err != nil {
  45. logrus.Warnf("could not determine size of volume %s: %v", name, err)
  46. }
  47. err = daemon.volumes.Remove(v)
  48. if err != nil {
  49. logrus.Warnf("could not remove volume %s: %v", name, err)
  50. return nil
  51. }
  52. rep.SpaceReclaimed += uint64(vSize)
  53. rep.VolumesDeleted = append(rep.VolumesDeleted, name)
  54. }
  55. return nil
  56. }
  57. err := daemon.traverseLocalVolumes(pruneVols)
  58. return rep, err
  59. }
  60. // ImagesPrune removes unused images
  61. func (daemon *Daemon) ImagesPrune(config *types.ImagesPruneConfig) (*types.ImagesPruneReport, error) {
  62. rep := &types.ImagesPruneReport{}
  63. var allImages map[image.ID]*image.Image
  64. if config.DanglingOnly {
  65. allImages = daemon.imageStore.Heads()
  66. } else {
  67. allImages = daemon.imageStore.Map()
  68. }
  69. allContainers := daemon.List()
  70. imageRefs := map[string]bool{}
  71. for _, c := range allContainers {
  72. imageRefs[c.ID] = true
  73. }
  74. // Filter intermediary images and get their unique size
  75. allLayers := daemon.layerStore.Map()
  76. topImages := map[image.ID]*image.Image{}
  77. for id, img := range allImages {
  78. dgst := digest.Digest(id)
  79. if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
  80. continue
  81. }
  82. topImages[id] = img
  83. }
  84. for id := range topImages {
  85. dgst := digest.Digest(id)
  86. hex := dgst.Hex()
  87. if _, ok := imageRefs[hex]; ok {
  88. continue
  89. }
  90. deletedImages := []types.ImageDelete{}
  91. refs := daemon.referenceStore.References(dgst)
  92. if len(refs) > 0 {
  93. if config.DanglingOnly {
  94. // Not a dangling image
  95. continue
  96. }
  97. nrRefs := len(refs)
  98. for _, ref := range refs {
  99. // If nrRefs == 1, we have an image marked as myreponame:<none>
  100. // i.e. the tag content was changed
  101. if _, ok := ref.(reference.Canonical); ok && nrRefs > 1 {
  102. continue
  103. }
  104. imgDel, err := daemon.ImageDelete(ref.String(), false, true)
  105. if err != nil {
  106. logrus.Warnf("could not delete reference %s: %v", ref.String(), err)
  107. continue
  108. }
  109. deletedImages = append(deletedImages, imgDel...)
  110. }
  111. } else {
  112. imgDel, err := daemon.ImageDelete(hex, false, true)
  113. if err != nil {
  114. logrus.Warnf("could not delete image %s: %v", hex, err)
  115. continue
  116. }
  117. deletedImages = append(deletedImages, imgDel...)
  118. }
  119. rep.ImagesDeleted = append(rep.ImagesDeleted, deletedImages...)
  120. }
  121. // Compute how much space was freed
  122. for _, d := range rep.ImagesDeleted {
  123. if d.Deleted != "" {
  124. chid := layer.ChainID(d.Deleted)
  125. if l, ok := allLayers[chid]; ok {
  126. diffSize, err := l.DiffSize()
  127. if err != nil {
  128. logrus.Warnf("failed to get layer %s size: %v", chid, err)
  129. continue
  130. }
  131. rep.SpaceReclaimed += uint64(diffSize)
  132. }
  133. }
  134. }
  135. return rep, nil
  136. }
  137. // localNetworksPrune removes unused local networks
  138. func (daemon *Daemon) localNetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) {
  139. rep := &types.NetworksPruneReport{}
  140. var err error
  141. // When the function returns true, the walk will stop.
  142. l := func(nw libnetwork.Network) bool {
  143. nwName := nw.Name()
  144. predefined := runconfig.IsPreDefinedNetwork(nwName)
  145. if !predefined && len(nw.Endpoints()) == 0 {
  146. if err = daemon.DeleteNetwork(nw.ID()); err != nil {
  147. logrus.Warnf("could not remove network %s: %v", nwName, err)
  148. return false
  149. }
  150. rep.NetworksDeleted = append(rep.NetworksDeleted, nwName)
  151. }
  152. return false
  153. }
  154. daemon.netController.WalkNetworks(l)
  155. return rep, err
  156. }
  157. // clusterNetworksPrune removes unused cluster networks
  158. func (daemon *Daemon) clusterNetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) {
  159. rep := &types.NetworksPruneReport{}
  160. cluster := daemon.GetCluster()
  161. networks, err := cluster.GetNetworks()
  162. if err != nil {
  163. return rep, err
  164. }
  165. networkIsInUse := regexp.MustCompile(`network ([[:alnum:]]+) is in use`)
  166. for _, nw := range networks {
  167. if nw.Name == "ingress" {
  168. continue
  169. }
  170. // https://github.com/docker/docker/issues/24186
  171. // `docker network inspect` unfortunately displays ONLY those containers that are local to that node.
  172. // So we try to remove it anyway and check the error
  173. err = cluster.RemoveNetwork(nw.ID)
  174. if err != nil {
  175. // we can safely ignore the "network .. is in use" error
  176. match := networkIsInUse.FindStringSubmatch(err.Error())
  177. if len(match) != 2 || match[1] != nw.ID {
  178. logrus.Warnf("could not remove network %s: %v", nw.Name, err)
  179. }
  180. continue
  181. }
  182. rep.NetworksDeleted = append(rep.NetworksDeleted, nw.Name)
  183. }
  184. return rep, nil
  185. }
  186. // NetworksPrune removes unused networks
  187. func (daemon *Daemon) NetworksPrune(config *types.NetworksPruneConfig) (*types.NetworksPruneReport, error) {
  188. rep := &types.NetworksPruneReport{}
  189. clusterRep, err := daemon.clusterNetworksPrune(config)
  190. if err != nil {
  191. logrus.Warnf("could not remove cluster networks: %v", err)
  192. } else {
  193. rep.NetworksDeleted = append(rep.NetworksDeleted, clusterRep.NetworksDeleted...)
  194. }
  195. localRep, err := daemon.localNetworksPrune(config)
  196. if err != nil {
  197. logrus.Warnf("could not remove local networks: %v", err)
  198. } else {
  199. rep.NetworksDeleted = append(rep.NetworksDeleted, localRep.NetworksDeleted...)
  200. }
  201. return rep, err
  202. }