VRRP协议
-
VRRP概念
随着Internet的发展,人们对网络的可靠性的要求越来越高。对于局域网用户来说,能够时刻与外部网络保持联系是非常重要的。通常情况下,内部网络中的所有主机都设置一条相同的缺省路由,指向出口网关,实现主机与外部网络的通信。当出口网关发生故障时,主机与外部网络的通信就会中断。
VRRP是一种容错协议,它通过把几台路由设备联合组成一台虚拟的路由设备,并通过一定的机制来保证当主机的下一跳设备出现故障时,可以及时将业务切换到其它设备,从而保持通讯的连续性和可靠性。
使用VRRP的优势在于:既不需要改变组网情况,也不需要在主机上配置任何动态路由或者路由发现协议,就可以获得更高可靠性的缺省路由。
-
VRRP原理
VRRP将局域网的一组路由器构成一个备份组,相当于一台虚拟路由器。局域网内的主机只需要知道这个虚拟路由器的IP地址,并不需知道具体某台设备的IP地址,将网络内主机的缺省网关设置为该虚拟路由器的IP地址,主机就可以利用该虚拟网关与外部网络进行通信。
VRRP将该虚拟路由器动态关联到承担传输业务的物理路由器上,当该物理路由器出现故障时,再次选择新路由器来接替业务传输工作,整个过程对用户完全透明,实现了内部网络和外部网络不间断通信。
主机利用该虚拟网关与外部网络通信。路由器工作机制如下:
根据优先级的大小挑选Master设备。Master的选举有两种方法:
比较优先级的大小,优先级高者当选为Master。
当两台优先级相同的路由器同时竞争Master时,比较接口IP地址大小。接口地址大者当选为Master。
其它路由器作为备份路由器,随时监听Master的状态。
当主路由器正常工作时,它会每隔一段时间(Advertisement_Interval)发送一个VRRP组播报文,以通知组内的备份路由器,主路由器 处于正常工作状态。
当组内的备份路由器一段时间(Master_Down_Interval)内没有接收到来自主路由器的报文,则将自己转为主路由器。一个VRRP组里 有多台备份路由器时,短时间内可能产生多个Master,此时,路由器将会将收到的VRRP报文中的优先级与本地优先级做比较。从而选取优先级高的设备做Master。
VRRP是一个“选举”协议,它能够动态地将一个虚拟路由器的责任指定至同一个VRRP组中的其它路由器上,从而消除了静态路由配置的单点故障。
高可用集群 HA Cluster
-
高可用概念
高可用集群(High Availability Cluster,简称HA Cluster),是指以减少服务中断时间为目的的服务器集群技术。它通过保护用户的业务程序对外不间断提供的服务,把因软件、硬件、人为造成的故障对业务的影响降低到最小程度。
通常用平均无故障时间(MTTF)来度量系统的可靠性,用平均故障维修时间(MTTR)来度量系统的可维护性。于是可用性被定义为:
描述 | 通俗叫法 | 可用性级别 | 年度停机时间 |
基本可用性 | 2个9 | 99% | 87.6小时 |
较高可用性 | 3个9 | 99.9% | 8.8小时 |
具有故障自动恢复能力的可用性 | 4个9 | 99.99% | 53分钟 |
极高可用性 | 5个9 | 99.999% | 5分钟 |
高可用集群主要实现自动侦测(Auto-Detect)故障、自动切换/故障转移(FailOver)和自动恢复(FailBack)。
简单来说就是,用高可用集群软件实现故障检查和故障转移(故障/备份主机切换)的自动化,当然像负载均衡、DNS分发也可提供高可性。
-
FailOver 故障转移
自动切换阶段某一主机如果确认对方故障,则正常主机除继续进行原来的任务,还将依据各种容错备援模式接管预先设定的备援作业程序,并进行后续的程序及服务。
集群资源
集群资源是集群中使用的规则、服务和设备等,如VIP、httpd服务、STONITH设备等。类型如下:
- Primitive:主资源,在某一时刻只能运行在某个节点上,如VIP。
- group:组,资源容器,使得多个资源同时停/启等,一般只包含primitive资源。
- clone:克隆,可以在多个节点运行的资源,例如stonith设备管理进程、集群文件系统的分布式锁(dlm)作为资源,应运行在所有节点上。
- master/slave:特殊的clone资源,运行在两个节点上,一主一从,如:分布式复制块设备drbd(2.6.33之后整合进内核了)。
资源倾向
资源粘性:资源对节点倾向程度,资源是否倾向于当前节点。score,正值倾向于当前节点(还要和位置约束结合)。
资源约束(Constraint):资源和资源之间的关系
- 排列约束 (colocation):资源间的依赖/互斥性,定义资源是否运行在同一节点上。score,正值表示要运行在同一节点上,负值则不可。
- 位置约束(location):每个节点都有一个score值,正值则倾向于本节点,负值倾向于其他节点,所有节点score比较,倾向于最大值的节点。
- 顺序约束(order):定义资源执行动作的次序,例如vip应先配置,httpd服务后配置。特殊的score值,-inf 负无穷,inf 正无穷。
-
高可用集群模型
Active/Passive:主备模型
一个活动主节点,另一个不活动作为备用节点,当主节点故障,转移到备节点,这时备节点就成为了主节点。备节点完全冗余,造成一定浪费。
Active/Active:双主模型
两个节点都是活动的,两个节点运行两个不同的服务,也互为备用节点。也可以提供同一个服务,比如ipvs,前端基于DNS轮询。这种模型可以使用比较均衡的主机配置,不会造成浪费。
N+1
N个活动主节点N个服务,一个备用节点。这需要额外的备用节点必须能够代替任何主节点,当任何主节点故障时,备节点能够负责它的角色对外提供相应的服务。
N+M
N个活动主节点,M个备用节点。像上面的N+1模型,一个备用节点可能无法提供足够的备用冗余能力,备用节点的数量M是成本和可靠性要求之间的折衷。
N-to-N
N个主节点N个备用节点。这是A/A双主和N + M模型的组合,N节点都有服务,如果一个坏了,剩下的每个节点都可以作为替代提供服务。
keepalived
keepalived的诞生最初是为LVS ipvs(director)提供高可用性的,后来发展一个多功能、通用的轻量级高可用组件,可以为ipvs、nginx、haproxy等诸多服务提供高可用功能,主要应用在负载均衡调度器上,同时也可以检查后端各realserver的健康状态。
-
keepalived基础
keepalived主要是模块是VRRP Stack和Cheackers
VRRP主要实现VIP(及MAC)的高可用
Cheackers主要实现各服务如ipvs、nginx等的高可用及realserver健康状态检查
其中ipvs和realserver健康状态检查通过配置文件配置就可以实现,而其他服务高可用则需要通过自己编写脚本,然后配置keepalived调用来实现。
Keepalived运行有3个守护进程。父进程主要负责读取配置文件初始化、监控2个子进程等
然后两个子进程,一个负责VRRP,另一个负责Cheackers健康检查。其中父进程监控模块为 WacthDog,工作实现:每个子进程打开一个接受 unix 域套接字,父进程连接到那些unix域套接字并向子进程发送周期性(5s)hello包。
-
keepalived配置
单主模型
修改配置文件 /etc/keepalived/leepalived.conf
[root@CentOS74 ~]# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs { #全局通用配置
notification_email {
root@localhost
}
notification_email_from jiangbowen@keepalived.com
smtp_server 127.0.0.1 #邮件服务器IP
smtp_connect_timeout 30 #邮件发送超时
router_id 74_HOST #本机ID标识
vrrp_skip_check_adv_addr
vrrp_strict #自动生成iptables规则
vrrp_garp_interval 0
vrrp_gna_interval 0
vrrp_mcast_group4 224.1.1.18 #默认为224.0.0.18,建议修改
}
vrrp_instance VI_1 { #定义VRRP实例
state MASTER #定义主从
interface ens33 #使用的网卡名称
virtual_router_id 7 #0到255之间,识别同一个虚拟路由
priority 100 #0到255之间
advert_int 1 #通报间隔
authentication { #认证方式
auth_type PASS
auth_pass linux
}
virtual_ipaddress { #虚拟IP
192.168.30.7
}
}
首先在低优先级的从节点上开启 keepalived 服务,并查看日志
Jul 26 18:08:40 CentOS75.Miariam Keepalived_vrrp[1952]: Registering gratuitous ARP shared channel
Jul 26 18:08:40 CentOS75.Miariam Keepalived_vrrp[1952]: Opening file '/etc/keepalived/keepalived.conf'.
Jul 26 18:08:40 CentOS75.Miariam Keepalived_healthcheckers[1951]: Initializing ipvs
Jul 26 18:08:40 CentOS75.Miariam Keepalived_healthcheckers[1951]: Opening file '/etc/keepalived/keepalived.conf'.
Jul 26 18:08:40 CentOS75.Miariam Keepalived_vrrp[1952]: VRRP_Instance(VI_1) removing protocol VIPs.
Jul 26 18:08:40 CentOS75.Miariam Keepalived_vrrp[1952]: VRRP_Instance(VI_1) removing protocol iptable drop rule
Jul 26 18:08:40 CentOS75.Miariam Keepalived_vrrp[1952]: Using LinkWatch kernel netlink reflector...
Jul 26 18:08:40 CentOS75.Miariam Keepalived_vrrp[1952]: VRRP_Instance(VI_1) Entering BACKUP STATE
Jul 26 18:08:40 CentOS75.Miariam Keepalived_vrrp[1952]: VRRP sockpool: [ifindex(2), proto(112), unicast(0), fd(10,11)]
Jul 26 18:08:44 CentOS75.Miariam Keepalived_vrrp[1952]: VRRP_Instance(VI_1) Transition to MASTER STATE
从节点在一段时间内通过 VRRP 组播没有发现优先级比自己高的节点或者主节点的话,自己将主动提升为主节点。但是当主节点或者优先级比自己高的节点上线,根据抢占模式,他将降级为从节点
Jul 26 18:26:12 CentOS75.Miariam Keepalived_vrrp[2072]: VRRP_Instance(VI_1) Received advert with higher priority 100, ours 90
Jul 26 18:26:12 CentOS75.Miariam Keepalived_vrrp[2072]: VRRP_Instance(VI_1) Entering BACKUP STATE
Jul 26 18:26:12 CentOS75.Miariam Keepalived_vrrp[2072]: VRRP_Instance(VI_1) removing protocol VIPs.
Jul 26 18:26:12 CentOS75.Miariam Keepalived_vrrp[2072]: VRRP_Instance(VI_1) removing protocol iptable drop rule
查看主节点的 keepalived 日志
Jul 26 18:25:56 CentOS74.Miriam Keepalived_vrrp[11334]: Opening file '/etc/keepalived/keepalived.conf'.
Jul 26 18:26:12 CentOS74.Miriam Keepalived_vrrp[11334]: (VI_1): Cannot start in MASTER state if not address owner
Jul 26 18:26:12 CentOS74.Miriam Keepalived_vrrp[11334]: VRRP_Instance(VI_1) removing protocol VIPs.
Jul 26 18:26:12 CentOS74.Miriam Keepalived_vrrp[11334]: VRRP_Instance(VI_1) removing protocol iptable drop rule
Jul 26 18:26:12 CentOS74.Miriam Keepalived_vrrp[11334]: Using LinkWatch kernel netlink reflector...
Jul 26 18:26:12 CentOS74.Miriam Keepalived_vrrp[11334]: VRRP_Instance(VI_1) Entering BACKUP STATE
Jul 26 18:26:12 CentOS74.Miriam Keepalived_vrrp[11334]: VRRP sockpool: [ifindex(2), proto(112), unicast(0), fd(10,11)]
Jul 26 18:26:12 CentOS74.Miriam Keepalived_vrrp[11334]: VRRP_Instance(VI_1) forcing a new MASTER election
Jul 26 18:26:13 CentOS74.Miriam Keepalived_vrrp[11334]: VRRP_Instance(VI_1) Transition to MASTER STATE
使用 ip 命令查看指定网卡,观察虚拟地址
[root@CentOS74 ~]# ip addr show dev ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:a1:ad:b8 brd ff:ff:ff:ff:ff:ff
inet 192.168.30.74/24 brd 192.168.30.255 scope global ens33
valid_lft forever preferred_lft forever
inet 192.168.30.7/32 scope global ens33 #成功添加网卡别名
valid_lft forever preferred_lft forever
inet6 fe80::a815:1b4e:5d89:c847/64 scope link
valid_lft forever preferred_lft forever
双主模型
双主模型无非是将两个节点相互作为主备
[root@CentOS174 ~]# cat /etc/keepalived/keepalived.conf
! Configuration File for keepalived
global_defs {
notification_email {
root@localhost
}
notification_email_from jiangbowen@keepalived.com
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id 74_HOST
vrrp_skip_check_adv_addr
vrrp_strict
vrrp_garp_interval 0
vrrp_gna_interval 0
vrrp_mcast_group4 224.1.1.18
}
vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 7
priority 95
advert_int 1
authentication {
auth_type PASS
auth_pass linux
}
virtual_ipaddress {
192.168.30.7/24
}
}
vrrp_instance VI_2 {
state MASTER
interface ens33
virtual_router_id 74
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass linux
}
virtual_ipaddress {
192.168.30.8/24
}
}
vrrp_instance 其他配置项:
virtual_ipaddress { #定义虚拟IP的属性
src <IPADDR> [to] <IPADDR>/<MASK> via|gw <IPADDR> [or <IPADDR>] dev <STRING> scope <SCOPE> table <TABLE>
}
track_interface { #配置要监控的网络接口,一旦接口出现故障,则转为FAULT状态
eth0
eth1
...
}
nopreempt #定义工作模式为非抢占模式
preempt_delay 300 #抢占式模式下,节点上线后触发新选举操作的延迟时长
使用 ip 命令查看虚拟 IP 的分配情况
[root@CentOS74 ~]# ip addr show dev ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:a1:ad:b8 brd ff:ff:ff:ff:ff:ff
inet 192.168.30.74/24 brd 192.168.30.255 scope global ens33
valid_lft forever preferred_lft forever
inet 192.168.30.7/24 scope global ens33
valid_lft forever preferred_lft forever
inet6 fe80::a815:1b4e:5d89:c847/64 scope link
valid_lft forever preferred_lft forever
[root@CentOS174 ~]# ip addr show dev ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:3d:70:fc brd ff:ff:ff:ff:ff:ff
inet 192.168.30.174/24 brd 192.168.30.255 scope global ens33
valid_lft forever preferred_lft forever
inet 192.168.30.8/24 scope global ens33
valid_lft forever preferred_lft forever
inet6 fe80::bb94:dae0:fd12:4086/64 scope link
valid_lft forever preferred_lft forever
通知脚本
通知脚本能够在指定的情况下,使用通知脚本通知管理者当前服务器状态
notify_master <STRING>|<QUOTED-STRING> 当前节点成为主节点时触发的脚本
notify_backup <STRING>|<QUOTED-STRING> 当前节点转为备节点时触发的脚本
notify_fault <STRING>|<QUOTED-STRING> 当前节点转为“失败”状态时触发的脚本
使用通知脚本,并在配置文件中定义
#!/bin/bash
#
contact='root@localhost'
notify() {
local mailsubject="$(hostname) to be $1, vip floating"
local mailbody="$(date +'%F %T'): vrrp transition, $(hostname) changed to be $1"
echo "$mailbody" | mail -s "$mailsubject" $contact
}
case $1 in
master)
notify master
;;
backup)
notify backup
;;
fault)
notify fault
;;
*)
echo "Usage: $(basename $0) {master|backup|fault}"
exit 1
;;
esac
notify_master "/etc/keepalived/notify.sh master"
notify_backup "/etc/keepalived/notify.sh backup"
notify_fault "/etc/keepalived/notify.sh fault"
-
keepalived高可用LVS
使用 keepalived 高可用 LVS,需要修改配置文件定义 ipvs 规则
virtual_server 192.168.200.100 443 { #VS配置段
delay_loop 6 #服务轮询的时间间隔
lb_algo rr #定义调度方法
lb_kind NAT #集群的类型
persistence_timeout 50 #持久连接时长
protocol TCP #服务协议,仅支持TCP
real_server 192.168.201.100 443 {
weight 1 #轮询权重
SSL_GET { #七层检测,也支持使用HTTP_GET
url {
path / #URL路径
digest ff20ad2481f97b1754ef3e12ecd3a9cc #检测校验码
#status_code <INT> #判断上述检测机制为健康状态的响应码
}
connect_timeout 3 #连接请求的超时时长
nb_get_retry 3 #重试次数
delay_before_retry 3 #重试之前的延迟时长
bindto <IP ADDRESS> #发出健康状态检测请求时使用的源地址
bind_port <PORT> #发出健康状态检测请求时使用的源端口
connect_ip <IP ADDRESS> #向当前RS的哪个IP地址发起健康状态检测请求
connect_port <PORT> #向当前RS的哪个PORT发起健康状态检测请求
}
TCP_CHECK {
connect_ip <IP ADDRESS> #向当前RS的哪个IP地址发起健康状态检测请求
connect_port <PORT> #向当前RS的哪个PORT发起健康状态检测请求
bindto <IP ADDRESS> #发出健康状态检测请求时使用的源地址;
bind_port <PORT> #发出健康状态检测请求时使用的源端口;
connect_timeout <INTEGER> #连接请求的超时时长;
}
}
}
根据上图中的拓扑配置集群,并通过 VIP 访问后端 RS 主机
启动 keepalived 服务后,将会根据配置文件自动生成 ipvs 规则
[root@CentOS74 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.30.7:80 rr
-> 192.168.30.69:80 Route 2 0 1
-> 192.168.30.75:80 Route 1 0 1
在客户端上使用 curl 工具访问 VIP
[root@CentOS174 ~]# for ((i=1;i<=5;i++));do curl http://192.168.30.7 ;done
This is host 75
This is host 69
This is host 75
This is host 69
This is host 75
停止调度器中的主节点上 keepalived 服务的,模拟主节点宕机。在一点时间过后,从节点接收不到主节点的 VRRP 报文,则会主动将自己的角色提升为主节点。再次使用 curl 访问 VIP
[root@CentOS174 ~]# for ((i=1;i<=5;i++));do curl http://192.168.30.7 ;done
This is host 75
This is host 69
This is host 75
This is host 69
This is host 75
这时候已经是由从节点来提供调度工作
高级配置
- sorry server
当后端主机宕机时,keepalived 会根据配置文件中定义的检测机制,在确认目标主机上服务故障后将该主机从 ipvs 规则中移除
[root@CentOS174 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.30.7:80 rr
-> 192.168.30.69:80 Route 2 0 0
[root@CentOS74 ~]# for ((i=1;i<=5;i++));do curl http://192.168.30.7 ;done
This is host 69
This is host 69
This is host 69
This is host 69
This is host 69
当后端主机服务全部故障时,可以在 keepalived 的 virtual_server 配置段中设置中添加 sorry_server 选项为用户提供自定义错误界面
sorry_server <IP ADDRESS> <PORT>
访问 VIP,查看 sorry server 工作状态
[root@CentOS174 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.30.7:80 rr
-> 127.0.0.1:80 Route 1 0 5
[root@CentOS74 ~]# for ((i=1;i<=5;i++));do curl http://192.168.30.7 ;done
sorry server 174
sorry server 174
sorry server 174
sorry server 174
sorry server 174
- 状态转换脚本
定义状态转换通知脚本,在服务状态发生变化时,使用脚本中定义的方式通知管理者
notify_up|down <STRING>|<QUOTED-STRING>
-
keepalived高可用Nginx
keepalived 服务并没有内建对 Nginx 健康状态检查的功能,但是我们可以通过 vrrp_script 选项自定义脚本,对类似 Nginx 或其他需要检测的服务进行健康状态检测
! Configuration File for keepalived
global_defs {
notification_email {
root@localhost
}
notification_email_from jiangbowen@keepalived.com
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id 74_HOST
vrrp_skip_check_adv_addr
vrrp_garp_interval 0
vrrp_gna_interval 0
vrrp_mcast_group4 224.1.1.18
}
vrrp_script nginx_health { #定义自定义脚本,注意先定义,后引用
script "killall -0 nginx && exit 0 || exit 1" #也可以指定脚本文件路径
interval 1 #执行间隔
weight -10 #当脚本执行失败,优先级偏移量
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 7
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass linux
}
virtual_ipaddress {
192.168.30.7
}
track_interface { #在vrrp_instance配置段中引用自定义脚本
ens33
}
notify_master "/etc/keepalived/notify.sh master" #在notify脚本中,当该节点的状态转变为BACKUP时
notify_backup "/etc/keepalived/notify.sh backup" #脚本会执行对检测服务的启动操作
notify_fault "/etc/keepalived/notify.sh fault"
track_script {
nginx_health
}
}
- 在前端方向代理服务器上停止 Nginx 服务,测试 notify 脚本的自我修复功能
#!/bin/bash
#
contact='root@localhost'
notify() {
local mailsubject="$(hostname) to be $1, vip floating"
local mailbody="$(date +'%F %T'): vrrp transition, $(hostname) changed to be $1"
echo "$mailbody" | mail -s "$mailsubject" $contact
}
case $1 in
master)
notify master
;;
backup)
notify backup
systemctl start nginx.service #当使用backup参数时,尝试启动nginx
;;
fault)
notify fault
;;
*)
echo "Usage: $(basename $0) {master|backup|fault}"
exit 1
;;
esac
抓包发现,主节点的优先级临时降到了90,又会升至100优先级,说明这期间 Nginx 服务停止后又恢复了
20:52:26.383737 IP 192.168.30.74 > 224.1.1.18: VRRPv2, Advertisement, vrid 7, prio 100, authtype simple, intvl 1s, length 20
20:52:27.384825 IP 192.168.30.74 > 224.1.1.18: VRRPv2, Advertisement, vrid 7, prio 90, authtype simple, intvl 1s, length 20
20:52:29.389889 IP 192.168.30.74 > 224.1.1.18: VRRPv2, Advertisement, vrid 7, prio 100, authtype simple, intvl 1s, length 20
- 禁用 notify 脚本中的自我修复功能,查看故障转移的情况
[root@CentOS174 ~]# ip addr show dev ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:3d:70:fc brd ff:ff:ff:ff:ff:ff
inet 192.168.30.174/24 brd 192.168.30.255 scope global ens33
valid_lft forever preferred_lft forever
inet 192.168.30.7/32 scope global ens33 #VIP成功发生了转移
valid_lft forever preferred_lft forever
inet6 fe80::bb94:dae0:fd12:4086/64 scope link
valid_lft forever preferred_lft forever
- 添加一个自定义脚本,实现主动的主节点临时迁移
vrrp_script chk_down {
script "/bin/bash [[ -f /etc/keepalived/down ]] && exit 1 || exit 0" #检查指定名称的文件是否存在,存在则降权
interval 1
weight -50 #优先级减50以保证该节点的优先级最低
}
注意:当执行 vrrp_script 时,将会以 keepalived 的子进程运行,在新版本中不在支持条件判断,需要使用 /bin/bash 定义环境,声明脚本为 bash 的参数。