ServiceAccount 为 Pod 中运行的进程提供了一个身份,Pod 内的进程可以使用其关联服务账号的身份,向集群的 APIServer 进行身份认证。当创建 Pod 的时候规范下面有一个 spec.serviceAccount 的属性用来指定该 Pod 使用哪个 ServiceAccount,如果没有指定的话则默认使用 default 这个 sa。然后通过投射卷,在 Pod 的目录 /run/secrets/kubernetes.io/serviceaccount/ 下有一个 token 令牌文件。我们通过 RBAC 对该 sa 授予了什么权限,那么容器里的应用拿着这个 token 后,就具备了对应的权限。但是需要注意的是不同的 K8s 版本对该 token 文件的使用是不一样的,所以我们这里分别进行下简单说明。☸ ➜ kind create cluster --name kind120 --image kindest/node:v1.20.15
☸ ➜ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind120-control-plane Ready control-plane,master 33s v1.20.15☸ ➜ kubectl create sa sa-demo
☸ ➜ kubectl get sa
NAME SECRETS AGE
default 1 43s
sa-demo 1 6s
☸ ➜ kubectl get secret
NAME TYPE DATA AGE
default-token-dv78w kubernetes.io/service-account-token 3 46s
sa-demo-token-4gvbw kubernetes.io/service-account-token 3 8s☸ ➜ kubectl describe secrets sa-demo-token-4gvbw
Name: sa-demo-token-4gvbw
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: sa-demo
kubernetes.io/service-account.uid: 1ae8eea9-acc6-4e3d-b378-07feb9146ac4
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1066 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImhQNmFMNjAyaDZ5OElyMmtTNGdPUWxRdHVDU1A4aGFfVkJiNHdHMkZjQlUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhLWRlbW8tdG9rZW4tNGd2YnciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoic2EtZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjFhZThlZWE5LWFjYzYtNGUzZC1iMzc4LTA3ZmViOTE0NmFjNCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OnNhLWRlbW8ifQ.j0DQmzTeSfagKYGc2dMUuzhYqVQh2puJAoQS0EMKeiAKD6rC4bUHWXWBrCu5Ttvpch6ZTEYwyCdRof1lDGWiLa3pJ1R1RwUNVQTCmVTZPs7tTuoGLRW0KGfEd0jyi4LU6uw4kA_6kwEsz4q2quWcB_fiH_Z3iKVfh1JolYTVAWTBMWnVn6gBvIrlXV5ny2oyvcPQeVfIek8aPQqhbsct_qOxrjqpZY8mpBz0ETR_EELjmcZxVVPLvomOdCqEqbV-FF5KRiFxizB3Xoh6NHz3EcsxpCZNRYdand-UFHaBQC9IPwJKzxhANGmuZuWJUCqCVGGRZTo9c6eoyVz831sZ0A☸ ➜ kubectl get secrets sa-demo-token-4gvbw -o jsnotallow='{.data.token}' | base64 -d
右侧部分显示了 token 被解码之后的内容,其中 PAYLOAD 部分是 token 里包含的 sa-demo 的信息,可以看到里面没有过期时间,也说明了该 token 是永不过期的。现在我们使用上面我们创建的 sa 来运行一个 Pod:# demo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
serviceAccount: sa-demo
containers:
- name: demo
image: nginx:1.7.9
ports:
- containerPort: 80☸ ➜ kubectl apply -f demo-pod.yaml
☸ ➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
demo 1/1 Running 0 81s
☸ ➜ kubectl get pod demo -oyaml
apiVersion: v1
kind: Pod
metadata:
name: demo
namespace: default
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: demo
# ......
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: sa-demo-token-4gvbw
readOnly: true
# ......
volumes:
- name: sa-demo-token-4gvbw
secret:
defaultMode: 420
secretName: sa-demo-token-4gvbw☸ ➜ kubectl exec -it demo -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImhQNmFMNjAyaDZ5OElyMmtTNGdPUWxRdHVDU1A4aGFfVkJiNHdHMkZjQlUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhLWRlbW8tdG9rZW4tNGd2YnciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoic2EtZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjFhZThlZWE5LWFjYzYtNGUzZC1iMzc4LTA3ZmViOTE0NmFjNCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OnNhLWRlbW8ifQ.j0DQmzTeSfagKYGc2dMUuzhYqVQh2puJAoQS0EMKeiAKD6rC4bUHWXWBrCu5Ttvpch6ZTEYwyCdRof1lDGWiLa3pJ1R1RwUNVQTCmVTZPs7tTuoGLRW0KGfEd0jyi4LU6uw4kA_6kwEsz4q2quWcB_fiH_Z3iKVfh1JolYTVAWTBMWnVn6gBvIrlXV5ny2oyvcPQeVfIek8aPQqhbsct_qOxrjqpZY8mpBz0ETR_EELjmcZxVVPLvomOdCqEqbV-FF5KRiFxizB3Xoh6NHz3EcsxpCZNRYdand-UFHaBQC9IPwJKzxhANGmuZuWJUCqCVGGRZTo9c6eoyVz831sZ0A☸ ➜ kind create cluster --name kind122 --image kindest/node:v1.22.15
☸ ➜ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind122-control-plane Ready control-plane,master 115s v1.22.15☸ ➜ kubectl create sa sa-demo
☸ ➜ kubectl get sa
NAME SECRETS AGE
default 1 43s
sa-demo 1 6s
☸ ➜ kubectl get secret
NAME TYPE DATA AGE
default-token-9w9bp kubernetes.io/service-account-token 3 116s
sa-demo-token-g7d2g kubernetes.io/service-account-token 3 8s☸ ➜ kubectl get secrets sa-demo-token-g7d2g -o jsnotallow='{.data.token}' | base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6Im1ERkhnQ3Y3b1oxUmNHbWVhN210SDEwNXY2dVNkc0QzdXJjTkhsY21FRVEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhLWRlbW8tdG9rZW4tZzdkMmciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoic2EtZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjI3ZGI0M2FjLTdjYjItNDQ2Yi05N2Q1LWU0MGUzOWRjZTg4YyIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OnNhLWRlbW8ifQ.fnSaqrZKolTfz2pi9t32X38Er60WSzUoRHArte6qVmQ1NTaMis4F6rESWekeJvGW26szTJdll6vK8KtL_IRO2m6sp_fEAYfNMQMXL4CuaRByXeAavDqLgMHhodf4k4Yg-Mj4LCQ3aHOxojbAbPT1i_h17Ewivc39fmzp-dAXbHhhWhCW2Vl_CkM-F-UtzLyDwThvJedkeetrfyOOjE7K6HpzWfqIQyMUdCJog3WnFO_4kHXacFCgYg_gNPMYyViQAsTsxB8FplGdEzRuWKnQO9cDE55V4l55IxmE0er-dSSdG8085PzxaM_lMCtRI8YtjRjxcbxS5QkTm5R_ps0IsA
从解码后的值可以看到该 token 值里面同样不包含任何过期时间,也说明了我们创建 sa 之后,所对应的 token 是永不过期的。同样我们再次使用上面的 sa 来创建一个 Pod,如下所示:# demo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
serviceAccount: sa-demo
containers:
- name: demo
image: nginx:1.7.9
ports:
- containerPort: 80☸ ➜ kubectl apply -f demo-pod.yaml
☸ ➜ kubectl get pods
NAME READY STATUS RESTARTS AGE
demo 1/1 Running 0 81s
☸ ➜ kubectl get pod demo -oyaml
apiVersion: v1
kind: Pod
metadata:
name: demo
namespace: default
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: demo
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-6wmfb
readOnly: true
# ......
volumes:
- name: kube-api-access-6wmfb
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace☸ ➜ kubectl exec -it demo -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6Im1ERkhnQ3Y3b1oxUmNHbWVhN210SDEwNXY2dVNkc0QzdXJjTkhsY21FRVEifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzA1MDI1NDU4LCJpYXQiOjE2NzM0ODk0NTgsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJkZW1vIiwidWlkIjoiNzY1ODRmODAtZjU1My00Mzk2LWIxOTUtMDEwOTBhMzM4MWYyIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJzYS1kZW1vIiwidWlkIjoiMjdkYjQzYWMtN2NiMi00NDZiLTk3ZDUtZTQwZTM5ZGNlODhjIn0sIndhcm5hZnRlciI6MTY3MzQ5MzA2NX0sIm5iZiI6MTY3MzQ4OTQ1OCwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6c2EtZGVtbyJ9.TAoe1eCHCXUoHh6oM4uySp8kzRaLQ44GZdU02Ir8m_dzYpdFSw4nwsNyqPggrZdDL3BMH4zceudBEdQuyxiSsrpVDeQKww2wTGhXAr2hWujrJq4ycmu6aMywyv2iRX9Vn-Las1giWK_bFuzCxiR10Lcgyd5N7VjB2WcT7K8rN7dAeUWgiH2s9lMOzoaIorUDXzlnSTcmxkhz1h7RXYKVGaqZBbd5wJsRnINZPGxqsS-wi21Aw2FFmIeeK8GGlnAqnS0f3VS1N2jm03gKPii-sMt0GARse4HsmhGAhyJnt9za6ZNpBgcybd7uEBjgIVrRFTkqBJOjPrAnMvRucVtwww
可以看到该 token 值解码后的 PAYLOAD 数据中包含了很多不同的数据,其中的 exp 字段表示该 token 的过期时间,可以看到过期时间是 1 年。这里我们可以总结下在 v1.21 到 v1.23 版本的 K8s 集群,当创建 ServiceAccount 对象后,系统仍然会自动创建一个 secret 对象,该 secret 对象里面包含的 token 仍然是永不过期的,但是 Pod 里面并不会使用该 secret 的 token 值了。从上面查看创建后的 Pod 资源清单可以看出,现在创建 Pod 后,Kubernetes 控制平面会自动添加一个投射卷到 Pod,此卷包括了访问 Kubernetes API 的 token,该清单片段定义了由三个数据源组成的投射卷,这三个数据源是:☸ ➜ kubectl create token -h
Request a service account token.
Examples:
# Request a token to authenticate to the kube-apiserver as the service account "myapp" in the current namespace
kubectl create token myapp
# Request a token for a service account in a custom namespace
kubectl create token myapp --namespace myns
# Request a token with a custom expiration
kubectl create token myapp --duration 10m
# Request a token with a custom audience
kubectl create token myapp --audience https://example.com
# Request a token bound to an instance of a Secret object
kubectl create token myapp --bound-object-kind Secret --bound-object-name mysecret
# Request a token bound to an instance of a Secret object with a specific uid
kubectl create token myapp --bound-object-kind Secret --bound-object-name mysecret --bound-object-uid
0d4691ed-659b-4935-a832-355f77ee47cc
Options:
# ......☸ ➜ kind create cluster --name kind125 --image kindest/node:v1.25.3
☸ ➜ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind125-control-plane Ready control-plane,master 115s v1.25.3☸ ➜ kubectl create sa sa-demo
☸ ➜ kubectl get sa
NAME SECRETS AGE
default 0 39d
sa-demo 0 5s
☸ ➜ kubectl get secrets
No resources found in ns1 namespace# demo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
serviceAccount: sa-demo
containers:
- name: demo
image: nginx:1.7.9
ports:
- containerPort: 80☸ ➜ kubectl apply -f demo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
namespace: default
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: demo
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-pftqd
readOnly: true
# ......
volumes:
- name: kube-api-access-pftqd
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace☸ ➜ kubectl exec -it demo -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6IndnazJLZENQTktiZkxVejhnMnhmTHJYRTlkZ2ZnOHJGQmgwVW4td3BWd0kifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzA0ODg0MDg0LCJpYXQiOjE2NzMzNDgwODQsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJkZW1vIiwidWlkIjoiMTY0ZTIwZTYtYjNjMi00ZmQ5LWI3ZTUtMDZjYTExZWIyOWM4In0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJzYS1kZW1vIiwidWlkIjoiYjJlNWM3ZmYtNjlhNy00NzYyLTkxMDctM2UxNzZhYmQ3NTdiIn0sIndhcm5hZnRlciI6MTY3MzM1MTY5MX0sIm5iZiI6MTY3MzM0ODA4NCwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6c2EtZGVtbyJ9.lhYscyn_d9Y3GZSipSqGj4Jtsu8qsIyz34L18lv37HxjjGU_bQmUFCXYf_CRom8DfadHppmlaskZS18KmyTV1Z09BeujJd8viUnnYCWb9K6VJB5uPBYWLB0FETfgQy7Kqu8Gvk8qBKLjdCkl8U2vr2Oqd2qSEDyvqhNBQXnckQRH6wyypBUc7EXSGAJf6dPVE3c6XqnbXMJ7SRZb5svE-hv0lZKmJrouz9Ia4qxUXUtpzDlMPnHOym2x9d1TSSZ1Lp7BOsqTnxlUQVueh9w869jAajrP1G9e5zhZwZBfzRfARqCVqoLid_hOQP-mo4MLfHbn61SWItlCBd75nl2WLQ
从上面的数据可以看到这里的 token 的有效期也为 1 年,这个 token 在 Pod 里也是每 1 小时会更新一次,如果 Pod 被删除重建,那么会重新申领一个新的 token,被删除 Pod 里的 token 立即过期。需要注意的没有特定的机制使通过 TokenRequest 签发的令牌无效,如果你不再信任为某个 Pod 绑定的 ServiceAccount 令牌,你可以删除该 Pod,删除 Pod 将使其绑定的令牌过期。尽管存在手动创建长久 ServiceAccount 令牌的机制,但还是推荐使用 TokenRequest 获得短期的 API 访问令牌。
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。