1. Added a basic version of network stats inside network package.
2. Introducing a new checkpoint file 'network.stats' which will contain the network runtime information (veth interface names for now). 3. Adding network stats to 'nsinit stats'. 4. Added a libcontainer Stats API to get both network and cgroup stats Docker-DCO-1.1-Signed-off-by: Vishnu Kannan <vishnuk@google.com> (github: vishh)
This commit is contained in:
parent
ce034dff2e
commit
9253412ee1
|
@ -0,0 +1,25 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/libcontainer"
|
||||||
|
"github.com/docker/libcontainer/cgroups/fs"
|
||||||
|
|
||||||
|
"github.com/docker/libcontainer/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Returns all available stats for the given container.
|
||||||
|
func GetContainerStats(container *libcontainer.Container, networkInfo *network.NetworkRuntimeInfo) (*ContainerStats, error) {
|
||||||
|
containerStats := NewContainerStats()
|
||||||
|
stats, err := fs.GetStats(container.Cgroups)
|
||||||
|
if err != nil {
|
||||||
|
return containerStats, err
|
||||||
|
}
|
||||||
|
containerStats.CgroupStats = stats
|
||||||
|
networkStats, err := network.GetStats(networkInfo)
|
||||||
|
if err != nil {
|
||||||
|
return containerStats, err
|
||||||
|
}
|
||||||
|
containerStats.NetworkStats = networkStats
|
||||||
|
|
||||||
|
return containerStats, nil
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/libcontainer/cgroups"
|
||||||
|
"github.com/docker/libcontainer/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContainerStats struct {
|
||||||
|
NetworkStats network.NetworkStats `json:"network_stats, omitempty"`
|
||||||
|
CgroupStats *cgroups.Stats `json:"cgroup_stats, omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContainerStats() *ContainerStats {
|
||||||
|
cgroupStats := cgroups.NewStats()
|
||||||
|
return &ContainerStats{CgroupStats: cgroupStats}
|
||||||
|
}
|
|
@ -81,7 +81,7 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string
|
||||||
defer cleaner.Cleanup()
|
defer cleaner.Cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := InitializeNetworking(container, command.Process.Pid, syncPipe); err != nil {
|
if err := InitializeNetworking(container, command.Process.Pid, syncPipe, dataPath); err != nil {
|
||||||
command.Process.Kill()
|
command.Process.Kill()
|
||||||
command.Wait()
|
command.Wait()
|
||||||
return -1, err
|
return -1, err
|
||||||
|
@ -156,14 +156,14 @@ func SetupCgroups(container *libcontainer.Config, nspid int) (cgroups.ActiveCgro
|
||||||
|
|
||||||
// InitializeNetworking creates the container's network stack outside of the namespace and moves
|
// InitializeNetworking creates the container's network stack outside of the namespace and moves
|
||||||
// interfaces into the container's net namespaces if necessary
|
// interfaces into the container's net namespaces if necessary
|
||||||
func InitializeNetworking(container *libcontainer.Config, nspid int, pipe *SyncPipe) error {
|
func InitializeNetworking(container *libcontainer.Config, nspid int, pipe *SyncPipe, dataPath string) error {
|
||||||
context := map[string]string{}
|
context := map[string]string{}
|
||||||
for _, config := range container.Networks {
|
for _, config := range container.Networks {
|
||||||
strategy, err := network.GetStrategy(config.Type)
|
strategy, err := network.GetStrategy(config.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := strategy.Create((*network.Network)(config), nspid, context); err != nil {
|
if err := strategy.Create((*network.Network)(config), nspid, context, dataPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Struct describing the network specific checkpoint that will be maintained by libcontainer for all running containers
|
||||||
|
// This is an internal checkpoint, so do not depend on it outside of libcontainer.
|
||||||
|
type NetworkRuntimeInfo struct {
|
||||||
|
// The name of the veth interface on the Host.
|
||||||
|
VethHost string `json:"veth_host,omitempty"`
|
||||||
|
// The name of the veth interface created inside the container for the child.
|
||||||
|
VethChild string `json:"veth_child,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// The name of the network checkpoint file
|
||||||
|
const networkInfoFile = "network.json"
|
||||||
|
|
||||||
|
var ErrNetworkRuntimeInfoNotFound = errors.New("Network Checkpoint not found")
|
||||||
|
|
||||||
|
// Returns the path to the network checkpoint given the path to the base directory of network checkpoint.
|
||||||
|
func getNetworkRuntimeInfoPath(basePath string) string {
|
||||||
|
return filepath.Join(basePath, networkInfoFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshalls the input network runtime info struct into a json object and stores it inside basepath.
|
||||||
|
func writeNetworkRuntimeInfo(networkInfo *NetworkRuntimeInfo, basePath string) error {
|
||||||
|
data, err := json.Marshal(networkInfo)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to checkpoint network runtime information - %s", err)
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(getNetworkRuntimeInfoPath(basePath), data, 0655)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads the network runtime info from the checkpoint and returns the unmarshaled content.
|
||||||
|
func LoadNetworkRuntimeInfo(basePath string) (NetworkRuntimeInfo, error) {
|
||||||
|
var networkRuntimeInfo NetworkRuntimeInfo
|
||||||
|
checkpointPath := getNetworkRuntimeInfoPath(basePath)
|
||||||
|
f, err := os.Open(checkpointPath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return networkRuntimeInfo, ErrNetworkRuntimeInfoNotFound
|
||||||
|
}
|
||||||
|
return networkRuntimeInfo, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
if err := json.NewDecoder(f).Decode(&networkRuntimeInfo); err != nil {
|
||||||
|
return networkRuntimeInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return networkRuntimeInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes the network checkpoint under basePath
|
||||||
|
func deleteNetworkRuntimeInfo(basePath string) error {
|
||||||
|
return os.Remove(getNetworkRuntimeInfoPath(basePath))
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ import (
|
||||||
type Loopback struct {
|
type Loopback struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Loopback) Create(n *Network, nspid int, context map[string]string) error {
|
func (l *Loopback) Create(n *Network, nspid int, context map[string]string, _ string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
type NetNS struct {
|
type NetNS struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *NetNS) Create(n *Network, nspid int, context map[string]string) error {
|
func (v *NetNS) Create(n *Network, nspid int, context map[string]string, _ string) error {
|
||||||
context["nspath"] = n.NsPath
|
context["nspath"] = n.NsPath
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NetworkStats struct {
|
||||||
|
RxBytes uint64 `json:"rx_bytes,omitempty"`
|
||||||
|
RxPackets uint64 `json:"rx_packets,omitempty"`
|
||||||
|
RxErrors uint64 `json:"rx_errors,omitempty"`
|
||||||
|
RxDropped uint64 `json:"rx_dropped,omitempty"`
|
||||||
|
TxBytes uint64 `json:"tx_bytes,omitempty"`
|
||||||
|
TxPackets uint64 `json:"tx_packets,omitempty"`
|
||||||
|
TxErrors uint64 `json:"tx_errors,omitempty"`
|
||||||
|
TxDropped uint64 `json:"tx_dropped,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the network statistics for the network interfaces represented by the NetworkRuntimeInfo.
|
||||||
|
func GetStats(networkRuntimeInfo *NetworkRuntimeInfo) (NetworkStats, error) {
|
||||||
|
// This can happen if the network runtime information is missing - possible if the container was created by an old version of libcontainer.
|
||||||
|
if networkRuntimeInfo.VethHost == "" {
|
||||||
|
return NetworkStats{}, nil
|
||||||
|
}
|
||||||
|
data, err := readSysfsNetworkStats(networkRuntimeInfo.VethHost)
|
||||||
|
if err != nil {
|
||||||
|
return NetworkStats{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NetworkStats{RxBytes: data["rx_bytes"],
|
||||||
|
RxPackets: data["rx_packets"],
|
||||||
|
RxErrors: data["rx_errors"],
|
||||||
|
RxDropped: data["rx_dropped"],
|
||||||
|
TxBytes: data["tx_bytes"],
|
||||||
|
TxPackets: data["tx_packets"],
|
||||||
|
TxErrors: data["tx_errors"],
|
||||||
|
TxDropped: data["tx_dropped"]}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads all the statistics available under /sys/class/net/<EthInterface>/statistics as a map with file name as key and data as integers.
|
||||||
|
func readSysfsNetworkStats(ethInterface string) (map[string]uint64, error) {
|
||||||
|
out := make(map[string]uint64)
|
||||||
|
|
||||||
|
fullPath, err := filepath.Abs(filepath.Join("/sys/class/net", ethInterface, "statistics/"))
|
||||||
|
if err != nil {
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
err = filepath.Walk(fullPath, func(path string, _ os.FileInfo, _ error) error {
|
||||||
|
// skip fullPath.
|
||||||
|
if path == fullPath {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
base := filepath.Base(path)
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value, err := strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out[base] = value
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return out, err
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ var strategies = map[string]NetworkStrategy{
|
||||||
// NetworkStrategy represents a specific network configuration for
|
// NetworkStrategy represents a specific network configuration for
|
||||||
// a container's networking stack
|
// a container's networking stack
|
||||||
type NetworkStrategy interface {
|
type NetworkStrategy interface {
|
||||||
Create(*Network, int, map[string]string) error
|
Create(*Network, int, map[string]string, string) error
|
||||||
Initialize(*Network, map[string]string) error
|
Initialize(*Network, map[string]string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ type Veth struct {
|
||||||
|
|
||||||
const defaultDevice = "eth0"
|
const defaultDevice = "eth0"
|
||||||
|
|
||||||
func (v *Veth) Create(n *Network, nspid int, context map[string]string) error {
|
func (v *Veth) Create(n *Network, nspid int, context map[string]string, dataPath string) error {
|
||||||
var (
|
var (
|
||||||
bridge = n.Bridge
|
bridge = n.Bridge
|
||||||
prefix = n.VethPrefix
|
prefix = n.VethPrefix
|
||||||
|
@ -45,7 +45,8 @@ func (v *Veth) Create(n *Network, nspid int, context map[string]string) error {
|
||||||
if err := SetInterfaceInNamespacePid(name2, nspid); err != nil {
|
if err := SetInterfaceInNamespacePid(name2, nspid); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
networkRuntimeInfo := NetworkRuntimeInfo{VethHost: name1, VethChild: name2}
|
||||||
|
return writeNetworkRuntimeInfo(&networkRuntimeInfo, dataPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Veth) Initialize(config *Network, context map[string]string) error {
|
func (v *Veth) Initialize(config *Network, context map[string]string) error {
|
||||||
|
|
|
@ -7,7 +7,8 @@ import (
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/docker/libcontainer"
|
"github.com/docker/libcontainer"
|
||||||
"github.com/docker/libcontainer/cgroups/fs"
|
"github.com/docker/libcontainer/api"
|
||||||
|
"github.com/docker/libcontainer/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
var statsCommand = cli.Command{
|
var statsCommand = cli.Command{
|
||||||
|
@ -22,7 +23,12 @@ func statsAction(context *cli.Context) {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stats, err := getContainerStats(container)
|
networkRuntimeInfo, err := loadNetworkRuntimeInfo()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, err := getContainerStats(container, &networkRuntimeInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to get stats - %v\n", err)
|
log.Fatalf("Failed to get stats - %v\n", err)
|
||||||
}
|
}
|
||||||
|
@ -31,8 +37,8 @@ func statsAction(context *cli.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the container stats in json format.
|
// returns the container stats in json format.
|
||||||
func getContainerStats(container *libcontainer.Config) (string, error) {
|
func getContainerStats(container *libcontainer.Config, networkRuntimeInfo *network.NetworkRuntimeInfo) (string, error) {
|
||||||
stats, err := fs.GetStats(container.Cgroups)
|
stats, err := libcontainer.GetContainerStats(container, networkRuntimeInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/docker/libcontainer"
|
"github.com/docker/libcontainer"
|
||||||
|
"github.com/docker/libcontainer/network"
|
||||||
)
|
)
|
||||||
|
|
||||||
func loadContainer() (*libcontainer.Config, error) {
|
func loadContainer() (*libcontainer.Config, error) {
|
||||||
|
@ -24,6 +25,17 @@ func loadContainer() (*libcontainer.Config, error) {
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadNetworkRuntimeInfo() (network.NetworkRuntimeInfo, error) {
|
||||||
|
data, err := network.LoadNetworkRuntimeInfo(dataPath)
|
||||||
|
if err != nil {
|
||||||
|
if err == network.ErrNetworkRuntimeInfoNotFound {
|
||||||
|
return network.NetworkRuntimeInfo{}, nil
|
||||||
|
}
|
||||||
|
return network.NetworkRuntimeInfo{}, err
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
func openLog(name string) error {
|
func openLog(name string) error {
|
||||||
f, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0755)
|
f, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue