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 10, 2018
1 parent d68e5de commit 895f552
Show file tree
Hide file tree
Showing 17 changed files with 476 additions and 25 deletions.
47 changes: 47 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,16 @@ spec:
max-traces: 100000
ingress:
enabled: false # <6>
agent:
strategy: DaemonSet # <7>
----
<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, an ingress object is created for the query service. It can be disabled by setting its `enabled` option to `false`. This is also available for the `query` spec.
<7> By default, the operator assumes that agents are deployed as sidecars within the target pods. Specifying the strategy as "DaemonSet" changes that and makes the operator deploy the agent as DaemonSet. Note that your tracer client will probably have to override the "JAEGER_AGENT_HOST" env var to use the node's IP.

== Accessing the UI

Expand Down Expand Up @@ -164,6 +167,50 @@ 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.

Your tracer client will then most likely need to be told where the agent is located. This is usually done by setting the env var `JAEGER_AGENT_HOST` and should be set to the value of the Kubernetes node's IP, like:

[source,yaml]
----
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: acme/myapp:myversion
env:
- name: JAEGER_AGENT_HOST
valueFrom:
fieldRef:
fieldPath: status.hostIP
----

== 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 @@ -110,15 +110,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 @@ -128,6 +130,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)
}
Loading

0 comments on commit 895f552

Please sign in to comment.