網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
前言
最近遇到一個(gè)臨時(shí)需求,需要將客戶(hù)環(huán)境中一個(gè)服務(wù)每天的日志進(jìn)行一系列復(fù)雜處理,并生成數(shù)據(jù)報(bào)表。由于數(shù)據(jù)處理邏輯復(fù)雜,且需要存入數(shù)據(jù)庫(kù),在客戶(hù)環(huán)境使用?shell
?腳本無(wú)法處理,因此就需要將日志先拷貝到本地,再進(jìn)行處理;同時(shí)為了避免每天人工拷貝日志,需要實(shí)現(xiàn)自動(dòng)化,整條鏈路自動(dòng)執(zhí)行,無(wú)需人工干預(yù)。平時(shí)使用?Go
?語(yǔ)言較多,由此就引出了?Go
?語(yǔ)言?ssh
?連接遠(yuǎn)程客戶(hù)服務(wù)器,并利用?scp
?將數(shù)據(jù)拷貝下來(lái)的一系列操作。
說(shuō)明:本文中的示例,均是基于Go1.17 64位機(jī)器
連接遠(yuǎn)程服務(wù)器并執(zhí)行命令(ssh)
如下給出了使用?用戶(hù)名+密碼
?的方式連接遠(yuǎn)程服務(wù)器并執(zhí)行了?/usr/bin/whoami
?命令的示例,步驟如下:
- 生成?
ClientConfig
:想要連接遠(yuǎn)程服務(wù)器,必須要至少指定一種實(shí)現(xiàn)了?Auth
?的?AuthMethod
,我們這里使用密碼的方式;同時(shí)需要提供 一種用于安全校驗(yàn)遠(yuǎn)程服務(wù)端key的方法?HostKeyCallback
,我們這里使用的是不校驗(yàn)的方式?ssh.InsecureIgnoreHostKey()
,生產(chǎn)情況下建議使用?ssh.FixedHostKey(key PublicKey)
; - 調(diào)用?
Dial
?:Dial
?方法與遠(yuǎn)程服務(wù)器建立連接,并返回一個(gè)?client
?; -
NewSession
:NewSession
方法開(kāi)啟一個(gè)會(huì)話(huà),在一個(gè)會(huì)話(huà)內(nèi)可以通過(guò)?Run
?方法執(zhí)行一個(gè)命令。
import?( ?"bytes" ?"fmt" ?"log" ?? ??"golang.org/x/crypto/ssh" ) func?main()?{ ?var?( ??username?=?"your?username" ??password?=?"your?password" ??addr?????=?"ip:22" ?) ? ?config?:=?&ssh.ClientConfig{ ??User:?username, ??Auth:?[]ssh.AuthMethod{ ???ssh.Password(password), ??}, ??HostKeyCallback:?ssh.InsecureIgnoreHostKey(), ?} ?client,?err?:=?ssh.Dial("tcp",?addr,?config) ?if?err?!=?nil?{ ??log.Fatal("Failed?to?dial:?",?err) ?} ?defer?client.Close() ?//?開(kāi)啟一個(gè)session,用于執(zhí)行一個(gè)命令 ?session,?err?:=?client.NewSession() ?if?err?!=?nil?{ ??log.Fatal("Failed?to?create?session:?",?err) ?} ?defer?session.Close() ?//?執(zhí)行命令,并將執(zhí)行的結(jié)果寫(xiě)到?b?中 ?var?b?bytes.Buffer ?session.Stdout?=?&b ?? ??//?也可以使用?session.CombinedOutput()?整合輸出 ?if?err?:=?session.Run("/usr/bin/whoami");?err?!=?nil?{ ??log.Fatal("Failed?to?run:?"?+?err.Error()) ?} ?fmt.Println(b.String())??//?root }
上面的例子,我們?cè)?Run
?方法里面?zhèn)魅肓艘粋€(gè)命令,然后遠(yuǎn)程服務(wù)器會(huì)將執(zhí)行結(jié)果返回給我們,如果是復(fù)雜操作,通過(guò)傳入命令的方式就比較麻煩。比如上面提到的需求,需要我從?k8s
?容器中拷貝出服務(wù)每天的日志,拆解后的步驟為:獲取服務(wù)的多個(gè)?k8s pod
?名稱(chēng),根據(jù)當(dāng)前日期,從多個(gè)容器中分別拷貝日志文件,然后整合成一個(gè)日志文件。針對(duì)復(fù)雜操作,我們可以在遠(yuǎn)程服務(wù)器編寫(xiě)一個(gè)腳本,然后?Run
?方法中傳入執(zhí)行腳本的命令。
簡(jiǎn)單舉個(gè)示例,我們?cè)谶h(yuǎn)程服務(wù)器編寫(xiě)了一個(gè)腳本?test.sh
,放在了?/opt
?目錄下,腳本內(nèi)容 與 調(diào)用方式分別如下:
#?腳本文件 #!/bin/bash today=$(date?+"%Y-%m-%d") #?將數(shù)據(jù)寫(xiě)入文件 $(df?-h?>?$today.log)
package?main import?( ?"fmt" ?"log" ?? ??"golang.org/x/crypto/ssh" ) func?main()?{ ?var?( ??username?=?"your?username" ??password?=?"your?password" ??addr?????=?"ip:22" ?) ?config?:=?&ssh.ClientConfig{ ??User:?username, ??Auth:?[]ssh.AuthMethod{ ???ssh.Password(password), ??}, ??HostKeyCallback:?ssh.InsecureIgnoreHostKey(), ?} ?client,?err?:=?ssh.Dial("tcp",?addr,?config) ?if?err?!=?nil?{ ??log.Fatal("Failed?to?dial:?",?err) ?} ?defer?client.Close() ?session,?err?:=?client.NewSession() ?if?err?!=?nil?{ ??log.Fatal("Failed?to?create?session:?",?err) ?} ?defer?session.Close() ??//?調(diào)用遠(yuǎn)程服務(wù)器腳本腳本 ?res,?err?:=?session.CombinedOutput("sh?/opt/test.sh") ?if?err?!=?nil?{ ??log.Fatal("Failed?to?run:?"?+?err.Error()) ?} ?fmt.Println(string(res)) ?? ??/* ??Filesystem??????Size??Used?Avail?Use%?Mounted?on ??devtmpfs????????909M?????0??909M???0%?/dev ??tmpfs???????????919M???24K??919M???1%?/dev/shm ??tmpfs???????????919M??540K??919M???1%?/run ??tmpfs???????????919M?????0??919M???0%?/sys/fs/cgroup ??/dev/vda1????????50G??6.9G???40G??15%?/ ??tmpfs???????????184M?????0??184M???0%?/run/user/0 ??*/ }
拷貝遠(yuǎn)程服務(wù)器文件到本地(scp)
拷貝文件步驟比較簡(jiǎn)單:
- 建立?
ssh client
- 基于?
ssh client
?創(chuàng)建?sftp client
- 打開(kāi)遠(yuǎn)程服務(wù)器文件并拷貝到本地
package?main import?( ?"io" ?"log" ?"os" ?"time" ?? ??"github.com/pkg/sftp" ?"golang.org/x/crypto/ssh" ) func?main()?{ ?var?( ??username?=?"your?username" ??password?=?"your?password" ??addr?????=?"ip:22" ?) ?//?1.?建立?ssh?client ?config?:=?&ssh.ClientConfig{ ??User:?username, ??Auth:?[]ssh.AuthMethod{ ???ssh.Password(password), ??}, ??HostKeyCallback:?ssh.InsecureIgnoreHostKey(), ?} ?client,?err?:=?ssh.Dial("tcp",?addr,?config) ?if?err?!=?nil?{ ??log.Fatal("Failed?to?dial:?",?err) ?} ?defer?client.Close() ?//?2.?基于ssh?client,?創(chuàng)建?sftp?客戶(hù)端 ?sftpClient,?err?:=?sftp.NewClient(client) ?if?err?!=?nil?{ ??log.Fatal("Failed?to?init?sftp?client:?",?err) ?} ?defer?sftpClient.Close() ?//?3.?打開(kāi)遠(yuǎn)程服務(wù)器文件 ?filename?:=?time.Now().Format("2006-01-02")?+?".log" ?source,?err?:=?sftpClient.Open("/opt/"?+?filename) ?if?err?!=?nil?{ ??log.Fatal("Failed?to?open?remote?file:?",?err) ?} ?defer?source.Close() ?//?4.?創(chuàng)建本地文件 ?target,?err?:=?os.OpenFile(filename,?os.O_RDWR|os.O_CREATE|os.O_TRUNC,?0644) ?if?err?!=?nil?{ ??log.Fatal("Failed?to?open?local?file:?",?err) ?} ?defer?target.Close() ?//?5.?數(shù)據(jù)復(fù)制 ?n,?err?:=?io.Copy(target,?source) ?if?err?!=?nil?{ ??log.Fatal("Failed?to?copy?file:?",?err) ?} ?log.Println("Succeed?to?copy?file:?",?n) }
在?sftp client
?中,還有許多方法,例如?Walk
、ReadDir
、Stat
、Mkdir
等,針對(duì)文件也有?Read
、Write
、WriteTo
、ReadFrom
等方法,像操作本地文件系統(tǒng)一樣,非常便利。
簡(jiǎn)單封裝下
package?main import?( ?"fmt" ?"io" ?"log" ?"os" ?"time" ?"github.com/pkg/sftp" ?"golang.org/x/crypto/ssh" ) type?Cli?struct?{ ?user???string ?pwd????string ?addr???string ?client?*ssh.Client } func?NewCli(user,?pwd,?addr?string)?Cli?{ ?return?Cli{ ??user:?user, ??pwd:??pwd, ??addr:?addr, ?} } //?Connect?連接遠(yuǎn)程服務(wù)器 func?(c?*Cli)?Connect()?error?{ ?config?:=?&ssh.ClientConfig{ ??User:?c.user, ??Auth:?[]ssh.AuthMethod{ ???ssh.Password(c.pwd), ??}, ??HostKeyCallback:?ssh.InsecureIgnoreHostKey(), ?} ?client,?err?:=?ssh.Dial("tcp",?c.addr,?config) ?if?nil?!=?err?{ ??return?fmt.Errorf("connect?server?error:?%w",?err) ?} ?c.client?=?client ?return?nil } //?Run?運(yùn)行命令 func?(c?Cli)?Run(shell?string)?(string,?error)?{ ?if?c.client?==?nil?{ ??if?err?:=?c.Connect();?err?!=?nil?{ ???return?"",?err ??} ?} ?session,?err?:=?c.client.NewSession() ?if?err?!=?nil?{ ??return?"",?fmt.Errorf("create?new?session?error:?%w",?err) ?} ?defer?session.Close() ?buf,?err?:=?session.CombinedOutput(shell) ?return?string(buf),?err } //?Scp?復(fù)制文件 func?(c?Cli)?Scp(srcFileName,?targetFileName?string)?(int64,?error)?{ ?if?c.client?==?nil?{ ??if?err?:=?c.Connect();?err?!=?nil?{ ???return?0,?err ??} ?} ?sftpClient,?err?:=?sftp.NewClient(c.client) ?if?err?!=?nil?{ ??return?0,?fmt.Errorf("new?sftp?client?error:?%w",?err) ?} ?defer?sftpClient.Close() ?source,?err?:=?sftpClient.Open(srcFileName) ?if?err?!=?nil?{ ??return?0,?fmt.Errorf("sftp?client?open?file?error:?%w",?err) ?} ?defer?source.Close() ?target,?err?:=?os.OpenFile(targetFileName,?os.O_RDWR|os.O_CREATE|os.O_TRUNC,?0644) ?if?err?!=?nil?{ ??return?0,?fmt.Errorf("open?local?file?error:?%w",?err) ?} ?defer?target.Close() ?n,?err?:=?io.Copy(target,?source) ?if?err?!=?nil?{ ??return?0,?fmt.Errorf("copy?file?error:?%w",?err) ?} ?return?n,?nil } //?調(diào)用測(cè)試 func?main()?{ ?var?( ??username?=?"your?username" ??password?=?"your?password" ??addr?????=?"ip:22" ?) ?//?初始化 ?client?:=?NewCli(username,?password,?addr) ?//?ssh?并運(yùn)行腳本 ?_,?err?:=?client.Run("sh?/opt/test.sh") ?if?err?!=?nil?{ ??log.Printf("failed?to?run?shell,err=[%v]\n",?err) ??return ?} ?//?scp?文件到本地 ?filename?:=?time.Now().Format("2006-01-02")?+?".log" ?n,?err?:=?client.Scp("/opt/"+filename,?filename) ?if?err?!=?nil?{ ??log.Printf("failed?to?scp?file,err=[%v]\n",?err) ??return ?} ?log.Printf("Succeed?to?scp?file,?size=[%d]\n",?n) ?//?處理文件并刪除本地文件...... }
通過(guò)上面的一系列操作,就可以實(shí)現(xiàn)了我的需求:
1.編寫(xiě)程序:
- 連接客戶(hù)服務(wù)器
- 執(zhí)行遠(yuǎn)程服務(wù)器的腳本,生成日志文件
- 拷貝遠(yuǎn)程服務(wù)器的日志文件到本地
- 處理日志文件
- 刪除本地文件
2.在服務(wù)器上啟動(dòng)一個(gè)定時(shí)任務(wù)運(yùn)行該程序
原文鏈接:https://mp.weixin.qq.com/s/nVAAMAW0jIeSMx9oJysUjw
相關(guān)推薦
- 2022-09-09 Python?OpenCV?Hough直線(xiàn)檢測(cè)算法的原理實(shí)現(xiàn)_python
- 2022-03-27 Android?Studio實(shí)現(xiàn)井字游戲_Android
- 2022-12-22 C++模擬實(shí)現(xiàn)STL容器vector的示例代碼_C 語(yǔ)言
- 2022-02-15 多標(biāo)簽界面:動(dòng)態(tài)組件 & 異步組件
- 2022-12-22 C++?boost?thread庫(kù)用法詳細(xì)講解_C 語(yǔ)言
- 2022-03-15 Response to preflight request doesn‘t pass access
- 2022-10-12 排查服務(wù)器異常流量教程詳解_nginx
- 2022-05-21 C++實(shí)現(xiàn)快捷店會(huì)員管理系統(tǒng)_C 語(yǔ)言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支