Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Envoy support (part 1) #734

Merged
merged 9 commits into from
Dec 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions cloud-resource-manager/platform/dind/dind-appinst.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

"github.com/mobiledgex/edge-cloud/cloud-resource-manager/dockermgmt"
"github.com/mobiledgex/edge-cloud/cloud-resource-manager/k8smgmt"
"github.com/mobiledgex/edge-cloud/cloud-resource-manager/nginx"
"github.com/mobiledgex/edge-cloud/cloud-resource-manager/proxy"
"github.com/mobiledgex/edge-cloud/cloud-resource-manager/platform/pc"
"github.com/mobiledgex/edge-cloud/cloudcommon"
"github.com/mobiledgex/edge-cloud/edgeproto"
Expand Down Expand Up @@ -36,21 +36,21 @@ func (s *Platform) CreateAppInst(ctx context.Context, clusterInst *edgeproto.Clu
}
// NOTE: for DIND we don't check whether this is internal
if len(appInst.MappedPorts) > 0 {
log.SpanLog(ctx, log.DebugLevelMexos, "AddNginxProxy for dind", "ports", appInst.MappedPorts)
log.SpanLog(ctx, log.DebugLevelMexos, "Add Proxy for dind", "ports", appInst.MappedPorts)
cluster, err := FindCluster(names.ClusterName)
if err != nil {
return err
}
masterIP := cluster.MasterAddr
network := GetDockerNetworkName(cluster)
err = nginx.CreateNginxProxy(client,
err = proxy.CreateNginxProxy(ctx, client,
names.AppName,
masterIP,
appInst.MappedPorts,
nginx.WithDockerNetwork(network),
nginx.WithDockerPublishPorts())
proxy.WithDockerNetwork(network),
proxy.WithDockerPublishPorts())
if err != nil {
log.SpanLog(ctx, log.DebugLevelMexos, "cannot add nginx proxy", "appName", names.AppName, "ports", appInst.MappedPorts)
log.SpanLog(ctx, log.DebugLevelMexos, "cannot add proxy", "appName", names.AppName, "ports", appInst.MappedPorts)
return err
}
}
Expand Down Expand Up @@ -105,8 +105,8 @@ func (s *Platform) DeleteAppInst(ctx context.Context, clusterInst *edgeproto.Clu

if len(appInst.MappedPorts) > 0 {
log.SpanLog(ctx, log.DebugLevelMexos, "DeleteNginxProxy for dind")
if err = nginx.DeleteNginxProxy(client, names.AppName); err != nil {
log.SpanLog(ctx, log.DebugLevelMexos, "cannot delete nginx proxy", "name", names.AppName)
if err = proxy.DeleteNginxProxy(ctx, client, names.AppName); err != nil {
log.SpanLog(ctx, log.DebugLevelMexos, "cannot delete proxy", "name", names.AppName)
return err
}
}
Expand Down
6 changes: 3 additions & 3 deletions cloud-resource-manager/platform/dind/dind-cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

sh "github.com/codeskyblue/go-sh"
"github.com/mobiledgex/edge-cloud/cloud-resource-manager/k8smgmt"
"github.com/mobiledgex/edge-cloud/cloud-resource-manager/nginx"
"github.com/mobiledgex/edge-cloud/cloud-resource-manager/proxy"
"github.com/mobiledgex/edge-cloud/cloudcommon"
"github.com/mobiledgex/edge-cloud/edgeproto"
"github.com/mobiledgex/edge-cloud/log"
Expand Down Expand Up @@ -114,7 +114,7 @@ func (s *Platform) CreateDINDCluster(ctx context.Context, clusterName, kconfName

// bridge nginxL7 network to this cluster's network
out, err = sh.Command("docker", "network", "connect",
GetDockerNetworkName(&cluster), nginx.NginxL7Name).CombinedOutput()
GetDockerNetworkName(&cluster), proxy.NginxL7Name).CombinedOutput()
if err != nil && strings.Contains(string(out), "already exists") {
err = nil
}
Expand Down Expand Up @@ -143,7 +143,7 @@ func (s *Platform) DeleteDINDCluster(ctx context.Context, clusterInst *edgeproto

// disconnect nginxL7 network
out, err := sh.Command("docker", "network", "disconnect",
GetDockerNetworkName(cluster), nginx.NginxL7Name).CombinedOutput()
GetDockerNetworkName(cluster), proxy.NginxL7Name).CombinedOutput()
if err != nil && strings.Contains(string(out), "is not connected") {
err = nil
}
Expand Down
4 changes: 2 additions & 2 deletions cloud-resource-manager/platform/dind/dind.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package dind
import (
"context"

"github.com/mobiledgex/edge-cloud/cloud-resource-manager/nginx"
"github.com/mobiledgex/edge-cloud/cloud-resource-manager/proxy"
"github.com/mobiledgex/edge-cloud/cloud-resource-manager/platform"
"github.com/mobiledgex/edge-cloud/cloud-resource-manager/platform/pc"
"github.com/mobiledgex/edge-cloud/edgeproto"
Expand All @@ -23,7 +23,7 @@ func (s *Platform) Init(ctx context.Context, platformConfig *platform.PlatformCo
return err
}
updateCallback(edgeproto.UpdateTask, "Setting up Nginx L7 Proxy")
err = nginx.InitL7Proxy(client, nginx.WithDockerPublishPorts())
err = proxy.InitL7Proxy(ctx, client, proxy.WithDockerPublishPorts())
if err != nil {
return err
}
Expand Down
209 changes: 209 additions & 0 deletions cloud-resource-manager/proxy/envoy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package proxy

import (
"bytes"
"context"
"fmt"
"html/template"
"strings"

"github.com/mobiledgex/edge-cloud/cloud-resource-manager/dockermgmt"
"github.com/mobiledgex/edge-cloud/cloud-resource-manager/platform/pc"
"github.com/mobiledgex/edge-cloud/cloudcommon"
dme "github.com/mobiledgex/edge-cloud/d-match-engine/dme-proto"
"github.com/mobiledgex/edge-cloud/log"
)

// Envoy is now handling all of our L4 TCP loadbalancing
// Eventually L7 and UDP Proxying support will be added so
// all of our loadbalancing is handled by envoy.
// UDP proxying is currently blocked by: https://github.com/envoyproxy/envoy/issues/492

var envoyYamlT *template.Template

// this is the default value in envoy, for DOS protection
const defaultConcurrentConns uint64 = 1024

func init() {
envoyYamlT = template.Must(template.New("yaml").Parse(envoyYaml))
}

func CreateEnvoyProxy(ctx context.Context, client pc.PlatformClient, name, originIP string, ports []dme.AppPort, ops ...Op) error {
log.SpanLog(ctx, log.DebugLevelMexos, "create envoy", "name", name, "originip", originIP, "ports", ports)
opts := Options{}
opts.Apply(ops)

out, err := client.Output("pwd")
if err != nil {
return err
}
pwd := strings.TrimSpace(string(out))

dir := pwd + "/envoy/" + name
log.SpanLog(ctx, log.DebugLevelMexos, "envoy remote dir", "name", name, "dir", dir)

err = pc.Run(client, "mkdir -p "+dir)
if err != nil {
return err
}
accesslogFile := dir + "/access.log"
err = pc.Run(client, "touch "+accesslogFile)
if err != nil {
log.SpanLog(ctx, log.DebugLevelMexos,
"envoy %s can't create file %s", name, accesslogFile)
return err
}
eyamlName := dir + "/envoy.yaml"
err = createEnvoyYaml(ctx, client, eyamlName, name, originIP, ports)
if err != nil {
return fmt.Errorf("create envoy.yaml failed, %v", err)
}

// container name is envoy+name for now to avoid conflicts with the nginx containers
cmdArgs := []string{"run", "-d", "-l edge-cloud", "--restart=unless-stopped", "--name", "envoy" + name}
if opts.DockerPublishPorts {
cmdArgs = append(cmdArgs, dockermgmt.GetDockerPortString(ports, dockermgmt.UsePublicPortInContainer)...)
}
if opts.DockerNetwork != "" {
// For dind, we use the network which the dind cluster is on.
cmdArgs = append(cmdArgs, "--network", opts.DockerNetwork)
}
cmdArgs = append(cmdArgs, []string{
"-v", accesslogFile + ":/var/log/access.log",
"-v", eyamlName + ":/etc/envoy/envoy.yaml",
"docker.mobiledgex.net/mobiledgex/mobiledgex_public/envoy-with-curl"}...)
cmd := "docker " + strings.Join(cmdArgs, " ")
log.SpanLog(ctx, log.DebugLevelMexos, "envoy docker command", "name", "envoy"+name,
"cmd", cmd)
out, err = client.Output(cmd)
if err != nil {
return fmt.Errorf("can't create envoy container %s, %s, %v", "envoy"+name, out, err)
}
log.SpanLog(ctx, log.DebugLevelMexos, "created envoy container", "name", name)
return nil
}

func createEnvoyYaml(ctx context.Context, client pc.PlatformClient, yamlname, name, originIP string, ports []dme.AppPort) error {
spec := ProxySpec{
Name: name,
MetricPort: cloudcommon.ProxyMetricsPort,
}
for _, p := range ports {
switch p.Proto {
// only support tcp for now
case dme.LProto_L_PROTO_TCP:
tcpPort := TCPSpecDetail{
Port: p.PublicPort,
Origin: originIP,
OriginPort: p.InternalPort,
}
tcpconns, err := getTCPConcurrentConnections()
if err != nil {
return err
}
tcpPort.ConcurrentConns = tcpconns
spec.TCPSpec = append(spec.TCPSpec, &tcpPort)
spec.L4 = true
}
}

log.SpanLog(ctx, log.DebugLevelMexos, "create envoy yaml", "name", name)
buf := bytes.Buffer{}
err := envoyYamlT.Execute(&buf, &spec)
if err != nil {
return err
}
err = pc.WriteFile(client, yamlname, buf.String(), "envoy.yaml", pc.NoSudo)
if err != nil {
log.SpanLog(ctx, log.DebugLevelMexos, "write envoy.yaml failed",
"name", name, "err", err)
return err
}
return nil
}

// TODO: Probably should eventually find a better way to uniquely name clusters other than just by the port theyre getting proxied from
var envoyYaml = `
{{if .L4 -}}
static_resources:
listeners:
{{- range .TCPSpec}}
- address:
socket_address:
address: 0.0.0.0
port_value: {{.Port}}
filter_chains:
- filters:
- name: envoy.tcp_proxy
config:
stat_prefix: ingress_tcp
cluster: backend{{.Port}}
access_log:
- name: envoy.file_access_log
config:
path: /var/log/access.log
json_format: {
"start_time": "%START_TIME%",
"duration": "%DURATION%",
"bytes_sent": "%BYTES_SENT%",
"bytes_received": "%BYTES_RECEIVED%",
"client_address": "%DOWNSTREAM_REMOTE_ADDRESS%",
"upstream_cluster": "%UPSTREAM_CLUSTER%"
}
{{- end}}
clusters:
{{- range .TCPSpec}}
- name: backend{{.Port}}
connect_timeout: 0.25s
type: strict_dns
circuit_breakers:
thresholds:
max_connections: {{.ConcurrentConns}}
lb_policy: round_robin
hosts:
- socket_address:
address: {{.Origin}}
port_value: {{.OriginPort}}
{{- end}}
admin:
access_log_path: "/var/log/admin.log"
address:
socket_address:
address: 0.0.0.0
port_value: {{.MetricPort}}
{{- end}}
`

func DeleteEnvoyProxy(ctx context.Context, client pc.PlatformClient, name string) error {
log.SpanLog(ctx, log.DebugLevelMexos, "delete envoy", "name", "envoy"+name)
out, err := client.Output("docker kill " + "envoy" + name)
deleteContainer := false
if err == nil {
deleteContainer = true
} else {
if strings.Contains(string(out), "No such container") {
log.SpanLog(ctx, log.DebugLevelMexos,
"envoy LB container already gone", "name", "envoy"+name)
} else {
return fmt.Errorf("can't delete envoy container %s, %s, %v", name, out, err)
}
}
envoyDir := "envoy/" + name
out, err = client.Output("rm -rf " + envoyDir)
if err != nil {
log.SpanLog(ctx, log.DebugLevelMexos, "delete envoy dir", "name", name, "dir", envoyDir, "out", out, "err", err)
}
if deleteContainer {
out, err = client.Output("docker rm " + "envoy" + name)
if err != nil && !strings.Contains(string(out), "No such container") {
return fmt.Errorf("can't remove envoy container %s, %s, %v", "envoy"+name, out, err)
}
}

log.SpanLog(ctx, log.DebugLevelMexos, "deleted envoy", "name", name)
return nil
}

func GetEnvoyContainerName(name string) string {
return "envoy" + name
}
Loading