이 연결에는 주로 다음 두 가지 기능이 있습니다.
파일 수집(파일에 대한 일괄 작업, 여러 파일의 공통 파일 일괄 수집 포함) 호스트) 로그 파일 등)
파일 배포(파일에 대한 일괄 작업, 여러 호스트의 로그 파일과 같은 공통 파일의 일괄 배포 포함)
ansible에는 Windows 설치 패키지이므로 테스트를 용이하게 하기 위해 후속 작업을 위해 Linux 환경이 구축되었습니다.
이번에는 yum 방식을 사용하여 Ansible을 설치한 후 먼저 EPEL 소스를 설치합니다.
yum install -y http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
EPEL 소스에서 Ansible 버전 보기
yum 정보 ansible
이 버전을 직접 설치해 보세요. 다른 요구 사항이 있으면 소스를 조정하고 다른 Ansible 버전을 설치하세요.
yum install -y ansible
설치가 완료된 후 Ansible 버전 정보를 확인하세요.
ansible - -version
Ansible 서버 목록 구성
인벤토리 파일 /etc/ansible/hosts, 이 파일에 노드 호스트의 해당 IP 주소와 포트를 기록합니다.
여기서 데모를 하고 있을 뿐입니다. IP 뒤에 노드의 SSH 포트를 추가할 수 있습니다. 정의된 내용 위에 [] 목록이 있고, 그 안에 있는 내용은 바인딩된 노드 호스트를 작동하기 위해 그룹 목록이라고 부르는 데 익숙합니다
간단한 인증, 추가된 호스트에 ping
Ansible 설치 성공! !
이름에서 알 수 있듯이 파일 배포는 로컬 파일을 여러 호스트에 배포하는 것입니다.
이 때, 로컬 파일을 생성하려면 Apache POI(해당 패키지를 가져올 수 있음)가 필요합니다(Ansible Host 구성 파일도 POI를 통해 생성됩니다)
package com.tiduyun.cmp.operation.utils; import com.tiduyun.cmp.common.model.operation.HostInfo; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Component; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * @author huyuan@tiduyun.com ansible创建文件 */ @Slf4j @Component public class AnsibleCreateFileUtils { private final static String filename = "hosts"; public static String passWordConnect(List<HostInfo> hostInfo, String hostGroup , String directory) throws IOException{ /** 在本地新建一个文件夹 里面创建一个文件 向里面写入内容 */ // 创建文件夹对象 创建文件对象 File folder = new File(directory); // 如果文件夹不存在 就创建一个空的文件夹 if (!folder.exists()) { log.info("创建了文件夹{}" , folder); folder.mkdirs(); } File file = new File(directory, filename); // 如果文件不存在 就创建一个空的文件 if (!file.exists()) { try { log.info("创建了文件{}" , file); file.createNewFile(); } catch (IOException e) { log.error("error data{}" , e); } } // 写入数据 // 创建文件字节输出流 FileOutputStream fos = new FileOutputStream(file); try { List<String> list = new ArrayList<>(); for (HostInfo data : hostInfo) { // 开始写 String string = data.getHost() + " ansible_ssh_pass=" + data.getPasswd() + " ansible_ssh_user=" + data.getAccount() + " ansible_ssh_port=" + data.getPort(); list.add(string); } String splicingData = StringUtils.join(list, "\n"); String str = "[" + hostGroup + "]" + "\n" + splicingData; byte[] bytes = str.getBytes(); // 将byte数组中的所有数据全部写入 fos.write(bytes); fos.flush(); log.info("文件内容{}" , str); // 删除文件 // deleteFile(file); // 关闭流 } catch (IOException e) { log.error("error data{}" , e); throw e; }finally { if (fos != null) { fos.close(); } } return directory; } public static void deleteFile(File file) { if (file.exists()) {// 判断路径是否存在 if (file.isFile()) {// boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。 file.delete(); } else {// 不是文件,对于文件夹的操作 // 保存 路径D:/1/新建文件夹2 下的所有的文件和文件夹到listFiles数组中 File[] listFiles = file.listFiles();// listFiles方法:返回file路径下所有文件和文件夹的绝对路径 for (File file2 : listFiles) { /* * 递归作用:由外到内先一层一层删除里面的文件 再从最内层 反过来删除文件夹 * 注意:此时的文件夹在上一步的操作之后,里面的文件内容已全部删除 * 所以每一层的文件夹都是空的 ==》最后就可以直接删除了 */ deleteFile(file2); } } file.delete(); } else { log.error("该file路径不存在!!"); } } }
Note: Ansible은 두 가지 연결 방식으로 나뉘는데요, 여기서는 키 연결을 사용하며, 생성된 파일은 키와 연결되어 있습니다! ! ! 이는 향후 수집 및 배포에 활용됩니다. (이해가 안되면 ansible 연결 방법을 찾아보시면 됩니다)
@Override public void ansibleCreateHost(HostInfo hostInfo, String Key) { ParamCheckUtils.notNull(hostInfo, "hostInfo"); List<HostInfo> HostIp = Arrays.asList(hostInfo); for (HostInfo data : HostIp) { String ansiblePassWd = data.getPasswd(); String PassWd = hostInfoService.decode(ansiblePassWd); data.setPasswd(PassWd); } try { AnsibleCreateFileUtils.passWordConnect(HostIp, ansibleConfigurationItemVo.getHostGroup(), ansibleConfigurationItemVo.getDirectory()); } catch (IOException e) { log.error("Failed to create host configuration{}", e); } }
호스트 구성 파일이 구성되었습니다. 다음 단계는 ansible의 해당 명령을 실행하고 이를 통해 ansible 명령을 연결하는 것입니다. 자바.
명령 도구 클래스 실행
<br>
package com.tiduyun.cmp.operation.utils; import lombok.extern.slf4j.Slf4j; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import static cn.hutool.db.DbUtil.close; /** * @author huyuan@tiduyun.com ansible执行命令工具类 * upload 上传文件 * createRemoteDirectory 创建远程目录 */ @Slf4j public class AnsibleExecuteTheOrderUtils { private final static String commandBin = "/bin/sh"; private final static String commandC = "-c"; /** * 创建远程目录 */ public static void createRemoteDirectory(String hostGroup, String remotePath, String directory) throws IOException { Runtime run = Runtime.getRuntime(); String[] cmds = new String[3]; cmds[0] = commandBin; cmds[1] = commandC; cmds[2] = "ansible " + hostGroup + " -m command -a " + "\"mkdir " + remotePath + "\"" + " -i " + directory + "/hosts"; // 执行CMD命令 Process p = run.exec(cmds); log.info("ansible远程执行命令为{}", cmds[2]); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8"))); try { String lineMes; while ((lineMes = br.readLine()) != null) log.info(lineMes);// 打印输出信息 try { // 检查命令是否执行失败。 if (p.waitFor() != 0) { if (p.exitValue() == 1)// 0表示正常结束,1:非正常结束 log.error("命令执行失败"); } } catch (InterruptedException e) { log.error("error data{}", e); } } catch (IOException e) { log.error("fail to carry out command{}", e); throw e; } finally { if (br != null) { br.close(); } } } /** * 文件分发 */ public static void upload(String hostGroup, String localPath, String remotePath, String directory) throws IOException { Runtime run = Runtime.getRuntime(); String[] cmds = new String[3]; cmds[0] = commandBin; cmds[1] = commandC; cmds[2] = "ansible " + hostGroup + " -m copy -a " + "\"src=" + localPath + " dest=" + remotePath + "\"" + " -i " + directory + "/hosts"; // 执行CMD命令 Process p = run.exec(cmds); log.info("ansible命令为{}", cmds[2]); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8"))); try { String lineMes; while ((lineMes = br.readLine()) != null) log.info("ansible输出信息为 :" + lineMes);// 打印输出信息 try { // 检查命令是否执行失败。 if (p.waitFor() != 0) { if (p.exitValue() == 1)// 0表示正常结束,1:非正常结束 log.error("命令执行失败"); } } catch (InterruptedException e) { log.error("error data{}", e); } } catch (IOException e) { log.error("fail to carry out command{}", e); throw e; } finally { if (br != null) { br.close(); } } } /** * 文件采集 */ public static void fileCollection(String hostGroup, String remotePath, String localPath , String directory) throws IOException { Runtime run = Runtime.getRuntime(); String[] cmds = new String[3]; cmds[0] = commandBin; cmds[1] = commandC; cmds[2] = "ansible " + hostGroup + " -m fetch -a " + "\"src=" + remotePath + " dest=" + localPath + " force=yes backup=yes\"" + " -i " + directory + "/hosts"; // 执行CMD命令 Process p = run.exec(cmds); log.info("ansible远程采集文件命令为{}", cmds[2]); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8"))); try { String lineMes; while ((lineMes = br.readLine()) != null) log.info(lineMes);// 打印输出信息 try { // 检查命令是否执行失败。 if (p.waitFor() != 0) { if (p.exitValue() == 1)// 0表示正常结束,1:非正常结束 log.error("命令执行失败"); } } catch (InterruptedException e) { log.error("error data{}", e); } } catch (IOException e) { log.error("fail to carry out command{}", e); throw e; } finally { if (br != null) { br.close(); } } } public static void ExecuteTheOrder(String command) throws IOException { log.info("start execute cmd {}", command); String[] cmd = new String[] {"/bin/bash", "-c", command}; Runtime run = Runtime.getRuntime(); Process p = run.exec(cmd); // 执行CMD命令 BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream(), Charset.forName("UTF-8"))); try { String lineMes; while ((lineMes = br.readLine()) != null) log.info("输出信息为 {}", lineMes);// 打印输出信息 try { // 检查命令是否执行失败。 if (p.waitFor() != 0) { if (p.exitValue() == 1)// 0表示正常结束,1:非正常结束 log.error("命令执行失败"); } } catch (InterruptedException e) { log.error("error data{}", e); } } catch (IOException e) { log.error("fail to carry out command{}", e); throw e; } finally { if (br != null) { br.close(); } } } public static void disconnect() { try { close(); } catch (Exception ex) { // Ignore because disconnection is quietly } } // public void execute(String command) throws Exception { // log.info("start execute cmd {}", command); // try (Session session = sshClient.startSession()) { // Session.Command exec = session.exec(command); // // Integer readLineCount = 0; // InputStream in = exec.getInputStream(); // log.info(IOUtils.readFully(in).toString()); // String errorMessage = IOUtils.readFully(exec.getErrorStream(), LoggerFactory.DEFAULT).toString(); // log.info(errorMessage); // if (exec.getExitStatus() != null && exec.getExitStatus() != 0) { // throw new RuntimeException( // "exec " + command + " error,error message is " + errorMessage + ",error code " + exec.getExitStatus()); // } // log.info("exec result code {}", exec.getExitStatus()); // // } // // } }
다음 단계는
package com.tiduyun.cmp.operation.service.impl; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; import com.tiduyun.cmp.common.model.flow.UploadFile; import com.tiduyun.cmp.common.model.operation.ComponentInfo; import com.tiduyun.cmp.common.model.operation.HostInfo; import com.tiduyun.cmp.common.provider.service.ExceptionBuildService; import com.tiduyun.cmp.operation.constant.OperationExceptionCode; import com.tiduyun.cmp.operation.constant.StartCmdSeparate; import com.tiduyun.cmp.operation.model.AnsibleConfigurationItemVo; import com.tiduyun.cmp.operation.model.vo.FileQueryVo; import com.tiduyun.cmp.operation.service.AnsibleTaskRecordService; import com.tiduyun.cmp.operation.service.ComposerDeployService; import com.tiduyun.cmp.operation.service.HostInfoService; import com.tiduyun.cmp.operation.service.UploadFileService; import com.tiduyun.cmp.operation.utils.AnsibleExecuteTheOrderUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @Slf4j @Service public class AnsibleDeployServiceImpl implements ComposerDeployService { @Value(value = "${cmp.operation.commandHeader:cmd /c}") private String commandHeader; @Value(value = "${cmp.operation.filePath:/data/cmp/file}") private String filePath; @Value(value = "${cmp.operation.remoteFilePath:/tmp}") private String remoteFilePath; @Autowired private AnsibleTaskRecordService ansibleTaskRecordService; @Autowired private AnsibleConfigurationItemVo ansibleConfigurationItemVo; @Autowired private UploadFileService uploadFileService; @Autowired private HostInfoService hostInfoService; @Autowired private ExceptionBuildService exceptionBuildService; @Override public void deploy(HostInfo hostInfo, ComponentInfo componentInfo, String cpmposerName) { ansibleTaskRecordService.ansibleCreateHost(hostInfo, null); try { String remotePath = StringUtils.join(remoteFilePath, "/", cpmposerName, "-", componentInfo.getName(), "-", RandomUtil.randomString(3)); log.info("remote file path = {}", remotePath); List<Integer> fileIds = getFileIds(componentInfo.getFileUrl()); if (CollectionUtils.isNotEmpty(fileIds)) { FileQueryVo uploadFileQueryVo = new FileQueryVo(); uploadFileQueryVo.setIds(fileIds); List<UploadFile> uploadFiles = uploadFileService.query(uploadFileQueryVo); for (UploadFile uploadFile : uploadFiles) { String path = StringUtils.join(filePath, uploadFile.getFilePath()); File file = new File(path); if (!file.exists()) { log.error("file url is {}", file.getPath()); throw exceptionBuildService.buildException(OperationExceptionCode.FILE_NOT_EXIST, new Object[] {uploadFile.getFileName()}); } // 创建远程目录 AnsibleExecuteTheOrderUtils.createRemoteDirectory(ansibleConfigurationItemVo.getHostGroup(), StringUtils.join(remotePath), ansibleConfigurationItemVo.getDirectory()); // 分发文件 AnsibleExecuteTheOrderUtils.upload(ansibleConfigurationItemVo.getHostGroup(), path, StringUtils.join(remotePath, "/", uploadFile.getFileName()), ansibleConfigurationItemVo.getDirectory()); } } List<String> startCmds = getStartCmds(componentInfo.getStartCmd()); if (CollectionUtils.isNotEmpty(startCmds)) { String cdCmd = StringUtils.join("cd ", remotePath); String execCmd = StringUtils.join(startCmds, ";"); execCmd = StringUtils.join(cdCmd, ";", execCmd); log.info("execCmd= " + execCmd); // sshClient.execute(execCmd); AnsibleExecuteTheOrderUtils.ExecuteTheOrder(execCmd); } else { log.error("parse startCmd fail {}", componentInfo.getStartCmd()); } } catch (Exception e) { log.error("主机[{}]部署[{}]组件失败,主机ID[{}],组件ID[{}]:", hostInfo.getHost(), componentInfo.getName(), hostInfo.getId(), componentInfo.getId(), e); throw exceptionBuildService.buildException(OperationExceptionCode.EXECUTE_CMD_ERROR, new Object[] {e.getMessage()}); } finally { AnsibleExecuteTheOrderUtils.disconnect(); } } @Override public boolean isSupport(HostInfo hostInfo) { return true; } private List<Integer> getFileIds(String fileIds) { List<Integer> ids = new ArrayList<>(); if (fileIds == null) { return null; } String[] split = StringUtils.split(fileIds, ","); for (String s : split) { ids.add(Integer.parseInt(s)); } return ids; } private List<String> getStartCmds(String startCmd) { List<String> cmd = new ArrayList<>(); if (startCmd == null) { return cmd; } String[] split = StrUtil.split(startCmd, StartCmdSeparate.SIGN); cmd.addAll(Arrays.asList(split)); return cmd; } public static Boolean needCd(String s) { String[] splits = StrUtil.split(s, "&&"); int maxIndex = splits.length - 1; String cmd = splits[maxIndex]; if (StrUtil.startWith(cmd, "cd")) { return false; } else { return true; } } }
위와 동일합니다. 도구 클래스 두 개를 호출합니다
@Override public void fileCollection(HostInfo hostInfo, String remotePath, String localPath) { ansibleCreateHost(hostInfo, null); try { log.info("remote file path = {}", remotePath); log.info("local file path = {}", localPath); // 文件采集 AnsibleExecuteTheOrderUtils.fileCollection(ansibleConfigurationItemVo.getHostGroup(), remotePath, localPath , ansibleConfigurationItemVo.getDirectory()); } catch (Exception e) { log.error("主机[{}]文件采集失败,主机ID[{}]:", hostInfo.getHost(), hostInfo.getId(), e); throw exceptionBuildService.buildException(OperationExceptionCode.EXECUTE_CMD_ERROR, new Object[] {e.getMessage()}); } finally { AnsibleExecuteTheOrderUtils.disconnect(); } }
위 내용은 Java는 Ansible 자동 운영 및 유지 관리 플랫폼과 어떻게 인터페이스합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!