Skip to content

Init Design #3

@groundwater

Description

@groundwater

Init is a job-control service.

HTTP IPC

You communicate with init over http.

For example, to start a job you PUT to /job/:name where :name is the unique identifier for your job.

{
  "tasks": [
    {
      "exec": "node",
      "args": ["server.js"],
      "envs": {
        "PATH": "/bin:/root/lib/node_modules/myapp/node_modules/.bin"
      },
      "cwd": "/root/lib/node_modules/myapp",
      "fd": [...],
      "user": 2,
      "group": 3
    }
  ]
}

All of the above are required, aside from user and group.

File Descriptors

File descriptors are pointers to various IO conduits of a process. There are many things that can be represented by a file descriptor,

  • a normal file on the file system
  • an unnamed pipe between two processes
  • a named pipe
  • a network socket
  • a unix domain socket
  • a block or character device

Each of the above is represented by a file-descriptor, and various system calls can be made to each descriptor based on the thing it represents.

Going forward, we probably want init to work with all of the above.

{ // a file
  type: "file",
  path: "/root/docs/myfile.txt",
  mode: "rw"  
}

{ // a unix socket
  type: "unix socket",
  path: "/root/var/myapp.sock"
}

{ // a network socket
  type: "network socket",
  bind: "127.0.0.1",
  port: 8080
}

{ // a named pipe
  type: "pipe",
  path: "..."
}

I don't want to support all of these yet, but it's something to keep in mind. I'm also not sure how to represent unnamed pipes between processes.

Init should respond with 501 Not Implemented, rather than a 400/500 error when using an unsupported file type.

Authentication

HTTP is secure. If you think otherwise, you should stop using all websites.

HTTP security can be poorly implemented however. We actually punt on handling security init the server. Instead, we require that you secure the socket.

For simple cases, you can bind to localhost. Only processes on the same host can access init.

A better approach might be binding to a unix domain socket, for example /root/var/init.sock. You can use file-system permissions to restrict access to the http server. This is how Docker works.

Permissions

There are no permissions. Either you have access to everything, or nothing.

In a multi-user environment, each user will run their own init service.

-+ init (root)
  \
   +-+ init (bob)
   |  \
   |   server.js (bob's server)
   |
   +-+ init (tom)
      \
       server.js (tom's server)

Restart Semantics

There are no restart semantics. You can restart a job by defining a second restart task, e.g.

{
  tasks: [
    {...}, // job to run 
    {...}  // command to restart job
  ]
}

Restart semantics are difficult

  • A process that exits immediately due to an error can peg the CPU at 100%.
  • Generally we do not want to ever stop trying to restart a process.
    Do you care more if your CPU gets pegged for a while, or your website goes down?
  • Sometimes we want notifications when processes exit.

We let the caller to init decide how they want to handle restart.

  • Tasks are always run serially.
    The previous task always exist before the next one starts.
  • The exit code of the previous process is available via the
    environment variable TASK_EXIT.
    This will either be the number or signal name that
    caused the previous task to exit.

Review

  • Init is simple, yet flexible.
  • Init is focused on running tasks, and jobs.
  • Init should export a well-defined API, to allow multiple products interact with it.

cc @piranna

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions