使用ansible批量初始化服务器

简介

本文详细介绍ansible怎么批量初始化服务器,包括ansible批量初始化服务器详细配置和步骤,有需要的小伙伴们可以参考借鉴,希望对大家有所帮助。

详细步骤

1、ansible要初始化的主机
[root@nginx ansible]# tail -3 /etc/ansible/hosts   #要初始的主机如下
[node]
192.168.20.4
192.168.20.5
2、配置ssh免密登录

playbook文件内容如下:

[root@nginx ansible]# cat ssh.yaml 
---
- name: configure ssh connectionhosts: nodegather_facts: falseconnection: localtasks:- name: configure ssh connectionshell: |ssh-keyscan {{inventory_hostname}} >>~/.ssh/known_hostssshpass -p '123.com' ssh-copy-id root@{{inventory_hostname}}
...

注:

  • gather\_facts:如果值为false,则表示不收集目标主机上的节点信息,默认为true,为收集节点信息,如果收集节点信息,则会慢很多,如果在接下来的操作中,不需要节点上的信息,可设置为false。
  • connection:local表示在ansible端本地执行任务,hosts:localhost和connection:local容易搞混,虽然两者的效果都是在本地执行任务,但是hosts:localhost是从inventory中筛选出了目标节点localhost来执行任务,而connection:local则筛选出来执行任务的目标主机是node组中的节点,但因为指定了local连接类型,使得node组中有多少个节点,就会在ansible本地执行几次该play。
3、配置主机名

配置主机名可以使用shell模块,但是对于不太专业,ansible提供了一个专用于配置主机名的模块:hostname模块。

当然,要使用ansible去设置多个主机名,要求目标主机和目标名称已经关联好,否则多个主机和多个主机名之间无法对应去设置。

例如:分别设置node组中的两个节点主机名为node01和node02,playbook内容如下:

[root@ansible ansible]# cat test.yaml 
---
- name: set hostnamehosts: nodegather_facts: falsevars:hostnames:- host: 192.168.20.4name: node01- host: 192.168.20.5name: node02tasks:- name: set hostnamehostname:name: "{{item.name}}"when: item.host == inventory_hostnameloop: "{{hostnames}}"

在上面的hostname模块中,需要详细介绍vars指令以及when、loop指令。

1)vars设置变量

vars指令可用于设置变量,可以设置一个或多个变量。下面几种方式都是合理的:

# 设置单个变量
vars:var1: value1vars:- var1: value1# 设置多个变量vars:var1: value1var2: value2vars:- var1: value1- var2: value2

vars可以设置在play级别,也可以设置在task级别,设置在play级别,该play范围内的task可以访问这些变量,其他play范围内则无法访问;设置在task级别,只有该task能访问这些变量,其他task和其他play则无法访问。

例如:

[root@ansible ansible]# cat test.yaml 
---
- name: play1hosts: localhostgather_facts: falsevars:- var1: "value1"tasks:- name: access var1debug:msg: "var1's value: {{var1}}"- name: play2hosts: localhostgather_facts: falsetasks:- name: cat's access vars from play1debug:var: var1- name: set and access var2 in this taskdebug:var: var2vars:var2: "value2"- name: cat't accesss var2debug:var: var2

执行结果如下:

[root@ansible ansible]# ansible-playbook test.yaml PLAY [play1] **************************************************************************TASK [access var1] ********************************************************************
ok: [localhost] => {"msg": "var1's value: value1"
}PLAY [play2] **************************************************************************TASK [cat's access vars from play1] ***************************************************
ok: [localhost] => {"var1": "VARIABLE IS NOT DEFINED!"
}TASK [set and access var2 in this task] ***********************************************
ok: [localhost] => {"var2": "value2"
}TASK [cat't accesss var2] *************************************************************
ok: [localhost] => {"var2": "VARIABLE IS NOT DEFINED!"
}PLAY RECAP ****************************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

回到我们更改主机名的配置vars指令中:

  vars:hostnames:- host: 192.168.20.4name: node01- host: 192.168.20.5name: node02

上面只设置了一个变量hostnames,但这个变量的值是一个数组结构,数组的两个元素又都是对象(字典/hash)结构。

所以想要访问主机名node01和它的IP地址192.168.20.4,可以:

  tasks:- debug:var: hostnames[0].name- debug:var: hostnames[0].host
2)when条件判断

在ansible中,提供的唯一一个通用的条件判断是when指令,当when指令的值为true时,则执行该任务,否则不执行该任务。

例如:

[root@ansible ansible]# cat test.yaml 
---
- name: play1hosts: localhostgather_facts: falsevars:- myname: "Ray"tasks:- name: task will skipdebug:msg: "myname is : {{myname}}"when: myname == "lv"- name: task will executedebug:msg: "myname is : {{myname}}"when: myname == "Ray"

在上面的myname值设置为Ray,第一个任务因为when的判断条件是myname==“lv”,所以判断结果为false,该任务不执行,同理,第二个任务因为when的值为true,所以执行了。

该playbook的执行结果:

PLAY [play1] **************************************************************************TASK [task will skip] *****************************************************************
skipping: [localhost]TASK [task will execute] **************************************************************
ok: [localhost] => {"msg": "myname is : Ray"
}PLAY RECAP ****************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0  
4、互相添加DNS解析记录
[root@ansible ansible]# cat add_dns.yaml 
---
- name: play1hosts: nodegather_facts: truetasks:- name: add DNSlineinfile:path: "/etc/hosts"line: "{{item}} {{hostvars[item].ansible_hostname}}"when: item != inventory_hostnameloop: "{{ play_hosts }}"

执行结果如下:

TASK [Gathering Facts] ****************************************************************
ok: [192.168.20.4]
ok: [192.168.20.5]TASK [add DNS] ************************************************************************
skipping: [192.168.20.4] => (item=192.168.20.4) 
changed: [192.168.20.4] => (item=192.168.20.5)
changed: [192.168.20.5] => (item=192.168.20.4)
skipping: [192.168.20.5] => (item=192.168.20.5) 
5、配置yum镜像源并安装软件

需求如下:

  • 备份原有yum镜像源文件,并配置清华大学的yum镜像源:os源和epel源
  • 安装常用软件,包括lrzsz、dos2unix、wget、curl、vim等;

playbook如下:

[root@ansible ansible]# cat config_yum.yaml 
- name: config yum repo add install softwarehosts: nodegather_facts: falsetasks:- name: backup origin yum reposshell:cmd: "mkdir bak; mv *.repo bak"chdir: /etc/yum.repos.dcreates: /etc/yum.repos.d/bak- name: add os repo and epel repoyum_repository:name: "{{item.name}}"description: "{{item.name}} repo"baseurl: "{{item.baseurl}}"file: "{{item.name}}"enabled: 1gpgcheck: 0reposdir: /etc/yum.repos.dloop:- name: osbaseurl: "https://mirrors.tuna.tsinghua.edu.cn/centos/7/os/$basearch"- name: epelbaseurl: "https://mirrors.tuna.tsinghua.edu.cn/epel/7/$basearch"- name: install pkgsyum:name: lrzsz,vim,dos2unix,wget,curlstate: present

在上面的yaml文件中,第一个任务是将所有系统默认的repo文件备份到bak目录中,chdir参数表示在执行shell模块的命令前先切换到/etc/yum.repos.d目录下,creates参数表示bak目录存在时则不执行shell模块。

第二个任务是使用yum\_repository模块配置yum源,该模块可添加或移除yum源。

相关参数如下:

  • name:指定repo的名称,对应于repo文件中的[name];
  • description:repo的描述信息,对应repo文件中的name:xxx;
  • baseurl:指定该repo的路径;
  • file:指定repo的文件名,不需要加.repo后缀,会自动加上;
  • reposdir:repo文件所在的目录,默认为/etc/yum.repos.d目录;
  • enabled:是否启用该repo,对应于repo文件中的enabled;
  • gpgcheck:该repo是否启用gpgcheck,对应于repo文件中的gpgcheck;
  • state:present表示保证该repo存在,absent表示移除该repo。

在上面的配置中使用了一个loop循环来添加两个repo:os和epel。

第三个任务是使用yum模块安装一些rpm包,yum模块可以更新、安装、移除、下载包。

yum常用参数说明:

  • name:指定要操作的包名

    • 可以带版本号;
    • 可以是单个包名,也可以是包名列表,或者逗号分隔多个包名;
    • 可以是url;
    • 可以是本地rpm包
  • state:

    • present和installed:保证包已安装,它们是等价的别名;
    • latest:保证包已安装了最新版本,如果不是则更新;
    • absent和removed:移除包,它们是等价的别名;
  • download\_only:仅下载不安装包(ansible 2.7才支持)
  • download\_dir:下载包存放在哪个目录下(ansible 2.8才支持)

yum模块是RHEL系列的包管理器,如果是ubuntu则无法使用,可以使用另一个更为通用的包管理器模块:package,它可以自动探测目标节点的包管理器类型并使用它们去管理软件。大多数时候使用package来代替yum或代替apt-install等不会有什么问题,但是有些包名在不同的操作系统上是不一样的,这是需要注意的。

6、时间同步

保证时间同步可以避免很多玄学性的问题,特别是对集群中的节点。

通常会使用ntpd时间服务器来保证时间的同步,这里使用aliyun提供的时间服务器来保证时间同步,并将同步后的时间同步到硬件。

playbook文件如下:

---
- name: sync timehosts: nodegather_facts: falsetasks:- name: install and sync timeblock:- name: install ntpdateyum:name: ntpdatestate: present- name: ntpdate to sync timeshell: |ntpdate ntp1.aliyun.comhwclock -w

上面使用了一个block指令来组织了两个有关联性的任务,将他们作为了一个整体。block更多的用于多个关联性任务之间的异常处理。

7、关闭selinux

关闭selinux的playbook如下:

[root@ansible roles]# cat disable_selinux.yaml 
---
- name: disable selinuxhosts: nodegather_facts: falsetasks:- name: disable on the flyshell: setenforce 0ignore_errors: true   #由于上条命令执行后的返回状态码不一定为0,所以为了防止非0报错并停止palsybook接下来的任务,所以使用ignore_errors忽略错误- name: disable forever in configlineinfile:path: /etc/selinux/configline: "SELINUX=disabled"     #修改配置文件中的值,以便永久关闭regexp: '^SELINUX='          #要修改的内容

注:ignore\_errors也经常结合block使用,因为在block级别上设置异常处理,可以处理block内部的所有错误。

8、配置iptables规则

playbook文件如下:

- name: Set Firewallhosts: nodegather_facts: falsetasks: - name: set iptables ruleshell: |# 备份已有规则iptables-save > /tmp/iptables.bak$(date +"%F-%T")# 给它三板斧iptables -Xiptables -Fiptables -Z# 放行lo网卡和允许pingiptables -A INPUT -i lo -j ACCEPTiptables -A INPUT -p icmp -j ACCEPT# 放行关联和已建立连接的包,放行22、443、80端口iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPTiptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPTiptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPTiptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT# 配置filter表的三链默认规则,INPUT链丢弃所有包iptables -P INPUT DROPiptables -P FORWARD DROPiptables -P OUTPUT ACCEPT
9、远程修改sshd配置文件并重启

有时候为了服务器的安全,可能会去修改目标节点上sshd服务的默认配置,比如禁止root用户登录、禁止密码认证登录而只允许使用ssh密码认证等。

在修改服务的配置文件时,一般有几种方法:

  • 通过远程执行sed等命令进行修改配置文件;
  • 通过lineinfile模块去修改配置文件;
  • 在ansible本地段写好配置文件,然后使用copy模块或者template模块传输到目标节点上。

相对来说,第三种方案是最统一、最易维护的方案。

此外,对于服务进程来说,修改了配置文件往往意味着要重启服务,使其加载新的配置文件,对于sshd也一样如此,但是sshd要比其他服务特殊一些,因为ansible默认基于ssh连接,重启sshd服务会使ansible连接断开,好在ansible默认会重试建立连接,无非是多等待几秒。但重建连接有可能会失败,比如修改了配置文件不允许重试、修改了sshd的监听端口等,这可能会使得ansible因连接失败而无法再继续执行后续任务。

所以,在修改sshd配置文件时,有如下建议:

  • 将此任务作为初始化服务器的最后一个任务,即使连接失败也无所谓;
  • 在playbook中加入连接失败的异常处理;
  • 如果目标节点修改了sshd端口号,建议通过ansible自动或者我们手动去修改inventory文件中的ssh连接端口号。

这里为了简单,我准备使用lineinfile模块去修改配置文件,要修改的内容只有两项:

  • 将PermitRootLogin指令设置为no,禁止root用户直接登录;
  • 将PasswordAuthentication指令设置为no,不允许使用密码认证的方式登录

playbook内容如下:

[root@ansible roles]# cat sshd_config.yaml 
---
- name: modify sshd_confighosts: nodegather_facts: falsetasks:# 1.备份/etc/ssh/sshd_config文件- name: backup sshd configshell:/usr/bin/cp -f {{path}} {{path}}.bakvars:- path: /etc/ssh/sshd_config# 2.设置PermitRootLogin no- name: disable root loginlineinfile:path: "/etc/ssh/sshd_config"line: "PermitRootLogin no"insertafter: "^#PermitRootLogin"regexp: "^PermitRootLogin"notify: "restart sshd"# 3.设置PasswordAuthentication no- name: disable password authlineinfile:path: "/etc/ssh/sshd_config"line: "PasswordAuthentication no"regexp: "^PasswordAuthentication yes"notify: "restart sshd"handlers:- name: "restart sshd"service:name: sshdstate: restarted

关于notify和handlers的作用如下:

ansible会监控playbook执行后的changed的状态,如果changed=1,则表示关注的状态发生了改变,即本次任务的执行不具备幂等性,如果changed=0,则表示本次任务要么没执行,要么执行了也没有影响,即本次任务具备幂等性。ansible提供了notify指令和handlers功能,如果在某个task中定义notify指令,当ansible在监控到该任务changed=1时,会触发该notify指令所定义的handler,然后去执行handler。所谓handler,其实就是task,无论是在写法上还是作用上它和task都没有什么区别,唯一的区别在于handler是被触发而被动执行的,不像普通task一样会按流程正常执行。

唯一需要注意的是,notify和handler中任务的名称必须一致。比如: notify: "restart sshd",那么handlers中必须得有一个任务设置了 name: "restart sshd"。

此外,在上面的playbook中,两个lineinfile任务都设置了相同的notify,但ansible不会多次去重启sshd,而是在最后重启一次。实际上,ansible在执行完某个任务之后,并不会立即去执行对应的handler,而是在当前play中所有普通任务都执行完成后再去执行handler,这样的好处是可以多次触发notify,但最后只执行一次对应的handler,从而避免多次重启。

10、整合所有任务到单个playbook中

这里将前面所有的playbook集合到单个playbook文件中去,这样就可以一次性执行所有任务。

整合后的playbook如下:

---
- name: Configure ssh Connectionhosts: nodegather_facts: falseconnection: localtasks:- name: configure ssh connectionshell: |ssh-keyscan {{inventory_hostname}} >>~/.ssh/known_hostssshpass -p'123.com' ssh-copy-id root@{{inventory_hostname}}- name: Set Hostnamehosts: nodegather_facts: falsevars:hostnames:- host: 192.168.20.4name: node01- host: 192.168.20.5name: node02tasks: - name: set hostnamehostname: name: "{{item.name}}"when: item.host == inventory_hostnameloop: "{{hostnames}}"- name: Add DNS For Eachhosts: nodegather_facts: truetasks: - name: add DNSlineinfile: path: "/etc/hosts"line: "{{item}} {{hostvars[item].ansible_hostname}}"when: item != inventory_hostnameloop: "{{ play_hosts }}"- name: Config Yum Repo And Install Softwarehosts: nodegather_facts: falsetasks: - name: backup origin yum reposshell: cmd: "mkdir bak; mv *.repo bak"chdir: /etc/yum.repos.dcreates: /etc/yum.repos.d/bak- name: add os repo and epel repoyum_repository: name: "{{item.name}}"description: "{{item.name}} repo"baseurl: "{{item.baseurl}}"file: "{{item.name}}"enabled: 1gpgcheck: 0reposdir: /etc/yum.repos.dloop:- name: osbaseurl: "https://mirrors.tuna.tsinghua.edu.cn/centos/7/os/$basearch"- name: epelbaseurl: "https://mirrors.tuna.tsinghua.edu.cn/epel/7/$basearch"- name: install pkgsyum: name: lrzsz,vim,dos2unix,wget,curlstate: present- name: Sync Timehosts: nodegather_facts: falsetasks: - name: install and sync timeblock: - name: install ntpdateyum: name: ntpdatestate: present- name: ntpdate to sync timeshell: |ntpdate ntp1.aliyun.comhwclock -w- name: Disable Selinuxhosts: nodegather_facts: falsetasks: - block: - name: disable on the flyshell: setenforce 0- name: disable forever in configlineinfile: path: /etc/selinux/configline: "SELINUX=disabled"regexp: '^SELINUX='ignore_errors: true- name: Set Firewallhosts: nodegather_facts: falsetasks: - name: set iptables ruleshell: |# 备份已有规则iptables-save > /tmp/iptables.bak$(date +"%F-%T")# 给它三板斧iptables -Xiptables -Fiptables -Z# 放行lo网卡和允许pingiptables -A INPUT -i lo -j ACCEPTiptables -A INPUT -p icmp -j ACCEPT# 放行关联和已建立连接的包,放行22、443、80端口iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPTiptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPTiptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPTiptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT# 配置filter表的三链默认规则,INPUT链丢弃所有包iptables -P INPUT DROPiptables -P FORWARD DROPiptables -P OUTPUT ACCEPT- name: Modify sshd_confighosts: nodegather_facts: falsetasks:- name: backup sshd configshell: /usr/bin/cp -f {{path}} {{path}}.bakvars: - path: /etc/ssh/sshd_config- name: disable root loginlineinfile: path: "/etc/ssh/sshd_config"line: "PermitRootLogin no"insertafter: "^#PermitRootLogin"regexp: "^PermitRootLogin"notify: "restart sshd"- name: disable password authlineinfile: path: "/etc/ssh/sshd_config"line: "PasswordAuthentication no"regexp: "^PasswordAuthentication yes"notify: "restart sshd"handlers: - name: "restart sshd"service: name: sshdstate: restarted

按照以上步骤一步一步操作,你将轻松完成批量服务器的初始化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/224121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VINS-MONO代码解读6----pose_graph

开始pose_graph部分,本部分记住一句话无论是快速重定位还是正常重定位,求出 T w 1 w 2 T_{w_1w_2} Tw1​w2​​就是终极目标。 还剩一个整体Pipeline~~ 1. pose_graph_node.cpp 注意,定义全局变量时即实例化了一个对象 PoseGraph posegra…

C++之模板

目录 泛型编程 模板 函数模板 函数模板的实例化 隐式实例化 显示实例化 类模板 我们知道STL(标准模板库)是C学习的精华所在,在学习STL之前我们得先学习一个新的知识点-------模板。那么模板究竟是什么呢?围绕着这个问题&a…

绘图示例---QT手动调用绘图事件,按钮控制图片

效果: 点击 “移动” 图片向右移动20,点击 “西理win嘛” 图片每秒向右移动20 QQ录屏20231212164128 下面时代码详解: 注意使用UI和代码实现按钮的不同 UI: ui->pushButton->setGeometry(windowWidth-105, windowHeight-25, 100, 20);…

【思考】只有实对称矩阵才能正交对角化吗?【矩阵的合同】

1:命题改写(A可以正交对角化) 2:左乘Q右乘Q逆(Q转置) 3:取转置 4:得证 总结 可以看到,矩阵如果可以正交对角化,那么一定是实对称矩阵。 另外,这…

【期末复习向】长江后浪推前浪之ChatGPT概述

参考文章:GPT系列模型技术路径演进-CSDN博客 这篇文章讲了之前称霸NLP领域的预训练模型bert,它是基于预训练理念,采用完形填空和下一句预测任务2个预训练任务完成特征的提取。当时很多的特定领域的NLP任务(如情感分类&#xff0c…

xtu oj 1327 字符矩阵

按照示例的规律输出字符矩阵。 比如输入字母D时,输出字符矩阵如下 ABCDCBA BBCDCBB CCCDCCC DDDDDDD CCCDCCC BBCDCBB ABCDCBA字符矩阵行首、尾都无空格。 输入 每行一个大写英文字母,如果字符为#,表示输入结束,不需要处理。 …

LLM之Agent(六)| 使用AutoGen、LangChian、RAG以及函数调用构建超级对话系统

本文我们将尝试AutoGen集成函数调用功能。函数调用最早出现在Open AI API中,它允许用户调用外部API来增强系统的整体功能和效率。例如,在对话过程中根据需要调用天气API。 函数调用和Agent有各种组合,在这里我们将通过函数调用调用RAG检索增强…

【SpringBoot】配置文件

配置文件官网 1. 配置方式 application.propertiesapplication.yml / application.yaml 2. 自定义配置信息 将实体类中的本应该写死的信息写在属性配置文件中。 可以使用 Value("${键名}") 获取,也可以使用 ConfigurationProperties(prefix"前…

访谈型软文写作方式,媒介盒子告诉你

访谈型软文一般用于维护企业形象,分享品牌故事。但是许多企业在写访谈型软文时经常容易跑偏或者写来写去没有逻辑,今天媒介盒子就来和大家分享访谈型软文的写作方式,看完这四点,小白也能写好访谈型软文! 一、 访谈对象…

前后端传参格式

前端发送 Serialize()方法 是指将一个抽象的JavaScript对象(数据结构)转换成字符串。这个字符串可以利用标准格式发送到服务器,被视为URL查询字符串或者POST数据,或者由于复杂的AJAX请求。这个方法使用的数据结构可以是JavaScri…

C++_构造函数与析构函数

目录 1、构造函数的写法 1.2 构造函数优化写法 2、默认构造函数与默认成员函数 2.1 默认成员函数对不同类型的处理 3、对内置类型的补丁 4、析构函数 4.1 析构函数的写法 5、默认析构函数 6、初始化列表 6.1 初始化列表的写法 6.2 初始化列表的作用 6.3 回顾与总结 …

【Proteus仿真】【51单片机】电子门铃设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真51单片机控制器,使共阴数码管,按键、无源蜂鸣器等。 主要功能: 系统运行后,数码管默认显示第一种门铃音调,可通过K1键切…

408考研笔记合集

文章目录 数据结构计算机组成原理操作系统计算机网络 距离24考研还剩10天,我终于把OS的王道课刷完了,到此为止我的408笔记就齐了,为了便于复习,我直接弄一个合集出来 数据结构 数据结构笔记——线性表、栈、队列、串(…

flutter的SingleChildScrollView控件详解

文章目录 SingleChildScrollView的介绍和使用场景详细介绍 SingleChildScrollView的介绍和使用场景 SingleChildScrollView 是 Flutter 中的一个小部件,用于创建一个可滚动的单个子部件。它通常用于处理内容超出屏幕可见区域的情况,允许用户通过滚动来查…

5.3 Linux DNS 服务

1、概念介绍 DNS(Domain Name System)域名系统,是互联网的一项核心服务,可以作为域名和IP地址相互映射的一个分布式数据库,提供域名与IP地址的解析服务,能够使人们更加方便的使用域名访问互联网而不是记住…

Vue3-11- 【v-for】循环数组

v-for的基本介绍 v-for 是一个指令&#xff0c; 它是用来在 html 模板中实现循环的。它可以循环 普通的数组、也可以直接循环一个范围值&#xff0c;也可以循环对象的每个属性。v-for 的语法介绍 <div v-for"(item,index) in arrayName" : key"index"…

有哪些好用的运维管理软件?哪个工单管理系统的操作简单一些?

运维管理软件可以帮助企业更有效地管理公司内外的事务&#xff0c;比如现在不少公司就引入了工单管理系统来处理后勤和售后的事务。那么&#xff0c;有哪些好用的运维管理软件&#xff1f;哪个的操作简单一些呢&#xff1f;   随着技术的发展和成熟&#xff0c;现在的工单管理…

vue实现浏览器不同分辨率下的不同样式,css的媒体查询与js判断当前浏览器宽度

前言&#xff1a; 实现实现浏览器不同分辨率下的不同样式的方法很多&#xff0c;这里整理两种&#xff0c;1个是css的媒体查询来实现&#xff0c;另一个是js判断当前浏览器的宽度&#xff0c;然后动态给他添加不同的class名&#xff0c;或者动态用style修改样式&#xff0c;添加…

Linux0.11内核源码解析-printk

printk实现原理 printk->tty_write->con_write printk格式化输出 printk的函数&#xff0c;用于在控制台上输出格式化的字符串。 该函数使用了可变参数列表&#xff0c;可以接受任意数量的参数。 首先&#xff0c;使用va_start宏初始化一个va_list类型的变量args&#x…

使用海康SDK开发软件在发布注意事项

【注意事项】 Windows环境下: 更新设备网络SDK时&#xff0c;SDK开发包【库文件】里的HCNetSDK.dll、HCCore.dll、HCNetSDKCom文件夹、libssl-1_1.dll、libcrypto-1_1.dll、hlog.dll、hpr.dll、zlib1.dll、PlayCtrl.dll、SuperRender.dll、AudioRender.dll等文件均要加载到程序…