Make service start up containerd and services

This moves most of the initialisation of containers to the
service init in the `service` command.

Still leaves remounting root file systems read only but this
will go away shortly. Another step closer to removing shell
scripts in base system.

Signed-off-by: Justin Cormack <justin.cormack@docker.com>
This commit is contained in:
Justin Cormack 2017-07-26 11:21:14 +01:00
parent 29ead2bd9d
commit e40db14598
4 changed files with 90 additions and 37 deletions

View File

@ -9,6 +9,12 @@ import (
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
) )
const (
defaultSocket = "/run/containerd/containerd.sock"
defaultPath = "/containers/services"
defaultContainerd = "/usr/bin/containerd"
)
var ( var (
defaultLogFormatter = &log.TextFormatter{} defaultLogFormatter = &log.TextFormatter{}
) )
@ -67,9 +73,9 @@ func main() {
switch args[0] { switch args[0] {
case "start": case "start":
start(args[1:]) startCmd(args[1:])
case "system-init": case "system-init":
systemInit(args[1:]) systemInitCmd(args[1:])
default: default:
fmt.Printf("%q is not valid command.\n\n", args[0]) fmt.Printf("%q is not valid command.\n\n", args[0])
flag.Usage() flag.Usage()

View File

@ -15,7 +15,7 @@ import (
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
) )
func start(args []string) { func startCmd(args []string) {
invoked := filepath.Base(os.Args[0]) invoked := filepath.Base(os.Args[0])
flags := flag.NewFlagSet("start", flag.ExitOnError) flags := flag.NewFlagSet("start", flag.ExitOnError)
flags.Usage = func() { flags.Usage = func() {
@ -24,7 +24,8 @@ func start(args []string) {
flags.PrintDefaults() flags.PrintDefaults()
} }
sock := flags.String("sock", "/run/containerd/containerd.sock", "Path to containerd socket") sock := flags.String("sock", defaultSocket, "Path to containerd socket")
path := flags.String("path", defaultPath, "Path to service configs")
dumpSpec := flags.String("dump-spec", "", "Dump container spec to file before start") dumpSpec := flags.String("dump-spec", "", "Dump container spec to file before start")
@ -40,55 +41,65 @@ func start(args []string) {
} }
service := args[0] service := args[0]
rootfs := filepath.Join("/containers/services", service, "rootfs")
log.Infof("Starting service: %q", service) log.Infof("Starting service: %q", service)
log := log.WithFields(log.Fields{ log := log.WithFields(log.Fields{
"service": service, "service": service,
}) })
client, err := containerd.New(*sock) id, pid, msg, err := start(service, *sock, *path, *dumpSpec)
if err != nil { if err != nil {
log.WithError(err).Fatal("creating containerd client") log.WithError(err).Fatal(msg)
}
log.Debugf("Started %s pid %d", id, pid)
}
func start(service, sock, path, dumpSpec string) (string, uint32, string, error) {
rootfs := filepath.Join(path, service, "rootfs")
client, err := containerd.New(sock)
if err != nil {
return "", 0, "creating containerd client", err
} }
ctx := namespaces.WithNamespace(context.Background(), "default") ctx := namespaces.WithNamespace(context.Background(), "default")
var spec *specs.Spec var spec *specs.Spec
specf, err := os.Open(filepath.Join("/containers/services", service, "config.json")) specf, err := os.Open(filepath.Join(path, service, "config.json"))
if err != nil { if err != nil {
log.WithError(err).Fatal("failed to read service spec") return "", 0, "failed to read service spec", err
} }
if err := json.NewDecoder(specf).Decode(&spec); err != nil { if err := json.NewDecoder(specf).Decode(&spec); err != nil {
log.WithError(err).Fatal("failed to parse service spec") return "", 0, "failed to parse service spec", err
} }
log.Debugf("Rootfs is %s", rootfs)
spec.Root.Path = rootfs spec.Root.Path = rootfs
if *dumpSpec != "" { if dumpSpec != "" {
d, err := os.Create(*dumpSpec) d, err := os.Create(dumpSpec)
if err != nil { if err != nil {
log.WithError(err).Fatal("failed to open file for spec dump") return "", 0, "failed to open file for spec dump", err
} }
enc := json.NewEncoder(d) enc := json.NewEncoder(d)
enc.SetIndent("", " ") enc.SetIndent("", " ")
if err := enc.Encode(&spec); err != nil { if err := enc.Encode(&spec); err != nil {
log.WithError(err).Fatal("failed to write spec dump") return "", 0, "failed to write spec dump", err
} }
} }
ctr, err := client.NewContainer(ctx, service, containerd.WithSpec(spec)) ctr, err := client.NewContainer(ctx, service, containerd.WithSpec(spec))
if err != nil { if err != nil {
log.WithError(err).Fatal("failed to create container") return "", 0, "failed to create container", err
} }
io := func() (*containerd.IO, error) { io := func() (*containerd.IO, error) {
logfile := filepath.Join("/var/log", service+".log") logfile := filepath.Join("/var/log", service+".log")
// We just need this to exist. // We just need this to exist.
if err := ioutil.WriteFile(logfile, []byte{}, 0666); err != nil { if err := ioutil.WriteFile(logfile, []byte{}, 0600); err != nil {
log.WithError(err).Fatal("failed to touch logfile") // if we cannot write to log, discard output
logfile = "/dev/null"
} }
return &containerd.IO{ return &containerd.IO{
Stdin: "/dev/null", Stdin: "/dev/null",
@ -101,13 +112,13 @@ func start(args []string) {
task, err := ctr.NewTask(ctx, io) task, err := ctr.NewTask(ctx, io)
if err != nil { if err != nil {
// Don't bother to destroy the container here. // Don't bother to destroy the container here.
log.WithError(err).Fatal("failed to create task") return "", 0, "failed to create task", err
} }
if err := task.Start(ctx); err != nil { if err := task.Start(ctx); err != nil {
// Don't destroy the container here so it can be inspected for debugging. // Don't destroy the container here so it can be inspected for debugging.
log.WithError(err).Fatal("failed to start task") return "", 0, "failed to start task", err
} }
log.Debugf("Started %s pid %d", ctr.ID(), task.Pid()) return ctr.ID(), task.Pid(), "", nil
} }

View File

@ -4,9 +4,12 @@ import (
"context" "context"
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"syscall" "syscall"
"time"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/containerd/containerd" "github.com/containerd/containerd"
@ -49,7 +52,7 @@ func cleanupTask(ctx context.Context, ctr containerd.Container) error {
} }
} }
func systemInit(args []string) { func systemInitCmd(args []string) {
invoked := filepath.Base(os.Args[0]) invoked := filepath.Base(os.Args[0])
flags := flag.NewFlagSet("system-init", flag.ExitOnError) flags := flag.NewFlagSet("system-init", flag.ExitOnError)
flags.Usage = func() { flags.Usage = func() {
@ -58,7 +61,9 @@ func systemInit(args []string) {
flags.PrintDefaults() flags.PrintDefaults()
} }
sock := flags.String("sock", "/run/containerd/containerd.sock", "Path to containerd socket") sock := flags.String("sock", defaultSocket, "Path to containerd socket")
path := flags.String("path", defaultPath, "Path to service configs")
binary := flags.String("containerd", defaultContainerd, "Path to containerd")
if err := flags.Parse(args); err != nil { if err := flags.Parse(args); err != nil {
log.Fatal("Unable to parse args") log.Fatal("Unable to parse args")
@ -71,6 +76,33 @@ func systemInit(args []string) {
os.Exit(1) os.Exit(1)
} }
// remove (unlikely) old containerd socket
_ = os.Remove(*sock)
// start up containerd
cmd := exec.Command(*binary)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.WithError(err).Fatal("cannot start containerd")
}
// wait for containerd socket to appear
for {
_, err := os.Stat(*sock)
if err == nil {
break
}
err = cmd.Process.Signal(syscall.Signal(0))
if err != nil {
// process not there, wait() to find error
err = cmd.Wait()
log.WithError(err).Fatal("containerd process exited")
}
time.Sleep(100 * time.Millisecond)
}
// connect to containerd
client, err := containerd.New(*sock) client, err := containerd.New(*sock)
if err != nil { if err != nil {
log.WithError(err).Fatal("creating containerd client") log.WithError(err).Fatal("creating containerd client")
@ -83,6 +115,7 @@ func systemInit(args []string) {
log.WithError(err).Fatal("listing containers") log.WithError(err).Fatal("listing containers")
} }
// Clean up any old containers
// None of the errors in this loop are fatal since we want to // None of the errors in this loop are fatal since we want to
// keep trying. // keep trying.
for _, ctr := range ctrs { for _, ctr := range ctrs {
@ -99,4 +132,18 @@ func systemInit(args []string) {
log.WithError(err).Error("deleting container") log.WithError(err).Error("deleting container")
} }
} }
// Start up containers
files, err := ioutil.ReadDir(*path)
// just skip if there is an error, eg no such path
if err != nil {
return
}
for _, file := range files {
if id, pid, msg, err := start(file.Name(), *sock, *path, ""); err != nil {
log.WithError(err).Error(msg)
} else {
log.Debugf("Started %s pid %d", id, pid)
}
}
} }

View File

@ -1,25 +1,14 @@
#!/bin/sh #!/bin/sh
# bring up containerd
printf "\nStarting containerd\n"
/usr/bin/containerd &
# wait for socket to be there
while [ ! -S /run/containerd/containerd.sock ]
do
sleep 0.1
done
# start service containers # start service containers
service system-init
if [ -d /containers/services ] if [ -d /containers/services ]
then then
for f in $(find /containers/services -mindepth 1 -maxdepth 1 | sort) for f in $(find /containers/services -mindepth 1 -maxdepth 1 | sort)
do do
/bin/mount --bind "$f/rootfs" "$f/rootfs" /bin/mount --bind "$f/rootfs" "$f/rootfs"
mount -o remount,rw "$f/rootfs" mount -o remount,rw "$f/rootfs"
service start "$(basename $f)"
done done
fi fi
service system-init