github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/daemon/start.go (about) 1 package daemon // import "github.com/docker/docker/daemon" 2 3 import ( 4 "context" 5 "runtime" 6 "time" 7 8 "github.com/containerd/containerd" 9 "github.com/containerd/containerd/containers" 10 "github.com/docker/distribution/reference" 11 "github.com/docker/docker/api/types" 12 containertypes "github.com/docker/docker/api/types/container" 13 "github.com/docker/docker/container" 14 "github.com/docker/docker/errdefs" 15 "github.com/moby/sys/mount" 16 "github.com/pkg/errors" 17 "github.com/sirupsen/logrus" 18 ) 19 20 // ContainerStart starts a container. 21 func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error { 22 if checkpoint != "" && !daemon.HasExperimental() { 23 return errdefs.InvalidParameter(errors.New("checkpoint is only supported in experimental mode")) 24 } 25 26 ctr, err := daemon.GetContainer(name) 27 if err != nil { 28 return err 29 } 30 31 validateState := func() error { 32 ctr.Lock() 33 defer ctr.Unlock() 34 35 if ctr.Paused { 36 return errdefs.Conflict(errors.New("cannot start a paused container, try unpause instead")) 37 } 38 39 if ctr.Running { 40 return containerNotModifiedError{running: true} 41 } 42 43 if ctr.RemovalInProgress || ctr.Dead { 44 return errdefs.Conflict(errors.New("container is marked for removal and cannot be started")) 45 } 46 return nil 47 } 48 49 if err := validateState(); err != nil { 50 return err 51 } 52 53 // Windows does not have the backwards compatibility issue here. 54 if runtime.GOOS != "windows" { 55 // This is kept for backward compatibility - hostconfig should be passed when 56 // creating a container, not during start. 57 if hostConfig != nil { 58 logrus.Warn("DEPRECATED: Setting host configuration options when the container starts is deprecated and has been removed in Docker 1.12") 59 oldNetworkMode := ctr.HostConfig.NetworkMode 60 if err := daemon.setSecurityOptions(ctr, hostConfig); err != nil { 61 return errdefs.InvalidParameter(err) 62 } 63 if err := daemon.mergeAndVerifyLogConfig(&hostConfig.LogConfig); err != nil { 64 return errdefs.InvalidParameter(err) 65 } 66 if err := daemon.setHostConfig(ctr, hostConfig); err != nil { 67 return errdefs.InvalidParameter(err) 68 } 69 newNetworkMode := ctr.HostConfig.NetworkMode 70 if string(oldNetworkMode) != string(newNetworkMode) { 71 // if user has change the network mode on starting, clean up the 72 // old networks. It is a deprecated feature and has been removed in Docker 1.12 73 ctr.NetworkSettings.Networks = nil 74 if err := ctr.CheckpointTo(daemon.containersReplica); err != nil { 75 return errdefs.System(err) 76 } 77 } 78 ctr.InitDNSHostConfig() 79 } 80 } else { 81 if hostConfig != nil { 82 return errdefs.InvalidParameter(errors.New("Supplying a hostconfig on start is not supported. It should be supplied on create")) 83 } 84 } 85 86 // check if hostConfig is in line with the current system settings. 87 // It may happen cgroups are umounted or the like. 88 if _, err = daemon.verifyContainerSettings(ctr.OS, ctr.HostConfig, nil, false); err != nil { 89 return errdefs.InvalidParameter(err) 90 } 91 // Adapt for old containers in case we have updates in this function and 92 // old containers never have chance to call the new function in create stage. 93 if hostConfig != nil { 94 if err := daemon.adaptContainerSettings(ctr.HostConfig, false); err != nil { 95 return errdefs.InvalidParameter(err) 96 } 97 } 98 return daemon.containerStart(ctr, checkpoint, checkpointDir, true) 99 } 100 101 // containerStart prepares the container to run by setting up everything the 102 // container needs, such as storage and networking, as well as links 103 // between containers. The container is left waiting for a signal to 104 // begin running. 105 func (daemon *Daemon) containerStart(container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (err error) { 106 start := time.Now() 107 container.Lock() 108 defer container.Unlock() 109 110 if resetRestartManager && container.Running { // skip this check if already in restarting step and resetRestartManager==false 111 return nil 112 } 113 114 if container.RemovalInProgress || container.Dead { 115 return errdefs.Conflict(errors.New("container is marked for removal and cannot be started")) 116 } 117 118 if checkpointDir != "" { 119 // TODO(mlaventure): how would we support that? 120 return errdefs.Forbidden(errors.New("custom checkpointdir is not supported")) 121 } 122 123 // if we encounter an error during start we need to ensure that any other 124 // setup has been cleaned up properly 125 defer func() { 126 if err != nil { 127 container.SetError(err) 128 // if no one else has set it, make sure we don't leave it at zero 129 if container.ExitCode() == 0 { 130 container.SetExitCode(128) 131 } 132 if err := container.CheckpointTo(daemon.containersReplica); err != nil { 133 logrus.Errorf("%s: failed saving state on start failure: %v", container.ID, err) 134 } 135 container.Reset(false) 136 137 daemon.Cleanup(container) 138 // if containers AutoRemove flag is set, remove it after clean up 139 if container.HostConfig.AutoRemove { 140 container.Unlock() 141 if err := daemon.ContainerRm(container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil { 142 logrus.Errorf("can't remove container %s: %v", container.ID, err) 143 } 144 container.Lock() 145 } 146 } 147 }() 148 149 if err := daemon.conditionalMountOnStart(container); err != nil { 150 return err 151 } 152 153 if err := daemon.initializeNetworking(container); err != nil { 154 return err 155 } 156 157 spec, err := daemon.createSpec(container) 158 if err != nil { 159 return errdefs.System(err) 160 } 161 162 if resetRestartManager { 163 container.ResetRestartManager(true) 164 container.HasBeenManuallyStopped = false 165 } 166 167 if err := daemon.saveAppArmorConfig(container); err != nil { 168 return err 169 } 170 171 if checkpoint != "" { 172 checkpointDir, err = getCheckpointDir(checkpointDir, checkpoint, container.Name, container.ID, container.CheckpointDir(), false) 173 if err != nil { 174 return err 175 } 176 } 177 178 createOptions, err := daemon.getLibcontainerdCreateOptions(container) 179 if err != nil { 180 return err 181 } 182 183 ctx := context.TODO() 184 185 imageRef, err := reference.ParseNormalizedNamed(container.Config.Image) 186 if err != nil { 187 return err 188 } 189 190 err = daemon.containerd.Create(ctx, container.ID, spec, createOptions, withImageName(imageRef.String())) 191 if err != nil { 192 if errdefs.IsConflict(err) { 193 logrus.WithError(err).WithField("container", container.ID).Error("Container not cleaned up from containerd from previous run") 194 // best effort to clean up old container object 195 daemon.containerd.DeleteTask(ctx, container.ID) 196 if err := daemon.containerd.Delete(ctx, container.ID); err != nil && !errdefs.IsNotFound(err) { 197 logrus.WithError(err).WithField("container", container.ID).Error("Error cleaning up stale containerd container object") 198 } 199 err = daemon.containerd.Create(ctx, container.ID, spec, createOptions, withImageName(imageRef.String())) 200 } 201 if err != nil { 202 return translateContainerdStartErr(container.Path, container.SetExitCode, err) 203 } 204 } 205 206 // TODO(mlaventure): we need to specify checkpoint options here 207 pid, err := daemon.containerd.Start(context.Background(), container.ID, checkpointDir, 208 container.StreamConfig.Stdin() != nil || container.Config.Tty, 209 container.InitializeStdio) 210 if err != nil { 211 if err := daemon.containerd.Delete(context.Background(), container.ID); err != nil { 212 logrus.WithError(err).WithField("container", container.ID). 213 Error("failed to delete failed start container") 214 } 215 return translateContainerdStartErr(container.Path, container.SetExitCode, err) 216 } 217 218 container.SetRunning(pid, true) 219 container.HasBeenStartedBefore = true 220 daemon.setStateCounter(container) 221 222 daemon.initHealthMonitor(container) 223 224 if err := container.CheckpointTo(daemon.containersReplica); err != nil { 225 logrus.WithError(err).WithField("container", container.ID). 226 Errorf("failed to store container") 227 } 228 229 daemon.LogContainerEvent(container, "start") 230 containerActions.WithValues("start").UpdateSince(start) 231 232 return nil 233 } 234 235 // Cleanup releases any network resources allocated to the container along with any rules 236 // around how containers are linked together. It also unmounts the container's root filesystem. 237 func (daemon *Daemon) Cleanup(container *container.Container) { 238 daemon.releaseNetwork(container) 239 240 if err := container.UnmountIpcMount(); err != nil { 241 logrus.Warnf("%s cleanup: failed to unmount IPC: %s", container.ID, err) 242 } 243 244 if err := daemon.conditionalUnmountOnCleanup(container); err != nil { 245 // FIXME: remove once reference counting for graphdrivers has been refactored 246 // Ensure that all the mounts are gone 247 if mountid, err := daemon.imageService.GetLayerMountID(container.ID, container.OS); err == nil { 248 daemon.cleanupMountsByID(mountid) 249 } 250 } 251 252 if err := container.UnmountSecrets(); err != nil { 253 logrus.Warnf("%s cleanup: failed to unmount secrets: %s", container.ID, err) 254 } 255 256 if err := mount.RecursiveUnmount(container.Root); err != nil { 257 logrus.WithError(err).WithField("container", container.ID).Warn("Error while cleaning up container resource mounts.") 258 } 259 260 for _, eConfig := range container.ExecCommands.Commands() { 261 daemon.unregisterExecCommand(container, eConfig) 262 } 263 264 if container.BaseFS != nil && container.BaseFS.Path() != "" { 265 if err := container.UnmountVolumes(daemon.LogVolumeEvent); err != nil { 266 logrus.Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err) 267 } 268 } 269 270 container.CancelAttachContext() 271 272 if err := daemon.containerd.Delete(context.Background(), container.ID); err != nil { 273 logrus.Errorf("%s cleanup: failed to delete container from containerd: %v", container.ID, err) 274 } 275 } 276 277 func withImageName(n string) containerd.NewContainerOpts { 278 return func(ctx context.Context, _ *containerd.Client, c *containers.Container) error { 279 c.Image = n 280 return nil 281 } 282 }