curl —— HTTP 客户端的瑞士军刀
一句话定义
curl 是命令行 HTTP / HTTPS / FTP 客户端。在运维和开发场景里它的真正用途是**"用最简单的方式发一个请求看响应"**——调 REST API、看响应头、验 TLS、测连通性、下载文件。
典型场景
训练营文档里 curl 出场 54 次,主要在:
- 测 K8s service 通不通:
curl http://svc.ns.svc.cluster.local - 调 apiserver REST API:
curl -k https://10.0.24.28:6443/healthz - 下载安装脚本:
curl -fsSL https://get.docker.com | bash - 看响应头:
curl -I https://example.com - 调 Prometheus / Grafana 验证:
curl -s http://prom:9090/api/v1/query?query=up
核心 8 个 flag(90% 用法)
curl -i URL # 显示响应头 + 响应体(最常用)
curl -I URL # 只看响应头(HEAD 请求)
curl -v URL # verbose,看完整请求 + 响应(排错最强)
curl -s URL # silent,不显示进度条(脚本必加)
curl -L URL # follow redirect(跟随 3xx)
curl -k URL # 不验证 TLS 证书(仅限调试自签证书)
curl -o file URL # 写到文件(指定文件名)
curl -O URL # 用 URL 末段做文件名
记忆方法:-fsSL 是 shell 脚本下载的标配组合:
curl -fsSL https://get.helm.sh/helm-v3.14.0-linux-amd64.tar.gz | tar -xz
-f—— fail:HTTP 错误(4xx / 5xx)退出码非 0(脚本必加,不加的话 404 会把错误页当成内容下载)-s—— silent:不显示进度-S—— show-error:silent 模式下错误还是显示-L—— follow redirect
看响应头(最常用排错动作)
$ curl -I https://example.com
HTTP/2 200
content-type: text/html
content-length: 1234
last-modified: Mon, 26 May 2026 14:00:00 GMT
server: ECS (sec/12AB)
-I 发 HEAD 请求。看:
- 状态码(200 / 301 / 404 / 502 ...)
content-type对不对cache-control/expires缓存策略server服务端是啥x-*自定义头(K8s service mesh 经常加 x-trace-id 这种)
注意:有的服务对 HEAD 和 GET 行为不一致,HEAD 报 405 但 GET 正常。这时改用
curl -i URL或curl -sD - URL -o /dev/null(只看 header,body 丢掉)。
verbose 模式 -v —— 看完整握手
$ curl -v https://example.com
* Trying 93.184.215.14:443...
* Connected to example.com (93.184.215.14) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* ...
* Server certificate:
* subject: CN=www.example.org
* start date: Mar 15 00:00:00 2026 GMT
* expire date: Apr 15 23:59:59 2027 GMT
* issuer: CN=DigiCert ...
* SSL certificate verify ok.
> GET / HTTP/2
> Host: example.com
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/2 200
< content-type: text/html
...
* —— curl 内部 / TLS / 连接信息 > —— 发出的请求行 / 头 < —— 收到的响应行 / 头 (无前缀) —— 响应体
排错黄金动作:
curl -v URL 2>&1 | grep -E '^[*<>]' # 只看握手 + 头,过滤 body
-vvv 更详细(TLS 内部状态、debug 信息)。
发不同方法 / 数据
POST JSON
curl -X POST https://api.example.com/users \
-H 'Content-Type: application/json' \
-d '{"name":"alice","email":"a@e.com"}'
短写:
curl -X POST https://api.example.com/users \
-H 'Content-Type: application/json' \
-d @user.json # 从文件读 body
form 表单
curl -X POST https://example.com/login \
-d 'user=admin&pass=123' # application/x-www-form-urlencoded
multipart(含文件上传)
curl -X POST https://example.com/upload \
-F 'file=@./data.csv' \
-F 'name=mydata'
PUT / DELETE / PATCH
curl -X DELETE https://api.example.com/users/1
curl -X PUT https://api.example.com/users/1 -d '{"name":"bob"}'
curl -X PATCH https://api.example.com/users/1 -d '{"email":"new@e.com"}'
自定义 header
curl -H 'Authorization: Bearer xxx' \
-H 'X-Request-ID: 12345' \
https://api.example.com/me
-H 可以多次出现。
看请求体编码
curl -X POST https://httpbin.org/post -d 'a=1&b=2' | jq
# httpbin 回显你发了什么,验证 curl 写对没
TLS 相关
-k / --insecure:忽略证书验证
curl -k https://10.0.24.28:6443/healthz # K8s apiserver 自签证书
仅限调试。生产用 -k = 自欺欺人地认为通了。
指定 CA / client cert
curl --cacert /etc/kubernetes/pki/ca.crt \
--cert /etc/kubernetes/pki/admin.crt \
--key /etc/kubernetes/pki/admin.key \
https://10.0.24.28:6443/api/v1/namespaces
K8s apiserver 接受这种 client cert 认证,等价于 admin 用户。
看证书指纹
curl -vI https://example.com 2>&1 | grep -E "subject:|expire date"
或者用 openssl 更直接:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -fingerprint
强制 TLS 版本
curl --tlsv1.2 --tls-max 1.2 https://example.com
curl --tlsv1.3 https://example.com
排查"是不是新版 TLS 兼容性问题"用。
重试 / 超时(脚本必备)
curl -fsSL \
--connect-timeout 10 \
--max-time 60 \
--retry 3 \
--retry-delay 2 \
--retry-max-time 60 \
https://github.com/.../release.tar.gz
| flag | 含义 |
|---|---|
--connect-timeout | TCP 建连超时(秒) |
--max-time | 整个请求总超时 |
--retry N | 失败重试 N 次 |
--retry-delay S | 每次重试间隔(默认指数退避) |
--retry-all-errors | 默认只对部分错误重试,加这个对所有 |
裸 curl URL 不加超时默认能等几分钟,脚本里会让 CI 卡死。
看耗时(性能排查)
curl -s -o /dev/null -w '\nDNS: %{time_namelookup}s\nConn: %{time_connect}s\nTLS: %{time_appconnect}s\nTotal: %{time_total}s\n' \
https://example.com
或者写成 alias:
alias curl-time='curl -s -o /dev/null -w "DNS: %{time_namelookup}\nConn: %{time_connect}\nTLS: %{time_appconnect}\nStart: %{time_starttransfer}\nTotal: %{time_total}\n"'
排查"这次请求慢,慢在哪一段":
time_namelookup—— DNS 解析time_connect—— TCP 建连time_appconnect—— TLS 握手完成time_pretransfer—— 等待发送(含 TLS)time_starttransfer—— 收到第一个字节(TTFB)time_total—— 整个完成
训练营典型场景
1. 装机时下载脚本
curl -fsSL https://get.docker.com | bash # 装 Docker
curl -fsSL https://raw.githubusercontent.com/.../install.sh | bash
⚠️ 生产不建议
curl | bash—— 不可审计、被 MITM 就完了。教程图省事可以,正式安装应该先下载、看完、再跑。
2. 测 K8s apiserver
# 从节点上测
curl -k https://10.0.24.28:6443/healthz # 应该返回 "ok"
curl -k https://10.0.24.28:6443/version
curl -k https://10.0.24.28:6443/api/v1/namespaces \
-H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
3. pod 内部测 service
# 在一个 pod 里
curl -v http://myservice.mynamespace.svc.cluster.local
curl -v http://myservice # 同 ns 内简写
# 测 ClusterIP(不一定行,取决于 selector / endpoints)
curl http://10.96.0.10:53 # CoreDNS service IP
4. 调 Prometheus
# 查指标
curl -s 'http://prom:9090/api/v1/query?query=up' | jq
# 看 targets
curl -s http://prom:9090/api/v1/targets | jq '.data.activeTargets[] | {job: .labels.job, health}'
# Pushgateway 推指标
curl -X POST -d 'my_metric 42' http://pushgateway:9091/metrics/job/myjob
5. 下载 K8s 二进制
VERSION=v1.28.0
curl -fsSLo /usr/local/bin/kubectl \
"https://dl.k8s.io/release/${VERSION}/bin/linux/amd64/kubectl"
chmod +x /usr/local/bin/kubectl
6. 测 ingress
curl -H "Host: myapp.example.com" http://<ingress-ip>/
# 跳过 DNS,直接打到 ingress IP,host 头让 ingress 路由到正确 backend
常见踩坑
坑 1:脚本里 curl URL 不加 -fsSL,404 当成下载内容
curl https://github.com/.../release.tar.gz | tar -xz
# URL 错了返回 404 页面,tar 收到 HTML 然后报错
# 但 curl 退出码 0(HTTP 错误不算 curl 错误)
永远加 -f:
curl -fsSL https://github.com/.../release.tar.gz | tar -xz
# 404 → curl 返回非 0 → 管道也失败
坑 2:用 -X POST -d '' 但没设 Content-Type
curl -X POST https://api.example.com/data -d '{"x":1}'
# 默认 Content-Type: application/x-www-form-urlencoded
# 服务端期望 JSON → 解析失败
发 JSON 一定加:
curl -X POST https://api.example.com/data \
-H 'Content-Type: application/json' \
-d '{"x":1}'
坑 3:变量在 JSON 里被 shell 吃了
NAME=alice
curl -X POST URL -d '{"name":"$NAME"}' # ❌ 单引号阻止变量展开,发出去是字面量
curl -X POST URL -d "{\"name\":\"$NAME\"}" # ✅ 双引号,但要转义内部引号
curl -X POST URL -d "$(jq -n --arg n "$NAME" '{name:$n}')" # ✅ jq 生成 JSON
含变量的 JSON 推荐用 jq 生成,少踩引号坑。
坑 4:跟随重定向时改 method
curl -X POST -L https://example.com/api
# 服务端 301 跳转 → curl 默认用 GET 跟随(不再是 POST)
要保持 method:
curl -X POST -L --post301 --post302 --post303 URL
# 或者更狠:
curl -X POST -L --location-trusted URL # 跟随时甚至传 Authorization
坑 5:-d 把空格丢了
curl -d 'name=alice doe' URL
# 实际发 "name=alice doe" → 服务端可能拿到 "name=alice"
URL encode:
curl --data-urlencode 'name=alice doe' URL
# 发 name=alice%20doe
坑 6:进度条干扰 grep
curl URL | grep error
# 进度条混进 stderr 看着乱
加 -s:
curl -sS URL | grep error
# -s 关进度,-S 保留错误信息
坑 7:长时间不动卡死
curl https://slow-server.example.com
# 等了 5 分钟还没动
--max-time 30 或者 --connect-timeout 10,避免脚本卡死。
坑 8:-k 失败说"unable to get local issuer certificate"
curl https://internal.example.com
# curl: (60) SSL certificate problem: unable to get local issuer certificate
-k 通常能跳过,但更好的做法是把 CA 装到系统:
cp my-ca.crt /usr/local/share/ca-certificates/
update-ca-certificates
# 之后 curl 不需要 -k
K8s 内部 CA 在 /etc/kubernetes/pki/ca.crt。
坑 9:curl 在 alpine 容器里没装
sh: curl: not found
debian/ubuntu 默认有 curl 但 alpine 没。
apk add --no-cache curl # alpine
或者用 wget(alpine 自带 BusyBox 版):
wget -qO- URL # 等价 curl -s URL