mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
libcontainer: move example code out of README
Example code in README is outdated (especially since cgroups is moved to a separate repository) and lacks proper import statements. And, since it is not code, it is hard to keep it up to date. Let's move it out to the example_test.go file and refer to it. Note we still don't run it, but it will be compiled and linted in CI. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
+4
-215
@@ -14,6 +14,8 @@ host system and which is (optionally) isolated from other containers in the syst
|
|||||||
|
|
||||||
## Using libcontainer
|
## Using libcontainer
|
||||||
|
|
||||||
|
For a brief overview of using libcontainer, see [example_test.go](example_test.go).
|
||||||
|
|
||||||
### Container init
|
### Container init
|
||||||
|
|
||||||
Because containers are spawned in a two step process you will need a binary that
|
Because containers are spawned in a two step process you will need a binary that
|
||||||
@@ -23,223 +25,10 @@ arg "init", we call the first step process "bootstrap", so you always need a "in
|
|||||||
function as the entry of "bootstrap".
|
function as the entry of "bootstrap".
|
||||||
|
|
||||||
In addition to the go init function the early stage bootstrap is handled by importing
|
In addition to the go init function the early stage bootstrap is handled by importing
|
||||||
[nsenter](https://github.com/opencontainers/runc/blob/master/libcontainer/nsenter/README.md).
|
[nsenter](../nsenter/README.md).
|
||||||
|
|
||||||
For details on how runc implements such "init", see
|
For details on how runc implements such "init", see
|
||||||
[init.go](https://github.com/opencontainers/runc/blob/master/init.go)
|
[../init.go](../init.go) and [init_linux.go](init_linux.go).
|
||||||
and [libcontainer/init_linux.go](https://github.com/opencontainers/runc/blob/master/libcontainer/init_linux.go).
|
|
||||||
|
|
||||||
### Device management
|
|
||||||
|
|
||||||
If you want containers that have access to some devices, you need to import
|
|
||||||
this package into your code:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
_ "github.com/opencontainers/cgroups/devices"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
Without doing this, libcontainer cgroup manager won't be able to set up device
|
|
||||||
access rules, and will fail if devices are specified in the container
|
|
||||||
configuration.
|
|
||||||
|
|
||||||
### Container creation
|
|
||||||
|
|
||||||
To create a container you first have to create a configuration
|
|
||||||
struct describing how the container is to be created. A sample would look similar to this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
defaultMountFlags := unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
|
|
||||||
var devices []*devices.Rule
|
|
||||||
for _, device := range specconv.AllowedDevices {
|
|
||||||
devices = append(devices, &device.Rule)
|
|
||||||
}
|
|
||||||
config := &configs.Config{
|
|
||||||
Rootfs: "/your/path/to/rootfs",
|
|
||||||
Capabilities: &configs.Capabilities{
|
|
||||||
Bounding: []string{
|
|
||||||
"CAP_KILL",
|
|
||||||
"CAP_AUDIT_WRITE",
|
|
||||||
},
|
|
||||||
Effective: []string{
|
|
||||||
"CAP_KILL",
|
|
||||||
"CAP_AUDIT_WRITE",
|
|
||||||
},
|
|
||||||
Permitted: []string{
|
|
||||||
"CAP_KILL",
|
|
||||||
"CAP_AUDIT_WRITE",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Namespaces: configs.Namespaces([]configs.Namespace{
|
|
||||||
{Type: configs.NEWNS},
|
|
||||||
{Type: configs.NEWUTS},
|
|
||||||
{Type: configs.NEWIPC},
|
|
||||||
{Type: configs.NEWPID},
|
|
||||||
{Type: configs.NEWUSER},
|
|
||||||
{Type: configs.NEWNET},
|
|
||||||
{Type: configs.NEWCGROUP},
|
|
||||||
}),
|
|
||||||
Cgroups: &configs.Cgroup{
|
|
||||||
Name: "test-container",
|
|
||||||
Parent: "system",
|
|
||||||
Resources: &configs.Resources{
|
|
||||||
MemorySwappiness: nil,
|
|
||||||
Devices: devices,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MaskPaths: []string{
|
|
||||||
"/proc/kcore",
|
|
||||||
"/sys/firmware",
|
|
||||||
},
|
|
||||||
ReadonlyPaths: []string{
|
|
||||||
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
|
|
||||||
},
|
|
||||||
Devices: specconv.AllowedDevices,
|
|
||||||
Hostname: "testing",
|
|
||||||
Mounts: []*configs.Mount{
|
|
||||||
{
|
|
||||||
Source: "proc",
|
|
||||||
Destination: "/proc",
|
|
||||||
Device: "proc",
|
|
||||||
Flags: defaultMountFlags,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Source: "tmpfs",
|
|
||||||
Destination: "/dev",
|
|
||||||
Device: "tmpfs",
|
|
||||||
Flags: unix.MS_NOSUID | unix.MS_STRICTATIME,
|
|
||||||
Data: "mode=755",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Source: "devpts",
|
|
||||||
Destination: "/dev/pts",
|
|
||||||
Device: "devpts",
|
|
||||||
Flags: unix.MS_NOSUID | unix.MS_NOEXEC,
|
|
||||||
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Device: "tmpfs",
|
|
||||||
Source: "shm",
|
|
||||||
Destination: "/dev/shm",
|
|
||||||
Data: "mode=1777,size=65536k",
|
|
||||||
Flags: defaultMountFlags,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Source: "mqueue",
|
|
||||||
Destination: "/dev/mqueue",
|
|
||||||
Device: "mqueue",
|
|
||||||
Flags: defaultMountFlags,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Source: "sysfs",
|
|
||||||
Destination: "/sys",
|
|
||||||
Device: "sysfs",
|
|
||||||
Flags: defaultMountFlags | unix.MS_RDONLY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
UIDMappings: []configs.IDMap{
|
|
||||||
{
|
|
||||||
ContainerID: 0,
|
|
||||||
HostID: 1000,
|
|
||||||
Size: 65536,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
GIDMappings: []configs.IDMap{
|
|
||||||
{
|
|
||||||
ContainerID: 0,
|
|
||||||
HostID: 1000,
|
|
||||||
Size: 65536,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Networks: []*configs.Network{
|
|
||||||
{
|
|
||||||
Type: "loopback",
|
|
||||||
Address: "127.0.0.1/0",
|
|
||||||
Gateway: "localhost",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Rlimits: []configs.Rlimit{
|
|
||||||
{
|
|
||||||
Type: unix.RLIMIT_NOFILE,
|
|
||||||
Hard: uint64(1025),
|
|
||||||
Soft: uint64(1025),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Once you have the configuration populated you can create a container
|
|
||||||
with a specified ID under a specified state directory:
|
|
||||||
|
|
||||||
```go
|
|
||||||
container, err := libcontainer.Create("/run/containers", "container-id", config)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
To spawn bash as the initial process inside the container and have the
|
|
||||||
processes pid returned in order to wait, signal, or kill the process:
|
|
||||||
|
|
||||||
```go
|
|
||||||
process := &libcontainer.Process{
|
|
||||||
Args: []string{"/bin/bash"},
|
|
||||||
Env: []string{"PATH=/bin"},
|
|
||||||
User: "daemon",
|
|
||||||
Stdin: os.Stdin,
|
|
||||||
Stdout: os.Stdout,
|
|
||||||
Stderr: os.Stderr,
|
|
||||||
Init: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := container.Run(process)
|
|
||||||
if err != nil {
|
|
||||||
container.Destroy()
|
|
||||||
logrus.Fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for the process to finish.
|
|
||||||
_, err := process.Wait()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// destroy the container.
|
|
||||||
container.Destroy()
|
|
||||||
```
|
|
||||||
|
|
||||||
Additional ways to interact with a running container are:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// return all the pids for all processes running inside the container.
|
|
||||||
processes, err := container.Processes()
|
|
||||||
|
|
||||||
// get detailed cpu, memory, io, and network statistics for the container and
|
|
||||||
// it's processes.
|
|
||||||
stats, err := container.Stats()
|
|
||||||
|
|
||||||
// pause all processes inside the container.
|
|
||||||
container.Pause()
|
|
||||||
|
|
||||||
// resume all paused processes.
|
|
||||||
container.Resume()
|
|
||||||
|
|
||||||
// send signal to container's init process.
|
|
||||||
container.Signal(signal)
|
|
||||||
|
|
||||||
// update container resource constraints.
|
|
||||||
container.Set(config)
|
|
||||||
|
|
||||||
// get current status of the container.
|
|
||||||
status, err := container.Status()
|
|
||||||
|
|
||||||
// get current container's state information.
|
|
||||||
state, err := container.State()
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Checkpoint & Restore
|
## Checkpoint & Restore
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,241 @@
|
|||||||
|
package libcontainer_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/opencontainers/cgroups"
|
||||||
|
"github.com/opencontainers/cgroups/devices/config"
|
||||||
|
|
||||||
|
// To enable device management code, import cgroups/devices package.
|
||||||
|
// Without it, cgroup manager won't be able to set up device access rules,
|
||||||
|
// and will fail if devices are specified in the container configuration.
|
||||||
|
_ "github.com/opencontainers/cgroups/devices"
|
||||||
|
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/specconv"
|
||||||
|
|
||||||
|
// Required for container enter functionality.
|
||||||
|
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example_container() {
|
||||||
|
const defaultMountFlags = unix.MS_NOEXEC | unix.MS_NOSUID | unix.MS_NODEV
|
||||||
|
|
||||||
|
// Default set of allowed devices.
|
||||||
|
var devices []*config.Rule
|
||||||
|
for _, device := range specconv.AllowedDevices {
|
||||||
|
devices = append(devices, &device.Rule)
|
||||||
|
}
|
||||||
|
// To create a container you first have to create a configuration
|
||||||
|
// struct describing how the container is to be created.
|
||||||
|
config := &configs.Config{
|
||||||
|
Rootfs: "/your/path/to/rootfs",
|
||||||
|
Capabilities: &configs.Capabilities{
|
||||||
|
Bounding: []string{
|
||||||
|
"CAP_KILL",
|
||||||
|
"CAP_AUDIT_WRITE",
|
||||||
|
},
|
||||||
|
Effective: []string{
|
||||||
|
"CAP_KILL",
|
||||||
|
"CAP_AUDIT_WRITE",
|
||||||
|
},
|
||||||
|
Permitted: []string{
|
||||||
|
"CAP_KILL",
|
||||||
|
"CAP_AUDIT_WRITE",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Namespaces: configs.Namespaces([]configs.Namespace{
|
||||||
|
{Type: configs.NEWNS},
|
||||||
|
{Type: configs.NEWUTS},
|
||||||
|
{Type: configs.NEWIPC},
|
||||||
|
{Type: configs.NEWPID},
|
||||||
|
{Type: configs.NEWUSER},
|
||||||
|
{Type: configs.NEWNET},
|
||||||
|
{Type: configs.NEWCGROUP},
|
||||||
|
}),
|
||||||
|
Cgroups: &cgroups.Cgroup{
|
||||||
|
Name: "test-container",
|
||||||
|
Parent: "system",
|
||||||
|
Resources: &cgroups.Resources{
|
||||||
|
MemorySwappiness: nil,
|
||||||
|
Devices: devices,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MaskPaths: []string{
|
||||||
|
"/proc/kcore",
|
||||||
|
"/sys/firmware",
|
||||||
|
},
|
||||||
|
ReadonlyPaths: []string{
|
||||||
|
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
|
||||||
|
},
|
||||||
|
Devices: specconv.AllowedDevices,
|
||||||
|
Hostname: "testing",
|
||||||
|
Mounts: []*configs.Mount{
|
||||||
|
{
|
||||||
|
Source: "proc",
|
||||||
|
Destination: "/proc",
|
||||||
|
Device: "proc",
|
||||||
|
Flags: defaultMountFlags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "tmpfs",
|
||||||
|
Destination: "/dev",
|
||||||
|
Device: "tmpfs",
|
||||||
|
Flags: unix.MS_NOSUID | unix.MS_STRICTATIME,
|
||||||
|
Data: "mode=755",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "devpts",
|
||||||
|
Destination: "/dev/pts",
|
||||||
|
Device: "devpts",
|
||||||
|
Flags: unix.MS_NOSUID | unix.MS_NOEXEC,
|
||||||
|
Data: "newinstance,ptmxmode=0666,mode=0620,gid=5",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Device: "tmpfs",
|
||||||
|
Source: "shm",
|
||||||
|
Destination: "/dev/shm",
|
||||||
|
Data: "mode=1777,size=65536k",
|
||||||
|
Flags: defaultMountFlags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "mqueue",
|
||||||
|
Destination: "/dev/mqueue",
|
||||||
|
Device: "mqueue",
|
||||||
|
Flags: defaultMountFlags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Source: "sysfs",
|
||||||
|
Destination: "/sys",
|
||||||
|
Device: "sysfs",
|
||||||
|
Flags: defaultMountFlags | unix.MS_RDONLY,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UIDMappings: []configs.IDMap{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 1000,
|
||||||
|
Size: 65536,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GIDMappings: []configs.IDMap{
|
||||||
|
{
|
||||||
|
ContainerID: 0,
|
||||||
|
HostID: 1000,
|
||||||
|
Size: 65536,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Networks: []*configs.Network{
|
||||||
|
{
|
||||||
|
Type: "loopback",
|
||||||
|
Address: "127.0.0.1/0",
|
||||||
|
Gateway: "localhost",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Rlimits: []configs.Rlimit{
|
||||||
|
{
|
||||||
|
Type: unix.RLIMIT_NOFILE,
|
||||||
|
Hard: uint64(1025),
|
||||||
|
Soft: uint64(1025),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once you have the configuration populated you can create a container
|
||||||
|
// with a specified ID under a specified state directory:
|
||||||
|
container, err := libcontainer.Create("/run/containers", "container-id", config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// To spawn bash as the initial process inside the container and have the
|
||||||
|
// processes pid returned in order to wait, signal, or kill the process:
|
||||||
|
process := &libcontainer.Process{
|
||||||
|
Args: []string{"/bin/bash"},
|
||||||
|
Env: []string{"PATH=/bin"},
|
||||||
|
UID: 0,
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
Init: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.Run(process)
|
||||||
|
if err != nil {
|
||||||
|
_ = container.Destroy()
|
||||||
|
log.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the process to finish.
|
||||||
|
_, err = process.Wait()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy the container.
|
||||||
|
err = container.Destroy()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional ways to interact with a running container are:
|
||||||
|
|
||||||
|
// Return all the pids for all processes running inside the container.
|
||||||
|
processes, err := container.Processes()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Print(processes)
|
||||||
|
|
||||||
|
// Get detailed cpu, memory, io, and network statistics for the container and
|
||||||
|
// it's processes.
|
||||||
|
stats, err := container.Stats()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Print(stats)
|
||||||
|
|
||||||
|
// Pause all processes inside the container.
|
||||||
|
err = container.Pause()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume all paused processes.
|
||||||
|
err = container.Resume()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send signal to container's init process.
|
||||||
|
err = container.Signal(unix.SIGHUP)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update container resource constraints.
|
||||||
|
err = container.Set(*config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current status of the container.
|
||||||
|
status, err := container.Status()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Print(status)
|
||||||
|
|
||||||
|
// Get current container's state information.
|
||||||
|
state, err := container.State()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Print(state)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user