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