大人気K8Sシリーズ。 今回でなんと第4回目 まずは、過去の振り返りをやっていきましょう。
振り返り
過去の奮闘記録。 構築日記その1
構築日記その3 第1回目と第二回目はインフラ構築を頑張っていた感じです。引っ越しとかあって、K8Sの勉強は飛び飛びになっている感じでね。 そして、第3回にして、やっと色々とインフラ構築をスムーズにできて、さまざまな概念についても理解できてきた、という感じです。 はい、サービスね。これが大事な概念だった。POD間で通信をするためには、Serviceが必要だったのではないか?とね。ServiceでPODをcoreDNSに登録することができる。 そして、基本的に一つのサービス内での通信はしない。サービス間の通信はあるが。なので、docker composeのようにはいかないという話だな。
そして、今回の第4回目では、K8Sの使い方をもっと詳しく理解していきましょう、という回になっています。 具体的には、「つくって、こわして、直して学ぶkubernetes入門」という本に従って色々と進めていきたいと思っています。はい。 そして、第5回目に、私が開発した競馬システムをついにデプロイする、という感じに持っていきたいと思っています。よろしくお願いします。
まずは、harborにログインしておく
の前にロボットアカウントを作らないといけない感じ?
docker login 100.64.1.61:20080 -u username -p password server
nerdctlを使っているので、buildを使うためにbuildkitをインストールする必要がある。
しかも、buildkitdというのを走らせておく必要がある。
テストでgoで簡易サーバを作って走らせる
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/",func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,"Hellow, wordl!")
})
log.Println("starting server on port 8880")
err := http.ListenAndServe(":8880", nil)
if err != nil {
log.Fatal(err)
}
}
dockerfile
FROM golang:1.23 AS builder
WORKDIR /app
COPY . .
ENV CGO_ENABLED=0
RUN go mod tidy
RUN go build -o hello .
FROM scratch
COPY --from=builder /app/hello /hello
ENTRYPOINT ["/hello"]
go.modを入れるのを忘れずに。 go mod init github.com/hogehoge とかで問題ない。
harborにpushする
sudo docker tag hello-server:1.0 harbor.ingenboy.com/your-project/hello-server:1.0
sudo docker login harbor.ingenboy.com
sudo docker push harbor.ingenboy.com/test/hello-server:1.0
INFO[0000] pushing as a reduced-platform image (application/vnd.docker.distribution.manifest.v2+json, sha256:829e18e060ed137fd22e9a9342c0f8e8a53ff5c5103e551d116020c8611c6ce5)
manifest-sha256:829e18e060ed137fd22e9a9342c0f8e8a53ff5c5103e551d116020c8611c6ce5: waiting |--------------------------------------|
config-sha256:e2c92b75c6c63eb9edff9a6e8f3caa832b476d763a424c68ceb3a6e7d8d66b5a: done |++++++++++++++++++++++++++++++++++++++|
elapsed: 0.4 s total: 598.0 (1.5 KiB/s)
FATA[0000] failed to copy: unexpected status from PUT request to https://harbor.ingenboy.com/v2/test/hello-server/blobs/uploads/c93fd610-3616-4e4a-85f6-eec987460bf0?_state=2_rJUktfTkKy8Ursd7Da3_Weiu7UzcSi_Q5vuLtFvOB7Ik5hbWUiOiJ0ZXN0L2hlbGxvLXNlcnZlciIsIlVVSUQiOiJjOTNmZDYxMC0zNjE2LTRlNGEtODVmNi1lZWM5ODc0NjBiZjAiLCJPZmZzZXQiOjAsIlN0YXJ0ZWRBdCI6IjIwMjUtMDEtMTRUMTM6MzM6MTYuOTU1MTI1MDcxWiJ9&digest=sha256%3A424ca45be863e2033647e5e492f2f820172e0a376b2dafef7d47083702bd5a78: 413 Request Entity Too Large
request entity too largeのエラー。 これはnginxの設定のせいらしい。何を変えればいいかというと、以下です。
server {
listen 443 ssl;
server_name harbor.ingenboy.com;
ssl_certificate /etc/letsencrypt/live/harbor.ingenboy.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/harbor.ingenboy.com/privkey.pem; # managed by Certbot
client_max_body_size 1024M;
location / {
proxy_pass http://100.64.1.61:20080;
proxy_set_header Host harbor.ingenboy.com;
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;
}
}
これで問題ないです。
再度upload
ray@delta:~|⇒ sudo docker push harbor.ingenboy.com/test/hello-server:1.0
INFO[0000] pushing as a reduced-platform image (application/vnd.docker.distribution.manifest.v2+json, sha256:829e18e060ed137fd22e9a9342c0f8e8a53ff5c5103e551d116020c8611c6ce5)
manifest-sha256:829e18e060ed137fd22e9a9342c0f8e8a53ff5c5103e551d116020c8611c6ce5: done |++++++++++++++++++++++++++++++++++++++|
config-sha256:e2c92b75c6c63eb9edff9a6e8f3caa832b476d763a424c68ceb3a6e7d8d66b5a: done |++++++++++++++++++++++++++++++++++++++|
elapsed: 1.8 s
harborを見るとちゃんとuploadされています。素晴らしい!
work dir
/home/ray/k8s_manifests/tukutte_kowasite_naosite
です。
namespace
kubectl create namespace tkn
作って、壊して、直してのtkn:チキン。
4章:とりあえず作ってみる
以下のnginxを立てる
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.25.3
ports:
- containerPort: 80
デプロイ
kubectl -n tkn apply -f ./nginx.yam
確認
kubectl get po -n tkn
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 115s
もう一発
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
app: myapp
spec:
containers:
- name: hello-server
image: blux2/hello-server:1.0
ports:
- containerPort: 8080
デプロイ
kubectl apply -f ./myapp.yaml -n tkn
確認
ray@master:~/k8s_manifests/tukutte_kowasite_naosite/chapter-04 $ kubectl get po -n tkn
NAME READY STATUS RESTARTS AGE
myapp 1/1 Running 0 79s
nginx 1/1 Running 0 4m25s
いい感じ。
特定のpodの中に入る方法
kubectl exec -it -n <namespace> <pod-name> -- /bin/sh
今回でいうと以下
ray@master:~/k8s_manifests/tukutte_kowasite_naosite/chapter-04 $ kubectl exec -it -n tkn nginx -- /bin/bash
root@nginx:/#
素晴らしいね。
deleteはこうやる(pod単体)
ray@master:~/k8s_manifests/tukutte_kowasite_naosite/chapter-04 $ kubectl get po -n tkn
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 32m
ray@master:~/k8s_manifests/tukutte_kowasite_naosite/chapter-04 $ kubectl delete po nginx -n tkn
pod "nginx" deleted
ray@master:~/k8s_manifests/tukutte_kowasite_naosite/chapter-04 $ kubectl get po -n tkn
No resources found in tkn namespace.
マニフェストファイルごとはこう
kubectl delete -f <manifest-file>
5章:トラブルシューティング
kubectl get po で得られるポッドのステータス
Pending:準備中 Running : 実行中 ほかにも囲炉裏おあるけど、 ErrImagePull: イメージのプルに失敗 OOMKilled : コンテナが、out of memoryで終了。リソースを増やそう!
-o wideは覚えておこう
どのノードで動いているかがわかりますよ
ray@master:~/k8s_manifests/tukutte_kowasite_naosite/chapter-04 $ kubectl get po -n tkn -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp 1/1 Running 0 11m 10.244.3.12 zeta <none> <none>
nginx 1/1 Running 0 14m 10.244.2.15 phi <none> <none>
-o yamlもある
yaml形式でリソースを取得することができる。
kubectl describe pod -n
ray@master:~/k8s_manifests/tukutte_kowasite_naosite/chapter-04 $ kubectl describe pod myapp -n tkn
Name: myapp
Namespace: tkn
Priority: 0
Service Account: default
Node: zeta/192.168.3.5
Start Time: Thu, 16 Jan 2025 13:51:50 +0000
Labels: app=myapp
Annotations: <none>
Status: Running
IP: 10.244.3.12
IPs:
IP: 10.244.3.12
Containers:
hello-server:
Container ID: containerd://715c1f7dc367ca604acdcf2dc2ce4889aed247e946cef3e5278194cb1ff556d7
Image: blux2/hello-server:1.0
Image ID: docker.io/blux2/hello-server@sha256:35ab584cbe96a15ad1fb6212824b3220935d6ac9d25b3703ba259973fac5697d
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Thu, 16 Jan 2025 13:51:56 +0000
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-d9mmt (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-d9mmt:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 16m default-scheduler Successfully assigned tkn/myapp to zeta
Normal Pulling 16m kubelet Pulling image "blux2/hello-server:1.0"
Normal Pulled 16m kubelet Successfully pulled image "blux2/hello-server:1.0" in 4.947s (4.947s including waiting)
Normal Created 16m kubelet Created container hello-server
Normal Started 16m kubelet Started container hello-server
podのipにアクセスしてみると、こんな感じ
ray@master:~/k8s_manifests/tukutte_kowasite_naosite/chapter-04 $ curl 10.244.3.12:8080
Hello, world!
ログを見る方法
ray@master:~/k8s_manifests/tukutte_kowasite_naosite/chapter-04 $ kubectl logs myapp -n tkn -f
2025/01/16 13:51:56 Starting server on port 8080
便利ですねーーー -f をつけると、tailの-fみたいに、どんどん流れてくる感じになります。素晴らしい。
6章 DeploymentとReplicaset
ここからですよ。ここから!
ReplicasetはPodの複製を作ります。
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: httpserver
labels:
app: httpserver
spec:
replicas: 3
selector:
matchLabels:
app: httpserver
template:
metadata:
labels:
app: httpserver
spec:
containers:
- name: nginx
image: nginx:1.25.3
こんな感じですね
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-06 $ kubectl get po -n tkn
NAME READY STATUS RESTARTS AGE
httpserver-dwnql 1/1 Running 0 39s
httpserver-lckqz 1/1 Running 0 39s
httpserver-tcfqt 1/1 Running 0 39s
replicasetは実はあまり使わない
という話らしい。実際は、deploymentをめっちゃ使うという話です。 ReplicasetとDeploymentのdiffは Deploymentを使うとローリングアップデートができる、という点です。 Replicasetではできません。覚えておいてください。 というか、Repilicasetは「全く同じポッドの集合」という概念みたいなものです。 deploymentを使ってポッドをupdateするときは、二つのreplicasetができます。 そんなに難しくないですね。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.24.0
ports:
- containerPort: 80
Deploymentに紐づくPodのログを取得する方法
kubectl logs deploy/<Deployment名>
今回の例でいうと
kubectl logs deploy/nginx-deployment -n tkn
結果はこんな感じになります。
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-06 $ kubectl logs deploy/nginx-
deployment -n tkn
Found 3 pods, using pod/nginx-deployment-595dff4799-8zrm2
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2025/01/17 14:19:58 [notice] 1#1: using the "epoll" event method
2025/01/17 14:19:58 [notice] 1#1: nginx/1.24.0
2025/01/17 14:19:58 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6)
2025/01/17 14:19:58 [notice] 1#1: OS: Linux 5.15.0-1066-raspi
2025/01/17 14:19:58 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2025/01/17 14:19:58 [notice] 1#1: start worker processes
2025/01/17 14:19:58 [notice] 1#1: start worker process 29
2025/01/17 14:19:58 [notice] 1#1: start worker process 30
2025/01/17 14:19:58 [notice] 1#1: start worker process 31
2025/01/17 14:19:58 [notice] 1#1: start worker process 32
DeploymentとReplicaset,Podに関する僕の誤解を解く
まず、Deployment一つにつき、Replicasetが一つ紐づけられます。(というのも正確ではない) Deploymentを一つ作ると、自動的にReplicasetが一つできます。 そして、Replicaが9つある、という状態は、Podが9つある状態です。 updateの時は、deployment内に、新しい状態のポッドのレプリカセットと古い状態のポッドのレプリカセットの二つができます。
chatgpt先生に聞いた答えはこちら
k8sでdeployment内のpodをローリングアップデートするときは、
deployment内に、新しい状態のポッドのレプリカセットと古い状態のポッドのレプリカセットの二つができる、という認識で会っていますか?
はい、その認識で正しいです。Kubernetes(k8s)でDeploymentをローリングアップデートする際には、古いバージョンのポッドのReplicaSetと新しいバージョンのポッドのReplicaSetが同時に存在することがあります。アップデートプロセス中、新しいReplicaSetのポッドが徐々に増加し、古いReplicaSetのポッドが徐々に減少していきます。これにより、サービスの中断を最小限に抑えながらアップデートが行われます。各ステップで指定された数のポッドが常に稼働していることを保証し、新旧のバージョンが一定の割合で共存する状態を作り出します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.24.0
ports:
- containerPort: 80
こちらのファイルでは、Podが9つあります。それを管理しているのは1つのdeploymentで、1つのReplicasetです。 OKですか?? で、ローリングアップデートするときは、各Podに対して一つ一つ実行されていく感じでございます。 よろしくお願いします。ということですね。
strategy typeについて
PODのUpdateをするときにローリングアップデートをするか、recreate updateをするかで選べる 以下はrecreate updateの例
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 10
strategy:
type: Recreate
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.24.0
ports:
- containerPort: 80
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]
kubectl get po -n tkn –watch で反映の様子を見る
全断されていた。なるほどな。
rolling updateについて
まあ、これは順番にアップデートしていくやつですね。 で、全体の内のどれくらいのPODを止めてもいいかという指標がmaxUnavailable で、どれくらいの勢いで増やしていいかが、maxSurge
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 10%
maxSurge: 10%
replicas: 10
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25.3
ports:
- containerPort: 80
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]
これは、maxSurgeが10%、maxUnavailableが10%なので、10個のPodに対して、 1個ずつrollingUpdateしていることになりますね。
ちょうだいじ。サービスについて。
サービスは、PODやdeploymentを外部にサービスとして公開するためのネットワークルーティング 設定のようなものです。 書き方はこうです
apiVersion: v1
kind: Service
metadata:
name: hello-server-service
spec:
selector:
app: hello-server
ports:
- protocol: TCP
port: 8080
targetPort: 8080
ここでい一番大事だと個人的に思っているのは、spec.selector.app です。ここで、どのPODやdeploymentがこのサービスに紐づくかが決まるのです。
対応するデプロイメントがこちらです。
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-server
labels:
app: hello-server
spec:
replicas: 3
selector:
matchLabels:
app: hello-server
template:
metadata:
labels:
app: hello-server
spec:
containers:
- name: hello-server
image: blux2/hello-server:1.0
ports:
- containerPort: 8080
metadata.lables.app でつけた名前のリソースをサービスのspec.selector.appで指定します。 これがガチで大事だと思います。
DNS問い合わせでserviceにアクセスする方法
これまたおもろいですわ。 すごいです。 サービスのドメイン名は以下の規則でcoreDNSに登録されます。
<service名>.<Namespace名>.svc.cluster.local
という感じですね。
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-06 $ kubectl exec -it -n tkn ubuntu-bash-deployment-cd49b75dd-nrl6x -- /bin/bash
実際がこちらです。 適当に作ったubuntuのコンテナに入って
root@ubuntu-bash-deployment-cd49b75dd-nrl6x:/# curl hello-server-service.tkn.svc.c
luster.local:8080
serviceのtypeについて
デフォルトではClusterIpです。これは、クラスタ内部からアクセスできるserviceです。 実際はingressを作って外部とやり取りをします。
後はNodePortもあります。これは、どのクラスタでも指定したIPでそのサービスにアクセスできるという、 穴だらけな感じのサービスです。
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-06 $ cat service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: hello-server-external
spec:
type: NodePort
selector:
app: hello-server
ports:
- port: 8080
targetPort: 8080
nodePort: 30599
外からアクセスしてみよう
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-06 $ curl zeta.ingenboy.local:30
599
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-06 $ chaptphi.ingenboy.local:3059b
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-06 $ curl phi.ingenboy.local:30599
Hello, world!ray@master:~/k8s_manifests/bbf-kubernetes/chapter-06 $
えぐいて、ノードポート。何が起こっているや。 まあ、本番ではノードが故障することも考えて、やはりClusterIp + ingressがよさそうです。
configMapについて
これはかなり大事です。ないがしろにしない方がいいで。 ConfigMapを利用して、環境変数をPODに流し込む。わけです。
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-server
labels:
app: hello-server
spec:
replicas: 1
selector:
matchLabels:
app: hello-server
template:
metadata:
labels:
app: hello-server
spec:
containers:
- name: hello-server
image: blux2/hello-server:1.4
env:
- name: PORT
valueFrom:
configMapKeyRef:
name: hello-server-configmap
key: PORT
---
apiVersion: v1
kind: ConfigMap
metadata:
name: hello-server-configmap
data:
PORT: "8081"
こんな感じで、spec.env.nameでPORTを指定しているよね。 どこから読み込むのかというのも指定している。 hello-server-configmapから読み込むって指定しているわけです。 その下にhello-server-configmapというConfigMapがあるわけですね。 そこでdata.PORTを指定しているわけですね。
7章 HC(ヘルス✓)等
Rediness probeとLiveness Probeというのがありますね。 Rediness probeでは、指定したHCエンドポイントへアクセスできるまで、そのポッドにはリクエストを投げない設定です。これも便利ですねー。
liveness probeというのは、HCが失敗したときにそのPODを再起動する方法です。 どちらのHCもHC間隔を決められます。便利です。
7章 リソースについて
k8sではポッドに割り当てるリソース量を決められます。 これはマジでslurmとかと同じですね。 最大値と最小値を決められます。それに応じて、どのNodeにスケジュールするかが決まります。便利ですねー。
apiVersion: v1
kind: Pod
metadata:
labels:
app: hello-server
name: hello-server
spec:
containers:
- name: hello-server
image: blux2/hello-server:1.6
resources:
requests:
memory: "64Mi"
cpu: "10m"
limits:
memory: "64Mi"
cpu: "10m"
9章 腕試し!!
はい、原因調査完了して、bug fixしました
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-server-external NodePort 10.97.198.195 <none> 8081:30599/TCP 7h48m
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-08 $ curl localhost:8081
curl: (7) Failed to connect to localhost port 8081 after 0 ms: Couldn't connect to server
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-08 $ curl localhost:8081
curl: (7) Failed to connect to localhost port 8081 after 0 ms: Couldn't connect to server
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-08 $ curl localhost:30599
Hello, world! Let's build, break and fix!ray@master:~/k8s_manifests/bbf-kubernetes/chapter-08 $ curl 10.97.198.1910.97.198.195:80801
curl: (3) URL using bad/illegal format or missing URL
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-08 $ curl 10.97.198.195:8081
Hello, world! Let's build, break and fix!
まあ原因は以下でしたね。
ConfigMapのポート番号が間違っていたこと。 maxUnavailableが10%で、3つの10%て、0.3で変えられないじゃんってこと 後、liveness probeのinitialDelaySecondsが短すぎて、PODが立ち上がる前にリスタートしていたこと。そして、health checkのパスが間違っていたこと diffはこんなかんじ
ray@master:~/k8s_manifests/bbf-kubernetes/chapter-08 $ diff hello-server-fix.yaml hello-server.yaml
32c32
< image: blux2/hello-server:2.0.1
---
> image: blux2/hello-server:1.8
48c48
< path: /healthz
---
> path: /health
54c54
< path: /healthz
---
> path: /health
56c56
< initialDelaySeconds: 30
---
> initialDelaySeconds: 10
85c85
< maxUnavailable: 50%
---
> maxUnavailable: 10%
答え合わせと行きましょうか。 maxUnavailable:10%はあまり関係なかったみたいだけど、ほかは問題なさそうだ。OK!
9章 アーキテクチャを理解しよう
ここまででIngressに関して何もやってないのがすごく心配になるが、、どうなんだ!! まあいいか、、、Ingressやりたかったんだけどなー。 まあ、ここではとりあえずk8sのアーキテクチャの説明をしています。
コントロールプレーンと、ワーカーノードがあって、 コントロールプレーンには、etcdとkubeapi_server、kube_scheduler、 ワーカノードには、kubeletと、kube-proxy、そして、コンテナラインタイム(containerd)がいる。 kubectlで、kubeapi_serverと通信して、etcdにマニフェストの内容を登録する。 それを見てkube-schedulerがPODをスケジューリングする。 で、コントロールプレーンが死んでもサービスは生きる、っていうのが特徴かな。これはすごい。 まあ、ほとんどのc-planeとD-planeでそういう設計になっているのだけれどもね。
10章
argo cdの説明がされています。 gitopsというci/cdの手法にカテゴライズされるみたいです。 別にgitを使っているからというわけではないらしい。すごい。
そして、マニフェストファイルの管理にkustomizeを使え!!ってはなし。 仕事でも使ったけど、環境ごとに地味に設定が違うときとかにすごい便利って話だよね。 baseディレクトリに共通事項を書いておく。 そして、overlays/prod, overlays/dev, overlays/stg に各環境ごとの設定を書いておく。 これは実際のやつを見て勉強しながらの方が早いですね。
11章 o11yについて
prometheusとgrafanaについて書かれています。 まあ、どちらもやってみるのがいいと思います。楽しいだろうしね。
12章 今後の勉強指針
やってみたいのはやっぱりk8s the hard way. 後は、青山さんのk8s大全を読め!という話と。 cncfからk8s関連の資格が2つあるよ。アプリケーション運用者向けと、システム運用者向けと。という感じ。 後は、Linuxに詳しくなれ!というお話と、実装よめという話以上!
結局Ingressに関して書かれていなかったという話。たのみますよー
なので、k8s大全の6章。Service APIs カテゴリを読みたいと思いますよー。 うーん、難しい。IngressのバックエンドにServiceをおいて、それらはNodePortにしないとだめらしい。 で、Ingressの設定でバックエンドにNodePort Serviceを設定する感じ。 chatgptに書いてもらったのがこちら。
apiVersion: v1
kind: Namespace
metadata:
name: my-api-namespace
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-api-service
labels:
app: my-api-service
spec:
replicas: 3
selector:
matchLabels:
app: my-api-service
template:
metadata:
labels:
app: my-api-service
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
values:
- hello-server
operator: In
topologyKey: kubernetes.io/hostname
containers:
- name: hello-server
image: blux2/hello-server:2.0.1
env:
- name: PORT
valueFrom:
configMapKeyRef:
name: hello-server-configmap
key: PORT
resources:
requests:
memory: "256Mi"
cpu: "10m"
limits:
memory: "256Mi"
cpu: "10m"
readinessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 30
periodSeconds: 5
---
apiVersion: v1
kind: ConfigMap
metadata:
name: hello-server-configmap
data:
PORT: "8081"
HOST: "localhost"
---
apiVersion: v1
kind: Service
metadata:
name: my-api-service
namespace: my-api-namespace
spec:
selector:
app: my-api-service
ports:
- protocol: TCP
port: 80
targetPort: 8081
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-api-ingress
namespace: my-api-namespace
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
ingressClassName: nginx
rules:
- host: my-api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-api-service
port:
number: 80
serviceはデフォルトでClusterIpになると思うので、これだと青山さんの言っていることと違うけどどうなんだろう。 いったんデプロイしてみるか。
apiVersion: v1
kind: Namespace
metadata:
name: my-api-namespace
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-api-service
namespace: my-api-namespace
labels:
app: my-api-service
spec:
replicas: 3
selector:
matchLabels:
app: my-api-service
template:
metadata:
labels:
app: my-api-service
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
values:
- hello-server
operator: In
topologyKey: kubernetes.io/hostname
containers:
- name: hello-server
image: blux2/hello-server:2.0.1
env:
- name: PORT
valueFrom:
configMapKeyRef:
name: hello-server-configmap
key: PORT
resources:
requests:
memory: "256Mi"
cpu: "10m"
limits:
memory: "256Mi"
cpu: "10m"
readinessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 30
periodSeconds: 5
---
apiVersion: v1
kind: ConfigMap
metadata:
name: hello-server-configmap
namespace: my-api-namespace
data:
PORT: "8081"
HOST: "localhost"
---
apiVersion: v1
kind: Service
metadata:
name: my-api-svc
namespace: my-api-namespace
spec:
selector:
app: my-api-service
ports:
- protocol: TCP
port: 80
targetPort: 8081
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-api-ingress
namespace: my-api-namespace
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
ingressClassName: nginx
rules:
- host: my-api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-api-svc
port:
number: 80
やはりこれではアクセスできず。NodePortにする必要があるのではないか?と思っている。
ray@master:~/k8s_manifests/bbf-kubernetes $ kubectl get svc -n my-api-namespace
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-api-service ClusterIP 10.96.59.54 <none> 80/TCP 27m
my-api-svc NodePort 10.110.149.87 <none> 80:31337/TCP 21m
ray@master:~/k8s_manifests/bbf-kubernetes $ curl localhost:31337
Hello, world! Let's build, break and fix!ray@master:~/k8s_manifests/bbf-kubernetes $ ^C
ray@master:~/k8s_manifests/bbf-kubernetes $ curl 10.110.149.87
Hello, world! Let's build, break and fix!ray@master:~/k8s_manifests/bbf-kubernetes $
ちゃんと読んでなかったけど、ingress controllerというものを導入する必要があるみたいです。 はい、以下のmanifestをapplyした。 https://github.com/kubernetes/ingress-nginx/blob/main/deploy/static/provider/cloud/deploy.yaml で、デプロイしたんだけど、それでもマジでアクセスで金くて困っているのだが。
ray@master:~/k8s_manifests/install_ingress_ctl $ kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.108.157.49 <pending> 80:31129/TCP,443:31892/TCP 23m
ingress-nginx-controller-admission ClusterIP 10.96.58.201 <none> 443/TCP 23m
ray@master:~/k8s_manifests/install_ingress_ctl $ curl 10.108.157.49 -H "Host:my-api.example.com"
Hello, world! Let's build, break and fix!
ingressを通してのアクセスはできた。しかしどうやら31129なのだよね。
ray@master:~/k8s_manifests/install_ingress_ctl $ curl localhost:31129 -H "Host:my-api.example.com"
Hello, world! Let's build, break and fix!
できている。
ほかのノードからはどうか。
ray@delta:~|⇒ curl master.ingenboy.local:31129 -H "Host:my-api-example.com"
だめだね。タイムアウト。なんで??? どうやら、ingressのsvcでloadbalancerのやつがpendingになってるでしょ?これはクラウド環境用のファイルを適応してしまったかららしい。
今回はラズパイで構築しているので、こっちでないとだめ https://github.com/kubernetes/ingress-nginx/blob/main/deploy/static/provider/baremetal/deploy.yaml ベアメタル用。
はい、外部からアクセスしてみますねー そしたらこれで行けましたね。
ray@delta:~|⇒ curl master.ingenboy.local:30492 -H "Host:my-api.example.com"
Hello, world! Let's build, break and fix!%
まあ確かにこれで内部で複数のサービスを保持できることにはなるがな。しかし面倒くさすぎるな。 ingressのノードポートのポートを指定することはできないのかな?
できなくはない。ingressControllerのインストールスクリプトで以下のようにポート番号を指定すればいい。
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.12.0
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- appProtocol: http
name: http
port: 80
protocol: TCP
targetPort: http
nodePort: 30080
- appProtocol: https
name: https
port: 443
protocol: TCP
targetPort: https
nodePort: 30443
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
type: NodePort
しかし、nodeportで指定できるのは30000以上らしいので80とかは無理。 ただ、nodeportで指定可能なぽーとレンジを変更することもできる。実はね。 それがこんな感じです。
ray@master:~/k8s_manifests/install_ingress_ctl $ sudo cat /etc/kubernetes/manifests/kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.3.8:6443
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=192.168.3.8
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
- --secure-port=6443
- --service-account-issuer=https://kubernetes.default.svc.cluster.local
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
- --service-cluster-ip-range=10.96.0.0/12
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
image: registry.k8s.io/kube-apiserver:v1.28.15
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 8
httpGet:
host: 192.168.3.8
path: /livez
port: 6443
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
name: kube-apiserver
readinessProbe:
failureThreshold: 3
httpGet:
host: 192.168.3.8
path: /readyz
port: 6443
scheme: HTTPS
periodSeconds: 1
timeoutSeconds: 15
resources:
requests:
cpu: 250m
startupProbe:
failureThreshold: 24
httpGet:
host: 192.168.3.8
path: /livez
port: 6443
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
volumeMounts:
- mountPath: /etc/ssl/certs
name: ca-certs
readOnly: true
- mountPath: /etc/ca-certificates
name: etc-ca-certificates
readOnly: true
- mountPath: /etc/kubernetes/pki
name: k8s-certs
readOnly: true
- mountPath: /usr/local/share/ca-certificates
name: usr-local-share-ca-certificates
readOnly: true
- mountPath: /usr/share/ca-certificates
name: usr-share-ca-certificates
readOnly: true
hostNetwork: true
priority: 2000001000
priorityClassName: system-node-critical
securityContext:
seccompProfile:
type: RuntimeDefault
volumes:
- hostPath:
path: /etc/ssl/certs
type: DirectoryOrCreate
name: ca-certs
- hostPath:
path: /etc/ca-certificates
type: DirectoryOrCreate
name: etc-ca-certificates
- hostPath:
path: /etc/kubernetes/pki
type: DirectoryOrCreate
name: k8s-certs
- hostPath:
path: /usr/local/share/ca-certificates
type: DirectoryOrCreate
name: usr-local-share-ca-certificates
- hostPath:
path: /usr/share/ca-certificates
type: DirectoryOrCreate
name: usr-share-ca-certificates
status: {}
ここに以下を追加する。
- command:
- kube-apiserver
- --service-node-port-range=20000-40000
まあもう面倒くさいからいいや。 k8sなかなか理解できました!!ありがとう!!そんなに難しくないわ!楽しかった!!これはただの道具ですわ。 使いこなせばいいサービスが作れるのではないでしょうか??お疲れ様です。