package service import ( "aps_crm/conf" "aps_crm/pkg/logx" "aps_crm/utils" "context" "encoding/json" "errors" "os" "os/exec" "time" ) // DockerImpl 平台下的用户容器管理 type DockerImpl struct{} func (slf *DockerImpl) Image() string { return "/var/data/aps/docker/image" } func (slf *DockerImpl) GetContainerRoot() string { return "/var/data/aps/docker" } // Init 初始化(创建容器,并启动) func (slf *DockerImpl) Init(cid string) error { uDir := slf.GetContainerRoot() + "/" + cid //if !utils.DirExists(uDir + "/db") { if !utils.DirExists(uDir) { err := os.MkdirAll(uDir, os.ModePerm) if err != nil { logx.Errorf("MkdirAll err:", err) return err } } var err error defer func() { if err != nil { logx.Errorf("docker init defer err:", err) os.RemoveAll(uDir) } }() //启动容器 err = slf.Start(cid) if err != nil { logx.Errorf("start container err:", err, " id:", cid) return err } return nil } type ContainerDockerInfo struct { Name string `json:"Name"` Image string `json:"Image"` State struct { Status string `json:"SalesReturnStatus"` Running bool `json:"Running"` } `json:"State"` } // Exist 判断容器是否存在 func (slf *DockerImpl) Exist(cid string) (*ContainerDockerInfo, error) { sudoPwd := conf.Conf.System.SudoPassword if sudoPwd == "" { return nil, errors.New("sudoPassword 配置有误") } sh := "echo " + sudoPwd + "|sudo -S docker inspect " + cid cmd := exec.Command("sh", "-c", sh) ret, _ := cmd.Output() //当前cid容器不存在时,会报exit status 1错误并且 ret是空数组。所以不再判断Output的输出Error var cdi []ContainerDockerInfo e := json.Unmarshal(ret, &cdi) if e != nil { logx.Errorf("unmarshal e:", e) return nil, e } if len(cdi) == 1 { return &cdi[0], nil } return nil, ContainerNotFound } // Start 启动容器 func (slf *DockerImpl) Start(cid string) error { sudoPwd := conf.Conf.System.SudoPassword if sudoPwd == "" { return errors.New("sudoPassword 配置有误") } // 判断容器是否存在 cdi, ce := slf.Exist(cid) logx.Infof("slf.Exist containerInfo:", cdi, " ce:", ce) if ce != nil { // 容器不存在 uhome := slf.GetContainerRoot() + "/" + cid //if !utils.DirExists(uhome + "/db") if !utils.DirExists(uhome) { //err := os.MkdirAll(uhome+"/db", os.ModePerm) err := os.MkdirAll(uhome, os.ModePerm) if err != nil { logx.Errorf("docker Init err:", err) return err } } //sh := "echo " + sudoPwd + "|sudo -S docker run -d --restart=always --net host --name " + cid + " -v " + uhome + "/db:/saas/db saasuser:latest start.sh " + cid sh := "echo " + sudoPwd + "|sudo -S docker run -d --restart=always --net host --name " + cid + " aps-admin:latest /bin/bash /aps/start.sh " + cid cmd := exec.Command("sh", "-c", sh) ret, e := cmd.Output() if e != nil { return e } logx.Infof("docker start container:", cid, " ret:", ret) return nil } if cdi.State.Running { // 容器存在,运行状态 return nil } // 容器存在,停止状态 { sh := "echo " + sudoPwd + "|sudo -S docker start " + cid cmd := exec.Command("sh", "-c", sh) ret, e := cmd.Output() if e != nil { return e } logx.Infof("docker start container:", cid, " ret:", ret) return nil } } // Stop 停止容器 func (slf *DockerImpl) Stop(cid string) error { sudoPwd := conf.Conf.System.SudoPassword if sudoPwd == "" { return errors.New("sudoPassword 配置有误") } sh := "echo " + sudoPwd + "|sudo -S docker stop " + cid + "" cmd := exec.Command("sh", "-c", sh) ret, e := cmd.Output() if e != nil { return e } logx.Infof("docker stop container:", cid, " ret:", ret) return nil } // Restart 重启容器 func (slf *DockerImpl) Restart(cid string) error { sudoPwd := conf.Conf.System.SudoPassword if sudoPwd == "" { return errors.New("sudoPassword 配置有误") } sh := "echo " + sudoPwd + "|sudo -S docker restart " + cid + "" cmd := exec.Command("sh", "-c", sh) ret, e := cmd.Output() if e != nil { return e } logx.Infof("docker restart container:", cid, " ret:", ret) return nil } // BakData 备份数据 func (slf *DockerImpl) BakData(cid string) error { sudoPwd := conf.Conf.System.SudoPassword if sudoPwd == "" { return errors.New("sudoPassword 配置有误") } bdir := slf.GetContainerRoot() + "/bak" if !utils.DirExists(bdir) { err := os.MkdirAll(bdir, os.ModePerm) if err != nil { return err } } sh := "echo " + sudoPwd + "|sudo -S cp -r " + slf.GetContainerRoot() + "/" + cid + " " + bdir cmd := exec.Command("sh", "-c", sh) ret, e := cmd.Output() if e != nil { return e } logx.Infof("docker Bake container:", cid, " ret:", ret) return nil } // Destroy 销毁容器包括数据(销毁前先备份) func (slf *DockerImpl) Destroy(cid string) error { sudoPwd := conf.Conf.System.SudoPassword if sudoPwd == "" { return errors.New("sudoPassword 配置有误") } //删除前先备份数据 if err := slf.BakData(cid); err != nil { return err } sh := "echo " + sudoPwd + "|sudo -S docker rm -f " + cid + "" cmd := exec.Command("sh", "-c", sh) ret, e := cmd.Output() if e != nil { return e } logx.Infof("docker rm container:", cid, " ret:", ret) dsh := "echo " + sudoPwd + "|sudo -S rm -rf " + slf.GetContainerRoot() + "/" + cid dmd := exec.Command("sh", "-c", dsh) dr, de := dmd.Output() if de != nil { return de } logx.Infof("rm -rf volume: ", slf.GetContainerRoot()+"/"+cid, " dret:", string(dr)) return nil } // 监控是否有未启动的用户容器 func (slf *DockerImpl) watch() { ctx := context.Background() for { select { case <-ctx.Done(): return default: time.Sleep(60 * time.Second) //slf.initContainerNotFound() } } } func (slf *DockerImpl) initContainerNotFound() { //users, _ := model.NewUserSearch(nil).FindNotTotal() //for _, u := range users { // if u.ParentId == "" { //是主账号 // //判断容器是否存在时是否需要加锁?用户注册主账号时是异步请求,创建docker容器没有冲突,因为容器的id不同。 // //拉起容器和用户主动启动容器也没有时间冲突 // _, e := slf.Exist(u.UUID) // if e != ContainerNotFound { // continue // } // if err := slf.Init(u.UUID); err != nil { // logx.Errorf("init ContainerNotFound err:", err) // } // } //} }