如果使用 Knative Serving 部署一個 Nginx 你可能會發現服務起來了,但是無法通路到 Nginx 中的服務。當然這不是 Nginx 的問題,這是因為 Knative 對 Container 的端口有要求。預設 Nginx 的服務端口是 80 ,而 Knative Serving queue 8012 預設是代理到容器的 8080 端口。是以如果業務容器監聽的不是 8080 端口預設配置就不能對外暴露服務。具體的 spec 定義見這裡: https://github.com/kubedemo/serving/blob/v0.6.0/docs/spec/spec.md
注:當然 如果想要監聽多個端口也是不行的,雖然容器能夠啟動成功,都是無法通過 Service 暴露服務,進而無法通過 Istio gateway 暴露到外面,是以也就不能提供服務。
當你在檢視上述 Spec 定義的時候你可能已經注意到了下面這兩行注釋。為什麼會保留這幾個端口呢?

下面我通過一張圖來解釋一下 Knative Serving 流量轉發鍊路:
如上圖所示,業務流量并不是從 Gateway 直接打到業務容器中的,而是經過 queue 容器轉發的。queue 容器預設會監聽 8012 和 8013 分别用于轉發 http1 的流量和 http2 的流量。具體代碼參見:
queue 容器在建立的時候會設定一個叫做 USER_PORT 的環境變量,queue 是通過這個環境變量來擷取應該轉發到業務容器的哪個端口的。是以隻要我們能夠修改 USER_PORT 這個環境變量我們就能自定義監聽端口了。 USER_PORT 是通過 getUserPort 函數擷取的,具體函數定義如下:
代碼讀到這裡就已經很明朗了,建立 Knative Service 的時候可以指定 Ports 端口,然後可以通過端口的 containerPort 字段指定自定義端口。不過需要注意的是 Knative 隻支援一個容器端口,是以雖然 Ports 是一個數組,但也隻能設定一個。
執行個體如下:
apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
name: wordpress-serving
namespace: default
spec:
template:
metadata:
labels:
app: wordpress
annotations:
autoscaling.knative.dev/target: "100"
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/knative-sample/wordpress:5.2-20190524100810
ports:
- name: http1
containerPort: 80
env:
- name: WORDPRESS_DB_HOST
value: rm-2xx.mysql.rds.aliyuncs.com:3306
- name: WORDPRESS_DB_USER
value: wordpress
- name: WORDPRESS_DB_PASSWORD
value: xxx
imagePullPolicy: Always
上面在分析的過程中提到了 HTTP2 協定,如果目前提供的是 grpc 類型的服務就需要使用 http2 協定。
如果使用的是 HTTP2 協定那麼也去請求就需要轉發到 queue 的 8013 端口上,然後由 8013 轉發給容器的業務端口。那麼如何指定我們使用的協定呢?
在前面的分析中我們得出的結論是通過 port[0].containerPort 來指定自定義端口,其實也可以指定 port[0].name 字段。而 Revision Controller 生成 Deployment 部署服務的時候就是這個 name 字段來判斷應該使用 http1(8012端口) 協定還是 http2(8013端口) 協定的。
- http1 代表使用 http1(8012端口) 協定,同時 http1 也是預設政策,也就是說如果未指定那麼預設也是 http1 協定
- h2c 代表使用 http2(8013端口) 協定
代碼分析如下:
https://github.com/kubedemo/serving/blob/release-0.6/pkg/apis/serving/v1alpha1/revision_lifecycle.go#L104那麼怎樣才能指定容器的自定義端口呢?通過查閱代碼可知
https://github.com/kubedemo/serving/blob/release-0.6/pkg/reconciler/serverlessservice/resources/services.go#L32小結
Knative Service 可以給容器添加 ports 字段,并且 ports 隻能設定一個端口。ports 有兩個關鍵字段 Name 和 ContainerPort
ports:
- name: http1
containerPort: 80
- name 字段表示使用的協定,有效值有 http1 和 h2c 兩個,其中:
- http1 表示使用 http1 協定,比如 web 服務和 websock 都可以使用 http1
- grpc 需要設定成 h2c
- containerPort 字段就是容器提供服務的唯一端口。業務端口可以設定 1-65535 這個範圍中除了 8012、8013、8022、8091 和 8092 以外的任意端口