Simplify how the Docker containers are handled.
This is done by introducting the concept of "namespaces" of container names. The aliases of a container are under this namespace. Namespace names are of the form: //<namespace>/<alias> This allows us to (within cAdvisor) query all docker containers as //docker regardless of whether this is a systemd or a non-systemd system. This does break our ability to handle Docker aliases with the /container endpoint. I think this is acceptable as our support there was not consistent between system types.
This commit is contained in:
parent
fbafdbb860
commit
f97e57df88
@ -31,6 +31,9 @@ import (
|
|||||||
|
|
||||||
var ArgDockerEndpoint = flag.String("docker", "unix:///var/run/docker.sock", "docker endpoint")
|
var ArgDockerEndpoint = flag.String("docker", "unix:///var/run/docker.sock", "docker endpoint")
|
||||||
|
|
||||||
|
// The namespace under which Docker aliases are unique.
|
||||||
|
var DockerNamespace = "docker"
|
||||||
|
|
||||||
// Basepath to all container specific information that libcontainer stores.
|
// Basepath to all container specific information that libcontainer stores.
|
||||||
var dockerRootDir = flag.String("docker_root", "/var/lib/docker", "Absolute path to the Docker state root directory (default: /var/lib/docker)")
|
var dockerRootDir = flag.String("docker_root", "/var/lib/docker", "Absolute path to the Docker state root directory (default: /var/lib/docker)")
|
||||||
|
|
||||||
@ -54,7 +57,7 @@ type dockerFactory struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *dockerFactory) String() string {
|
func (self *dockerFactory) String() string {
|
||||||
return "docker"
|
return DockerNamespace
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *dockerFactory) NewContainerHandler(name string) (handler container.ContainerHandler, err error) {
|
func (self *dockerFactory) NewContainerHandler(name string) (handler container.ContainerHandler, err error) {
|
||||||
@ -102,19 +105,14 @@ func ContainerNameToDockerId(name string) string {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a list of possible full container names for the specified Docker container name.
|
// Returns a full container name for the specified Docker ID.
|
||||||
func FullDockerContainerNames(dockerName string) []string {
|
func FullContainerName(dockerId string) string {
|
||||||
names := make([]string, 0, 2)
|
|
||||||
|
|
||||||
// Add the full container name.
|
// Add the full container name.
|
||||||
if useSystemd {
|
if useSystemd {
|
||||||
names = append(names, path.Join("/system.slice", fmt.Sprintf("docker-%s.scope", dockerName)))
|
return path.Join("/system.slice", fmt.Sprintf("docker-%s.scope", dockerId))
|
||||||
} else {
|
} else {
|
||||||
names = append(names, path.Join("/docker", dockerName))
|
return path.Join("/docker", dockerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the Docker alias name.
|
|
||||||
return append(names, path.Join("/docker", dockerName))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Docker handles all containers under /docker
|
// Docker handles all containers under /docker
|
||||||
|
@ -92,7 +92,11 @@ func newDockerContainerHandler(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to inspect container %s - %s\n", id, err)
|
return nil, fmt.Errorf("failed to inspect container %s - %s\n", id, err)
|
||||||
}
|
}
|
||||||
handler.aliases = append(handler.aliases, path.Join("/docker", ctnr.Name))
|
|
||||||
|
// Add the name and bare ID as aliases of the container.
|
||||||
|
handler.aliases = append(handler.aliases, strings.TrimPrefix(ctnr.Name, "/"))
|
||||||
|
handler.aliases = append(handler.aliases, id)
|
||||||
|
|
||||||
return handler, nil
|
return handler, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +104,7 @@ func (self *dockerContainerHandler) ContainerReference() (info.ContainerReferenc
|
|||||||
return info.ContainerReference{
|
return info.ContainerReference{
|
||||||
Name: self.name,
|
Name: self.name,
|
||||||
Aliases: self.aliases,
|
Aliases: self.aliases,
|
||||||
|
Namespace: DockerNamespace,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,12 +302,6 @@ func (self *dockerContainerHandler) ListContainers(listType container.ListType)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// On non-systemd systems Docker containers are under /docker.
|
|
||||||
containerPrefix := "/docker"
|
|
||||||
if useSystemd {
|
|
||||||
containerPrefix = "/system.slice"
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := make([]info.ContainerReference, 0, len(containers)+1)
|
ret := make([]info.ContainerReference, 0, len(containers)+1)
|
||||||
for _, c := range containers {
|
for _, c := range containers {
|
||||||
if !strings.HasPrefix(c.Status, "Up ") {
|
if !strings.HasPrefix(c.Status, "Up ") {
|
||||||
@ -310,8 +309,9 @@ func (self *dockerContainerHandler) ListContainers(listType container.ListType)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ref := info.ContainerReference{
|
ref := info.ContainerReference{
|
||||||
Name: path.Join(containerPrefix, c.ID),
|
Name: FullContainerName(c.ID),
|
||||||
Aliases: c.Names,
|
Aliases: append(c.Names, c.ID),
|
||||||
|
Namespace: DockerNamespace,
|
||||||
}
|
}
|
||||||
ret = append(ret, ref)
|
ret = append(ret, ref)
|
||||||
}
|
}
|
||||||
|
@ -53,10 +53,16 @@ type ContainerSpec struct {
|
|||||||
|
|
||||||
// Container reference contains enough information to uniquely identify a container
|
// Container reference contains enough information to uniquely identify a container
|
||||||
type ContainerReference struct {
|
type ContainerReference struct {
|
||||||
// The absolute name of the container.
|
// The absolute name of the container. This is unique on the machine.
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Other names by which the container is known within a certain namespace.
|
||||||
|
// This is unique within that namespace.
|
||||||
Aliases []string `json:"aliases,omitempty"`
|
Aliases []string `json:"aliases,omitempty"`
|
||||||
|
|
||||||
|
// Namespace under which the aliases of a container are unique.
|
||||||
|
// An example of a namespace is "docker" for Docker containers.
|
||||||
|
Namespace string `json:"namespace,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerInfoQuery is used when users check a container info from the REST api.
|
// ContainerInfoQuery is used when users check a container info from the REST api.
|
||||||
|
@ -104,8 +104,7 @@ func newContainerData(containerName string, driver storage.StorageDriver, handle
|
|||||||
logUsage: logUsage,
|
logUsage: logUsage,
|
||||||
stop: make(chan bool, 1),
|
stop: make(chan bool, 1),
|
||||||
}
|
}
|
||||||
cont.info.Name = ref.Name
|
cont.info.ContainerReference = ref
|
||||||
cont.info.Aliases = ref.Aliases
|
|
||||||
|
|
||||||
return cont, nil
|
return cont, nil
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ func New(driver storage.StorageDriver) (Manager, error) {
|
|||||||
glog.Infof("cAdvisor running in container: %q", selfContainer)
|
glog.Infof("cAdvisor running in container: %q", selfContainer)
|
||||||
|
|
||||||
newManager := &manager{
|
newManager := &manager{
|
||||||
containers: make(map[string]*containerData),
|
containers: make(map[namespacedContainerName]*containerData),
|
||||||
quitChannels: make([]chan error, 0, 2),
|
quitChannels: make([]chan error, 0, 2),
|
||||||
storageDriver: driver,
|
storageDriver: driver,
|
||||||
cadvisorContainer: selfContainer,
|
cadvisorContainer: selfContainer,
|
||||||
@ -99,8 +99,17 @@ func New(driver storage.StorageDriver) (Manager, error) {
|
|||||||
return newManager, nil
|
return newManager, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A namespaced container name.
|
||||||
|
type namespacedContainerName struct {
|
||||||
|
// The namespace of the container. Can be empty for the root namespace.
|
||||||
|
Namespace string
|
||||||
|
|
||||||
|
// The name of the container in this namespace.
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
type manager struct {
|
type manager struct {
|
||||||
containers map[string]*containerData
|
containers map[namespacedContainerName]*containerData
|
||||||
containersLock sync.RWMutex
|
containersLock sync.RWMutex
|
||||||
storageDriver storage.StorageDriver
|
storageDriver storage.StorageDriver
|
||||||
machineInfo info.MachineInfo
|
machineInfo info.MachineInfo
|
||||||
@ -198,7 +207,9 @@ func (self *manager) GetContainerInfo(containerName string, query *info.Containe
|
|||||||
defer self.containersLock.RUnlock()
|
defer self.containersLock.RUnlock()
|
||||||
|
|
||||||
// Ensure we have the container.
|
// Ensure we have the container.
|
||||||
cont, ok = self.containers[containerName]
|
cont, ok = self.containers[namespacedContainerName{
|
||||||
|
Name: containerName,
|
||||||
|
}]
|
||||||
}()
|
}()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown container %q", containerName)
|
return nil, fmt.Errorf("unknown container %q", containerName)
|
||||||
@ -221,10 +232,7 @@ func (self *manager) containerDataToContainerInfo(cont *containerData, query *in
|
|||||||
|
|
||||||
// Make a copy of the info for the user.
|
// Make a copy of the info for the user.
|
||||||
ret := &info.ContainerInfo{
|
ret := &info.ContainerInfo{
|
||||||
ContainerReference: info.ContainerReference{
|
ContainerReference: cinfo.ContainerReference,
|
||||||
Name: cinfo.Name,
|
|
||||||
Aliases: cinfo.Aliases,
|
|
||||||
},
|
|
||||||
Subcontainers: cinfo.Subcontainers,
|
Subcontainers: cinfo.Subcontainers,
|
||||||
Spec: cinfo.Spec,
|
Spec: cinfo.Spec,
|
||||||
Stats: stats,
|
Stats: stats,
|
||||||
@ -275,20 +283,15 @@ func (self *manager) DockerContainersInfo(containerName string, query *info.Cont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Strip first "/"
|
// Check for the container in the Docker container namespace.
|
||||||
containerName = strings.Trim(containerName, "/")
|
cont, ok := self.containers[namespacedContainerName{
|
||||||
|
Namespace: docker.DockerNamespace,
|
||||||
// Get the specified container (check all possible Docker names).
|
Name: containerName,
|
||||||
possibleNames := docker.FullDockerContainerNames(containerName)
|
}]
|
||||||
for _, fullName := range possibleNames {
|
|
||||||
cont, ok := self.containers[fullName]
|
|
||||||
if ok {
|
if ok {
|
||||||
containers = append(containers, cont)
|
containers = append(containers, cont)
|
||||||
break
|
} else {
|
||||||
}
|
return fmt.Errorf("unable to find Docker container %q", containerName)
|
||||||
}
|
|
||||||
if len(containers) == 0 {
|
|
||||||
return fmt.Errorf("unable to find Docker container %q with full names %v", containerName, possibleNames)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -345,16 +348,23 @@ func (m *manager) createContainer(containerName string) error {
|
|||||||
m.containersLock.Lock()
|
m.containersLock.Lock()
|
||||||
defer m.containersLock.Unlock()
|
defer m.containersLock.Unlock()
|
||||||
|
|
||||||
|
namespacedName := namespacedContainerName{
|
||||||
|
Name: containerName,
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the container didn't already exist.
|
// Check that the container didn't already exist.
|
||||||
_, ok := m.containers[containerName]
|
_, ok := m.containers[namespacedName]
|
||||||
if ok {
|
if ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the container name and all its aliases.
|
// Add the container name and all its aliases. The aliases must be within the namespace of the factory.
|
||||||
m.containers[containerName] = cont
|
m.containers[namespacedName] = cont
|
||||||
for _, alias := range cont.info.Aliases {
|
for _, alias := range cont.info.Aliases {
|
||||||
m.containers[alias] = cont
|
m.containers[namespacedContainerName{
|
||||||
|
Namespace: cont.info.Namespace,
|
||||||
|
Name: alias,
|
||||||
|
}] = cont
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
@ -362,7 +372,7 @@ func (m *manager) createContainer(containerName string) error {
|
|||||||
if alreadyExists {
|
if alreadyExists {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
glog.Infof("Added container: %q (aliases: %s)", containerName, cont.info.Aliases)
|
glog.Infof("Added container: %q (aliases: %v, namespace: %q)", containerName, cont.info.Aliases, cont.info.Namespace)
|
||||||
|
|
||||||
// Start the container's housekeeping.
|
// Start the container's housekeeping.
|
||||||
cont.Start()
|
cont.Start()
|
||||||
@ -373,7 +383,10 @@ func (m *manager) destroyContainer(containerName string) error {
|
|||||||
m.containersLock.Lock()
|
m.containersLock.Lock()
|
||||||
defer m.containersLock.Unlock()
|
defer m.containersLock.Unlock()
|
||||||
|
|
||||||
cont, ok := m.containers[containerName]
|
namespacedName := namespacedContainerName{
|
||||||
|
Name: containerName,
|
||||||
|
}
|
||||||
|
cont, ok := m.containers[namespacedName]
|
||||||
if !ok {
|
if !ok {
|
||||||
// Already destroyed, done.
|
// Already destroyed, done.
|
||||||
return nil
|
return nil
|
||||||
@ -386,11 +399,14 @@ func (m *manager) destroyContainer(containerName string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove the container from our records (and all its aliases).
|
// Remove the container from our records (and all its aliases).
|
||||||
delete(m.containers, containerName)
|
delete(m.containers, namespacedName)
|
||||||
for _, alias := range cont.info.Aliases {
|
for _, alias := range cont.info.Aliases {
|
||||||
delete(m.containers, alias)
|
delete(m.containers, namespacedContainerName{
|
||||||
|
Namespace: cont.info.Namespace,
|
||||||
|
Name: alias,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
glog.Infof("Destroyed container: %s (aliases: %s)", containerName, cont.info.Aliases)
|
glog.Infof("Destroyed container: %q (aliases: %v, namespace: %q)", containerName, cont.info.Aliases, cont.info.Namespace)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,7 +416,9 @@ func (m *manager) getContainersDiff(containerName string) (added []info.Containe
|
|||||||
defer m.containersLock.RUnlock()
|
defer m.containersLock.RUnlock()
|
||||||
|
|
||||||
// Get all subcontainers recursively.
|
// Get all subcontainers recursively.
|
||||||
cont, ok := m.containers[containerName]
|
cont, ok := m.containers[namespacedContainerName{
|
||||||
|
Name: containerName,
|
||||||
|
}]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, fmt.Errorf("failed to find container %q while checking for new containers", containerName)
|
return nil, nil, fmt.Errorf("failed to find container %q while checking for new containers", containerName)
|
||||||
}
|
}
|
||||||
@ -414,15 +432,17 @@ func (m *manager) getContainersDiff(containerName string) (added []info.Containe
|
|||||||
allContainersSet := make(map[string]*containerData)
|
allContainersSet := make(map[string]*containerData)
|
||||||
for name, d := range m.containers {
|
for name, d := range m.containers {
|
||||||
// Only add the canonical name.
|
// Only add the canonical name.
|
||||||
if d.info.Name == name {
|
if d.info.Name == name.Name {
|
||||||
allContainersSet[name] = d
|
allContainersSet[name.Name] = d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Added containers
|
// Added containers
|
||||||
for _, c := range allContainers {
|
for _, c := range allContainers {
|
||||||
delete(allContainersSet, c.Name)
|
delete(allContainersSet, c.Name)
|
||||||
_, ok := m.containers[c.Name]
|
_, ok := m.containers[namespacedContainerName{
|
||||||
|
Name: c.Name,
|
||||||
|
}]
|
||||||
if !ok {
|
if !ok {
|
||||||
added = append(added, c)
|
added = append(added, c)
|
||||||
}
|
}
|
||||||
@ -474,7 +494,9 @@ func (self *manager) watchForNewContainers(quit chan error) error {
|
|||||||
func() {
|
func() {
|
||||||
self.containersLock.RLock()
|
self.containersLock.RLock()
|
||||||
defer self.containersLock.RUnlock()
|
defer self.containersLock.RUnlock()
|
||||||
root, ok = self.containers["/"]
|
root, ok = self.containers[namespacedContainerName{
|
||||||
|
Name: "/",
|
||||||
|
}]
|
||||||
}()
|
}()
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("Root container does not exist when watching for new containers")
|
return fmt.Errorf("Root container does not exist when watching for new containers")
|
||||||
|
@ -18,15 +18,19 @@ package manager
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/cadvisor/container"
|
"github.com/google/cadvisor/container"
|
||||||
|
"github.com/google/cadvisor/container/docker"
|
||||||
"github.com/google/cadvisor/info"
|
"github.com/google/cadvisor/info"
|
||||||
itest "github.com/google/cadvisor/info/test"
|
itest "github.com/google/cadvisor/info/test"
|
||||||
stest "github.com/google/cadvisor/storage/test"
|
stest "github.com/google/cadvisor/storage/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO(vmarmol): Refactor these tests.
|
||||||
|
|
||||||
func createManagerAndAddContainers(
|
func createManagerAndAddContainers(
|
||||||
driver *stest.MockStorageDriver,
|
driver *stest.MockStorageDriver,
|
||||||
containers []string,
|
containers []string,
|
||||||
@ -44,10 +48,20 @@ func createManagerAndAddContainers(
|
|||||||
if ret, ok := mif.(*manager); ok {
|
if ret, ok := mif.(*manager); ok {
|
||||||
for _, name := range containers {
|
for _, name := range containers {
|
||||||
mockHandler := container.NewMockContainerHandler(name)
|
mockHandler := container.NewMockContainerHandler(name)
|
||||||
ret.containers[name], err = newContainerData(name, driver, mockHandler, false)
|
cont, err := newContainerData(name, driver, mockHandler, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
ret.containers[namespacedContainerName{
|
||||||
|
Name: name,
|
||||||
|
}] = cont
|
||||||
|
// Add Docker containers under their namespace.
|
||||||
|
if strings.HasPrefix(name, "/docker") {
|
||||||
|
ret.containers[namespacedContainerName{
|
||||||
|
Namespace: docker.DockerNamespace,
|
||||||
|
Name: strings.TrimPrefix(name, "/docker/"),
|
||||||
|
}] = cont
|
||||||
|
}
|
||||||
f(mockHandler)
|
f(mockHandler)
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
|
@ -19,7 +19,7 @@ func serveDockerPage(m manager.Manager, w http.ResponseWriter, u *url.URL) error
|
|||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
// The container name is the path after the handler
|
// The container name is the path after the handler
|
||||||
containerName := u.Path[len(DockerPage)-1:]
|
containerName := u.Path[len(DockerPage):]
|
||||||
|
|
||||||
var data *pageData
|
var data *pageData
|
||||||
if containerName == "/" {
|
if containerName == "/" {
|
||||||
|
Loading…
Reference in New Issue
Block a user