Istio 部署 gRPC 服务并配置网关证书
gRPC 环境
gRPC 是 Google公司基于 Protobuf 开发的跨语言的开源 RPC 框架。gRPC 基于 HTTP/2 协议设计,可以基于一个HTTP/2链接提供多个服务
安装
1. protobuf
从 protobuf 这里下载,把 bin 目录加入环境变量
2. go protobuf 库
go get -u github.com/golang/protobuf@v1.5.2
3. go 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
该插件会根据 .proto
文件生成一个后缀为 .pb.go
的文件,包含所有 .proto
文件中定义的类型及其序列化方法
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
该插件会生成一个后缀为 _grpc.pb.go
的文件,其中包含:
- 一种接口类型(或存根) ,供客户端调用的服务方法。
- 服务器要实现的接口类型。
上述命令会默认将插件安装到 $GOPATH/bin
,为了 protoc
编译器能找到这些插件,请确保你的$GOPATH/bin
在环境变量中
4. grpc 库
go get -u google.golang.org/grpc@v1.46.2
编写 .proto 定义服务
src/prod_model.proto:
syntax = "proto3";
option go_package = "src/pbfiles";
message ProdRequest {
int32 prod_id =1;
}
message ProdModel {
int32 id=1;
string name=2;
}
message ProdResponse {
ProdModel result=1;
}
src/prod_service.proto:
syntax = "proto3";
import "prod_model.proto";
option go_package = "src/pbfiles";
service ProdService {
rpc GetProd(ProdRequest) returns (ProdResponse);
}
在项目更目录执行以下命令,根据 proto 生成 go 源码文件
protoc320 --proto_path=src --go_out=./ prod_model.proto
protoc320 --proto_path=src --go-grpc_out=./ prod_service.proto
编写 server 端:
type ProdService struct {
pbfiles.UnimplementedProdServiceServer
}
func NewProdService() *ProdService {
return &ProdService{}
}
func (this *ProdService) GetProd(ctx context.Context, req *pbfiles.ProdRequest) (*pbfiles.ProdResponse, error) {
model := &pbfiles.ProdModel{
Id: req.ProdId,
Name: fmt.Sprintf("%s%d", "测试商品", req.ProdId),
}
rsp := &pbfiles.ProdResponse{Result: model}
return rsp, nil
}
func main() {
myserver := grpc.NewServer()
// 创建服务
pbfiles.RegisterProdServiceServer(myserver, services.NewProdService())
// 监听8080
lis, _ := net.Listen("tcp", ":8080")
if err := myserver.Serve(lis); err != nil {
log.Fatal(err)
}
}
测试客户端
func main() {
client, err := grpc.DialContext(context.Background(), ":8080", grpc.WithInsecure())
rsp := &pbfiles.ProdResponse{}
err = client.Invoke(context.Background(),
"/ProdService/GetProd",
&pbfiles.ProdRequest{ProdId: 123}, rsp)
if err != nil {
log.Fatal(err)
}
fmt.Println(rsp.Result)
}
部署网格内 gRPC 服务
示例如下
创建 gRPC Gate 网关
demo.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
components:
egressGateways:
- enabled: true
name: istio-egressgateway
ingressGateways:
- enabled: true
name: istio-ingressgateway
- enabled: true
label:
app: grpc-ingressgateway # 自定义标签
istio: grpc-ingressgateway
k8s:
resources:
requests:
cpu: 10m
memory: 40Mi
service:
ports:
- name: status-port
port: 15021
targetPort: 15021
- name: http2
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
- name: tcp
port: 31400
targetPort: 31400
- name: tls
port: 15443
targetPort: 15443
name: grpc-ingressgateway
执行部署:
istioctl install -f demo.yaml
创建网关规则
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: grpc-gateway
namespace: myistio
spec:
selector:
istio: grpc-ingressgateway
servers:
- port:
number: 80
name: grpc
protocol: HTTPS
hosts:
- "*"
创建虚拟服务
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: grpcvs
namespace: myistio
spec:
hosts:
- "*"
gateways:
- grpc-gateway
http:
- route:
- destination:
host: gprodsvc
port:
number: 80
配置网关证书
配置方式
1. 文件挂载的方式
Istio 网关将会自动加载 istio-system 命名空间下名称为 stio-ingressgateway-certs
的 secret,并分别挂载到 /etc/istio/ingressgateway-certs/tls.crt
和 /etc/istio/ingressgateway-certs/tls.key
。指定 serverCertificate
和 privateKey
字段
2. 指定密钥的方式
指定 credentialName
字段
配置 HTTP TLS 证书
kubectl create -n istio-system secret tls istio-ingressgateway-certs --key api.virtuallain.com.key --cert api.virtuallain.com.crt
Gateway 加入 tls 节点:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: p-gateway
namespace: myistio
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- api.virtuallain.com
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
serverCertificate: /etc/istio/ingressgateway-certs/tls.crt # 使用挂载证书文件
privateKey: /etc/istio/ingressgateway-certs/tls.key
# credentialName: ssl-ingressgateway-certs
hosts:
- api.virtuallain.com
配置 gRPC 证书(单向认证)
使用 certstrap 开源库生成证书
1. 自签 CA 证书
./cert init --common-name "virtuallainCA" --expires "20 years"
2. 服务端证书
# 得到证书请求文件
./cert request-cert -cn grpc.virtuallain.com -domain "*.virtuallain.com"
# CA去签名请求文件
./cert sign grpc.virtuallain.com --CA virtuallainCA
3. 导入 k8s
kubectl create -n istio-system secret tls grpc-ingressgateway-certs --key=grpc.virtuallain.com.key --cert=grpc.virtuallain.com.crt
# 效果同上
kubectl create -n istio-system secret generic grpc-ingressgateway-certs --from-file=key=grpc.virtuallain.com.key --from-file=cert=grpc.virtuallain.com.crt
4. 配置 Gateway
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: grpc-gateway
namespace: myistio
spec:
selector:
istio: grpc-ingressgateway
servers:
- port:
number: 80
name: grpc
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: grpc-ingressgateway-certs
hosts:
- "*"
5. 客户端测试
func main() {
creds, err := credentials.NewClientTLSFromFile("tools/out/grpc.virtuallain.com.crt",
"grpc.virtuallain.com")
if err != nil {
log.Fatal(err)
}
client, err := grpc.DialContext(context.Background(),
"grpc.virtuallain.com:30090",
grpc.WithTransportCredentials(creds))
if err != nil {
log.Fatal(err)
}
rsp := &pbfiles.ProdResponse{}
err = client.Invoke(context.Background(),
"/ProdService/GetProd",
&pbfiles.ProdRequest{ProdId: 123}, rsp)
if err != nil {
log.Fatal(err)
}
fmt.Println(rsp.Result)
}
配置 gRPC 证书(双向认证)
1. 同步生成客户端证书
./cert request-cert -cn clientgrpc
./cert sign clientgrpc --CA virtuallainCA
2. 重新导入证书
kubectl create -n istio-system secret generic grpc-ingressgateway-certs --from-file=key=grpc.virtuallain.com.key --from-file=cert=grpc.virtuallain.com.crt --from-file=cacert=virtuallainCA.crt
3. 配置 Gateway
修改 mode 为 MUTUAL
tls:
mode: MUTUAL
credentialName: grpc-ingressgateway-certs
4. 客户端测试
func main() {
cert, err := tls.LoadX509KeyPair("tools/out/clientgrpc.crt", "tools/out/clientgrpc.key")
if err != nil {
log.Fatal(err)
}
certPool := x509.NewCertPool()
ca, err := os.ReadFile("tools/out/virtuallainCA.crt")
if err != nil {
log.Fatal(err)
}
certPool.AppendCertsFromPEM(ca)
creds := credentials.NewTLS(&tls.Config{
Certificates: []tls.Certificate{cert},
ServerName: "grpc.virtuallain.com",
RootCAs: certPool,
})
client, err := grpc.DialContext(context.Background(),
"grpc.virtuallain.com:30090",
grpc.WithTransportCredentials(creds))
rsp := &pbfiles.ProdResponse{}
err = client.Invoke(context.Background(),
"/ProdService/GetProd",
&pbfiles.ProdRequest{ProdId: 123}, rsp)
if err != nil {
log.Fatal(err)
}
fmt.Println(rsp.Result)
}