Linux enterprise operation and maintenance articles --ansible configuration and usage

A. Installation ansible

server1:
1. Installation ansible

[root@server1 ~]# 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
sshpass-1.06-1.el7.x86_64.rpm
[root@server1 ]# yum install -y *rpm

Here Insert Picture Description
2. Add the implementation of user profiles

[root@server1 ~]# useradd devops
[root@server1 ~]# id devops
uid=1000(devops) gid=1000(devops) groups=1000(devops)
[root@server1 ~]# passwd devops
Changing password for user devops.
New password: 
BAD PASSWORD: The password is shorter than 8 characters
Retype new password: 
passwd: all authentication tokens updated successfully.

3. server2: the establishment of a user on the same, id have the same

[root@server2 ~]# useradd devops
[root@server2 ~]# id devops
uid=1000(devops) gid=1000(devops) groups=1000(devops)
[root@server2 ~]# passwd devops
Changing password for user devops.
New password: 
BAD PASSWORD: The password is shorter than 8 characters
Retype new password: 
passwd: all authentication tokens updated successfully.

server3: the establishment of a user on the same, id have the same

[root@server3 ~]# useradd devops
[root@server3 ~]# id devops
uid=1000(devops) gid=1000(devops) groups=1000(devops)
[root@server3 ~]# passwd devops
Changing password for user devops.
New password: 
BAD PASSWORD: The password is shorter than 8 characters
Retype new password: 
passwd: all authentication tokens updated successfully.

4. server1: In devops the user's home directory, create a cfg file:

[root@server1 ansible]# cd
[root@server1 ~]# su - devops
[devops@server1 ~]$ mkdir ansible
[devops@server1 ~]$ cd ansible/
[devops@server1 ansible]$ vim ansible.cfg
[defaults]

inventory = inventory #读取当前文件inventory

Here Insert Picture Description
5. 编辑文件inventory
[devops@server1 ansible]$ ls
ansible.cfg
[devops@server1 ansible]$ vim inventory
[test]
server2

[db]
server3

[webservers:children]
test
db

Here Insert Picture Description
6. Free-tight

[devops@server1 ansible]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/devops/.ssh/id_rsa): 
Created directory '/home/devops/.ssh'.
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:
1d:f1:83:9f:71:17:67:71:36:ef:bf:25:6d:6b:19:dc devops@server1
The key's randomart image is:
+--[ RSA 2048]----+
|          .    +*|
|           +   o*|
|          o + . o|
|         . o = o |
|        S . o . o|
|               +E|
|              . B|
|               =+|
|              .o |
+-----------------+

[devops@server1 ansible]$ ssh-copy-id server2
[devops@server1 ansible]$ ssh-copy-id server3

Here Insert Picture Description
7. Check the available host

[devops@server1 ansible]$ ansible all --list-hosts
  hosts (2):
    server3
    server2

8. Test ping module

 [devops@server1 ansible]$ ansible all -m ping #-m 调用模块
    server2 | SUCCESS => {
        "changed": false, 
        "ping": "pong"
    }
    server3 | SUCCESS => {
        "changed": false, 
        "ping": "pong"
    }

Here Insert Picture Description

Two .ansible command

-a Module Parameters
-f The number of threads, concurrent
-i Specifies the inventory file
-m shell Execute a shell script
-a Specifies the command

1.-a

[devops@server1 ansible]$ ansible test -a 'df -h'

Here Insert Picture Description
2.-m Module

[devops@server1 ansible]$ ansible test -m copy -a 'src=/etc/passwd dest=/tmp/passwd'
[devops@server1 ansible]$ ansible test -a 'ls /tmp/passwd'

Here Insert Picture Description
3. View and modify permissions

[devops@server1 ansible]$ ansible test -a 'ls -l /tmp/passwd'
[devops@server1 ansible]$ ansible test -m file -a 'dest=/tmp/passwd mode=600'

Here Insert Picture Description

[devops@server1 ansible]$ ansible test -a 'ls -l /tmp/passwd'

Here Insert Picture Description
server2:

[root@server2 ~]# vim /etc/sudoers #强制保存退出
devops      ALL=(ALL)       NOPASSWD: ALL

Here Insert Picture Description
server3 in:

[root@server3 ~]# vim /etc/sudoers
devops      ALL=(ALL)       NOPASSWD: ALL

Here Insert Picture Description

4. Set the default root performed

[devops@server1 ansible]$ vim ansible.cfg 
[defaults]

inventory       = inventory

[privilege_escalation] #写到ansible.cfg中下载就不用加-b
become=True
become_method=sudo
become_user=root
become_ask_pass=False

Here Insert Picture Description

The help documentation

[devops@server1 ansible]$ ansible-doc yum

Here Insert Picture Description
6. Download httpd

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

Here Insert Picture Description

[devops@server1 ansible]$ ansible test -a 'rpm -q httpd'

Here Insert Picture Description

[devops@server1 ansible]$ ansible webservers -m yum -a 'name=httpd state=present' #不加-b就可以下载

Here Insert Picture Description

7. Uninstall from the test in httpd

[devops@server1 ansible]$ ansible test -m yum -a 'name=httpd state=absent'

Here Insert Picture Description
Again unloading will tell you not to install httpd

[devops@server1 ansible]$ ansible test -m yum -a 'name=httpd state=absent'

Here Insert Picture Description8. Turn the db server3 of service httpd

[devops@server1 ansible]$ ansible db -m service -a 'name=httpd state=started'

Here Insert Picture Description
9. Access Server3 (all hosts in this case firewall is turned off)

[devops@server1 ansible]$ curl server3

Here Insert Picture Description10. Edit the default publishing pages

[devops@server1 ansible]$ ansible db -m copy -a 'content="www.redhat.org\n" dest=/var/www/html/index.html' #记得加换行符
[devops@server1 ansible]$ curl server3

Here Insert Picture Description
11. Open firewall

[devops@server1 ansible]$ ansible db -m service -a 'name=firewalld state=started'

Here Insert Picture Description
12. Set the firewall boot from Kai

[devops@server1 ansible]$ ansible db -m service -a 'name=firewalld state=started enabled=true'

Here Insert Picture Description
13. The firewall is turned on but it is not connected to the write strategy

[devops@server1 ansible]$ curl server3
curl: (7) Failed connect to server3:80; No route to host

14. The write strategy

[devops@server1 ansible]$ ansible db -m firewalld -a 'service=http state=enabled permanent=yes immediate=yes'
server3 | CHANGED => {
    "changed": true, 
    "msg": "Permanent and Non-Permanent(immediate) operation, Changed service http to enabled"
}

Here Insert Picture Description

[devops@server1 ansible]$ curl server3
www.redhat.org

Here Insert Picture Description

15. yml modify the file type of the tab ansible and spacebar

[devops@server1 ansible]$ cd
[devops@server1 ~]$ vim .vimrc
autocmd filetype yaml setlocal ai ts=2 sw=2 et

Here Insert Picture Description
16. The preparation of playbook.yml file

[devops@server1 ~]$ cd ansible/
[devops@server1 ansible]$ vim playbook.yml
---
# deploy apache
- hosts: webservers
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state: latest
        
    - name: start httpd
      service:
        name: httpd
        state: started

Here Insert Picture Description
17. syntax check

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

playbook: playbook.yml

18. Add the default publishing page

[devops@server1 ansible]$ vim playbook.yml
---
# deploy apache
- hosts: webservers
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state: latest

    - name: create index.html
      copy:
        content: "www.redhat.org\n"
        dest: /var/www/html/index.html

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

Here Insert Picture Description

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

playbook: playbook.yml

Prior to the test in the httpd re-download, configure, and in the same db, opening services for the preparation default page, open the firewall, configure policies

[devops@server1 ansible]$ ansible test -m yum -a 'name=httpd state=present'
[devops@server1 ansible]$ ansible test -m copy -a 'content="www.redhat.org\n" dest=/var/www/html/index.html' 
[devops@server1 ansible]$ ansible test -m service -a 'name=httpd state=started'
[devops@server1 ansible]$ ansible test -m firewalld -a 'service=http state=enabled permanent=yes immediate=yes'

19. Test

[devops@server1 ansible]$ curl server2
www.redhat.org
[devops@server1 ansible]$ curl server3
www.redhat.org

19. Check setup module

[devops@server1 ansible]$ ansible test -m setup

Here Insert Picture Description20. Filter View Host

[devops@server1 ansible]$ ansible test -m setup | grep hostname
        "ansible_hostname": "server2", 
[devops@server1 ansible]$ ansible test -m setup | grep fqdn
        "ansible_fqdn": "server2", 

Here Insert Picture Description
21. Configuration httpd

[devops@server1 ansible]$ vim playbook.yml
---
# deploy apache
- hosts: webservers
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state: latest

    - name: create index.html
      copy:
        content: "www.redhat.org\n"
        dest: /var/www/html/index.html

    - name: configure httpd
      copy:
        src: files/httpd.conf
        dest: /etc/httpd/conf/httpd.conf

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

Here Insert Picture Description

[devops@server1 ansible]$ mkdir files 
[devops@server1 ansible]$ cd files/
[devops@server1 files]$ ls
[devops@server1 files]$ scp server3:/etc/httpd/conf/httpd.conf .
httpd.conf    100%   11KB  11.5KB/s   00:00  
[devops@server1 files]$ cd -
/home/devops/ansible
[devops@server1 ansible]$ ansible-playbook playbook.yml --syntax-check

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

Here Insert Picture Description

[devops@server1 ansible]$ vim playbook.yml 
---
# deploy apache
- hosts: webservers
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state: latest

    - name: create index.html
      copy:
        content: "www.redhat.org\n"
        dest: /var/www/html/index.html

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


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

Here Insert Picture Description

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

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

Here Insert Picture Description

 [devops@server1 ansible]$ cd files/
 [devops@server1 files]$ ls
 httpd.conf
 [devops@server1 files]$ vim httpd.conf #修改端口

Here Insert Picture Description

[devops@server1 files]$ md5sum httpd.conf 
04e9239e7bd5d5b9b85864226d60eee5  httpd.conf

3 and Comparative server2

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

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

[devops@server1 files]$ cd -
/home/devops/ansible
[devops@server1 ansible]$ ansible-playbook playbook.yml

Here Insert Picture Description
View server2 and server3 discovery port has not changed, because there is no service httpd restart
[root @ server2 ~] # netstat -antlp
Here Insert Picture Description
[root @ server3 ~] # netstat -antlp
Here Insert Picture Description
22. The set boot from Kai

[devops@server1 ansible]$ vim playbook.yml 
 ---
# deploy apache
- hosts: webservers
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state: latest

    - name: create index.html
      copy:
        content: "www.redhat.org\n"
        dest: /var/www/html/index.html

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


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

Here Insert Picture Description

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

Here Insert Picture Description23.server2,3 see whether the boot from Kai

[root@server2 ~]# systemctl is-enabled httpd
enabled
[root@server3 ~]# systemctl is-enabled httpd
enabled

24. Modify Restart

[devops@server1 ansible]$ vim playbook.yml 
# deploy apache
- hosts: webservers
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state: latest

    - name: create index.html
      copy:
        content: "www.redhat.org\n"
        dest: /var/www/html/index.html

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


    - name: start httpd
      service:
        name: httpd
        state: started
        enabled: true
        
  handlers:
    - name: restart httpd
      service:
        name: httpd
        state: restarted

Here Insert Picture Description

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

playbook: playbook.yml
[devops@server1 ansible]$ ansible-playbook playbook.yml #看见改变说明成功

Here Insert Picture Description
Server2 and 3 in view the port has changed
[root @ server2 ~] # netstat -antlp

[root@server3 ~]# netstat -antlp
Here Insert Picture Description
Here Insert Picture Description

III. Use jinja statement to set the variable

1. jinja statement to set the variable
first httpd port changes back 80
Here Insert Picture Description

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

At this port has been modified successfully

[root@server2 ~]# netstat -antlp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      637/sshd            
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      774/master          
tcp        0      0 172.25.70.2:22          172.25.70.250:54518     ESTABLISHED 1047/sshd: root@pts 
tcp6       0      0 :::80                   :::*                    LISTEN      2239/httpd          
tcp6       0      0 :::22                   :::*                    LISTEN      637/sshd            
tcp6       0      0 ::1:25                  :::*         
[devops@server1 ansible]$ vim playbook.yml
---
# deploy apache
- hosts: webservers
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state: latest

    - name: create index.html
      copy:
        content: "{{ ansible_facts['hostname'] }}\n"
        dest: /var/www/html/index.html

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

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

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

Here Insert Picture Description

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

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

Here Insert Picture Description

[devops@server1 ansible]$ curl server2
server2
[devops@server1 ansible]$ curl server3
server3

2. Add the host ip

[devops@server1 ansible]$ vim playbook.yml
---
# deploy apache
- hosts: webservers
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state: latest

    - name: create index.html
      copy:
        content: "{{ ansible_facts['hostname'] }} {{ ansible_facts['default_ipv4']['address'] }}\n"
        dest: /var/www/html/index.html

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

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

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

Here Insert Picture Description

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

Here Insert Picture Description

[devops@server1 ansible]$ curl server2
server2 172.25.70.2
[devops@server1 ansible]$ curl server3
server3 172.25.70.3

Here Insert Picture Description
3. Add vars

[devops@server1 ansible]$ vim playbook.yml
---
# deploy apache
- hosts: webservers
  vars:
    http_port: 8080
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state: latest

    - name: create index.html
      copy:
        content: "{{ ansible_facts['hostname'] }} {{ ansible_facts['default_ipv4']['address'] }}\n"
        dest: /var/www/html/index.html

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

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

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

Here Insert Picture Description

[devops@server1 ansible]$ vim httpd.conf.j2 
Listen {{ http_port }}

[devops@server1 ansible]$ ansible-playbook playbook.yml -t one #根据tags名执行某部分
PLAY [webservers] **************************************************************

TASK [Gathering Facts] *********************************************************
ok: [server2]
ok: [server3]

PLAY RECAP *********************************************************************
server2                    : ok=1    changed=0    unreachable=0    failed=0   
server3                    : ok=1    changed=0    unreachable=0    failed=0 

Here Insert Picture Description

[devops@server1 ansible]$ ansible-playbook playbook.yml
[devops@server1 ansible]$ ansible webservers -a 'netstat -anltp'

Here Insert Picture Description4. View Host Information

[devops@server1 ansible]$ ansible test -m setup

Here Insert Picture Description

[devops@server1 ansible]$ vim hostinfo.yml
---
- hosts: webservers
  tasks:
    - name: create infofile
      template:
        src: templates/info.j2
        dest: /mnt/info

Here Insert Picture Description

[devops@server1 ansible]$ mkdir templates
[devops@server1 ansible]$ cd templates/
[devops@server1 templates]$ vim info.j2
主机名:{{ ansible_facts['hostname'] }}
主机IP地址:{{ ansible_facts['default_ipv4']['address'] }}
跟分区大小:{{ ansible_facts['devices']['dm-0']['size'] }}
系统内核:{{ ansible_facts['kernel'] }}

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

Here Insert Picture Description

[devops@server1 ansible]$ ansible all -a 'ls -l /mnt/info'
server3 | CHANGED | rc=0 >>
-rw-r--r-- 1 root root 112 Jun 19 04:31 /mnt/info

server2 | CHANGED | rc=0 >>
-rw-r--r-- 1 root root 112 Jun 19 04:31 /mnt/info

[devops@server1 ansible]$ ansible all -a 'cat /mnt/info'
server3 | CHANGED | rc=0 >>
主机名:server3
主机IP地址:172.25.70.3
跟分区大小:8.00 GB
系统内核:3.10.0-514.el7.x86_64

server2 | CHANGED | rc=0 >>
主机名:server2
主机IP地址:172.25.70.2
跟分区大小:8.00 GB
系统内核:3.10.0-514.el7.x86_64

Here Insert Picture Description

four.

1. Download different hosts different services

[devops@server1 ansible]$ vim install.yml
---
- hosts: all
  tasks:
    - name: install httpd
      yum:
        name: httpd
        state: present
      when: ansible_facts['hostname'] == 'server2'

    - name: install mariadb
      yum:
        name: mariadb
        state: present
      when: ansible_facts['hostname'] == 'server3'

Here Insert Picture Description

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

Here Insert Picture Description
2. Download multiple services using a loop

[devops@server1 ansible]$ vim install.yml 
    ---
- hosts: all
  tasks:
    - name: install httpd
      yum:
        name: "{{ item }}"
        state: present
      when: ansible_facts['hostname'] == 'server2'
      loop: 
        - httpd
        - mariadb
        - php
        - php-mysql

    - name: install mariadb
      yum:
        name: mariadb
        state: present
      when: ansible_facts['hostname'] == 'server3'

Here Insert Picture Description

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

Here Insert Picture Description

3. Set to multiple hosts hosts file

[devops@server1 ansible]$ vim install.yml 
---
- hosts: all
  tasks:
    - name: create infofile
      template:
        src: templates/info.j2
        dest: /mnt/info

    - name: create hosts
      template:                         
        src: templates/hosts.j2
        dest: /etc/hosts                                  owner: root                                       group: root
        mode: 0644  

Here Insert Picture Description

[devops@server1 ansible]$ cd templates/
[devops@server1 templates]$ vim host.j2
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
{% for host in groups['webservers'] %}
{{ hostvars[host]['ansible_facts']['eth0']['ipv4']['address'] }}  {{ hostvars[host]['ansible_facts']['hostname'] }}
{% endfor %}

Here Insert Picture Description
Add users in bulk

[devops@server1 templates]$ cd ..
[devops@server1 ansible]$ vim adduser.yml 
---
- hosts: all
  tasks:
    - name: create users
      user:
        name: "{{ item }}"
        state: present
        password: redhat
      loop:                                    
        - user1
        - user2
        - user3
        - user4

Here Insert Picture Description
state in the present and installed, absent and removed by a known source is the same
source:

if state in ['installed', 'present']:
    if disable_gpg_check:
        yum_basecmd.append('--nogpgcheck')
    res = install(module, pkgs, repoq, yum_basecmd, conf_file, en_repos, dis_repos)
elif state in ['removed', 'absent']:
    res = remove(module, pkgs, repoq, yum_basecmd, conf_file, en_repos, dis_repos)
elif state == 'latest':
    if disable_gpg_check:
        yum_basecmd.append('--nogpgcheck')
    res = latest(module, pkgs, repoq, yum_basecmd, conf_file, en_repos, dis_repos)
else:
    # should be caught by AnsibleModule argument_spec
    module.fail_json(msg="we should never get here unless this all"
            " failed", changed=False, results='', errors='unexpected state')
return res

[devops @ server1 ansible] $ ansible -playbook adduser.yml
Here Insert Picture Descriptionencrypted user information

[devops@server1 ansible]$ mkdir vars
[devops@server1 ansible]$ cd vars/
[devops@server1 vars]$ vim userlist.yml
---
userlist:
  - user: user1
    pass: westos
  - user: user2
    pass: redhat

Here Insert Picture Description

[devops@server1 vars]$ ansible-vault encrypt userlist.yml
New Vault password: 
Confirm New Vault password: 
Encryption successful
[devops@server1 vars]$ cat userlist.yml

Here Insert Picture Description

[devops@server1 vars]$ ansible-vault view userlist.yml 
Vault password: 
---
userlist:
  - user: user1
    pass: westos
  - user: user2
    pass: redhat

Here Insert Picture Description
delete users

[devops@server1 vars]$ cd ..
[devops@server1 ansible]$ vim adduser.yml 
---
- hosts: all
  vars_files:
    - vars/userlist.yml
  tasks:
    - name: create users
      user:
        name: "{{ item }}"
        state: absent
        password: westos
      loop: "{{ userlist }}"

Here Insert Picture Description

[devops@server1 ansible]$ ansible-playbook adduser.yml --ask-vault-pass

Here Insert Picture Description

Guess you like

Origin blog.csdn.net/weixin_44321116/article/details/92686085