[GKE] Deploymentのreplicasを0にしてGoアプリにOSシグナル SIGTERM を通知
お題
表題の通り。
実際の動きとしてそうなることを確認したかっただけ。
前提
- GCP環境は用意済み。
- GCPローカル設定済み。(
gcloud
コマンドが使用できる状態になっている。)
-
kubectl
コマンドが使用できる状態になっている。
- GKEクラスタ作成済み。
開発環境
# OS - Linux(Ubuntu)
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.5 LTS (Bionic Beaver)"
# gcloud
$ gcloud version
Google Cloud SDK 312.0.0
# 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
実践
ソース一式
gcloud
コマンドが使用できる状態になっている。)kubectl
コマンドが使用できる状態になっている。# OS - Linux(Ubuntu)
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.5 LTS (Bionic Beaver)"
# gcloud
$ gcloud version
Google Cloud SDK 312.0.0
# 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
実践
ソース一式
ソース
Golang
適当にWebサーバを立てておいて、OSシグナル(SIGTERM
)を受信したらログ(GOT_NOTIFY
)を吐く。
defer
でもログを仕込んでおいて、OSシグナル受信時に、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イメージはContainer Registryを使う。
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'
上記を使ってビルドする用のシェルは下記。
#!/usr/bin/env bash
set -euox pipefail
SCRIPT_DIR=$(dirname "$0")
cd "${SCRIPT_DIR}"
gcloud builds submit --config cloudbuild.yaml .
デプロイ設定
Container RegistryからDockerイメージを取得する。
Podは3つ。
コンテナポートは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
上記を使ってデプロイするシェルは下記。
自分が使っているGCPプロジェクトのIDが必要で、それ自体はローカル環境でgcloud
コマンドから拾えるのだけど、
k8sのYamlに直接書かずにGCPプロジェクトIDを指定する方法(※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イメージを作成してContainer Registryに格納)
$ ./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が3つ。
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
golang-app-try01 3/3 3 3 4m19s
この時点でコンテナログを見ると、3つのPodそれぞれで、アプリ起動時とOSシグナル待受開始のログが出ていることがわかる。
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
でなく、OSシグナル(SIGTERM
)受信用の Goroutine を別途立てて対応。
Author And Source
この問題について([GKE] Deploymentのreplicasを0にしてGoアプリにOSシグナル SIGTERM を通知), 我々は、より多くの情報をここで見つけました https://qiita.com/sky0621/items/03e0580caddd02b547ab著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .