文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

一文详解Ansible的自动化运维

2024-12-03 16:02

关注

一、Ansible 概述

Ansible 是近年来越来越火的一款开源运维自动化工具,通过Ansible可以实现运维自动化,提高运维工程师的工作效率,减少人为失误。

Ansible 通过本身集成的非常丰富的模块可以实现各种管理任务,其自带模块超过上千个。更为重要的是,它操作非常简单,即使小白也可以轻松上手,但它提供的功能又非常丰富,在运维领域,几乎可以做任何事。

1、Ansible 特点

Ansible 自 2012 年发布以来,很快在全球流行,其特点如下:

    Ansible发布后,也陆续被 AWS、Google Cloud Platform、Microsoft Azure、Cisco、HP、VMware、Twitter 等大公司接纳并投入使用;

二、Ansible的角色

1、使用者

如下图所示:Ansible 使用者可以采用多种方式和 Ansible 交互,图中展示了四种方式:

2、Ansible 工具集

Ansible 工具集包含 Inventory、Modules、Plugins 和 API。

其中:Inventory:用来管理设备列表,可以通过分组实现,对组的调用直接影响组内的所有主机;Modules:是各种执行模块,几乎所有的管理任务都是通过模块执行的;Plugins:提供了各种附加功能;API:为编程人员提供一个接口,可以基于此做 Ansible的二次开发;具体表现如下:

3、作用对象

Ansible 的作用对象不仅仅是 Linux 和非 Linux 操作系统的主机,也可以作用于各类PUBLIC/PRIVATE、商业和非商业设备的网络设施。

使用者使用 Ansible 或 Ansible-Playbooks 时,在服务器终端输入 Ansible 的 Ad-Hoc命令集或 Playbooks 后,Ansible 会遵循预选安排的规则将 Playbooks 逐步拆解为Play,再将 Play 组织成 Ansible 可以识别的任务,随后调用任务涉及的所有模块和插件,根据 Inventory 中定义的主机列表通过 SSH 将任务集以临时文件或命令的形式传输到远程客户端执行并返回执行结果,如果是临时文件则执行完毕后自动删除。

三、Ansible的配置

1、Ansible安装

Ansible的安装部署非常简单,以RPM安装为例,其依赖软件只有Python和SSH,且系统默认均已安装。Ansible的管理端只能是Linux,如Redhat、Debian、Centos。

1)通过YUM安装Ansible

可以自行从互联网上直接下载Ansible所需软件包,本篇博客提供安装Ansible自动化运维工具所需的依赖软件包 

  1. [root@centos01 ~]# cd /mnt/ansiblerepo/ansiblerepo/repodata/  
  2. [root@centos01 ansiblerepo]# vim /etc/yum.repos.d/local.repo  
  3. [local] 
  4. name=centos 
  5. baseurl=file:///mnt/ansiblerepo/ansiblerepo    
  6. enabled=1  
  7. gpgcheck=0  
  8. [root@centos01 ~]# yum -y install ansible  
  9.                  

2)验证安装结果 

  1. [root@centos01 ~]# ansible --version  
  2.       
  3. ansible 2.3.1.0  
  4.   config file = /etc/ansible/ansible.cfg  
  5.   configured module search path = Default w/o overrides  
  6.   python version = 2.7.5 (default, Nov  6 2016, 00:28:07) [GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] 

3)创建 SSH 免交互登录

Ansible 通过 SSH 对设备进行管理,而 SSH 包含两种认证方式:一种是通过密码认证,另一种是通过密钥对验证。前者必须和系统交互,而后者是免交互登录。如果希望通过 Ansible 自动管理设备,应该配置为免交互登录被管理设备。 

  1. [root@centos01 ~]# ssh-keygen -t rsa    
  2. Generating public/private rsa key pair.  
  3. Enter file in which to save the key (/root/.ssh/id_rsa):  
  4. Created directory '/root/.ssh'.  
  5. Enter passphrase (empty for no passphrase):  
  6.          
  7. Enter same passphrase again:      
  8. Your identification has been saved in /root/.ssh/id_rsa.  
  9. Your public key has been saved in /root/.ssh/id_rsa.pub.  
  10. The key fingerprint is:  
  11. SHA256:cJz6NRTrvMDxX+Jpce6LRnWI3vVEl/zvARL7D10q9WY root@centos01  
  12. The key's randomart image is:  
  13. +---[RSA 2048]----+  
  14. |          .   . .|  
  15. |       . . +   oo|  
  16. |      . = o o. oo|  
  17. |       = * o..+ *|  
  18. |      . S *.=+=*+|  
  19. |       . o =+XooE|  
  20. |        . ..=.++.|  
  21. |           ..o ..|  
  22. |           .. o. |  
  23. +----[SHA256]-----+  
  24. [root@centos01 ~]# ssh-copy-id -i .ssh/id_rsa.pub  root@192.168.100.20     
  25. [root@centos01 ~]# ssh-copy-id -i .ssh/id_rsa.pub  root@192.168.100.30     

至此,已经完成 Ansible 的部署,接下来就可以通过 Ansible 对设备进行管理了。

2、Ansible 配置

Inventory 是 Ansible 管理主机信息的配置文件,相当于系统 Hosts 文件的功能,默认存放在 /etc/ansible/hosts。

在 hosts 文件中,通过分组来组织设备,Ansible 通过 Inventory 来定义主机和分组,通过在 ansible 命令中使用选项-i或—inventory-file来指定 Inventory。 

  1. [root@centos01 ~]# ansible -i /etc/ansible/hosts web -m ping 

如果使用默认的 Inventory文件(/etc/ansible/hosts),也可以不指定 Inventory 文件,例如: 

  1. [root@centos01 ~]# ansible web -m ping 

Ansible 通过设备列表以分组的方式添加到 /etc/ansible/hosts 文件来实现对设备的管理,所以在正式管理之前,首先要编写好 hosts 文件。hosts 文件中,以[ ]包含的部分代表组名,设备列表支持主机名和IP地址。

默认情况下,通过访问22端口(SSH)来管理设备。若目标主机使用了非默认的SSH端口,还可以在主机名称之后使用冒号加端口标明,以行为单位分隔配置。另外,hosts文件还支持通配符。 

  1. [root@centos01 ~]# vim /etc/ansible/hosts  
  2. ............     
  3. [web]  
  4. 192.168.100.20 
  5. 192.168.100.30  
  6. [test]  
  7. www.benet.com:222                          
  8. [mail]  
  9. yj1.kgc.cn  
  10. yj[2:5].kgc.cn  
  11.  

可以将一个主机同时归置在不同的组中。

配置完成之后,可以针对hosts定义的组进行远程操作,也可以针对组中的某一个或多个主机操作。例如:

1)只对web组中192.168.1.2主机操作,通过—limit参数限定主机的变更。 

  1. [root@centos01 ~]# ansible web -m command -a "systemctl status httpd" --limit "192.168.100.20"  
  2. 192.168.100.20 | SUCCESS | rc=0 >>  
  3.   
  4.  

2)只对192.168.100.20主机操作。通过IP限定主机的变更。 

  1. [root@centos01 ~]# ansible 192.168.100.20 -m command -a "systemctl status httpd"  
  2. 192.168.100.20 | SUCCESS | rc=0 >> 

3)只对192.168.100.0网段主机操作,这就需要使用到通配符来限定主机的变更了。 

  1. [root@centos01 ~]# ansible 192.168.1.* -m command -a "systemctl status httpd"  
  2. 192.168.100.20 | SUCCESS | rc=0 >>  
  3. .......    
  4. 192.168.100.30 | SUCCESS | rc=0 >>  
  5. .......      
  6.  

3、Ansible 命令

Ansible 的维护命令大多数是以 ansible 开头,在终端输入 ansible 后连续按两次Tab键,会补全所有跟 ansible 相关的命令。 

  1. [root@centos01 ~]# ansible    
  2. ansible               ansible-console-2     ansible-galaxy        ansible-playbook-2.7  ansible-vault-2  
  3. ansible-2             ansible-console-2.7   ansible-galaxy-2      ansible-pull          ansible-vault-2.7  
  4. ansible-2.7           ansible-doc           ansible-galaxy-2.7    ansible-pull-2  
  5. ansible-connection    ansible-doc-2         ansible-playbook      ansible-pull-2.7  
  6. ansible-console       ansible-doc-2.7       ansible-playbook-2    ansible-vault 

1)ansible

ansible 是生产环境中使用非常频繁的命令之一,主要在以下场景使用:

非固化需求;

临时一次性操作;

二次开发接口调用;

非固化需求是指临时性的维护,如查看web服务器组磁盘使用情况、复制一个文件到其他机器等。类似这些没有规律的、临时需要做的任务,我们成为非固化需求,临时一次性操作,语法如下:

  1. Ansible  <host-pattern> [options] 

①检查所有主机是否存活,执行命令如下: 

  1. [root@centos01 ~]# ansible all -f 5 -m ping  
  2.   
  3. 192.168.100.20 | SUCCESS => {                 
  4.     "changed": false,                          
  5.     "ping": "pong"                    
  6.  
  7. 192.168.100.30 | SUCCESS => {  
  8.     "changed": false,  
  9.     "ping": "pong"  

②列出web组所有的主机列表,执行命令如下: 

  1. [root@centos01 ~]# ansible web --list        
  2.   hosts (2):  
  3.     192.168.100.20  
  4.     192.168.100.30 

③批量显示web组中的磁盘使用空间,执行命令如下: 

  1. [root@centos01 ~]# ansible web -m command -a "df -hT"  
  2. 192.168.100.30 | SUCCESS | rc=0 >>  
  3. 文件系统            类型      容量  已用  可用 已用% 挂载点  
  4. /dev/mapper/cl-root xfs        17G  4.4G   13G   26% /  
  5. devtmpfs            devtmpfs  897M     0  897M    0% /dev  
  6. tmpfs               tmpfs     912M   84K  912M    1% /dev/shm  
  7. tmpfs               tmpfs     912M     0  912M    0% /sys/fs/cgroup  
  8. /dev/sda1           xfs      1014M  173M  842M   18% /boot  
  9. tmpfs               tmpfs     183M   16K  183M    1% /run/user/42  
  10. tmpfs               tmpfs     183M     0  183M    0% /run/user/0  
  11. 192.168.100.20 | SUCCESS | rc=0 >>  
  12. 文件系统            类型      容量  已用  可用 已用% 挂载点  
  13. /dev/mapper/cl-root xfs        17G  4.3G   13G   26% /  
  14. devtmpfs            devtmpfs  897M     0  897M    0% /dev  
  15. tmpfs               tmpfs     912M   84K  912M    1% /dev/shm  
  16. tmpfs               tmpfs     912M     0  912M    0% /sys/fs/cgroup  
  17. /dev/sda1           xfs      1014M  173M  842M   18% /boot  
  18. tmpfs               tmpfs     183M   16K  183M    1% /run/user/42  
  19. tmpfs               tmpfs     183M     0  183M    0% /run/user/0  
  20. /dev/sr0            iso9660   4.1G  4.1G     0  100% /mnt 

web关键字需要提前在/etc/ansible/hosts文件中定义组。

Ansible的返回结果非常友好,一般会用三种颜色来表示执行结果:

2)Ansible-doc

Ansible-doc用来查询ansible模块文档的说明,类似于man命令,针对每个模块都有详细的用法说明及应用案例介绍,语法如下: 

  1. ansible-doc [options] [module……] 

列出支持的模块: 

  1. [root@centos01 ~]#ansible-doc -l 

查询ping模块的说明信息: 

  1. [root@centos01 ~]# ansible-doc ping  
  2. > PING    (/usr/lib/python2.7/site-packages/ansible/modules/system/ping.py)  
  3.   A trivial test module, this module always returns `pong' on successful contact. It  
  4.   does not make sense in playbooks, but it is useful from `/usr/bin/ansible' to verify  
  5.   the ability to login and that a usable python is configured. This is NOT ICMP ping,  
  6.   this is just a trivial test module.  
  7. EXAMPLES:  
  8. # Test we can logon to 'webservers' and execute python with json lib.  
  9. ansible webservers -m ping  
  10. MAINTAINERS: Ansible Core Team, Michael DeHaan 
  11. METADATA:  
  12.         Status: ['stableinterface']  
  13.         Supported_by: core 

3)Ansible-playbook

Ansible-playbook是日常应用中使用频率最高的命令,类似于Linux中的sh或source命令,用来执行系列任务。

其工作机制:通过读取预先编写好的playbook文件实现集中处理任务。Ansible-playbook命令后跟yml格式的playbook文件,playbook文件存放了要执行的任务代码,命令使用方式如下: 

  1. Ansible-playbook playbook.yml  
  2.  

4)Ansible-console

Ansible-console是Ansible为用户提供的一款交互式工具,类似于Windows的cmd或者是Linux中shell。用户可以在ansible-console虚拟出来的终端上像shell一样使用Ansible内置的各种命令,这为习惯于使用shell交互式方式的用户提供了良好的使用体验。在终端输入ansible-console命令后,显示如下: 

  1. [root@centos01 ~]# ansible-console  
  2. Welcome to the ansible console.  
  3. Type help or ? to list commands.  
  4.         
  5. root@all (2)[f:5]$ cd web      
  6. root@web (2)[f:5]$ list                   
  7. 192.168.100.20  
  8. 192.168.100.30  
  9.  

4、Ansible模块

1)command模块

command模块在远程主机执行命令,不支持管道、重定向等shell的特性。常用的参数如下:

在所有主机上运行“ls ./”命令,运行前切换到/home目录下。操作如下: 

  1. [root@centos01 ~]# ansible web -m command -a "chdir=/ ls ./" 

2)shell模块

shell模块在远程主机执行命令,相当于调用远程主机的Shell进程,然后在该Shell下打开一个子Shell运行命令。和command模块的区别是它支持Shell特性:如管道、重定向等。

示例如下: 

  1. [root@centos01 ~]# ansible web -m shell -a "echo hello world "          
  2. 192.168.100.20 | SUCCESS | rc=0 >>  
  3. hello world  
  4. 192.168.100.30 | SUCCESS | rc=0 >>  
  5. hello world  
  6. [root@centos01 ~]# ansible web -m shell -a "echo hello world > /1.txt"     
  7. 192.168.100.20 | SUCCESS | rc=0 >>  
  8. 192.168.100.30 | SUCCESS | rc=0 >> 

3)copy模块

copy模块用于复制指定主机文件到远程主机的指定位置。常见的参数如下:

示例如下: 

  1. [root@centos01 ~]# ansible web -m copy -a "src=/etc/hosts 
  2. dest=/root/a1.hosts mode=777 owner=root group=root 
  3.   
  4. [root@centos01 ~]# ansible web -m shell -a "/usr/bin/mount  
  5. /dev/cdrom /mnt"     
  6.  [WARNING]: Consider using mount module rather than running mount  
  7. 192.168.100.20 | SUCCESS | rc=0 >>  
  8. mount: /dev/sr0 写保护,将以只读方式挂载  
  9. 192.168.100.30 | SUCCESS | rc=0 >>  
  10. mount: /dev/sr0 写保护,将以只读方式挂载  
  11. [root@centos01 ~]# ansible web -m yum -a "name=httpd  
  12. state=present"    
  13. [root@centos01 ~]# ansible web -m shell -a "rpm -qa | grep httpd"  
  14.       
  15.  [WARNING]: Consider using yum, dnf or zypper module rather than running rpm  
  16. 192.168.100.20 | SUCCESS | rc=0 >>  
  17. httpd-2.4.6-67.el7.centos.x86_64  
  18. httpd-tools-2.4.6-67.el7.centos.x86_64  
  19. 192.168.100.30 | SUCCESS | rc=0 >>  
  20. httpd-2.4.6-67.el7.centos.x86_64  
  21. httpd-tools-2.4.6-67.el7.centos.x86_64  
  22. [root@centos01 ~]# ansible web -m shell -a "systemctl start httpd"         
  23. [root@centos01 ~]# ansible web -m shell -a "netstat -anptu | grep httpd"       
  24. 192.168.100.20 | SUCCESS | rc=0 >>  
  25. tcp6       0      0 :::80                   :::*                    LISTEN      2072/httpd  
  26. 192.168.100.30 | SUCCESS | rc=0 >>  
  27. tcp6       0      0 :::80                   :::*                    LISTEN      3098/httpd 

管理端只是发送yum指令到被管理端,被管理端要存在可用的yum仓库才可以成功安装。

6)service模块

service模块为用来管理远程主机上的服务的模块。常见的参数如下:

示例如下: 

  1. [root@centos01 ~]# ansible web -m service -a "name=httpd  
  2. enabled=yes state=restarted 
  3.  

7)user模块

user模块主要用于管理远程主机上的用户账号。常见的参数如下:

name:必选参数,账号名称;

state=present|absent:创建账号或者删除账号,present表示创建,absent表示删除;

system=yes|no:是否为系统账户;

uid:用户UID;

group:用户的基本组

groups:用户的附加组;

shell:默认使用的shell;

home:用户的家目录;

mve_home=yes|no:

如果设置的家目录已经存在,是否将已存在的家目录进行移动;

pssword:用户的密码,建议使用加密后的字符串;

comment:

用户的注释信息;

remore=yes|no:

当state=absent时,是否要删除用户的家目录;

创建用户示例如下: 

  1. [root@centos01 ~]# ansible web -m user -a "name=user01  
  2. system=yes uid=502 group=root groups=root shell=/etc/nologin  
  3. home=/home/user01 password=pwd@123"  
  4.   
  5. [web1]  
  6. 192.168.100.20  
  7. [web2]  
  8. 192.168.100.30  
  9. [root@centos01 ~]# vim /etc/ansible/a.yml  
  10.                      
  11. ---  
  12. - hosts: web1                     
  13.   remote_user: root                      
  14.   tasks:                  
  15.         - name: adduser                                 
  16.           user: name=user1 state=present   
  17.           tags:                  
  18.           - aaa                   
  19.         - name: addgroup             
  20.           group: name=root system=yes   
  21.           tags:                 
  22.           - bbb                 
  23. - hosts: web2                 
  24.   remote_user: root          
  25.   tasks:                       
  26.         - name: copy file to web              
  27.           copy: src=/etc/passwd dest=/home          
  28.           tags:                          
  29.           - ccc                       
  30. ... 

所有的“-”和“:”后面均有空格,而且注意缩进和对齐,如下图所示:

playbook的核心元素包含:

hosts:任务的目标主机,多个主机用冒号分隔,一般调用/etc/ansible/hosts中的分组信息;

remote_user:远程主机上,运行此任务的默认身份为root;

tasks:任务,即定义的具体任务,由模块定义的操作列表;

handlers:触发器,类似tasks,只是在特定的条件下才会触发的任务。

某任务的状态在运行后为changed时,可通过“notify”通知给相应的handlers进行触发执行;

roles:角色,将hosts剥离出去,由tasks、handlers等所组成的一种特定的结构集合;

playbook文件定义的任务需要通过ansible-playbook命令进行调用并执行。ansible-playbook命令用法如下:

  1. ansible-playbook [option] /PATH/TO/PLAYBOOK.yaml 

其中,[option]部分的功能包括:

执行playbook的示例如下: 

  1. [root@centos01 ~]# ansible-playbook --syntax-check /etc/ansible/a.yml      
  2. playbook: /etc/ansible/a.yml       
  3. [root@centos01 ~]# ansible-playbook -C /etc/ansible/a.yml  
  4.            
  5.     .................  
  6. 192.168.100.20       : ok=3    changed=1    unreachable=0    failed=0 
  7. 192.168.100.30       : ok=2    changed=1    unreachable=0    failed=0  
  8.   
  9. [root@centos01 ~]# ansible-playbook --list-hosts /etc/ansible/a.yml  
  10.   
  11. [root@centos01 ~]# ansible-playbook --list-tasks /etc/ansible/a.yml  
  12.   
  13. [root@centos01 ~]# ansible-playbook --list-tags /etc/ansible/a.yml             
  14. [root@centos01 ~]# ansible-playbook /etc/ansible/a.yml                  
  15. [root@centos01 ~]# ssh 192.168.100.20 tail -1 /etc/passwd   
  16. user1:x:1001:1001::/home/user1:/bin/bash  
  17. [root@centos01 ~]# ssh 192.168.100.30 ls -ld /home/passwd  
  18. -rw-r--r--. 1 root root 2342 7月  23 16:06 /home/passwd  
  19.  

通常情况下先执行 ansible-playbook -C /PATH/TO/PLAYBOOK.yaml 命令进行测试,测试没问题后再执行 ansible-playbook /PATH/TO/PLAYBOOK.yml 命令。

2、触发器

需要触发才能执行的任务,当之前定义在tasks中的任务执行成功后,若希望在此基础上触发其他任务,这时就需要定义handlers。例如,当通过ansible的模块对目标主机的配置文件进行修改之后,如果任务执行成功,可以触发一个触发器,在触发器中定义目标主机的服务重启操作,以使配置文件生效。handlers触发器具有以下特点:

handlers触发器的使用示例如下: 

  1. [root@centos01 ~]# ssh 192.168.100.20 netstat -anpt | grep 80                    
  2. tcp6       0      0 :::80         :::*          LISTEN      94858/httpd  
  3.   
  4. [root@centos01 ~]# vim /etc/ansible/httpd.yml  
  5.               
  6. ---  
  7. - hosts: web1  
  8.   remote_user: root  
  9.   tasks:  
  10.         - name: change port  
  11.           command: sed -i 's/Listen\ 80/Listen\ 8080/g' /etc/httpd/conf/httpd.conf  
  12.           notify:                               
  13.                 - restart httpd server      
  14.   handlers:                                        
  15.         - name: restart httpd server    
  16.           service: name=httpd state=restarted  
  17. ...  
  18.   
  19. [root@centos01 ~]# ansible-playbook -C /etc/ansible/httpd.yml            
  20. [root@centos01 ~]# ansible-playbook  /etc/ansible/httpd.yml                 
  21. [root@centos01 ~]# ssh 192.168.100.20 netstat -anpt | grep 8080          
  22. tcp6       0      0 :::8080        :::*         LISTEN      103594/httpd 

3、角色

将多种不同的tasks的文件集中存储在某个目录下,则该目录就是角色。角色一般存放在 /etc/ansible/roles/ 目录,可通过ansible的配置文件来调整默认的角色目录,/etc/ansible/roles/ 目录下有很多子目录,其中每一个子目录对应一个角色,每个角色也有自己的目录结构,如下图所示:

/etc/ansible/roles/为角色集合,该目录下有自定义的各个子目录:

每个角色的定义,以特定的层级目录结构进行组织。以mariadb(mysql角色)为例:

上述目录中,tasks、handlers、vars、meta、default至少应该包含一个main.yml文件,该目录下也可以有其他.yml文件,但是需要在main.yml文件中用include指令将其他.yml文件包含进来。

有了角色后,可以直接在yaml文件(playbook配置文件)中调用角色,示例如下: 

  1. - hosts: web  
  2.   remote_user: root  
  3.   roles:  
  4.   - mysql          
  5.   - httpd              

可以只调用一个角色,也可以调用多个角色,当定义了角色后,用ansible-playbook PALYBOOK文件执行即可。

此时ansible会到角色集合的目录(/etc/ansible/roles)去找mysql和httpd目录,然后依次运行mysql和httpd目录下的所有代码。

下面来个安装及配置mariadb数据库的实例

需求分析:

 

来源:运维派内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯