Skip to content

Commit

Permalink
add kubernetes ingress controller (close #25) (#102)
Browse files Browse the repository at this point in the history
* add kubernetes ingress controller (close #25)

* update according to review comments

* update documentation

* update go.mod to fix test failure

* minor refactor according to review comment

Co-authored-by: Bomin Zhang <[email protected]>
  • Loading branch information
localvar and localvar authored Jul 8, 2021
1 parent d6d7969 commit 1f1e9b0
Show file tree
Hide file tree
Showing 9 changed files with 1,171 additions and 8 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
- [Easegress](#easegress)
- [What is Easegress](#what-is-easegress)
- [Features](#features)
- [Get Started](#get-started)
- [Getting Started](#getting-started)
- [Setting up Easegress](#setting-up-easegress)
- [Create an HTTPServer and Pipeline](#create-an-httpserver-and-pipeline)
- [Test](#test)
Expand All @@ -15,15 +15,15 @@
## What is Easegress
<a href="https://megaease.com/easegress">
<img src="./doc/easegress.svg"
alt="Easegress logo" title="Easegress" height="100" width="100" align="right"/>
alt="Easegress logo" title="Easegress" height="100" width="100" align="right"/>
</a>

`Easegress` is a Cloud Native traffic orchestration system designed for:

- **High Availability:** Built-in Raft consensus & leader election provides 99.99% availability.
- **Traffic Orchestration:** Simple orchestration of various filters for each traffic pipeline.
- **High Performance:** Lightweight and essential features speed up the performance.
- **Observability:** There are many meaningful statistics periodically in a readable way.
- **Observability:** There are many meaningful statistics periodically in a readable way.
- **Extensibility:** It's easy to develop your own filter or controller with high-level programming language.
- **Integration:** The simple interfaces make it easy to integrate with other systems, such as Kubernetes Ingress, EaseMesh(open source coming soon) sidecar, Workflow, etc.

Expand Down Expand Up @@ -68,6 +68,7 @@ The architecture of Easegress:
- **Third-Part Integration**
- **FaaS** integrates with the serverless platform Knative.
- **Service Discovery** integrates with Eureka, Consul, Etcd, and Zookeeper.
- **Ingress Controller** integrates with Kubernetes as an ingress controller.
- **High Performance and Availability**
- **Adaption**: adapts request, response in the handling chain.
- **Validation**: headers validation, OAuth2, JWT, and HMAC verification.
Expand All @@ -78,7 +79,7 @@ The architecture of Easegress:
- **Operation**
- **Easy to Integrate:** command line(`egctl`), MegaEase Portal, HTTP clients such as curl, postman, etc.
- **Distributed Tracing**
- Built-in [Open Zipkin](https://zipkin.io/)
- Built-in [Open Zipkin](https://zipkin.io/)
- [Open Tracing](https://opentracing.io/) for vendor-neutral APIs
- **Observability**
- **Node:** role(leader, writer, reader), health or not, last heartbeat time, and so on
Expand All @@ -89,7 +90,7 @@ The architecture of Easegress:
- **Status Codes:** HTTP status codes.
- **TopN:** sorted by aggregated APIs(only in server dimension).

## Get Started
## Getting Started

The basic common usage of Easegress is to quickly set up proxy for the backend servers. We split it into multiple simple steps to illustrate the essential concepts and operations.

Expand Down
32 changes: 31 additions & 1 deletion doc/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- [Business Controllers](#business-controllers)
- [EaseMonitorMetrics](#easemonitormetrics)
- [Function](#function)
- [IngressController](#ingresscontroller)
- [MeshController](#meshcontroller)
- [ConsulServiceRegistry](#consulserviceregistry)
- [EtcdServiceRegistry](#etcdserviceregistry)
Expand Down Expand Up @@ -136,9 +137,38 @@ kafka:
TODO (@ben)
### IngressController
The IngressController is an implementation of [Kubernetes ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/), it watches Kubernetes Ingress, Service, Endpoints, and Secrets then translates them to Easegress HTTP server and pipelines. The config looks like:
```yaml
kind: IngressController
name: ingress-controller-example
kubeConfig:
masterURL:
namespaces: ["default"]
ingressClass: easegress
httpServer:
port: 8080
https: false
keepAlive: true
keepAliveTimeout: 60s
maxConnections: 10240
```
| Name | Type | Description | Required |
| ------------ | -------- | ------------------------------------------------------------------------- | --------------------- |
| kubeConfig | string | Path of the Kubernetes configuration file. | No |
| masterURL | string | The address of the Kubernetes API server. | No |
| namespaces | []string | An array of Kubernetes namespaces which the IngressController needs to watch, all namespaces are watched if left empty. | No |
| ingressClass | string | The IngressController only handles `Ingresses` with `ingressClassName` set to the value of this option. | No (default: easegress) |
| httpServer | [httpserver.Spec](#httpserver) | Basic configuration for the shared HTTP traffic gate. The routing rules will be generated dynamically according to Kubernetes ingresses and should not be specified here. | Yes |

**Note**: IngressController uses `kubeConfig` and `masterURL` to connect to Kubernetes, at least one of them must be specified when deployed outside of a Kubernetes cluster, and both are optional when deployed inside a cluster.

### MeshController

MeshController contains the ingress controller, master(control plane), worker/sidecar(data plane). The config looks like:
MeshController contains the ingress controller (note this ingress controller is for mesh deployment, not the [Kubernetes ingress controller](#ingresscontroller) described above), master(control plane), worker/sidecar(data plane). The config looks like:

```yaml
name: mesh-controller-example
Expand Down
252 changes: 252 additions & 0 deletions doc/ingresscontroller.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
# IngressController


The IngressController is an implementation of [Kubernetes ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/), it watches Kubernetes Ingress, Service, Endpoints, and Secrets then translates them to Easegress HTTP server and pipelines.

## Prerequisites
1. K8s cluster : **v1.18+**

## Configuration

### Controller spec

```yaml
kind: IngressController
name: ingress-controller-example
kubeConfig:
masterURL:
namespaces: ["default"]
ingressClass: easegress
httpServer:
port: 8080
https: false
keepAlive: true
keepAliveTimeout: 60s
maxConnections: 10240
```
* IngressController uses `kubeConfig` and `masterURL` to connect to Kubernetes, at least one of them must be specified when deployed outside of a Kubernetes cluster, and both are optional when deployed inside a cluster.
* The `namespaces` is an array of Kubernetes namespaces which the IngressController needs to watch, all namespaces are watched if left empty.
* IngressController only handles `Ingresses` with `ingressClassName` set to `ingressClass`, the default value of `ingressClass` is `easegress`.
* One IngressController manages a shared HTTP traffic gate and multiple pipelines according to the Kubernetes ingress. The `httpServer` section in the spec is the basic configuration for the shared HTTP traffic gate. The routing part of the HTTP server and pipeline configurations will be generated dynamically according to Kubernetes ingresses.

## Getting Started

### Role Based Access Control configuration

If your cluster is configured with RBAC, you will need to authorize Easegress IngressController for using the Kubernetes API firstly. Below is an example configuration:

```yaml
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: easegress-ingress-controller
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["services", "endpoints", "secrets"]
verbs: ["get", "watch", "list"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "watch", "list"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: easegress-ingress-controller
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: easegress-ingress-controller
namespace: default
subjects:
- kind: ServiceAccount
name: easegress-ingress-controller
namespace: default
roleRef:
kind: ClusterRole
name: easegress-ingress-controller
apiGroup: rbac.authorization.k8s.io
```

Note the name of the ServiceAccount we just created is `easegress-ingress-controller`, it will be used later.

### Deploy Easegress IngressController

To deploy the IngressController, we will create a Deployment and a Service as below:

```yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
name: easegress-ingress
name: easegress-ingress
spec:
replicas: 1
selector:
matchLabels:
app: easegress-ingress
template:
metadata:
labels:
app: easegress-ingress
spec:
serviceAccountName: easegress-ingress-controller
containers:
- command:
- /bin/sh
- -c
- |-
echo name: $POD_NAME > /easegress-ingress/config.yaml && echo '
cluster-request-timeout: 10s
cluster-role: writer
api-addr: 0.0.0.0:2381
cluster-name: easegress-ingress-controller
' >> /easegress-ingress/config.yaml && /opt/easegress/bin/easegress-server -f /easegress-ingress/config.yaml
image: megaease/easegress:latest
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
name: easegress-ingress
volumeMounts:
- mountPath: /easegress-ingress
name: ingress-params-volume
volumes:
- emptyDir: {}
name: ingress-params-volume
---
kind: Service
apiVersion: v1
metadata:
name: easegress-ingress-service
namespace: default
spec:
selector:
app: easegress-ingress
ports:
- name: admin
protocol: TCP
port: 2381
nodePort: 2381
- name: web
protocol: TCP
port: 8080
nodePort: 80
type: NodePort
```

The service exposes two ports, the `admin` port is for Easegress administration, we exposed it for simplicity here, but for security consideration, we don't recommend you to expose it in your environment; the `web` port is to receive external HTTP requests and forward them to the HTTP server in Easegress.

Now let's create the IngressController with the below command (suppose the spec in [Controller Spec](#controller-spec) has been saved to a file named `controller.yaml`):

```bash
curl -XPOST --data-binary @controller.yaml http://{NODE_IP}:2381/apis/v1/objects
```

### Create backend service & Kubernetes ingress

Apply below YAML configuration to Kubernetes:

```yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-deployment
spec:
selector:
matchLabels:
app: products
department: sales
replicas: 2
template:
metadata:
labels:
app: products
department: sales
spec:
containers:
- name: hello-v1
image: "gcr.io/google-samples/node-hello:1.0"
env:
- name: "PORT"
value: "50001"
- name: hello-v2
image: "gcr.io/google-samples/hello-app:2.0"
env:
- name: "PORT"
value: "50002"
---
apiVersion: v1
kind: Service
metadata:
name: hello-service
spec:
type: NodePort
selector:
app: products
department: sales
ports:
- name: port_v1
protocol: TCP
port: 60001
targetPort: 50001
- name: port_v2
protocol: TCP
port: 60002
targetPort: 50002
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-example
spec:
ingressClassName: easegress
rules:
- host: "www.example.com"
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: hello-service
port:
number: 60001
- host: "*.megaease.com"
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: hello-service
port:
number: 60002
```

After a while, we can leverage the below command to access both versions of the `hello` application:

```bash
$ curl http://{NODE_IP}/ -HHost:www.megaease.com
Hello, world!
Version: 2.0.0
Hostname: hello-deployment-6cbf765985-r6242
$ curl http://{NODE_IP}/ -HHost:www.example.com
Hello Kubernetes!
```

And we can see Easegress IngressController has forwarded requests to the correct application version according to Kubernetes ingress.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ require (
k8s.io/api v0.20.7
k8s.io/apimachinery v0.20.7
k8s.io/cli-runtime v0.20.7 // indirect
k8s.io/client-go v0.20.7
knative.dev/client v0.23.1
knative.dev/serving v0.23.1-0.20210614141420-380a090c2039
)
Expand Down
4 changes: 2 additions & 2 deletions pkg/object/httpserver/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func (spec *Spec) Validate() error {
}

if spec.HTTPS {
if spec.CertBase64 == "" && spec.KeyBase64 == "" && spec.Certs == nil && spec.Keys == nil {
if spec.CertBase64 == "" && spec.KeyBase64 == "" && len(spec.Certs) == 0 && len(spec.Keys) == 0 {
return fmt.Errorf("certBase64/keyBase64, certs/keys are both empty when https enabled")
}
_, err := spec.tlsConfig()
Expand Down Expand Up @@ -130,7 +130,7 @@ func (spec *Spec) tlsConfig() (*tls.Config, error) {
if secret, exists := spec.Keys[k]; exists {
cert, err := tls.X509KeyPair([]byte(v), []byte(secret))
if err != nil {
return nil, fmt.Errorf("generate x5099 key pair for %s failed: %s ", k, err)
return nil, fmt.Errorf("generate x509 key pair for %s failed: %s ", k, err)
}
certificates = append(certificates, cert)
} else {
Expand Down
Loading

0 comments on commit 1f1e9b0

Please sign in to comment.