Ansible 简介
Ansible 基于 Python paramiko 开发,无客户端、无服务端,默认 SSH 连接被控主机,YAML 语法编写剧本,核心靠模块实现批量操作。 核心特性:
no agents:被控机器不用装客户端,仅管理节点安装 Ansible
no server:无中心服务,直接命令运行
默认 SSH 通信,支持多级编排
区分两种使用模式:
Ad-hoc:临时单行快速命令,适合简单一次性操作
Playbook:YAML 剧本,复杂批量部署、持久复用
Ansible 安装
[root@server1 ~]# yum install -y ansible-core.x86_64
[root@server1 ~]# yum install -y ansible-collection-redhat-rhel_mgmt.noarch
[root@server1 ~]# netstat -antlp | grep :22
[root@server1 ~]# cd /etc/ansible/
[root@server1 ansible]# cat ansible.cfg
[root@server1 ansible]# vim hosts
显示主机清单文件的主机信息
[root@server1 ansible]# ansible-inventory --graph
Ansible Ad-hoc
免密配置
[root@server1 ansible]# ssh-keygen
[root@server1 ansible]# cat hosts
[root@server1 ansible]# ssh-copy-id 192.168.164.161
[root@server1 ansible]# ssh-copy-id 192.168.164.162
[root@server1 ansible]# ssh-copy-id 192.168.164.171
ansible后一定跟解析过的
[root@server1 ansible]# ansible centos7 -m command -a 'hostname'
node1 | CHANGED | rc=0 >>
node1
[root@server1 ansible]# ansible rocky9 -a 'hostname'
[root@server1 ansible]# ansible rocky9 -a 'touch /tmp/test.txt'
[root@server1 ansible]# ansible rocky9 -a 'ls -l /tmp/test.txt'
[root@server1 ansible]# ansible rocky9 -a 'rm -f /tmp/test.txt'
语法
[root@ansible ~]# ansible <pattern> -m <module_name> -a <arguments>
参数详解
pattern: inventory文件里定义的主机组名,主机名,IP,别名等,all表示所有的主机,支持通配符,正则
: - 冒号,多个组,组名之间用冒号隔开
web-组名或主机名中含web的
webservers[0] - webservers组中的第一台主机, -1表示最后一台主机,[1:2]表示组中的第二台和第三台
all 所有
-m module_name:模块名称,默认为command
-a arguments:传递给模块的参数
-i file_name:指定inventory文件,默认为/etc/ansible/hosts
-k : 询问密码
Ansible模块
主机连通性
保持连通状态
[root@server1 ansible]# ansible web -m ping
192.168.164.162 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
Ansible Playbook剧本
playbook
严格缩进
Playbook 组成
Target section
定义将要执行 playbook的远程主机组 - hosts
Variable section(可选)
定义 playbook运行时需要使用的变量- vars
Task section
定义将要在远程主机上执行的任务列表 - tasks
Handler section(可选)
定义 task执行完成以后需要调用的任务 - handlers
语法规则
文件的第一行应该以"---"(三个连字符)开始,表明YAML文件的开始。
YAML中的 列表元素 以”-”开头然后紧跟着一个空格,后面为元素内容。
同一个列表中的元素应该保持相同的缩进。否则会被当做错误处理。
play中hosts,variables,roles,tasks等对象的表示方法都是键值中间以":"分隔表示,":"后面还要增加一个空格。
检测
语法检测
[root@server1 ansible]# ansible-playbook --syntax-check /etc/ansible/test.yml
playbook: /etc/ansible/test.yml
测试运行
[root@server1 ansible]# ansible-playbook -C /etc/ansible/test.yml
基本使用案例
创建普通用户
[root@server1 ~]# ansible-config dump
[root@server1 ~]# useradd testuser
[root@server1 ~]# su - testuser
[testuser@server1 ~]$ pwd
[testuser@server1 ~]$ ansible-config dump
[root@server2 ~]# useradd testuser
[root@server2 ~]# passwd testuser
[testuser@server1 ~]$ ssh-keygen
[testuser@server1 ~]$ ssh-copy-id testuser@server2
[testuser@server1 ~]$ ssh testuser@server2 记得退出ssh连接
[testuser@server1 ~]$ ansible web -m ping
设置visudo
[root@server2 ~]# visudo
创建目录
[testuser@server1 ~]$ mkdir ansible
[testuser@server1 ~]$ cd ansible/
[testuser@server1 ansible]$ ls
[testuser@server1 ansible]$ vim apache.yml
[root@server1 ~]# ansible-doc yum 查看examples,q退出
注意权限
[testuser@server1 ansible]$ ansible-playbook --syntax-check apache.yml
[testuser@server1 ansible]$ ansible-playbook -C apache.yml
[testuser@server1 ansible]$ ansible-playbook apache.yml
[testuser@server1 ansible]$ ansible-playbook apache.yml 重复命令无影响
[testuser@server1 ansible]$ vim apache.yml
[root@server1 ~]# ansible-doc service
[root@server1 ~]# ansible-doc copy
[testuser@server1 ansible]$ ansible-playbook apache.yml
[testuser@server1 ansible]$ curl server2
远程下载
[testuser@server1 ansible]$ ansible-config init --disable > ansible.cfg
[testuser@server1 ansible]$ ls
[testuser@server1 ansible]$ ansible-config dump
[testuser@server1 ansible]$ vim hosts
[web]
192.168.164.162
[db]
192.168.164.171
[lamp:children]]
web
db
[testuser@server1 ansible]$ vim ansible.cfg
inventory=./hosts
[testuser@server1 ansible]$ ansible-config dump | grep hosts
DEFAULT_HOST_LIST(/home/testuser/ansible/ansible.cfg) = ['/home/testuser/ansible/hosts']
[testuser@server1 ansible]$ ansible-inventory --graph
[testuser@server1 ansible]$ ansible-inventory --list
[root@node1 ~]# useradd testuser
[root@node1 ~]# passwd testuser
[root@node1 ~]# visudo
[testuser@server1 ansible]$ ssh-copy-id testuser@node1
[testuser@server1 ansible]$ ansible lamp -m ping
[testuser@server1 ansible]$ vim ansible.cfg
全局定义后playbook不需要再定义become
控制机身份不一样时,指定remote_user
[testuser@server1 ansible]$ vim ansible.cfg
remote_user=testuser
端口
[testuser@server1 ansible]$ vim apache.yml
- name: Ensure the default Apache port is 8080
ansible.builtin.lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen '
insertafter: '^#Listen '
line: Listen 8080
[testuser@server1 ansible]$ ansible-playbook apache.yml
服务重启生效
[root@server2 ~]# systemctl restart httpd
[root@server2 ~]# netstat -antlp | grep :8080
playbook一键实现重启,使用触发器
[testuser@server1 ansible]$ vim apache.yml
加入变量
[testuser@server1 ansible]$ vim apache.yml
[testuser@server1 ansible]$ ansible web -m setup -a 'filter=ansible_hostname'
192.168.164.162 | SUCCESS => {
"ansible_facts": {
"ansible_hostname": "server2",
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false
}
[testuser@server1 ansible]$ ansible-playbook apache.yml
[testuser@server1 ansible]$ curl server2
server2
[testuser@server1 ansible]$ vim apache.yml
[testuser@server1 ansible]$ ansible-playbook apache.yml
[testuser@server1 ansible]$ vim apache.yml
[testuser@server1 ansible]$ ansible-playbook apache.yml
变量
[testuser@server1 ansible]$ vim apache.yml
vars:
- http_port: 80
[testuser@server1 ansible]$ ansible-playbook apache.yml
如果多主机可先注释掉vars,修改hosts
[testuser@server1 ansible]$ vim hosts
[web]
192.168.164.162 http_port=80
[db]
192.168.164.171 http_port=8080
[lamp:children]
web
db
[testuser@server1 ansible]$ ansible-playbook apache.yml
template
[testuser@server1 ansible]$ vim apache.yml
[testuser@server1 ansible]$ scp server2:/etc/httpd/conf/httpd.conf .
[testuser@server1 ansible]$ mv httpd.conf httpd.conf.j2
[testuser@server1 ansible]$ vim httpd.conf.j2
Listen {{ http_port }}
[testuser@server1 ansible]$ vim hosts
[web]
192.168.164.162
[db]
192.168.164.171
[lamp:children]
web
db
[lamp:vars]
http_port=80
[testuser@server1 ansible]$ ansible-playbook apache.yml
高可用keepalived配置
[testuser@server1 ansible]$ cd
[root@server1 ~]# cd /etc/ansible
[root@server1 ansible]# passwd testuser
[root@server1 ansible]# su - testuser
[testuser@server1 ~]$ ssh-copy-id 192.168.164.161
[testuser@server1 ~]$ logout
[root@server1 ansible]# visudo
testuser ALL=(ALL) NOPASSWD: ALL
[testuser@server1 ~]$ cd ansible/
[testuser@server1 ansible]$ vim hosts
[testuser@server1 ansible]$ cp apache.yml keepalived.yml
[root@server1 ~]# yum install -y keepalived
[root@server1 ~]# ll /etc/keepalived/keepalived.conf
-rw-r--r-- 1 root root 3550 Nov 2 2025 /etc/keepalived/keepalived.conf
[testuser@server1 ansible]$ vim keepalived.yml
[testuser@server1 ansible]$ cp /etc/keepalived/keepalived.conf .
[testuser@server1 ansible]$ mv keepalived.conf keepalived.conf.j2
[testuser@server1 ansible]$ vim keepalived.conf.j2
[testuser@server1 ansible]$ ansible-playbook keepalived.yml
[testuser@server1 ansible]$ sudo systemctl status keepalived
[root@server2 ~]# systemctl status keepalived
批量化创建loop
传递数组
[testuser@server1 ansible]$ vim user.yml
[testuser@server1 ansible]$ vim ansible.cfg
deprecation_warnings=false
[testuser@server1 ansible]$ ansible-playbook user.yml
[root@node1 ~]# cat /etc/shadow
tags标签
[testuser@server1 ansible]$ ansible-playbook apache.yml --list-tasks
[testuser@server1 ansible]$ vim apache.yml
[testuser@server1 ansible]$ ansible-playbook apache.yml --list-tags
执行标签
[testuser@server1 ansible]$ ansible-playbook apache.yml --tags=t1
跳过标签
[testuser@server1 ansible]$ ansible-playbook apache.yml --skip-tags=t1
变量
facts
lineinfile
追加解析
[testuser@server1 ansible]$ ansible db -m setup setup模块
[testuser@server1 ansible]$ vim lineinfile.yml
[testuser@server1 ansible]$ ansible-playbook lineinfile.yml
[root@node1 ~]# cat /etc/hosts
魔术变量
循环
[testuser@server1 ansible]$ vim hosts
[webservers]
192.168.164.161
192.168.164.162
192.168.164.171
[testuser@server1 ansible]$ vim groups.yml
删除解析
[testuser@server1 ansible]$ vim test.j2
[root@server2 ~]# vim /etc/hosts
[root@server3 ~]# vim /etc/hosts
魔术变量,for循环
[testuser@server1 ansible]$ vim test.j2
[testuser@server1 ansible]$ cat /etc/hosts
模板
haproxy
server1作为调度器,server2、node1作为web-server
[root@server1 ansible]# yum install -y haproxy
[testuser@server1 ansible]$ vim hosts
[webservers]
192.168.164.162
192.168.164.171
[webservers:vars]
http_port=80
[haproxy]
server1
[haproxy:vars]
http_port=80
关闭keepalived
[testuser@server1 ansible]$ vim keepalived.yml
[testuser@server1 ansible]$ ansible-playbook keepalived.yml --tags=t1
[testuser@server1 ansible]$ vim haproxy.yml
---
- hosts: haproxy,webservers
tasks:
- name: deplo haproxy
block:
- name: install haproxy
ansible.builtin.yum:
name: haproxy
state: present
- name: configure the haproxy
ansible.builtin.template:
src: haproxy.cfg.j2
dest: /etc/haproxy/haproxy.cfg
notify: restart service haproxy
- name: enable service haproxy
ansible.builtin.service:
name: haproxy
state: started
enabled: yes
when: ansible_hostname == 'server1'
- name: deploy apache
block:
- name: Install the Apache
ansible.builtin.yum:
name: httpd
state: present
- name: Ensure the default Apache port is {{ http_port }}
ansible.builtin.template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
notify: restart service httpd
- name: Start service httpd
ansible.builtin.service:
name: httpd
state: started
enabled: yes
- name: creat index.html
ansible.builtin.copy:
content: "{{ ansible_hostname }}\n"
dest: /var/www/html/index.html
when: "'webservers' in group_names"
handlers:
- name: restart service haproxy
ansible.builtin.service:
name: haproxy
state: restarted
- name: restart service httpd
ansible.builtin.service:
name: httpd
state: restarted
[testuser@server1 ansible]$ vim httpd.conf.j2
Listen {{ http_port }}
[testuser@server1 ansible]$ cp /etc/haproxy/haproxy.cfg .
[testuser@server1 ansible]$ mv haproxy.cfg haproxy.cfg.j2
[testuser@server1 ansible]$ vim haproxy.cfg.j2
[testuser@server1 ansible]$ ansible-playbook --syntax-check haproxy.yml
playbook: haproxy.yml
[testuser@server1 ansible]$ ansible-playbook haproxy.yml
验证
浏览器访问http://192.168.164.161:80
再创建一台虚拟机node2
[root@node2 ~]# useradd testuser
[root@node2 ~]# passwd testuser
[root@node2 ~]# visudo
root ALL=(ALL) ALL
testuser ALL=(ALL) NOPASSWD: ALL
解析
[root@node2 ~]# vim /etc/hosts
[root@server1 ansible]# vim /etc/hosts
[testuser@server1 ansible]$ vim hosts
[webservers]
server2
node1
node2
[testuser@server1 ansible]$ ansible-playbook haproxy.yml
验证
浏览器访问http://192.168.164.161:80
功能集合
[testuser@server1 ansible]$ ansible-galaxy collection install ansible.mysql
[testuser@server1 ansible]$ ansible-galaxy collection list
ansible_playbook层级结构
ansible-vault --help 查看帮助
加密
[testuser@server1 ansible]$ vim user.yml
[testuser@server1 ansible]$ vim userlist.yml
[testuser@server1 ansible]$ ansible-playbook --syntax-check user.yml
[testuser@server1 ansible]$ ansible-playbook user.yml
加密
[testuser@server1 ansible]$ ansible-vault encrypt userlist.yml
[testuser@server1 ansible]$ cat userlist.yml 无法查看
解密
[testuser@server1 ansible]$ ansible-vault decrypt userlist.yml
查看
[testuser@server1 ansible]$ ansible-vault view userlist.yml
修改密码
[testuser@server1 ansible]$ ansible-vault rekey userlist.yml
编辑
[testuser@server1 ansible]$ ansible-vault edit userlist.yml
执行加密后的剧本加参数--ask-vault-pass
[testuser@server1 ansible]$ ansible-playbook user.yml --ask-vault-pass
变量文件层级结构
[testuser@server1 ansible]$ vim hosts
[web]
server2
[db]
node1
[testuser@server1 ansible]$ mkdir host_vars
[testuser@server1 ansible]$ cd host_vars/
[testuser@server1 host_vars]$ mkdir node1
[testuser@server1 host_vars]$ cd node1/
[testuser@server1 node1]$ cp ../../userlist.yml vault
[testuser@server1 node1]$ ansible-vault encrypt vault
[testuser@server1 node1]$ cd ..
[testuser@server1 host_vars]$ cd ..
[testuser@server1 ansible]$ vim user.yml
#vars_files:
# - userlist.yml
[testuser@server1 ansible]$ ansible-playbook user.yml --ask-vault-pass
apache
[testuser@server1 ansible]$ vim apache.yml
- hosts: webservers
[testuser@server1 ansible]$ vim hosts
[testuser@server1 ansible]$ mkdir group_vars
[testuser@server1 ansible]$ cd group_vars/
[testuser@server1 group_vars]$ mkdir webservers/
[testuser@server1 group_vars]$ cd webservers/
[testuser@server1 webservers]$ vim vars
---
http_port: 80
[testuser@server1 webservers]$ cd ..
[testuser@server1 group_vars]$ cd ..
[testuser@server1 ansible]$ ansible-playbook --syntax-check apache.yml
playbook: apache.yml
[testuser@server1 ansible]$ ansible-playbook apache.yml --ask-vault-pass
剧本参数
滚动部署:serial: 1
全局并行进程数:ansible.cfg
[defaults] forks = 10
或者ansible-playbook apache.yml -f 10
限制最大并发:throttle: 3
剧本安装zabbix
参考网站galaxy.ansible.com
ansible-galaxy collection install ansible.posix
vim zabbix.yml
ansible-playbook --syntax-check zabbix.yml
执行完 playbook,访问:http://192.168.164.162/zabbix,进入 zabbix 初始化向导,默认账号Admin,密码zabbix
ansible roles角色
apache_role.yml配置
role读取路径
[testuser@server1 ansible]$ vim ansible.cfg
roles_path=./ansible
[testuser@server1 ansible]$ cd roles/
[testuser@server1 ansible]$ cd roles/
[testuser@server1 roles]$ ansible-galaxy role --help
添加role
[testuser@server1 roles]$ ansible-galaxy role init apache
- Role apache was created successfully
查看结构
[testuser@server1 roles]$ sudo yum install -y tree
[testuser@server1 roles]$ cd apache/
[testuser@server1 apache]$ tree .
拆分apache.yml
[testuser@server1 apache]$ cd tests/
[testuser@server1 tests]$ cat test.yml
[testuser@server1 apache]$ rm -rf tests/
[testuser@server1 apache]$ ls
另开终端
[root@server1 ~]# su - testuser
[testuser@server1 ~]$ cd ansible/
[testuser@server1 ansible]$ vim apache.yml
配置角色apache
[testuser@server1 apache]$ cd tasks/
[testuser@server1 tasks]$ vim main.yml
[testuser@server1 tasks]$ cd ..
[testuser@server1 apache]$ cd handlers/
[testuser@server1 handlers]$ vim main.yml
[testuser@server1 handlers]$ cd ..
[testuser@server1 apache]$ cd defaults/
[testuser@server1 defaults]$ vim main.yml
---
http_port: 80
[testuser@server1 defaults]$ cd /ansible/
[testuser@server1 ansible]$ ansible-inventory --graph
[testuser@server1 ansible]$ mv host_vars/ group_vars /tmp/
[testuser@server1 ansible]$ ansible-inventory --graph
[testuser@server1 ansible]$ ansible-playbook apache_role.yml
改变端口,读取优先级
[testuser@server1 ansible]$ vim apache_role.yml
http_port: 8080
[testuser@server1 ansible]$ ansible-playbook apache_role.yml
[root@server2 ~]# netstat -anplt | grep :8080
配置防火墙
[testuser@server1 ansible]$ vim apache_role.yml
[testuser@server1 ansible]$ ansible-playbook apache_role.yml
另开终端
[root@server1 ~]# cd /home/testuser/ansible/
[root@server1 ansible]# cat apache.yml
[testuser@server1 ansible]$ vim apache_role.yml
[testuser@server1 ansible]$ ansible-playbook apache_role.yml
普通用户安装,避免环境混乱
[testuser@server1 ansible]$ ansible-galaxy collection install ansible.posix
[testuser@server1 ansible]$ ansible-galaxy collection list
[testuser@server1 ansible]$ ansible-doc -l
套用模板
[testuser@server1 ansible]$ ansible-doc ansible.posix.firewalld
[testuser@server1 ansible]$ vim apache_role.yml
[testuser@server1 ansible]$ ansible-playbook apache_role.yml
selinux需要重启生效
[testuser@server1 ansible]$ ansible-doc ansible.posix.selinux
安装系统角色
[testuser@server1 ansible]$ yum search role
[testuser@server1 ansible]$ sudo yum install -y rhel-system-roles.noarch
[testuser@server1 ansible]$ rpm -ql rhel-system-roles
roles_path=./ansible:/usr/share/ansible/roles
[testuser@server1 ansible]$ ansible-galaxy role list
系统角色路径
[testuser@server1 ~]$ cd /usr/share/doc/
[testuser@server1 doc]$ cd rhel-system-roles/
时间同步
[testuser@server1 rhel-system-roles]$ cd timesync/
[testuser@server1 timesync]$ cp example-single-pool-playbook.yml ~/ansible/
[testuser@server1 timesync]$ cd ~/ansible/
[testuser@server1 ansible]$ mv example-single-pool-playbook.yml timesync.yml
[testuser@server1 ansible]$ vim timesync.yml
---
- name: system timesync
hosts: webservers
vars:
timesync_ntp_servers:
- hostname: ntp1.aliyun.com
pool: true
iburst: true
roles:
- rhel-system-roles.timesync
[testuser@server1 ansible]$ ansible-playbook timesync.yml
剧本导入,用于linux版本不同场景
导入import_playbook
[testuser@server1 ansible]$ vim apache_role.yml
- name: timesync palaybook
import_playbook: timesync.yml
[testuser@server1 ansible]$ ansible-playbook apache_role.yml
selinux
node1上
[root@node1 ~]# vim /etc/httpd/conf/httpd.conf
Listen 8000
server1上
[testuser@server1 ~]$ cd /usr/share/doc/
[testuser@server1 doc]$ cd rhel-system-roles/
[testuser@server1 rhel-system-roles]$ cd selinux/
[testuser@server1 selinux]$ cp example-selinux-playbook.yml ~/ansible/
[testuser@server1 ~]$ cd ~/ansible/
[testuser@server1 ansible]$ mv example-selinux-playbook.yml selinux.yml
[testuser@server1 ansible]$ vim selinux.yml
[testuser@server1 ansible]$ vim apache_role.yml
[testuser@server1 ansible]$ ansible-playbook apache_role.yml
[root@node1 ~]# firewall-cmd --list-all
角色role
nginx
server2
[root@server2 ~]# systemctl disable --now zabbix-server
[root@server2 ~]# systemctl disable --now zabbix-agent
[root@server2 ~]# systemctl disable --now php-fpm
[root@server2 ~]# systemctl disable --now httpd
[root@server2 ~]# systemctl disable --now mysqld
网站Ansible Galaxy
[testuser@server1 ~]$ ansible-galaxy role install nginxinc.nginx
[testuser@server1 ~]$ ansible-galaxy role install geerlingguy.nginx
role位置
[testuser@server1 ~]$ ansible-galaxy role list
[testuser@server1 ~]$ cd .ansible/roles
[testuser@server1 roles]$ cd geerlingguy.nginx
[testuser@server1 geerlingguy.nginx]$ ls
[testuser@server1 geerlingguy.nginx]$ cd meta
[testuser@server1 meta]$ cat main.yml
[testuser@server1 meta]$ cd ..
[testuser@server1 geerlingguy.nginx]$ cd vars
[testuser@server1 vars]$ ls
另开终端
ansible.cfg配置
[testuser@server1 ansible]$ vim ansible.cfg
roles_path=./ansible:/usr/share/ansible/roles:/home/testuser/.ansible/roles
[testuser@server1 ansible]$ vim nginx.yml
---
- hosts: server2
roles:
- role: geerlingguy.nginx
[testuser@server1 ansible]$ ansible-playbook nginx.yml --list-tasks
[testuser@server1 ansible]$ ansible-playbook nginx.yml
[testuser@server1 ansible]$ curl server2
server2
[root@server2 ~]# rpm -qa|grep nginx
注意生产不推荐直接使用
删除角色remove
gitee
创建gitee账号
[testuser@server1 ~]$ cd .ssh/
[testuser@server1 .ssh]$ cat id_rsa.pub
上传公钥
新建仓库
server1上初始化
[testuser@server1 ~]$ cd ansible/roles/apache/
[testuser@server1 apache]$ git init
[testuser@server1 apache]$ git config --global user.name "uu"
git config --global user.email "17456376+uu_2026@user.noreply.gitee.com"
[testuser@server1 apache]$ git add .
[testuser@server1 apache]$ git commit -m "ansible-roleapache"
提交到本地[testuser@server1 apache]$ ls .git/
[testuser@server1 apache]$ git remote add origin git@gitee.com:uu_2026/ansible.git ssh方式免密
[testuser@server1 apache]$ git push -u origin "master"
下载role
[testuser@server1 apache]$ cd ..
[testuser@server1 roles]$ vim requirements.yml
---
- src: git@gitee.com:uu_2026/ansible.git
scm: git
version: master
name: apache
[testuser@server1 roles]$ ansible-galaxy role install -r roles/requirements.yml -p roles/
另一种方式直接克隆,修改名字即可