docker-archive / classicswarm

Swarm Classic: a container clustering system. Not to be confused with Docker Swarm which is at https://github.com/docker/swarmkit
Apache License 2.0
5.75k stars 1.08k forks source link

Proposal: Use Swarm place holders to supply target host and port to Docker run #1106

Closed vbichov closed 4 years ago

vbichov commented 9 years ago

Overview

Some (many?) containers must know at docker run time, what is the hostname/port of the host they are running on.

Apps that explicitly expose their IP for other apps for example. Many clustered applications / DBs do this, as well as Consul-registrator, Consul-template etc.

example:

docker run -d --net=host --name registrator -v /var/run/docker.sock:/tmp/docker.sock gliderlabs/registrator consul://$(hostname -i):8500

Problem

When using Swarm, one does not know the target hostname/IP because the target host is unknown by design.

The needed data cannot be provided and so, Swarm cannot be used to run such containers.

It may be tempting for one to view this as a "Docker" daemon issue: In a similar fashion as Swarm does, one may use a Docker client to connect to a Docker daemon and attempt to run such a container. One will not be able to use: docker run ... -e my_ip=$(hostname -i) ...

However, that is not the case. When a user or automated system connect to a remote docker daemon, they possess the information of what host is being targeted.

Not so when using Swarm. Swarm, and only Swarm holds the information of what the target host is, and Swarm is not in a sharing mood.

Solution

Add Swarm placeholders: swarm_target_ip, swarm_target_host.

When finding these in the swarm run command, Swarm will replace these with the needed data.

pros: simple, works. cons: ugly. alternatives: ???

code example for the fix: (works great)

In cluster/swarm/cluster.go

// CreateContainer aka schedule a brand new container into the cluster.
func (c *Cluster) CreateContainer(config *cluster.ContainerConfig, name string) (*cluster.Container, error) {
    c.scheduler.Lock()
    defer c.scheduler.Unlock()

    // Ensure the name is avaliable
    if cID := c.getIDFromName(name); cID != "" {
        return nil, fmt.Errorf("Conflict, The name %s is already assigned to %s. You have to delete (or rename) that container to be able to assign %s to a container again.", name, cID, name)
    }

    // Associate a Swarm ID to the container we are creating.
    config.SetSwarmID(c.generateUniqueID())

    n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), config)
    if err != nil {
        return nil, err
    }

    TranslateSwarmVars(config, n)

    if nn, ok := c.engines[n.ID]; ok {
        container, err := nn.Create(config, name, true)
        if err != nil {
            return nil, err
        }

        st := &state.RequestedState{
            ID:     container.Id,
            Name:   name,
            Config: config,
        }
        return container, c.store.Add(container.Id, st)
    }

    return nil, nil
}

//Translate Swarm target host variables
func TranslateSwarmVars(config *cluster.ContainerConfig,  node *node.Node) {

    config.Hostname = strings.Replace(config.Hostname, "swarm_target_ip", node.IP, -1)
    config.Hostname = strings.Replace(config.Hostname, "swarm_target_host", node.Name, -1)

    for i := 0; i < len(config.Env); i++ {
        config.Env[i] = strings.Replace(config.Env[i], "swarm_target_ip", node.IP, -1)
        config.Env[i] = strings.Replace(config.Env[i], "swarm_target_host", node.Name, -1)
    }
    for i := 0; i < len(config.Cmd); i++ {
        config.Cmd[i] = strings.Replace(config.Cmd[i], "swarm_target_ip", node.IP, -1)
        config.Cmd[i] = strings.Replace(config.Cmd[i], "swarm_target_host", node.Name, -1)
    }
    for i := 0; i < len(config.Entrypoint); i++ {
        config.Entrypoint[i] = strings.Replace(config.Entrypoint[i], "swarm_target_ip", node.IP, -1)
        config.Entrypoint[i] = strings.Replace(config.Entrypoint[i], "swarm_target_host", node.Name, -1)
    }
}
buluxan commented 5 years ago