相关文章 |
---|
ansible自动化运维(一)简介及清单,模块-CSDN博客 |
ansible自动化运维(三)jinja2模板&&roles角色管理-CSDN博客 |
ansible自动化运维(四)运维实战-CSDN博客 |
一.Ansible中的playbook模式
Playbook不同于使用单个模块操作远程服务器,Playbook的功能更加强大。如果说单个模块执行类似于Linux系统中的命令,那么Playbook就类似于shell脚本,将多个模块组合起来实现一组的操作。
Playbook还是会用到ad-hoc模式中的模块及参数,只不过Playbook与ad-hoc的写法不一样
1.1playbook的优势
- 功能比ad-hoc更全
- 能很好的控制先后执行顺序,以及依赖关系
- 语法展现更加直观
- ad-hoc无法持久使用,playbook可以持久使用
1.2playbook的组成
Plsy:一个完整的部署任务,并且必须包含以下前两项:
Hosts:定义对哪些主机进程操作
tasks:定义的是具体执行的任务
Become:表示是否要以特权用户(通常是root)身份执行任务。如果设置为yes,则任务回以特权身份执行。
Playbook:由一个或多个play组成,一个play可以包含多个task任务
说明:Ansible中的playbook文件结尾为。yml格式
示例httpd.yml:安装httpd:
[root@server ~]# cat 1.yml --- - name: apache hosts: web become: yes tasks: - name: Install httpd Server yum: name: httpd state: present |
[root@server ~]# ansible-playbook 1.yml |
如上就在node1节点安装完毕
解析:
name:定义一个playbook的名称,用于标识playbook的用途;
Hosts:指定要在哪个主机上执行,也是写主机或主机组名,需提前在/etc/ansible/hosts中配置好:
beacome:yes表示使用特权用户;
tasks:属于一个任务列表,主要写具体执行什么的(可以有多个);
name:每个任务的名称,用于描述干什么的;上述yml中则是安装httpd服务;
Yum:表示使用哪个模块来进行操作:模块的参数可以看ad-hoc中的,用的都是一样的,写法不一样就是:
name:要安装的服务名称,我们这里是httpd;
state:要进行的操作,可以是安装、卸载、更新;
其实tasks就是Ansible的模块以YAML语法写入到playbook中。
生产环境中为了可读性与可维护性通常一个playbook中只编写一个play,如果某些主机需要执行多个play,那么可以使用including关键字在一个playbook中导入playbook,
1.3playbook命令
格式:
ansible-playbook [选项] playbook.yml |
常用选项:
选项/参数 |
选项/参数 解析 |
-T |
建立SSH连接的超时时间 |
-i |
指定inventory文件 |
-r |
并发执行的进程数,默认为5 |
- -list-hosts |
匹配服务器列表 |
- -list-tasks |
列出任务列表 |
- -step |
每执行一个任务后停止,等待用户确认 |
- -syntax-check |
语法检测 |
- -list-tage |
列出此yml文件中的所有tag标签 |
- -skip-tage |
执行-skip-tags之外的标签任务 |
-C |
检查当前这个playbook是否会修改受控端,模拟执行 |
1.4playbook的语法
权限
remot_user指定playbook运行时的用户身份,可以写在hosts下,也可以每个tasks做定义;
become该选项为布尔值,当等于yes表示以管理员身份通常与become_method一起使用;
become_method:su或sudo
# 指定使用哪个用户执行此任务 remote_user: root # 是否使用特权用户 become: yes |
通知与触发
notify如果指定的操作执行了,会触发handlers的操作,指定的是handler的名称;
handlers和notify指定的名称必须相同,否则无法触发。
handlers中需要-name 指定名称,handlers只会在所有的tasks执行完后执行,并且,即便一个handlers被触发多次,也会执行一次。handlers是一种特殊的tasks。
handlers(触发事件)
notify: 触发 handlers:触发的动作 # 两者的名称一定要相同,否则无法触发。 |
使用上场景:一般都是修改配置文件时
正常情况时handlers是不会执行的,除非触发任务,才会执行
[root@server ~]# cat 2.yml - hosts: node1 remote_user: root become: yes tasks: - name: install redis yum: name: redis state: present - name: copy file template: src: redis.conf dest: /etc/redis.conf tags: copyfile notify: restart # 触发:触发名称 - name: start service: name: redis state: started enabled: yes handlers: # 触发动作 - name: restart service: name: redis state: restarted |
[root@server ~]# ansible-playbook 2.yml |
以为节点没放redis所以没有找到软件包
1.5playbook中的变量
变量提供了便捷的方式来管理Ansible playbook 的每一个项目中的动态值,比如nginx-1.6.3这个软件包的版本,在其他地方或许会反复使用,那么如果将此值设置为变量,然后再在其他的playbook中调用,会方便许多。如此一来还方便维护,减少维护的成本。 |
- 变量的定义方式
- 通过命令行进行变量定义
- 在play文件中进行变量定义
- 通过inventory主机信息文件中进行变量定义
变量读取的优先级为:命令行>playbook文件>inventory文件
通过vars定义变量
[root@server ~]# cat LAMP.yml --- - name: LAMP hosts: web vars: packages_name: - httpd - mariadb-server - php - php-mysql tasks: - name: install LAMP yum: name: "{{packages_name}}" state: present |
执行此yml:
# 语法校验 ansible-playbook --syntax-check LAMP.yml # 执行yml ansible-playbook LAMP.yml(定义的yml文件名) |
通过vars_files定义变量
当变量较少时,使用vars定义没有问题,当变量较多时,可以将变量保存到一个独立的文件中;
需要多个yml文件,一个主文件,需要调用其他yml独立文件,主要是最后运行的;其他是定义包名的yml文件。
#定义阶段 # my_vars.yml --- httpd_package: httpd mariadb_package: mariadb-server #调用阶段 # apache.yml --- - name: apache hosts: web become: yes vars_files: - my_vars.yml # 引入变量文件 tasks: - name: Install httpd yum: name: "{{ httpd_package }}" state: present become: yes - name: Install mariadb-server yum: name: "{{ mariadb_package }}" state: present become: yes |
上面是两个文件
执行此yml:
# 语法校验 ansible-playbook --syntax-check apache.yml # 执行yml ansible-playbook apache.yml(定义的yml文件名) |
官方推荐定义变量方法
之前的几种变量定义都不是很好用,比较好用的是在Ansible项目目录下船舰两个变量目录;
host_vars
group_vars
目录名字一定要一致,不能做任何修改
理解如何设置和使用host_vars和group_vars可以使你的Ansible管理更加灵活和有组织。以下是更详细的步骤,从设置目录结构到创建playbook的执行:
1.目录结构
在你的Ansible项目目录下,确保设置以下目录结构;
your_ansible_project/ ├── group_vars/ │ └── web-servers.yml ├── host_vars/ │ └── web-server.yml ├── playbook.yml └── inventory.ini |
- group_vars目录用于存放组级别的变量定义。
- Host_vars目录用于存放主机级别的变量定义。
- Playbook.yml是你的Ansible Playbook。
- Inventory.ini是Ansible的主机清单文件,其中列出了你的主机和主机组。
2.清单文件(inventory.ini)
确保在清单文件中定义了你的主机和主机组。例如:
vi /etc/ansible/inventory.ini |
[web-servers]
172.16.11.209 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh-pass=’123123’
3. **组级别变量 (`group_vars/web-servers.yml`)**: 在`group_vars`目录下创建一个YAML文件,例如`web-servers.yml`,并在其中定义组级别的变量,如: `mkdir -p /etc/ansible/group_vars && vim /etc/ansible/group_vars/web-servers.yml` ```yaml # group_vars/web-servers.yml --- httpd_package: httpd mariadb_package: mariadb-server |
3.主机级别变量(host_vars/web-server.yml)
在host_vars目录下创建一个YAML文件,例如web-server.yml,并在其中定义主机级别的变量,如:
mkdir -p /etc/ansible/host_vars && vi /etc/ansible/host_vars/web-servers.yml # host_vars/web-server.yml --- http_port: 80 |
4.playbook(playbook.yml)
创建你的Ansible Playbook,例如:
vi /etc/ansible/playbook.yml
--- - name: Configure Web Server hosts: web-servers become: yes tasks: - name: Install HTTPd yum: name: "{{ httpd_package }}" state: present - name: Start HTTPd service: name: httpd state: started |
5.运行playbook
使用以下命令运行你的Playbook:
ansible-playbook -i inventory.ini playbook.yml |
此时,Ansible会自动加载group_vars和host_vars目录中的变量,你的Playbook将使用这些变量来配置主机。
通过这种方式,你可以更加有组织地管理变量,特别是对于多主机和多组的环境。你可以为不同的组或主机设置特定的变量,而不必在Playbook中硬编码这些值,提高了可维护和可读性。
- 命令行定义变量
Ansible-playbook命令提供-e选项,用于在命令行定义变量,命令行定义变量的优先级最高。
[root@localhost yml]# cat /etc/ansible/hosts [web] 172.16.11.209 [web:vars] ansible_ssh_pass='123123' # 执行:命令行定义变量 [root@localhost yml]# vim test.yml --- - name: apache hosts: web become: yes vars: httpd_package: - httpd tasks: - name: install httpd service yum: name: "{{ httpd_package }}" state: present #定义阶段 [root@ansible ~]# ansible-playbook test.yml -e "web_server=vsftpd" |
1.6变量注册
当ansible的模块在运行之后,其实都会返回一些result结果,就像是执行脚本,我们有时候需要脚本给我们一些return返回值,我们才知道,上一步是否可以执行成功,但是默认情况下,ansible'的result并不会显示出来,所以,我们可以把这些返回值‘存储’到变量中,这样我们就能通过‘调用’对应的变量名,从而获取到这些result,这种将模块的返回值,写入到变量中的方法称为变量注册。
[root@ansible ~]# vim register.yml --- - hosts: web tasks:
- name: define a var1 shell: "whoami" register: user_name - name: get msg debug: msg: "{{ user_name }}" - name: get stdout msg debug: msg: "{{ user_name.stdout_lines }}"
# 执行 [root@localhost yml]# ansible-playbook register.yml [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details PLAY [node1] *************************************************************************************************************************** TASK [Gathering Facts] ***************************************************************************************************************** ok: [host1] TASK [define a var1] ******************************************************************************************************************* changed: [host1] TASK [get msg] ************************************************************************************************************************* ok: [host1] => { "msg": { "changed": true, "cmd": "whoami", "delta": "0:00:00.004007", "end": "2024-12-03 15:28:02.002016", "failed": false, "rc": 0, "start": "2024-12-03 15:28:01.998009", "stderr": "", "stderr_lines": [], "stdout": "root", "stdout_lines": [ "root" ] } } TASK [get stdout msg] ****************************************************************************************************************** ok: [host1] => { "msg": [ "root" ] } PLAY RECAP ***************************************************************************************************************************** host1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 |
说明:
debug:模块常用参数
msg:#调试输出的消息
var:#将某个任务执行的输出作为变量传递给debug模块,debug会直接将其打印输出
verbosity:#debug的级别(默认是0级,全部显示)
1.7debug模块:将上一步任务执行的结果打印出来
将上一步任务执行的结果打印出来,不管成功还是失败都会返回;
--- - name: debug测试 hosts: all remote_user: root
tasks: - name: 查看root目录下的文件 command: ls /root register: root_directory_contents # 存储命令输出 - name: 输出的结果 debug: var: root_directory_contents.stdout_lines |
1.8facts缓存
[root@localhost yml]# ansible nginx -m setup 172.16.11.209 | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "172.17.0.1", "172.16.11.209" ], "ansible_all_ipv6_addresses": [ "fe80::20c:29ff:feaf:dbfa" ], 以下省略… |
在playbook中引用facts变量
二.playbook中的流程控制
2.1条件
在所有的编程语言控制语句中,条件语句是必不可少的,在使用Ansible的过程中,条件判断的使用频率及其高。
例如:
- 使用不同的系统的时候,可以通过判断系统来对软件包进行安装。
- 在nfs和rsync安装过程中,客户端服务器不需要推送配置文件,之前我们都是写多个play。会影响效率。
- 在源码安装nginx的时候,执行第二遍就无法执行了,此时我们就可以进行判断是否安装过。
playbook中的条件判断语句使用when
tasks: #官方案例 - name: "shut down Debian flavored systems" command: /sbin/shutdown -t now when: ansible_facts['os_family'] == "Debian" # note that all variables can be used directly in conditionals without double curly braces - hosts: web_group tasks: - name: Install CentOS Httpd yum: name: httpd state: present #官方 when: ansible_facts['os_family'] == "CentOS" #非官方 when: ansible_distribution == "CentOS" - name: Install Ubuntu Httpd apt: name: apache2 state: present when: ansible_facts['os_family'] == "Ubuntu" |
还可以使用括号对条件进行分组
tasks: - name: "shut down CentOS 6 and Debian 7 systems" command: /sbin/shutdown -t now when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7") |
也可以指定多条件为列表
tasks: - name: "shut down CentOS 6 systems" command: /sbin/shutdown -t now when: - ansible_facts['distribution'] == "CentOS" - ansible_facts['distribution_major_version'] == "6" |
条件运算
tasks: - shell: echo "only on Red Hat 6, derivatives, and later" when: ansible_facts['os_family'] == "RedHat" and ansible_facts['distribution_major_version ']|int >= 6 |
实例:
[root@server ~]# cat when.yml - hosts: node1 remote_user: root #代表用root用户执行,默认是root,可以省略 tasks: - name: createfile copy: content="test3" dest=/etc/ansible/yml/when.yml when: a=='3' - name: createfile copy: content="test4" dest=/etc/ansible/yml/when.yml when: a=='4' |
如果啊“3”,就将“test3”,写入到web组下被管控机的/etc/ansible/yml/when.uml
如果啊“4”,就将“test4”,写入到web组下被管控机的/etc/ansible/yml/when.uml
执行:
[root@server ~]# ansible-playbook -e a=3 when.yml |
2.2循环
1.标准循环使用场景-批量安装软件
[root@server ~]# cat with_items.yml --- - hosts: all remote_user: root tasks: - name: Installed packages yum: name: "{{ item }}" state: present
with_items: - wget - tree - lrzsz |
执行:
[root@server ~]# ansible-playbook with_items.yml |
2.标准循环使用场景批量创建用户
[root@server ~]# cat item2.yml --- - name: 批量创建用户 hosts: all remote_user: root tasks: - name: 配置创建用户操作 user: name: "{{ item }}" state: present with_items: - cs1 - cs2 - cs3 |
执行:
[root@server ~]# ansible-playbook item2.yml |
查看:
[root@server ~]# tail -3 /etc/passwd cs1:x:1000:1000::/home/cs1:/bin/bash cs2:x:1001:1001::/home/cs2:/bin/bash cs3:x:1002:1002::/home/cs3:/bin/bash |
3.循环嵌套使用场景-批量创建用户并给用户添加用户组
root@server ~]# cat item3.yml --- - name: 批量创建用户名及用户组 hosts: all remote_user: root tasks: - name: create group group: name={{ item }} state=present with_items: - group1 - group2 - group3 - name: create user user: name={{ item.name }} group={{ item.groups }} state=present with_items: - {'name': cs1,'groups': group1} - {'name': cs2,'groups': group2} - {'name': cs3,'groups': group3} |
执行:
[root@server ~]# ansible-playbook item3.yml |
4.循环嵌套使用场景-批量删除用户及用户组
[root@server ~]# cat item4.yml --- - name: 批量删除用户及用户组 hosts: all remote_user: root
tasks: - name: drop user user: name: "{{ item.name }}" group: "{{ item.group }}" remove: yes state: absent with_items: - {'name': cs1,'group': group1} - {'name': cs2,'group': group2} - {'name': cs3,'group': group3} - name: drop group group: name: "{{ item }}" state: absent with_items: - group1 - group2 - group3 |
执行:
[root@server ~]# ansible-playbook item4.yml |
5.标准循环使用场景-批量拷贝多个文件
[root@server ~]# cat item5.yml - name: 批量拷贝文件 hosts: all remote_user: root tasks: - name: bulk copy copy: src=/home/test/{{ item.src }} dest=/home/cs/{{ item.dest }} mode={{ item.mode }} with_items: - {src: "a.sh", dest: "a.sh", mode: "0777"} - {src: "tongji.sh", dest: "tongji.sh", mode: "0777"}
# -------------------------------此为分界线,上下互不相干---------------------------------
- name: 批量拷贝文件到不同的路径 hosts: all remote_user: root tasks: - name: bulk copy copy: src=/home/test/{{ item.src }} dest={{ item.dest }} mode={{ item.mode }} with_items: - {src: "a.sh", dest: "/home/cs/a.sh", mode: "0777"} - {src: "tongji.sh", dest: "/home/cs1/tongji.sh", mode: "0777"} |
执行:
[root@server ~]# ansible-playbook item5.yml |
2.3异常处理
默认Playbook会检查命令和模块的返回状态,如遇到错误中断playbook的执行
加入参数:ignore_errors:yes#忽略错误
[root@server ~]# cat expect.yml --- - name: 忽略错误演示 hosts: all remote_user: root tasks: - name: 使用一个未知的命令 command: a ignore_errors: yes - name: 创建一个文件 file: path=/home/cs/yichang state=touch |
执行:
[root@server ~]# ansible-playbook expect.yml |
代码解析:
如上代码,执行到command的a,命令时,服务器是没有这个命令的,就会报错,报错了之后,下面的服务就不会继续执行了;
我们现在添加了一个ignore_reeors:yes,这样就可以跳过这个报错,继续执行下面的内容:
2.4tage标签
默认情况下,Ansible在执行一个playbook中定义的所有任务;Ansible playbook中的tag标签是一种用于选择性运行特定任务或任务集的机制。通过为每个任务指定标签,您可以在运行playbook时选择只运行带有特定标签的任务吧,而不运行其他任务。这对于控制和管理Ansible playbook的执行非常有用,特别是当您的playbook包含许多其他任务时。
- 1.打标签的方式
对一个对象打一个标签
对一个对象打多个标签
对多个对象打一个标签
- 2.标签使用,通过tags和任务对象进行捆绑,控制部分或者指定的task执行
-t:执行指定的tag标签任务
--list-tags:列出此yml文件中的所有tag标签
--skip-tags:执行-skip-tags之外的标签任务
- 3.以下是tag标签的作用:
1.选择性运行任务:可以使用--tags参数在运行ansible-playbook命令时指定一个或多个标签,只有带有指定标签的任务会运行。这对于在大型playbook中只运行特定任务非常有用,而不是运行整个playbook
- 排除任务:您可以使用--skip-tags参数来排除具有特定标签的任务,从而运行除带有指定标签的任务之外的所有其他任务 。这对于在大型playbook中排除不需要运行的任务非常有用。
- 组织任务:标签可以帮助您组织和分类任务。例如,您可以为配置任务添加一个config标签,为安装任务添加一个install 标签,以便更容易了解每个任务的用途。
4.文档和注释:标签还可以作为任务的文档和注释。您可以将标签用作描述任务的方式,以便其他人更容易理解每个任务的目的。
下面是一些示例,演示如何在运行ansible-playbook.yml时使用的标签:
- 仅运行带有install标签的任务:
[root@server ~]# ansible-playbook your_playbook.yml --tags install |
- 排除带有test标签的任务
[root@server ~]# ansible-playbook your_playbook.yml --skip-tags test |
- 运行带有多个标签的任务
[root@server ~]# ansible-playbook your_playbook.yml --tags "install,config" |
- 列出yml文件中所有的标签
[root@server ~]# ansible-playbook your_playbook.yml --list-tags |
标签功能有助于增加Ansible playbook的可维护性,使您能够更精细地控制任务的执行。
- 4.【实例】部署apache服务并启动,中间可以自行设置tags标签。
[root@server ~]# cat tags.yml --- - name: 部署apache服务 hosts: node2 remote_user: root vars: - http_port: 8080 tasks: - name: Install Http Server yum: name: httpd state: present tags: - install_httpd - httpd_server - name: configure httpd server copy: src: /httpd.conf dest: /etc/httpd/conf/httpd.conf mode: 0777 notify: Restart Httpd Server tags: - config_httpd - httpd_server - name: start httpd server service: name: httpd state: started enabled: yes tags: start_httpd
- name: stop httpd server service: name: httpd state: stopped tags: stop_httpd - name: uninstall httpd server yum: name: httpd state: absent tags: uninstall_httpd handlers: - name: Restart Httpd Server service: name: httpd state: restarted |
执行:
[root@server ~]# ansible-playbook tags.yml |
这是一个Ansible playbook的YAML文件,用于部署Apache HTTP服务器。YAML文件解析:
1.name: 部署apache服务:这是整个Ansible playbook的名称或描述。
2.hosts: web:这指定了该playbook将在名为web的主机组上执行。您需要在您的Ansible inventory文件中定义主机组web,或者在命令行上指定主机。
3.remote_user: root:指定了运行任务时使用root用户身份执行。
4.vars::这是一个变量部分,用于定义变量。在这里,定义了一个名为http_port的变量,其值为8080。
5.tasks::这是实际任务部分,其中包含一系列任务,每个任务都有一个名称和相应的操作。以下是每个任务的解释:
- 第一个任务:Install Http Server:这个任务使用yum模块来安装httpd软件包,确保Apache HTTP服务器已经安装在主机上。此任务有两个tags:install_httpd和httpd_server,以便后续可以选择性地运行这些任务。
- 第二个任务:configure httpd server:这个任务使用copy模块将本地文件httpd.conf复制到目标主机的/etc/httpd/conf/httpd.conf位置,并给目标文件设置执行权限,从而配置Apache HTTP服务器。此任务有两个tags:config_httpd和httpd_server。(为什么要多余移动这个呢,因为这个配置里我们自己写的,同时也修改了httpd的端口为:8080)。notify: Restart Httpd Server意味着当该任务的状态发生变化时(通常是成功完成任务),它将触发名为 “Restart Httpd Server” 的处理程序handlers。
- 第三个任务:start httpd server:这个任务使用service模块来启动并启用Apache HTTP服务器。此任务有一个tag:start_httpd。
- 第四个任务:stop httpd server:停止HTTP服务器。它使用 service 模块将 httpd 服务停止。此任务有一个标签 stop_httpd。
- 第五个任务:uninstall httpd server:卸载HTTP服务器。它使用 yum 模块卸载 httpd 软件包。此任务有一个标签 uninstall_httpd。
6.handlers::这部分定义了处理程序,这是一些在任务中使用的命名动作,通常与通知一起使用。在这里,定义了一个名为Restart Httpd Server的处理程序,当copy任务完成后,可以通知它。此处理程序使用systemd模块来重新启动httpd服务。
这个Playbook允许您执行与Apache HTTP服务器有关的各种操作,如安装、配置、启动、停止和卸载。使用不同的标签可以选择性地运行特定任务或任务组。
这个Playbook允许您执行与Apache HTTP服务器有关的各种操作,如安装、配置、启动、停止和卸载。使用不同的标签可以选择性地运行特定任务或任务组。
执行:
#查看此yml文件中的所有tags标签 [root@server ~]# ansible-playbook tags.yml --list-tags [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details playbook: tags.yml play #1 (node2): 部署apache服务 TAGS: [] TASK TAGS: [config_httpd, httpd_server, install_httpd, start_httpd, stop_httpd, uninstall_httpd] # 执行 install_httpd,config_httpd(安装、移动配置文件)标签;执行移动配置文件会触发处理程序,所以会自动启动服务; [root@server ~]# ansible-playbook tags.yml -t install_httpd,config_httpd # 执行 uninstall_httpd(卸载httpd服务) 标签 root@server ~]# ansible-playbook tags.yml -t uninstall_httpd # 执行 httpd_server 标签 [root@server ~]# ansible-playbook tags.yml -t httpd_server # httpd_server标签包含(Install Http Server、configure httpd server、Restart Httpd Server) # 所以会直接执行这三个任务,也就是安装、移动配置文件(因为使用移动文件触发了处理程序notify,所以就会执行Restart Httpd Server重启服务) # 总结就是:执行httpd_server标签,会安装服务并启动服务。 # 跳过 httpd_server,start_httpd(安装加启动,启动)标签 [root@server ~]# ansible-playbook tags.yml --skip-tags httpd_server,start_httpd # 执行结果为,关闭httpd服务并卸载httpd服务;因为已经跳过 httpd_server ,所以不会执行安装和启动; |
上面是查看tags.yml中所有的标签
都看到这了留个您宝贵的一键三呗谢谢你们啦