mirror of
https://github.com/game-ci/unity-builder.git
synced 2025-07-07 20:35:33 -04:00
push k8s logs to LOG SERVICE IP
This commit is contained in:
parent
50d7f57f9d
commit
2e2eae0acf
BIN
dist/index.js
generated
vendored
BIN
dist/index.js
generated
vendored
Binary file not shown.
BIN
dist/index.js.map
generated
vendored
BIN
dist/index.js.map
generated
vendored
Binary file not shown.
@ -16,11 +16,14 @@ import { ProviderResource } from '../provider-resource';
|
||||
import { ProviderWorkflow } from '../provider-workflow';
|
||||
import { RemoteClientLogger } from '../../remote-client/remote-client-logger';
|
||||
import { KubernetesRole } from './kubernetes-role';
|
||||
import KubernetesLogService from './kubernetes-log-service';
|
||||
import { CloudRunnerSystem } from '../../services/core/cloud-runner-system';
|
||||
|
||||
class Kubernetes implements ProviderInterface {
|
||||
public static Instance: Kubernetes;
|
||||
public kubeConfig!: k8s.KubeConfig;
|
||||
public kubeClient!: k8s.CoreV1Api;
|
||||
public kubeClientApps!: k8s.AppsV1Api;
|
||||
public kubeClientBatch!: k8s.BatchV1Api;
|
||||
public rbacAuthorizationV1Api!: k8s.RbacAuthorizationV1Api;
|
||||
public buildGuid: string = '';
|
||||
@ -40,6 +43,7 @@ class Kubernetes implements ProviderInterface {
|
||||
this.kubeConfig = new k8s.KubeConfig();
|
||||
this.kubeConfig.loadFromDefault();
|
||||
this.kubeClient = this.kubeConfig.makeApiClient(k8s.CoreV1Api);
|
||||
this.kubeClientApps = this.kubeConfig.makeApiClient(k8s.AppsV1Api);
|
||||
this.kubeClientBatch = this.kubeConfig.makeApiClient(k8s.BatchV1Api);
|
||||
this.rbacAuthorizationV1Api = this.kubeConfig.makeApiClient(k8s.RbacAuthorizationV1Api);
|
||||
this.namespace = 'default';
|
||||
@ -47,13 +51,17 @@ class Kubernetes implements ProviderInterface {
|
||||
}
|
||||
|
||||
async PushLogUpdate(logs: string) {
|
||||
const body: k8s.V1ConfigMap = new k8s.V1ConfigMap();
|
||||
body.data = {};
|
||||
body.data['logs'] = logs;
|
||||
body.metadata = { name: `${this.jobName}-logs`, namespace: this.namespace, labels: { app: 'unity-builder' } };
|
||||
RemoteClientLogger.log(`Pushing to Kubernetes ConfigMap`);
|
||||
await this.kubeClient.createNamespacedConfigMap(this.namespace, body);
|
||||
RemoteClientLogger.log(`Pushed logs to Kubernetes ConfigMap`);
|
||||
// push logs to nginx file server via 'LOG_SERVICE_IP' env var
|
||||
const ip = process.env[`LOG_SERVICE_IP`];
|
||||
if (ip === undefined) {
|
||||
RemoteClientLogger.logWarning(`LOG_SERVICE_IP not set, skipping log push`);
|
||||
|
||||
return;
|
||||
}
|
||||
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[]> {
|
||||
@ -232,6 +240,7 @@ class Kubernetes implements ProviderInterface {
|
||||
) {
|
||||
for (let index = 0; index < 3; index++) {
|
||||
try {
|
||||
const ip = await KubernetesLogService.createLogDeployment(this.namespace, this.kubeClientApps, this.kubeClient);
|
||||
const jobSpec = KubernetesJobSpecFactory.getJobSpec(
|
||||
commands,
|
||||
image,
|
||||
@ -246,9 +255,12 @@ class Kubernetes implements ProviderInterface {
|
||||
this.jobName,
|
||||
k8s,
|
||||
this.containerName,
|
||||
ip,
|
||||
);
|
||||
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);
|
||||
CloudRunnerLogger.log(`Build job created`);
|
||||
await new Promise((promise) => setTimeout(promise, 5000));
|
||||
|
@ -20,6 +20,7 @@ class KubernetesJobSpecFactory {
|
||||
jobName: string,
|
||||
k8s: any,
|
||||
containerName: string,
|
||||
ip: string = '',
|
||||
) {
|
||||
const job = new k8s.V1Job();
|
||||
job.apiVersion = 'batch/v1';
|
||||
@ -81,6 +82,7 @@ class KubernetesJobSpecFactory {
|
||||
|
||||
return environmentVariable;
|
||||
}),
|
||||
{ name: 'LOG_SERVICE_IP', value: ip },
|
||||
],
|
||||
volumeMounts: [
|
||||
{
|
||||
|
153
src/model/cloud-runner/providers/k8s/kubernetes-log-service.ts
Normal file
153
src/model/cloud-runner/providers/k8s/kubernetes-log-service.ts
Normal 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;
|
@ -39,6 +39,7 @@ export class RemoteClientLogger {
|
||||
return;
|
||||
}
|
||||
CloudRunnerLogger.log(`Collected Logs`);
|
||||
CloudRunnerLogger.log(process.env[`LOG_SERVICE_IP`] || ``);
|
||||
const logs = fs.readFileSync(RemoteClientLogger.LogFilePath).toString();
|
||||
CloudRunnerLogger.log(logs);
|
||||
await Kubernetes.Instance.PushLogUpdate(logs);
|
||||
|
Loading…
Reference in New Issue
Block a user