github.com/chenbh/concourse/v6@v6.4.2/worker/workercmd/containerd.go (about) 1 // +build linux 2 3 package workercmd 4 5 import ( 6 "fmt" 7 "io/ioutil" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "syscall" 12 "time" 13 14 "code.cloudfoundry.org/garden/server" 15 "code.cloudfoundry.org/lager" 16 "code.cloudfoundry.org/localip" 17 concourseCmd "github.com/chenbh/concourse/v6/cmd" 18 "github.com/chenbh/concourse/v6/worker/runtime" 19 "github.com/chenbh/concourse/v6/worker/runtime/libcontainerd" 20 "github.com/tedsuo/ifrit" 21 "github.com/tedsuo/ifrit/grouper" 22 ) 23 24 // TODO This constructor could use refactoring using the functional options pattern. 25 func containerdGardenServerRunner( 26 logger lager.Logger, 27 bindAddr, 28 containerdAddr string, 29 requestTimeout time.Duration, 30 dnsServers []string, 31 networkPool string, 32 maxContainers int, 33 restrictedNetworks []string, 34 ) (ifrit.Runner, error) { 35 const ( 36 graceTime = 0 37 namespace = "concourse" 38 ) 39 40 backendOpts := []runtime.GardenBackendOpt{} 41 networkOpts := []runtime.CNINetworkOpt{} 42 43 if len(dnsServers) > 0 { 44 networkOpts = append(networkOpts, runtime.WithNameServers(dnsServers)) 45 } 46 47 if len(restrictedNetworks) > 0 { 48 networkOpts = append(networkOpts, runtime.WithRestrictedNetworks(restrictedNetworks)) 49 } 50 51 if networkPool != "" { 52 networkOpts = append(networkOpts, runtime.WithCNINetworkConfig( 53 runtime.CNINetworkConfig{ 54 BridgeName: "concourse0", 55 NetworkName: "concourse", 56 Subnet: networkPool, 57 })) 58 } 59 60 cniNetwork, err := runtime.NewCNINetwork(networkOpts...) 61 if err != nil { 62 return nil, fmt.Errorf("new cni network: %w", err) 63 } 64 65 backendOpts = append(backendOpts, 66 runtime.WithNetwork(cniNetwork), 67 runtime.WithRequestTimeout(requestTimeout), 68 runtime.WithMaxContainers(maxContainers), 69 ) 70 71 gardenBackend, err := runtime.NewGardenBackend( 72 libcontainerd.New(containerdAddr, namespace, requestTimeout), 73 backendOpts..., 74 ) 75 if err != nil { 76 return nil, fmt.Errorf("containerd containerd init: %w", err) 77 } 78 79 server := server.New("tcp", bindAddr, 80 graceTime, 81 &gardenBackend, 82 logger, 83 ) 84 85 return gardenServerRunner{logger, server}, nil 86 } 87 88 // writeDefaultContainerdConfig writes a default containerd configuration file 89 // to a destination. 90 // 91 func writeDefaultContainerdConfig(dest string) error { 92 // disable plugins we don't use: 93 // 94 // - CRI: we're not supposed to be targetted by a kubelet, so there's no 95 // need to bring up kubernete's container runtime interface plugin. 96 // 97 // - AUFS/BTRFS/ZFS: since linux 3.18, `overlayfs` is in upstream, which 98 // most distros should include, so by keeping a focus 99 // on a single snapshotter implementation we can better 100 // reason about potential problems down the road. 101 // 102 const config = `disabled_plugins = ["cri", "aufs", "btrfs", "zfs"]` 103 104 err := ioutil.WriteFile(dest, []byte(config), 0755) 105 if err != nil { 106 return fmt.Errorf("write file %s: %w", dest, err) 107 } 108 109 return nil 110 } 111 112 func (cmd *WorkerCommand) containerdRunner(logger lager.Logger) (ifrit.Runner, error) { 113 const sock = "/run/containerd/containerd.sock" 114 115 var ( 116 config = filepath.Join(cmd.WorkDir.Path(), "containerd.toml") 117 root = filepath.Join(cmd.WorkDir.Path(), "containerd") 118 bin = "containerd" 119 ) 120 121 err := os.MkdirAll(root, 0755) 122 if err != nil { 123 return nil, err 124 } 125 126 if cmd.Containerd.Config.Path() != "" { 127 config = cmd.Containerd.Config.Path() 128 } else { 129 err := writeDefaultContainerdConfig(config) 130 if err != nil { 131 return nil, fmt.Errorf("write default containerd config: %w", err) 132 } 133 } 134 135 if cmd.Containerd.Bin != "" { 136 bin = cmd.Containerd.Bin 137 } 138 139 command := exec.Command(bin, 140 "--address="+sock, 141 "--root="+root, 142 "--config="+config, 143 ) 144 145 command.Stdout = os.Stdout 146 command.Stderr = os.Stderr 147 command.SysProcAttr = &syscall.SysProcAttr{ 148 Pdeathsig: syscall.SIGKILL, 149 } 150 151 members := grouper.Members{} 152 153 dnsServers := cmd.Containerd.DNSServers 154 if cmd.Containerd.DNS.Enable { 155 dnsProxyRunner, err := cmd.dnsProxyRunner(logger.Session("dns-proxy")) 156 if err != nil { 157 return nil, err 158 } 159 160 lip, err := localip.LocalIP() 161 if err != nil { 162 return nil, err 163 } 164 165 dnsServers = append(dnsServers, lip) 166 167 members = append(members, grouper.Member{ 168 Name: "dns-proxy", 169 Runner: concourseCmd.NewLoggingRunner( 170 logger.Session("dns-proxy-runner"), 171 dnsProxyRunner, 172 ), 173 }) 174 } 175 176 gardenServerRunner, err := containerdGardenServerRunner( 177 logger, 178 cmd.bindAddr(), 179 sock, 180 cmd.Containerd.RequestTimeout, 181 dnsServers, 182 cmd.Containerd.NetworkPool, 183 cmd.Containerd.MaxContainers, 184 cmd.Containerd.RestrictedNetworks, 185 ) 186 if err != nil { 187 return nil, fmt.Errorf("containerd garden server runner: %w", err) 188 } 189 190 members = append(grouper.Members{ 191 { 192 Name: "containerd", 193 Runner: CmdRunner{command}, 194 }, 195 { 196 Name: "containerd-garden-backend", 197 Runner: gardenServerRunner, 198 }, 199 }, members...) 200 201 // Using the Ordered strategy to ensure containerd is up before the garden server is started 202 return grouper.NewOrdered(os.Interrupt, members), nil 203 }