Ansible安装、ad-hoc管理、playbook

一、基本概念

1.什么是ansible?

ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。
  ansible是基于 paramiko 开发的,并且基于模块化工作,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。ansible不需要在远程主机上安装client/agents,因为它们是基于ssh来和远
程主机通讯的。ansible目前已经已经被红帽官方收购,是自动化运维工具中大家认可度最高的,并且上手容易,学习简单。是每位运维工程师必须掌握的技能之一。

2.ansible 任务执行
ansible 任务执行模式

Ansible 系统由控制主机对被管节点的操作方式可分为两类,即adhoc和playbook:

  • ad-hoc模式(点对点模式)
    使用单个模块,支持批量执行单条命令。ad-hoc 命令是一种可以快速输入的命令,而且不需要保存起来的命令。就相当于bash中的一句话shell。

  • playbook模式(剧本模式)
    是Ansible主要管理方式,也是Ansible功能强大的关键所在。playbook通过多个task集合完成一类功能,如Web服务的安装部署、数据库服务器的批量备份等。可以简单地把playbook理解为通过组合多条ad-hoc操作的配置文件。

二、ansible安装

1.获得资源包安装ansible(此处会有依赖性)

[root@server1 ~]# ls
ansible
[root@server1 ~]# cd ansible/
[root@server1 ansible]# ls
ansible-2.7.8-1.el7.noarch.rpm		#单装这个会产生依赖性缺失
ansible-tower-setup-bundle-3.4.2-1.el7.tar.gz
libtomcrypt-1.17-25.el7.x86_64.rpm
libtommath-0.42.0-5.el7.x86_64.rpm
python2-crypto-2.6.1-13.el7.x86_64.rpm
python2-jmespath-0.9.0-1.el7.noarch.rpm
python-httplib2-0.9.2-0.1.el7.noarch.rpm
python-keyczar-0.71c-2.el7.noarch.rpm
python-paramiko-2.1.1-0.9.el7.noarch.rpm
roles
sshpass-1.06-1.el7.x86_64.rpm
[root@server1 ansible]# yum install -y *.rpm		#所以此处都安装(其他的是依赖性包)

2.查看版本

[root@server1 ansible]# ansible --version

在这里插入图片描述
3.建立用户,(ansible不推荐用root进行管理)

[root@server1 ansible]# useradd devops
[root@server1 ansible]# cd /etc/ansible/		#ansible的配置文件目录
[root@server1 ansible]# ls
ansible.cfg  hosts  roles

在server2、server3、server4上也建立用户,修改密码

[root@server2 ~]# useradd devops
[root@server2 ~]# echo westos | passwd --stdin devops
[root@server3 ~]# useradd devops
[root@server3 ~]# echo westos | passwd --stdin devops
[root@server4 ~]# useradd devops
[root@server4 ~]# echo westos | passwd --stdin devops

4.切换到普通用户,建立新的ansible目录(方便以后修改配置,针对当前用户的)

root@server1 ansible]# su - devops
[devops@server1 ~]$ ls
[devops@server1 ~]$ mkdir ansible

在这里插入图片描述
5.编写当前用户的inventory,编写配置文件(读取顺序是先读取当前的配置文件)

[devops@server1 ansible]$ vim inventory
[dev]
server2

[test]
server3

[prod]
server4

[webservers:children]
dev
test
prod
[devops@server1 ansible]$ vim ansible.cfg
[defaults]
inventory=inventory

测试:

[devops@server1 ansible]$ ansible-inventory --list all#查看组的定义(uground表示默认组)

在这里插入图片描述
[devops@server1 ansible]$ ansible dev --list-host #显示定义组下的主机
在这里插入图片描述

5.测试主机之间网络的连通性

[devops@server1 ansible]$ cat ansible.cfg 
[defaults]
inventory=inventory
host_key_checking=False		#不要key
[devops@server1 ansible]$ ansible all -m ping

在这里插入图片描述

[devops@server1 ansible]$ ansible all -m ping -k		#此处输入的密码是普通用户的密码

在这里插入图片描述
此时在进行,则成功
[devops@server1 ansible]$ ansible all -m ping
在这里插入图片描述
测试:此时可在中控机上查看各目标主机的信息
[devops@server1 ansible]$ ansible all -a “hostname”
在这里插入图片描述
[devops@server1 ansible]$ ansible all -m shell -a “df -h”
在这里插入图片描述

6.配置免密连接

[devops@server1 ansible]$ ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/home/devops/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/devops/.ssh/id_rsa.
Your public key has been saved in /home/devops/.ssh/id_rsa.pub.
The key fingerprint is:
cc:c5:66:64:22:60:b2:97:eb:f6:ec:e5:19:be:e2:88 devops@server1
The key's randomart image is:
+--[ RSA 2048]----+
|  . o.. . o      |
|   + . . =       |
|  . o     =      |
|   . . o +       |
|    .   S        |
|   .             |
|    o   o        |
|   o +.+ o       |
|  E .o=.=.       |
+-----------------+
[devops@server1 ~]$ l.
.  ..  .ansible  .bash_logout  .bash_profile  .bashrc  .ssh  .viminfo
[devops@server1 ~]$ ssh-copy-id server2
[devops@server1 ~]$ ssh-copy-id server3
[devops@server1 ~]$ ssh-copy-id server4

三、ad-hoc管理

1.安装httpd服务,并测试

帮助文档查看:

[devops@server1 ansible]$ ansible-doc -l		#查看模块
[devops@server1 ansible]$ ansible-doc -l | grep ssh
[devops@server1 ansible]$ ansible-doc yum		#查看服务使用

在这里插入图片描述
安装httpd服务,但是会出现报错

[devops@server1 ansible]$ ansible dev -m yum -a "name=httpd state=present" -b

在这里插入图片描述

注意:报错是因为目标主机的普通用户需要密码切换到超户,进行设置
[root@server2 ~]# vim /etc/sudoers
在这里插入图片描述
[root@server3 ~]# vim /etc/sudoers
[root@server4 ~]# vim /etc/sudoers

再次进行安装
[devops@server1 ansible]$ ansible dev -m yum -a “name=httpd state=present” -b
在这里插入图片描述

在server2上查看是否已经安装
[root@server2 ~]# rpm -q httpd
httpd-2.4.6-45.el7.x86_64

2.设置开机自启:

[devops@server1 ansible]$ ansible dev -m service -a "name=httpd state=started enabled=yes" -b

在这里插入图片描述

在server2上查看
[root@server2 ~]# netstat -antlp
在这里插入图片描述

3.开启防火墙,开机自启

[devops@server1 ansible]$ ansible dev -m service -a "name=firewalld state=started enabled=yes" -b

在这里插入图片描述

在server2上查看
[root@server2 ~]# firewall-cmd --list-services
dhcpv6-client ssh
[root@server2 ~]# systemctl status firewalld
在这里插入图片描述

4.编写规则,将httpd写进防火墙策略

[devops@server1 ansible]$ ansible dev -m firewalld -a "service=http permanent=yes immediate=yes state=enabled" -b

在这里插入图片描述
在server2上查看
[root@server2 ~]# firewall-cmd --list-services
dhcpv6-client ssh http
[root@server2 ~]# iptables -L
在这里插入图片描述
[devops@server1 ansible]$ curl server2 #此时查看的是httpd默认发布页

在这里插入图片描述

5.写文件(写默认发布页)

[devops@server1 ansible]$ ansible dev -m copy -a 'content="<h1>www.westos.org</h1>\n" dest=/var/www/html/index.html' -b

在这里插入图片描述
测试:
[devops@server1 ansible]$ curl server2

在这里插入图片描述
不用加-b操作:

[devops@server1 ansible]$ vim /etc/ansible/ansible.cfg		#复制信息
[devops@server1 ansible]$ vim ansible.cfg
[defaults]
inventory=inventory
host_key_checking=False

[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False

[devops@server1 ansible]$ ansible dev -m copy -a 'content="<h1>www.westos.org</h1>\n" dest=/var/www/html/index.html'

在这里插入图片描述

四、playbook

playbook是严格按照缩进执行的,因此进行定义:

[devops@server1 ~]$ vim .vimrc
autocmd filetype yaml set ai ts=2 sw=2 et	##写table的缩进,yml类型的文件,一个tab键代表两个空格

1.编写通过playbook执行的内容

[devops@server1 ~]$ cd ansible/
[devops@server1 ansible]$ vim playbook.yml
---
- name: apache playbook
  hosts: test		##组,写在inventory 文件下,即针对哪些主机
  tasks:			##任务
    - name: install httpd	##任务简介
      yum:	
        name: httpd
        state: present

    - name: start httpd
      service:
        name: httpd
        state: started
        enabled: true

    - name: enabled firewalld
      service:
        name: firewalld
        state: started
        enabled: true

    - name: configure firewalld
      firewalld:
        service: http
        state: enabled
        permanent: true
        immediate: true

    - name: copy index.html
      copy:
        content: "<h1>www.westos.org</h1>\n"
        dest: /var/www/html/index.html

2.安装http服务

语法检测:

[devops@server1 ansible]$ ansible-playbook --syntax-check playbook.yml

playbook: playbook.yml		#表示语法正确

执行内容,用ansible进行推送

[devops@server1 ansible]$ ansible-playbook playbook.yml

在这里插入图片描述

测试:
[devops@server1 ansible]$ curl server3
在这里插入图片描述

  • 当对httpd配置文件进行修改时,需要重新加载服务

把httpd服务所需的配置文件复制

[devops@server1 ansible]$ mkdir files
[devops@server1 ansible]$ cd files/
[devops@server1 files]$ scp server2:/etc/httpd/conf/httpd.conf .
[devops@server1 files]$ vim httpd.conf		#将端口改为8000

添加任务

[devops@server1 ansible]$ vim playbook.yml
[devops@server1 ansible]$ vim playbook.yml
---
- name: apache playbook
  hosts:  test
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state:  present

    - name: configure httpd
      copy:
        src:  files/httpd.conf
        dest: /etc/httpd/conf/httpd.conf
        owner:  root
        group:  root
        mode: '0644'

    - name: start httpd
      service:
        name: httpd
        state:  started
        enabled:  true

    - name: enabled firewalld
      service:
        name: firewalld
        state:  started
        enabled:  true

    - name: configure firewalld
      firewalld:
        service: http
        state:  enabled
        permanent:  true
        immediate:  true

    - name: copy  index.html
      copy:
        content:  "<h1>www.westos.org</h1>\n"
        dest: /var/www/html/index.html

  handlers:			#触发器(没遇到notify的时候,触发器不会触发)
    - name: restart httpd
      service:
        name: httpd
        state:  restarted
[devops@server1 ansible]$ ansible-playbook --syntax-check playbook.yml 

playbook: playbook.yml
[devops@server1 ansible]$ ansible-playbook playbook.yml

在这里插入图片描述

注意:此时,改变一个地方;
[root@server3 ~]# vim /etc/httpd/conf/httpd.conf #配置文件更改了
在这里插入图片描述
[root@server3 ~]# netstat -antlp #但是此时仍然是80
在这里插入图片描述

添加动作,让触发器生效
[devops@server1 ansible]$ vim files/httpd.conf #改为80

[devops@server1 ansible]$ vim playbook.yml
---
- name: apache  playbook
  hosts:  test
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state:  present

    - name: configure httpd
      copy:
        src:  files/httpd.conf
        dest: /etc/httpd/conf/httpd.conf
        owner:  root
        group:  root
        mode: '0644'
      notify: restart httpd		#仅添加此行

    - name: start httpd
      service:
        name: httpd
        state:  started
        enabled:  true

    - name: enabled firewalld
      service:
        name:  firewalld
        state:  started
        enabled:  true

    - name: configure firewalld
      firewalld:
        service:  http
        state:  enabled
        permanent:  true
        immediate:  true

    - name: copy  index.html
      copy:
        content:  "<h1>www.westos.org</h1>\n"
        dest: /var/www/html/index.html

  handlers:
    - name: restart httpd
      service:
        name: httpd
        state:  restarted
[devops@server1 ansible]$ ansible-playbook --syntax-check playbook.yml 

playbook: playbook.yml
[devops@server1 ansible]$ ansible-playbook playbook.yml

在这里插入图片描述

注意:此时改变两次信息

[root@server3 ~]# vim /etc/httpd/conf/httpd.conf #配置文件和端口一致

注意:是基于md5码的信息机进行比对,进行推送
[devops@server1 ansible]$ md5sum files/httpd.conf
f5e7449c0f17bc856e86011cb5d152ba files/httpd.conf

[root@server3 ~]# md5sum /etc/httpd/conf/httpd.conf
f5e7449c0f17bc856e86011cb5d152ba /etc/httpd/conf/httpd.conf

此时是一致的,再次进行推送不会有所更改

  • 虚拟主机的配置

1.创建虚拟主机配置文档 #模板在/usr/share/doc/httpd-2.4.6(可以rpm -ql httpd)

[devops@server1 ansible]$ cd files/
[devops@server1 files]$ ls
httpd.conf
[devops@server1 files]$ vim vhosts.conf
<VirtualHost *:80>
DocumentRoot /var/www/html
ServerName server3
</VirtualHost>

<VirtualHost *:80>
        DocumentRoot /var/www/vhosts/example.com
        ServerName server3.example.com
        ErrorLog "/var/log/httpd/example.com-error_log"
        CustomLog "/var/log/httpd/example.com-access_log" common
</VirtualHost>

<Directory "/var/www/vhosts/example.com">
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
</Directory>

2.编写playbook

---
- name: apache  playbook
  hosts:  test
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state:  present

    - name: configure httpd
      copy:
        src:  files/httpd.conf
        dest: /etc/httpd/conf/httpd.conf
        owner:  root
        group:  root
        mode: '0644'
      notify: restart httpd

    - name: start httpd
      service:
        name: httpd
        state:  started
        enabled:  true

    - name: enabled firewalld
      service:
        name:  firewalld
        state:  started
        enabled:  true

    - name: configure firewalld
      firewalld:
        service:  http
        state:  enabled
        permanent:  true
        immediate:  true

    - name: copy  index.html
      copy:
        content:  "<h1>www.westos.org</h1>\n"
        dest: /var/www/html/index.html

    - name: create  /var/www/vhosts/example.com
      file:
        path: /var/www/vhosts/example.com
        state:  directory
        mode: '0755'

    - name: create  vhost.html
      copy:
        content:  "server3.example.com-vhosts\n"
        dest: /var/www/vhosts/example.com/vhost.html

    - name: copy  vhosts.conf
      copy:
        src:  files/vhosts.conf
        dest: /etc/httpd/conf.d/vhosts.conf
      notify: restart httpd

  handlers:
    - name: restart httpd
      service:
        name: httpd
        state:  restarted

[devops@server1 ansible]$ ansible-playbook --syntax-check playbook.yml

playbook: playbook.yml
[devops@server1 ansible]$ ansible-playbook playbook.yml
在这里插入图片描述

测试:
[root@server1 ansible]# vim /etc/hosts
172.25.254.3 server3 server3.example.com
在这里插入图片描述

  • playbook多个软件的安装

采用定义变量的方法:

1.在playbook文件里定义变量

[devops@server1 ansible]$ vim playbook.yml
---
- name: apache  playbook
  hosts:  test
  vars:
    web_package:  httpd
    web_service:  httpd
    firewalld_service:  firewalld
  tasks:
    - name: install httpd
      yum:
        name: "{{ web_package }}"
        state:  present

    - name: configure httpd
      copy:
        src:  files/httpd.conf
        dest: /etc/httpd/conf/httpd.conf
        owner:  root
        group:  root
        mode: '0644'
      notify: restart httpd

    - name: start httpd
      service:
        name: "{{ web_service }}"
        state:  started
        enabled:  true

    - name: enabled firewalld
      service:
        name:  "{{ firewalld_service }}"
        state:  started
        enabled:  true

    - name: configure firewalld
      firewalld:
        service:  http
        state:  enabled
        permanent:  true
        immediate:  true

    - name: copy  index.html
      copy:
        content:  "<h1>www.westos.org</h1>\n"
        dest: /var/www/html/index.html

    - name: create  /var/www/vhosts/example.com
      file:
        path: /var/www/vhosts/example.com
        state:  directory
        mode: '0755'

    - name: create  vhost.html
      copy:
        content:  "server3.example.com-vhosts\n"
        dest: /var/www/vhosts/example.com/vhost.html

    - name: copy  vhosts.conf
      copy:
        src:  files/vhosts.conf
        dest: /etc/httpd/conf.d/vhosts.conf
      notify: restart httpd

  handlers:
    - name: restart httpd
      service:
        name: "{{ web_service }}"
        state:  restarted

[devops@server1 ansible]$ ansible-playbook --syntax-check playbook.yml

playbook: playbook.yml
[devops@server1 ansible]$ ansible-playbook playbook.yml
在这里插入图片描述

注意:此时不会有更改,因为信息没变,只是采用了定义变量的方式

2.局外定义变量

[devops@server1 ansible]$ mkdir vars
[devops@server1 ansible]$ cd vars/
[devops@server1 vars]$ vim web.yml
---
web_package:  httpd
web_service:  httpd
firewalld_service:  firewalld
[devops@server1 ansible]$ vim playbook.yml
  1 ---
  2 - name: apache  playbook
  3   hosts:  test
  4   vars_files:
  5     - vars/web.yml
  6   tasks:
  7     - name: install httpd
  8       yum:
  9         name: "{{ web_package }}"
 10         state:  present

[devops@server1 ansible]$ ansible-playbook --syntax-check playbook.yml

playbook: playbook.yml
[devops@server1 ansible]$ ansible-playbook playbook.yml
在这里插入图片描述

注意:也不会改变

3.想要在现有基础上添加服务进火墙策略(方法一)
[root@server3 ~]# firewall-cmd --list-service
dhcpv6-client ssh http

[devops@server1 ansible]$ vim playbook.yml
  1 ---
  2 - name: apache  playbook
  3   hosts:  test
  4   vars:
  5     firewalld_svc:
  6       - http
  7       - https
  8   vars_files:
  9     - vars/web.yml
 10   tasks:
 11     - name: install httpd
 12       yum:
 13         name: "{{ web_package }}"
 14         state:  present

 37     - name: configure firewalld
 38       firewalld:
 39         service:  "{{ item }}"
 40         state:  enabled
 41         permanent:  true
 42         immediate:  true
 43		  loop: "{{ firewalld_svc }}"

[devops@server1 ansible]$ ansible-playbook --syntax-check playbook.yml

playbook: playbook.yml
[devops@server1 ansible]$ ansible-playbook playbook.yml
在这里插入图片描述

在server3查看
在这里插入图片描述

4.想要在现有基础上添加服务进火墙策略(方法二)

[devops@server1 ansible]$ vim playbook.yml
 1 ---
  2 - name: apache  playbook
  3   hosts:  test
  4   vars:
  5     firewalld_svc:
  6       - http
  7       - https
  8   vars_files:
  9     - vars/web.yml
 10   tasks:
 11     - name: install httpd
 12       yum:
 13         name: "{{ web_package }}"
 14         state:  present

 37     - name: configure firewalld
 38       firewalld:
 39         service:  "{{ item }}"
 40         state:  enabled
 41         permanent:  true
 42         immediate:  true
 43       loop:
 44         - http
 45         - https
 46         - ftp

[devops@server1 ansible]$ ansible-playbook --syntax-check playbook.yml

playbook: playbook.yml
[devops@server1 ansible]$ ansible-playbook playbook.yml
在这里插入图片描述

在server3上查看
在这里插入图片描述

  • 不同主机安装不同的服务
[devops@server1 ansible]$ vim playbook.yml
 37     - name: configure firewalld
 38       firewalld:
 39         service:  "{{ item }}"
 40         state:  enabled
 41         permanent:  true
 42         immediate:  true
 43       loop: "{{ firewalld_svc }}"
[devops@server1 vars]$ cp web.yml ftp.yml
[devops@server1 vars]$ vim ftp.yml
---
ftp_package:  vsftpd
ftp_service:  vsftpd
firewalld_service:  firewalld
[devops@server1 ansible]$ cp playbook.yml vsftp.yml
[devops@server1 ansible]$ vim vsftp.yml
---
- name: vsftp  playbook
  hosts:  prod
  vars:
    firewalld_svc:
      - ftp
  vars_files:
    - vars/ftp.yml
  tasks:
    - name: install vsftpd
      yum:
        name: "{{ ftp_package }}"
        state:  present

    - name: configure vsftpd
      copy:
        src:  files/vsftpd.conf
        dest: /etc/vsftpd/vsftpd.conf
        owner:  root
        group:  root
        mode: '0600'
      notify: restart vsftpd

    - name: start vsftpd
      service:
        name: "{{ ftp_service }}"
        state:  started
        enabled:  true

    - name: enabled firewalld
      service:
        name:  "{{ firewalld_service }}"
        state:  started
        enabled:  true

    - name: configure firewalld
      firewalld:
        service:  "{{ item }}"
        state:  enabled
        permanent:  true
        immediate:  true
      loop: "{{ firewalld_svc }}"

  handlers:
    - name: restart vsftpd
      service:
        name: "{{ ftp_service }}"
        state:  restarted
[devops@server1 files]$ ls
httpd.conf  vhosts.conf  vsftpd.conf
[devops@server1 files]$ ll 
total 24
-rw-r--r-- 1 devops devops 11753 May 10 22:57 httpd.conf
-rw-rw-r-- 1 devops devops   474 May 10 23:02 vhosts.conf
-rw-r--r-- 1 root   root    5030 May 11 00:03 vsftpd.conf
[root@server1 files]# chown devops.devops vsftpd.conf
[devops@server1 ansible]$ cp playbook.yml apache.yml
[devops@server1 ansible]$ vim playbook.yml
---
- import_playbook:  apache.yml
- import_playbook:  vsftp.yml
[devops@server1 ansible]$ rm -rf playbook.retry

[devops@server1 ansible]$ ansible-playbook --syntax-check vsftp.yml

playbook: vsftp.yml
[devops@server1 ansible]$ ansible-playbook --syntax-check apache.yml

playbook: apache.yml

[devops@server1 ansible]$ ansible-playbook playbook.yml
在这里插入图片描述

  • 让按照需求在对应的主机安装对应的服务
[devops@server1 ansible]$ vim test.yml
---
- name: test
  hosts:
    - prod
    - test
  tasks:
    - name: deploy  apache
      yum:
        name: httpd
        state:  latest
      when: ansible_hostname in groups['test']

    - name: deploy  vsftpd
      yum:
        name: vsftpd
        state:  present
      when: ansible_hostname in groups['prod']

[devops@server1 ansible]$ ansible-playbook --syntax-check test.yml

playbook: test.yml
[devops@server1 ansible]$ ansible-playbook test.yml
在这里插入图片描述

  • 用户的创建针创建不同用户(用户和密码对应)
[devops@server1 ansible]$ cd vars/
[devops@server1 vars]$ vim user.yml
---
userlist:
  - name: user1
    pass: westos
  - name: user2
    pass: redhat

[devops@server1 ansible]$ vim test.yml
---
- name: test
  hosts:
    - test
  vars_files:
    - vars/user.yml
  tasks:
    - name: create  user
      user:
        name: "{{ item.name }}"
        state:  present
        password: "{{ item.pass }}"
      loop: "{{ userlist }}"

[devops@server1 ansible]$ ansible-playbook --syntax-check test.yml

playbook: test.yml
[devops@server1 ansible]$ ansible-playbook test.yml
在这里插入图片描述

注意:此时有警告,是因为密码是明文的
在这里插入图片描述
在这里插入图片描述

采用加密的方式:

[devops@server1 ansible]$ vim test.yml
---
- name: test
  hosts:
    - test
  vars_files:
    - vars/user.yml
  tasks:
    - name: create  user
      user:
        name: "{{ item.name }}"
        state:  present
        password: "{{ item.pass | password_hash('sha512') }}"
      loop: "{{ userlist }}"

[devops@server1 ansible]$ ansible-playbook --syntax-check test.yml

playbook: test.yml
[devops@server1 ansible]$ ansible-playbook test.yml
在这里插入图片描述

测试:
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值