范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

ansible之playbook

  ansible-playbookplaybook & yml 说明playbook  由一个或多个 play  组成.playbook  中 每个play  必须包含 hosts  和 tasks .playbook  以 yaml  语法编写.可读性强 脚本语言交互性能力强 使用实现语言的数据类型 一致性的信息模型 易于实现 基于流模式处理 表达能力好, 扩展性强 YAML  约定以 ---  开头 和 开始不同的 play  .YAML  以 #  作为注释.YAML  必须统一缩进, 空格 与 tab  不能混用, 缩进的级别也必须相同, 同级缩进代表同样的级别.YAML  文件内容 是大小写敏感的, 跟 Linux 一样区分大小写.YAML  key/value 形式可写在同一行也可以换行写. 同行使用 :  隔开.YAML  一个完整的代码块功能最少包含2个元素. 如 name: taskYAML  一个 name 下只能包含一个 taskYAML  -  开头的为列表, key/value  形式的为字典.YAML  特性playbook 核心元素hosts  远程主机列表 ( ip_addr/hostname/groupname ) tasks  任务集, 任务列表, 有两种写法。 action: module args  action 参数。 module: args  参数 (一般使用这种)。 ignore_errors: True  当前 task 出错时仍然会向下执行。 varniables  内置变量或自定义变量在 playbook 文件中调用。 templates  模板,可替换模板文件中的变量并实现一些简单逻辑的文件。 handles  与 notity 结合使用, 由特定条件触发的操作, 满足条件执行, 否则不执行。 tags  标签 指定任务执行, 用于执行一个 playbook 中的部分代码. 主要用于测试ansible的语法与执行验证。 ansible-playbook 命令ansible-playbook -C --check  Check 检查脚本运行情况, 不会在远程服务器里运行。 --list-hosts  列出执行此任务的主机。 --list-tasks  列出任务组的具体任务列表。 --limit  只对主机列表中的某台主机执行。 -v -vv -vvv  显示详细的执行DEBUG信息过程, 多个v参数等于DEBUG信息的叠加,显示更为详细。 ansible-playbook setup介绍: 这个模块默认会被playbooks自动调用,用于收集远程主机的相关变量信息,获取到变量信息可以被playbooks调用。针对 setup模块,我们经常使用的是  fact  ,在此只对 fact 做详细讲解,其他的就不过多叙述了,如果想了解详细信息,可以访问官方文档获取帮助。fact 是ansible模块setup的功能,主要用于获取相关信息作为变量继承给playbook子任务调用。 gather_facts: # ansible k3s-cluster -m setup ubuntu20-bj03 | SUCCESS => {     "ansible_facts": {         "ansible_all_ipv4_addresses": [             "10.0.16.4"         ],         "ansible_all_ipv6_addresses": [             "fe80::5054:ff:fed6:42a8"         ],         "ansible_apparmor": {             "status": "enabled"         },         "ansible_architecture": "x86_64",         "ansible_bios_date": "04/01/2014",         "ansible_bios_vendor": "SeaBIOS",         "ansible_bios_version": "seabios-1.9.1-qemu-project.org",         "ansible_board_asset_tag": "NA",         "ansible_board_name": "NA",         "ansible_board_serial": "NA",         "ansible_board_vendor": "NA",         "ansible_board_version": "NA",         "ansible_chassis_asset_tag": "NA",         "ansible_chassis_serial": "NA",         "ansible_chassis_vendor": "Smdbmds",         "ansible_chassis_version": "3.0",                  # 多余的冗余信息就不放了,自己可以执行验证下。         # setup获得变量信息,都可以用于继承给playbook调用。 } 自定义Fact手动设置:ansible除了能获取到内置的fact的变量信息,还可以手动为某个主机组或者主机定制 本地 fact 。本地  fact  默认存放宿主机的/etc/ansible/facts.d 目录下,支持的文件格式为ini、json。加载后的fact的key是ansible_local的特殊变量。
  denis_test.fact [general] package = vsftpd service = vsftpd state = starte
  setup_facts.yaml --- - name: Install Remote Facts   hosts: k3s-cluster   vars:      remote_dir: /etc/ansible/facts.d     facts_file: denis_test.fact   tasks:     - name: Create Directory       file:         state: directory         recurse: yes         path: "{{ remote_dir }}"     - name: Install the new facts       copy:         src: "{{ facts_file }}"         dest: "{{ remote_dir }}"
  执行测试 # ansible-playbook setup_facts.yaml # ansible test -m setup         ubuntu20-bj03 | SUCCESS => {     "ansible_facts": {              # -----分隔符-----                  "ansible_local": {             "custom": {                 "general": {                     "package": "vsftpd",                     "service": "vsftpd",                     "state": "started"                 }             }         },          # -----分隔符-----    }
  调用测试
  deniss_test.yaml - name: Install Apache and starts the service   hosts: k3s-cluster   tasks:     - name: Install Package       yum:          name: "{{ ansible_facts.ansible_local.custom.general.package }}"         state: latest     - name: Start Service       service:          name: "{{ ansible_facts.ansible_local.custom.general.service }}"         state: "{{ ansible_facts.ansible_local.custom.general.state }}" ansible-playbook set_fact使用set_fact设置新的变量set_fact 可以自定义变量通过template或者变量的方式在playbook中继承使用。如:假设你需要获取一个进程使用的内存的使用率,必须通过set_fact来进行计算之后得出结果,并将其值在playbook中继承使用。
  deniss_fact_demo.yaml - name: set_fact demo   hosts: k3s-cluster   tasks:     - name: Calculate InnoDB buffer pool size       set_fact: innodb_buffer_pool_size_mb="{{ ansible_memtotal_mb / 2 |int }}"           - debug: var=innodb_buffer_pool_size_mb
  执行测试 # ansible-playbook deniss_fact_demo.yaml  PLAY [set_fact demo] *****************************************************************************************************************************************************  TASK [Gathering Facts] ****************************************************************************************************************************************************** ok: [ubuntu20-bj03]  TASK [Calculate InnoDB buffer pool size] ************************************************************************************************************************************ ok: [ubuntu20-bj03]  TASK [debug] **************************************************************************************************************************************************************** ok: [ubuntu20-bj03] => {     "innodb_buffer_pool_size_mb": "2911.2" }  PLAY RECAP ****************************************************************************************************************************************************************** ubuntu20-bj03                : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0    手动采集 fact我们在运行playbook的时候,Ansible会先ssh连接被控端采集fact,如果被控制端的ssh还没有完成运行,就会导致整个playbook执行失败。解决这个问题,可以先在配置中关闭fact采集,然后在task中通过wait_for探测被控端ssh端口是否正常监听,然后在task中在手动setup模块来采集fact。 - hosts: k3s-cluster   name: test demo    gather_facts: False   tasks:     - name: wait for ssh to be running       local_action: wait_for port=22 host="{{ inventory_hostname }}" search_regex=OpenSSH     - name: gather facts       setup: fact缓存如果在playbook中需要继承fact,可启用fact缓存来提高效率。fact支持缓存  json、memcached、redis ansible.cfg中的配置说明:json  以json格式文件作为fact缓存后端,ansible将会把采集的fact写入到宿主机的本地目录,最好是SSD硬盘。 redis  使用redis做缓存。 memcached  使用memcached做缓存。 smart  表示默认收集 facts,但 facts 已有的情况下不会收集,即使用缓存 facts; implicit  表示默认收集 facts,要禁止收集,必须使用 gather_facts: False; explicit  则表示默认不收集,要显式收集,必须使用 gather_facts: Ture。 [defaults] gathering = smart # 缓存时间 fact_caching_timeout = 86400     fact_caching = {jsonfile/redis/memcached} # 指定ansible包含fact的json文件位置,如果目录不存在,会自动创建 # local fact_caching_connection = /tmp/ansible_fact_cache # redis fact_caching_connection = 127.0.0.1:6379:admin # memcached fact_caching_connection = ["127.0.0.1:11211"] 关闭fact(提高执行效率)在配置中关闭fact,整个playbookfact变量将不会在显示,可以提高执行效率,但是有时候又需要使用 facts 中的信息,这时候可以按照上述设置 facts 的缓存,在空闲的时候收集 facts,缓存下来,在需要的时候直接读取缓存进行引用。 # playbook 配置 - hosts: k3s-cluster   gather_facts: no # ansible.cfg 配置   [defaults] gathering = explicit ansible-playbook 变量变量名要求: 只允许使用  字母  、数字  、 _  组成, 而且只能以 字母 开头。内置的公共变量: ansible k3s-cluster -m setup -a "filter=*addresses*"  可使用 filter 参数进行过滤 使用  ansible k3s-cluster -m setup  可以获取到主机的系统变量名称 通过文件自定义变量: 对主机组中的主机单独定义变量, 优先级高于公共变量。 对主机组中的所有主机定义统一变量, 优先级低于对单独主机定义的变量。 /etc/ansible/hosts  文件中定义[appserver] # 定义变量 node_id 10.0.8.2 node_id=17  # 对主机组 定义统一变量 domain_name [k3s-cluster:vars] domain_name=deniss.wang 使用变量灵活配置不同主机的  hostname --- - hosts: k3s-cluster   become: yes   become_user: root    tasks:     - name: set hostname       hostname: name={{ node_id }}.{{ domain_name }} 通过命令行定义变量: 通过命令行定义的变量优先级是最高的 ansible-playbook -e varname=valur  在  playbook  文件里 定义变量.通过  {{ 变量名 }}  使用变量,另外需要注意的是,如果有中文,需要使用""把变量括起来。通过  vars:  列表 定义多个 变量.--- - hosts: k3s-cluster   remote_user: root    # 定义变量   vars:     - pkg_name: httpd     - env_name: prod    tasks:     - name: {{ env_name }} install {{ pkg_name }}       yum: name={{ pkg_name }}  通过定义单独的变量文件 用于统一存放变量, 可避免变量的重复定义。 定义单独的 变量文件, 只需要将所有变量以  key: value  形式写入到 yaml  文件中既可。在  playbook  文件中, 只需要使用 vars_files:  指定 yaml  文件路径既可。vars.yaml 变量文件 --- pkg_name: httpd file_name: deniss.wang install.yaml --- - hosts: k3s-cluster   remote_user: root    # 配置模板文件   vars_files:     # 指定文件的路径     - vars.yaml    tasks:     - name: install {{ pkg_name }}       yum: name={{ pkg_name }}     - name: create {{ file_name }} file       file: name=/tmp/{{ file_name }}.txt state=touch  执行 playbook 操作 # ansible-playbook install.yaml PLAY [k3s-cluster] *******************************************************************************************************  TASK [Gathering Facts] ******************************************************************************************* ok: [10.0.8.2]  TASK [install httpd] ********************************************************************************************* changed: [10.0.8.2]  TASK [create deniss.wang file] ************************************************************************************** changed: [10.0.8.2]  PLAY RECAP ******************************************************************************************************* 10.0.8.2                  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 ansible-playbook templatetemplate 是ansible-playbook一个模块,用于存放生成配置的模板,使用jinja2语言编写,后缀为 xx.j2 ,只能用于 playbook。templates  文件, 可嵌套引用脚本。字符串: 使用单引号或双引号. 数字: 整数, 浮点数. 列表: [A1, A2, …] 元组: (B1, B2, …) 字典: {key1:value1, key2:value2, …} 布尔值: true/false 算术运算:  + , - , * , / , // , % , ** 比较操作:  == , != , > , >= , < , <= 逻辑运算: and, or, not 流表达式: for, if, when Jinja2  语法:templates  根据模板块文件动态生成对应的配置文件templates 的模板文件必须存放于 templates 目录下, 并且以 .j2  为后缀。templates 目录需要与  playbook  的 yaml  文件在同级目录中。# tree nginx/ |-- nginx.yaml |-- templates        |-- nginx.conf.j2 "
  算术运算nginx.conf.j2 user nginx; # 这里使用 环境变量 vcpus * 2,会根据操作系统CPU自动生成。 worker_processes {{ ansible_processor_vcpus * 2 }}; error_log /var/log/nginx/error.log; pid /run/nginx.pid;  include /usr/share/nginx/modules/*.conf;  events {     worker_connections 10240; }  http {     log_format  main  "$remote_addr - $remote_user [$time_local] "$request" "                       "$status $body_bytes_sent "$http_referer" "                       ""$http_user_agent" "$http_x_forwarded_for"";      access_log  /var/log/nginx/access.log  main;      sendfile            on;     tcp_nopush          on;     tcp_nodelay         on;     keepalive_timeout   65;     types_hash_max_size 2048;      include             /etc/nginx/mime.types;     default_type        application/octet-stream;      include /etc/nginx/conf.d/*.conf;      server {         listen       80 default_server;         listen       [::]:80 default_server;         server_name  _;         root         /usr/share/nginx/html;          # Load configuration files for the default server block.         include /etc/nginx/default.d/*.conf;          location / {         }          error_page 404 /404.html;             location = /40x.html {         }          error_page 500 502 503 504 /50x.html;             location = /50x.html {         }     } }  nginx.yaml --- - hosts: k3s-cluster   become: yes   become_user: root    tasks:     - name: install nginx       yum: name=nginx     - name: nginx template conf       # 如果yaml与templates在同一目录, src直接写.j2文件       template: src="/a2020/img/data-img.jpg" data-src=nginx.conf.j2 dest=/etc/nginx/nginx.conf       notify:         - restart nginx     - name: start nginx       service: name=nginx state=started enabled=yes    handlers:     - name: restart nginx       service: name=nginx state=restarted "
  when 条件语句when  条件语句 例子# tree nginx |-- nginx.yaml |-- templates        |-- nginx.conf.centos7.j2        |-- nginx.conf.centos8.j2 playbook  文件--- - hosts: k3s-cluster   become: yes   become_user: root    tasks:     - name: install nginx       yum: name=nginx     - name: template centos 7 conf       # 如果 yaml 与 templates 在同一目录, src 直接写.j2 文件。       template: src="/a2020/img/data-img.jpg" data-src=nginx.conf.centos7.j2 dest=/etc/nginx/nginx.conf       # 使用 when 语句进行判断 如果变量为 "7" 执行以下操作       when: ansible_distribution_major_version == "7"       notify:         - restart nginx     - name: template centos 8 conf       # 同上       template: src="/a2020/img/data-img.jpg" data-src=nginx.conf.centos8.j2 dest=/etc/nginx/nginx.conf       # 使用 when 语句进行判断 如果变量为 "Ubuntu" 且版本为20 执行以下操作    when: (ansible_distribution == "Ubuntu" and ansible_distribution_major_version == "20"       notify:         - restart nginx     - name: start nginx       service: name=nginx state=started enabled=yes    handlers:     - name: restart nginx       service: name=nginx state=restarted 执行 playbook 文件 skipping  状态表示跳过执行这个 TASK。 # ansible-playbook nginx.yml  PLAY [k3s-cluster] *******************************************************************************************  TASK [Gathering Facts] ******************************************************************************* ok: [10.0.8.2]  TASK [install nginx] ********************************************************************************* ok: [10.0.8.2]  TASK [template centos 7 conf] ************************************************************************ changed: [10.0.8.2]  TASK [template centos 8 conf] ************************************************************************ skipping: [10.0.8.2]  TASK [start nginx] *********************************************************************************** ok: [10.0.8.2]  RUNNING HANDLER [restart nginx] ********************************************************************** changed: [10.0.3.13]  PLAY RECAP ******************************************************************************************* 10.0.8.2      : ok=5    changed=2    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0 "
  迭代变量 with_tiems 迭代  with_items  执行重复任务。对于迭代选项, 固定变量名为  item  。在  task  中使用 with_items  指定需要迭代的元素列表。元素列表 支持  字符串  和 字典  。--- - hosts: k3s-cluster   become: yes   become_user: root    tasks:     - name: create multi files       # {{ item }} 为内置特殊变量, 代表 with_items 列表中的内容       file: name=/tmp/{{ item }} state=touch       with_items:         - file_one         - file_two         - file_three         - file_four     - name: install multi software       yum: name={{ item }}       with_items:         - vsftpd         - net-tools         - iftop "
  代嵌套子变量 (字典)迭代嵌套子变量. 对迭代中的变量进行嵌套关联的操作. playbook  文件 --- - hosts: k3s-cluster   become: yes   become_user: root    tasks:     - name: create some files       # {{ item }} 为特殊变量, 代表 with_itmes 列表中的内容       file: name=/tmp/{{ item }} state=touch       with_items:         - file_one         - file_two         - file_three         - file_four      - name: create multi group       group: name={{ item }}       with_items:         - jinja2_file1         - jinja2_file2         - jinja2_file3         - jinja2_file4      - name: create multi user       # 使用 item.key值 进行引用       user: name={{ item.name }} group={{ item.group }}       # 使用 字典 定义 嵌套的子 变量       with_items:         - { name: "file_one", group: "jinja2_file1" }         - { name: "file_two", group: "jinja2_file2" }         - { name: "file_three", group: "jinja2_file3" }         - { name: "file_four", group: "jinja2_file4" }      - name: permission multi files       file: name=/tmp/{{ item.name }} owner={{ item.name }} group={{ item.group }}       with_items:         - { file: "file_one", name: "file_one", group: "jinja2_file1" }         - { file: "file_two", name: "file_two", group: "jinja2_file2" }         - { file: "file_three", name: "file_three", group: "jinja2_file3" }         - { file: "file_four", name: "file_four", group: "jinja2_file4" }   执行  playbook  文件# ansible-playbook file.yml  PLAY [k3s-cluster] *****************************************************************************************  TASK [Gathering Facts] ***************************************************************************** ok: [10.0.8.2]  TASK [create multi files] *************************************************************************** changed: [10.0.8.2] => (item=file_one) changed: [10.0.8.2] => (item=file_two) changed: [10.0.8.2] => (item=file_three) changed: [10.0.8.2] => (item=file_four)  TASK [create multi group] *************************************************************************** changed: [10.0.8.2] => (item=jinja2_file1) changed: [10.0.8.2] => (item=jinja2_file2) changed: [10.0.8.2] => (item=jinja2_file3) changed: [10.0.8.2] => (item=jinja2_file4)  TASK [create multi user] **************************************************************************** changed: [10.0.8.2] => (item={u"group": u"jinja2_file1", u"name": u"file_one"}) changed: [10.0.8.2] => (item={u"group": u"jinja2_file2", u"name": u"file_two"}) changed: [10.0.8.2] => (item={u"group": u"jinja2_file3", u"name": u"file_three"}) changed: [10.0.8.2] => (item={u"group": u"jinja2_file4", u"name": u"file_four"})  TASK [permission multi files] *********************************************************************** changed: [10.0.8.2] => (item={u"group": u"jinja2_file1", u"name": u"file_one", u"file": u"file1"}) changed: [10.0.8.2] => (item={u"group": u"jinja2_file2", u"name": u"file_two", u"file": u"file2"}) changed: [10.0.8.2] => (item={u"group": u"jinja2_file3", u"name": u"file_three", u"file": u"file3"}) changed: [10.0.8.2] => (item={u"group": u"jinja2_file4", u"name": u"file_four", u"file": u"file4"})  PLAY RECAP ***************************************************************************************** 10.0.8.2    : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 "
  流程控制、循环 for 与 iffor 循环 {% for 语句块 %} ... {% endfor %} ansible-playbook 文件 --- - hosts: k3s-cluster   become: yes   become_user: root    vars:     # 列表     listen_port:       - 80       - 81       - 82     # 字典     service:       - name: web1         domain: deniss.wang         port: 9090         user: nginx         path: /var/www/html        - name: web2         domain: deniss.wang         port: 9091         user: nginx         path: /var/www/html       - name: web3         domain: deniss.wang         port: 9092         user: nginx         path: /var/www/html    tasks:     - name: copy template conf       template: src="/a2020/img/data-img.jpg" data-src=for.conf.j2 dest=/tmp/for.conf  for.conf.j2 文件 {% for port in listen_port %}  语句 listen_port  为 playbook 中定义的 vars  。{% for port in listen_port %}  server {    listen {{ port }} }  {% endfor %} 查看生成 for.conf 文件 # cat /root/for.conf  server {    listen 80 }   server {    listen 81 }   server {    listen 82 } 字典形式nginx.yaml --- - hosts: k3s-cluster   become: yes   become_user: root    vars:     # 字典的形式     service:       - name: web1         domain: deniss.wang         port: 9090         user: nginx         path: /var/www/html       - name: web2         domain: deniss.wang         port: 9091         user: nginx         path: /var/www/html       - name: web3         domain: deniss.wang         port: 9092         user: nginx         path: /var/www/html    tasks:     - name: copy template conf       template: src="/a2020/img/data-img.jpg" data-src=nginx.conf.j2 dest=/tmp/nginx.conf nginx.conf.j2 文件,放在templates下面 {% for s in service %} user {{ s.user }}; worker_processes {{ ansible_processor_vcpus * 2 }}; pid /run/nginx.pid;     server {         listen       {{ s.port }} default_server;         listen       [::]:{{ s.port }} default_server;         server_name  {{ s.name }}.{{ s.domain }};         root         {{ s.path }};     }  {% endfor %} if 流程控制 {% if 语句块 %} ... {% else %} ... {% endif %} playbook 文件 其中 web1, web2 不传 user 变量,web3 传 user 变量。 --- - hosts: k3s-cluster   become: yes   become_user: root    vars:     # 字典     service:       - name: web1         domain: deniss.wang         port: 90         path: /var/www/html        - name: web2         domain: deniss.wang         port: 91         path: /var/www/html        - name: web3         domain: deniss.wang         port: 92         user: nginx         path: /var/www/html    tasks:     - name: copy template conf       template: src="/a2020/img/data-img.jpg" data-src=nginx2.conf.j2 dest=/tmp/nginx2.conf nginx2.conf.j2 文件 {% if s.user is defined %}  判断 是否有 s.user 这个变量{% for s in service %}  {% if s.user is defined %} user {{ s.user }}; {% else %} user root; {% endif %} worker_processes {{ ansible_processor_vcpus * 2 }}; pid /run/nginx.pid;     server {         listen       {{ s.port }} default_server;         server_name  {{ s.name }}.{{ s.domain }};         root         {{ s.path }};     }  {% endfor %} 查看生成后的 nginx2.conf 第一个server 不包含 s.user 变量 所以  user root; 第二个server 不包含 s.user 变量 所以  user root; 第三个server 包含 s.user 变量 所以  user nginx;  等于变量值# cat  nginx2.conf user root; worker_processes 4; pid /run/nginx.pid;     server {         listen       90 default_server;         server_name  web1.deniss.wang;         root         /var/www/html;     }   user root; worker_processes 4; pid /run/nginx.pid;     server {         listen       91 default_server;         server_name  web2.deniss.wang;         root         /var/www/html;     }   user nginx; worker_processes 4; pid /run/nginx.pid;     server {         listen       92 default_server;         server_name  web3.deniss.wang;         root         /var/www/html;     } tasks 示范定义一组执行任务,可以包含多个模块的集合。
  Demo --- # 指定主机组 - hosts: k3s-cluster   # 开启提权,指定用户   become: yes   become_user: root    # 任务   tasks:     # 任务的名称     - name: ping server       ping:     - name: echo hostname       # shell 为模块名, 后面等同于 -a "" 参数       shell: hostname     - name: touch file       file: name=/tmp/file.txt state=touch     - name: echo file       shell: ls -l /tmp/file.txt     - name: write file       shell: echo "hello world" > /tmp/file.txt     - name: copy module write file       copy: content="hello deniss " dest=/tmp/deniss.txt     - name: display file content       shell: cat /tmp/file.txt       register: display_content1     - name: show       debug: var=display_content1.stdout verbosity=0     - name: display copy module file content       shell: cat /tmp/deniss.txt       register: display_content2     - name: show       debug: var=display_content2.stdout verbosity=0 # ansible-playbook hello.yaml PLAY [k3s-cluster] *******************************************************************************************************************************************************************  TASK [Gathering Facts] *************************************************************************************************************************************************************** ok: [ubuntu20-bj03]  TASK [ping server] ******************************************************************************************************************************************************************* ok: [ubuntu20-bj03]  TASK [echo hostname] ***************************************************************************************************************************************************************** changed: [ubuntu20-bj03]  TASK [touch file] ******************************************************************************************************************************************************************** changed: [ubuntu20-bj03]  TASK [echo file] ********************************************************************************************************************************************************************* changed: [ubuntu20-bj03]  TASK [write file] ******************************************************************************************************************************************************************** changed: [ubuntu20-bj03]  TASK [copy module write file] ******************************************************************************************************************************************************** ok: [ubuntu20-bj03]  TASK [display file content] ********************************************************************************************************************************************************** changed: [ubuntu20-bj03]  TASK [show] ************************************************************************************************************************************************************************** ok: [ubuntu20-bj03] => {     "display_content1.stdout": "hello world" }  TASK [display copy module file content] ********************************************************************************************************************************************** changed: [ubuntu20-bj03]  TASK [show] ************************************************************************************************************************************************************************** ok: [ubuntu20-bj03] => {     "display_content2.stdout": "hello deniss" }  PLAY RECAP *************************************************************************************************************************************************************************** ubuntu20-bj03              : ok=11   changed=6    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 状态释义: ok  : ok 未修改文件元数据,绿色changed  : 数据有修改,黄色handles 示范handles  与 notity  结合的例子同一个 name  下可以定义多个 notify  配置关联到不同的 handlers  中。--- # 指定主机组 - hosts: k3s-cluster   # 开启提权,指定用户   become: yes   become_user: root    tasks:     - name: copy httpd.conf       copy: src="/a2020/img/data-img.jpg" data-src=~/ansible/httpd.conf dest=/etc/httpd/conf/httpd.conf backup=yes       # 关联多个触发器的写法       notify:         - restart httpd         - check status httpd         - check network port
  Demo
  Centos --- # 指定主机组 - hosts: k3s-cluster   # 开启提权,指定用户   become: yes   become_user: root    tasks:     - name: install httpd       yum: name=httpd     - name: copy httpd.conf       copy: src="/a2020/img/data-img.jpg" data-src=/opt/ansible/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf backup=yes       # 此任务 如果有变动会触发如下定义名称的触发器       notify: restart httpd     - name: start httpd       service: name=httpd state=started enabled=yes    # 触发器, 需要配置 notify 触发   handlers:     - name: restart httpd       service: name=httpd state=restarted
  Ubuntu --- # 指定主机组 - hosts: k3s-cluster   # 开启提权,指定用户   become: yes   become_user: root    tasks:     - name: Update apt-get repo and cache       apt: update_cache=yes force_apt_get=yes cache_valid_time=3600     - name: Install Vsftpd       apt:         name: vsftpd     - name: copy vsftpd.conf       copy: src="/a2020/img/data-img.jpg" data-src=/opt/ansible/conf/vsftpd.conf dest=/etc/vsftpd.conf backup=yes       notify: restart vsftpd     - name: start vsftpd       service: name=vsftpd state=started enabled=yes   # 配置 notify 触发,修改配置文件的时候生效。   handlers:     - name: restart vsftpd       service: name=vsftpd state=restarted 执行安装vsftpd PlayBook # ansible-playbook install_vsftpd.yaml PLAY [k3s-cluster] *******************************************************************************************************************************************************************  TASK [Gathering Facts] *************************************************************************************************************************************************************** ok: [ubuntu20-bj03]  TASK [Update apt-get repo and cache] ************************************************************************************************************************************************* ok: [ubuntu20-bj03]  TASK [Install Vsftpd] **************************************************************************************************************************************************************** ok: [ubuntu20-bj03]  TASK [copy vsftpd.conf] ************************************************************************************************************************************************************** ok: [ubuntu20-bj03]  TASK [start vsftpd] ****************************************************************************************************************************************************************** ok: [ubuntu20-bj03]  PLAY RECAP *************************************************************************************************************************************************************************** ubuntu20-bj03              : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 修改vsftpd文件以后会触发重启操作。  # ansible-playbook install_pkg.yaml PLAY [k3s-cluster] *******************************************************************************************************************************************************************  TASK [Gathering Facts] *************************************************************************************************************************************************************** ok: [ubuntu20-bj03]  TASK [Update apt-get repo and cache] ************************************************************************************************************************************************* ok: [ubuntu20-bj03]  TASK [Install Vsftpd] **************************************************************************************************************************************************************** ok: [ubuntu20-bj03]  TASK [copy vsftpd.conf] ************************************************************************************************************************************************************** changed: [ubuntu20-bj03]  TASK [start vsftpd] ****************************************************************************************************************************************************************** ok: [ubuntu20-bj03]  RUNNING HANDLER [restart vsftpd] ***************************************************************************************************************************************************** changed: [ubuntu20-bj03]  PLAY RECAP *************************************************************************************************************************************************************************** ubuntu20-bj03              : ok=6    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 Tags示范tags 定义了  tags  后可通过定义的 tags  单独运行该 tags 来执行指定的tasks, 运行多个可用 ,  号分隔。多个不同的任务中可以定义相同的  tags 。--- # 指定主机组 - hosts: k3s-cluster   # 开启提权,指定用户   become: yes   become_user: root    tasks:     - name: Update apt-get repo and cache       apt: update_cache=yes force_apt_get=yes cache_valid_time=3600     - name: Install Vsftpd       apt:         name: vsftpd     - name: copy vsftpd.conf       copy: src="/a2020/img/data-img.jpg" data-src=conf/vsftpd.conf dest=/etc/vsftpd.conf backup=yes       notify: restart vsftpd       # 定义标签       tags: cpconf           - name: start vsftpd       service: name=vsftpd state=started enabled=yes       # 定义标签       tags: upvsftpd         # 配置 notify 触发,修改配置文件的时候生效。   handlers:     - name: restart vsftpd       service: name=vsftpd state=restarted           -t  指定标签执行# ansible-playbook -t upvsftpd  install_vsftpd.yaml PLAY [k3s-cluster] *******************************************************************************************************************************************************************  TASK [Gathering Facts] *************************************************************************************************************************************************************** ok: [ubuntu20-bj03]  TASK [start vsftpd] ****************************************************************************************************************************************************************** ok: [ubuntu20-bj03]  PLAY RECAP *************************************************************************************************************************************************************************** ubuntu20-bj03              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  # ansible-playbook -t cpconf  install_vsftpd.yaml PLAY [k3s-cluster] *******************************************************************************************************************************************************************  TASK [Gathering Facts] *************************************************************************************************************************************************************** ok: [ubuntu20-bj03]  TASK [copy vsftpd.conf] ************************************************************************************************************************************************************** changed: [ubuntu20-bj03]  RUNNING HANDLER [restart vsftpd] ***************************************************************************************************************************************************** changed: [ubuntu20-bj03]  PLAY RECAP *************************************************************************************************************************************************************************** ubuntu20-bj03              : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 ansible-vaultplaybook  文件加密工具。 ansible-vault encrypt hello.yaml encrypt : AES256 加密 ( 会提示输入密码 )。 view : 加密的情况下 查看 原来的内容。 edit : 编辑加密的 playbook 文件。 decrypt : 解密。 rekey : 修改加密密码。 ansible-consoleansible-console : 可交互执行命令, 支持 Tab  键。# ansible-consoleWelcome to the ansible console.Type help or ? to list commands.deniss.wang@all (10)[f:5]$ root@all (10) [f:5]$ root  当前执行用户。 all  表示当前主机列表。 (10)  表示当前主机清单下包含 10 台主机。 [f:5] : 表示并发执行任务数为 5 个… ansible RolesRoles  角色是 Ansible v1.2 版本新加入特性,用于层次性、结构化的组织 playbook 。Roles  能够根据层次结构自动加载- 变量文件、tasks、handler、template 文件等. 简单来讲就是将 这些文件归类到各自单独的文件目录中, 使 playbook 文件可以更好的通过 include  这些文件目录。Roles  一般用于基于 主机构建服务  的场景中, 但也可以用于构建 守护进程  等场景。Roles  默认的目录为 /etc/ansible/roles  。
  目录结构说明 playbook.yml  - 剧本文件 app  具体的角色项目名称, 比如 Nginx、PHP、Apache files  用于存放由copy 或script 模块调用的文件 templates  用于存放 Jinja2 模板, template 模块会自动在此目录中寻找 Jinja2 模板文件 tasks  main.yml文件为入口, 用于定义此角色的任务列表, 此文件可以使用include包含其它的位于此目录的 task 文件 handlers  main.yml文件为入口, 用于定义此角色中触发条件时执行的动作 vars  main.yml文件为入口,用于定义此角色用到的变量 defaults  main.yml文件为入口, 用于为当前角色设定默认变量 meta  main.yml文件为入口,用于定义此角色的特殊设定及其依赖关系 roles : 所有的角色必须放在roles目录下, 这个目录可以自定义位置,默认的位置在 /etc/ansible/roles nginx roles # tree .  |-- nginx     |-- defaults     |-- files     |-- handlers     |-- meta     |-- tasks     |-- templates     |-- vars 创建对应的文件 # tree  roles/ roles/ |-- nginx     |-- defaults     |-- files     |-- handlers     |-- meta     |-- tasks     |   |-- group.yaml     |   |-- main.yaml     |   |-- restart.yaml     |   |-- start.yaml     |   |-- template.yaml     |   |-- user.yaml     |   |-- yum.yaml     |-- templates     |   |-- nginx.conf.j2     |-- vars  /etc/ansible/nginx_roles.yml  与 roles存放位置在同一目录。--- - hosts: k3s-cluster   become: yes   become_user: root    # 选择 调用的 roles 属性   roles:       # 调用定义好的role,存放在roles目录中。     - role: nginx /etc/ansible/roles/nginx/tasks/main.yml  入口文件 配置 task 执行顺序。- include: roles/httpd/tasks/copyfile.yml 跨 roles 调用 tasks,需要写 roles 目录级的全路径。 如: - include: group.yml - include: user.yml - include: yum.yml - include: template.yml - include: start.yml /etc/ansible/roles/nginx/tasks/group.yml  单独的 tasks 文件只写单独的内容 如下:- name: create group   group: name=nginx gid=80 执行  nginx_roles.yml  文件# ansible-playbook nginx_roles.yml  PLAY [k3s-cluster] ****************************************************************************************  TASK [Gathering Facts] **************************************************************************** ok: [10.0.8.2]  TASK [nginx : create group] *********************************************************************** changed: [10.0.8.2]  TASK [nginx : create user] ************************************************************************ changed: [10.0.8.2]  TASK [nginx : install package] ******************************************************************** changed: [10.0.8.2]  TASK [nginx : copy conf] ************************************************************************** changed: [10.0.8.2]  TASK [nginx : start service] ********************************************************************** changed: [10.0.8.2]  PLAY RECAP **************************************************************************************** 10.0.8.2   : ok=6    changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  "
  roles tags 标签在 playbook 文件中 对 roles 配置相应的 tags 。 - hosts: k3s-cluster   become: yes   become_user: root     # 选择 roles 属性   roles:     # 配置相应的 tags 用 { } 引用     - { role: nginx, tags: ["web", "nginx"] }     - { role: mysql, tags: ["db", "mysql"] }     - { role: redis, tags: ["db", "redis"] }     - { role: golang, tags: ["web", "golang"] }     - { role: vue, tags: ["web", "vue"] }        - { role: app_demo, tags: "app_demo" }  跟上面的用法一样,通过  ansible-playbook -t  参数指定 tags 进行单独调用如下指定 tags 为 web 的 role 执行,其中会依次执行nginx 、golang、vue的roles。 # ansible-playbook -t web playbook.yml "
  roles when 语句对 role 进行条件的判断. ansible_distribution_major_version == "7" --- - hosts: all   become: yes   become_user: root    # 选择 roles 属性   roles:     # 配置相应的 tags 用 { } 引用     - { role: nginx, tags: ["web", "nginx"] }     - { role: mysql, tags: ["db", "mysql"] }     - { role: redis, tags: ["db", "redis"] }     # 只针对操作系统为 Centos7 的执行     - { role: golang, tags: ["web", "golang"], when: ansible_distribution_major_version == "7" }     # 只针对操作系统为 Ubuntu20 的执行     - { role: vue, tags: ["web", "vue"], when: (ansible_distribution == "Ubuntu" and ansible_distribution_major_version == "20")}     - { role: app, tags: "app" }  extra-vars--extra-vars  执行 playboook 的时候以参数方式传入变量。# 以变量方式传参 ansible-playbook deploy.yaml --extra-vars "hosts=k3s-cluster user=ubuntu"  # 以json格式传参 ansible-playbook deploy.yaml --extra-vars "{"app_name":"nginx", "pkg_name":"vsftpd"}" # 以json文件方式传参 ansible-playbook deploy.yml --extra-vars "@test_vars.json" 附上2个Demo
  Ubuntu 安装软件,传入参数即可安装软件。 --- # 定义集群,并设置提权root, - hosts: all   become: yes   become_user: root    vars:     # 传入参数     - DEPLOY_USER: ubuntu     - APP_NAME: "{{ app_name }}"      tasks:     - name: 更新 apt-get 仓库以及缓存       apt: update_cache=yes force_apt_get=yes cache_valid_time=3600     - name: 安装 {{ APP_NAME }} 程序       apt:         name: "{{ APP_NAME }}"     # - name: "复制 {{ APP_NAME }} 配置文件"     #   copy: src="/a2020/img/data-img.jpg" data-src=./conf/vsftpd.conf dest=/etc/{{ APP_NAME }}.conf backup=yes     #   notify: restart {{ APP_NAME }} # 此处必须与handlers一致     #   # 定义标签     #   tags: copyconf     - name: "启动 {{ APP_NAME }} 服务"       service: name={{ APP_NAME }} state=started enabled=yes       # 定义标签       tags: startvsftpd              # 配置 notify 触发,修改配置文件的时候生效。   handlers:     - name: restart {{ APP_NAME }}       service: name={{ APP_NAME }} state=restarted
  发布流程,Demo中的具体实现逻辑,需要根据自己的环境来定义,此处仅做参考,如果你有什么好的建议和意见,可以扫描下面的二维码,加我微信,一起交流。 --- - hosts: all   gather_facts: no    vars:     # OSS参数     - OSS_URL: "https://repo.opendevops.cn"     - OSS_PATH: "codo/codo-api/cclib/production"     - OSS_FILE: "xxxxx_20211020181130_dispatch-service-0.0.7.jar"     - OSS_FILE_KEY: "?xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"     # 部署参数     - DEPLOY_USER: "{{ deploy_user }}" # "ubuntu"     - APP_NAME: "{{ app_name }}" # "tomcat"     - APP_DIR:  "{{ app_dir }}" # "/tmp"     - DING_URL: "{{ ding_url }}"     - DING_TOKEN: "{{ ding_token }}"    tasks:      - name: "验证 {{ inventory_hostname }} SSH 端口"       local_action: wait_for port=22 host="{{ inventory_hostname }}" search_regex=OpenSSH     - name: gather facts       setup:          - name: "钉钉 {{ ding_url }} {{ ding_token }}"       shell: "echo {{ ding_url }}"          register: print_ding_url      - name: "获取当前发布主机IP"       shell: "curl http://ip.me"          register: get_ip_addr     - name: "获取当前发布主机IP SDTOUT"         debug: var=get_ip_addr.stdout            - name: "获取当前发布主机名称"       shell: "hostname"       register: get_hostname     - name: "获取当前发布主机名称 SDTOUT"       debug: var=get_hostname      # 发布的一些动作     - name: "发布主机: {{ get_hostname.stdout }} 验证 {{ APP_NAME }} 目录"       file:         path: "{{ APP_DIR }}/{{ APP_NAME }}"         state: directory         mode: "0755"          register: create_dir     - name: "发布主机: {{ get_hostname.stdout }} 验证 {{ APP_NAME }} 目录 STDOUT"       debug: var=create_dir      - name: "发布主机: {{ get_hostname.stdout }} APP {{ APP_NAME }} 程序包下载"                   shell: "wget {{ OSS_URL }}/{{ OSS_PATH }}/{{ OSS_FILE }}"{{ OSS_FILE_KEY }}" -O {{ APP_DIR }}/{{ APP_NAME }}/{{ OSS_FILE }}"       args:         chdir: /opt/         creates: /opt/{{ OSS_URL }}/{{ OSS_FILE }}       register: download_file       #notify: restart {{ APP_NAME }}     - name: "发布主机: {{ get_hostname.stdout }} APP {{ APP_NAME }} 程序包下载 CMD"       debug: var=download_file.cmd      - name: "发布主机: {{ get_hostname.stdout }} APP {{ APP_NAME }} 改名"       shell: "new_file_name=`ls {{ APP_DIR }}/{{ APP_NAME }}/{{ OSS_FILE }} |awk -F"_" "{print $3}"` && echo ${new_file_name} && mv {{ APP_DIR }}/{{ APP_NAME }}/{{ OSS_FILE }} {{ APP_DIR }}/{{ APP_NAME }}/${new_file_name}"       args:         chdir: /opt/         creates: /opt/{{ OSS_URL }}/{{ OSS_FILE }}       register: move_file     - name: "发布主机: {{ get_hostname.stdout }} APP {{ APP_NAME }} 改名 STDOUT"       debug: var=move_file.stdout      - name: "发布主机: {{ get_hostname.stdout }} APP {{ APP_NAME }} 权限修改"       file:         path: "{{ APP_DIR }}/{{ APP_NAME }}"         state: directory         owner: "{{ DEPLOY_USER }}"         group: "{{ DEPLOY_USER }}"         recurse: yes       register: changed_permissions     - name: "APP {{ APP_NAME }} 权限修改 STDOUT"       debug: var=changed_permissions      - name: "发布主机: {{ get_hostname.stdout }} APP {{ APP_NAME }} 服务重启"             shell: "echo {{ OSS_URL }}/{{ OSS_PATH }}/{{ OSS_FILE }} -O {{ APP_DIR }}/{{ APP_NAME }}/{{ OSS_FILE }}"       register: restart_service       notify: restart {{ APP_NAME }}     - name: "发布主机: {{ get_hostname.stdout }} APP {{ APP_NAME }} 服务重启 SDTOUT"       debug: var=restart_service.cmd      # 发布完成后的验证     - name: "发布主机: {{ get_hostname.stdout }} 验证 {{ APP_NAME }} 进程状态"             shell: "echo {{ OSS_URL }}/{{ OSS_PATH }}/{{ OSS_FILE }} -O {{ APP_DIR }}/{{ APP_NAME }}/{{ OSS_FILE }}"       args:         chdir: /opt/         creates: /opt/{{ OSS_URL }}/{{ OSS_FILE }}       register: process_status     - name: "发布主机: {{ get_hostname.stdout }} 验证 {{ APP_NAME }} 进程状态 STDOUT"       debug: var=process_status.stdout            - name: "发布主机: {{ get_hostname.stdout }} 验证 {{ APP_NAME }} 服务状态"             shell: "echo {{ OSS_URL }}/{{ OSS_PATH }}/{{ OSS_FILE }} -O {{ APP_DIR }}/{{ APP_NAME }}/{{ OSS_FILE }}"       args:         chdir: /opt/         creates: /opt/{{ OSS_URL }}/{{ OSS_FILE }}       register: service_status     - name: "发布主机: {{ get_hostname.stdout }} 验证 {{ APP_NAME }} 服务状态 STDOUT"       debug: var=service_status.stdout        - name: "发布主机: {{ get_hostname.stdout }} 验证 {{ APP_NAME }} 接口状态"             shell: "curl -s -L %{http_code} {{ OSS_URL }}  | grep "Welcome!" |awk "{print $1}""       args:         chdir: /opt/         creates: /opt/{{ OSS_URL }}/{{ OSS_FILE }}       register: interface_status     - name: "发布主机: {{ get_hostname.stdout }} 验证 {{ APP_NAME }} 接口状态 STDOUT"       debug: var=interface_status.stdout    # 重启服务   handlers:     - name: restart {{ APP_NAME }}       service: name={{ APP_NAME }} state=restarted
  执行参数 # 变量 ansible-playbook -C deploy.yaml -e "ding_url=ding.opendevops.cn app_name=tomcat app_dir=/tmp/deniss deploy_user=ubuntu, ding_token=qwerty&^%FDSFBSNFXZ&^%%" # json ansible-playbook -C deploy.yaml --extra-vars "{"app_name":"tomcat", "deploy_user":"ubuntu", "app_dir":"/tmp/deniss", "ding_url":"ding.opendevops.cn", "ding_token":"qwerty&^%FDSFBSNFXZ&^%%"}"
  至此,Ansible的基础篇与进阶篇已经完结,如果你认真的阅读学习并动手实践,你一定可以写出高效的 Ansible-PlayBook脚本 !敬请期待下一篇如何基于AnsibleAPI二次开发 任务中心之AnsibleAPI篇  。

营收下跌股价腰斩资本抛弃,三只松鼠成于电商败于电商wumiancaijing。com在增长放缓的情况下,线下分销五年再造百亿的难度显然不低。但三只松鼠没有太多选择,对网红品牌而言,来得快,去得也快。本文由无冕财经(wumianca资产已破3万亿,北京银行靠什么实现业绩跨越?wumiancaijing。com三季报显示,北京银行盈利能力持续增强,经营情况持续向好,全行零售转型取得显著成效。本文由无冕财经(wumiancaijing)发布编辑陈涧设计岚昇小米为何留不住年轻高管?wumiancaijing。com年龄在35岁上下的四名前小米员工,加入酷派后成为高管,小米还出现80后合伙人离职现象,曾大胆重用80后年轻高管的小米,发生了什么?本文由无冕财经(十大致命毒蜂1非洲劲蜂即是臭名昭著的非洲杀人蜂,是一种因为人为事故而繁育出的杂交蜂种,该种昆虫毒性剧烈,可以短时间内致人死命,每年都有成千上万人因此受害。据不完全统计,在短短的几十年里,已经有次元美人录之杯莫停司空琪司空鸩九大孙女。性格刚强,外向,是司空鸩九在外的好帮手。真实身份是司空家大小姐,宗家家臣。司空显之女,司空瑶的堂姐,司空明的侄女,宗家御医医不了的徒弟。坚毅刚强,聪明率真,活十大名贵蝴蝶1金斑喙凤蝶属凤蝶科喙凤属,体长30毫米左右,两翅展约110毫米左右,前翅上各有一条弧形金绿色的斑带,后翅中央有几块金黄色的斑块,后缘有月牙形的金黄色的斑,后翅的尾状突出细长,末端最恐怖的十大蚂蚁1子弹蚁子弹蚁,蚁科,色木工蚁属,节肢动物门,主要分布在亚马逊地区的雨林中,而且样子和外貌与黄蜂的祖先相似,数百万年来几乎没有什么改变。被评为全球十大毒性最强动物之一的子弹蚁,是蚁次元美人录之换世门生空灵慧空家成员,空志明的胞妹,五元堂大小姐。泰青泰宏古田加纳的主人,年龄与念阳枭相仿。元界的空灵慧是元界的念阳枭的挚爱。曾与念阳枭有婚约,因空志明被念阳枭杀害而继任堂主之位,想拿下次元美人录之天谕动画瞳洸玄水教教徒,后接替姐姐瞳溟的职位,担任了大祭司。黑帝神器黑曜石之镜的继承者。封印时棕褐色的大眼睛,白皙的肤色,金色长发及腰。花边泡泡袖和浅紫色的蓬蓬裙,配上一双蕾丝花边厚底鞋。次元美人录之侠岚辰月因三月出生,所以才得此名。温柔大方,平易近人,外柔内刚,聪颖细腻,率真善良,一丝不苟,富有同情心。战斗中有时候也展现出犹豫怯懦的一面。坚定的朝着成为一名优秀侠岚去努力拼搏黑色短十大听觉最好的动物大蜡螟蜡螟是鳞翅目螟蛾科蜡螟亚科的昆虫,会危害毛皮和植物,常见危害蜂群的有大蜡螟和小蜡螟两种,分布遍及世界各地。在世界上大多数地方,这些蛾子能听到频率高达300kHz的声音,到目前
兴戈觅澈MT3Pro体验以一敌十,耳机的位面碾压作为一个伪音质发烧友,我体验过的耳机中,价值最高的也不过56千元,常听人说万元以下听个响千元以下听个响百元左右听个响,虽说都是响,但每个阶层都有各自不同的表现,你只不过是你能不能区椰汁和椰子水有区别吗?区别在哪呢?在现实生活中我们经常碰到椰子水和椰汁。但是大多数人只有一个笼统的概念,很多人都觉得这是同一个东西。但其实不然,今天小编就跟大家一起来了解一下,椰汁和椰子水到底有什么区别?首先外观本男女皆宜的高颜值耳机,徕声AT200一周体验前些天收到了徕声的快递,打开后发现是他们家的新品,今天就给大家分享一下我这一周对徕声AT200真无线蓝牙耳机的实际体验。话不多说,直接进入开箱篇开箱篇这是我第一次接触徕声的耳机,说白茶开箱CHOETECHPD45W充电宝充电宝作为智能手机的备用电源,基本上算是人手一个,但目前市面上流行的充电宝大多还在18W左右徘徊,高于18W的充电宝可以说很少见,今天就给大家开箱简评一下这款充电功率高达45W的C煎炸炒煮锅样样精通,79元的知吾煮汤锅实测大家好,我是白茶,今天收到米家的知吾煮汤锅,个人感觉还不错,跟大家分享分享我对这款锅的实际体验。知吾煮汤锅全名知吾煮多用可立盖汤锅,汤锅外壁采用亮白色烤漆,内壁则是常见的黑色烤漆,不做穿prada的恶魔,只当用戴尔XPS139300的时髦精最近这几天,气温回升明显,户外春光大好,感觉穿在身上的冬装显得笨重又闷热难怪身边的职场小姐姐们脸上精致的妆都发生了改变容,穿上了轻薄时髦的春装,哪怕是重新回归996的紧张生活,也能手账达人新宠,啵哩小鸟L3打印机每晚3年出生,幸福程度就翻一倍,早出生18年的我也体验了一把现代科技带来的快乐。翻译笔作为学生工具,有幸体验了一番,而今天另外一个学习工具,又有幸体验了一番。早年读书的时候最烦的就小米11的这波营销方案堪称经典12月28日晚间,小米于北京召开新品发布会,发布全新旗舰级手机小米11等新品。小米11搭载一块2K级别屏幕,并首发高通骁龙888处理器,售价3999元起。小米手机的配置方面向来是舍iPhone12系列信号不好,运营商不背锅苹果手机信号不好,好像已经成为大家公认的了,信号很好,那就不是正宗苹果了,目前5G版的iPhone12系列一样逃不过信号差的魔咒!国外苹果iPhone12ProLTE和5G网络信号工业3D打印一场仍处在初级阶段的技术革命资本实验室今日创新观察聚焦前沿科技创新与传统产业升级张珂曾记否,几年前当我们在实体店看到3D打印机打印出的一个个小玩意时,那种油然而生的新鲜与神奇感?然而,短短几年,面对这种新事物猜想2040年的年轻人将拥有怎样的生活?资本实验室今日创新观察聚焦前沿科技创新与传统产业升级王进世界变化如此之快。今天的年轻人的生活与10年前20年前年轻人的生活已经大相径庭,而与30年前相比绝对是天壤之别。科技经济与社