Skip to content

Commit

Permalink
Jsonnet: Add Grafana Enterprise Logs library (#4165)
Browse files Browse the repository at this point in the history
* feat: Add enterprise-logs Jsonnet library

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* fixup: Use correct image for GEL

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* fix: Separate k8s-libsonnet dependency

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* chore: Simplify Makefile

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* fix: Remove extra comma in jsonnetfile.json

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* fix: Remove unused prerequisite of test evaluation

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* doc: Remove unnecessary comments

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* fix: Remove storageClassName from PVCs

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* feat: Ensure all GEL Pods run as non-root user

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* feat: Configure memberlist for a more stable ring

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* style: Simplify gossip-ring Service ports configuration

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* chore: Update dependency lock

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* chore: Add convenience test target

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* fix: Disable tokengen Job by default

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* doc: Add TODO for utility functions

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* chore: Add tooling for developer testing

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>
  • Loading branch information
jdbaldry authored Aug 17, 2021
1 parent de5d262 commit f381264
Show file tree
Hide file tree
Showing 11 changed files with 532 additions and 0 deletions.
3 changes: 3 additions & 0 deletions production/ksonnet/enterprise-logs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.eval
lib
vendor
50 changes: 50 additions & 0 deletions production/ksonnet/enterprise-logs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
include variables.mk

.ONESHELL:
.DELETE_ON_ERROR:
export SHELL := bash
export SHELLOPTS := pipefail:errexit
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rule

# Adapted from https://suva.sh/posts/well-documented-makefiles/
.PHONY: help
help: ## Display this help.
help:
@awk 'BEGIN {FS = ": ##"; printf "Usage:\n make <target>\n\nTargets:\n"} /^[a-zA-Z0-9_\.\-\/% ]+: ##/ { printf " %-45s %s\n", $$1, $$2 }' $(MAKEFILE_LIST)

jsonnetfile.lock.json: ## Update the locked dependency versions for the library.
jsonnetfile.lock.json: jsonnetfile.json
jb update

test: ## Evaluate the library Jsonnet.
test: test/.eval
:

test/.eval: # Cache testing results.
test/.eval: test/eval.jsonnet main.libsonnet test/lib/k.libsonnet test/vendor
cd $(@D); tmp=$$(mktemp); if tk eval $(<F) | tee $${tmp}; then mv $${tmp} $(@F); fi

test/lib:
mkdir -p $@

test/lib/k.libsonnet: # Install implicit k.libsonnet dependency used in testing.
test/lib/k.libsonnet: test/lib
printf "(import 'github.com/jsonnet-libs/k8s-libsonnet/%s/main.libsonnet')" $(K8S_VERSION) > $@

test/jsonnetfile.json: ## Update the jsonnetfile used in testing.
test/jsonnetfile.json: test/jsonnetfile.jsonnet variables.mk jsonnetfile.json
jsonnet --tla-str k8sVersion=$(K8S_VERSION) $< > $@

test/jsonnetfile.lock.json test/vendor: ## Update the locked dependency versions used in testing.
test/jsonnetfile.lock.json test/vendor: test/jsonnetfile.json
cd $(@D); jb update

.PHONY: k3d-cluster-create
k3d-cluster-create: ## Create a development k3d cluster.
./scripts/k3d-cluster create

.PHONY: k3d-cluster-create
k3d-cluster-delete: ## Delete a development k3d cluster.
k3d-cluster-delete:
./scripts/k3d-cluster delete
24 changes: 24 additions & 0 deletions production/ksonnet/enterprise-logs/jsonnetfile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"version": 1,
"dependencies": [
{
"source": {
"git": {
"remote": "https://github.com/grafana/loki.git",
"subdir": "production/ksonnet/loki"
}
},
"version": "main"
},
{
"source": {
"git": {
"remote": "https://github.com/grafana/jsonnet-libs",
"subdir": "ksonnet-util"
}
},
"version": "master"
}
],
"legacyImports": true
}
66 changes: 66 additions & 0 deletions production/ksonnet/enterprise-logs/jsonnetfile.lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"version": 1,
"dependencies": [
{
"source": {
"git": {
"remote": "https://github.com/grafana/jsonnet-libs.git",
"subdir": "consul"
}
},
"version": "00795013f5975f518a0a3de99253f9d5590271c8",
"sum": "Po3c1Ic96ngrJCtOazic/7OsLkoILOKZWXWyZWl+od8="
},
{
"source": {
"git": {
"remote": "https://github.com/grafana/jsonnet-libs.git",
"subdir": "jaeger-agent-mixin"
}
},
"version": "00795013f5975f518a0a3de99253f9d5590271c8",
"sum": "DsdBoqgx5kE3zc6fMYnfiGjW2+9Mx2OXFieWm1oFHgY="
},
{
"source": {
"git": {
"remote": "https://github.com/grafana/jsonnet-libs.git",
"subdir": "ksonnet-util"
}
},
"version": "00795013f5975f518a0a3de99253f9d5590271c8",
"sum": "OxgtIWL4hjvG0xkMwUzZ7Yjs52zUhLhaVQpwHCbqf8A="
},
{
"source": {
"git": {
"remote": "https://github.com/grafana/jsonnet-libs.git",
"subdir": "memcached"
}
},
"version": "00795013f5975f518a0a3de99253f9d5590271c8",
"sum": "dTOeEux3t9bYSqP2L/uCuLo/wUDpCKH4w+4OD9fePUk="
},
{
"source": {
"git": {
"remote": "https://github.com/grafana/loki.git",
"subdir": "production/ksonnet/loki"
}
},
"version": "9ea59f2062016d91398387ee2231e2e840af6a06",
"sum": "i27fS9sssvYd9Ywyq6uoB52EUWTaOPxo9DczCBVBuaM="
},
{
"source": {
"git": {
"remote": "https://github.com/jsonnet-libs/k8s-libsonnet.git",
"subdir": "1.18"
}
},
"version": "91008dbd2ea5734288467d6dcafef7c285c3f7e6",
"sum": "x881PM+6ARMsa9OSJcxO6L+4GOBy91clZipjeYbbzpw="
}
],
"legacyImports": false
}
220 changes: 220 additions & 0 deletions production/ksonnet/enterprise-logs/main.libsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// TODO(jdb): Use cluster_dns_suffix to configure absolute domain names so as to avoid excessive lookups.
// TODO(jdb): Introduce utility functions for mapping over all containers, all microservices (modules), all apps, etc..
local k = import 'github.com/grafana/jsonnet-libs/ksonnet-util/kausal.libsonnet',
clusterRole = k.rbac.v1.clusterRole,
clusterRoleBinding = k.rbac.v1.clusterRoleBinding,
container = k.core.v1.container,
configMap = k.core.v1.configMap,
deployment = k.apps.v1.deployment,
job = k.batch.v1.job,
policyRule = k.rbac.v1.policyRule,
pvc = k.core.v1.persistentVolumeClaim,
service = k.core.v1.service,
serviceAccount = k.core.v1.serviceAccount,
subject = k.rbac.v1.subject,
statefulSet = k.apps.v1.statefulSet;
local loki = import 'github.com/grafana/loki/production/ksonnet/loki/loki.libsonnet';
local util = (import 'github.com/grafana/jsonnet-libs/ksonnet-util/util.libsonnet').withK(k) {
withNonRootSecurityContext(uid, fsGroup=null)::
{ spec+: { template+: { spec+: { securityContext: {
fsGroup: if fsGroup == null then uid else fsGroup,
runAsNonRoot: true,
runAsUser: uid,
} } } } },
};

loki {
_config+:: {
commonArgs+:: {
'auth.enabled': 'true',
'auth.type': 'enterprise',
'cluster-name':
if self['auth.type'] == 'enterprise' then
error 'must set _config.commonArgs.cluster-name'
else null,
},

loki+: {
distributor+: {
ring: {
kvstore: {
store: 'memberlist',
},
},
},
ingester+: {
lifecycler+: {
ring: {
kvstore: {
store: 'memberlist',
},
},
},
},
memberlist: {
retransmit_factor: 2,
gossip_interval: '5s',
stream_timeout: '5s',
abort_if_cluster_join_fails: false,
bind_port: 7946,
join_members: ['gossip-ring'],
},
},

ingester_pvc_size: '50Gi',
stateful_ingesters: true,

querier_pvc_size: '50Gi',
stateful_queriers: true,

compactor_pvc_size: '50Gi',
},

_images+:: {
loki: 'grafana/enterprise-logs:v1.1.0',
kubectl: 'bitnami/kubectl',
},

admin_api_args:: self._config.commonArgs {
'bootstrap.license.path': '/etc/gel-license/license.jwt',
target: 'admin-api',
},
admin_api_container::
container.new(name='admin-api', image=self._images.loki)
+ container.withArgs(util.mapToFlags(self.admin_api_args))
+ container.withPorts(loki.util.defaultPorts)
+ container.resources.withLimits({ cpu: '2', memory: '4Gi' })
+ container.resources.withRequests({ cpu: '500m', memory: '1Gi' })
+ loki.util.readinessProbe,
admin_api_deployment:
deployment.new(name='admin-api', replicas=3, containers=[self.admin_api_container])
+ deployment.spec.selector.withMatchLabelsMixin({ name: 'admin-api' })
+ deployment.spec.template.metadata.withLabelsMixin({ name: 'admin-api', gossip_ring_member: 'true' })
+ deployment.spec.template.spec.withTerminationGracePeriodSeconds(15)
+ util.configVolumeMount('loki', '/etc/loki/config')
+ util.secretVolumeMount('gel-license', '/etc/gel-license/')
+ util.withNonRootSecurityContext(uid=10001),
admin_api_service:
util.serviceFor(self.admin_api_deployment),

compactor_data_pvc+: { spec+: { storageClassName:: null } },
compactor_statefulset+: util.withNonRootSecurityContext(10001),

// Remove consul in favor of memberlist.
consul_config_map:: {},
consul_deployment:: null,
consul_service:: null,

distributor_deployment+:
deployment.spec.template.metadata.withLabelsMixin({ gossip_ring_member: 'true' })
+ util.withNonRootSecurityContext(uid=10001),

gateway_args:: self._config.commonArgs {
'bootstrap.license.path': '/etc/gel-license/license.jwt',
'gateway.proxy.admin-api.url': 'http://admin-api:%s' % $._config.http_listen_port,
'gateway.proxy.compactor.url': 'http://compactor:%s' % $._config.http_listen_port,
'gateway.proxy.distributor.url': 'dns:///distributor:9095',
'gateway.proxy.ingester.url': 'http://ingester:%s' % $._config.http_listen_port,
'gateway.proxy.query-frontend.url': 'http://query-frontend:%s' % $._config.http_listen_port,
'gateway.proxy.ruler.url': 'http://ruler:%s' % $._config.http_listen_port,
target: 'gateway',
},
gateway_container::
container.new(name='gateway', image=self._images.loki)
+ container.withArgs(util.mapToFlags(self.gateway_args))
+ container.withPorts(loki.util.defaultPorts)
+ container.resources.withLimits({ cpu: '2', memory: '4Gi' })
+ container.resources.withRequests({ cpu: '500m', memory: '1Gi' })
+ loki.util.readinessProbe,
gateway_deployment:
deployment.new(name='gateway', replicas=3, containers=[self.gateway_container])
+ deployment.spec.template.spec.withTerminationGracePeriodSeconds(15)
+ deployment.spec.template.spec.securityContext.withFsGroup(10001)
+ deployment.spec.template.spec.securityContext.withRunAsNonRoot(true)
+ deployment.spec.template.spec.securityContext.withRunAsUser(10001)
+ util.configVolumeMount('loki', '/etc/loki/config')
+ util.withNonRootSecurityContext(uid=10001),
// Remove the htpasswd Secret used by the OSS Loki NGINX gateway.
gateway_secret:: null,
gateway_service:
util.serviceFor(self.gateway_deployment),

ingester_data_pvc+: { spec+: { storageClassName:: null } },
ingester_statefulset+:
statefulSet.spec.template.metadata.withLabelsMixin({ gossip_ring_member: 'true' })
+ util.withNonRootSecurityContext(uid=10001),

querier_data_pvc+: { spec+: { storageClassName:: null } },
querier_statefulset+:
statefulSet.spec.template.metadata.withLabelsMixin({ gossip_ring_member: 'true' })
+ util.withNonRootSecurityContext(uid=10001),

table_manager_deployment+: util.withNonRootSecurityContext(uid=10001),

tokengen_args:: self._config.commonArgs {
target: 'tokengen',
'tokengen.token-file': '/shared/admin-token',
},
tokengen_container::
container.new('tokengen', self._images.loki)
+ container.withPorts(loki.util.defaultPorts)
+ container.withArgs(util.mapToFlags(self.tokengen_args))
+ container.withVolumeMounts([
{ mountPath: '/etc/loki/config', name: 'config' },
{ mountPath: '/shared', name: 'shared' },
])
+ container.resources.withLimits({ memory: '4Gi' })
+ container.resources.withRequests({ cpu: '500m', memory: '500Mi' }),
tokengen_create_secret_container::
container.new('create-secret', self._images.kubectl)
+ container.withCommand([
'/bin/bash',
'-euc',
'kubectl create secret generic gel-admin-token --from-file=token=/shared/admin-token --from-literal=grafana-token="self.base64 <(echo :self.cat /shared/admin-token)))"',
])
+ container.withVolumeMounts([{ mountPath: '/shared', name: 'shared' }]),
tokengen_job::
job.new('tokengen')
+ job.spec.withCompletions(1)
+ job.spec.withParallelism(1)
+ job.spec.template.spec.withContainers([self.tokengen_create_secret_container])
+ job.spec.template.spec.withInitContainers([self.tokengen_container])
+ job.spec.template.spec.withRestartPolicy('Never')
+ job.spec.template.spec.withServiceAccount('tokengen')
+ job.spec.template.spec.withServiceAccountName('tokengen')
+ job.spec.template.spec.withVolumes([
{ name: 'config', configMap: { name: 'loki' } },
{ name: 'shared', emptyDir: {} },
])
+ util.withNonRootSecurityContext(uid=10001),
tokengen_service_account:
serviceAccount.new('tokengen'),
tokengen_cluster_role:
clusterRole.new('tokengen')
+ clusterRole.withRules([
policyRule.withApiGroups([''])
+ policyRule.withResources(['secrets'])
+ policyRule.withVerbs(['create']),
]),
tokengen_cluster_role_binding:
clusterRoleBinding.new()
+ clusterRoleBinding.metadata.withName('tokengen')
+ clusterRoleBinding.roleRef.withApiGroup('rbac.authorization.k8s.io')
+ clusterRoleBinding.roleRef.withKind('ClusterRole')
+ clusterRoleBinding.roleRef.withName('tokengen')
+ clusterRoleBinding.withSubjects([
subject.new()
+ subject.withName('tokengen')
+ subject.withKind('ServiceAccount')
+ { namespace: $._config.namespace },
]),

gossip_ring_service:
service.new(
name='gossip-ring',
selector={ gossip_ring_member: 'true' },
ports=[{ name: 'gossip-ring', port: 7946, protocol: 'TCP', targetPort: 7946 }],
)
+ service.spec.withClusterIp('None')
+ service.spec.withPublishNotReadyAddresses(true),
}
Loading

0 comments on commit f381264

Please sign in to comment.