Skip to content

Commit

Permalink
完成集群安装后,更新容器引擎的参数
Browse files Browse the repository at this point in the history
  • Loading branch information
shaohq committed Feb 27, 2022
1 parent 371a3d5 commit a589208
Show file tree
Hide file tree
Showing 6 changed files with 337 additions and 3 deletions.
4 changes: 2 additions & 2 deletions docs/support/change-log/v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ docker run -d \
* 可以在升级节点前手工排空节点
* 可以在升级节点后恢复节点调度
* 手动更新证书
* 完成集群安装后,更新容器引擎的参数

**优化**
* 集群状态显示列表中,展示节点是否处于暂停调度的状态
Expand All @@ -60,8 +61,7 @@ docker run -d \
* 不允许删除最后一个控制节点或最后一个 ETCD 节点

**问题解决**
* 不能添加 insecure registry 的问题【containerd】
* 不能添加 insecure registry 的问题【docker 待解决】
* containerd 容器引擎不能添加 insecure registry 的问题

## v1.0.0-beta.2

Expand Down
74 changes: 74 additions & 0 deletions server/api/cluster/operation/sync_container_engine_params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package operation

import (
"net/http"

"github.com/eip-work/kuboard-spray/api/cluster/cluster_common"
"github.com/eip-work/kuboard-spray/api/command"
"github.com/eip-work/kuboard-spray/common"
"github.com/gin-gonic/gin"
)

func SyncContainerEngineParams(c *gin.Context) {

var req OperationCommonRequest
c.ShouldBindUri(&req)
c.ShouldBindJSON(&req)

req.Operation = "sync_container_engine_params"

inventory, resourcePackage, err := updateResourcePackageVarsToInventory(req)
if err != nil {
common.HandleError(c, http.StatusInternalServerError, "failed to process inventory", err)
return
}

postExec := func(status command.ExecuteExitStatus) (string, error) {

var message string
if status.Success {
message += "\n"
message = "\033[32m[ " + "Finished synchronizing container engine params. ]\033[0m \n"
message += "\033[32m[ 成功同步容器引擎参数 ]\033[0m \n"
} else {
message += "\n"
message = "\033[31m\033[01m\033[05m[ Failed to synchronize container engine params. ]\033[0m \n"
message += "\033[31m\033[01m\033[05m[ 同步容器引擎参数失败. ]\033[0m \n"
}

return "\n" + message, nil
}

playbook := common.MapGet(resourcePackage, "data.supported_playbooks."+req.Operation).(string)

cmd := command.Execute{
OwnerType: "cluster",
OwnerName: req.Cluster,
Cmd: "ansible-playbook",
Args: func(execute_dir string) []string {
result := []string{
playbook,
"-i", execute_dir + "/inventory.yaml",
}
result = appendCommonParams(result, req, false)
return result
},
Dir: cluster_common.ResourcePackageDirForInventory(inventory),
Type: req.Operation,
PreExec: func(execute_dir string) error { return common.SaveYamlFile(execute_dir+"/inventory.yaml", inventory) },
PostExec: postExec,
}

if err := cmd.Exec(); err != nil {
common.HandleError(c, http.StatusInternalServerError, "Faild to drain node. ", err)
return
}

c.JSON(http.StatusOK, gin.H{
"code": http.StatusOK,
"message": "success",
"data": gin.H{
"pid": cmd.R_Pid,
},
})
}
1 change: 1 addition & 0 deletions server/kuboard-spray.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func setupRouter() *gin.Engine {
api.POST("/clusters/:cluster/drain_node", operation.DrainNode)
api.POST("/clusters/:cluster/uncordon_node", operation.UncordonNode)
api.POST("/clusters/:cluster/renew_cert", operation.RenewCert)
api.POST("/clusters/:cluster/sync_container_engine_params", operation.SyncContainerEngineParams)

api.POST("/clusters/:cluster/cis_scan", cis_scan.CisScan)

Expand Down
1 change: 1 addition & 0 deletions web/src/components/fields/FieldSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
{{ compute_display_value }}
</span>
<span v-else class="field_placeholder">{{ compute_placeholder }}</span>
<slot name="view_append"></slot>
</slot>
</template>
</FieldCommon>
Expand Down
10 changes: 9 additions & 1 deletion web/src/views/clusters/plan/global/ContainerManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ zh:
<ConfigSection v-if="cluster && cluster.resourcePackage" v-model:enabled="enabled" :label="$t('label')" :description="$t('description')" disabled anti-freeze>
<FieldSelect :holder="vars" fieldName="container_manager"
:prop="prop" required :loadOptions="loadContainerEngines" :disabled="cluster.resourcePackage === undefined">
<template #view_append>
<span style="float: right;">
<ContainerMangerSyncParamsTask v-if="isClusterOnline && cluster.resourcePackage.data.supported_playbooks.sync_container_engine_params"
:cluster="cluster"></ContainerMangerSyncParamsTask>
</span>
</template>
</FieldSelect>
<template v-if="vars.container_manager === 'containerd'">
<FieldBool :holder="vars" :prop="prop" fieldName="containerd_use_systemd_cgroup" disabled></FieldBool>
Expand All @@ -41,6 +47,7 @@ zh:

<script>
import ContainerMangerIrCnd from './ContainerMangerIrCnd.vue'
import ContainerMangerSyncParamsTask from './ContainerMangerSyncParamsTask.vue'
export default {
props: {
Expand All @@ -53,6 +60,7 @@ export default {
],
}
},
inject: ['isClusterOnline'],
computed: {
enabled: {
get () {return true},
Expand All @@ -64,7 +72,7 @@ export default {
},
prop() { return 'all.children.target.vars' }
},
components: { ContainerMangerIrCnd },
components: { ContainerMangerIrCnd, ContainerMangerSyncParamsTask },
mounted () {
},
methods: {
Expand Down
250 changes: 250 additions & 0 deletions web/src/views/clusters/plan/global/ContainerMangerSyncParamsTask.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
<i18n>
en:
verbose: Include task params
verbose_:
verbose_v: May include sensitive data in the trace, e.g. path to files, user name, password.
verbose_vvv: includes more information in log, only used in development.
v_: Normal
v_v: Details
v_vvv: More
control_params: Control params
operation: Operations
offlineNodes: Offline nodes
offlineNodesDesc: Exclude the following offline nodes.

sync_params: Sync params to container engine
sync_params_desc: Sync params to container engine.
sync_params_desc_2: Will result in outage of service for several minutes.
desc_label: Task description
zh:
verbose: 显示任务参数
verbose_: 正常输出的日志,通常选用此选项
verbose_v: 日志中会包含部分敏感信息,例如:文件路径、用户名密码等
verbose_vvv: 日志中会包含最详细的信息,通常只在开发阶段使用
v_: 正常
v_v: 详细
v_vvv: 更多
control_params: 控制选项
operation: 操作选项
offlineNodes: 离线节点
offlineNodesDesc: 排除以下不在线的节点:

sync_params: 更新容器引擎参数
sync_params_desc: 将更新所有节点的容器引擎参数,并重启容器引擎。
sync_params_desc_2: 将会导致服务短暂不可用,请认真考虑后再执行操作。
desc_label: 任务描述
</i18n>

<template>
<ExecuteTask :history="cluster.history" :loading="loading" :title="title" :startTask="execute"
placement="top-end" @refresh="$emit('refresh')" @visibleChange="onVisibleChange">
<div style="width: 450px;">
<div v-if="pingpong_loading" style="display: block;">
<el-skeleton animated></el-skeleton>
</div>
<el-form v-else ref="form" :model="form" @submit.prevent.stop label-position="left" label-width="120px" class="app_form_mini">
<el-form-item :label="$t('verbose')">
<el-radio-group v-model="form.verbose">
<el-radio-button label="">{{$t('v_')}}</el-radio-button>
<el-radio-button label="v">{{$t('v_v')}}</el-radio-button>
<el-radio-button label="vvv">{{$t('v_vvv')}}</el-radio-button>
</el-radio-group>
<div style="color: #aaa; font-size: 12px;">{{$t('verbose_' + form.verbose)}}</div>
</el-form-item>

<el-form-item :label="$t('offlineNodes')" class="app_margin_top" v-if="offlineNodes.length > 0">
<div class="form_description">{{ $t('offlineNodesDesc') }}</div>
<template v-for="node in offlineNodes" :key="'exclude' + node">
<el-tooltip class="box-item" effect="dark" :content="pingpong[node].message" placement="top-end">
<el-tag type="danger" effect="dark" style="margin: 0 10px 10px 0;">
<span class="app_text_mono" style="font-size: 14px; margin-right: 10px;">{{ node }}</span>
<el-icon :size="14" style="width: 14px; height: 14px; vertical-align: top;">
<el-icon-question-filled></el-icon-question-filled>
</el-icon>
</el-tag>
</el-tooltip>
</template>
</el-form-item>

<el-form-item :label="$t('desc_label')">
<span style="font-weight: bold;">{{ $t('sync_params_desc') }}</span>
</el-form-item>

<el-form-item v-if="cluster && cluster.resourcePackage && cluster.resourcePackage.data.supported_playbooks['sync_container_engine_params'] === undefined" prop="min_resource_package_version"
style="margin-top: -10px;"
:rules="min_resource_package_version_rules">
</el-form-item>
</el-form>
</div>
</ExecuteTask>
</template>

<script>
import ExecuteTask from '../../../common/task/ExecuteTask.vue'
function trimMark(str) {
if (str[str.length - 1] === ',') {
return str.slice(0, str.length - 1)
}
return str
}
export default {
props: {
cluster: { type: Object, required: true },
},
data() {
return {
loading: false,
form: {
verbose: '',
fork: 5,
action: 'upgrade_master_nodes',
min_resource_package_version: '',
nodes_to_exclude: [],
kube_nodes_to_upgrade: [],
skip_downloads: false,
drain_node: {
},
},
min_resource_package_version_rules: [
{ required: true, message: this.$t('newResourcePackageRequired') }
],
kube_nodes_to_upgrade_rules: [
{ required: true, message: this.$t('upgrade_multi_nodes_desc') }
],
pingpong: {},
pingpong_loading: true,
pods_on_node: undefined,
}
},
computed: {
title () {
return this.$t('sync_params')
},
requireSeparateDownloadAction () {
for (let hostName in this.cluster.inventory.all.children.target.children.k8s_cluster.children.kube_control_plane.hosts) {
let host = this.cluster.inventory.all.hosts[hostName]
if (host.kuboardspray_require_download) {
return true
}
}
return false
},
offlineNodes () {
let result = []
for (let key in this.cluster.inventory.all.hosts) {
if (this.pingpong[key] && this.pingpong[key].ping !== 'pong') {
result.push(key)
}
}
return result
},
},
components: { ExecuteTask },
mounted () {
this.setAction()
},
methods: {
requireDownloadForNodes(nodes) {
for (let node of nodes) {
if (this.cluster.inventory.all.hosts[node].kuboardspray_require_download) {
return true
}
}
return false
},
onVisibleChange(flag) {
if (flag) {
this.setAction()
this.form.kube_nodes_to_upgrade = []
this.testPingPong('target')
if (this.nodeName) {
this.getPodsOnNode()
}
}
},
setAction () {
},
testPingPong (nodes) {
this.pingpong = {}
this.pingpong_loading = true
let req = { nodes: nodes }
this.kuboardSprayApi.post(`/clusters/${this.cluster.name}/state/ping`, req).then(resp => {
this.pingpong = resp.data.data.items
this.pingpong_loading = false
}).catch(e => {
if (e.response && e.response.data) {
this.$message.error('不能测试节点是否在线: ' + e.response.data.message)
} else {
this.$message.error('不能测试节点是否在线: ' + e)
}
})
},
getPodsOnNode () {
this.pods_on_node = undefined
this.kuboardSprayApi.get(`/clusters/${this.cluster.name}/state/pods_on_node/${this.nodeName}`).then(resp => {
this.pods_on_node = resp.data.data.stdout
})
},
async execute () {
if (this.pingpong_loading) {
this.$message.error('Wait ..')
return
}
return new Promise((resolve, reject) => {
this.$refs.form.validate(flag => {
if (flag) {
let req = {
verbose: this.form.verbose,
fork: 1,
}
{ // 排除节点
let temp = ''
let excludes = {}
for (let node of this.offlineNodes) {
excludes[node] = true
}
for (let node in excludes) {
temp += '!' + node + ','
}
req.nodes_to_exclude = trimMark(temp)
}
this.kuboardSprayApi.post(`/clusters/${this.cluster.name}/sync_container_engine_params`, req).then(resp => {
let pid = resp.data.data.pid
resolve(pid)
}).catch(e => {
reject(e.response.data.message)
})
} else {
reject('请验证表单')
}
})
})
},
}
}
</script>

<style scoped lang="css">
.form_description {
font-size: 12px;
color: #aaa;
max-width: 700px;
}
.pods_on_node {
background-color: var(--el-text-color-primary);
color: var(--el-color-white);
padding: 20px;
margin: 0;
min-height: 200px;
}
.drain_cmd {
background-color: var(--el-text-color-primary);
color: var(--el-color-white);
padding: 10px 20px;
font-weight: bold;
}
</style>

0 comments on commit a589208

Please sign in to comment.