push k8s logs to LOG SERVICE IP

This commit is contained in:
Frostebite 2023-07-17 17:28:16 +01:00
parent 50d7f57f9d
commit 2e2eae0acf
6 changed files with 176 additions and 8 deletions

BIN
dist/index.js generated vendored

Binary file not shown.

BIN
dist/index.js.map generated vendored

Binary file not shown.

View File

@ -16,11 +16,14 @@ import { ProviderResource } from '../provider-resource';
import { ProviderWorkflow } from '../provider-workflow'; import { ProviderWorkflow } from '../provider-workflow';
import { RemoteClientLogger } from '../../remote-client/remote-client-logger'; import { RemoteClientLogger } from '../../remote-client/remote-client-logger';
import { KubernetesRole } from './kubernetes-role'; import { KubernetesRole } from './kubernetes-role';
import KubernetesLogService from './kubernetes-log-service';
import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
class Kubernetes implements ProviderInterface { class Kubernetes implements ProviderInterface {
public static Instance: Kubernetes; public static Instance: Kubernetes;
public kubeConfig!: k8s.KubeConfig; public kubeConfig!: k8s.KubeConfig;
public kubeClient!: k8s.CoreV1Api; public kubeClient!: k8s.CoreV1Api;
public kubeClientApps!: k8s.AppsV1Api;
public kubeClientBatch!: k8s.BatchV1Api; public kubeClientBatch!: k8s.BatchV1Api;
public rbacAuthorizationV1Api!: k8s.RbacAuthorizationV1Api; public rbacAuthorizationV1Api!: k8s.RbacAuthorizationV1Api;
public buildGuid: string = ''; public buildGuid: string = '';
@ -40,6 +43,7 @@ class Kubernetes implements ProviderInterface {
this.kubeConfig = new k8s.KubeConfig(); this.kubeConfig = new k8s.KubeConfig();
this.kubeConfig.loadFromDefault(); this.kubeConfig.loadFromDefault();
this.kubeClient = this.kubeConfig.makeApiClient(k8s.CoreV1Api); this.kubeClient = this.kubeConfig.makeApiClient(k8s.CoreV1Api);
this.kubeClientApps = this.kubeConfig.makeApiClient(k8s.AppsV1Api);
this.kubeClientBatch = this.kubeConfig.makeApiClient(k8s.BatchV1Api); this.kubeClientBatch = this.kubeConfig.makeApiClient(k8s.BatchV1Api);
this.rbacAuthorizationV1Api = this.kubeConfig.makeApiClient(k8s.RbacAuthorizationV1Api); this.rbacAuthorizationV1Api = this.kubeConfig.makeApiClient(k8s.RbacAuthorizationV1Api);
this.namespace = 'default'; this.namespace = 'default';
@ -47,13 +51,17 @@ class Kubernetes implements ProviderInterface {
} }
async PushLogUpdate(logs: string) { async PushLogUpdate(logs: string) {
const body: k8s.V1ConfigMap = new k8s.V1ConfigMap(); // push logs to nginx file server via 'LOG_SERVICE_IP' env var
body.data = {}; const ip = process.env[`LOG_SERVICE_IP`];
body.data['logs'] = logs; if (ip === undefined) {
body.metadata = { name: `${this.jobName}-logs`, namespace: this.namespace, labels: { app: 'unity-builder' } }; RemoteClientLogger.logWarning(`LOG_SERVICE_IP not set, skipping log push`);
RemoteClientLogger.log(`Pushing to Kubernetes ConfigMap`);
await this.kubeClient.createNamespacedConfigMap(this.namespace, body); return;
RemoteClientLogger.log(`Pushed logs to Kubernetes ConfigMap`); }
const url = `http://${ip}/api/log`;
RemoteClientLogger.log(`Pushing logs to ${url}`);
const response = await CloudRunnerSystem.Run(`curl -X POST -d "${logs}" ${url}`, false, true);
RemoteClientLogger.log(`Pushed logs to ${url} ${response}`);
} }
async listResources(): Promise<ProviderResource[]> { async listResources(): Promise<ProviderResource[]> {
@ -232,6 +240,7 @@ class Kubernetes implements ProviderInterface {
) { ) {
for (let index = 0; index < 3; index++) { for (let index = 0; index < 3; index++) {
try { try {
const ip = await KubernetesLogService.createLogDeployment(this.namespace, this.kubeClientApps, this.kubeClient);
const jobSpec = KubernetesJobSpecFactory.getJobSpec( const jobSpec = KubernetesJobSpecFactory.getJobSpec(
commands, commands,
image, image,
@ -246,9 +255,12 @@ class Kubernetes implements ProviderInterface {
this.jobName, this.jobName,
k8s, k8s,
this.containerName, this.containerName,
ip,
); );
await new Promise((promise) => setTimeout(promise, 15000)); await new Promise((promise) => setTimeout(promise, 15000));
await KubernetesRole.createRole(this.serviceAccountName, this.namespace, this.rbacAuthorizationV1Api);
// await KubernetesRole.createRole(this.serviceAccountName, this.namespace, this.rbacAuthorizationV1Api);
const result = await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec); const result = await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
CloudRunnerLogger.log(`Build job created`); CloudRunnerLogger.log(`Build job created`);
await new Promise((promise) => setTimeout(promise, 5000)); await new Promise((promise) => setTimeout(promise, 5000));

View File

@ -20,6 +20,7 @@ class KubernetesJobSpecFactory {
jobName: string, jobName: string,
k8s: any, k8s: any,
containerName: string, containerName: string,
ip: string = '',
) { ) {
const job = new k8s.V1Job(); const job = new k8s.V1Job();
job.apiVersion = 'batch/v1'; job.apiVersion = 'batch/v1';
@ -81,6 +82,7 @@ class KubernetesJobSpecFactory {
return environmentVariable; return environmentVariable;
}), }),
{ name: 'LOG_SERVICE_IP', value: ip },
], ],
volumeMounts: [ volumeMounts: [
{ {

View File

@ -0,0 +1,153 @@
import { CoreV1Api } from '@kubernetes/client-node';
import * as k8s from '@kubernetes/client-node';
class KubernetesLogService {
// static async function, creates a deployment and service
static async createLogService(serviceAccountName: string, namespace: string, kubeClient: CoreV1Api) {
const serviceAccount = new k8s.V1ServiceAccount();
serviceAccount.apiVersion = 'v1';
serviceAccount.kind = 'ServiceAccount';
serviceAccount.metadata = {
name: serviceAccountName,
};
serviceAccount.automountServiceAccountToken = true;
return kubeClient.createNamespacedServiceAccount(namespace, serviceAccount);
}
static async deleteLogService(serviceAccountName: string, namespace: string, kubeClient: CoreV1Api) {
await kubeClient.deleteNamespacedServiceAccount(serviceAccountName, namespace);
}
static async createLogDeployment(namespace: string, kubeClient: k8s.AppsV1Api, kubeClientCore: CoreV1Api) {
// json
/*
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
creationTimestamp: null
labels:
service: http-fileserver
name: http-fileserver
spec:
replicas: 1
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
service: http-fileserver
spec:
containers:
- image: my-docker-hub-account/http-fileserver-kubernetes:latest
imagePullPolicy: Always
name: http-fileserver
resources: {}
restartPolicy: Always
status: {}
*/
// create a deployment with above json
const deployment = new k8s.V1Deployment();
deployment.apiVersion = 'apps/v1';
deployment.kind = 'Deployment';
deployment.metadata = {
name: 'http-fileserver',
labels: {
service: 'http-fileserver',
},
};
deployment.spec = {
selector: {
matchLabels: {
service: 'http-fileserver',
},
},
replicas: 1,
strategy: {},
template: {
metadata: {
labels: {
service: 'http-fileserver',
},
},
spec: {
containers: [
{
image: 'my-docker-hub-account/http-fileserver-kubernetes:latest',
imagePullPolicy: 'Always',
name: 'http-fileserver',
resources: {},
},
],
restartPolicy: 'Always',
},
},
};
await kubeClient.createNamespacedDeployment(namespace, deployment);
await this.createLogServiceExpose(namespace, kubeClientCore);
// wait in loop until serivce ip address is exposed
for (let index = 0; index < 10; index++) {
// wait for service to share ip address
await new Promise((resolve) => setTimeout(resolve, 10000));
// get ip address of service
const service = await kubeClientCore.readNamespacedService('http-fileserver', namespace);
const ip = service.body.status?.loadBalancer?.ingress?.[0]?.ip;
if (ip) {
return ip;
}
}
}
// create kubernetes service to expose deployment
static async createLogServiceExpose(namespace: string, kubeClient: CoreV1Api) {
// json
/*
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
service: http-fileserver
name: http-fileserver
spec:
ports:
- name: 80-80
port: 80
protocol: TCP
targetPort: 80
selector:
service: http-fileserver
type: LoadBalancer
status:
loadBalancer: {}
*/
// create a service with above json
const service = new k8s.V1Service();
service.apiVersion = 'v1';
service.kind = 'Service';
service.metadata = {
name: 'http-fileserver',
labels: {
service: 'http-fileserver',
},
};
service.spec = {
ports: [
{
name: '80-80',
port: 80,
protocol: 'TCP',
targetPort: 80,
},
],
selector: {
service: 'http-fileserver',
},
type: 'LoadBalancer',
};
await kubeClient.createNamespacedService(namespace, service);
}
}
export default KubernetesLogService;

View File

@ -39,6 +39,7 @@ export class RemoteClientLogger {
return; return;
} }
CloudRunnerLogger.log(`Collected Logs`); CloudRunnerLogger.log(`Collected Logs`);
CloudRunnerLogger.log(process.env[`LOG_SERVICE_IP`] || ``);
const logs = fs.readFileSync(RemoteClientLogger.LogFilePath).toString(); const logs = fs.readFileSync(RemoteClientLogger.LogFilePath).toString();
CloudRunnerLogger.log(logs); CloudRunnerLogger.log(logs);
await Kubernetes.Instance.PushLogUpdate(logs); await Kubernetes.Instance.PushLogUpdate(logs);