github.com/chenbh/concourse/v6@v6.4.2/worker/workercmd/worker.go (about) 1 package workercmd 2 3 import ( 4 "fmt" 5 "net/http" 6 "os" 7 "path/filepath" 8 "time" 9 10 "code.cloudfoundry.org/lager" 11 "github.com/concourse/baggageclaim/baggageclaimcmd" 12 bclient "github.com/concourse/baggageclaim/client" 13 "github.com/chenbh/concourse/v6" 14 "github.com/chenbh/concourse/v6/atc/worker/gclient" 15 concourseCmd "github.com/chenbh/concourse/v6/cmd" 16 "github.com/chenbh/concourse/v6/worker" 17 "github.com/concourse/flag" 18 "github.com/tedsuo/ifrit" 19 "github.com/tedsuo/ifrit/grouper" 20 "github.com/tedsuo/ifrit/http_server" 21 "github.com/tedsuo/ifrit/sigmon" 22 ) 23 24 type WorkerCommand struct { 25 Worker WorkerConfig 26 27 TSA worker.TSAConfig `group:"TSA Configuration" namespace:"tsa"` 28 29 Certs Certs 30 31 WorkDir flag.Dir `long:"work-dir" required:"true" description:"Directory in which to place container data."` 32 33 BindIP flag.IP `long:"bind-ip" default:"127.0.0.1" description:"IP address on which to listen for the Garden server."` 34 BindPort uint16 `long:"bind-port" default:"7777" description:"Port on which to listen for the Garden server."` 35 36 DebugBindIP flag.IP `long:"debug-bind-ip" default:"127.0.0.1" description:"IP address on which to listen for the pprof debugger endpoints."` 37 DebugBindPort uint16 `long:"debug-bind-port" default:"7776" description:"Port on which to listen for the pprof debugger endpoints."` 38 39 HealthcheckBindIP flag.IP `long:"healthcheck-bind-ip" default:"0.0.0.0" description:"IP address on which to listen for health checking requests."` 40 HealthcheckBindPort uint16 `long:"healthcheck-bind-port" default:"8888" description:"Port on which to listen for health checking requests."` 41 HealthCheckTimeout time.Duration `long:"healthcheck-timeout" default:"5s" description:"HTTP timeout for the full duration of health checking."` 42 43 SweepInterval time.Duration `long:"sweep-interval" default:"30s" description:"Interval on which containers and volumes will be garbage collected from the worker."` 44 VolumeSweeperMaxInFlight uint16 `long:"volume-sweeper-max-in-flight" default:"3" description:"Maximum number of volumes which can be swept in parallel."` 45 ContainerSweeperMaxInFlight uint16 `long:"container-sweeper-max-in-flight" default:"5" description:"Maximum number of containers which can be swept in parallel."` 46 47 RebalanceInterval time.Duration `long:"rebalance-interval" default:"4h" description:"Duration after which the registration should be swapped to another random SSH gateway."` 48 49 ConnectionDrainTimeout time.Duration `long:"connection-drain-timeout" default:"1h" description:"Duration after which a worker should give up draining forwarded connections on shutdown."` 50 51 RuntimeConfiguration `group:"Runtime Configuration"` 52 53 // This refers to flags relevant to the operation of the Guardian runtime. 54 // For historical reasons it is namespaced under "garden" i.e. CONCOURSE_GARDEN instead of "guardian" i.e. CONCOURSE_GUARDIAN 55 Guardian GuardianRuntime `group:"Guardian Configuration" namespace:"garden"` 56 57 Containerd ContainerdRuntime `group:"Containerd Configuration" namespace:"containerd"` 58 59 ExternalGardenURL flag.URL `long:"external-garden-url" description:"API endpoint of an externally managed Garden server to use instead of running the embedded Garden server."` 60 61 Baggageclaim baggageclaimcmd.BaggageclaimCommand `group:"Baggageclaim Configuration" namespace:"baggageclaim"` 62 63 ResourceTypes flag.Dir `long:"resource-types" description:"Path to directory containing resource types the worker should advertise."` 64 65 Logger flag.Lager 66 } 67 68 func (cmd *WorkerCommand) Execute(args []string) error { 69 runner, err := cmd.Runner(args) 70 if err != nil { 71 return err 72 } 73 74 return <-ifrit.Invoke(sigmon.New(runner)).Wait() 75 } 76 77 func (cmd *WorkerCommand) Runner(args []string) (ifrit.Runner, error) { 78 if cmd.ResourceTypes == "" { 79 cmd.ResourceTypes = flag.Dir(concourseCmd.DiscoverAsset("resource-types")) 80 } 81 82 logger, _ := cmd.Logger.Logger("worker") 83 84 atcWorker, gardenServerRunner, err := cmd.gardenServerRunner(logger.Session("garden")) 85 if err != nil { 86 return nil, err 87 } 88 89 atcWorker.Version = concourse.WorkerVersion 90 91 baggageclaimRunner, err := cmd.baggageclaimRunner(logger.Session("baggageclaim")) 92 if err != nil { 93 return nil, err 94 } 95 96 healthChecker := worker.NewHealthChecker( 97 logger.Session("healthchecker"), 98 cmd.baggageclaimURL(), 99 cmd.gardenURL(), 100 cmd.HealthCheckTimeout, 101 ) 102 103 tsaClient := cmd.TSA.Client(atcWorker) 104 105 beaconRunner := worker.NewBeaconRunner( 106 logger.Session("beacon-runner"), 107 tsaClient, 108 cmd.RebalanceInterval, 109 cmd.ConnectionDrainTimeout, 110 cmd.gardenAddr(), 111 cmd.baggageclaimAddr(), 112 ) 113 114 gardenClient := gclient.BasicGardenClientWithRequestTimeout( 115 logger.Session("garden-connection"), 116 cmd.Guardian.RequestTimeout, 117 cmd.gardenURL(), 118 ) 119 120 baggageclaimClient := bclient.NewWithHTTPClient( 121 cmd.baggageclaimURL(), 122 123 // ensure we don't use baggageclaim's default retryhttp client; all 124 // traffic should be local, so any failures are unlikely to be transient. 125 // we don't want a retry loop to block up sweeping and prevent the worker 126 // from existing. 127 &http.Client{ 128 Transport: &http.Transport{ 129 // don't let a slow (possibly stuck) baggageclaim server slow down 130 // sweeping too much 131 ResponseHeaderTimeout: 1 * time.Minute, 132 }, 133 // we've seen destroy calls to baggageclaim hang and lock gc 134 // gc is periodic so we don't need to retry here, we can rely 135 // on the next sweeper tick. 136 Timeout: 5 * time.Minute, 137 }, 138 ) 139 140 containerSweeper := worker.NewContainerSweeper( 141 logger.Session("container-sweeper"), 142 cmd.SweepInterval, 143 tsaClient, 144 gardenClient, 145 cmd.ContainerSweeperMaxInFlight, 146 ) 147 148 volumeSweeper := worker.NewVolumeSweeper( 149 logger.Session("volume-sweeper"), 150 cmd.SweepInterval, 151 tsaClient, 152 baggageclaimClient, 153 cmd.VolumeSweeperMaxInFlight, 154 ) 155 156 var members grouper.Members 157 158 if !cmd.gardenServerIsExternal() { 159 members = append(members, grouper.Member{ 160 Name: "garden", 161 Runner: concourseCmd.NewLoggingRunner(logger.Session("garden-runner"), gardenServerRunner), 162 }) 163 } 164 165 members = append(members, grouper.Members{ 166 { 167 Name: "baggageclaim", 168 Runner: concourseCmd.NewLoggingRunner(logger.Session("baggageclaim-runner"), baggageclaimRunner), 169 }, 170 { 171 Name: "debug", 172 Runner: concourseCmd.NewLoggingRunner( 173 logger.Session("debug-runner"), 174 http_server.New( 175 fmt.Sprintf("%s:%d", cmd.DebugBindIP.IP, cmd.DebugBindPort), 176 http.DefaultServeMux, 177 ), 178 ), 179 }, 180 { 181 Name: "healthcheck", 182 Runner: concourseCmd.NewLoggingRunner( 183 logger.Session("healthcheck-runner"), 184 http_server.New( 185 fmt.Sprintf("%s:%d", cmd.HealthcheckBindIP.IP, cmd.HealthcheckBindPort), 186 http.HandlerFunc(healthChecker.CheckHealth), 187 ), 188 ), 189 }, 190 { 191 Name: "beacon", 192 Runner: concourseCmd.NewLoggingRunner( 193 logger.Session("beacon-runner"), 194 beaconRunner, 195 ), 196 }, 197 { 198 Name: "container-sweeper", 199 Runner: concourseCmd.NewLoggingRunner( 200 logger.Session("container-sweeper"), 201 containerSweeper, 202 ), 203 }, 204 { 205 Name: "volume-sweeper", 206 Runner: concourseCmd.NewLoggingRunner( 207 logger.Session("volume-sweeper"), 208 volumeSweeper, 209 ), 210 }, 211 }...) 212 213 return grouper.NewParallel(os.Interrupt, members), nil 214 } 215 216 func (cmd *WorkerCommand) gardenServerIsExternal() bool { 217 return cmd.ExternalGardenURL.URL != nil 218 } 219 220 func (cmd *WorkerCommand) gardenAddr() string { 221 if cmd.gardenServerIsExternal() { 222 return cmd.ExternalGardenURL.URL.Host 223 } 224 225 return fmt.Sprintf("%s:%d", cmd.BindIP, cmd.BindPort) 226 } 227 228 func (cmd *WorkerCommand) gardenURL() string { 229 return fmt.Sprintf("http://%s", cmd.gardenAddr()) 230 } 231 232 func (cmd *WorkerCommand) baggageclaimAddr() string { 233 return fmt.Sprintf("%s:%d", cmd.Baggageclaim.BindIP, cmd.Baggageclaim.BindPort) 234 } 235 236 func (cmd *WorkerCommand) baggageclaimURL() string { 237 return fmt.Sprintf("http://%s", cmd.baggageclaimAddr()) 238 } 239 240 func (cmd *WorkerCommand) workerName() (string, error) { 241 if cmd.Worker.Name != "" { 242 return cmd.Worker.Name, nil 243 } 244 245 return os.Hostname() 246 } 247 248 func (cmd *WorkerCommand) baggageclaimRunner(logger lager.Logger) (ifrit.Runner, error) { 249 volumesDir := filepath.Join(cmd.WorkDir.Path(), "volumes") 250 251 err := os.MkdirAll(volumesDir, 0755) 252 if err != nil { 253 return nil, err 254 } 255 256 cmd.Baggageclaim.VolumesDir = flag.Dir(volumesDir) 257 258 cmd.Baggageclaim.OverlaysDir = filepath.Join(cmd.WorkDir.Path(), "overlays") 259 260 return cmd.Baggageclaim.Runner(nil) 261 }