• ansible介绍、主机清单、临时命令的使用


    ansible介绍、主机清单、临时命令的用法

    Ansible介绍

    Ansible 基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。ansible是基于模块工作的,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。

    Ansible是一款自动化运维工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。

    Ansible是基于模块工作的,本身没有批量部署的能力。真正具有批量部署的是Ansible所运行的模块,Ansible只是提供一种框架。主要包括:
    (1) 连接插件connection plugins:负责和被监控端实现通信;
    (2) host inventory:指定操作的主机,是一个配置文件里面定义监控的主机;
    (3) 各种模块核心模块、command模块、自定义模块;
    (4) 借助于插件完成记录日志邮件等功能;
    (5) playbook:剧本执行多个任务时,非必需可以让节点一次性运行多个任务。

    img

    puppet简介

    前面已经介绍或一款自动化运维工具Ansible,ansible功能的实现依赖于模块,适用于小型的网络架构,而puppet功能实现是依赖于资源的,把需要实现的某些相同的功能定义为模块,相当于ansible的角色。

    saltstack简介

    saltstack是基于 Python 开发,采用 C/S 架构,使用SSL证书签方的方式进行认证管理,号称世界上最快的消息队列ZeroMQ使得SaltStack能快速在成千上万台机器上进行各种操作采用RSA Key方式确认身份传输采用AES加密,这使得它的安全性得到了保障。

    ansible安装

    Ansible 易于安装。 Ansible 软件只需要安装到要运行它的一个(或多个)控制节点上。由 Ansible管理的主机不需要安装 Ansible。

    对控制节点的要求:

    • 控制节点应是Linux或UNIX系统。不支持将Windows用作控制节点,但Windows系统可以是受管主机。
    • 控制节点需要安装Python3(版本3.5或以上)或Python2(版本2.7或以上)。

    如果操作系统是红帽8.0,Ansible 2.9可以自动使用 platform-python 软件包,该软件包支持使用Python的系统实用程序。你不需要从 AppStream安装python37或python27软件包。

    //先查找下载ansible的源,然后下载源。
    [root@localhost ~]# dnf list all | grep ansible
    ...
    centos-release-ansible-29.noarch                       1-2.el8                            
    [root@localhost ~]# dnf -y install centos-release-ansible-29
    ...
    [root@localhost ~]# ls /etc/yum.repos.d/
    CentOS-SIG-ansible-29.repo
    
    
    //查看python包
    [root@localhost ~]# yum list installed platform-python
    
    //安装ansoble,和查看版本
    [root@localhost ~]# yum -y install ansible
    [root@localhost ~]# ansible --version
    ansible 2.9.27
      config file = /etc/ansible/ansible.cfg		//显示正在使用的配置文件。
      configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
      ansible python module location = /usr/lib/python3.6/site-packages/ansible
      executable location = /usr/bin/ansible
      python version = 3.6.8 (default, Jan 19 2022, 23:28:49) [GCC 8.5.0 20210514 (Red Hat 8.5.0-7)]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    主机清单简介

    清单定义Ansible将要管理的一批主机。这些主机也可以分配到组中,以进行集中管理。组可以包含子组,主机也可以是多个组的成员。清单还可以设置应用到它所定义的主机和组的变量。

    可以通过两种方式定义主机清单。静态主机清单可以通过文本文件定义。动态主机清单可以根据需要使用外部信息提供程序通过脚本或其他程序来生成。

    //默认清单的位置,hosts就是刚安装后默认清单的位置
    [root@localhost ansible]# ls
    ansible.cfg  hosts  roles
    [root@localhost ansible]# pwd
    /etc/ansible
    
    //定义清单的配置文件
    [root@localhost ansible]# vim ansible.cfg 
    #inventory      = /etc/ansible/hosts   //主机清单默认就是在和主配置文件的同级目录,并且他是注释状态
    
    //查看主机清单的默认配置文件,默认配置都是注释状态的。
    [root@localhost ansible]# vim hosts
    ...	//如果要在主机清单里面添加主机,可以看是否添加进指定的主机组内,也可以单独一台主机(如果是单独一台主机需要添加在配置文件顶部)
    ## green.example.com		
    ## blue.example.com
    ## 192.168.100.1
    ## 192.168.100.10
    
    # Ex 2: A collection of hosts belonging to the 'webservers' group
    
    ## [webservers]        //默认配置文件自带的主机组(带有[]括主的配置就是一个主机组),下面的主机都是属于这个主机组的成员
    ## alpha.example.org
    ## beta.example.org
    ## 192.168.1.100
    ## 192.168.1.110
    ...
    ## www[001:006].example.com    //这里的[001:006]是表示www001.example.com 到 www006.example.com 的主机
    
    # Ex 3: A collection of database servers in the 'dbservers' group
    
    ## [dbservers]
    ## db01.intranet.mydomain.net
    ## db02.intranet.mydomain.net
    ## 10.25.1.56
    ## 10.25.1.57
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    验证主机清单

    可使用 ansible 命令验证计算机是否存在于清单中

    //显示主机清单内所有的主机。
    [root@localhost ansible]# ansible all --list-hosts
      hosts (8):
        green.example.com
        blue.example.com
        192.168.100.1
        192.168.100.10
        alpha.example.org
        beta.example.org
        192.168.1.100
        192.168.1.110
    
    //显示清单内一个主机组(主机组是webservers)的主机
    [root@localhost ansible]# ansible webservers --list-hosts
      hosts (4):
        alpha.example.org
        beta.example.org
        192.168.1.100
        192.168.1.110
    
    //显示清单内没有存在于仍和主机组的主机
    [root@localhost ansible]# ansible ungrouped --list-hosts
      hosts (4):
        green.example.com
        blue.example.com
        192.168.100.1
        192.168.100.10
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    **注意:**如果清单中含有名称相同的主机和主机组,ansible 命令将显示警告并以主机作为其目标。主机组则被忽略。

    应对这种情况的方法有多种,其中最简单的是确保主机组不使用与清单中主机相同的名称。

    自定义清单文件

    在/etc/ansible/目录中,创建一个名为inventory的自定义静态清单文件。

    服务器清单规格

    主机IP系统服务
    192.168.171.141centos8管理主机(ansible)
    192.168.171.142centos8被管理主机(web)
    192.168.171.133centos8被管理主机(web)
    192.168.171.150centos8被管理主机(web)

    编辑/etc/ansible/inventory文件,将上表中所列出的主机加入受管主机序列。

    //进入ansible主配置里面修改默认的清单位置,改成我们创建的自定义清单inventory
    [root@localhost ansible]# vim ansible.cfg
    inventory      = inventory  		//取消注释,然后修改成和主配置同级的文件inventory
    [root@localhost ansible]# ls
    ansible.cfg  hosts  inventory  roles
    
    //在自定义主机清单里面添加主机。
    [root@localhost ansible]# vim inventory 
    192.168.171.133
      
    [webservers]
    192.168.171.142
    192.168.171.150                   
    
    //用临时命令查看主机清单看有没有变成自定义的那个
    [root@localhost ansible]# ansible all --list-hosts
      hosts (3):
        192.168.171.133
        192.168.171.142
        192.168.171.150
    
    //查看清单内主机组
    [root@localhost ansible]# ansible webservers --list-hosts
      hosts (2):
        192.168.171.142
        192.168.171.150
    [root@localhost ansible]# 
    
    //也可以添加自定义清单的绝对路径,一样可以查看到
    [root@localhost ansible]# ansible webservers -i /etc/ansible/inventory --list-hosts
      hosts (2):
        192.168.171.142
        192.168.171.150
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    ansible配置文件优先级

    可以通过修改 Ansible 配置文件中的设置来自定义 Ansible安装的行为。Ansible从控制节点上多个可能的位置之一选择其配置文件。

    使用/etc/ansible/ansible.cfg
    ansible软件包提供一个基本的配置文件,它位于**/etc/ansible/ansible.cfg**。如果找不到其他配置文件,则使用此文件。

    使用~/.ansible.cfg
    Ansible在用户的家目录中查找**.ansible.cfg文件。如果存在此配置文件并且当前工作目录中也没有ansible.cfg文件,则使用此配置取代/etc/ansible/ansible.cfg**。

    使用./ansible.cfg
    如果执行ansible命令的目录中存在ansible.cfg文件,则使用它,而不使用全局文件或用户的个人文件。这样,管理员可以创建一种目录结构,将不同的环境或项目存储在单独的目录中,并且每个目录包含为独特的一组设置而定制的配置文件。

    推荐的做法是在需要运行Ansible命令的目录中创建ansible.cfg文件。此目录中也将包含任何供Ansible项目使用的文件,如清单和playbook。这是用于Ansible配置文件的最常用位置。实践中不常使用**~/.ansible.cfg/etc/ansible/ansible.cfg**文件

    使用ANSIBLE_CONFIG环境变量
    我们可以通过将不同的配置文件放在不同的目录中,然后从适当的目录执行Ansible命令,以此利用配置文件。但是,随着配置文件数量的增加,这种方法存在局限性并且难以管理。有一个更加灵活的选项,即通过ANSIBLE_CONFIG环境变量定义配置文件的位置。定义了此变量时,Ansible将使用变量所指定的配置文件,而不用上面提到的任何配置文件。

    ANSIBLE_CONFIG环境变量指定的任何文件将覆盖所有其他配置文件。如果没有设置该变量,则接下来检查运行ansible命令的目录中是否有ansible.cfg文件。如果不存在该文件,则检查用户的家目录是否有**.ansible.cfg文件。只有在找不到其他配置文件时,才使用全局/etc/ansible/ansible.cfg文件。如果/etc/ansible/ansible.cfg**配置文件不存在,Ansible包含它使用的默认值。

    //初始ansible的默认使用的配置文件是
    [root@localhost ~]# ansible --version
    ansible 2.9.27
      config file = /etc/ansible/ansible.cfg
      configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
      ansible python module location = /usr/lib/python3.6/site-packages/ansible
      executable location = /usr/bin/ansible
      python version = 3.6.8 (default, Jan 19 2022, 23:28:49) [GCC 8.5.0 20210514 (Red Hat 8.5.0-7)]
    
    //可以把它cp到家目录下,因为家目录的优先级大于默认的所以自动切换到加目录下的配置文件。
    [root@localhost ~]# cp /etc/ansible/ansible.cfg .ansible.cfg
    [root@localhost ~]# ls -a
    .  ..  .ansible  .ansible.cfg  .bash_history  .bash_logout  .bash_profile  .bashrc  .config  .cshrc  .tcshrc  .viminfo  anaconda-ks.cfg
    [root@localhost ~]# ansible --version
    ansible 2.9.27
      config file = /root/.ansible.cfg
      configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
      ansible python module location = /usr/lib/python3.6/site-packages/ansible
      executable location = /usr/bin/ansible
      python version = 3.6.8 (default, Jan 19 2022, 23:28:49) [GCC 8.5.0 20210514 (Red Hat 8.5.0-7)]
    [root@localhost ~]# 
    
    //我们也可以自己创建一个工作目录,然后把配置文件放进来,那样的优先级是最高的(必须要在创建的工作目录里面才成立)[root@localhost ~]# mkdir -p /opt/kangtou
    [root@localhost ~]# cd /opt/kangtou/
    [root@localhost kangtou]# ls
    [root@localhost kangtou]# cp /etc/ansible/ansible.cfg .
    [root@localhost kangtou]# ansible --version
    ansible 2.9.27
      config file = /opt/kangtou/ansible.cfg
    ...
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    由此可见配置文件的优先级为: /etc/ansible/ansible.cfg < /root/.ansible.cfg < 自己创建的工作目录的配置(需要进入创建的工作目录)

    ansible配置文件的含义

    ansible配置文件分为两个部分:

    • [defaults]部分设置Ansible操作的默认值
    • [privilege_escalation]配置Ansible如何在受管主机上执行特权升级

    如下:

    [defaults]
    inventory = ./inventory
    remote_user = user
    ask_pass = false
    
    [privilege_escalation]
    become = true
    become_method = sudo
    become_user = root
    become_ask_pass = false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    **Ansible配置 ** 含义

    指令描述
    inventory指定清单文件的路径。
    remote_user要在受管主机上登录的用户名。如果未指定则使用当前用户名
    ask_pass是否提示输入SSH密码。如果使用SSH公钥身份验证则可以是false
    become连接后是否自动在受管主机上切换用户(通常切换为root) 这也可以通过play来指定。
    become_method如何切换用户(通常为sudo,这也是默认设置,但可选择su)
    become_user要在受管主机上切换到的用户(通常是root,这也是默认值)
    become_ask_pass是否需要为become_method提示输入密码。默认为false。

    临时命令

    Ansible运行临时命令的语法如下:

    ansible host-pattern -m module [-a 'module arguments'] [-i inventory]
    
    • 1

    使用临时命令可以快速执行单个Ansible任务,不需要将它保存下来供以后再次运行。它们是简单的在线操作,无需编写playbook即可运行。

    临时命令对快速测试和更改很有用。例如,可以使用临时命令确保一组服务器上的**/etc/hosts**文件中存在某一特定的行。可以使用另一个临时命令在许多不同的计算机上高效的重启服务,或者确保特定的软件包为最新版本。

    临时命令对于通过Ansible快速执行简单的任务非常有用。它们确实也存在局限,而且总体而言,要使用Ansible Playbook来充分发挥Ansible的作用。但在许多情形中,临时命令正是快速执行简单任务所需要的工具。

    连接清单内的主机

    想要控制主机清单内的主机,可以在主机ip后面跟上用户名,和密码然后授权,但是这样安全就没有了保障,所以我们一般发送密钥的方式来管理清单内的主机。

    //修改配置文件的方式来授权主机。
    [root@localhost ~]# vim /etc/ansible/inventory
    192.168.171.133
      
    [webservers]
    192.168.171.142 ansible_user=root ansible_password=1
    192.168.171.150
    
    //配置文件修改好后还需要用管理主机登录一下被管理主机输入一遍密码,在使用临时命令查看
    [root@localhost ansible]# ssh root@192.168.171.142
    [root@RS]# exit
    [root@localhost ansible]# ansible webservers -m ping
    The authenticity of host '192.168.171.150 (192.168.171.150)' can't be established.
    ECDSA key fingerprint is SHA256:b2+ErORHLlANCY23XTlkC8uzQ6KKscSXnc5aIAK80dI.
    Are you sure you want to continue connecting (yes/no/[fingerprint])? 192.168.171.142 | SUCCESS => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": false,
        "ping": "pong"
    }						//可以看到142这个主机已经连接成功
    
    192.168.171.150 | UNREACHABLE! => {
        "changed": false,
        "msg": "Failed to connect to the host via ssh: Host key verification failed.",
        "unreachable": true
    }						//而150却连接不上,因为缺少了配置文件授权。
    
    //先生成密钥,用传输密钥的方式连接清单内的主机
    [root@localhost ansible]# ssh-keygen -t rsa			//下面一直默认回车就行了
    +---[RSA 3072]----+
    |        oB= .++*O|
    |     . = o=+EoX==|
    |      = = Bo+=.++|
    |     o o = =o.. +|
    |      . S .. o ..|
    |          .   o  |
    |           . .   |
    |            .    |
    |                 |
    +----[SHA256]-----+
    [root@localhost]# ssh-copy-id -i ./.ssh/id_rsa.pub 192.168.171.133
    [root@localhost]# ssh-copy-id -i ./.ssh/id_rsa.pub 192.168.171.150
    /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "./.ssh/id_rsa.pub"
    The authenticity of host '192.168.171.150 (192.168.171.150)' can't be established.
    ECDSA key fingerprint is SHA256:b2+ErORHLlANCY23XTlkC8uzQ6KKscSXnc5aIAK80dI.
    Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
    /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
    /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
    root@192.168.171.150's password: 
    
    Number of key(s) added: 1
    
    Now try logging into the machine, with:   "ssh '192.168.171.150'"
    and check to make sure that only the key(s) you wanted were added.
    
    //用传输密钥的方式就不需要在次登陆主机了(推荐使用这种方式)
    [root@localhost ansible]# ansible all -m ping
    192.168.171.142 | SUCCESS => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": false,
        "ping": "pong"
    }
    192.168.171.133 | SUCCESS => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": false,
        "ping": "pong"
    }
    192.168.171.150 | SUCCESS => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": false,
        "ping": "pong"
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80

    利用模块来使用临时命令

    Ansible常用模块

    模块类别模块
    文件模块copy:将本地文件复制到受管主机 file:设置文件的权限和其他属性 lineinfile:确保特定行是否在文件中 synchronize:使用rsync同步内容
    软件包模块package:使用操作系统本机的自动检测软件包管理器管理软件包 yum:使用yum管理软件包 apt:使用APT管理软件包 dnf:使用dnf管理软件包 gem:管理Ruby gem pip:从PyPI管理Python软件包
    系统模块firewalld:使用firewalld管理防火墙 reboot:重启计算机 service:管理服务 user:添加、删除和管理用户帐户
    Net Tools模块get_url:通过HTTP、HTTPS或FTP下载文件 nmcli:管理网络 uri:与Web服务交互

    大部分模块会取用参数。可在模块的文档中找到可用于该模块的参数列表。临时命令可以通过-a选项向模块传递参数。无需参数时,可从临时命令中省略-a选项。如果需要指定多个参数,请以引号括起的空格分隔列表形式提供。

    //在管理机上面控制已经连接的清单内的主机创建个用户
    [root@localhost ansible]# ansible 192.168.171.133 -m user -a 'name=tom uid=2020 state=present'
    192.168.171.133 | CHANGED => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": true,
        "comment": "",
        "create_home": true,
        "group": 2020,
        "home": "/home/tom",
        "name": "tom",
        "shell": "/bin/bash",
        "state": "present",
        "system": false,
        "uid": 2020
    }
    
    //查看被管理主机上有没有被创建用户tom
    [root@web ~]# id tom
    uid=2020(tom) gid=2020(tom) groups=2020(tom)
    
    //控制清单内多台主机
    [root@localhost ansible]# ansible all -m user -a 'name=tom uid=2020 state=present'
    192.168.171.133 | SUCCESS => {				//显示任务已经完成就显示SUCCESS,跟下面的是不一样的。
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "append": false,
        "changed": false,
        "comment": "",
        "group": 2020,
        "home": "/home/tom",
        "move_home": false,
        "name": "tom",
        "shell": "/bin/bash",
        "state": "present",
        "uid": 2020
    }
    192.168.171.142 | CHANGED => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": true,
        "comment": "",
        "create_home": true,
        "group": 2020,
        "home": "/home/tom",
        "name": "tom",
        "shell": "/bin/bash",
        "state": "present",
        "system": false,
        "uid": 2020
    }
    192.168.171.150 | CHANGED => {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/libexec/platform-python"
        },
        "changed": true,
        "comment": "",
        "create_home": true,
        "group": 2020,
        "home": "/home/tom",
        "name": "tom",
        "shell": "/bin/bash",
        "state": "present",
        "system": false,
        "uid": 2020
    }
    
    //任务执行成功,会显示rc=0的,不然就是没成功。
    [root@localhost ansible]# ansible all -a 'date'
    192.168.171.150 | CHANGED | rc=0 >>
    Thu Oct 20 04:14:07 EDT 2022
    192.168.171.133 | CHANGED | rc=0 >>
    Thu Oct 20 04:14:07 EDT 2022
    192.168.171.142 | CHANGED | rc=0 >>
    Thu Oct 20 04:14:07 EDT 2022
    [root@localhost ansible]# 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
  • 相关阅读:
    【多线程那些事儿】多线程的执行顺序如你预期吗?
    2022下半年软考报名入口!
    大疆 dji mini4pro 不同充电器头 充电速度
    11_跳表(Skip List)
    一篇文章带你了解网页框架——Vue简单入门
    【试题039】 多个逻辑或例题
    关于到年底日常生活的工作计划
    SparkSQL系列-6、外部数据源 DataSource?
    CSS常用函数补充(var、clac、blur、gradient)
    360+城市空气质量指数-日度数据、良好天数统计(2001-2022年)
  • 原文地址:https://blog.csdn.net/nuanchunhujian/article/details/127430248