测试环境使用的jenkins
正式环境使用的gitlab-ci
测试环境
- 创建yaml文件
apiVersion: v1
kind: ConfigMap
metadata:name: dtk-go-tiktok-admin-configlabels:app.kubernetes.io/name: dtk-go-tiktok-adminapp.kubernetes.io/business: infrastructureapp.kubernetes.io/runtime: golangapp.kubernetes.io/tier: backendapp.kubernetes.io/environment: testapp.kubernetes.io/managed-by: yong.xd
data:config.yaml: |max-age: 0show-line: truelog-in-console: true---
apiVersion: v1
kind: ConfigMap
metadata:name: dtk-vue-tiktok-admin-configlabels:app.kubernetes.io/name: dtk-go-tiktok-adminapp.kubernetes.io/business: infrastructureapp.kubernetes.io/runtime: golangapp.kubernetes.io/tier: frontapp.kubernetes.io/environment: testapp.kubernetes.io/managed-by: yong.xd
data:default.conf: |server{listen 80 default_server;server_name _;access_log /dev/stdout;error_log /dev/stdout;root /opt/app/dist/;location / {try_files $uri $uri/ /index.html;}location /api {proxy_set_header Host $http_host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header X-Forwarded-Proto $scheme;rewrite ^/api/(.*)$ /$1 break; #重写add_header 'dtk-debug' 'api';#一个deployment2个pod是网络资源是共享的,所以可以直接代理proxy_pass http://127.0.0.1:8888; # 设置代理服务器的协议和地址}location /api/swagger/index.html {proxy_pass http://127.0.0.1:8888/swagger/index.html;}location /health {access_log off;return 200;}}---
apiVersion: apps/v1
kind: Deployment
metadata:name: dtk-go-tiktok-adminlabels:app.kubernetes.io/name: dtk-go-tiktok-adminapp.kubernetes.io/business: infrastructureapp.kubernetes.io/runtime: golangapp.kubernetes.io/tier: backendapp.kubernetes.io/environment: testapp.kubernetes.io/managed-by: yong.xd
spec:replicas: 1selector:matchLabels:app.kubernetes.io/name: dtk-go-tiktok-adminapp.kubernetes.io/business: infrastructureapp.kubernetes.io/runtime: golangapp.kubernetes.io/tier: backendapp.kubernetes.io/environment: testapp.kubernetes.io/managed-by: yong.xdtemplate:metadata:labels:app.kubernetes.io/name: dtk-go-tiktok-adminapp.kubernetes.io/business: infrastructureapp.kubernetes.io/runtime: golangapp.kubernetes.io/tier: backendapp.kubernetes.io/environment: testapp.kubernetes.io/managed-by: yong.xdspec:imagePullSecrets:- name: aliyun-regcredserviceAccountName: defaultsecurityContext:dnsPolicy: NonednsConfig:nameservers:- 172.31.74.196searches:- test1.svc.cluster.local- svc.cluster.local- cluster.localcontainers:- name: golangsecurityContext:runAsUser: 0image: "registry.buydance.com/dataoke-test/dtk-go-tiktok-admin-golang:latest"volumeMounts:- name: configmountPath: /opt/app/conf/ports:- name: httpcontainerPort: 8888protocol: TCPlivenessProbe:httpGet:path: /healthport: 8888initialDelaySeconds: 5periodSeconds: 20timeoutSeconds: 3readinessProbe:httpGet:path: /healthport: 8888initialDelaySeconds: 5periodSeconds: 10timeoutSeconds: 3resources:requests:cpu: 1mmemory: 20Mi- name: nginxsecurityContext:image: "registry.buydance.com/dataoke-test/dtk-go-tiktok-admin-nginx:latest"volumeMounts:- name: ng-configmountPath: /etc/nginx/conf.d/ports:- name: httpcontainerPort: 80protocol: TCPlivenessProbe:httpGet:path: /healthport: 80periodSeconds: 5readinessProbe:httpGet:path: /healthport: 80periodSeconds: 5resources:requests:cpu: 1mmemory: 64Milimits:cpu: 200mmemory: 256Mivolumes:- name: ng-configconfigMap:name: dtk-vue-tiktok-admin-config- name: configconfigMap:name: dtk-go-tiktok-admin-config
---
apiVersion: v1
kind: Service
metadata:name: dtk-go-tiktok-admin
spec:ports:- port: 80targetPort: 80protocol: TCPname: nginx- port: 8888targetPort: 8888protocol: TCPname: golangselector:app.kubernetes.io/name: dtk-go-tiktok-adminapp.kubernetes.io/business: infrastructureapp.kubernetes.io/runtime: golangapp.kubernetes.io/tier: backendapp.kubernetes.io/environment: testapp.kubernetes.io/managed-by: yong.xd
- 启动服务
kubectl apply -f ./ -n test1
- 配置jenkins
#!/usr/bin/env groovy
import groovy.json.JsonOutputString gitRepositryURL = 'https://test.com/dtk-go-tiktok-admin.git'
String dockerRegistry = 'test.com'
String dockerRegistryURL = 'https://test.com'
String dockerRegistryNameSpace = 'dataoke-test'
#dockerfile路径
String kubeManifestsRepo = '/home/jenkins/repo/dtk-kubernetes-test/app'
Map dockerFiles = ["nginx":"Dockerfile.test.nginx", "golang":"Dockerfile.test.golang"]
Map dockerImages = [:]
boolean notify = falseString jobBaseName = env.JOB_NAME[4..-1]
String jobK8sName = jobBaseName.replaceAll('_', "-") Map commitInfo = [:]
Map buildInfo = [:]commitInfo.projectName = gitRepositryURL.replaceFirst(/^.*\/([^\/]+?).git$/, '$1')
commitInfo.gitRepositryURL = gitRepositryURL
buildInfo.buildId = currentBuild.id@NonCPS
def newSh(String cmd) {def script = '#!/bin/sh +x\n' << cmdresult = sh(returnStdout: true, script: script.toString())return result
}pipeline {agent anyoptions {buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '10', numToKeepStr: '10')}parameters {choice( name: 'PENV',choices: ['dev1','dev2','test1','test2', 'test3', 'test4','test5','test6','test7','huise',
'huise4','huise3'], description: '选择发布环境,默认发布至dev1测试环境')gitParameter(name: 'GIT_BRANCH', type: 'PT_BRANCH_TAG',branchFilter: 'origin/(.*)',defaultValue: 'master',selectedValue: 'DEFAULT',sortMode: 'DESCENDING_SMART',quickFilterEnabled: true, description: 'Select your branch or tag.')booleanParam(name: 'force', defaultValue: false, description: '代码重复强制发版')}stages {stage('预处理') {steps {script {def now = new Date()buildInfo.buildDate = now.format("yy-MM-dd HH:mm", TimeZone.getTimeZone('UTC'))def causes = currentBuild.getBuildCauses()buildInfo.buildUser = causes[0]['userName']userList = readYaml(file:'/etc/jenkins/users.yaml')if (!(PENV in userList.env.dev ) && !(buildInfo.buildUser in userList.user.allow)) {error(message: "开发只能发布环境到${userList.env.dev.join(',')}") }buildInfo.gitBranch = GIT_BRANCHbuildInfo.publishEnv = PENVcurrentBuild.description = "k8s环境: ${PENV} 构建人:${buildInfo.buildUser} 分支: ${GIT_BRANCH}"newSh("check.py -u ${buildInfo.buildUser} -e ${PENV}")}}}stage('同步代码仓库') {steps {script {def scmVars = checkout([$class: 'GitSCM', branches: [[name: "${GIT_BRANCH}"]], extensions: [[$class: 'CheckoutOption', timeout: 20], [$class: 'CloneOption', depth: 1]], userRemoteConfigs: [[credentialsId: "5411496d-3606-4855-ab9c-2e4453cd2880", url: "${gitRepositryURL}"]]])commitInfo.gitCommit = scmVars.GIT_COMMITcommitInfo.gitBranch = GIT_BRANCHcommitInfo.xiangmu_name = env.JOB_BASE_NAMEcommitInfo.commitDate = newSh('git log --pretty=format:"%ci" -1')commitInfo.cmmitMessage = newSh('git log --pretty=format:"%s" -1')String gitDiff = newSh('git diff HEAD^ HEAD')def committer = [:]committer.name = newSh('git log --pretty=format:"%cn" -1')committer.email = newSh('git log --pretty=format:"%ce" -1')commitInfo.committer = committerString consoleStdout = "\n\n---------SYNCHRONIZE GIT REPOSITORY---------\nGit repo sync successfully.\n\n" + JsonOutput.prettyPrint(JsonOutput.toJson(commitInfo))println(consoleStdout)consoleStdout = "\n\n---------CHANGE LOGS---------\nGit diff:\n\n" + gitDiffprintln(consoleStdout)getDatabaseConnection(type: 'GLOBAL') {def sqlString="select commit_seccec from jenkins_commit.jenkins_jilu where xm_name = ? and env = ?"def params=[commitInfo.xiangmu_name,PENV]def rest_null = sql sql:sqlString,parameters:paramsif (rest_null.size() == 0){def sqlString2="insert into jenkins_commit.jenkins_jilu(xm_name,env,commit_seccec) values(?,?,?)"def params2=[commitInfo.xiangmu_name,PENV,commitInfo.gitCommit]sql sql:sqlString2,parameters:params2println("mysql插入数据")}if (rest_null.size() >= 1) {def Map rest = rest_null.get(0)if (rest.get("commit_seccec") == commitInfo.gitCommit && force == "false"){currentBuild.description = "k8s环境: ${PENV} 构建人:${buildInfo.buildUser} 分支: ${GIT_BRANCH} 构建失败: 代码重复不发版"println("代码没有变化不做发版")error "上一个版本和现在正在发的版本一致,不做发版"}}}}}}stage('构建') { agent {docker { image 'registry.buydance.com/dataoke-test/golang:1.19'args '--user root -v /data/jenkins_build_cache/.cache:/.cache'args '--user root -v /data/lib/go:/go'reuseNode true} }steps {script {env.STAGE = "goujian"def now = new Date()String buildDate = now.format("yy-MM-dd HH:mm", TimeZone.getTimeZone('UTC'))sh (script: '#!/bin/sh +x\n' + '''cd ./servermkdir -p .cacheexport GO111MODULE=onexport GOPROXY=https://goproxy.cn,directexport GOPRIVATE="gitlab.buydance.com/*"export CGO_ENABLED=0go mod tidygo build -o main''')def files = findFiles(glob: '**/main')String artifactPath = files[0].pathString sha1Checksum = sha1(file: artifactPath)String sha256Checksum = sha256(file: artifactPath)consoleStdout = "\n\n---------BUILD RESULTS---------\nBuild info generated successfully.\n\n" + JsonOutput.prettyPrint(JsonOutput.toJson(buildInfo))println(consoleStdout)} }}stage('构建vue') { agent {docker { image 'node:14.19.3-alpine3.15'args '--user root -v /data/jenkins_build_cache/.cache:${HOME}/.cache'args '--user root -v /data/lib/composer:/root/.composer'reuseNode true} }steps {script {sh """cd ./webnpm config set puppeteer_download_host=https://npm.taobao.org/mirrorsnpm i --registry=https://registry.npm.taobao.orgnpm run build"""consoleStdout = "\n\n---------BUILD RESULTS---------\nBuild info generated successfully.\n\n" + JsonOutput.prettyPrint(JsonOutput.toJson(buildInfo))println(consoleStdout)} }}stage('Docker') {steps {script {env.STAGE = "DOCKERFIEL"// docker.withRegistry(dockerRegistryURL, '8f1a40fa-3258-4717-825c-a9f87299916d') {docker.withRegistry(dockerRegistryURL) {String dockerFile = kubeManifestsRepo + '/' + jobBaseName + '/' + 'Dockerfile.test.nginx'if (!fileExists(dockerFile)) {dockerFile = kubeManifestsRepo + '/' + jobK8sName + '/' + 'Dockerfile.test.nginx'}String dockerRepository = dockerRegistry + '/' + dockerRegistryNameSpace + '/' + jobK8sName + '-' + 'nginx'def customImage = docker.build(dockerRepository, "-f ${dockerFile} .")customImage.push(commitInfo.gitCommit)customImage.push('latest')dockerImages.nginx = dockerRepository + ':' + commitInfo.gitCommitdockerFile = kubeManifestsRepo + '/' + jobBaseName + '/' + 'Dockerfile.test.golang'if (!fileExists(dockerFile)) {dockerFile = kubeManifestsRepo + '/' + jobK8sName + '/' + 'Dockerfile.test.golang'}dockerRepository = dockerRegistry + '/' + dockerRegistryNameSpace + '/' + jobK8sName + '-' + 'golang'customImage = docker.build(dockerRepository, "-f ${dockerFile} .")customImage.push(commitInfo.gitCommit)customImage.push('latest')dockerImages."golang" = dockerRepository + ':' + commitInfo.gitCommit}}}}stage('部署') {steps {script {env.STAGE = "bushu"dockerImages.each { k, v -> newSh("kubectl -n ${PENV} set image deployment/${jobK8sName} ${k}=${v}")newSh("kubectl -n ${PENV} rollout status deployment/${jobK8sName} --timeout=2m")}String content = "![screenshot](https://comquent.de/wp-content/uploads/CQ-Pipeline-Kurs.png)\n\n### Jenkins Pipeline\n>**构建信息**:\n>- 构建项目: ${jobBaseName}\n>- 构建id: ${currentBuild.number}\n>- 构建人: ${buildInfo.buildUser}\n>- 构建分支: ${GIT_BRANCH}\n>- 发布环境: ${PENV}\n>**版本信息**:\n>- commit_hash: ${commitInfo.gitCommit}\n>- commit_date: ${commitInfo.commitDate}\n>- commit_message: ${commitInfo.cmmitMessage}\n>- committer: ${commitInfo.committer.name}" def workflowMessage = ["msgtype": "actionCard","actionCard":["title":"构建信息","text":content,"btnOrientation": "0","btns": [["title": "详细信息","actionURL": "https://k8sjenkins.haojiequ.com/blue/organizations/jenkins/k8s_dtk_go_app_api/detail/k8s_dtk_go_app_api/${currentBuild.number}/pipeline"],["title": "日志监控","actionURL": "http://k8skibana.haou.com/app/kibana#/discover?_g=()&_a=(columns:!(_source),index:'18d51920-96c3-11eb-811f-1383c86a1d0',interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))"],]]] String workflowMessageJSON = JsonOutput.toJson(workflowMessage)timeout(unit: 'SECONDS', time: 30) {newSh("curl 'https://oapi.dingtalk.com/robot/send?access_token=9e91f6860736ff69e7f6f986179e154e497b70e26fd749c' -s -H 'Content-Type: application/json' -d '${workflowMessageJSON}'")}}}}stage("commit入库"){steps {script{getDatabaseConnection(type: 'GLOBAL') {def sqlString3="update jenkins_commit.jenkins_jilu set commit_seccec=? where xm_name=? and env=?;"def params3=[commitInfo.gitCommit,commitInfo.xiangmu_name,PENV]sql sql:sqlString3,parameters:params3}}}}}post {failure {script{if (STAGE == "goujian") {println("----> goujian失败")}if (STAGE == "bushu") {println("----> bushu失败") }if (STAGE == "DOCKERFIEL") {println("----> DOCKERFIEL失败")}}}}
}
- 配置nginx
upstream dtk-vue-tiktok-admin {server dtk-go-tiktok-admin weight=1 max_fails=0 fail_timeout=0s;keepalive 20;
}server {
listen 80;
listen 443 ssl;
server_name test.com;
access_log /var/log/nginx/dtest.com.access.log json;
error_log /var/log/nginx/dtest.com.error.log;
ssl_certificate conf.d/dtkcert/test.com.pem;
ssl_certificate_key conf.d/dtkcert/test.com.key;
more_set_headers 'Access-Control-Allow-Headers: Cookie,DNT,X-CSRF-Token,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,Auth-token';
more_set_headers 'Access-Control-Allow-Origin: *';
more_set_headers 'Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT';
more_set_headers 'Access-Control-Allow-Credentials: true';
default_type 'text/html';
set $backend 'dtk-vue-tiktok-admin';include public/deny.conf;
location / {proxy_pass http://$backend;
}location ~ /\.ht {deny all;
}
}
- jenkins机器上的dockerfile
[root@k8s-jenkins dtk-go-tiktok-admin]# cat Dockerfile.test.golang
FROM test.com/dataoke-test/alpine:3.12-CST as test
WORKDIR /opt/app
COPY $CI_PROJECT_DIR/server/main /opt/app/main
CMD ["/opt/app/main", "-c", "/opt/app/conf/config.yaml"][root@k8s-jenkins dtk-go-tiktok-admin]# cat Dockerfile.test.nginx
FROM test.com/dataoke-test/openresty:base
WORKDIR /opt/app/dist/
COPY --chown=nobody:nobody web/dist /opt/app/dist
线上环境配置
- yaml其它都一样除了svc,因为svc需要绑定slb地址
apiVersion: v1
kind: Service
metadata:name: dtk-go-tiktok-adminannotations:#开启slb使用service.beta.kubernetes.io/alibaba-cloud-loadbalancer-force-override-listeners: "true"#slb地址service.beta.kubernetes.io/alibaba-cloud-loadbalancer-id: lb-2ze1hpcomc# service.beta.kubernetes.io/alibaba-cloud-loadbalancer-protocol-port: "http:9090"service.beta.kubernetes.io/alibaba-cloud-loadbalancer-scheduler: "wrr"namespace: default
spec:type: LoadBalancerexternalTrafficPolicy: Localports:- port: 16107 #slb端口targetPort: 80 #pod服务端口protocol: TCPselector:app.kubernetes.io/name: dtk-go-tiktok-adminapp.kubernetes.io/business: infrastructureapp.kubernetes.io/runtime: golangapp.kubernetes.io/tier: backendapp.kubernetes.io/environment: prodapp.kubernetes.io/managed-by: yong.xd
- 启动
cat .gitlab-ci.ymlstages:- build- package- docker- deploy- notifybuild:stage: buildimage: test.com/dataoke-prod/golang:1.19-with-repo-certcache:key:files:- go.modpaths:- .cache/pkgartifacts:expire_in: 20 minsuntracked: falsepaths:- $CI_PROJECT_DIR/server/mainscript:- mkdir -p .cache- cd ./server - export GOPATH="$CI_PROJECT_DIR/.cache"- go env -w GO111MODULE=on - go env -w GOPROXY=https://goproxy.cn,direct - go env -w GOPRIVATE=gitlab.buydance.com - go env -w CGO_ENABLED=0- go build -o mainonly:- tagspackage:stage: packageimage: test.com/dataoke-prod/node:14.19.3-alpine3.15script:- cd ./web- npm config set puppeteer_download_host=https://npm.taobao.org/mirrors- npm i --registry=https://registry.npm.taobao.org- npm run buildcache:key:files:- package.jsonpaths:- node_modulesartifacts:name: "dist"untracked: falseexpire_in: 5 minspaths:- $CI_PROJECT_DIR/web/distonly:- tagsdocker:stage: dockerimage: test.com/dataoke-prod/kaniko-executor:debugscript:- mkdir -p /kaniko/.docker- echo "${DOCKER_AUTH_CONFIG}" > /kaniko/.docker/config.json- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile.prod.nginx --destination test.aliyuncs.com/dataoke-prod/`echo ${CI_PROJECT_NAME} |sed 's@_@-@g'`-nginx:${CI_COMMIT_SHORT_SHA} --destination test.aliyuncs.com/dataoke-prod/`echo ${CI_PROJECT_NAME} |sed 's@_@-@g'`-nginx:latest- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile.prod.golang --destination test.aliyuncs.com/dataoke-prod/`echo ${CI_PROJECT_NAME} |sed 's@_@-@g'`-golang:${CI_COMMIT_SHORT_SHA} --destination test.aliyuncs.com/dataoke-prod/`echo ${CI_PROJECT_NAME} |sed 's@_@-@g'`-golang:latestonly:- tagsdeploy:stage: deployimage: test.com/dataoke-prod/kubectl:1.18.1variables:GIT_STRATEGY: noneK8S_NAME_SPACE: defaultscript:- mkdir -p $HOME/.kube- echo "$KUBERNETES_SECRET" >> "$HOME/.kube/config"- kubectl version- kubectl get deployments.apps -n ${K8S_NAME_SPACE}- kubectl -n ${K8S_NAME_SPACE} set image deployment/`echo ${CI_PROJECT_NAME} |sed 's@_@-@g'` nginx=test.aliyuncs.com/dataoke-prod/`echo ${CI_PROJECT_NAME} |sed 's@_@-@g'`-nginx:${CI_COMMIT_SHORT_SHA} golang=test.aliyuncs.com/dataoke-prod/`echo ${CI_PROJECT_NAME} |sed 's@_@-@g'`-golang:${CI_COMMIT_SHORT_SHA} --record- kubectl rollout status deployment/`echo ${CI_PROJECT_NAME} |sed 's@_@-@g'`only:- tagsnotifyFailWeChat:stage: notifyimage: test.aliyuncs.com/dataoke-prod/curl-image:v1script:- curl 'https://oapi.dingtalk.com/robot/send?access_token=6147ec1eb7d8b9bd5cd1b15f1c' -H 'Content-Type:application/json' -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$CI_PROJECT_NAME项目构建失败\n>本次构建由:$GITLAB_USER_NAME 触发\n>项目名称:$CI_PROJECT_NAME\n>提交号:$CI_COMMIT_SHA\n>提交日志:$CI_COMMIT_MESSAGE\n>构建分支:$CI_COMMIT_BRANCH\n>流水线地址:[$CI_PIPELINE_URL]($CI_PIPELINE_URL)\"}}"only:- tagswhen: on_failure# 构建成功时的通知消息
notifySuccessWeChat:stage: notifyimage: test.aliyuncs.com/dataoke-prod/curl-image:v1script:- curl 'https://oapi.dingtalk.com/robot/send?access_token=d6147ec1eb7d8b9bd5cd1b15f1c' -H 'Content-Type:application/json' -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$CI_PROJECT_NAME项目构建成功\n>本次构建由:$GITLAB_USER_NAME 触发\n>项目名称:$CI_PROJECT_NAME\n>提交号:$CI_COMMIT_SHA\n>提交日志:$CI_COMMIT_MESSAGE\n>构建分支:$CI_COMMIT_BRANCH\n>流水线地址:[$CI_PIPELINE_URL]($CI_PIPELINE_URL)\"}}"only:- tagswhen: on_success
- 配置dockerfile
cat Dockerfile.prod.golangFROM test.aliyuncs.com/dataoke-prod/alpine:3.12-CST as prod
WORKDIR /opt/app
COPY $CI_PROJECT_DIR/server/main /opt/app/main
CMD ["/opt/app/main", "-c", "/opt/app/conf/config.yaml"]cat Dockerfile.prod.nginxFROM test.aliyuncs.com/dataoke-prod/openresty:base
WORKDIR /opt/app/dist/
COPY --chown=nobody:nobody web/dist /opt/app/dist
- 配置nginx
upstream dtk-go-tiktok-admin {
#svc内网ip
server 192.168.10.123:16107 weight=1 max_fails=0 fail_timeout=0s;
keepalive 20;
}server {
listen 80;
listen 443 ssl;
server_name test.com;
access_log /var/log/nginx/dtest.com.access.log json;
error_log /var/log/nginx/dtest.com.error.log;
ssl_certificate conf.d/dtkcert/test.com.pem;
ssl_certificate_key conf.d/dtkcert/test.com.key;more_set_headers 'Access-Control-Allow-Headers: Cookie,DNT,X-CSRF-Token,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,Auth-token';
more_set_headers 'Access-Control-Allow-Origin: *';
more_set_headers 'Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT';
more_set_headers 'Access-Control-Allow-Credentials: true';
default_type 'text/html';
set $backend 'dtk-vue-tiktok-admin';include public/deny.conf;
location / {
proxy_pass http://$backend;
}location ~ /\.ht {
deny all;
}
}
- 结果图
原文