blob: 6ec924925ffe1fae8fae6285de0a56bed77803ee [file] [log] [blame]
Serge Bazanski363bf4f2020-08-24 21:00:56 +02001local kube = import "../../../kube/kube.libsonnet";
2
3{
4 // Global sets up a global tier instance of the hscloud monitoring infrastructure.
5 //
6 // This currently consists of Victoria Metrics, to which the agent tier sends metrics data via
7 // the prometheus remote_write protocol.
8 // Victoria Metrics is here used as a long-term storage solution. However, right now, it
9 // just keeps data locally on disk. In the future, S3 snapshots/backups should be introduced.
10 Global(name):: {
11 local global = self,
12 local cfg = global.cfg,
13
14 cfg:: {
15 name: name,
16 namespace: "monitoring-global-%s" % [cfg.name],
17
18 images: {
19 victoria: "victoriametrics/victoria-metrics:v1.40.0",
20 vmauth: "victoriametrics/vmauth:v1.40.0",
Serge Bazanski4f7caf82020-10-10 17:58:09 +020021 grafana: "grafana/grafana:7.2.1",
Serge Bazanski363bf4f2020-08-24 21:00:56 +020022 },
23
24 hosts: {
25 // DNS hostname that this global tier will use. Ingress will run under it.
26 globalAPI: error "hosts.globalAPI must be set",
Serge Bazanski4f7caf82020-10-10 17:58:09 +020027 globalDashboard: error "hosts.globalDashboard must be set",
Serge Bazanski363bf4f2020-08-24 21:00:56 +020028 },
29
30 storageClasses: {
31 // Storage class used for main data retention.
32 victoria: error "storageClasses.victoria must be set",
33 },
34
Serge Bazanski4f7caf82020-10-10 17:58:09 +020035 oauth: {
36 clientId: error "oauth.clientId must be set",
37 clientSecret: error "oauth.clientSecret must be set",
38 },
39
Serge Bazanski363bf4f2020-08-24 21:00:56 +020040 // A list of agents that will push metrics to this instance.
41 // List of:
42 // {
43 // username: the username that the agent will authenticate with
44 // password: the password that the agent will authenticate with
45 // }
46 agents: [],
47 },
48
49 // Generated URLs that agents should use to ship metrics over. Both require HTTP basic
50 // auth, configured via cfg.agents.
Serge Bazanski4f7caf82020-10-10 17:58:09 +020051 // The internal URL that should be used for agents colocated in the same Kubernetes cluster.
Serge Bazanski363bf4f2020-08-24 21:00:56 +020052 internalIngestURL:: "http://%s/api/v1/write" % [global.victoria.serviceAPI.host_colon_port],
Serge Bazanski4f7caf82020-10-10 17:58:09 +020053 // The internal URL that should be used for readers colocated in the same Kubernetes cluster.
54 internalReadURL:: "http://%s/" % [global.victoria.serviceAPI.host_colon_port],
55 // The global URL that should be used for agents sending data over the internet.
Serge Bazanski363bf4f2020-08-24 21:00:56 +020056 globalIngestURL:: "https://%s/api/v1/write" % [cfg.hosts.globalAPI],
Serge Bazanski4f7caf82020-10-10 17:58:09 +020057 // The global URL that should be used for readers over the internet.
58 globalReadURL:: "https://%s" % [cfg.hosts.globalAPI],
Serge Bazanski363bf4f2020-08-24 21:00:56 +020059
60 namespace: kube.Namespace(cfg.namespace),
61 local ns = global.namespace,
62
63 victoria: {
64 local victoria = self,
65
66 pvc: ns.Contain(kube.PersistentVolumeClaim("victoria-data")) {
67 spec+: {
68 storageClassName: cfg.storageClasses.victoria,
69 accessModes: ["ReadWriteOnce"],
70 resources: {
71 requests: {
72 storage: "64Gi",
73 },
74 },
75 },
76 },
77
78 authSecret: ns.Contain(kube.Secret("vmauth")) {
79 data+: {
80 "config.yaml": std.base64(std.manifestJson({
81 users: [
82 {
83 username: a.username,
84 password: a.password,
85 url_prefix: "http://localhost:8428",
86 }
Serge Bazanski4f7caf82020-10-10 17:58:09 +020087 for a in (cfg.agents + [cfg.loopbackGrafanaUser])
Serge Bazanski363bf4f2020-08-24 21:00:56 +020088 ],
89 }) + "\n")
90 },
91 },
92
93 deploy: ns.Contain(kube.Deployment("victoria")) {
94 spec+: {
95 template+: {
96 spec+: {
97 containers_: {
98 default: kube.Container("default") {
99 image: cfg.images.victoria,
100 volumeMounts_: {
101 data: { mountPath: "/victoria-metrics-data", },
102 },
103 },
104 vmauth: kube.Container("vmauth") {
105 image: cfg.images.vmauth,
106 command: [
107 "/vmauth-prod",
108 "-auth.config", "/mnt/secret/config.yaml",
109 ],
110 volumeMounts_: {
111 secret: { mountPath: "/mnt/secret", },
112 },
113 ports_: {
114 api: { containerPort: 8427 }
115 },
116 }
117 },
118 volumes_: {
119 data: kube.PersistentVolumeClaimVolume(victoria.pvc),
120 secret: kube.SecretVolume(victoria.authSecret),
121 },
122 },
123 },
124 },
125 },
126
127 serviceAPI: ns.Contain(kube.Service("victoria-api")) {
128 target_pod: victoria.deploy.spec.template,
129 spec+: {
130 ports: [
131 { name: "api", port: 8427, targetPort: 8427, protocol: "TCP" },
132 ],
133 type: "ClusterIP",
134 },
135 },
136
137 ingressAPI: ns.Contain(kube.Ingress("victoria-api")) {
138 metadata+: {
139 annotations+: {
140 "kubernetes.io/tls-acme": "true",
141 "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
142 },
143 },
144 spec+: {
145 tls: [
146 { hosts: [cfg.hosts.globalAPI], secretName: "ingress-tls" },
147 ],
148 rules: [
149 {
150 host: cfg.hosts.globalAPI,
151 http: {
152 paths: [ { path: "/", backend: { serviceName: victoria.serviceAPI.metadata.name, servicePort: 8427 } }, ],
153 },
154 }
155 ],
156 },
157 },
158 },
Serge Bazanski4f7caf82020-10-10 17:58:09 +0200159
160 grafana: {
161 local grafana = self,
162
163 // grafana.ini, serialized to secret.
164 ini:: {
165 sections: {
166 "auth": {
167 "disable_login_form": true,
168 "oauth_auto_login": true,
169 },
170 "security": {
171 # We do not disable basic auth, as we want to use builtin
172 # users as API users (eg for config reload), but we want
173 # to disable the default admin:admin user.
174 "disable_initial_admin_creation": true,
175 },
176 "auth.generic_oauth": {
177 enabled: true,
178 client_id: cfg.oauth.clientId,
179 client_secret: cfg.oauth.clientSecret,
Serge Bazanskicc2ff792021-01-30 17:26:47 +0100180 auth_url: "https://sso.hackerspace.pl/oauth/authorize",
181 token_url: "https://sso.hackerspace.pl/oauth/token",
182 api_url: "https://sso.hackerspace.pl/api/1/userinfo",
Serge Bazanski4f7caf82020-10-10 17:58:09 +0200183 scopes: "openid",
184 email_attribute_path: "email",
185 allow_sign_up: true,
186 role_attribute_path: "contains(groups, 'grafana-admin')",
187 },
188 "server": {
189 domain: cfg.hosts.globalDashboard,
190 root_url: "https://%s/" % [ cfg.hosts.globalDashboard ],
191 },
192 },
193 },
194
195 datasources:: {
196 apiVersion: 1,
197 datasources: [
198 {
199 name: "victoria-global",
200 type: "prometheus",
201 uid: "victoria-global",
202 isDefault: true,
203 url: global.internalReadURL,
204 basicAuth: true,
205 basicAuthUser: cfg.loopbackGrafanaUser.username,
206 secureJsonData: {
207 basicAuthPassword: cfg.loopbackGrafanaUser.password,
208 },
209 },
210 ],
211 },
212
213 config: ns.Contain(kube.Secret("grafana-config")) {
214 data+: {
215 "grafana.ini": std.base64(std.manifestIni(grafana.ini)),
216 "datasources.yaml": std.base64(std.manifestYamlDoc(grafana.datasources)),
217 },
218 },
219
220 pvc: ns.Contain(kube.PersistentVolumeClaim("grafana-data")) {
221 spec+: {
222 storageClassName: cfg.storageClasses.grafana,
223 accessModes: ["ReadWriteOnce"],
224 resources: {
225 requests: {
226 storage: "8Gi",
227 },
228 },
229 },
230 },
231
232 deploy: ns.Contain(kube.Deployment("grafana")) {
233 spec+: {
234 template+: {
235 spec+: {
236 containers_: {
237 default: kube.Container("default") {
238 image: cfg.images.grafana,
239 ports_: {
240 public: { containerPort: 3000 },
241 },
242 env_: {
243 GF_PATHS_CONFIG: "/etc/hscloud-config/grafana.ini",
244 GF_PATHS_PROVISIONING: "/etc/hscloud-config/provisioning",
245 GF_PATHS_DATA: "/var/lib/grafana",
246 },
247 volumeMounts_: {
248 config: { mountPath: "/etc/hscloud-config", },
249 data: { mountPath: "/var/lib/grafana", },
250 },
251 resources: {
252 requests: { cpu: "100m", memory: "256M", },
253 limits: { cpu: "200m", memory: "512M", },
254 },
255 },
256 },
257 volumes_: {
258 data: kube.PersistentVolumeClaimVolume(grafana.pvc),
259 config: kube.SecretVolume(grafana.config) {
260 secret+: {
261 items: [
262 { key: "grafana.ini", path: "grafana.ini", },
263 { key: "datasources.yaml", path: "provisioning/datasources/datasources.yaml", },
264 ],
265 },
266 },
267 },
268 },
269 },
270 },
271 },
272
273 service: ns.Contain(kube.Service("grafana-public")) {
274 target_pod: grafana.deploy.spec.template,
275 spec+: {
276 ports: [
277 { name: "public", port: 3000, targetPort: 3000, protocol: "TCP" },
278 ],
279 },
280 },
281
282 ingress: ns.Contain(kube.Ingress("grafana-public")) {
283 metadata+: {
284 annotations+: {
285 "kubernetes.io/tls-acme": "true",
286 "certmanager.k8s.io/cluster-issuer": "letsencrypt-prod",
287 },
288 },
289 spec+: {
290 tls: [
291 { hosts: [cfg.hosts.globalDashboard], secretName: "ingress-grafana-tls" },
292 ],
293 rules: [
294 {
295 host: cfg.hosts.globalDashboard,
296 http: {
297 paths: [ { path: "/", backend: { serviceName: grafana.service.metadata.name, servicePort: 3000 } }, ],
298 },
299 }
300 ],
301 },
302 },
303 },
Serge Bazanski363bf4f2020-08-24 21:00:56 +0200304 }
305}