/
scale.go
152 lines (124 loc) · 3.5 KB
/
scale.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package cmd
import (
"fmt"
"strings"
"github.com/emicklei/go-restful"
"github.com/samalba/dockerclient"
)
type ContainerNumberInfo struct {
Current int
Need int
ContainerId string
}
type ScaleResult struct {
Scaled []string
Errors []string
}
func initScaleInfo(info map[string]ContainerNumberInfo, scaleApp []ScaleApp) {
for _, v := range scaleApp {
info[v.App] = ContainerNumberInfo{Current: 0, Need: v.Number, ContainerId: ""}
}
}
func releaseContainers(info map[string]ContainerNumberInfo, client *dockerclient.DockerClient) {
containers, err := client.ListContainers(true, false, "")
if err != nil {
}
for _, c := range containers {
fmt.Printf("container image name:%s\n", c.Image)
containerNumberInfo, ok := info[c.Image]
if !ok {
fmt.Printf("out of scale:%s\n", c.Image)
continue
}
containerNumberInfo.Current++
if containerNumberInfo.ContainerId == "" {
containerNumberInfo.ContainerId = c.Id
}
info[c.Image] = containerNumberInfo
cid, ok2 := info[c.Image]
if ok2 {
fmt.Printf("image [%s] container id is:%s\n", c.Image, cid.ContainerId)
}
if containerNumberInfo.Current > containerNumberInfo.Need {
// stop container with 5 seconds timeout
client.StopContainer(c.Id, 5)
// force remove, delete volume
client.RemoveContainer(c.Id, true, true)
}
}
}
func deployContainers(info map[string]ContainerNumberInfo, client *dockerclient.DockerClient) {
for _, v := range info {
if v.Current < v.Need {
n := v.Need - v.Current
scaleContainer(v.ContainerId, n, client)
}
}
}
func scaleContainer(id string, numInstances int, client *dockerclient.DockerClient) ScaleResult {
var (
errChan = make(chan (error))
resChan = make(chan (string))
result = ScaleResult{Scaled: make([]string, 0), Errors: make([]string, 0)}
)
// docker client get container info
containerInfo, err := client.InspectContainer(id)
if err != nil {
result.Errors = append(result.Errors, err.Error())
return result
}
for i := 0; i < numInstances; i++ {
go func(instance int) {
config := containerInfo.Config
// clear hostname to get a newly generated
config.Hostname = ""
hostConfig := containerInfo.HostConfig
config.HostConfig = *hostConfig // sending hostconfig via the Start-endpoint is deprecated starting with docker-engine 1.12
// using docker client create Container
id, err := client.CreateContainer(config, "", nil)
if err != nil {
errChan <- err
return
}
// using docker client start container
if err := client.StartContainer(id, hostConfig); err != nil {
errChan <- err
return
}
resChan <- id
}(i)
}
for i := 0; i < numInstances; i++ {
select {
case id := <-resChan:
result.Scaled = append(result.Scaled, id)
case err := <-errChan:
result.Errors = append(result.Errors, strings.TrimSpace(err.Error()))
}
}
return result
}
func (this PluginResource) scaleApp(request *restful.Request,
response *restful.Response) {
scaleApp := []ScaleApp{}
dockerClient, err := dockerclient.NewDockerClient(DockerHost, nil)
if err != nil {
fmt.Printf("init docker client error:%s", err)
}
err = request.ReadEntity(&scaleApp)
if err != nil {
}
fmt.Println("scale : ", scaleApp)
/*
{
"ats:latest":{2, 20}
"hadoop:v1.0":{20, 2}
}
*/
scaleInfo := make(map[string]ContainerNumberInfo)
initScaleInfo(scaleInfo, scaleApp)
fmt.Println("map info: ", scaleInfo)
releaseContainers(scaleInfo, dockerClient)
deployContainers(scaleInfo, dockerClient)
//fmt.Println("scale map: ", scaleInfo["ats"])
}