运维工作

image.png
Ansible Architecture

image.png
运维日常任务:发布--变更--故障处理
准备节点,安装操作系统
服务器的选型:CPU/内存/核心...
IDC机房(扛服务器):上架
系统安装工具:PXE/Cobbler...
云:虚拟节点(中小企业)--运行实例/按需买资源
部署应用程序
每个节点的任务:接入/负载/处理/存储...
安装软件/按需修改配置文件/启动服务
每个节点负载的业务(业务系统)
发布新版本(业务系统)
故障处理
依赖关系/环境
配置文件如何改--测试环境--业务放上去--压测--正常情况--上线
前任留下的系统--微调参数
各个应用配置文件--文档化 or 配置系统(自动化)
持续集成CI:新模块(业务的一部分)替换旧模块,测试是否可以正常运行
持续交付CD
持续部署CD
自动化--DevOps--AIOps:宜信AIOps
Jenkins
Ansible
node1 192.168.1.5 管控节点
node2 192.168.1.6 管控节点
node3 192.168.1.8 ansible节点
vim /etc/hosts
192.168.1.5 node1.fgq.com
192.168.1.6 node2.fgq.com
192.168.1.8 node3.fgq.com
node3
yum info ansible|less
yum -y install ansible
vim /etc/ansible/ansible.cfg
vim /etc/ansible/hosts
命令行/剧本--进行管控
ansible -h
Options
-m MODULE_NAME 模块名
-a MODULE_ARGS 模块参数
-C 检查 干跑一边,仅仅为了检查是否有误
-f FORKS ssh:默认一批管控5个主机
-i INVENTORY 主机清单
--list-hosts 列出匹配到的主机
--syntax-check 检查playbook的语法
-t TREE 日志输出至哪个目录下
Connection Options
-c CONNECTION 链接方式,默认smart,或指定local(本地连接)
--private-key=PRIVATE_KEY_FILE 使用密钥连接目标主机
-u REMOTE_USER 连接用的用户名,默认none
Privilege Escalation Options
其他用户操作需要切换身份--sudo/su...
node3需要连接node1和node2,使用密钥连接
ssh-keygen -t rsa -P '' 生成私钥文件
ssh-copy-id -i .ssh/id_rsa.pub root@node1.fgq.com
设定管理员远程连接另外一台主机的方式
ssh-copy-id -i .ssh/id_rsa.pub root@node2.fgq.com
如果管控自己,也可以向自己发送密钥文件
vim /etc/ansible/hosts 定义被管理的主机
文件最后添加信息即可
[websrvs]
192.168.1.5
192.168.1.6
[dbsrvs]
192.168.1.6
进行管控
ansible all 管控所有
ansible 192.168.1.5
ansible node*.fgq.com 正则/通配符
ansible all -m ping -C
ansible all -m ping --list-host
ansible all -m ping
ansible-doc -h
ansible-doc -a 显示所有模块/插件
ansible-doc -l 显示可用模块
ansible-doc -s group/... 显示某个插件的playbook定义
有"="必须指定,无"="有默认值
name=
state present--创建;absent--删除;不写--默认present
system yes--系统组;不写--默认根据id号判定(999以内--yes)
定义期望其所处的目标状态:如文件必须存在,且属性必须是我们定义的
操作必须是幂等的:次幂=次方--重复数次的结果是相等的
ansible all -m group -a "gid=3000 name=mygrp state=present system=no"
"changed": true, 已经创建
node2和node3:tail -1 /etc/group 显示创建
ansible all -m group -a "gid=3000 name=mygrp state=absent"
node2和node3:tail -1 /etc/group 显示删除
node2和node3:tail /var/log/messages ansible操作记录日志
ansible-doc -s user
move_home 原家目录有文件,yes则创建新家目录时,把原家目录文件移过来
generate_ssh_key 创建一对密钥,原有密钥,则不会覆盖,重新创建文件
group
groups 附加组
ansible all -m group -a "gid=3000 name=mygrp state=present system=no"
ansible all -m user -a "uid=5000 name=test state=present groups=mygrp shell=/bin/tcsh"
node2和node3:id test;tail -1 /etc/passwd 创建ok
ansible-doc -s copy
src 有/,则复制/下面的内容;无/,则负责目录本身
content src为空,把生成的文件内容,复制到远程其他主机
remote_src originating/master machine
ansible all -m copy -a "src=/etc/fstab dest=/tmp/fatab.ansible mode=600"
不指定group,owner,默认当前用户
node2和node3:ls -l /tmp/
ansible all -m copy -a "src=/etc/pam.d/ dest=/app/"
node2和node3:ls /app
ansible all -m copy -a "src=/etc/pam.d/ dest=/app/" -C
在执行一次,结果还是相同,没有变化--幂等
ansible all -m copy -a "content='hello world\n' dest=/app/hello.txt"
node2和node3:cat /app/hello.txt; ls -l /app/hello.txt
ansible all -m copy -a "content='hello world\n' dest=/app/hello.txt owner=test group=mygrp"
node2和node3:ls -l /app/hello.txt
ansible-doc -s fetch 从远程主机复制文件到本机
fail_on_missing 文件不存在,就报错
ansible-doc -s command 在远程主机执行命令
chdir 切换至某一目录下进行命令
ansible all -m command -a "ifconfig" 直接给出命令即可
ansible all -m command -a "chdir=/app mkdir hello" 创建目录
ansible all -m command -a "chdir=/app mkdir hello" -C
显示FAILED,再次执行此命令会失败,本身不幂等
ansible all -m command -a "chdir=/app/hello touch hello.txt"
node2和node3:ls /app/hello
ansible all -m command -a "echo qianggedu | passwd --stdin test"
打印字符串,没有管道传输创建口令--没有指明shell来解析
ansible-doc -s shell 基于shell本身执行
ansible all -m shell -a "echo qianggedu | passwd --stdin test" 口令创建成功
ansible-doc -s file 设定文件属性
state directory创建一个目录/link创建符号链接(文件存在才可以,设定属性)
src 创建符号链接
path= 目标文件是...
ansible all -m file -a "path=/app/hi state=directory" 创建目录
ansible all -m copy -a "src=/etc/fstab dest=/app/fstab.ansible"
ansible all -m file -a "src=/app/fstab.ansible path=/app/fstab.link state=link"
path=符号链接文件 state=link
node2和node3:ll /app
ansible-doc -s cron 计划任务
ansible all -m cron -a "minute=*/3 job='/usr/sbin/update ntp1.aliyun.com &> /dev/null'"
每3分钟与"ntp1.aliyun.com"同步一次时间
node2和node3:crontab -l 显示"Ansible: None"--没有设定名字
ansible all -m cron -a "minute=*/3 job='/usr/sbin/update ntp1.aliyun.com &> /dev/null' state=absent name=None"
最好设定时指明名称--可明确哪些是有ansible生成的
node2和node3:crontab -l 已经删除
ansible-doc -s yum
ansible all -m yum -a "name=nginx state=installed"
node2和node3:rpm -q nginx
ansible all -m yum -a "name=nginx state=installed" -C
再执行一次,显示已经安装--幂等"rc": 0
ansible-doc -s service
ansible all -m service -a "name=nginx state=started" 启动服务
node2和node3:ss -ntlp
ansible all -m service -a "name=nginx state=stopped" 停止服务
node2和node3:ss -ntlp
ansible-doc -l|less
dnf Manages packages with the `dnf` package manager 可能取代yum
easy_install Installs Python libraries
filesystem Makes a filesystem
firewalld Manage arbitrary ports/services with firewalld
...
ansible-doc -s script
vim /app/test.sh
#!/bin/bash
echo "Ansible Script" > /app/ansible.txt
ansible all -m script -a "/app/test.sh"
node2和node3:cat /app/ansible.txt
node1 192.168.1.5 管控节点
node2 192.168.1.6 管控节点
node3 192.168.1.8 ansible节点
剧本--进行管控:Playbook
node3
ansible-playbook -h
--list-hosts
--list-tags 指明仅执行哪些任务(排除标签:符合标签的--不执行;不符合标签的--执行)
--list-tasks
--step 单步执行,确定完一步,在执行下一步
--syntax-check 检查语法,不会真正执行
-e EXTRA_VARS 调用参数
-t TAGS 仅执行一部分内容
mkdir playbooks;cd playbooks/
vim first.yaml
- hosts: all
remote_user: root
tasks:
- name: install redis
yum: name=redis state=latest
- name: start redis
service: name=redis state=started
ansible-playbook --list-tasks --list-host first.yaml
ansible-playbook --syntax-check first.yaml
ansible-playbook -C first.yaml
按任务顺序在所有主机上执行,若某个任务在某台主机上卡主了,则其他主机也都不能完成任务
ansible-playbook first.yaml
显示:Gathering Facts 收集变量信息
node1和node2:ss -ntl 127.0.0.1:6379 监听在本机,不合适
ansible 192.168.1.5 -m fetch -a "src=/etc/redis.conf dest=./" 从远程主机复制文件到本机
cp 192.168.1.5/etc/redis.conf .
rm -rf 192.168.1.5/
vim redis.conf
bind 0.0.0.0
# requirepass foobared
requirepass qianggedu
vim first.yaml
- hosts: all
remote_user: root
tasks:
- name: install redis
yum: name=redis state=latest
- name: copy config file 新添加
copy: src=/root/playbooks/redis.conf dest=/etc/redis.conf owner=redis
- name: start redis
service: name=redis state=started
ansible-playbook -C first.yaml
ansible-playbook first.yaml
node1和node2:ss -ntl 127.0.0.1:6379; vim /etc/redis 仍监听在本机
但bind和requirepass信息已经变更,此时需要handlers(条件触发器)
cp first.yaml second.yaml
vim second.yaml
- hosts: all
remote_user: root
tasks:
- name: install redis
yum: name=redis state=latest
- name: copy config file
copy: src=/root/playbooks/redis.conf dest=/etc/redis.conf owner=redis
notify: restart redis 新添加
tags: configfile 新添加
- name: start redis
service: name=redis state=started
handlers: 新添加
- name: restart redis
service: name=redis state=restarted
vim redis.conf
requirepass qianggedu.com 配置需要发生改变才会引起触发器
ansible-playbook -C second.yaml 显示可执行全部任务
ansible-playbook -t configfile -C second.yaml 只执行其中一部分信息
ansible-playbook -t configfile second.yaml
node1和node2:ss -ntl 显示监听地址:*:6379
(不操作:yum info facter; yum -y install facter; facter -p 获取环境信息)
ansible-doc -s setup 可用获取各个主机的环境变量/信息
filter 过滤--指定自己想要的信息
ansible 192.168.1.5 -m setup|less 获取某主机所有的额环境信息
ansible 192.168.1.5 -m setup -a "filter=ansible_default_ipv4" 部分信息
vim third.yaml
- hosts: all
remote_user: root
tasks:
- name: copy file
copy: content={{ ansible_env }} dest=/tmp/ansible.env
{{ ansible_env }} 变量引用创建文件--facts--可直接引用
ansible-playbook --syntax-check third.yaml
ansible-playbook -C third.yaml
ansible-playbook third.yaml
node1和node2:cat /tmp/ansible.env
vim fourth.yaml
- hosts: all
remote_user: root
tasks:
- name: install {{ pkgname }} 或直接写packagename
yum: name={{ pkgname }} state=latest
ansible-playbook -e pkgname=varnish -C fourth.yaml 显示可执行
-e pkgname=varnish 调用参数
ansible websrvs --list-host 显示某个组内的所有主机
ansible dbsrvs --list-host
vim /etc/ansible/hosts
[websrvs]
node[1:3].fgq.com
ansible websrvs --list-host 显示如下
hosts (3):
node1.fgq.com
node2.fgq.com
node3.fgq.com
vim users.yaml
- hosts: all
remote_user: root
tasks:
- name: add user
user: name=csn system=no state=present
- name: set password
shell: echo qianggedu | passwd --stdin csn
vim /etc/ansible/hosts
[websrvs]
192.168.1.5
192.168.1.6
ansible-playbook --syntax-check users.yaml
ansible-playbook -C users.yaml
ansible-playbook users.yaml
ssh -l csn 192.168.1.5
exit
vim /etc/ansible/hosts
[websrvs]
192.168.1.5 ansible_ssh_user=csn ansible_ssh_pass=qianggedu
192.168.1.6 ansible_ssh_user=csn ansible_ssh_pass=qianggedu
ansible websrvs -m ping
ansible websrvs -m command -a "whoami"
ansible dbsrvs -m command -a "whoami"
dbsrvs继承了websrvs的用户设置--192.168.1.5/6 csn
vim /etc/ansible/hosts
[websrvs]
192.168.1.5 ansible_ssh_user=csn ansible_ssh_pass=qianggedu
192.168.1.6 ansible_ssh_user=csn ansible_ssh_pass=qianggedu
[dbsrvs]
192.168.1.5 ansible_ssh_user=root
192.168.1.6 ansible_ssh_user=root
ansible websrvs -m command -a "whoami"
ansible dbsrvs -m command -a "whoami"
websrvs继承了dbsrvs的用户设置--192.168.1.5/6 root
vim /etc/ansible/hosts
[websrvs]
192.168.1.5
192.168.1.6
[websrvs:vars] 定义组都可以引用的变量
http_port=8080
vim vars.yaml
- hosts: websrvs
remote_user: root
vars: 必须加--否则pbvar--an undefined variable
- pbvar: playbook variable testing
tasks:
- name: command line variables
copy: content={{ cmdvar }} dest=/tmp/cmd.var
- name: playbook variables
copy: content={{ pbvar }} dest=/tmp/pb.var
- name: host iventory variables
copy: content={{ http_port }} dest=/tmp/hi.var
vim /etc/ansible/hosts
[websrvs]
192.168.1.5 http_port=8080
192.168.1.6 http_port=10080
ansible-playbook -e cmdvar="command_line_variable_testing" vars.yaml -C
node1和node2:cat /tmp/*.var ...8080/10080...
ansible-doc -s template 显示"Templates a file out to a remote server"
yum info python-jinja2
cp redis.conf{,.j2}
ansible 192.168.1.5 -m setup |less
ansible 192.168.1.5 -m setup -a "filter=ansible_ens34"
vim redis.conf.j2
/^bind
bind {{ ansible_ens34.ipv4.address }}
vim template.yaml
- hosts: all
remote_user: root
tasks:
- name: install config file
template: src=/root/playbooks/redis.conf.j2 dest=/tmp/redis.conf
ansible-playbook template.yaml -C
ansible-playbook template.yaml
node1和node2:vim /tmp/redis.conf bind的IP自动变为各主机自己的IP
ansible all -m service -a "name=nginx state=stopped" 关闭nginx
node1和node2:ss -ntl 80端口消失
vim /etc/ansible/hosts
[websrvs] 确保不变即可
192.168.1.5 http_port=8080
192.168.1.6 http_port=10080
vim mylisten.conf
Listen {{ http_port }} 调用主机清单中的变量
vim httpd.yaml
- hosts: websrvs
remote_user: root
tasks:
- name: install httpd
yum: name=httpd state=latest
- name: install config file
template: src=/root/playbooks/mylisten.conf dest=/etc/httpd/conf.d/mylisten.conf
- name: start httpd
service: name=httpd state=started
ansible-playbook -C httpd.yaml
ansible-playbook httpd.yaml
node1和node2:ss -ntl 80/8080/10080端口
ansible 192.168.1.5 -m setup -a "filter=ansible_processor_*"
nginx的进程数小于或等于cpu核心数
参考文档
ansible 192.168.1.5 -m setup -a "filter=ansible_distribution*"
ansible 192.168.1.5 -m setup -a "filter=ansible_os*" RedHat
ansible 192.168.1.5 -m setup -a "filter=ansible_pkg*" yum
vim os.yaml
- hosts: all
remote_user: root
tasks:
- name: install httpd
yum: name=httpd state=latest
when: ansible_os_family == "RedHat" 条件
- name: install apache2
yum: name=apache2 state=latest
when: ansible_os_family == "Debian" 条件
ansible-playbook -C os.yaml
skipping条件不满足
vim iter.yaml
- hosts: all
remote_user: root
tasks:
- name: install {{ item }} package
yum: name={{ item }} state=latest
with_items:
- nginx
- tomcat
- mariadb-server
- redis
ansible-playbook -C iter.yaml
vim iter.yaml
- hosts: all
remote_user: root
tasks:
- name: install {{ item }} package 迭代
yum: name={{ item }} state=latest
with_items: 迭代
- tomcat
- tomcat-admin-webapps
- tomcat-webapps
ansible-playbook -C iter.yaml
ansible-playbook iter.yaml
node1和node2:rpm -q tomcat;rpm -q tomcat-admin-webapps
迭代另外一种方法--字典,参考文档
剧本--进行管控:Playbook
relose
cd playbooks/
less /etc/ansible/ansible.cfg
#roles_path = /etc/ansible/roles
mkdir -pv /etc/ansible/roles/nginx/{files,templates,tasks,vars,handlers,meta,default}
vim /etc/ansible/roles/nginx/tasks/main.yml
- name: install nginx
yum: name=nginx state=latest
when: ansible_os_family == "RedHat"
vim nginx.yaml (在/root/playbooks/下)
- hosts: websrvs
remote_user: root
roles:
- nginx
ansible-playbook --syntax-check nginx.yaml
ansible-playbook -C nginx.yaml
ansible all -m setup -a "filter=ansible_fqdn" 显示主机名
vim /etc/ansible/roles/nginx/templates/vhost1.conf.j2 模板文件.j2结尾
server {
listen 80;
server_name {{ ansible_fqdn }};
location / {
root "/ngxdata/vhost1";
}
}
vim /etc/ansible/roles/nginx/tasks/main.yml
- name: install nginx
yum: name=nginx state=latest
when: ansible_os_family == "RedHat"
- name: install config file 新添加的任务
template: src=vhost1.conf.j2 dest=/etc/nginx/conf.d/vhost1.conf
ansible-playbook -C nginx.yaml
vim /etc/ansible/roles/nginx/tasks/main.yml
- name: install nginx
yum: name=nginx state=latest
when: ansible_os_family == "RedHat"
- name: install config file
template: src=vhost1.conf.j2 dest=/etc/nginx/conf.d/vhost1.conf
tags: conf
notify: restart nginx 触发条件重启--handlers
- name: install site home dir
file: path={{ ngx_root_dir }} state=directory
- name: install index page
copy: src=index.html dest={{ ngx_root_dir }}/
- name: start nginx 正常情况启动
service: name=nginx state=started
vim /etc/ansible/roles/nginx/handlers/main.yml
- name: restart nginx 触发条件重启
service: name=nginx state=restarted
vim /etc/ansible/roles/nginx/vars/main.yml
ngx_root_dir: /ngxdata/vhost1
定义变量时,用字典--即前面不加杠"-"
/ngxdata/vhost1使用{{ ngx_root_dir }}变量代替
一旦目录变化,就可自动变化,不用再手动更改
vim /etc/ansible/roles/ngin/files/index.html
<h1>Vhost1 Website</h1>
ansible all -m service -a "name=httpd state=stopped"
node1和node2: ss -ntl 80端口消失
ansible-playbook -C nginx.yaml
ansible-playbook nginx.yaml
node1和node2: ss -ntl 80端口出现
图形界面:firefox node1.fgq.com; firefox node1.fgq.com ok可访问
vim roles/nginx/templates/vhost1.conf.j2
server {
listen 8080; 把80替换为8080
server_name {{ ansible_fqdn }};
location / {
root "/ngxdata/vhost1";
}
}
ansible-playbook -t conf playbooks/nginx.yaml -C
ansible-playbook -t conf playbooks/nginx.yaml 80和8080端口
浏览器:http://192.168.1.5:8080/;http://192.168.1.6:8080/ 可访问
grep -v "^#" /etc/ansible/ansible.cfg |grep "\[.*\]"
vim /etc/ansible/ansible.cfg
#forks = 5 5-->10
#module_name = command
ansible all -a "ifconfig" 省略 -m command
#cache_plugins = /usr/share/ansible/plugins/cache
ansible手动操作的命令--变为任务计划
ansible-pull -h 从远程ansible服务器获取ansible配置文件,应用到本地
要实现ansible-pull功能,需要第三方插件ansible-vcs
但是此时,不要再使用ansible了,建议使用puppet/saltstack
lnat:nginx--tomcat+httpd
tomcat的会话保存在redis中--msm
node1:192.168.1.9
node2:192.168.1.5
node3:192.168.1.6
node4:192.168.1.8
时间同步服务
名称解析:所有主机都可以基于某个dns服务器完成主机名解析(做成roles)--此处不深究
vim /etc/hosts
192.168.1.9 node1.fgq.com
192.168.1.5 node2.fgq.com
192.168.1.6 node3.fgq.com
192.168.1.8 node4.fgq.com
node4
yum -y install ansible
vim /etc/ansible/hosts
文件最下面添加主机清单信息
[lb]
node1.fgq.com
[tcsrvs]
node[2:3].fgq.com
ssh-keygen -t rsa -P '' 生成密钥
无密钥认证到3个主机
for i in {1..3};do ssh-copy-id -i .ssh/id_rsa.pub root@node$i.fgq.com;done
ansible all --list-host
ansible lb --list-host
ansible tcsrvs --list-host
mkdir -pv /etc/ansible/roles/{nginx,tomcat}/{files,tasks,handlers,templates,vars,meta,default}
vim /etc/ansible/roles/nginx/tasks/main.yml
- name: install nginx
yum: name=nginx state=latest
when: ansible_os_family == "RedHat"
- name: install conf
copy: src=lb.conf dest=/etc/nginx/conf.d/
tags: conf
notify: restart nginx
- name: start nginx
service: name=nginx state=started enabled=yes
vim /etc/ansible/roles/nginx/handlers/main.yml
- name: restart nginx
service: name=nginx state=restarted
vim /etc/ansible/roles/nginx/files/lb.conf
upstream tcsrvs {
server node2.fgq.com:8080;
server node3.fgq.com:8080;
}
server {
listen 80;
server_name www.fgq.io;
location / {
proxy_pass http://tcsrvs;
}
}
vim /etc/hosts(所有节点都操作)
192.168.1.9 node1.fgq.com www.fgq.io
192.168.1.5 node2.fgq.com
192.168.1.6 node3.fgq.com
192.168.1.8 node4.fgq.com
vim nt.yml
- hosts: lb
remote_user: root
roles:
- nginx 或者 - { role:nginx,username:nginx }
ansible-playbook --syntax-check nt.yml
ansible-playbook -C nt.yml
vim /etc/ansible/roles/tomcat/tasks/main.yml
- name: install package
yum: name={{ item }} state=latest
with_items:
- tomcat
- tomcat-admin-webapps
- tomcat-webapps
- tomcat-docs-webapp
when: ansible_os_family == "RedHat"
- name: start tomcat
service: name=tomcat state=started enabled=yes
mkdir -pv /etc/ansible/roles/jdk/{files,tasks,handlers,templates,vars,meta,default}
vim /etc/ansible/roles/jdk/tasks/main.yml
- name: install openjdk
yum: name=java-{{ version }}-openjdk-devel state=latest
- name: install env file
copy: src=java.sh dest=/etc/profile.d/
vim /etc/ansible/roles/jdk/files/java.sh
export JAVA_HOME=/usr
vim nt.yml
- hosts: lb
remote_user: root
roles:
- nginx
- hosts: tcsrvs
remote_user: root
roles:
- { role: jdk, version: 1.8.0 } 传递变量给角色
- tomcat
ansible-playbook -C nt.yml
ansible-playbook nt.yml
图形界面:firefox www.fgq.io ok可以访问
nginx--2台--keepalived高可用--反代
varnish--2台--静态
tomcat--2台--动态
...
架构简单

image.png
架构扩展

image.png
网友评论