Ansible自动化运维工具 —— Playbook 剧本
playbooks 本身由以下各部分组成
(1)Tasks:任务,即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行
(2)Variables:变量
(3)Templates:模板
(4)Handlers:处理器,当changed状态条件满足时,(notify)触发执行的操作
(5)Roles:角色
playbook 剧本 总结
vim XXX.yaml- name: #指定play名称hosts: #指定主机组remote_user: #执行用户 gather_facts: true|false #是否收集远程主机facts信息vars: #定义变量tasks: #定义task任务列表- name: #定义task任务名称模块: #定义任务使用的模块和参数with_items: #定义循环列表when: #定义判断条件(== != >= > <= <),true则执行任务,否则不执行任务ignore_errors: true #忽略任务失败notify: #定义task任务changed状态时触发的任务名tags: #指定标签,ansible-playbook --tags 仅执行拥有指定 tags 标签的任务(always标签总会执行)handlers: #定义notify触发的任务列表
task任务 模块语法格式
模块名: 参数选项1=值 参数选项2={{变量名}} ...模块名:参数选项1: 值参数选项2: "{{变量名}}"...
with_items 和 变量 的语法格式
with_items: ["值1", "值2", "值3"]with_items: - 值1 - 值2 - 值3值为对象(键值对字段)时:
with_items: - {key1: value1, key2: value2, ...} - {key1: value3, key2: value4, ...}with_items: - key1: value1key2: value2 - key1: value3key2: value4
template模板模块
1)先要准备一个 xxx.j2 模板文件,在文件中使用 {{变量名}} 引用主机变量 或者 vars 自定义的变量 及 facts 字段的值
2)在 playbook 中的 tasks 中定义 template 模板配置 template: src=xxx.j2 dest=xxx
roles 角色 的作用?【重中之重】
把playbook剧本里的各个play看作为角色,将各个角色的tasks任务、vars变量、templates模板、files文件等内容放置到角色的目录中统一管理,需要的时候可在playbook中直接使用roles调用,所以roles可以实现playbook代码的复用。
实验
架构
192.168.80.101 ansible
192.168.80.102 被控服务器
192.168.80.103 被控服务器
/etc/ansible/hosts配置如下
cat /etc/ansible/hosts
示例1 安装httpd服务
mkdir -p /etc/ansible/playbook/ cp /etc/httpd/conf/httpd.conf /etc/ansible/playbook/httpd.conf #本机的httpd配置复制到ansible目录。可以自定义位置,在下面yaml更改对应位置即可。vim /etc/ansible/playbook/test1.yaml--- #yaml文件以---开头,以表明这是一个yaml文件,可省略。#若文件中存在多个--- 则代表有多个yaml配置文件存在于同一个文件中 - name: the first play for install apache #定义一个play的名称,可省略gather_facts: false #设置不进行facts信息收集,这可以加快执行速度,可省略hosts: webservers #指定要执行任务的被管理主机组,如多个主机组用冒号分隔remote_user: root #指定被管理主机上执行任务的用户tasks: #定义任务列表,任务列表中的各任务按次序逐个在hosts中指定的主机上执行- name: test connection #自定义任务名称ping: #使用 module: [options] 格式来定义一个任务- name: disable selinuxcommand: '/sbin/setenforce 0' #command模块和shell模块无需使用key=value格式ignore_errors: True #如执行命令的返回值不为0,就会报错,tasks停止,可使用ignore_errors忽略失败的任务。#这里需要忽略是因为如果selinux已经关闭再次关闭会返回1- name: disable selinux foreverreplace: path=/etc/selinux/config regexp="enforcing" replace="disabled"- name: disable firewalldservice: name=firewalld state=stopped enabled=no #使用 module: options 格式来定义任务,option使用key=value格式 #====================================================================================================================== # 若想要挂载光盘使用本地yum源安装(需要在控制服务器存在/etc/yum.repos.d/repo.bak/local.repo配置文件)可以省略- name: mount cdrommount: src=/dev/sr0 path=/mnt fstype=iso9660 state=mounted- name: copy local yum configuration filecopy: src=/etc/yum.repos.d/repo.bak/local.repo dest=/etc/yum.repos.d/local.repo #======================================================================================================================- name: install httpdyum: name=httpd state=latest- name: prepare httpd configuration filecopy: src=/etc/ansible/playbook/httpd.conf dest=/etc/httpd/conf/httpd.conf #这里需要一个事先准备好的/opt/httpd.conf文件notify: "restart httpd" ##如以上操作后为changed的状态时,会通过notify指定的名称触发对应名称的handlers操作- name: start apache httpdservice: name=httpd state=started enabled=yeshandlers: #handlers中定义由notify触发的任务- name: restart httpd #notify和handlers中任务的名称必须一致。注意reload不能让httpd重新加载配置文件中端口等配置,只能restartservice: name=httpd state=restarted##Ansible在执行完某个任务之后并不会立即去执行对应的handler,而是在当前play中所有普通任务都执行完后再去执行handler ##这样的好处是可以多次触发notify,但最后只执行一次对应的handler,从而避免多次重启。
//运行playbookansible-playbook test1.yaml//补充参数: -k(–ask-pass):用来交互输入ssh密码 -K(-ask-become-pass):用来交互输入sudo密码 -u:指定用户ansible-playbook test1.yaml --syntax-check #检查yaml文件的语法是否正确 ansible-playbook test1.yaml --list-task #检查tasks任务 ansible-playbook test1.yaml --list-hosts #检查生效的主机 ansible-playbook test1.yaml --start-at-task='install httpd' #指定从某个task开始运行
定义、引用变量
vim /etc/ansible/playbook/test2.yaml- name: second playhosts: dbserversremote_user: root#remote_user: zhangsan#become: yes #这三行代表 远程控制时 使用的普通用户zhangsan提升权限使用root用户#become_user: root #需要先修改sudo配置/etc/sudoers使zhangsan用户可以使用sudo提权vars: #定义变量- groupname: mysql #格式为 key: value- username: nginx- filename: /opt/123.txtgather_facts: true #可以不写 默认开启tasks:- name: create groupgroup: name={{groupname}} system=yes gid=2800 #使用 {{key}} 引用变量的值 system为默认项可以省略不写不影>响- name: create useruser: name={{username}} uid={{uid}} group={{groupname}} #uid并未写在var中,由外部命令执行时传参- name: copy file#copy: content="{{ansible_default_ipv4}}" dest={{filename}} #在setup模块中可以获取facts变量信息 这里获>取ipv4块所有信息copy: content="{{ansible_default_ipv4.address}}" dest={{filename}} #在setup模块中可以获取facts变量信息 这里获取ipv4块中address一项信息- name: modify username and groupname of filefile: path={{filename}} owner={{username}} group={{groupname}}ansible-playbook test2.yaml -e "username=nginx2" -e "uid=1234" #在命令行里定义变量 命令行内指定的参数优先级更高可以覆盖playbook var内写的变量可看到命令行的传参username覆盖了playbook中定义的变量,并且uid也传参进去,其余参数都从playbook中已经定义的var变量中取
指定远程主机sudo切换用户在上面的脚本做修改
额外注意在 /etc/ansible/hosts 组变量中是否已经指定了 用户等参数!优先级:命令行传参>host组定义>playbook中定义变量
若host组中已经规定了用户,playbook中的用户设置,become提权等可能被覆盖不会生效!
--- - hosts: dbserversremote_user: zhangsan become: yes #2.6版本以后的参数,之前是sudo,意思为切换用户运行become_user: root #指定sudo用户为root
运行前需要先在被控制的远程主机上创建zhangsan用户,并修改sudo配置使zhangsan用户可以使用sudo提权,才能在playbook中使用become 使用root用户
adduser zhangsan passwd zhangsanvim /etc/sudoerszhangsan ALL=ALL执行playbook,原先命令上附加上 -k -K参数
ansible-playbook test2.yaml -e "username=nginx3" -e "uid=1357" -k -K-k -K -k(–ask-pass):用来交互输入ssh密码 -K(-ask-become-pass):用来交互输入sudo密码 都输入zhangsan密码即可 -u:指定用户
when条件判断
在Ansible中,提供的唯一一个通用的条件判断是when指令,当when指令的值为true时,则该任务执行,否则不执行该任务。
when一个比较常见的应用场景是实现跳过某个主机不执行任务或者只有满足条件的主机执行任务
vim /etc/ansible/playbook/test3.yaml--- - name: third playhosts: allremote_user: roottasks:- name: touch filefile: path=/opt/1.txt state=touch #创建文件#command: /sbin/shutdown/ -r now #重启指令when: inventory_hostname == "192.168.80.102"#写法1 inventory_hostname为主机清单 /etc/ansible/hosts的主机名#when: ansible_default_ipv4.address != "192.168.80.103" #写法2 ansible_default_ipv4.address为gather_facts获取的主机信息 != 含义为除了103主机#when指令中的变量名不需要手动加上 {{}}ansible-playbook test3.yaml
迭代(循环) with_items
Ansible提供了很多种循环结构,一般都命名为with_items,作用等同于 loop 循环。
with_items普通取值 写法演示
vim /etc/ansible/playbook/test4.yaml#😊with_items普通取值 --- - name: fouth playhosts: dbserversremote_user: root ##############################################################################vars:myfile:- /opt/a- /opt/b- /opt/c- /opt/dtasks:- name: touch directory #✨方法1 预先编写var变量,随后赋值给with_itemswith_items: "{{myfile}}"file: path={{item}} state=directory #🧯模组 横向写法 ##############################################################################- name: touch file 1 #✨方法2 直接在with_items中定义file: #🧯模组 纵向写法path: "{{item}}"state: touchwith_items: #🔥with_items纵向写法- /root/a- /root/b- /root/c- /root/d- name: touch file 2file: #🧯模组 纵向写法path: "{{item}}"state: touchwith_items: [ /opt/aa, /opt/bb, /opt/cc, /opt/dd ] #🔥with_items横向写法with_items——值为对象(键值对字段) 写法演示
vim /etc/ansible/playbook/test5.yaml#😊with_items——值为对象(键值对字段) --- - name: fifth playhosts: dbserversremote_user: roottasks:- name: touch filewith_items: #✨with_items(键值对字段对象)横向写法- {filename: /opt/afile, username: xue, groupname: xue}- {filename: /opt/bfile, username: zhangsan, groupname: zhangsan}#当值为对象(键值对字段)引用值需要像item.filename指定对象中的某个字段file: path={{item.filename}} owner={{item.username}} group={{item.groupname}} state=touch #模块 横向写法- name: create dirwith_items: #✨with_items(键值对字段对象)纵向写法- filename: /opt/cfileusername: xuegroupname: xue- filename: /opt/dfileusername: zhangsangroupname: zhangsanfile: #模块 纵向写法path: "{{item.filename}}"owner: "{{item.username}}"group: "{{item.groupname}}"state: directory
执行playbook test4
ansible-playbook test4.yaml
执行playbook test5
由于playbook中指定了用户与组,需要在被控制的远程主机创建 adduser xue adduser zhangsan查看用户 与 组 信息(playbook中指定的{filename: /opt/afile, username: xue, groupname: xue}一定要与系统中的用户—组对应关系相匹配!不然报错)
在ansible服务器运行 ansible-playbook test5.yaml
Templates 模块
Jinja是基于Python的模板引擎。Template类是Jinja的一个重要组件,可以看作是一个编译过的模板文件,用来产生目标文本,传递Python的变量给模板去替换模板中的标记。
1)先要准备一个 xxx.j2 模板文件,在文件中使用 {{变量名}} 引用主机变量 或者 vars 自定义的变量 及 facts 字段的值
2)在 playbook 中的 tasks 中定义 template 模板配置 template: src=xxx.j2 dest=xxx1.先准备一个以 .j2 为后缀的 template 模板文件,设置引用的变量
cp /etc/httpd/conf/httpd.conf /etc/ansible/playbook/httpd.conf.j2 vim /etc/ansible/playbook/httpd.conf.j2Listen {{http_port}} #42行 ServerName {{server_name}} #95行 DocumentRoot "{{root_dir}}" #119行2.修改主机清单文件,定义变量,用于传参给模版
vim /etc/ansible/hosts[webservers] 192.168.80.102 http_port=192.168.80.102:80 server_name=www.ws1.com:80 root_dir=/var/www/html/webserver1 192.168.80.103 http_port=192.168.80.103:80 server_name=www.ws2.com:80 root_dir=/var/www/html/webserver23.编写 playbook,在其中引用第一步定义的template模版。运行时host文件变量会传给playbook yaml配置文件再传参给模版。
根据不同host ip为不同的服务器生成指定的配置文件。
vim /etc/ansible/playbook/test6.yaml- name: sixth playhosts: webserversremote_user: rootvars:- pkgname: httpdtasks:- name: install apacheyum: name=httpd state=latest#创建webserver1 webserver2文件夹(此处直接在每个主机上都创建这两个文件夹,可以指定when不同ip做更细分优化)- name: create root dirfile: state=directory path={{item}}with_items:- /var/www/html/webserver1- /var/www/html/webserver2#根据不同ip生成不同页面- name: create index.html in www.ws1.comcopy: content="<h1>this is web1</h1>" dest=/var/www/html/webserver1/index.htmlwhen: ansible_default_ipv4.address == "192.168.80.102"- name: create index.html in www.ws2.comcopy: content="<h1>this is web2</h1>" dest=/var/www/html/webserver2/index.htmlwhen: inventory_hostname == "192.168.80.103"#引用template模板,生成配置后触发restart - name: prepare configuration filetemplate: src=/etc/ansible/playbook/httpd.conf.j2 dest=/etc/httpd/conf/httpd.confnotify: "restart apache" #本处可以为reload也可以为restart。虽然触发项最后才执行,会在start后reload。#但是实测可能是由于start需要时间,reload第一次运行会报错,第二次运行httpd完全启动后才正常。#所以直接使用restart,即使未启动restart也会直接执行start不报错。#后续补充:restart 似乎也需要执行两次,应该也与触发项或是启动时间有关- name: start apacheservice: name={{pkgname}} state=started enabled=yeshandlers:- name: reload apacheservice: name={{pkgname}} state=reloadedansible-playbook test6.yaml
tags 模块
可以在一个playbook中为某个或某些任务定义“标签”,在执行此playbook时通过ansible-playbook命令使用--tags选项能实现仅运行指定的tasks。
playbook还提供了一个特殊的tags为always。作用就是当使用always作为tags的task时,无论执行哪一个tags时,定义有always的tags都会执行。vim /etc/ansible/playbook/test7.yaml- name: seventh playhosts: dbserversremote_user: roottasks:- name: create together-do.txtfile: path=/opt/together-do.txt state=touchtags:- xiaoming_do_it- xiaohong_do_it- name: create always-do.txtfile: path=/opt/always—do.txt state=touchtags:- always- name: create xiaohong-do.txtcopy: content="0721" dest=/opt/xiaohong—do.txttags:- xiaohong_do_itansible-playbook test7.yaml --tags="xiaoming_do_it" #执行 tag:xiaoming与always
ansible-playbook test7.yaml --tags="xiaohong_do_it" #执行 tag:xiaohong与always
Roles 模块
roles用于层次性、结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令引入即可。
简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并可以便捷的include它们的一种机制。roles一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中。主要使用场景代码复用度较高的情况下。roles 角色 的作用?
把playbook剧本里的各个play看作为角色,将各个角色的tasks任务、vars变量、templates模板、files文件等内容放置到角色的目录中统一管理,需要的时候可在playbook中直接使用roles调用,所以roles可以实现playbook代码的复用。
roles 的目录结构
cd /etc/ansible/ tree roles/roles/ ├── httpd/ #相当于 playbook 中的 每一个 play 主题,目录名即为角色名 │ ├── files/ #存放copy 模块或 script 模块调用的文件 │ ├── templates/ #存放template 模块调用的jinjia2 模板文件 │ ├── tasks/main.yml #定义此角色的任务列表 │ ├── handlers/main.yml #定义此角色通过notity触发条件时执行的任务列表 │ ├── vars/main.yml #定义此角色用到的自定义变量 │ ├── defaults/main.yml #定义此角色用到的设定默认变量(一般不用) │ └── meta/main.yml #定义此角色的元数据信息 └── mysql/├── files/├── templates/├── tasks/├── handlers/├── vars/├── defaults/└── meta/●files
用来存放由 copy 模块或 script 模块调用的文件。●templates
用来存放 jinjia2 模板,template 模块会自动在此目录中寻找 jinjia2 模板文件。●tasks
此目录应当包含一个 main.yml 文件,用于定义此角色的任务列表,此文件可以使用 include 包含其它的位于此目录的 task 文件。●handlers
此目录应当包含一个 main.yml 文件,用于定义此角色中触发条件时执行的动作。●vars
此目录应当包含一个 main.yml 文件,用于定义此角色用到的变量。●defaults
此目录应当包含一个 main.yml 文件,用于为当前角色设定默认变量。 这些变量具有所有可用变量中最低的优先级,并且可以很容易地被任何其他变量覆盖。所以生产中我们一般不在这里定义变量●meta
此目录应当包含一个 main.yml 文件,用于定义此角色的元数据信息及其依赖关系。
在一个 playbook 中使用 roles的步骤
(1)创建以 roles 命名的目录
mkdir /etc/ansible/roles/ -p #yum装完默认就有(2)创建全局变量目录(可选)
mkdir /etc/ansible/group_vars/ -p touch /etc/ansible/group_vars/all #文件名自己定义,引用的时候注意(3)在 roles 目录中分别创建以各角色名称命名的目录,如 httpd、mysql
mkdir /etc/ansible/roles/httpd mkdir /etc/ansible/roles/mysql(4)在每个角色命名的目录中分别创建files、handlers、tasks、templates、meta、defaults和vars目录,用不到的目录可以创建为空目录,也可以不创建
mkdir /etc/ansible/roles/httpd/{files,templates,tasks,handlers,vars,defaults,meta} mkdir /etc/ansible/roles/mysql/{files,templates,tasks,handlers,vars,defaults,meta}(5)在每个角色的 handlers、tasks、meta、defaults、vars 目录下创建 main.yml 文件,千万不能自定义文件名
touch /etc/ansible/roles/httpd/{defaults,vars,tasks,meta,handlers}/main.yml touch /etc/ansible/roles/mysql/{defaults,vars,tasks,meta,handlers}/main.yml(6)修改 site.yml 文件,针对不同主机去调用不同的角色
vim /etc/ansible/site.yml --- - hosts: webserversremote_user: rootroles:- httpd - hosts: dbserversremote_user: rootroles:- mysql(7)运行 ansible-playbook
cd /etc/ansible ansible-playbook site.yml
示例:安装分布式LNMP架构web服务器
创建工作目录
mkdir /etc/ansible/roles/nginx/{files,templates,tasks,handlers,vars,defaults,meta} -p mkdir /etc/ansible/roles/mysql/{files,templates,tasks,handlers,vars,defaults,meta} -p mkdir /etc/ansible/roles/php/{files,templates,tasks,handlers,vars,defaults,meta} -ptouch /etc/ansible/roles/nginx/{defaults,vars,tasks,meta,handlers}/main.yaml touch /etc/ansible/roles/mysql/{defaults,vars,tasks,meta,handlers}/main.yaml touch /etc/ansible/roles/php/{defaults,vars,tasks,meta,handlers}/main.yaml编辑主机列表
vim /etc/ansible/hosts[webservers] 192.168.80.101[mysqlservers] 192.168.80.102[phpservers] 192.168.80.103创建playbook,包含roles
vim /etc/ansible/playbook/lnmp.yaml- name: nginx playhosts: webserversremote_user: rootroles:- nginx- name: php playhosts: phpserversremote_user: rootroles:- php- name: mysql playhosts: mysqlserversremote_user: rootroles:- mysql

role:nginx
vars (变量)
vim /etc/ansible/roles/nginx/vars/main.yamlhttp_port: 192.168.80.101:80 http_hostname: www.xue.com root_dir: /usr/share/nginx/html php_remote: 192.168.80.103:9000 pkg: nginx service: nginxtasks (任务)
vim /etc/ansible/roles/nginx/tasks/init.yaml- name: disable firewalldservice: name=firewalld state=stopped enabled=no- name: disable selinuxshell: "/usr/sbin/setenforce 0"ignore_errors: truevim /etc/ansible/roles/nginx/tasks/main.yaml- include: "init.yaml"- name: copy nginx yum repo filecopy: src=nginx.repo dest=/etc/yum.repos.d/- name: install nginxyum: name={{pkg}} state=latest- name: copy index.phpunarchive: src=/etc/ansible/roles/nginx/files/wordpress-4.9.4-zh_CN.tar.gz dest={{root_dir}} copy=yes- name: copy nginx template configuration filetemplate: src=default.conf.j2 dest=/etc/nginx/conf.d/default.confnotify: reload nginx- name: start nginxservice: name={{service}} state=started enabled=yeshandles (task中触发器)
vim /etc/ansible/roles/nginx/handlers/main.yaml- name: reload nginxservice: name={{service}} state=reloadedfiles (文件)
vim /etc/ansible/roles/nginx/files/nginx.reponginx.repo 用于yum下载nginx。 也可以不使用这个方法用不着准备这个文件,直接yum install epel 更新epel源[nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true准备 WordPress论坛安装包 (不一定要藏这么深的文件夹,只要脚本中对应上路径即可。为了显示file文件夹的作用,就放在这里)/etc/ansible/roles/nginx/files/wordpress-4.9.4-zh_CN.tar.gztemplates(模版 用于给nginx服务器生成配置)
vim /etc/ansible/roles/nginx/templates/default.conf.j2server {listen {{http_port}};server_name {{http_hostname}};#access_log /var/log/nginx/host.access.log main;location / {root {{root_dir}};index index.php index.html index.htm;}#error_page 404 /404.html;# redirect server error pages to the static page /50x.html#error_page 500 502 503 504 /50x.html;location = /50x.html {root /usr/share/nginx/html;}# proxy the PHP scripts to Apache listening on 127.0.0.1:80##location ~ \.php$ {# proxy_pass http://127.0.0.1;#}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000#location ~ \.php$ {root {{root_dir}};fastcgi_pass {{php_remote}};fastcgi_index index.php;fastcgi_param SCRIPT_FILENAME {{root_dir}}$fastcgi_script_name;include fastcgi_params;}# deny access to .htaccess files, if Apache's document root# concurs with nginx's one##location ~ /\.ht {# deny all;#} }
role:mysql
vars (变量)
vim /etc/ansible/roles/mysql/vars/main.yamlhttp_port: 192.168.80.101:80 http_hostname: www.xue.com root_dir: /usr/share/nginx/html php_remote: 192.168.80.103:9000 pkg: nginx service: nginxtasks (任务)
vim /etc/ansible/roles/mysql/tasks/init.yaml- name: disable firewalldservice: name=firewalld state=stopped enabled=no- name: disable selinuxshell: "/usr/sbin/setenforce 0"ignore_errors: truevim /etc/ansible/roles/mysql/tasks/main.yaml- name: yum uninstall mariadb*yum: name=mariadb* state=absent- name: wget mysqlshell: wget -i -c http://repo.mysql.com/mysql57-community-release-el7-11.noarch.rpm -P /etc/yum.repos.d- name: rpm mysql57-community-releaseshell: rpm -ivh /etc/yum.repos.d/mysql57-community-release-el7-11.noarch.rpmignore_errors: True- name: turn off yum gpgcheckreplace: path=/etc/yum.repos.d/mysql-community.repo regexp='gpgcheck=1' replace='gpgcheck=0'- name: yum install mysqlyum: name=mysql-server- name: start and enable mysqlservice: enabled=true name=mysqld.service state=started- name: get passwd from logshell: grep "password" /var/log/mysqld.log | awk 'NR==1{print $NF}'register: mysql_password #将获取的密码导入到mysql_password的变量中 - name: echo passwddebug: msg: "{{ mysql_password }}" #输出变量mysql_password的值 #grep "password" /var/log/mysqld.log #在日志文件中找出root用户的初始密码 #2021-07-31T07:55:00.366359Z 1 [Note] A temporary password is generated for root@localhost: ga7%<d<0*jD& #grep "password" /var/log/mysqld.log | awk '{print $NF}'#临时密码修改 - name: grant locationshell: mysql --connect-expired-password -uroot -p"{{ mysql_password['stdout'] }}" -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'Admin@123';"ignore_errors: True #授予root用户所有权限 - name: grant optionshell: mysql --connect-expired-password -uroot -pAdmin@123 -e "grant all privileges on *.* to 'root'@'%' identified by 'Admin@123456' with grant option;"ignore_errors: True #创建wordpress数据库 - name: create databaseshell: mysql --connect-expired-password -uroot -pAdmin@123 -e "create database wordpress;"ignore_errors: True #赋予mywordpress用户访问wordpress的权限(本地权限与远程权限) - name: grantshell: mysql --connect-expired-password -uroot -pAdmin@123 -e "grant all on wordpress.* to 'admin'@'%' identified by 'mywordpress@123456';"ignore_errors: True - name: grantshell: mysql --connect-expired-password -uroot -pAdmin@123 -e "grant all on wordpress.* to 'admin'@'localhost' identified by 'mywordpress@123456';"ignore_errors: True #刷新权限 - name: flush privilegesshell: mysql --connect-expired-password -uroot -pAdmin@123 -e 'flush privileges;'ignore_errors: True- name: yum uninstall mariadb*yum: name=mysql57-community-release-el7-11.noarch.rpm state=absent#为了防止每次yum操作都会自动更新,卸载这个软件
role:php
vars (变量)
vim /etc/ansible/roles/php/vars/main.yamltimezone: Asia/Shanghai user_name: php http_port: 192.168.80.103:9000 nginx_addr: 192.168.80.101 root_dir: /usr/share/nginx/html service: php-fpmtasks (任务)
vim /etc/ansible/roles/php/tasks/init.yaml- name: disable firewalldservice: name=firewalld state=stopped enabled=no- name: disable selinuxshell: "/usr/sbin/setenforce 0"ignore_errors: truevim /etc/ansible/roles/php/tasks/main.yaml- include: "init.yaml"- name: install yum reposhell: "rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm && rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm"ignore_errors: True- name: install phpwith_items:- php72w- php72w-cli- php72w-common- php72w-devel- php72w-embedded- php72w-gd- php72w-mbstring- php72w-pdo- php72w-xml- php72w-fpm- php72w-mysqlnd- php72w-opcacheyum: name={{item}}- name: create php useruser: name={{user_name}}- name: crate web root dirfile: name={{root_dir}} state=directory- name: copy index.phpunarchive: src=/etc/ansible/roles/nginx/files/wordpress-4.9.4-zh_CN.tar.gz dest={{root_dir}} copy=yes- name: modify php configuration filereplace: path=/etc/php.ini regexp=";date.timezone =" replace="date.timezone = Asia/Shanghai"notify: reload php- name: modify username and groupname in www.confreplace: path=/etc/php-fpm.d/www.conf regexp="apache" replace="{{user_name}}"notify: reload php- name: modify listen addr in www.confreplace: path=/etc/php-fpm.d/www.conf regexp="127.0.0.1:9000" replace="{{http_port}}"notify: reload php- name: modify allowed client in www.confreplace: path=/etc/php-fpm.d/www.conf regexp="127.0.0.1" replace="{{nginx_addr}}"notify: reload php- name: start phpservice: name={{service}} state=started enabled=yeshandles (task中触发器)
vim /etc/ansible/roles/php/handlers/main.yaml- name: reload phpservice: name={{service}} state=reloaded
配置密钥对验证 免交互登录
ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsayum install -y sshpass sshpass -p '密码' ssh-copy-id -o StrictHostKeyChecking=no root@192.168.80.102 sshpass -p '密码' ssh-copy-id -o StrictHostKeyChecking=no root@192.168.80.102 sshpass -p '密码' ssh-copy-id -o StrictHostKeyChecking=no root@192.168.80.103执行
ansible-playbook /etc/ansible/playbook/lnmp.yaml访问http://192.168.80.101/wordpress/index.php
故障处理
出现这种情况,由于是分布式部署lnmp,与单机不同,分布式部署必须在php服务器和nginx服务器上都存在相同的html目录以及其中的内容。
本次只在NGINX的html存放了WordPress论坛文件,而没有创建php服务器的文件夹,导致出错
相关文章:
Ansible自动化运维工具 —— Playbook 剧本
playbooks 本身由以下各部分组成 (1)Tasks:任务,即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行 (2)Variables:变量 (3)Templates:模…...
第二章:多态
系列文章目录 文章目录 系列文章目录前言多态的概念概念 多态的定义及实现多态的构成条件虚函数虚函数的重写C11 override 和 final重载、覆盖(重写)、隐藏(重定义)的对比 抽象类概念接口继承和实现继承 多态的原理虚函数表多态的原理动态绑定与静态绑定 单继承和多继承关系的虚…...
C++面向对象设计基础
一般类、&、const、模板、友元函数、操作符重载基本用法及实现 complex.h #ifndef COMPLEX_H #define COMPLEX_H #include<ostream> using namespace std;template<typename T> class Complex{public:Complex():re(0),img(0){}// 为什么构造函数不能传引用&a…...
Linux定时运行sh脚本,如果sh文件已经在运行,则忽略本次运行
需求来源 我需要linux的crontab定期每10分钟运行lan.sh脚本。但由于lan.sh运行需要较长时间,有时超过10分钟。这样会导致系统多次运行lan.sh脚本,引发运行堆积,导致一些非必要的错误。 解决方法 解决方法是写一个脚本,如果lan.…...
SpringBoot项目中的web安全防护
最近这个月公司对项目进行了几次安全性扫描,然后扫描出来了一些安全漏洞,所以最近也一直在修复各种安全漏洞,还有就是最近在备考软考高级系统架构设计师,也刚好复习到了网络安全这一个章节,顺便将最近修复的安全漏洞总…...
stm32和python串口数据收发
1-1 串口发送端(stm32) 1字符串发送 void USART_SendData(USART_TypeDef* USARTx, uint16_t Data) {/* Check the parameters */assert_param(IS_USART_ALL_PERIPH(USARTx));assert_param(IS_USART_DATA(Data)); /* Transmit Data */USARTx->DR (D…...
无涯教程-jQuery - Dropable移动函数
Drop-able 功能可与JqueryUI中的交互一起使用。此功能可在任何DOM元素上启用可放置功能。 Drop able - 语法 $( "#droppable" ).droppable(); Drop able - 示例 以下是一个简单的示例,显示了drop-able的用法- <html><head><title>…...
【Python】Web学习笔记_flask(4)——钩子函数
钩子函数可以用来注册在请求处理的不同阶段执行出 Flask的请求钩子指的是在执行视图函数前后执行的一些函数, 之前是有4种,但是 before_first_request已经被删除了,使用时会报错 before_request:在每次请求前执行,…...
JavaScript 原型链解析,宏任务和微任务
目录 什么是原型链? 原型与构造函数 原型链的工作原理 实例:理解原型链 宏任务(Macro Task) 微任务(Micro Task) 什么是原型链? JavaScript 是一门基于原型的语言,而原型链是…...
05|Oracle学习(UNIQUE约束)
1. UNIQUE约束介绍 也叫:唯一键约束,用于限定数据表中字段值的唯一性。 1.1 UNIQUE和primary key区别: 主键/联合主键每张表中只有一个。UNIQUE约束可以在一张表中,多个字段中存在。例如:学生的电话、身份证号都是…...
glide加载content://com.android.contacts图片源码粗略梳理
获取链路是这样的; UriLoader类里定义了协议头: 里面有个内部类StreamFactory: 通过StreamLocalUriFetcher类的loadResource方法获取InputStream然后把流转换成为图片; 在这里作个草稿笔记给自己看...
【机器学习】Feature Engineering and Polynomial Regression
Feature Engineering and Polynomial Regression 1. 多项式特征2. 选择特征3. 缩放特征4. 复杂函数附录 首先,导入所需的库: import numpy as np import matplotlib.pyplot as plt from lab_utils_multi import zscore_normalize_features, run_gradien…...
Rust- 变量绑定
In Rust, you bind values to a variable name using the let keyword. This is often referred to as “variable binding” because it’s like binding a name to a value. Here’s a simple example: let x 5;In this example, x is bound to the value 5. By default, …...
向“数”而“深”,联想凌拓的“破局求变”底气何来?
前言:要赢得更多机遇,“破局求变”尤为重要。 【全球存储观察 | 热点关注】2019年2月25日,承袭联想集团与NetApp的“双基因”,联想凌拓正式成立。历经四年多的发展,联想凌拓已成为中国企业级数据管理领域的…...
pytorch实战-图像分类(二)(模型训练及验证)(基于迁移学习(理解+代码))
目录 1.迁移学习概念 2.数据预处理 3.训练模型(基于迁移学习) 3.1选择网络,这里用resnet 3.2如果用GPU训练,需要加入以下代码 3.3卷积层冻结模块 3.4加载resnet152模 3.5解释initialize_model函数 3.6迁移学习网络搭建 3.…...
b 树和 b+树的理解
项目场景: 图灵奖获得者(Niklaus Wirth )说过: 程序 数据结构 算法, 也就说我们无时无刻 都在和数据结构打交道。 只是作为 Java 开发,由于技术体系的成熟度较高,使得大部分人认为࿱…...
正则表达式 —— Awk
Awk awk:文本三剑客之一,是功能最强大的文本工具 awk也是按行来进行操作,对行操作完之后,可以根据指定命令来对行取列 awk的分隔符,默认分隔符是空格或tab键,多个空格会压缩成一个 awk的用法 awk的格式…...
国芯新作 | 四核Cortex-A53@1.4GHz,仅168元起?含税?哇!!!
创龙科技SOM-TLT507是一款基于全志科技T507-H处理器设计的4核ARM Cortex-A53全国产工业核心板,主频高达1.416GHz。核心板CPU、ROM、RAM、电源、晶振等所有元器件均采用国产工业级方案,国产化率100%。 核心板通过邮票孔连接方式引出MIPI CSI、HDMI OUT、…...
【MyBatis】 框架原理
目录 10.3【MyBatis】 框架原理 10.3.1 【MyBatis】 整体架构 10.3.2 【MyBatis】 运行原理 10.4 【MyBatis】 核心组件的生命周期 10.4.1 SqlSessionFactoryBuilder 10.4.2 SqlSessionFactory 10.4.3 SqlSession 10.4.4 Mapper Instances 与 Hibernate 框架相比&#…...
三、线性工作流
再生产的各个环节,正确使用gamma编码及gamma解码,使得最终得到的颜色数据与最初输入的物理数据一致。如果使用gamma空间的贴图,在传给着色器前需要从gamma空间转到线性空间。 如果不在线性空间下进行渲染,会产生的问题:…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...
云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...


















