使用crypto/ssh做的简单远程发布工具,scp,kill,start
昨天项目组中的一个兄弟,问我我们远程发布是怎么做的,说现在他在维护好几个项目,每个项目需要部署的时候,要把编译好的文件拖过去,然后重启tomcat,本来很简单的事情,但是很浪费时间,问我有没有办法脚本化
他自己的机器是windows的,scp命令可以通过安装解决,但是要远程关闭、开启tomcat,却不是脚本解决的事情,应该通过ssh解决,然后又需要放在其他机器上拿过去用,我就直接给提用golang实现了一个,代码如下:
package main
import (
"bufio"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"os"
"path/filepath"
"strings"
"golang.org/x/crypto/ssh"
)
var (
auth string
publicKey string
passwd string
user string
ip_port string
dpath string
spath string
tomcatPath string
killName string
)
func main() {
log.SetFlags(log.Lshortfile | log.Ltime | log.Ldate)
flag.Parse()
//read config file
myConfig := new(Config)
myConfig.InitConfig("./config.conf")
auth = myConfig.Read("test", "auth")
publicKey = myConfig.Read("test", "publicKey")
passwd = myConfig.Read("test", "passwd")
user = myConfig.Read("test", "user")
ip_port = myConfig.Read("test", "ip_port")
dpath = myConfig.Read("test", "dpath")
spath = myConfig.Read("test", "spath")
tomcatPath = myConfig.Read("test", "tomcatPath")
killName = myConfig.Read("test", "killName")
//目标文件
// File, err := os.Open(spath)
// if err != nil {
// fmt.Println("打开文件失败:", err)
// os.Exit(1)
// }
// info, _ := File.Stat()
// defer File.Close()
var Client *ssh.Client
var err error
if strings.EqualFold(auth, "password") {
Client, err = dail(user, passwd, ip_port)
} else {
Client, err = dailPublic(user, publicKey, ip_port)
}
if err != nil {
fmt.Printf("连接%s失败.\n", err)
}
defer Client.Close()
// scp(Client, File, info.Size(), dpath)
session, err := Client.NewSession()
defer session.Close()
if err != nil {
fmt.Println("创建Session失败:", err)
return
}
//遍历并复制
err = filepath.Walk(spath, func(path string, f os.FileInfo, err error) error {
if f == nil {
return err
}
if f.IsDir() {
newpath := strings.Replace(path, spath, dpath, len(spath))
dname := strings.Replace(newpath, "\\", "/", -1)
mkdir(Client, dname)
return nil
}
//不是路径的,就复制文件
File, err := os.Open(path)
if err != nil {
fmt.Println("打开文件失败:", err)
os.Exit(1)
}
info, _ := File.Stat()
defer File.Close()
newpath := strings.Replace(path, spath, dpath, len(spath))
dname := strings.Replace(newpath, "\\", "/", -1)
fmt.Printf("dname %s\n", dname)
scp(Client, File, info.Size(), dname)
return nil
})
//停止tomcat
kill(Client, killName)
//启动tomcat
start(Client, tomcatPath)
}
func scp(client *ssh.Client, File io.Reader, size int64, path string) {
filename := filepath.Base(path)
dirname := strings.Replace(filepath.Dir(path), "\\", "/", -1)
session, err := client.NewSession()
if err != nil {
fmt.Println("创建Session失败:", err)
return
}
go func() {
w, _ := session.StdinPipe()
fmt.Fprintln(w, "C0644", size, filename)
io.CopyN(w, File, size)
fmt.Fprint(w, "\x00")
w.Close()
}()
fmt.Println("dir name is %s", dirname)
if err := session.Run(fmt.Sprintf("/usr/bin/scp -qrt %s/", dirname)); err != nil {
fmt.Println("执行scp命令失败:", err)
if err != nil {
session.Close()
return
}
} else {
fmt.Printf("%s 发送成功.\n")
session.Close()
}
if session, err = client.NewSession(); err == nil {
defer session.Close()
buf, err := session.Output(fmt.Sprintf("/usr/bin/md5sum %s", path))
if err != nil {
fmt.Println("检查md5失败:", err)
return
}
fmt.Printf("MD5:\n%s\n", string(buf))
}
}
func mkdir(client *ssh.Client, path string) {
fmt.Printf("create path %s\n", path)
session, err := client.NewSession()
if err != nil {
fmt.Println("创建Session失败:", err)
return
}
session.Run(fmt.Sprintf("[ ! -d %s ] && mkdir %s", path, path))
defer session.Close()
}
func kill(client *ssh.Client, path string) {
fmt.Printf("kill tomcat path is %s\n", path)
session, err := client.NewSession()
if err != nil {
fmt.Println("创建Session失败:", err)
return
}
session.Run(fmt.Sprintf("kill -9 `ps -ef|grep %s|grep -v 'grep'|awk '{print $2}'`", path))
defer session.Close()
}
func start(client *ssh.Client, path string) {
fmt.Printf("start tomcat path is %s\n", path)
session, err := client.NewSession()
if err != nil {
fmt.Println("创建Session失败:", err)
return
}
mt := fmt.Sprintf("source /etc/profile && %s/startup.sh", path)
fmt.Println(mt)
session.Stdout = os.Stdout
session.Stderr = os.Stderr
err = session.Run(mt)
if err != nil {
fmt.Println(err)
}
defer session.Close()
}
func dail(user, password, ip_port string) (*ssh.Client, error) {
PassWd := []ssh.AuthMethod{ssh.Password(password)}
Conf := ssh.ClientConfig{User: user, Auth: PassWd, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
return ssh.Dial("tcp", ip_port, &Conf)
}
func dailPublic(user, publicKey, ip_port string) (*ssh.Client, error) {
if strings.HasSuffix(publicKey, ".pub") {
publicKey = strings.TrimSuffix(publicKey, ".pub")
}
signer, err := readPrivateKey(publicKey)
if err != nil {
fmt.Println(err)
}
PassWd := []ssh.AuthMethod{ssh.PublicKeys(signer)}
Conf := ssh.ClientConfig{User: user, Auth: PassWd, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
return ssh.Dial("tcp", ip_port, &Conf)
}
func readPrivateKey(path string) (ssh.Signer, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
b, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
return ssh.ParsePrivateKey(b)
}
//配置
type Config struct {
Mymap map[string]string
strcet string
}
func (c *Config) InitConfig(path string) {
c.Mymap = make(map[string]string)
f, err := os.Open(path)
if err != nil {
panic(err)
}
defer f.Close()
r := bufio.NewReader(f)
for {
b, _, err := r.ReadLine()
if err != nil {
if err == io.EOF {
break
}
panic(err)
}
s := strings.TrimSpace(string(b))
//fmt.Println(s)
if strings.Index(s, "#") == 0 {
continue
}
n1 := strings.Index(s, "[")
n2 := strings.LastIndex(s, "]")
if n1 > -1 && n2 > -1 && n2 > n1+1 {
c.strcet = strings.TrimSpace(s[n1+1 : n2])
continue
}
if len(c.strcet) == 0 {
continue
}
index := strings.Index(s, "=")
if index < 0 {
continue
}
frist := strings.TrimSpace(s[:index])
if len(frist) == 0 {
continue
}
second := strings.TrimSpace(s[index+1:])
pos := strings.Index(second, "\t#")
if pos > -1 {
second = second[0:pos]
}
pos = strings.Index(second, " #")
if pos > -1 {
second = second[0:pos]
}
pos = strings.Index(second, "\t//")
if pos > -1 {
second = second[0:pos]
}
pos = strings.Index(second, " //")
if pos > -1 {
second = second[0:pos]
}
if len(second) == 0 {
continue
}
key := c.strcet + "." + frist
c.Mymap[key] = strings.TrimSpace(second)
}
}
func (c Config) Read(node, key string) string {
key = node + "." + key
v, found := c.Mymap[key]
if !found {
return ""
}
return v
}
使用配置文件指定参数:
[test] auth = key #password or key,现在只做了password和rsa公钥 publicKey = C:\Users\issuser\Desktop\key\id_rsa passwd = xxxx user = xxxx ip_port = xx.xx.xx.xx:22 dpath = /work/ovuems/dapingAgent/webapps/dapingAgent/WEB-INF/classes/com #目标路径 spath = D:\ideaSpace\dapingAgent\target\classes\com #本地路径 killName = /work/ovuems/dapingAgent/bin #和下面的tomcat路径一致,防止需要杀掉多个进程的时候,用于灵活处理 tomcatPath = /work/ovuems/dapingAgent/bin #tomcat路径,到bin这一级
说明:
该项目只做了4件事
1、连接ssh
2、复制本地编译好的class文件到远端(递归方式)
3、kill掉tomcat
4、启动tomcat
发现问题,启动tomcat的时候,启动不了,打印出来的消息为:
Neither the JAVA_HOME nor the JRE_HOME environment variable is defined At least one of these environment variable is needed to run this program
但是在机器上面查找,发现环境变量已经配置,没有办法,执行之前加上了 source /etc/profile予以解决

浙公网安备 33010602011771号