Skip to content

Commit

Permalink
Support deployment of agent as a DaemonSet
Browse files Browse the repository at this point in the history
Signed-off-by: Juraci Paixão Kröhling <[email protected]>
  • Loading branch information
jpkrohling committed Oct 9, 2018
1 parent 864626d commit 5a507fc
Show file tree
Hide file tree
Showing 17 changed files with 449 additions and 25 deletions.
20 changes: 20 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,15 @@ spec:
log-level: debug # <4>
memory: # <5>
max-traces: 100000
agent:
strategy: DaemonSet # <6>
----
<1> The default strategy is `all-in-one`. The only other possible value is `production`.
<2> The image to use, in a regular Docker syntax
<3> The options to be passed verbatim to the underlying binary.Refer to the Jaeger documentation and/or to the `--help` option from the related binary for all the available options
<4> The option is a simple `key: value` map. In this case, we want the option `--log-level=debug` to be passed to the binary.
<5> Some options are namespaced and we can alternatively break them into nested objects. We could have specified `memory.max-traces: 100000`.
<6> By default, the operator expects agents to be deployed as sidecars to the target applications. Specify the strategy as "DaemonSet" use that instead.

== Accessing the UI

Expand Down Expand Up @@ -161,6 +164,23 @@ oc get routes

NOTE: make sure to use `https` with the hostname/port you get from the command above, otherwise you'll see a message like: "Application is not available".

== Agent as DaemonSet

By default, the Operator expects the agents to be deployed as sidecars to the target applications. This is convenient for several purposes, like in a multi-tenant scenario or to have better load balancing, but there are scenarios where it's desirable to install the agent as a `DaemonSet`. In that case, specify the Agent's strategy to `DaemonSet`, as follows:

[source,yaml]
----
apiVersion: io.jaegertracing/v1alpha1
kind: Jaeger
metadata:
name: my-jaeger
spec:
agent:
strategy: DaemonSet
----

IMPORTANT: if you attempt to install two Jaeger instances on the same cluster with `DaemonSet` as the strategy, only *one* will end up deploying a `DaemonSet`, as the agent is required to bind to well-known ports on the node. Because of that, the second daemon set will fail to bind to those ports.

== Removing an instance

To remove an instance, just use the `delete` command with the file used for the instance creation:
Expand Down
9 changes: 9 additions & 0 deletions deploy/examples/agent-as-daemonset.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: io.jaegertracing/v1alpha1
kind: Jaeger
metadata:
name: agent-as-daemonset
spec:
agent:
strategy: DaemonSet
options:
log-level: debug
5 changes: 3 additions & 2 deletions pkg/apis/io/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ type JaegerCollectorSpec struct {

// JaegerAgentSpec defines the options to be used when deploying the agent
type JaegerAgentSpec struct {
Strategy string `json:"strategy"` // can be either 'DaemonSet' or 'Sidecar' (default)
Image string `json:"image"`
Strategy string `json:"strategy"` // can be either 'DaemonSet' or 'Sidecar' (default)
Image string `json:"image"`
Options Options `json:"options"`
}

// JaegerStorageSpec defines the common storage options to be used for the query and collector
Expand Down
3 changes: 2 additions & 1 deletion pkg/apis/io/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions pkg/controller/all-in-one.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ func (c allInOneController) Create() []sdk.Object {

dep := deployment.NewAllInOne(c.jaeger)
os := []sdk.Object{dep.Get()}

ds := deployment.NewAgent(c.jaeger).Get()
if nil != ds {
os = append(os, ds)
}

for _, svc := range dep.Services() {
os = append(os, svc)
}
Expand Down
27 changes: 23 additions & 4 deletions pkg/controller/all-in-one_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,18 @@ func TestCreateAllInOneDeployment(t *testing.T) {
name := "TestCreateAllInOneDeployment"
c := newAllInOneController(context.TODO(), v1alpha1.NewJaeger(name))
objs := c.Create()
assertDeploymentsAndServicesForAllInOne(t, name, objs)
assertDeploymentsAndServicesForAllInOne(t, name, objs, false)
}

func TestCreateAllInOneDeploymentWithDaemonSetAgent(t *testing.T) {
name := "TestCreateAllInOneDeploymentWithDaemonSetAgent"

j := v1alpha1.NewJaeger(name)
j.Spec.Agent.Strategy = "DaemonSet"

c := newAllInOneController(context.TODO(), j)
objs := c.Create()
assertDeploymentsAndServicesForAllInOne(t, name, objs, true)
}

func TestUpdateAllInOneDeployment(t *testing.T) {
Expand All @@ -30,14 +41,22 @@ func TestUpdateAllInOneDeployment(t *testing.T) {
assert.Len(t, objs, 0)
}

func assertDeploymentsAndServicesForAllInOne(t *testing.T, name string, objs []sdk.Object) {
assert.Len(t, objs, 6)
func assertDeploymentsAndServicesForAllInOne(t *testing.T, name string, objs []sdk.Object, hasDaemonSet bool) {
if hasDaemonSet {
assert.Len(t, objs, 7)
} else {
assert.Len(t, objs, 6)
}

// we should have one deployment, named after the Jaeger's name (ObjectMeta.Name)
deployments := map[string]bool{
name: false,
}

daemonsets := map[string]bool{
fmt.Sprintf("%s-agent-daemonset", name): !hasDaemonSet,
}

// and these services
services := map[string]bool{
fmt.Sprintf("%s-agent", name): false,
Expand All @@ -51,5 +70,5 @@ func assertDeploymentsAndServicesForAllInOne(t *testing.T, name string, objs []s
fmt.Sprintf("%s-query", name): false,
}

assertHasAllObjects(t, name, objs, deployments, services, ingresses)
assertHasAllObjects(t, name, objs, deployments, daemonsets, services, ingresses)
}
14 changes: 10 additions & 4 deletions pkg/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,17 @@ func getDeployments(objs []sdk.Object) []*appsv1.Deployment {
return deps
}

func assertHasAllObjects(t *testing.T, name string, objs []sdk.Object, deployments map[string]bool, services map[string]bool, ingresses map[string]bool) {
func assertHasAllObjects(t *testing.T, name string, objs []sdk.Object, deployments map[string]bool, daemonsets map[string]bool, services map[string]bool, ingresses map[string]bool) {
for _, obj := range objs {
switch typ := obj.(type) {
case *appsv1.Deployment:
deployments[obj.(*appsv1.Deployment).ObjectMeta.Name] = true
deployments[obj.(*appsv1.Deployment).Name] = true
case *appsv1.DaemonSet:
daemonsets[obj.(*appsv1.DaemonSet).Name] = true
case *v1.Service:
services[obj.(*v1.Service).ObjectMeta.Name] = true
services[obj.(*v1.Service).Name] = true
case *v1beta1.Ingress:
ingresses[obj.(*v1beta1.Ingress).ObjectMeta.Name] = true
ingresses[obj.(*v1beta1.Ingress).Name] = true
default:
assert.Failf(t, "unknown type to be deployed", "%v", typ)
}
Expand All @@ -108,6 +110,10 @@ func assertHasAllObjects(t *testing.T, name string, objs []sdk.Object, deploymen
assert.True(t, v, "Expected %s to have been returned from the list of deployments", k)
}

for k, v := range daemonsets {
assert.True(t, v, "Expected %s to have been returned from the list of daemonsets", k)
}

for k, v := range services {
assert.True(t, v, "Expected %s to have been returned from the list of services", k)
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/controller/production.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ func (c *productionController) Create() []sdk.Object {
agent.InjectSidecar(*query.Get()),
}

ds := agent.Get()
if nil != ds {
components = append(components, ds)
}

for _, svc := range collector.Services() {
components = append(components, svc)
}
Expand Down
27 changes: 23 additions & 4 deletions pkg/controller/production_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,18 @@ func TestCreateProductionDeployment(t *testing.T) {
name := "TestCreateProductionDeployment"
c := newProductionController(context.TODO(), v1alpha1.NewJaeger(name))
objs := c.Create()
assertDeploymentsAndServicesForProduction(t, name, objs)
assertDeploymentsAndServicesForProduction(t, name, objs, false)
}

func TestCreateProductionDeploymentWithDaemonSetAgent(t *testing.T) {
name := "TestCreateProductionDeploymentWithDaemonSetAgent"

j := v1alpha1.NewJaeger(name)
j.Spec.Agent.Strategy = "DaemonSet"

c := newProductionController(context.TODO(), j)
objs := c.Create()
assertDeploymentsAndServicesForProduction(t, name, objs, true)
}

func TestUpdateProductionDeployment(t *testing.T) {
Expand Down Expand Up @@ -85,14 +96,22 @@ func TestOptionsArePassed(t *testing.T) {
}
}

func assertDeploymentsAndServicesForProduction(t *testing.T, name string, objs []sdk.Object) {
assert.Len(t, objs, 6)
func assertDeploymentsAndServicesForProduction(t *testing.T, name string, objs []sdk.Object, hasDaemonSet bool) {
if hasDaemonSet {
assert.Len(t, objs, 7)
} else {
assert.Len(t, objs, 6)
}

deployments := map[string]bool{
fmt.Sprintf("%s-collector", name): false,
fmt.Sprintf("%s-query", name): false,
}

daemonsets := map[string]bool{
fmt.Sprintf("%s-agent-daemonset", name): !hasDaemonSet,
}

services := map[string]bool{
fmt.Sprintf("%s-collector", name): false,
fmt.Sprintf("%s-query", name): false,
Expand All @@ -103,5 +122,5 @@ func assertDeploymentsAndServicesForProduction(t *testing.T, name string, objs [
fmt.Sprintf("%s-query", name): false,
}

assertHasAllObjects(t, name, objs, deployments, services, ingresses)
assertHasAllObjects(t, name, objs, deployments, daemonsets, services, ingresses)
}
94 changes: 90 additions & 4 deletions pkg/deployment/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/spf13/viper"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"

"github.com/jaegertracing/jaeger-operator/pkg/apis/io/v1alpha1"
"github.com/jaegertracing/jaeger-operator/pkg/service"
Expand All @@ -28,17 +30,94 @@ func NewAgent(jaeger *v1alpha1.Jaeger) *Agent {
}

// Get returns a Agent pod
func (a *Agent) Get() *appsv1.Deployment {
func (a *Agent) Get() *appsv1.DaemonSet {
if strings.ToLower(a.jaeger.Spec.Agent.Strategy) != "daemonset" {
logrus.Infof(
"The Jaeger instance '%v' is using a Sidecar strategy for the Jaeger Agent. Skipping its DaemonSet deployment.",
a.jaeger.ObjectMeta.Name,
a.jaeger.Name,
)
return nil
}

logrus.Infof("DaemonSet deployments aren't supported yet. Jaeger instance: '%v'", a.jaeger.ObjectMeta.Name)
return nil
args := append(a.jaeger.Spec.Agent.Options.ToArgs(), fmt.Sprintf("--collector.host-port=%s:14267", service.GetNameForCollectorService(a.jaeger)))
trueVar := true
selector := a.selector()
annotations := map[string]string{
"prometheus.io/scrape": "true",
"prometheus.io/port": "5778",
}

return &appsv1.DaemonSet{
TypeMeta: metav1.TypeMeta{
APIVersion: "apps/v1",
Kind: "DaemonSet",
},
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-agent-daemonset", a.jaeger.Name),
Namespace: a.jaeger.Namespace,
OwnerReferences: []metav1.OwnerReference{
metav1.OwnerReference{
APIVersion: a.jaeger.APIVersion,
Kind: a.jaeger.Kind,
Name: a.jaeger.Name,
UID: a.jaeger.UID,
Controller: &trueVar,
},
},
},
Spec: appsv1.DaemonSetSpec{
Selector: &metav1.LabelSelector{
MatchLabels: selector,
},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: selector,
Annotations: annotations,
},
Spec: v1.PodSpec{
Containers: []v1.Container{{
Image: a.jaeger.Spec.Agent.Image,
Name: "jaeger-agent-daemonset",
Args: args,
Ports: []v1.ContainerPort{
{
ContainerPort: 5775,
HostPort: 5775,
Name: "zk-compact-trft",
Protocol: v1.ProtocolUDP,
},
{
ContainerPort: 5778,
HostPort: 5778,
Name: "config-rest",
},
{
ContainerPort: 6831,
HostPort: 6831,
Name: "jg-compact-trft",
Protocol: v1.ProtocolUDP,
},
{
ContainerPort: 6832,
HostPort: 6832,
Name: "jg-binary-trft",
Protocol: v1.ProtocolUDP,
},
},
ReadinessProbe: &v1.Probe{
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/metrics",
Port: intstr.FromInt(5778),
},
},
InitialDelaySeconds: 1,
},
}},
},
},
},
}
}

// InjectSidecar adds a new container to the deployment, containing Jaeger's agent
Expand All @@ -51,6 +130,7 @@ func (a *Agent) InjectSidecar(dep appsv1.Deployment) *appsv1.Deployment {
{
ContainerPort: 5775,
Name: "zk-compact-trft",
Protocol: v1.ProtocolUDP,
},
{
ContainerPort: 5778,
Expand All @@ -59,14 +139,20 @@ func (a *Agent) InjectSidecar(dep appsv1.Deployment) *appsv1.Deployment {
{
ContainerPort: 6831,
Name: "jg-compact-trft",
Protocol: v1.ProtocolUDP,
},
{
ContainerPort: 6832,
Name: "jg-binary-trft",
Protocol: v1.ProtocolUDP,
},
},
}

dep.Spec.Template.Spec.Containers = append(dep.Spec.Template.Spec.Containers, sidecar)
return &dep
}

func (a *Agent) selector() map[string]string {
return map[string]string{"app": "jaeger", "jaeger": a.jaeger.Name, "jaeger-component": "agent-daemonset"}
}
2 changes: 1 addition & 1 deletion pkg/deployment/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func TestGetDaemonSetDeployment(t *testing.T) {
jaeger := v1alpha1.NewJaeger("TestNewAgent")
jaeger.Spec.Agent.Strategy = "daemonset"
agent := NewAgent(jaeger)
assert.Nil(t, agent.Get()) // it's not implemented yet
assert.NotNil(t, agent.Get())
}

func TestInjectSidecar(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/deployment/all-in-one.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (a *AllInOne) Get() *appsv1.Deployment {
Port: intstr.FromInt(14269),
},
},
InitialDelaySeconds: 5,
InitialDelaySeconds: 1,
},
}},
},
Expand Down
Loading

0 comments on commit 5a507fc

Please sign in to comment.