feat: Create load balancer with health check. (#10419)

Refs https://github.com/1Panel-dev/1Panel/issues/10412
This commit is contained in:
CityFun 2025-09-19 18:18:04 +08:00 committed by GitHub
parent 7ed81996b8
commit da75f050e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 74 additions and 15 deletions

View file

@ -3074,6 +3074,10 @@ func (w WebsiteService) UpdateLoadBalance(req request.WebsiteLBUpdate) error {
if !fileOp.Stat(filePath) {
return nil
}
oldContent, err := fileOp.GetContent(filePath)
if err != nil {
return err
}
parser, err := parser.NewParser(filePath)
if err != nil {
return err
@ -3125,10 +3129,7 @@ func (w WebsiteService) UpdateLoadBalance(req request.WebsiteLBUpdate) error {
if err = nginx.WriteConfig(config, nginx.IndentedStyle); err != nil {
return buserr.WithErr("ErrUpdateBuWebsite", err)
}
if err = opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil {
return err
}
return nil
return nginxCheckAndReload(string(oldContent), filePath, nginxInstall.ContainerName)
}
func (w WebsiteService) DeleteLoadBalance(req request.WebsiteLBDelete) error {

View file

@ -2531,6 +2531,8 @@ const message = {
strategy: 'Strategy',
strategyDown: 'Down',
strategyBackup: 'Backup',
ipHashBackupErr: 'IP hash does not support backup nodes',
staticChangePHPHelper: 'Currently a static website, you can switch to a PHP website',
proxyCache: 'Reverse Proxy Cache',
cacheLimit: 'Cache Space Limit',

View file

@ -2507,6 +2507,8 @@ const message = {
strategy: 'Estrategia',
strategyDown: 'Baja',
strategyBackup: 'Backup',
ipHashBackupErr: 'IP hash does not support backup nodes',
staticChangePHPHelper: 'Actualmente es un sitio estático, puedes cambiarlo a PHP',
proxyCache: 'Caché de proxy inverso',
cacheLimit: 'Límite de espacio de caché',

View file

@ -2447,6 +2447,8 @@ const message = {
strategy: '戦略',
strategyDown: '無効',
strategyBackup: 'バックアップ',
ipHashBackupErr: 'IPハッシュはバックアップードをサポートしていません',
staticChangePHPHelper: '現在は静的ウェブサイトですがPHPウェブサイトに切り替えることができます',
proxyCache: 'リバースプロキシキャッシュ',
cacheLimit: 'キャッシュスペース制限',

View file

@ -2405,6 +2405,8 @@ const message = {
strategy: '전략',
strategyDown: '비활성화',
strategyBackup: '백업',
ipHashBackupErr: 'IP 해시는 백업 노드를 지원하지 않습니다',
staticChangePHPHelper: '현재 정적 웹사이트이며 PHP 웹사이트로 전환할 있습니다.',
proxyCache: '리버스 프록시 캐시',
cacheLimit: '캐시 공간 제한',

View file

@ -2502,6 +2502,8 @@ const message = {
strategy: 'Strategi',
strategyDown: 'Lumpuh',
strategyBackup: 'Sandaran',
ipHashBackupErr: 'Hash IP tidak menyokong nod sandaran',
staticChangePHPHelper: 'Kini laman web statik, boleh ditukar ke laman web PHP.',
proxyCache: 'Cache Proksi Terbalik',
cacheLimit: 'Had Ruang Cache',

View file

@ -2503,6 +2503,8 @@ const message = {
strategy: 'Estratégia',
strategyDown: 'Desativar',
strategyBackup: 'Backup',
ipHashBackupErr: 'Hash IP não suporta nós de backup',
staticChangePHPHelper: 'Atualmente um site estático, pode ser alterado para um site PHP.',
proxyCache: 'Cache de Proxy Reverso',
cacheLimit: 'Limite de Espaço de Cache',

View file

@ -2500,6 +2500,8 @@ const message = {
strategy: 'Стратегия',
strategyDown: 'Отключить',
strategyBackup: 'Резервный',
ipHashBackupErr: 'IP хэш не поддерживает резервные узлы',
staticChangePHPHelper: 'В настоящее время статический сайт, можно переключить на PHP сайт.',
proxyCache: 'Кэш Обратного Прокси',
cacheLimit: 'Ограничение Пространства Кэша',

View file

@ -2559,6 +2559,8 @@ const message = {
strategy: 'Strateji',
strategyDown: 'Kapalı',
strategyBackup: 'Yedek',
ipHashBackupErr: 'IP 哈希不支持备用节点',
staticChangePHPHelper: 'Şu anda statik bir web sitesi, PHP web sitesine geçiş yapabilirsiniz',
proxyCache: 'Ters Vekil Önbelleği',
cacheLimit: 'Önbellek Alanı Sınırı',

View file

@ -2358,6 +2358,8 @@ const message = {
strategy: '策略',
strategyDown: '停用',
strategyBackup: '備用',
ipHashBackupErr: 'IP 哈希不支援備用節點',
staticChangePHPHelper: '目前為靜態網站可切換為 PHP 網站',
proxyCache: '反向代理快取',
cacheLimit: '快取空間限制',

View file

@ -2350,6 +2350,8 @@ const message = {
strategy: '策略',
strategyDown: '停用',
strategyBackup: '备用',
ipHashBackupErr: 'IP 哈希不支持备用节点',
staticChangePHPHelper: '当前为静态网站可以切换为 PHP 网站',
proxyCache: '反代缓存',
cacheLimit: '缓存空间限制',

View file

@ -7,7 +7,7 @@
</el-button>
<el-alert :closable="false" class="!mt-2">
<template #default>
<span style="white-space: pre-line">{{ $t('website.loadBalanceHelper') }}</span>
<span class="whitespace-pre-line">{{ $t('website.loadBalanceHelper') }}</span>
</template>
</el-alert>
</template>
@ -39,7 +39,15 @@
<el-tag>{{ $t('website.maxConns') }}: {{ item.maxConns }}</el-tag>
</td>
<td v-if="item.flag != ''">
<el-tag type="info">{{ $t('website.strategy') }}: {{ item.flag }}</el-tag>
<el-tag type="info">
{{ $t('website.strategy') }}:
<span v-if="item.flag === 'backup'">
{{ $t('website.strategyBackup') }}
</span>
<span v-if="item.flag === 'down'">
{{ $t('website.strategyDown') }}
</span>
</el-tag>
</td>
</tr>
</table>

View file

@ -110,7 +110,7 @@ import { createLoadBalance, updateLoadBalance } from '@/api/modules/website';
import i18n from '@/lang';
import { FormInstance } from 'element-plus';
import { ref } from 'vue';
import { MsgSuccess } from '@/utils/message';
import { MsgError, MsgSuccess } from '@/utils/message';
import { Rules, checkNumberRange } from '@/global/form-rules';
import { getAlgorithms, getStatusStrategy } from '@/global/mimetype';
import { Website } from '@/api/interface/website';
@ -204,23 +204,51 @@ const acceptParams = async (req: LoadBalanceOperate) => {
open.value = true;
};
const handleServers = () => {
for (const server of item.value.servers) {
if (!server.weight || server.weight == '') {
server.weight = 0;
}
if (!server.maxFails || server.maxFails == '') {
server.maxFails = 0;
}
if (!server.maxConns || server.maxConns == '') {
server.maxConns = 0;
}
}
};
const rollBackServers = () => {
for (const server of item.value.servers) {
if (server.weight == 0) {
server.weight = undefined;
}
if (server.maxFails == 0) {
server.maxFails = undefined;
}
if (server.maxConns == 0) {
server.maxConns = undefined;
}
}
};
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate(async (valid) => {
if (!valid) {
return;
}
let checkBackup = false;
if (item.value.algorithm == 'ip_hash') {
checkBackup = true;
}
for (const server of item.value.servers) {
if (!server.weight || server.weight == '') {
server.weight = 0;
}
if (!server.maxFails || server.maxFails == '') {
server.maxFails = 0;
}
if (!server.maxConns || server.maxConns == '') {
server.maxConns = 0;
if (checkBackup && server.flag == 'backup') {
MsgError(i18n.global.t('website.ipHashBackupErr'));
return;
}
}
handleServers();
loading.value = true;
try {
if (item.value.operate === 'edit') {
@ -231,6 +259,8 @@ const submit = async (formEl: FormInstance | undefined) => {
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
}
handleClose();
} catch {
rollBackServers();
} finally {
loading.value = false;
}