Commit 327fce99 authored by 智勇's avatar 智勇

Merge branch 'jiakewei' into 'master'

Jiakewei

See merge request QA/tke-eos!44
parents b9c8cd2b 6b978808
const Router = require('koa-router')
const fs = require('fs')
const router = new Router();
module.exports = router
router.get('/', async (ctx) => {
let data = fs.readdirSync('./kubeService/')
data = data.filter(i => i.endsWith('.yaml')).map(i => i.split('.')[0].split('-').slice(1).join('-'))
ctx.body = ctx.ok(data)
})
......@@ -12,15 +12,17 @@ const namespaceRoute = require('./namespace')
const serviceRoute = require('./service')
const repositoryRouter = require('./repository')
const tag = require('./tag')
const cluster = require('./cluster')
const ingressRoute = require('./ingress')
const deploy = require('./restartLatest')
const client = require('../middleware/client')
const logFormat = ':req[x-real-ip] :req[x-forwarded-for] - -'
+ ' ":method :url HTTP/:http-version"'
+ ' :status :content-length ":referrer"'
+ ' ":user-agent" :req[x-auth-token] :response-timems'
const cluster = require('../services/tke.clusterService').create()
// const cluster = require('../services/tke.clusterService').create()
const container = require('../services/tke.containerService').create()
const logger = log4js.getLogger()
......@@ -30,10 +32,11 @@ function loadRoutes(router) {
.use(error())
.use(result())
.use(validate())
.use(client())
.use(async (ctx, next) => {
// 腾讯云
ctx.container = container
ctx.cluster = cluster
// ctx.cluster = cluster
await next()
}, bodyParser())
......@@ -42,9 +45,10 @@ function loadRoutes(router) {
.use('/ingress', ingressRoute.routes())
.use('/repository', repositoryRouter.routes())
.use('/tag', tag.routes())
.use('/cluster', cluster.routes())
}
exports.start = function () {
exports.start = function (port) {
// 加载各种服务
const app = new Koa()
const router = new Router()
......@@ -56,7 +60,7 @@ exports.start = function () {
deploy()
app.use(log4js.koaLogger(log4js.getLogger('http'), { level: 'auto', format: logFormat }))
app.use(router.routes())
app.listen(4000)
app.listen(port)
logger.info('server listening 4000')
logger.info('加载环境配置: ', process.env.NODE_ENV)
}
const Router = require('koa-router')
const { ingressGet, ingressUpdate } = require('../kubeService/ingress')
const { ingressGet, ingressUpdate, ingressDelete } = require('../kubeService/ingress')
const router = new Router();
module.exports = router
router.get('/', async (ctx) => {
// const data = await ctx.cluster.ingress_get(ctx.query.namespace)
const data = await ingressGet(ctx.query.namespace, ctx.query.serviceName)
const data = await ingressGet(ctx.client, ctx.query.namespace, ctx.query.serviceName)
ctx.body = ctx.ok(data)
})
......@@ -15,7 +15,7 @@ namespace
rules
*/
router.post('/modify', async (ctx) => {
const data = await ingressUpdate(ctx.request.body.namespace, ctx.request.body.serviceName, ctx.request.body.host)
const data = await ingressUpdate(ctx.client, ctx.request.body.namespace, ctx.request.body.serviceName, ctx.request.body.host)
ctx.body = ctx.ok(data)
})
// router.post('/modify', async (ctx) => {
......@@ -29,7 +29,7 @@ router.post('/modify', async (ctx) => {
// })
router.post('/create', async (ctx) => {
const ingress = await ctx.cluster.ingress_get(ctx.request.body.namespace)
const ingress = await ingressGet(ctx.request.body.namespace)
if (ingress) {
ctx.body = ctx.fail('namespace下已存在ingress')
return ctx.body
......@@ -39,6 +39,6 @@ router.post('/create', async (ctx) => {
})
router.post('/delete', async (ctx) => {
await ctx.cluster.ingress_delete(ctx.request.body.ingressName, ctx.request.body.namespace)
await ingressDelete(ctx.request.body.ingressName, ctx.request.body.namespace)
ctx.body = ctx.ok()
})
const Router = require('koa-router')
const yaml = require('js-yaml')
const qs = require('querystring')
const templates = require('../serviceTemplate')
const { getAllNamespace, createNamespace } = require('./../kubeService/service')
let newNsKey = ''
// const newNsKey = ''
const router = new Router();
module.exports = router
const keepNamespace = ['default', 'kube-system', 'monitor']
router.get('/', async (ctx) => {
const data = await ctx.cluster.namespace_get()
data.namespaces = data.namespaces && data.namespaces.filter(item => !keepNamespace.includes(item.name))
const data = await getAllNamespace(ctx.client)
ctx.body = ctx.ok(data)
})
router.get('/get_namespace_for_jenkins', async (ctx) => {
const data = await ctx.cluster.namespace_get()
const data = await getAllNamespace(ctx.client)
const ret = data.namespaces.map(item => item.name)
ctx.body = ret.filter((item) => {
if (!['default', 'kube-system'].includes(item)) {
return item
}
return null
}).join('\n')
ctx.body = ret.join('\n')
})
router.get('/info', async (ctx) => {
const data = await ctx.cluster.namespace_get()
const data = await getAllNamespace(ctx.client)
const ns = data.namespaces.find(item => item.name === qs.unescape(ctx.request.query.namespace))
ctx.body = ctx.ok(ns)
})
router.post('/create', async (ctx) => {
await ctx.cluster.namespace_create(ctx.request.body.name, ctx.request.body.description)
// await ctx.cluster.ingress_create(ctx.request.body.name)
ctx.body = ctx.ok()
})
const IMAGES = {
zookeeper: 'zookeeper:3.4.10',
rabbitmq: 'rabbitmq:3.6-management',
mysql: 'mysql:5.7',
redis: 'ccr.ccs.tencentyun.com/qa-db/redis:v4',
}
router.post('/init', async (ctx) => {
const body = ctx.request.body
// if (body.code !== newNsKey) {
// throw new Error('创建确认码不正确')
// }
await ctx.cluster.namespace_create(body.namespace)
await ctx.cluster.ingress_create(body.namespace)
async function serviceCreate(element, index) {
if (index !== 'common') {
const data = {
namespace: body.namespace,
image: `ccr.ccs.tencentyun.com/qa-${index}/${element}:latest`,
type: index,
serviceName: element,
system_name: element,
}
const template = templates[index].replace(/{{([A-Za-z0-9_]+)}}/g, function () {
if (data[arguments[1]] === undefined) {
throw new Error('缺少模板所需变量')
}
return data[arguments[1]]
})
const params = yaml.load(template)
await ctx.cluster.service_create(params, index)
}
if (index === 'common') {
const data = {
namespace: body.namespace,
image: IMAGES[element],
}
const template = templates[element].replace(/{{([A-Za-z0-9_]+)}}/g, function () {
if (data[arguments[1]] === undefined) {
throw new Error('缺少模板所需变量')
}
return data[arguments[1]]
})
const params = yaml.load(template)
await ctx.cluster.service_create(params)
}
}
for (const index in body.systems) {
if ({}.hasOwnProperty.call(body.systems, index)) {
for (const item in body.systems[index]) {
if ({}.hasOwnProperty.call(body.systems[index], item)) {
serviceCreate(body.systems[index][item], index)
}
}
}
}
ctx.body = ctx.ok('正在创建')
})
router.post('/delete', async (ctx) => {
await ctx.cluster.namespace_delete(ctx.request.body.name)
// await ctx.cluster.namespace_create(ctx.request.body.name, ctx.request.body.description)
await createNamespace(ctx.client, ctx.request.body.name, ctx.request.body.description)
ctx.body = ctx.ok()
})
function generateUUID() {
let d = new Date().getTime();
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : ((r & 0x3) | 0x8)).toString(16);
});
return uuid;
}
router.get('/get_code', async (ctx) => {
newNsKey = newNsKey === '' ? generateUUID() : newNsKey
ctx.body = ctx.ok(newNsKey)
})
// const IMAGES = {
// zookeeper: 'zookeeper:3.4.10',
// rabbitmq: 'rabbitmq:3.6-management',
// mysql: 'mysql:5.7',
// redis: 'ccr.ccs.tencentyun.com/qa-db/redis:v4',
// }
//
// router.post('/init', async (ctx) => {
// const body = ctx.request.body
// await ctx.cluster.namespace_create(body.namespace)
// await ctx.cluster.ingress_create(body.namespace)
//
// async function serviceCreate(element, index) {
// if (index !== 'common') {
// const data = {
// namespace: body.namespace,
// image: `ccr.ccs.tencentyun.com/qa-${index}/${element}:latest`,
// type: index,
// serviceName: element,
// system_name: element,
// }
// const template = templates[index].replace(/{{([A-Za-z0-9_]+)}}/g, function () {
// if (data[arguments[1]] === undefined) {
// throw new Error('缺少模板所需变量')
// }
// return data[arguments[1]]
// })
// const params = yaml.load(template)
// await ctx.cluster.service_create(params, index)
// }
// if (index === 'common') {
// const data = {
// namespace: body.namespace,
// image: IMAGES[element],
// }
// const template = templates[element].replace(/{{([A-Za-z0-9_]+)}}/g, function () {
// if (data[arguments[1]] === undefined) {
// throw new Error('缺少模板所需变量')
// }
// return data[arguments[1]]
// })
// const params = yaml.load(template)
// await ctx.cluster.service_create(params)
// }
// }
//
// for (const index in body.systems) {
// if ({}.hasOwnProperty.call(body.systems, index)) {
// for (const item in body.systems[index]) {
// if ({}.hasOwnProperty.call(body.systems[index], item)) {
// serviceCreate(body.systems[index][item], index)
// }
// }
// }
// }
//
// ctx.body = ctx.ok('正在创建')
// })
//
// router.post('/delete', async (ctx) => {
// await ctx.cluster.namespace_delete(ctx.request.body.name)
// ctx.body = ctx.ok()
// })
//
// function generateUUID() {
// let d = new Date().getTime();
// const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
// const r = (d + Math.random() * 16) % 16 | 0;
// d = Math.floor(d / 16);
// return (c === 'x' ? r : ((r & 0x3) | 0x8)).toString(16);
// });
// return uuid;
// }
//
// router.get('/get_code', async (ctx) => {
// newNsKey = newNsKey === '' ? generateUUID() : newNsKey
// ctx.body = ctx.ok(newNsKey)
// })
const Router = require('koa-router')
const yaml = require('js-yaml')
const templates = require('../serviceTemplate')
const IMAGES = ['rabbitmq:3.6-management']
const router = new Router()
module.exports = router
router.post('/create', async (ctx) => {
// let svc = await ctx.client.service_get('rabbitmq', ctx.request.body.namespace)
// if (svc) {
// ctx.body = ctx.fail('服务已经存在')
// return
// }
const data = {
namespace: ctx.request.body.namespace,
image: IMAGES[0],
}
const template = templates.rabbitmq.replace(/{{([A-Za-z0-9_]+)}}/g, function () {
if (data[arguments[1]] === undefined) {
throw new Error('缺少模板所需变量')
}
return data[arguments[1]]
})
const params = yaml.load(template)
await ctx.client.service_create(params)
ctx.body = ctx.ok('创建成功')
})
router.post('/delete', async (ctx) => {
await ctx.client.service_delete('rabbitmq', ctx.request.body.namespace)
ctx.body = ctx.ok('删除成功')
})
......@@ -2,14 +2,22 @@ const schedule = require('node-schedule')
const _ = require('lodash')
const sleep = require('sleep')
const logger = require('koa-log4').getLogger('deployLatest')
const cluster = require('../services/tke.clusterService').create()
// const cluster = require('../services/tke.clusterService').create()
const path = require('path')
const Client = require('kubernetes-client').Client
const config = require('kubernetes-client').config
const { serviceRestart, getServicesFormat, getAllNamespace } = require('../kubeService/service')
const container = require('../services/tke.containerService').create()
const { serviceRestart, getServicesFormat } = require('../kubeService/service')
const keepNamespace = ['default', 'kube-system']
const repoNS = ['qa-java', 'qa-ui', 'qa-node', 'qa-python']
const testNamespace = ['qa', 'fe', 'data', 'fis', 'xyqb2']
const client = new Client({
config: config.fromKubeconfig(
path.resolve(__dirname, './../kubeService/kubeConfig-qa.yaml'),
),
version: '1.10',
})
const deploy = async () => {
logger.info('deployLatest start')
for (const ns of repoNS) {
......@@ -19,15 +27,15 @@ const deploy = async () => {
const repoName = repo.reponame.split('/')[1]
const latest = await container.getTagByName(repo.reponame, 'latest')
const latestImageID = _.get(latest.tagInfo, '[0].tagId', '')
let ns = await cluster.namespace_get()
let ns = await getAllNamespace(client)
ns = ns.namespaces && ns.namespaces.filter(item => testNamespace.includes(item.name))
for (const namespace of ns) {
const svcs = await getServicesFormat(namespace.name)
const svcs = await getServicesFormat(client, namespace.name)
for (const svc of svcs) {
if (svc.serviceName === repoName && svc.image.indexOf('latest') !== -1) {
if (latestImageID !== svc.imageID.split('@')[1]) {
logger.info('部署项目', namespace.name, svc.podName)
await serviceRestart(namespace.name, svc.podName)
await serviceRestart(client, namespace.name, svc.podName)
sleep.msleep(500)
}
}
......
......@@ -2,10 +2,17 @@ const schedule = require('node-schedule')
const request = require('request')
const moment = require('moment')
const Redis = require('ioredis')
const logger = require('koa-log4')
.getLogger()
const path = require('path')
const Client = require('kubernetes-client').Client
const config = require('kubernetes-client').config
const { podGetstatus, getPods } = require('../kubeService/service')
const client = new Client({
config: config.fromKubeconfig(
path.resolve(__dirname, './../kubeService/kubeConfig-qa.yaml'),
),
version: '1.10',
})
const redis = new Redis(6380, '172.30.220.22')
const awaitRequest = function (options) {
return new Promise((resolve, reject) => {
......@@ -71,11 +78,6 @@ const dingTalkPush = async function (item, is_recover) {
},
body: JSON.stringify(dingData),
})
// if (JSON.parse(res).errcode === 0 && is_recover) {
// await redis.set(key, 'send')
// } else {
// logger.error(res.errmsg)
// }
}
const checkRecoverPod = async () => {
let stream = redis.scanStream({
......@@ -87,7 +89,7 @@ const checkRecoverPod = async () => {
const podname = resKeys[i].split('#')[1]
// 获取新的状态
if (podname.indexOf(resKeys[i].split('#')[1]) > -1) {
const Pod = await getPods(namespace)
const Pod = await getPods(client,namespace)
Pod.body.items.forEach(async (item) => {
if (item.metadata.name.indexOf(podname) > -1) {
if (item.status.conditions.length === 3) {
......@@ -119,7 +121,7 @@ const checkRecoverPod = async () => {
});
}
const checkErrorPod = async () => {
const listPods = await podGetstatus()
const listPods = await podGetstatus(client)
listPods.body.items.forEach((item) => {
const key = `${item.metadata.namespace}:${item.metadata.name}#${item.metadata.labels['qcloud-app']}`
item.status.conditions.forEach(async (value) => {
......@@ -127,7 +129,7 @@ const checkErrorPod = async () => {
const res = await redis.get(key)
if (res != null) {
if (res === 'send') {
logger.info(item.metadata.name, ':已到达阈值并已发送钉钉提醒服务')
// logger.info(item.metadata.name, ':已到达阈值并已发送钉钉提醒服务')
} else {
const counter = Number(res)
if (counter > 5) {
......
const Router = require('koa-router')
const logger = require('koa-log4').getLogger()
const logger = require('koa-log4')
.getLogger()
const _ = require('lodash')
const Redis = require('ioredis')
const redis = new Redis(6380, '172.30.220.22')
const { ingressCreate, ingressDelete } = require('../kubeService/ingress')
const { projectConfig, defaultConfig } = require('../serviceTemplate/resourceLimit')
const { projectConfig, defaultConfig } = require('../resource/resourceLimit')
const {
getPods,
serviceCreate,
......@@ -19,13 +20,17 @@ const {
replicaSetDelete,
pvcDelete,
} = require('../kubeService/service')
const getClient = require('./../resource/getClient')
const router = new Router()
module.exports = router
const clientNew = {}
router.get('/', async (ctx) => {
const data = await getServicesFormat(ctx.query.namespace)
ctx.body = ctx.ok({ services: data, namespace: ctx.query.namespace })
const data = await getServicesFormat(ctx.client, ctx.query.namespace)
ctx.body = ctx.ok({
services: data,
namespace: ctx.query.namespace,
})
})
const makeResouce = (serviceName, type) => {
......@@ -45,12 +50,12 @@ const createService = async (ctx) => {
data.resources = resources
logger.info('创建服务', data)
await serviceCreate(data)
await serviceCreate(ctx.cluster, ctx.client, data)
if (label !== 'base') {
if (serviceName === 'xyqb-user2') {
await ingressCreate(namespace, 'xyqb-user2-2', 'passportapi2')
await ingressCreate(ctx.client, namespace, 'xyqb-user2-2', 'passportapi2', ctx.clusterDomain)
}
await ingressCreate(namespace, serviceName, domain)
await ingressCreate(ctx.client, namespace, serviceName, domain, ctx.clusterDomain)
}
ctx.body = ctx.ok('创建成功')
}
......@@ -60,13 +65,17 @@ router.post('/create', async (ctx) => {
})
router.post('/details', async (ctx) => {
ctx.validate(ctx.Joi.object().keys({
serviceName: ctx.Joi.string().required(),
namespace: ctx.Joi.string().required(),
type: ctx.Joi.string().required(),
}))
const data = await getServiceDetail(ctx.request.body.namespace, ctx.request.body.serviceName, ctx.request.body.type)
ctx.validate(ctx.Joi.object()
.keys({
serviceName: ctx.Joi.string()
.required(),
namespace: ctx.Joi.string()
.required(),
type: ctx.Joi.string()
.required(),
}))
const data = await getServiceDetail(ctx.client, ctx.request.body.namespace, ctx.request.body.serviceName, ctx.request.body.type)
ctx.body = ctx.ok(data)
})
router.post('/delete', async (ctx) => {
......@@ -77,9 +86,9 @@ router.post('/delete', async (ctx) => {
if (res) {
await redis.set(`copy${namespace}&${serviceName}`, res)
}
await serviceDelete(namespace, serviceName)
await replicaSetDelete(namespace, serviceName)
await pvcDelete(namespace, serviceName)
await serviceDelete(ctx.client, namespace, serviceName)
await replicaSetDelete(ctx.client, namespace, serviceName)
await pvcDelete(ctx.client, namespace, serviceName)
// let rsData = await getReplicaSet(namespace)
// rsData = rsData.map(item => item.metadata.name).filter(item => item.indexOf(serviceName) !== -1)
......@@ -87,19 +96,19 @@ router.post('/delete', async (ctx) => {
// await replicaSetDelete(namespace, rs)
// }
await ingressDelete(namespace, serviceName)
await ingressDelete(ctx.client, namespace, serviceName)
if (serviceName === 'xyqb-user2') {
await ingressDelete(namespace, 'xyqb-user2-2')
await ingressDelete(ctx.client, namespace, 'xyqb-user2-2')
}
ctx.body = ctx.ok(`删除${serviceName}成功`)
})
router.post('/modifyImage', async (ctx) => {
let list = await getPods(ctx.request.body.namespace)
let list = await getPods(ctx.client, ctx.request.body.namespace)
list = list.body.items.map(item => (item.metadata.labels && item.metadata.labels['qcloud-app']) || item.metadata.name)
if (list.includes(ctx.request.body.serviceName)) {
await imageUpdate(ctx.request.body)
await imageUpdate(ctx.client, ctx.request.body)
} else {
await createService(ctx)
}
......@@ -114,7 +123,7 @@ router.post('/modifyDeploy', async (ctx) => {
}
const resources = makeResouce(data.serviceName, data.label)
data.resources = resources
await deployUpdate(data)
await deployUpdate(ctx.cluster, ctx.client, data)
ctx.body = ctx.ok('更新成功')
})
......@@ -126,26 +135,41 @@ router.post('/redeploy', async (ctx) => {
if (res) {
await redis.set(`copy${ctx.request.body.namespace}&${ctx.request.body.serviceName}`, res)
}
await serviceRestart(ctx.request.body.namespace, ctx.request.body.podName)
await serviceRestart(ctx.client, ctx.request.body.namespace, ctx.request.body.podName)
ctx.body = ctx.ok('重置服务成功')
})
// for dev
router.get('/listEnvVars', async (ctx) => {
const envVars = {}
let cluster;
let namespace;
const parmars = ctx.request.query.namespace || ''
if (parmars.search('@') === -1) {
cluster = 'qa'
namespace = ctx.request.query.namespace
} else {
namespace = parmars.split('@')[0];
cluster = parmars.split('@')[1]
}
if (!clientNew[cluster]) {
clientNew[cluster] = getClient(cluster)
}
const res = await Promise.all([
getPods(ctx.request.query.namespace),
getServices(ctx.request.query.namespace),
getPods(clientNew[cluster], namespace),
getServices(clientNew[cluster], namespace),
])
res[0].body.items.forEach(async (item) => {
const serviceName = (item.metadata.labels && item.metadata.labels['qcloud-app']) || item.metadata.name
const upperCaseName = serviceName.toUpperCase().replace(/-/g, '_')
const upperCaseName = serviceName.toUpperCase()
.replace(/-/g, '_')
envVars[`${upperCaseName}_SERVICE_HOST`] = item.status.hostIP
})
res[1].body.items.forEach(async (item) => {
const upperCaseName = item.metadata.name.toUpperCase().replace(/-/g, '_')
const upperCaseName = item.metadata.name.toUpperCase()
.replace(/-/g, '_')
envVars[`${upperCaseName}_SERVICE_PORT`] = _.get(item.spec.ports, '[0].nodePort', undefined)
item.spec.ports.forEach((i) => {
envVars[`${upperCaseName}_SERVICE_PORT_${i.port}`] = i.nodePort || i.port
......@@ -156,20 +180,38 @@ router.get('/listEnvVars', async (ctx) => {
envVars.DB_SERVICE_PORT = envVars.MYSQL_SERVICE_PORT
envVars.DB_SERVICE_PORT_3306 = envVars.MYSQL_SERVICE_PORT_3306
ctx.body = { details: envVars, success: true }
ctx.body = {
details: envVars,
success: true,
}
})
// for container
router.get('/listEnvVarsNew', async (ctx) => {
const envVars = {}
let cluster;
let namespace;
const parmars = ctx.request.query.namespace || ''
if (parmars.search('@') === -1) {
cluster = 'qa'
namespace = ctx.request.query.namespace
} else {
namespace = parmars.split('@')[0];
cluster = parmars.split('@')[1]
}
if (!clientNew[cluster]) {
clientNew[cluster] = getClient(cluster)
}
const res = await Promise.all([
getPods(ctx.request.query.namespace),
getServices(ctx.request.query.namespace),
getPods(clientNew[cluster], namespace),
getServices(clientNew[cluster], namespace),
])
res[0].body.items.forEach(async (item) => {
const serviceName = (item.metadata.labels && item.metadata.labels['qcloud-app']) || item.metadata.name
const upperCaseName = serviceName.toUpperCase().replace(/-/g, '_')
const upperCaseName = serviceName.toUpperCase()
.replace(/-/g, '_')
if (item.metadata.labels.type === 'base') {
envVars[`${upperCaseName}_SERVICE_HOST`] = serviceName
} else {
......@@ -178,7 +220,8 @@ router.get('/listEnvVarsNew', async (ctx) => {
})
res[1].body.items.forEach(async (item) => {
const upperCaseName = item.metadata.name.toUpperCase().replace(/-/g, '_')
const upperCaseName = item.metadata.name.toUpperCase()
.replace(/-/g, '_')
const type = item.metadata.labels.type
if (type === 'base') {
envVars[`${upperCaseName}_SERVICE_PORT`] = _.get(item.spec.ports, '[0].port', undefined)
......@@ -197,5 +240,8 @@ router.get('/listEnvVarsNew', async (ctx) => {
envVars.DB_SERVICE_PORT = envVars.MYSQL_SERVICE_PORT
envVars.DB_SERVICE_PORT_3306 = envVars.MYSQL_SERVICE_PORT_3306
ctx.body = { details: envVars, success: true }
ctx.body = {
details: envVars,
success: true,
}
})
const Router = require('koa-router')
const yaml = require('js-yaml')
const _ = require('lodash')
const logger = require('koa-log4').getLogger()
const templates = require('../serviceTemplate')
const lruCache = require('../services/lruCache.service')
const { ingressCreate, ingressDelete } = require('../kubeService/ingress')
const { projectConfig, defaultConfig } = require('../serviceTemplate/resourceLimit')
const { podGet, serviceCreate } = require('../kubeService/service')
const router = new Router()
module.exports = router
router.get('/', async (ctx) => {
// 取节点列表的第一个作为服务的访问ip
const cacheKey = 'k8s.nodes.first'
const c = lruCache.get(cacheKey)
let lanIp = lruCache.get(cacheKey)
if (!c) {
const res = await ctx.cluster.node_list()
lanIp = _.get(res, 'nodes[0].lanIp', '')
lruCache.set(cacheKey, lanIp)
}
const data = await ctx.cluster.service_list(ctx.query.namespace)
const podData = await podGet(ctx.query.namespace)
const getDetail = async (item) => {
const resData = await ctx.cluster.service_get(item.serviceName, ctx.query.namespace)
item.image = resData.service.containers[0].image
if (item.userLabels.type === 'base') {
item.portMappings = resData.service.portMappings
const pod = podData.body.items.filter(i => i.metadata.name.indexOf(item.serviceName) !== -1)
lanIp = pod[0].status.hostIP
item.lanIp = lanIp
}
}
const task = []
for (let i = 0; i < data.services.length; i += 1) {
task.push(getDetail(data.services[i]))
}
await Promise.all(task)
ctx.body = ctx.ok(data)
})
const createService = async (ctx) => {
const {
type, serviceName, namespace, image, system_name, domain, label, debug,
} = ctx.request.body
logger.info('创建服务', ctx.request.body)
if (label === 'base') {
await serviceCreate(namespace, serviceName, image, label)
ctx.body = ctx.ok('创建成功')
return
}
const data = {
serviceName,
namespace,
image,
system_name,
debug,
}
if (!system_name) {
// ui abTest的时候不一样
data.system_name = serviceName
}
// 资源限制
const resources = projectConfig[data.system_name] || defaultConfig[type]
logger.info('资源限制', JSON.stringify(resources))
data.resources = resources
const template = templates[type].replace(/{{([A-Za-z0-9_\.]+)}}/g, function () {
if (_.get(data, arguments[1], null) === null) {
throw new Error(`缺少模板所需变量: ${arguments[1]}`)
}
return _.get(data, arguments[1])
})
let params = yaml.load(template)
// todo: 放开限制,暂时只更新了clotho的镜像
// java项目的就绪检查需要定制
if (type === 'java' && data.system_name !== 'clotho') {
params = _.omitBy(params, (value, key) => key.indexOf('healthCheck') !== -1)
}
logger.info(params)
await ctx.cluster.service_create(params, label)
if (label !== 'base') {
if (serviceName === 'xyqb-user2') {
await ingressCreate(namespace, 'xyqb-user2-2', 'passportapi2')
}
await ingressCreate(namespace, serviceName, domain)
}
ctx.body = ctx.ok('创建成功')
}
router.post('/create', async (ctx) => {
await createService(ctx)
})
router.post('/details', async (ctx) => {
ctx.validate(ctx.Joi.object().keys({
serviceName: ctx.Joi.string().required(),
namespace: ctx.Joi.string().required(),
}))
const podData = await podGet(ctx.request.body.namespace)
// const lanIp = podData.body.items.filter(i => i.metadata.name.indexOf(ctx.request.body.serviceName) !== -1)[0].status.hostIP
const serviceItems = podData.body.items.filter(i => i.metadata.name.indexOf(ctx.request.body.serviceName) !== -1)
const lanIp = _.get(serviceItems, '[0].status.hostIP', '')
const data = await ctx.cluster.service_get(ctx.request.body.serviceName, ctx.request.body.namespace)
ctx.body = ctx.ok(Object.assign({}, data, { lanIp }))
})
router.post('/delete', async (ctx) => {
await ctx.cluster.service_delete(ctx.request.body.serviceName, ctx.request.body.namespace)
if (ctx.request.body.serviceName === 'xyqb-user2') {
await ingressDelete(ctx.request.body.namespace, 'xyqb-user2-2')
}
await ingressDelete(ctx.request.body.namespace, ctx.request.body.serviceName)
ctx.body = ctx.ok('删除成功')
})
router.post('/modifyImage', async (ctx) => {
let list = await ctx.cluster.service_list(ctx.request.body.namespace)
list = list.services.map(item => item.serviceName)
if (list.includes(ctx.request.body.serviceName)) {
await ctx.cluster.service_modifyImage(ctx.request.body.serviceName, ctx.request.body.image, ctx.request.body.namespace)
} else {
await createService(ctx)
}
ctx.body = ctx.ok('更新成功')
})
router.post('/instance', async (ctx) => {
const data = await ctx.cluster.instance_get(ctx.request.body.serviceName, ctx.request.body.namespace)
ctx.body = ctx.ok(data)
})
router.post('/redeploy', async (ctx) => {
await ctx.cluster.service_redeployment(ctx.request.body.serviceName, ctx.request.body.namespace)
ctx.body = ctx.ok('服务重部署成功')
})
const Router = require('koa-router')
const yaml = require('js-yaml')
const templates = require('../serviceTemplate')
const IMAGES = ['zookeeper:3.4.10']
const router = new Router()
module.exports = router
router.post('/create', async (ctx) => {
const data = {
namespace: ctx.request.body.namespace,
image: IMAGES[0],
}
const template = templates.zookeeper.replace(/{{([A-Za-z0-9_]+)}}/g, function () {
if (data[arguments[1]] === undefined) {
throw new Error('缺少模板所需变量')
}
return data[arguments[1]]
})
const params = yaml.load(template)
await ctx.client.service_create(params)
ctx.body = ctx.ok('创建成功')
})
router.post('/delete', async (ctx) => {
await ctx.client.service_delete('zookeeper', ctx.request.body.namespace)
ctx.body = ctx.ok('删除成功')
})
const app = require('../app')
app.start()
app.start(4000)
### 镜像说明
1. redis
```
基于redis-alpine,减小镜像大小
业务需要支持多端口,所以镜像中启动了5个单独的实例。
```
2. ui
```
基于centos7的openresty官方镜像
配置文件路径 /etc/nginx/conf.d/*.conf, 默认叫default.conf
ui项目,SYSTEM_NAME从环境变量获取,所以需要在nginx.conf中增加env
abTest的项目,在构建镜像时使用参数CONFIG=abTest/paycenter.conf
```
### 问题
1. 把自定义的conf加放到entrypoint中?
感觉就成了config_repository
FROM node:8-alpine
ARG CONFIG=conf/default.js
ARG PROJECT=koa2-hello
ARG TARGET=src/config/
# 拷贝代码
# todo: 从代码镜像中拷贝
ADD code.tar.gz /home/quant_group
# 拷贝配置文件
WORKDIR /home/quant_group/${PROJECT}
COPY ${CONFIG} ${TARGET}
COPY docker-entrypoint.sh ./
CMD ./docker-entrypoint.sh
#!/bin/bash
docker build --build-arg CONFIG=conf/opapi2.js -t test-nodejs .
\ No newline at end of file
#!/bin/bash
echo 192.168.4.3 git.q-gp.com >> /etc/hosts
curl -sSL http://git.q-gp.com/QA/qg-docker-entrypoints/raw/tke-ui/tke/nodejs.sh -o run.sh
sh run.sh
FROM redis:3-alpine
WORKDIR /home/quantgroups/redis
COPY conf/*.conf ./conf/
COPY start.sh .
RUN mkdir /var/log/redis \
&& mkdir /var/lib/redis \
&& mkdir /var/run/redis
EXPOSE 6379 6380 6381 6382 6383
CMD ./start.sh
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
daemonize no
pidfile /var/run/redis/redis.pid
port 6383
bind 0.0.0.0
timeout 0
tcp-keepalive 0
loglevel notice
logfile /var/log/redis.log
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /var/lib/redis/
slave-serve-stale-data yes
slave-read-only yes
repl-disable-tcp-nodelay no
slave-priority 100
appendonly no
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
#bin/bash
redis-server /home/quantgroups/redis/conf/redis-6380.conf &
redis-server /home/quantgroups/redis/conf/redis-6381.conf &
redis-server /home/quantgroups/redis/conf/redis-6382.conf &
redis-server /home/quantgroups/redis/conf/redis-6383.conf &
redis-server /home/quantgroups/redis/conf/redis-6379.conf
\ No newline at end of file
# 基于centos7
FROM openresty/openresty:1.13.6.2-centos
COPY nginx.conf /usr/local/openresty/nginx/conf/
WORKDIR /home/quant_group
COPY test-ui.conf ./conf.d/
COPY index.html ./test-ui/
CMD ["/usr/local/openresty/bin/openresty", "-g", "daemon off;"]
#!/bin/bash
docker build -t test-ui .
\ No newline at end of file
#!/bin/bash
docker build -t test-ui .
docker tag test-ui ccr.ccs.tencentyun.com/qa-app/test-ui:2
docker push ccr.ccs.tencentyun.com/qa-app/test-ui:2
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>2222</title>
</head>
<body>
<h1>22222</h1>
</body>
</html>
\ No newline at end of file
#user nobody;
worker_processes 1;
error_log logs/error.log;
error_log logs/error.log notice;
error_log logs/error.log info;
#pid logs/nginx.pid;
env SYSTEM_NAME;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
include /home/quant_group/conf.d/*.conf;
}
\ No newline at end of file
server {
listen 80;
server_name _;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 4;
gzip_types text/plain application/x-javascript text/css application/xml application/javascript;
gzip_vary on;
location / {
expires -1;
root /home/quant_group/test-ui;
try_files $uri $uri/ /index.html?$query_string;
}
}
\ No newline at end of file
# 基于centos7
FROM openresty/openresty:1.13.6.2-centos
RUN yum -y install epel-release \
&& yum -y install python-pip
WORKDIR /home/quant_group
# 加入测试工具集
COPY qa-replace .
RUN pip install -r requirements.txt \
&& yum clean all
# 拷贝nginx配置
COPY lua/*.lua /etc/nginx/lua/
ARG CONFIG=default.conf
COPY conf/nginx.conf /usr/local/openresty/nginx/conf/
COPY conf/${CONFIG} /etc/nginx/conf.d/
# COPY docker-entrypoint.sh ./
# CMD ./docker-entrypoint.sh
#!/bin/bash
tag=2.4
docker build -t test-ui .
# && docker tag test-ui 192.168.4.4/tmp/test-ui:${tag} && docker push 192.168.4.4/tmp/test-ui:${tag}
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>22</title>
</head>
<body>
<h1>op-xuezhijie.liangkebang.com</h1>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>1111</title>
</head>
<body>
<h1>1</h1>
</body>
</html>
\ No newline at end of file
server {
listen 80;
server_name _;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 4;
gzip_types text/plain application/x-javascript text/css application/xml application/javascript;
gzip_vary on;
set $normalPath "/home/quant_group/spider-center-ui/dist";
set $channelPath "/home/quant_group/new-spider-center-ui/dist";
set $registerChannel "999999";
set $merchantChannel "";
set $excludeChannel "504,900";
set $tailNumber "1,2,3,4,5,6,7,8,9";
location / {
expires -1;
set_by_lua_file $rootPath /etc/nginx/lua/phoneChannel.lua;
header_filter_by_lua_file /etc/nginx/lua/addCookie.lua;
root $rootPath;
try_files $uri $uri/ /index.html?$query_string;
}
}
server {
listen 80;
server_name _;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 4;
gzip_types text/plain application/x-javascript text/css application/xml application/javascript;
gzip_vary on;
location / {
expires -1;
set_by_lua $rootPath '
local pre = "/home/quant_group/code/";
local sys = os.getenv("SYSTEM_NAME");
return pre..sys.."/dist"
';
root $rootPath;
try_files $uri $uri/ /index.html?$query_string;
}
}
\ No newline at end of file
#user nobody;
worker_processes 1;
error_log logs/error.log;
error_log logs/error.log notice;
error_log logs/error.log info;
#pid logs/nginx.pid;
env SYSTEM_NAME;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
include /home/quant_group/conf.d/*.conf;
}
\ No newline at end of file
#!/bin/bash
echo 192.168.4.3 git.q-gp.com >> /etc/hosts
curl -sSL http://git.q-gp.com/QA/qg-docker-entrypoints/raw/tke-ui/tke/ui.sh -o run.sh
sh run.sh
local args = ngx.req.get_uri_args()
local register_uri = args['registerFrom']
local merchant_uri = args['merchantId']
if (register_uri) then
ngx.header['Set-Cookie'] = {'registerFrom=' .. register_uri .. '; path=/; domain=.liangkebang.com;'}
end
if (merchant_uri) then
ngx.header['Set-Cookie'] = {'merchantId=' .. merchant_uri .. '; path=/; domain=.liangkebang.com;'}
end
\ No newline at end of file
function string.split(str)
local splitlist = {};
string.gsub(str, '[^,]+', function(w) table.insert(splitlist, w) end )
return splitlist;
end
function isInTable(value, tbl)
for k,v in ipairs(tbl) do
if v == value then
return true;
end
end
return false;
end
local path = ngx.var.normalPath;
local channelPath = ngx.var.channelPath;
-- 解析手机号和特殊渠道
local tailNumbers = string.split(ngx.var.tailNumber);
local excludeChannel = string.split(ngx.var.excludeChannel)
-- 解析渠道设置
local register = string.split(ngx.var.registerChannel);
-- cookie值
local register_cookie = ngx.var.cookie_registerFrom
-- uri参数值
local args = ngx.req.get_uri_args()
local register_uri = args['registerFrom']
local phoneNo = args['phoneNo']
if (not phoneNo or string.len(phoneNo) == 0) then
phoneNo = ngx.var.cookie_phoneNo
end
local baiTiao = '222';
if (register_uri) then
isBaitiao = register_uri == baiTiao
else
isBatitiao = register_cookie == baiTiao
end
if (isBatitiao) then
return channelPath;
end
-- 没有设置渠道,设置了手机号,属于特殊渠道(不属于特殊,但符合尾号规则)时,返回旧版
if (ngx.var.excludeChannel ~= '' or ngx.var.tailNumber ~= '') then
local hitExclude = isInTable(register_uri, excludeChannel) or isInTable(register_cookie, excludeChannel)
if (hitExclude) then
return path;
end
-- 是否符合手机尾号规则
local hitTailNumber = false
if phoneNo then
for key,value in ipairs(tailNumbers)
do
if (string.match(phoneNo, value..'$')) then
hitTailNumber = true
break
end
end
end
if (hitTailNumber) then
return channelPath;
end
end
return channelPath;
function string.split(str)
local splitlist = {};
string.gsub(str, '[^,]+', function(w) table.insert(splitlist, w) end )
return splitlist;
end
function isInTable(value, tbl)
for k,v in ipairs(tbl) do
if v == value then
return true;
end
end
return false;
end
local path = ngx.var.normalPath;
local channelPath = ngx.var.channelPath;
-- 没有设置渠道,返回旧目录
if (ngx.var.registerChannel == '' and ngx.var.merchantChannel == '') then
return path;
end
-- 解析渠道设置
local register = string.split(ngx.var.registerChannel);
local merchant = string.split(ngx.var.merchantChannel);
-- cookie值
local register_cookie = ngx.var.cookie_registerFrom
local merchant_cookie = ngx.var.cookie_merchantId
-- uri参数值
local args = ngx.req.get_uri_args()
local register_uri = args['registerFrom']
local merchant_uri = args['merchantId']
-- 优先匹配参数中的registerFrom
if (register_uri) then
hitRegister = isInTable(register_uri, register)
else
hitRegister = isInTable(register_cookie, register)
end
-- 优先匹配参数中的merchantId
if (merchant_uri) then
hitMerchantId = isInTable(merchant_uri, register)
else
hitMerchantId = isInTable(merchant_cookie, register)
end
local baiTiao = '222';
--
if (register_uri) then
isBaitiao = register_uri == baiTiao
else
isBatitiao = register_cookie == baiTiao
end
-- 是白条,未设置merchantId规则,命中
if (isBaitiao and ngx.var.merchantChannel == '') then
return channelPath
end
-- 是白条,设置了merchantId规则,匹配merchantId后命中
if (isBaitiao and ngx.var.merchantChannel ~= '' and hitMerchantId) then
return channelPath
end
-- 不是白条,匹配registerFrom后命中
if ( not isBaitiao and hitRegister) then
return channelPath
end
return path;
const path = require('path')
const Client = require('kubernetes-client').Client
const config = require('kubernetes-client').config
const logger = require('koa-log4').getLogger()
// const client = new Client({ config: config.fromKubeconfig(), version: '1.9' })
const client = new Client({
config: config.fromKubeconfig(
path.resolve(__dirname, './kubeConfig.yaml'),
),
version: '1.10',
})
const getManifest = (namespace, servicename, doamin) => ({
const getManifest = (namespace, servicename, doamin, clusterDomain) => ({
apiVersion: 'extensions/v1beta1',
kind: 'Ingress',
metadata: {
......@@ -24,7 +13,7 @@ const getManifest = (namespace, servicename, doamin) => ({
spec: {
rules: [
{
host: `${doamin}-${namespace}.liangkebang.net`,
host: `${doamin}-${namespace}.${clusterDomain}`,
http: {
paths: [
{
......@@ -40,23 +29,23 @@ const getManifest = (namespace, servicename, doamin) => ({
],
},
})
const ingressGet = async (namespace, servicename) => client.apis.extensions.v1beta1.namespaces(namespace)
const ingressGet = async (client, namespace, servicename) => client.apis.extensions.v1beta1.namespaces(namespace)
.ingresses(servicename).get()
const ingressCreate = async (namespace, servicename, doamin) => {
const Manifest = getManifest(namespace, servicename, doamin)
const ingressCreate = async (client, namespace, servicename, doamin, clusterDomain) => {
const Manifest = getManifest(namespace, servicename, doamin, clusterDomain)
logger.info('创建ingress', JSON.stringify(Manifest))
await client.apis.extensions.v1beta1.namespace(namespace).ingresses
.post({ body: Manifest })
}
const ingressUpdate = async (namespace, servicename, host) => {
const Manifest = getManifest(namespace, servicename)
const ingressUpdate = async (client, namespace, servicename, host, clusterDomain) => {
const Manifest = getManifest(namespace, servicename, clusterDomain)
Manifest.spec.rules[0].host = host
logger.info('更新ingress', Manifest)
await client.apis.extensions.v1beta1.namespace(namespace).ingresses(servicename)
.patch({ body: Manifest })
}
const ingressDelete = async (namespace, servicename) => {
const ingressDelete = async (client, namespace, servicename) => {
logger.info('删除ingress', namespace, servicename)
await client.apis.extensions.v1beta1.namespaces(namespace).ingresses(servicename).delete()
}
......
apiVersion: v1
clusters:
- cluster:
certificate-authority: tke-cluster-dev-ops.crt
server: https://cls-pbwgwa1p.ccs.tencent-cloud.com
name: tke-cluster
contexts:
- context:
cluster: tke-cluster
user: tke-admin
name: tke-qa-system
current-context: tke-qa-system
kind: Config
preferences: {}
users:
- name: tke-admin
user:
password: SWjVjJ4PgvQrEdlGW9MNa7GRzWmBtdgl
username: admin
apiVersion: v1
clusters:
- cluster:
certificate-authority: tke-cluster-ca.crt
certificate-authority: tke-cluster-qa.crt
server: https://cls-acfx4pvj.ccs.tencent-cloud.com
name: tke-cluster
contexts:
......
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1EZ3lOekF6TlRVeU4xb1hEVEk1TURneU5EQXpOVFV5TjFvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTk10ClNzNlkzYlVJenl1REpSTXNHWnhqbWhOUDBtR2Z6VXY5a3BkWGpMK3RWdnZHQ3NOeFZmNXAreHdESFJtVGIvVVIKbWl2bHJ0ZDRMS0I0OTFIRFZyTGJrNXRITTJaVjhGbjZQdGpOTjQyczBNYWZjZ3BhbU1zOWhmbHg0UUFNMS9USwptNEhjbUxCSTVlUmR0QWVRZVZnbUg2c1J1RkV5Sm5TdHdPR3AwOFdHUzFpcWNvMVd3T21nYUx4OWhKUlU2ekhoCjhRZG8yNHdQMXFweUNMd20rczJ4WE1qcVlZWnkyVnlzQmZMK0dYNEV3UTVFblBRL3FIUXcyUVptRkF2Tkk1c3EKM1dRVEIvZ3JKMHNWeHpqVXBzQXhUcE1WUkxrNlZjRmU4bkNpZWorcHNPbjM1YXAwSFU1OTVXdHhEdyt3azhBQQpWdDJUdmk3OXYzOXljQ25tU1RrQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFNbHM1ZitLV1VJZmZ1STdNUHRaaEp2YWRVNHkKZDdweVJxSXh0eFh2Y0Y3V1dCb01RV0Vtc1pUeVREYnVNWnBMdStTM3doM1p5OTRVOUtkNVpaMVVNOGRMdFFxbwpzVHNjTnB4LzFDa0QvZkFhalo4ajNBWWNxTHA4Rnl0RWZXbVR6dk5tZ05RSGtxSGNpQitQL2d3dWNmVmNjVlA3CnJxOTZpaG9QN2lXc3ZBSVB2bTdNS2YvbHgwRkxBRnJLL3hpSmM4WXZJanZQeGk2WW5zaDVSaUtVZjRaTEQxa24KT1lOQldEY2h4K05KMnVMU3R3TVI0VHhHN1FvV2ljZ0tiZkZZTzdhZ3BZUU9RdlkySkdPS25CUGVaSkpLSk9OVAoveUhxUC9zbTR5czNqMU8zNDFKaCt1dVkrQnFJbko2RkJWblkwa3V0bksvRWpEUFIvcXRkdlNobHk2bz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
server: https://172.21.10.32:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJSDhGUk51WEx2VVV3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T1RBNE1qY3dNelUxTWpkYUZ3MHlNREE0TWpZd016VTFOREJhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXh2MkN0UXZma1FyOTBIdXkKa00reCtQOEhsampBMkc3aktkbDVjWWZ1ZlZ2OW1rTHg3VjUzYWM1RkNoOTVPNThvY0lUQ2JZcVZoS0NxalQyVAplbE11TlhiZXVHYytJQ1JINVpKeWZZdGJJenZlM3BMNmZCYzN4K1ZBNW1ZRVZpMzVPYk9obzQveW1Ia1JoV21kCjZVQTljMy9TckR6b3Uxc0djb0VzMkJaK2JWV092UENXZEIxNHJRMktLWUx0VFdPc3VBOVEwRTlieHFNai9uQjQKbkFYY2lJSWp2QTRUd1JNY3d2a1dPVU5CUmU2c1RTTWE5RHhlaks0YXE1YmZWNWlmdXdxWmdtTEJoNWUxZm44Zwp5dkgvZVZrVWs1aVRZRytkTnNhU0grUkV1a25nN0pwOFZVcGVqb011TUtQdHlUcnl4ZklhK1JydzZSd3Y4aEtDCjVRRVhoUUlEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFCa2lTdlAzQzh0SEpFUzFzc2diZTNyMFRvUVd6QjBtNzBIYwpHekpSZy9lVzcyUDIwUmZBQXNCY1lONUFTcytzTGduazQrRjV2WHlyVXBQRkxwMnRFMXhZbzQyNjl5S2k2bm1qCkI1RVA4WU9xUFRhS1Q1L2s0L2tPTjFUUHZHZy9nT2NVZXhZSm50aWtUa2VSMGgrRm5oQndsdFlmbW95bGp1REkKeDZzQUsxczhIMnpVZXc1Zk82eGQ1dlFnZ3lHVmNUVVo2cDlBMGxlVUlGTmx3QXlIL3J1UGh0OGJTbkJTUit3MgpZTVc3eFZ4blhqYVVHejRpekpiUnQvZ1p2N3JtUm9yaHZhZEV1MFF0T2lWSzJpdUtTVzVWTmtFcVhGZFk4L1B1CnZ5djJ5TTc3V2kvSFdKVm9aUnZPblRmdEdVdnprV2w1Q0oxd0V6YWVaV2JOOFdiNi9rWT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBeHYyQ3RRdmZrUXI5MEh1eWtNK3grUDhIbGpqQTJHN2pLZGw1Y1lmdWZWdjlta0x4CjdWNTNhYzVGQ2g5NU81OG9jSVRDYllxVmhLQ3FqVDJUZWxNdU5YYmV1R2MrSUNSSDVaSnlmWXRiSXp2ZTNwTDYKZkJjM3grVkE1bVlFVmkzNU9iT2hvNC95bUhrUmhXbWQ2VUE5YzMvU3JEem91MXNHY29FczJCWitiVldPdlBDVwpkQjE0clEyS0tZTHRUV09zdUE5UTBFOWJ4cU1qL25CNG5BWGNpSUlqdkE0VHdSTWN3dmtXT1VOQlJlNnNUU01hCjlEeGVqSzRhcTViZlY1aWZ1d3FaZ21MQmg1ZTFmbjhneXZIL2VWa1VrNWlUWUcrZE5zYVNIK1JFdWtuZzdKcDgKVlVwZWpvTXVNS1B0eVRyeXhmSWErUnJ3NlJ3djhoS0M1UUVYaFFJREFRQUJBb0lCQUFTRDFnWmptWlFCNnRaagpLQjhQQjF1OTlaOGI5SXcwbitUZ01WZzVIRmt0MENoQzRwaGZMMWh3UExMSkdnZ1hTV1NjTG9rcnZzNitrb0FYCjZKTzY5VzV6SHdTU0d4L0JrdndMZ1p1Rm9QREF1QUxXMU9ZaDVMc2RZWWFZdy9wc21wNExqRnhZdE1sdTNjM28KSXBRYlNBbXAyYzdjcHU4eFZva2NremJuODhCcFNVS2pPdGQyOWhkR1VpRWM2dWErZmxWbTBleit5Qy9EK2kyUwpHZCs2UENDZU9xMHRXNHN2b2Q5eDlmRlVuWGZRTWJnUzFkeHJnY25QUUFRYUpxdUtRVncyNTNUb0p2OENNdDhOCjdpNG1RTy9HajE5aXJ3ZmdySWg3bTdubTlDZ1FwdktCRFY1WFdTRUYvSnR3N3NOQ3ZQZGtOanRTbk5GZlN1SUQKZWNoMFJTRUNnWUVBK21oVkVhZGdoM2VlTjFLdVlJTVI3b1ZvRjVyR1ZmdVM0TkU3M3ZCRUkxS1JSbVNNU3pabwpXRm5RMi9rUE5CcS8xaXRJTWo1RzZkWkY1UzlmdFQ3OWN0ZlozOUcxZUVVYWtrcXJHSFB3dVh0bkorVkpjK21DCkRCR2JnWTQvZ3ljbGFZNllkM3c5SXp6eGprRDVLQk1VUlZIMjNwRlZqM0ZaT2M2YXNrK3FxNTBDZ1lFQXkyODEKTElqeEFycHhVZWRaZERyWDExbmhBeTdmRWhncGxGZFdHRHl5RlA4aURTSEpueUZHajR0eUJGQ2ljSnBJa1pFbApkd3JGQ3gvdkNncGp5dklPTWN5WGo0czJyMzZKNWQ3UTVMQno2cElXa1o1SjR2Q3g3ZWxub093VS9sZno2bGVqCldvUGh4RXNCdzMvWDE4RkJVclArWmxIdkJObGJSeXd2dHgydG13a0NnWUJQcEZkS0loNEZkL29PUm5ISHFHaFAKb0lhVzIxY3hpYTRSYXZyTDBVY2FwT3dubzFzcm9NWlhNMW5HWE5TRkVlenB4U0pWT0pGOVZmVXRDNEE1T1N1TApGelBvaDk1YnZqN1JRSVppWkoydm9Kd1NpRTV3VzFoTlZseExQRjdlNjlodVZqZ1A2S2ZaU1V6YW5iZWhFMUJqCks4cUV4MXliSHNYdUtOUHkybnZCMFFLQmdRQ2ZPMWR5bVdtVUNoMGl6Nk84RDBiY3YvUVl5cm85N24yMUo3c3kKMDJkTWVmck1McE1qSkNlWHViMHYxWEdnRTVNVHBYbWV1T1pNYzkxbGpZWlkxcWYzb0wxTTFObWorYmEwSDNpQQpXQUVUTE8vUFlRZFNRN1h4azR6U2I1ZHJFYXJRMVhIK0tsWXZqOG84bkxkMG9FYWlGbExkQWVyRmIrQTdVRUcwCjRkeitDUUtCZ1FDbG10VHRNSTYyN0tqc0x3MVJGcEJ2cXN3TlJVajFMV04zUGFBM2Q5cGhJS1pRQVRGS1ZnQzIKVDgwZjdZWG9iZHVBTHFvSDNEanRnSTJuQ1ZmM0ZmOXFRTURNb2prR1JGWGo2d2lHWXlVanRhUXhRRFA1NjM1OQpuUmlNZUExWkxJVFkwemt4K0xRaXhkeFEzK0NoSjA3ZEtLNng4SHRwaTBMYVpqMGMycXlrTnc9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
\ No newline at end of file
This diff is collapsed.
-----BEGIN CERTIFICATE-----
MIIDZDCCAkygAwIBAgIISbRZqjmbMGkwDQYJKoZIhvcNAQELBQAwUDELMAkGA1UE
BhMCQ04xKjARBgNVBAoTCnRlbmNlbnR5dW4wFQYDVQQKEw5zeXN0ZW06bWFzdGVy
czEVMBMGA1UEAxMMY2xzLXBid2d3YTFwMB4XDTE5MDQxODA3NDI1MFoXDTM5MDQx
ODA3NDI1MFowUDELMAkGA1UEBhMCQ04xKjARBgNVBAoTCnRlbmNlbnR5dW4wFQYD
VQQKEw5zeXN0ZW06bWFzdGVyczEVMBMGA1UEAxMMY2xzLXBid2d3YTFwMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt9+zDO0GFdhdF+dLj7O2LnvJ3T9h
vwGEDlZtJCzYu1+zcxDOHfAW3Dc/F2WEIs/uEobHrzqQcz0syB1K9XmU7oXPkKU+
qCv660wH0PtufSDojiIqxAve5tlGIdnOf0o53kArcE/QQe7Eof0smkeS/QXnQze9
jkKiDgOnB6rtY9DeDqY7/zpZ59mkPGh6+4OYfaPHEPNCSdLp7wrPoEIh5JoZcAlP
Hr+RpCGGRsHVW8uqBsRblYZujMO1TNET4HmmVUHzeuGqt0VQsaRoXmOy6BdAqL/F
+Ko0BjkuooeOQopcuoNEBP/Ogsb9yoBeUTOTrAnKJ2vtrbIIPVP7wlX/dwIDAQAB
o0IwQDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUF
BwMBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFKafPQghDbF
L4FAW6//kKtauIvWQ0KazV0zacnvOv3QJIVAIIyKzEGEfgTFj/u9bp+0phU+zh+t
i9o7Jpv/lP1RaTLhax717jmPnM3e1xgf54+w9pVGuXXQgizNC3ZSxcw58xvDfYQX
cxXnSxjueG3Po7P1x6dhCxLJkknyIqrNKbtmyvgDpIVK+vBzCxHsjn2g6NUeoZEZ
e1evV0N+Z78dlOpgAtcVPsS8ga/XMfGZDsoPd0zsBOdcRyxb779PoLX1sbh/VkfT
idZKA4rM2Y65JxSFR+vgSAJe/7CNzqGyJbCIfstShUvmrry7GaASyjF+jLLL1eJq
u4oYFYdjpQk=
-----END CERTIFICATE-----
......@@ -19,3 +19,9 @@ GrlQjPW8071pJoTiX15DsweMM/JdKLWqShuIuf7asjpU+IdmB57SH74Dty3Di58C
GVWAB13spmdjHlhCy9QdliC8dS5Fj/4HTHt2Ga0CDFKEY4NqwHe/+Hj6dV92krbG
gZ6FQkaHwVk=
-----END CERTIFICATE-----
const path = require('path')
const Client = require('kubernetes-client').Client
const config = require('kubernetes-client').config
module.exports = function client() {
const client = {}
return async function (ctx, next) {
const exclude = ['/service/listEnvVarsNew', '/service/listEnvVars', '/cluster']
if (exclude.includes(ctx.path)) {
await next()
} else {
const cluster = ctx.request.headers.cluster
let yaml = ''
let clusterDomain = ''
switch (cluster) {
case 'qa':
yaml = './../../kubeService/kubeConfig-qa.yaml'
clusterDomain = 'liangkebang.net'
break
case 'dev-ops':
yaml = './../../kubeService/kubeConfig-dev-ops.yaml'
clusterDomain = 'liangkebang.net'
break
case 'qke':
yaml = './../../kubeService/kubeConfig-qke.yaml'
clusterDomain = 'liangkebang.com'
break
default:
ctx.body = {
code: '0001',
msg: '请选择集群的名字',
}
return
}
if (!client[cluster]) {
client[cluster] = new Client({
config: config.fromKubeconfig(
path.resolve(__dirname, yaml),
),
version: '1.10',
})
}
ctx.client = client[cluster]
ctx.cluster = cluster
ctx.clusterDomain = clusterDomain
await next()
}
}
}
This diff is collapsed.
......@@ -4,6 +4,7 @@
"description": "",
"main": "bin/www.js",
"dependencies": {
"chai": "^4.2.0",
"ioredis": "^4.10.0",
"joi": "^14.3.1",
"js-yaml": "^3.12.1",
......@@ -23,17 +24,19 @@
"eslint": "^5.15.3",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.16.0",
"mocha": "^6.2.0",
"nodemon": "^1.18.9"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"test": "mocha",
"start": "NODE_ENV=env nodemon",
"lint": "eslint .",
"fix": "eslint --fix ."
},
"pre-commit": [
"fix",
"lint"
"lint",
"test"
],
"repository": {
"type": "git",
......
const path = require('path')
const Client = require('kubernetes-client').Client
const config = require('kubernetes-client').config
module.exports = function getClient(cluster) {
let yaml = ''
switch (cluster) {
case 'qa':
yaml = './../kubeService/kubeConfig-qa.yaml'
break;
case 'dev-ops':
yaml = './../kubeService/kubeConfig-dev-ops.yaml'
break
case 'qke':
yaml = './../kubeService/kubeConfig-qke.yaml'
break
default:
throw Error('未获取到Client')
}
return new Client({
config: config.fromKubeconfig(
path.resolve(__dirname, yaml),
),
version: '1.10',
})
}
const fs = require('fs')
const ui = fs.readFileSync('serviceTemplate/ui.template.txt', 'utf8')
const java = fs.readFileSync('serviceTemplate/java.template.txt', 'utf8')
const node = fs.readFileSync('serviceTemplate/node.template.txt', 'utf8')
const rabbitmq = fs.readFileSync('serviceTemplate/rabbitmq.template.txt', 'utf8')
const zookeeper = fs.readFileSync('serviceTemplate/zookeeper.template.txt', 'utf8')
const mysql = fs.readFileSync('serviceTemplate/mysql.template.txt', 'utf8')
const redis = fs.readFileSync('serviceTemplate/redis.template.txt', 'utf8')
module.exports = {
ui,
node,
java,
rabbitmq,
zookeeper,
mysql,
redis,
}
serviceName: {{serviceName}}
namespace: {{namespace}}
replicas: 1
accessType: ClusterIP
portMappings.0.protocol: TCP
portMappings.0.lbPort: 80
portMappings.0.containerPort: 80
containers.0.containerName: {{system_name}}
containers.0.image: ccr.ccs.tencentyun.com/{{image}}
containers.0.envs.0.name: SYSTEM_NAME
containers.0.envs.0.value: {{system_name}}
containers.0.envs.1.name: NAMESPACE
containers.0.envs.1.value: {{namespace}}
containers.0.envs.2.name: DEBUG
containers.0.envs.2.value: {{debug}}
containers.0.cpu: {{resources.cpu.request}}
containers.0.cpuLimits: {{resources.cpu.limit}}
containers.0.memory: {{resources.memory.request}}
containers.0.memoryLimits: {{resources.memory.limit}}
containers.0.healthCheck.0.type: readyCheck
containers.0.healthCheck.0.checkMethod: methodCmd
containers.0.healthCheck.0.cmd: '/home/quant_group/readyCheck.sh'
containers.0.healthCheck.0.delayTime: 60
containers.0.healthCheck.0.intervalTime: 5
containers.0.healthCheck.0.timeOut: 2
containers.0.healthCheck.0.healthNum: 1
containers.0.healthCheck.0.unhealthNum: 20
serviceName: mysql
namespace: {{namespace}}
replicas: 1
accessType: NodePort
portMappings.0.protocol: TCP
portMappings.0.containerPort: 3306
containers.0.containerName: mysql
containers.0.image: ccr.ccs.tencentyun.com/{{image}}
containers.0.volumeMounts.0.volumeName: mysql-vol
containers.0.volumeMounts.0.mountPath: /var/lib/mysql
containers.0.volumeMounts.0.mode: rw
containers.0.envs.0.name: MYSQL_USER
containers.0.envs.0.value: qa
containers.0.envs.1.name: MYSQL_PASSWORD
containers.0.envs.1.value: qatest
containers.0.envs.2.name: MYSQL_ROOT_PASSWORD
containers.0.envs.2.value: qatest
containers.0.cpu: {{resources.cpu.request}}
containers.0.cpuLimits: {{resources.cpu.limit}}
containers.0.memory: {{resources.memory.request}}
containers.0.memoryLimits: {{resources.memory.limit}}
volumes.0.name: mysql-vol
volumes.0.volumeType: hostPath
volumes.0.hostPath: /var/lib/data/mysql/{{namespace}}
serviceName: {{serviceName}}
namespace: {{namespace}}
replicas: 1
accessType: ClusterIP
portMappings.0.protocol: TCP
portMappings.0.lbPort: 80
portMappings.0.containerPort: 80
containers.0.containerName: {{serviceName}}
containers.0.image: ccr.ccs.tencentyun.com/{{image}}
containers.0.envs.0.name: SYSTEM_NAME
containers.0.envs.0.value: {{serviceName}}
containers.0.envs.1.name: NAMESPACE
containers.0.envs.1.value: {{namespace}}
containers.0.envs.2.name: DEBUG
containers.0.envs.2.value: {{debug}}
containers.0.cpu: {{resources.cpu.request}}
containers.0.cpuLimits: {{resources.cpu.limit}}
containers.0.memory: {{resources.memory.request}}
containers.0.memoryLimits: {{resources.memory.limit}}
serviceName: rabbitmq
namespace: {{namespace}}
replicas: 1
accessType: NodePort
portMappings.0.protocol: TCP
portMappings.0.containerPort: 5672
portMappings.1.protocol: TCP
portMappings.1.containerPort: 15672
containers.0.containerName: rabbitmq
containers.0.image: ccr.ccs.tencentyun.com/{{image}}
containers.0.volumeMounts.0.volumeName: rabbitmq-vol
containers.0.volumeMounts.0.mountPath: /var/lib/rabbitmq
containers.0.volumeMounts.0.mode: rw
containers.0.envs.0.name: RABBITMQ_DEFAULT_USER
containers.0.envs.0.value: qa
containers.0.envs.1.name: RABBITMQ_DEFAULT_PASS
containers.0.envs.1.value: qatest
volumes.0.name: rabbitmq-vol
volumes.0.volumeType: hostPath
volumes.0.hostPath: /var/lib/data/rabbitmq/{{namespace}}
containers.0.cpu: {{resources.cpu.request}}
containers.0.cpuLimits: {{resources.cpu.limit}}
containers.0.memory: {{resources.memory.request}}
containers.0.memoryLimits: {{resources.memory.limit}}
serviceName: redis
namespace: {{namespace}}
replicas: 1
accessType: NodePort
portMappings.0.protocol: TCP
portMappings.0.containerPort: 6379
portMappings.1.protocol: TCP
portMappings.1.containerPort: 6380
portMappings.2.protocol: TCP
portMappings.2.containerPort: 6381
portMappings.3.protocol: TCP
portMappings.3.containerPort: 6382
portMappings.4.protocol: TCP
portMappings.4.containerPort: 6383
containers.0.containerName: redis
containers.0.image: ccr.ccs.tencentyun.com/{{image}}
containers.0.volumeMounts.0.volumeName: redis-vol
containers.0.volumeMounts.0.mountPath: /var/lib/redis
containers.0.volumeMounts.0.mode: rw
volumes.0.name: redis-vol
volumes.0.volumeType: hostPath
volumes.0.hostPath: /var/lib/data/redis/{{namespace}}
containers.0.cpu: {{resources.cpu.request}}
containers.0.cpuLimits: {{resources.cpu.limit}}
containers.0.memory: {{resources.memory.request}}
containers.0.memoryLimits: {{resources.memory.limit}}
serviceName: {{serviceName}}
namespace: {{namespace}}
replicas: 1
accessType: ClusterIP
portMappings.0.protocol: TCP
portMappings.0.lbPort: 80
portMappings.0.containerPort: 80
containers.0.containerName: {{system_name}}
containers.0.image: ccr.ccs.tencentyun.com/{{image}}
containers.0.envs.0.name: SYSTEM_NAME
containers.0.envs.0.value: {{system_name}}
containers.0.envs.1.name: NAMESPACE
containers.0.envs.1.value: {{namespace}}
containers.0.envs.2.name: DEBUG
containers.0.envs.2.value: {{debug}}
containers.0.cpu: {{resources.cpu.request}}
containers.0.cpuLimits: {{resources.cpu.limit}}
containers.0.memory: {{resources.memory.request}}
containers.0.memoryLimits: {{resources.memory.limit}}
serviceName: zookeeper
namespace: {{namespace}}
replicas: 1
accessType: NodePort
portMappings.0.protocol: TCP
portMappings.0.containerPort: 2181
portMappings.1.protocol: TCP
portMappings.1.containerPort: 9090
containers.0.containerName: zookeeper
containers.0.image: ccr.ccs.tencentyun.com/{{image}}
containers.0.volumeMounts.0.volumeName: zookeeper-vol
containers.0.volumeMounts.0.mountPath: /var/lib/zookeeper
containers.0.volumeMounts.0.mode: rw
containers.0.envs.0.name: ZOO_USER
containers.0.envs.0.value: zookeeper
containers.0.envs.1.name: ZOO_PORT
containers.0.envs.1.value: 2181
volumes.0.name: zookeeper-vol
volumes.0.volumeType: hostPath
volumes.0.hostPath: /var/lib/data/zookeeper/{{namespace}}
containers.0.cpu: {{resources.cpu.request}}
containers.0.cpuLimits: {{resources.cpu.limit}}
containers.0.memory: {{resources.memory.request}}
containers.0.memoryLimits: {{resources.memory.limit}}
const logger = require('koa-log4').getLogger()
const Client = require('./tke.service')
const domainConfig = require('../config')
class Cluster extends Client {
constructor() {
super();
this.endpoint = domainConfig.clusterPoint
this.imageDomain = domainConfig.imageDomain
}
namespace_get() {
return this.post('DescribeClusterNameSpaces', { clusterId: this.clusterId })
}
namespace_create(name, description) {
return this.post('CreateClusterNamespace', { name, description, clusterId: this.clusterId })
}
namespace_delete(name) {
return this.post('DeleteClusterNamespace', { 'names.0': name, clusterId: this.clusterId })
}
service_list(namespace) {
return this.post('DescribeClusterService', { namespace, clusterId: this.clusterId, allnamespace: 0 })
}
service_get(serviceName, namespace) {
return this.post('DescribeClusterServiceInfo', { serviceName, namespace, clusterId: this.clusterId })
}
async service_create(params, lable) {
logger.info('创建服务详细信息:', params, lable)
params.clusterId = this.clusterId
const str = JSON.stringify(params)
await this.post('CreateClusterService', params)
// 腾讯云api暂只能通过修改服务来改成Recreate策略
const modifyParams = JSON.parse(str)
modifyParams.strategy = 'Recreate'
await this.post('ModifyClusterService', modifyParams)
const updateLabels = {
clusterId: params.clusterId,
serviceName: params.serviceName,
namespace: params.namespace,
'labels.type': lable,
}
logger.info('修改服务标签信息:', updateLabels)
return this.post('ModifyServiceLabels', updateLabels)
}
service_delete(serviceName, namespace) {
return this.post('DeleteClusterService', { serviceName, namespace, clusterId: this.clusterId })
}
service_modifyImage(serviceName, image, namespace) {
return this.post('ModifyClusterServiceImage', {
serviceName,
namespace,
image: `${this.imageDomain}/${image}`,
clusterId: this.clusterId,
})
}
service_redeployment(serviceName, namespace) {
return this.post('RedeployClusterService', { serviceName, namespace, clusterId: this.clusterId })
}
instance_get(serviceName, namespace) {
return this.post('DescribeServiceInstance', { serviceName, namespace, clusterId: this.clusterId })
}
ingress_get(namespace) {
return this.post('DescribeIngress', { namespace, clusterId: this.clusterId })
}
ingress_create(namespace) {
logger.info()
return this.post('CreateIngress', {
ingressName: `qa-${namespace}`,
ingressDesc: '',
namespace,
clusterId: this.clusterId,
})
}
ingress_delete(ingressName, namespace) {
return this.post('DeleteIngress', { ingressName, namespace, clusterId: this.clusterId })
}
ingress_modify(ingressName, namespace, rules) {
rules.ingressName = ingressName
rules.namespace = namespace
rules.clusterId = this.clusterId
return this.post('MosifyIngress', rules)
}
node_list(limit = 1) {
return this.post('DescribeClusterInstances', { limit, clusterId: this.clusterId })
}
}
exports.create = function () {
return new Cluster()
}
const { describe, it } = require('mocha')
const { expect } = require('chai');
const request = require('request')
const app = require('./../app')
const awaitRequest = function (options) {
return new Promise((resolve, reject) => {
request(Object.assign(options, {
headers: {
'Content-Type': 'application/json;charset=utf-8',
cluster: 'qa',
},
}), (error, res) => {
if (error) {
reject(error)
} else {
resolve(res)
}
})
})
}
const namespace = 'monitor'
describe('test tke-eos api', () => {
app.start(4000)
/**
* 获取所有的namespace
*/
describe('test get all namespace URL->http://localhost:4000/namespace', () => {
it('should return all namespace', async () => {
const res = await awaitRequest({
url: 'http://localhost:4000/namespace',
method: 'GET',
})
expect(res.statusCode).to.equal(200)
});
})
/**
* 获取某个namespace下的所有服务
*/
describe(`test get service URL->http://localhost:4000/service?namespace=${namespace}`, () => {
it(`should return service belong to namespace ${namespace}`, async () => {
const res = await awaitRequest({
url: `http://localhost:4000/service?namespace=${namespace}`,
method: 'GET',
})
expect(res.statusCode).to.equal(200)
});
})
/**
* 获取镜像
*/
describe('test get images URL->http://localhost:4000/repository', () => {
const data = { namespace: 'qa-ui' }
it('should return images list', async () => {
const res = await awaitRequest({
url: 'http://localhost:4000/repository',
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify(data),
})
expect(res.statusCode).to.equal(200)
});
})
/**
* 获取镜像标签
*/
describe('test get tag URL->http://localhost:4000/tag', () => {
const data = { reponame: 'qa-ui/new-op-ui' }
it('should return images list', async () => {
const res = await awaitRequest({
url: 'http://localhost:4000/tag',
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify(data),
})
expect(res.statusCode).to.equal(200)
});
})
/**
* 创建服务
*/
describe('test create service URL->http://localhost:4000/service/create', () => {
const data = {
debug: '"0"',
domain: 'new-op',
image: 'qa-ui/new-op-ui:latest',
label: 'ui',
mock: '0',
namespace,
serviceName: 'new-op-ui',
type: 'ui',
wechat: '"0"'
,
}
it('should return create service sucess', async () => {
const res = await awaitRequest({
url: 'http://localhost:4000/service/create',
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify(data),
})
expect(res.statusCode).to.equal(200)
});
})
describe('test details of a service URL->http://localhost:4000/service/details', async () => {
const data = {
namespace,
serviceName: 'new-op-ui',
type: 'ui',
}
it('should returns details of a service', async () => {
const res = await awaitRequest({
url: 'http://localhost:4000/service/details',
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify(data),
})
expect(res.statusCode).to.equal(200)
});
})
describe('test update a service URL->http://localhost:4000/service/modifyImage', async () => {
const data = {
namespace,
serviceName: 'new-op-ui',
type: 'ui',
image: 'qa-ui/new-op-ui:latest',
}
it('should update a service', async () => {
const res = await awaitRequest({
url: 'http://localhost:4000/service/modifyImage',
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify(data),
})
expect(res.statusCode).to.equal(200)
});
})
describe('test redeploy a service URL->http://localhost:4000/service/redeploy', async () => {
it('should redeploy a service', async () => {
const data1 = {
namespace,
serviceName: 'new-op-ui',
type: 'ui',
}
const res1 = await awaitRequest({
url: 'http://localhost:4000/service/details',
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify(data1),
})
const data = {
namespace,
podName: JSON.parse(res1.body).data.podName,
serviceName: 'new-op-ui',
}
const res = await awaitRequest({
url: 'http://localhost:4000/service/redeploy',
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify(data),
})
expect(res.statusCode).to.equal(200)
});
})
describe('test delete a service URL->http://localhost:4000/service/delete', async () => {
it('should delete a service', async () => {
const data1 = {
namespace,
serviceName: 'new-op-ui',
type: 'ui',
}
const res1 = await awaitRequest({
url: 'http://localhost:4000/service/details',
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify(data1),
})
const data = {
namespace,
podName: JSON.parse(res1.body).data.podName,
serviceName: 'new-op-ui',
}
const res = await awaitRequest({
url: 'http://localhost:4000/service/delete',
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8',
},
body: JSON.stringify(data),
})
expect(res.statusCode).to.equal(200)
});
})
})
......@@ -52,6 +52,10 @@ spec:
env:
- name: NAMESPACE
value: {{namespace}}
- name: NAMESPACECLUSTER
value: {{namespace}}@{{cluster}}
- name: CLUSTER
value: {{cluster}}
- name: SYSTEM_NAME
value: {{serviceName}}
- name: DEBUG
......
......@@ -35,8 +35,6 @@ spec:
qcloud-app: {{serviceName}}
type: base
spec:
nodeSelector:
zone: "3"
containers:
- image: ccr.ccs.tencentyun.com/{{image}}
imagePullPolicy: Always
......
......@@ -9,7 +9,7 @@ metadata:
spec:
type: NodePort
ports:
- port: 3306
- port: 3306
selector:
qcloud-app: {{serviceName}}
......@@ -53,44 +53,44 @@ spec:
type: base
spec:
containers:
- image: ccr.ccs.tencentyun.com/{{image}}
imagePullPolicy: Always
name: {{serviceName}}
resources:
requests:
cpu: {{resources.cpu.request}}m
memory: {{resources.memory.request}}Mi
limits:
cpu: {{resources.cpu.limit}}m
memory: {{resources.memory.limit}}Mi
env:
- name: MYSQL_DATABASE
value: db
- name: MYSQL_PASSWORD
value: qatest
- name: MYSQL_ROOT_PASSWORD
value: Quantgroup2017
- name: MYSQL_USER
value: qa
readinessProbe:
exec:
command: ["mysql", "-uqa", "-pqatest", "-e", "SELECT 1"]
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 1
failureThreshold: 40
volumeMounts:
- mountPath: "/var/lib/mysql"
name: {{serviceName}}
nodeSelector:
zone: "3"
- image: ccr.ccs.tencentyun.com/{{image}}
imagePullPolicy: Always
name: {{serviceName}}
resources:
requests:
cpu: {{resources.cpu.request}}m
memory: {{resources.memory.request}}Mi
limits:
cpu: {{resources.cpu.limit}}m
memory: {{resources.memory.limit}}Mi
env:
- name: MYSQL_DATABASE
value: db
- name: MYSQL_PASSWORD
value: qatest
- name: MYSQL_ROOT_PASSWORD
value: Quantgroup2017
- name: MYSQL_USER
value: qa
readinessProbe:
exec:
command: ["mysql", "-uqa", "-pqatest", "-e", "SELECT 1"]
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 1
failureThreshold: 40
volumeMounts:
- mountPath: "/var/lib/mysql"
name: {{serviceName}}
# nodeSelector:
# zone: "3"
volumes:
- name: {{serviceName}}
persistentVolumeClaim:
claimName: {{serviceName}}-{{namespace}}
imagePullSecrets:
- name: qcloudregistrykey
- name: tencenthubkey
- name: qcloudregistrykey
- name: tencenthubkey
restartPolicy: Always
terminationGracePeriodSeconds: 30
status: {}
......@@ -37,8 +37,6 @@ spec:
spec:
# hostname固定,容器重置后数据持久化才能正常
hostname: {{serviceName}}-{{namespace}}
nodeSelector:
zone: "3"
containers:
- image: ccr.ccs.tencentyun.com/{{image}}
imagePullPolicy: IfNotPresent
......
......@@ -20,8 +20,6 @@ spec:
type: base
qcloud-app: {{serviceName}}
spec:
nodeSelector:
zone: "3"
containers:
- image: ccr.ccs.tencentyun.com/{{image}}
imagePullPolicy: Always
......
This diff is collapsed.
# 现在eos的pvc声明显示依赖cbs
# 优化后,会使用默认的sc
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"storageclass.beta.kubernetes.io/is-default-class":"true","storageclass.kubesphere.io/supported_access_modes":"[\"ReadWriteOnce\"]"},"name":"csi-qingcloud"},"parameters":{"fsType":"ext4","maxSize":"1000","minSize":"10","stepSize":"10","type":"0"},"provisioner":"csi-qingcloud","reclaimPolicy":"Delete"}
storageclass.beta.kubernetes.io/is-default-class: "false"
storageclass.kubesphere.io/supported_access_modes: '["ReadWriteOnce"]'
name: cbs-3
selfLink: /apis/storage.k8s.io/v1/storageclasses/csi-qingcloud
parameters:
fsType: ext4
maxSize: "1000"
minSize: "10"
stepSize: "10"
type: "0"
provisioner: csi-qingcloud
reclaimPolicy: Delete
volumeBindingMode: Immediate
\ No newline at end of file
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment