Preface
Recently, I encountered a temporary requirement that a service's daily log in a customer environment needs to be processed in a series of complex processes and generate data reports. Because the data processing logic is complex and needs to be stored in the database, it is used in the customer environment.shell
The script cannot be processed, so the log needs to be copied to the local area first and then processed; at the same time, in order to avoid manual copying of logs every day, automation is needed, and the entire link is automatically executed without manual intervention. Usually usedGo
There are many languages, which leads toGo
languagessh
Connect to remote client server and usescp
A series of operations to copy the data.
Note: The examples in this article are all based on Go1.17 64-bit machines
Connect to the remote server and execute commands (ssh)
Use as given belowUsername + Password
Connect to the remote server and execute it/usr/bin/whoami
Example of the command, the steps are as follows:
- Generate
ClientConfig
: If you want to connect to a remote server, you must specify at least one implementation.Auth
ofAuthMethod
, We use passwords here; at the same time, we need to provide a method for secure verification of remote server keys.HostKeyCallback
, we are using the method of non-checking here.()
, it is recommended to use it in production(key PublicKey)
; - Call
Dial
:Dial
Method establishes a connection with the remote server and returns aclient
; -
NewSession
:NewSession
Method to open a session, which can be passed in one sessionRun
Method executes a command.
import ( "bytes" "fmt" "log" "/x/crypto/ssh" ) func main() { var ( username = "your username" password = "your password" addr = "ip:22" ) config := &{ User: username, Auth: []{ (password), }, HostKeyCallback: (), } client, err := ("tcp", addr, config) if err != nil { ("Failed to dial: ", err) } defer () // Open a session to execute a command session, err := () if err != nil { ("Failed to create session: ", err) } defer () // Execute the command and write the execution result into b var b = &b // You can also use() to integrate the output if err := ("/usr/bin/whoami"); err != nil { ("Failed to run: " + ()) } (()) // root }
In the above example, we areRun
A command is passed into the method, and the remote server will return the execution result to us. If it is a complex operation, it will be more troublesome to pass in the command. For example, the requirements mentioned above require me tok8s
Copy the daily log of the service in the container. The steps after disassembly are: obtain multiple services.k8s pod
Name, copy the log file from multiple containers according to the current date, and then integrate it into one log file. For complex operations, we can write a script on the remote server, and thenRun
Pass the command to execute the script in the method.
To give a brief example, we wrote a script on the remote server., put it
/opt
In the directory, the script content and call methods are as follows:
# Script File#!/bin/bash today=$(date +"%Y-%m-%d") # Write data to file$(df -h > $)
package main import ( "fmt" "log" "/x/crypto/ssh" ) func main() { var ( username = "your username" password = "your password" addr = "ip:22" ) config := &{ User: username, Auth: []{ (password), }, HostKeyCallback: (), } client, err := ("tcp", addr, config) if err != nil { ("Failed to dial: ", err) } defer () session, err := () if err != nil { ("Failed to create session: ", err) } defer () // Call remote server script script res, err := ("sh /opt/") if err != nil { ("Failed to run: " + ()) } (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 */ }
Copy remote server files to local (scp)
The steps to copy files are relatively simple:
- Establish
ssh client
- based on
ssh client
Createsftp client
- Open the remote server file and copy it to the local area
package main import ( "io" "log" "os" "time" "/pkg/sftp" "/x/crypto/ssh" ) func main() { var ( username = "your username" password = "your password" addr = "ip:22" ) // 1. Create ssh client config := &{ User: username, Auth: []{ (password), }, HostKeyCallback: (), } client, err := ("tcp", addr, config) if err != nil { ("Failed to dial: ", err) } defer () // 2. Create sftp client based on ssh client sftpClient, err := (client) if err != nil { ("Failed to init sftp client: ", err) } defer () // 3. Open the remote server file filename := ().Format("2006-01-02") + ".log" source, err := ("/opt/" + filename) if err != nil { ("Failed to open remote file: ", err) } defer () // 4. Create local files target, err := (filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { ("Failed to open local file: ", err) } defer () // 5. Data copy n, err := (target, source) if err != nil { ("Failed to copy file: ", err) } ("Succeed to copy file: ", n) }
existsftp client
There are many other methods, such asWalk
、ReadDir
、Stat
、Mkdir
etc. There are also documentsRead
、Write
、WriteTo
、ReadFrom
Such methods are as convenient as operating the local file system.
In simple package
package main import ( "fmt" "io" "log" "os" "time" "/pkg/sftp" "/x/crypto/ssh" ) type Cli struct { user string pwd string addr string client * } func NewCli(user, pwd, addr string) Cli { return Cli{ user: user, pwd: pwd, addr: addr, } } // Connect Connect Connect to the remote serverfunc (c *Cli) Connect() error { config := &{ User: , Auth: []{ (), }, HostKeyCallback: (), } client, err := ("tcp", , config) if nil != err { return ("connect server error: %w", err) } = client return nil } // Run commandfunc (c Cli) Run(shell string) (string, error) { if == nil { if err := (); err != nil { return "", err } } session, err := () if err != nil { return "", ("create new session error: %w", err) } defer () buf, err := (shell) return string(buf), err } // Scp Copy filefunc (c Cli) Scp(srcFileName, targetFileName string) (int64, error) { if == nil { if err := (); err != nil { return 0, err } } sftpClient, err := () if err != nil { return 0, ("new sftp client error: %w", err) } defer () source, err := (srcFileName) if err != nil { return 0, ("sftp client open file error: %w", err) } defer () target, err := (targetFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return 0, ("open local file error: %w", err) } defer () n, err := (target, source) if err != nil { return 0, ("copy file error: %w", err) } return n, nil } // Call testfunc main() { var ( username = "your username" password = "your password" addr = "ip:22" ) // Initialization client := NewCli(username, password, addr) // ssh and run the script _, err := ("sh /opt/") if err != nil { ("failed to run shell,err=[%v]\n", err) return } // scp file to local filename := ().Format("2006-01-02") + ".log" n, err := ("/opt/"+filename, filename) if err != nil { ("failed to scp file,err=[%v]\n", err) return } ("Succeed to scp file, size=[%d]\n", n) // Process the file and delete the local file...}
Through the above series of operations, my needs can be achieved:
1. Write the program:
- Connect to the client server
- Execute scripts of remote servers to generate log files
- Copy the log files of the remote server to the local
- Process log files
- Delete local files
2. Start a timed task on the server to run the program
The above is a detailed explanation of the method of implementing ssh&scp in Go language. For more information about implementing ssh scp in Go language, please pay attention to my other related articles!