将[GKE] Deployment的replicas设置为0,以便向Go应用程序发送操作系统信号SIGTERM通知
请在中文中进行释义,并仅提供一种选项:
题目
只是想确认一下实际情况会如此。
前提这个词的含义是指某件事情发生的前提条件,只提供一个选项如下:
条件
-
- GCP環境は用意済み。
-
- GCPローカル設定済み。(gcloudコマンドが使用できる状態になっている。)
kubectlコマンドが使用できる状態になっている。
GKEクラスタ作成済み。
开发环境
操作系统- Linux(Ubuntu)
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.5 LTS (Bionic Beaver)"
谷歌云 (GCP)
$ gcloud version
Google Cloud SDK 312.0.0
# kubectl –> kubectl命令
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.9", GitCommit:"4fb7ed12476d57b8437ada90b4f93b17ffaeed99", GitTreeState:"clean", BuildDate:"2020-07-15T16:18:16Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"17+", GitVersion:"v1.17.12-gke.2502", GitCommit:"974eff7a63e05b7eb05c9aded92fae8a3ce14521", GitTreeState:"clean", BuildDate:"2020-10-19T17:01:32Z", GoVersion:"go1.13.15b4", Compiler:"gc", Platform:"linux/amd64"}
后台
# 语言 – Golang
$ go version
go version go1.15.2 linux/amd64
实践
源码完整套装
请将以下内容用中文进行翻译,只需要一个选项:源。
Go语言
随便搭建一个Web服务器,在接收到操作系统信号(SIGTERM)时输出日志(GOT_NOTIFY)。
也可以在defer中设置日志,并在接收到操作系统信号时确认defer日志不会被输出。
package main
import (
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
)
func main() {
fmt.Println("APP_START")
defer fmt.Println("DEFER")
// OSシグナル(SIGTERM)の受信を待ち受ける Goroutine
go func() {
fmt.Println("BEFORE_NOTIFY")
q := make(chan os.Signal, 1)
signal.Notify(q, syscall.SIGTERM)
<-q
fmt.Println("GOT_NOTIFY")
os.Exit(-1)
}()
// 適当にHTTPサーバーを立ち上げておく
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if _, err := fmt.Fprint(w, "Hello"); err != nil {
fmt.Printf("HANDLE_ERROR_OCCURRED: %+v", err)
}
})
if err := http.ListenAndServe(":8080", nil); err != nil {
fmt.Printf("SERVE_ERROR_OCCURRED: %+v", err)
}
fmt.Println("APP_END")
}
Dockerfile: 原生的,只需要一个选项进行中文释义:配套文件。
一份普通无特色的多阶段构建的Dockerfile。
FROM golang:1.15 as builder
WORKDIR /app
COPY . .
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux go build -mod=readonly -v -o server
FROM gcr.io/distroless/base
COPY --from=builder /app/server /server
CMD ["/server"]
Cloud Build的配置
使用容器注册表来管理Docker镜像。
steps:
- name: 'gcr.io/cloud-builders/docker'
args: [ 'build', '-t', 'gcr.io/$PROJECT_ID/golang-app-try01', '.' ]
images:
- 'gcr.io/$PROJECT_ID/golang-app-try01'
以下是用于构建上述内容的shell脚本。
#!/usr/bin/env bash
set -euox pipefail
SCRIPT_DIR=$(dirname "$0")
cd "${SCRIPT_DIR}"
gcloud builds submit --config cloudbuild.yaml .
部署设置
从容器注册表中获取Docker镜像。
有三个Pod。
容器端口为8080(虽然这次用不到)。
apiVersion: apps/v1
kind: Deployment
metadata:
name: golang-app-try01
spec:
replicas: 3
selector:
matchLabels:
app: golang-app-try01
template:
metadata:
labels:
app: golang-app-try01
spec:
containers:
- name: golang-app-try01
image: gcr.io/MY_GCP_PROJECT_ID/golang-app-try01
ports:
- containerPort: 8080
使用以下Shell进行部署:
根据上述,需要使用我正在使用的GCP项目的ID,该ID可以从本地环境的gcloud命令中获取,但是查询如何指定GCP项目ID而不直接在k8s的Yaml文件中写入(可能可以通过ConfigMap或Secret来实现,但最好是简便方式)很麻烦,所以使用sed来替换。
#!/usr/bin/env bash
set -euox pipefail
SCRIPT_DIR=$(dirname "$0")
cd "${SCRIPT_DIR}"
project=$(gcloud config get-value project)
if [[ -z "${project}" ]]; then
echo -n "need project"
exit 1
fi
echo "${project}"
sed -i -e "s/MY_GCP_PROJECT_ID/${project}/" deployment.yaml
kubectl apply -f deployment.yaml
sed -i -e "s/${project}/MY_GCP_PROJECT_ID/" deployment.yaml
用的脚本来修改 Pod 数
#!/usr/bin/env bash
set -euox pipefail
SCRIPT_DIR=$(dirname "$0")
cd "${SCRIPT_DIR}"
num=${1:-}
if [ -z "${num}" ]; then
echo -n "input replicas number: "
read num
fi
kubectl scale deployment golang-app-try01 --replicas="${num}"
确认动作
构建应用程序(创建Docker镜像并存储到容器注册表)
$ ./build.sh
++ dirname ./build.sh
+ SCRIPT_DIR=.
+ echo .
.
+ cd .
+ gcloud builds submit --config cloudbuild.yaml .
Creating temporary tarball archive of 6 file(s) totalling 1.7 KiB before compression.
・
・
・
DONE
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ID CREATE_TIME DURATION SOURCE IMAGES STATUS
6452c516-cfbf-4497-b536-378023cbc34d 2020-11-03T19:29:14+00:00 29S gs://XXXXXXXX_cloudbuild/source/1604431752.38075-ccb069fbb0d0413382dc79d42e5c618a.tgz gcr.io/XXXXXXXX/golang-app-try01 (+1 more) SUCCESS
部署到GKE
$ ./deploy.sh
++ dirname ./deploy.sh
+ SCRIPT_DIR=.
+ echo .
.
+ cd .
・
・
・
+ kubectl apply -f deployment.yaml
deployment.apps/golang-app-try01 created
・
・
・
有三个Pod。
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
golang-app-try01 3/3 3 3 4m19s
当查看容器日志时,可以发现每个Pod上都有应用程序启动和操作系统信号等待启动的日志。
将Pod数更改为0
$ ./replica_n.sh 0
++ dirname ./replica_n.sh
+ SCRIPT_DIR=.
+ echo .
.
+ cd .
+ num=0
+ '[' -z 0 ']'
+ kubectl scale deployment golang-app-try01 --replicas=0
deployment.apps/golang-app-try01 scaled
当接收到OS信号时的日志(GOT_NOTIFY)在各个Pod的日志中显示。
使用了defer命令的日志(DEFER)不会显示。
总结
如果要在GKE上使用,那么希望在应用停止时确保处理的内容不应该是使用defer,而应该另外创建一个用于接收操作系统信号(SIGTERM)的Goroutine来进行处理。