F5 iRules TCL 脚本入门实战
学完前面三篇,你已经能完成 VIP 上线、配 Monitor 保障后端可用、配 SSL 卸载处理 HTTPS。
但如果要自定义流量处理逻辑——比如 URL 改写、按路径分发到不同 Pool、IP 白名单——靠标准配置就不够了。
这就是 iRules 的用武之地。
一、iRules 是什么
iRules 是 F5 上的TCL 脚本引擎,在流量经过 VS 时拦截,让你能写代码自定义处理逻辑。
客户端请求 → VS │ ├── Profile 栈(TCP/HTTP/SSL 正常处理) │ └── 🧩 iRules 在 Profile 处理中插入自定义逻辑 ├── 请求到达时 → 改 URL、插头、选 Pool ├── 响应返回时 → 改响应头、隐藏后端信息 └── 连接建立时 → IP 白名单过滤
二、TCL 基础语法(够用就行)
iRules 用 TCL 语言,但你不需要精通,记住 5 个核心语法就能覆盖 70% 场景:
变量
set my_var "hello" # 定义变量 log local0. "值是 $my_var" # 使用变量 set full_name "user_${my_var}" # 变量嵌套用 ${}条件判断
if { [条件表达式] } { # 条件为真 } elseif { [另一个条件] } { # 另一个条件 } else { # 都不满足 }字符串操作
if { [string equal $uri "/health"] } { ... } # 精确匹配 if { [string match "/api/*" $uri] } { ... } # 通配符匹配 set ext [string range $uri 0 3] # 取子串 set new_uri [string map {"/old/" "/new/"} $uri] # 替换列表操作
set allowed_ips [list "10.0.0.1" "10.0.0.2"] foreach ip $allowed_ips { ... } # 遍历 if { [lsearch -exact $list $item] >= 0 } { ... } # 查找正则
if { [regexp {^/api/v[0-9]+/} $uri] } { ... } # 匹配 regexp {user_id=([0-9]+)} $uri match user_id # 提取分组三、iRules 事件模型——理解触发时机
iRules 是事件驱动的,在不同的时机挂不同的代码。
核心事件
CLIENT_ACCEPTED ← TCP 连接建立成功(做 IP 过滤) │ HTTP_REQUEST ← 收到完整 HTTP 请求头(90% 用这个) │ HTTP_REQUEST_DATA ← 收到请求体 │ [ 流量发往后端 ] │ HTTP_RESPONSE ← 收到后端返回的响应头(改写响应头) │ HTTP_RESPONSE_DATA ← 收到响应体
怎么选事件
你想什么时候处理流量? │ ├── TCP 连接建立时 → CLIENT_ACCEPTED ├── 请求头到达时 → HTTP_REQUEST(90% 的场景) ├── 响应头返回时 → HTTP_RESPONSE └── 响应体到达时 → HTTP_RESPONSE_DATA
四、9 个实战场景(从易到难)
场景 1:HTTP → HTTPS 跳转
when HTTP_REQUEST { if { [HTTP::header "X-Forwarded-Proto"] ne "https" } { HTTP::redirect "https://[HTTP::host][HTTP::uri]" } }触发事件:HTTP_REQUEST效果:用户访问http://example.com/page→ 自动 301 到https://example.com/page
场景 2:插入真实客户端 IP
when HTTP_REQUEST { HTTP::header remove "X-Forwarded-For" # 删除伪造头 HTTP::header insert "X-Forwarded-For" [IP::client_addr] HTTP::header insert "X-Real-IP" [IP::client_addr] }触发事件:HTTP_REQUEST效果:后端收到X-Forwarded-For: 客户端真实 IP,不再是 F5 的 IP
场景 3:URL 路径重写(去掉前缀)
when HTTP_REQUEST { if { [string match "/api/v1/*" [HTTP::uri]] } { # 把 /api/v1/user → /user set new_uri [string range [HTTP::uri] 7 end] HTTP::uri $new_uri } }触发事件:HTTP_REQUEST效果:客户端请求/api/v1/user→ 后端实际收到/user
场景 4:按 URI 分发到不同 Pool
when HTTP_REQUEST { set uri [HTTP::uri] if { [string match "/api/*" $uri] } { pool api-pool # API → API 池 } elseif { [string match "/static/*" $uri] } { pool static-pool # 静态资源 → 静态池 } else { pool web-pool # 其他 → 默认池 } }触发事件:HTTP_REQUEST效果:一个 VIP 根据 URL 路径分发到不同后端集群
场景 5:按 User-Agent 分流(移动端 vs PC)
when HTTP_REQUEST { set ua [string tolower [HTTP::header "User-Agent"]] if { [string match "*mobile*" $ua] || [string match "*iphone*" $ua] } { pool mobile-pool } else { pool pc-pool } }触发事件:HTTP_REQUEST效果:手机用户和 PC 用户走不同的后端集群
场景 6:IP 白名单
when CLIENT_ACCEPTED { set allowed_subnets [list "10.0.0.0/8" "172.16.0.0/12" "192.168.0.0/16"] set client_ip [IP::client_addr] set allowed false foreach subnet $allowed_subnets { if { [IP::addr $client_ip equals $subnet] } { set allowed true break } } if { !$allowed } { log local0. "拒绝非内网访问: $client_ip" reject } }触发事件:CLIENT_ACCEPTED效果:非内网 IP 在 TCP 连接阶段直接断开,不给 HTTP 握手机会
场景 7:隐藏后端服务信息
when HTTP_RESPONSE { HTTP::header remove "Server" HTTP::header remove "X-Powered-By" HTTP::header remove "X-AspNet-Version" HTTP::header insert "Server" "F5 BIG-IP" }触发事件:HTTP_RESPONSE效果:客户端看不到后端是 Nginx 还是 Tomcat
场景 8:IP 级速率限制
when HTTP_REQUEST { # 每 10 秒超过 100 次请求 → 429 if { [table lookup -subtable "rate_limit" [IP::client_addr]] > 100 } { HTTP::respond 429 content "Rate limit exceeded" \ "Content-Type" "text/plain" return } table incr -subtable "rate_limit" -timeout 10 [IP::client_addr] }触发事件:HTTP_REQUEST效果:单个 IP 在 10 秒内超过 100 次请求,返回 429 Too Many Requests
场景 9:Cookie 会话保持
when HTTP_REQUEST { if { [HTTP::cookie exists "MY_SESSION"] } { set session_id [HTTP::cookie value "MY_SESSION"] persist uie $session_id # 固定到同一台后端 } } when HTTP_RESPONSE { if { [HTTP::cookie exists "MY_SESSION"] } { persist uie [HTTP::cookie value "MY_SESSION"] } }触发事件:HTTP_REQUEST+HTTP_RESPONSE效果:同一个 session 的请求始终发到同一台后端
五、创建和绑定 iRules
创建
# 方式一:从文件导入(推荐) tmsh create ltm rule my-url-rewrite from-local-file /shared/tmp/irule.tcl # 方式二:直接创建 tmsh create ltm rule my-simple-rule # 进入交互模式,粘贴脚本后 Ctrl+C 退出
查看和修改
# 查看内容 tmsh list ltm rule my-url-rewrite # 修改(重新导入) tmsh modify ltm rule my-url-rewrite from-local-file /shared/tmp/new-irule.tcl
绑定到 VS
# 创建 VS 时绑定 tmsh create ltm virtual myapp-vip-443 \ destination 10.0.0.100:443 \ pool myapp-pool \ rules { my-url-rewrite } # 已有 VS 添加 tmsh modify ltm virtual myapp-vip-443 rules add { my-url-rewrite } # 删除 tmsh modify ltm virtual myapp-vip-443 rules delete { my-url-rewrite } # 查看绑了哪些 tmsh list ltm virtual myapp-vip-443 rules删除 iRules
# 先从所有 VS 解绑 tmsh modify ltm virtual myapp-vip-443 rules delete { my-url-rewrite } # 再删除规则 tmsh delete ltm rule my-url-rewrite多个 iRules 的执行顺序
# iRules 按绑定顺序执行 tmsh modify ltm virtual myapp-vip-443 \ rules { ip-whitelist url-rewrite header-insert } # 执行顺序: # 1. ip-whitelist 先执行(IP 过滤) # 2. url-rewrite 后执行(URL 改写) # 3. header-insert 最后执行(插请求头) # 任一规则 return 或 reject,后续规则不再执行六、调试 iRules
写日志
when HTTP_REQUEST { log local0. "收到请求: [HTTP::uri]" log local0. "客户端 IP: [IP::client_addr]" }看日志
# 实时查看 tmsh tail -f /var/log/ltm | grep -i "irule\|myapp" # 打印所有请求头(排查用) when HTTP_REQUEST { foreach header [HTTP::header names] { log local0. "$header: [HTTP::header value $header]" } }七、常见 iRules 错误
1. 没有事件声明
# ❌ 错误 set my_var "hello" HTTP::header insert "X-Test" $my_var # ✅ 正确 when HTTP_REQUEST { set my_var "hello" HTTP::header insert "X-Test" $my_var }2. 错误的事件中调用不支持的函数
# ❌ 错误:CLIENT_ACCEPTED 时还没有 HTTP 请求 when CLIENT_ACCEPTED { HTTP::header insert "X-Test" "test" } # ✅ 正确:HTTP_REQUEST 时才能操作 HTTP 头 when HTTP_REQUEST { HTTP::header insert "X-Test" "test" }3. reject 后没有 return
# ❌ 错误:reject 后代码继续执行 when HTTP_REQUEST { if { [IP::client_addr] eq "bad.ip" } { reject } pool special-pool # 即使 reject 了也会执行 } # ✅ 正确 when HTTP_REQUEST { if { [IP::client_addr] eq "bad.ip" } { reject return } pool special-pool }4. 变量嵌套错误
# ❌ 错误 set prefix "api" if { [string match "/$prefix/*" $uri] } { } # $p 被当成了变量 # ✅ 正确:用 ${} 明确边界 if { [string match "/${prefix}/*" $uri] } { }八、iRules 命令速查
请求处理
| 命令 | 作用 | 可用事件 |
|---|---|---|
HTTP::uri | 获取/设置请求 URI | HTTP_REQUEST |
HTTP::host | 获取请求的 Host | HTTP_REQUEST |
HTTP::header | 获取/设置/删除请求头 | HTTP_REQUEST |
HTTP::cookie | 获取/设置 Cookie | HTTP_REQUEST |
HTTP::redirect | 返回 301 跳转 | HTTP_REQUEST |
HTTP::respond | 直接返回响应(不经过后端) | HTTP_REQUEST |
HTTP::status | 获取响应状态码 | HTTP_RESPONSE |
IP::client_addr | 客户端 IP | 所有事件 |
IP::local_addr | F5 自身 IP | 所有事件 |
reject | 拒绝连接 | CLIENT_ACCEPTED |
pool | 选择后端 Pool | HTTP_REQUEST |
九、学习路径建议
阶段 1:读懂 → 能看懂别人写的 iRules → 知道 when HTTP_REQUEST 是什么意思 阶段 2:改 → 在现有 iRules 基础上改参数 → 改 URL 匹配规则、改 Pool 名称 阶段 3:写 → 独立写 10 行以内的简单 iRules → URL 重写、请求头插入、IP 白名单 阶段 4:组合 → 多个 iRules 组合使用 → 理解执行顺序和冲突
你现在看完这篇,大概在阶段 1~2 之间。把上面 9 个场景在测试环境跑一遍,就进入阶段 3 了。
十、小结
iRules 是 F5 最灵活、威力最大的功能之一。不需要精通 TCL 语言,记住 5 个基础语法 + 事件模型 + 9 个场景模板,就能应对日常工作中 80% 的流量控制需求。
相关阅读:
F5 VIP 上线全流程:Pool / VS / Profile 三板斧
F5 健康检查 Monitor 深入篇
F5 SSL Profile 证书卸载
