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  }