#はじめに
「kubernetes the hard way を PC でやってみる」の15回目、「Smoke Test」についてです。 (目次)
今回は Smoke Test です。 様々な機能の検証を行います。
なお、次の Cleaning Up はオンプレなのでやる気ありません(笑)
ですので、これで最後になります。
「その他」で何かやろうかと思いましたが、その候補の1つの iptables は Qiita にすでにあるようなのでやめておきます。
それ以外に何かあれば、 kubernetes the hard way on PC とは別にやろうかな、と思います。
ようやく終わりですね。。。
内容は以下の通りです。
- Data Encryption
- Deployments
- Port Forwarding
- Logs
- Exec
- Service
Data Encryption
Secret を作成し、それが etcd に格納されたことを確認します。
まず、Secret を作成します。
# kubectl create secret generic kubernetes-the-hard-way \
> --from-literal="mykey=mydata"
secret/kubernetes-the-hard-way created
# kubectl get secret
NAME TYPE DATA AGE
default-token-vvcgl kubernetes.io/service-account-token 3 28h
kubernetes-the-hard-way Opaque 1 21s
yaml で出してみても、secret の内容は直接は表示されないようですね。
kubernetes のドキュメントによると、盗み見をさけるために get pod
や describe pod
では直接的には表示されないようです。
下記の mykey: bXlkYXRh
の部分ですね。
# kubectl get secret kubernetes-the-hard-way -o yaml
apiVersion: v1
data:
mykey: bXlkYXRh
kind: Secret
metadata:
creationTimestamp: "2020-05-10T10:05:30Z"
name: kubernetes-the-hard-way
namespace: default
resourceVersion: "118358"
selfLink: /api/v1/namespaces/default/secrets/kubernetes-the-hard-way
uid: 178bbd1b-c6f6-4904-bfc9-1e70b2513b2a
type: Opaque
では、確認するためにはどうするのか、は kubernetes のドキュメントの 「Secret のデコード」を参照してやってみます。
# echo bXlkYXRh | base64 --decode
mydata
表示されましたね。
重要なのは、 Secret は別に暗号化されているわけではない ということです。
まぁちょっとした秘密、という感じですね。 その気になれば見られます。
次に、etcd に入っているかを確認します。
# ETCDCTL_API=3 etcdctl get --endpoints=https://127.0.0.1:2379 \
> --cacert=/etc/etcd/ca.pem \
> --cert=/etc/etcd/kubernetes.pem \
> --key=/etc/etcd/kubernetes-key.pem \
> /registry/secrets/default/kubernetes-the-hard-way | hexdump -C
00000000 2f 72 65 67 69 73 74 72 79 2f 73 65 63 72 65 74 |/registry/secret|
00000010 73 2f 64 65 66 61 75 6c 74 2f 6b 75 62 65 72 6e |s/default/kubern|
00000020 65 74 65 73 2d 74 68 65 2d 68 61 72 64 2d 77 61 |etes-the-hard-wa|
00000030 79 0a 6b 38 73 3a 65 6e 63 3a 61 65 73 63 62 63 |y.k8s:enc:aescbc|
00000040 3a 76 31 3a 6b 65 79 31 3a 6f 0d 70 6f 7e 0d 76 |:v1:key1:o.po~.v|
00000050 e1 a7 12 b5 73 a9 78 88 6e ee 07 d6 6b 68 39 b0 |....s.x.n...kh9.|
00000060 b5 fa a0 cb 3e 1d 2c 01 d4 55 46 87 c5 c0 3c bc |....>.,..UF...<.|
00000070 a8 b4 7e 87 40 77 9b 73 7b 99 8a 8a 3f ea 2b da |..~.@w.s{...?.+.|
00000080 dd 8a 84 24 7c 60 78 3e df 4e 69 2b 96 1b 65 38 |...$|`x>.Ni+..e8|
00000090 9f da 2a 0d 19 c1 06 76 e2 80 b5 0c 91 c3 cd 4e |..*....v.......N|
000000a0 d3 06 db d6 48 a9 00 45 cc 7c cf ae 85 83 fc a5 |....H..E.|......|
000000b0 23 6d ea 89 aa 3b 70 ad 8f c7 5f 2e 38 89 a8 44 |#m...;p..._.8..D|
000000c0 3b bd 59 e9 0f 5b d6 e1 24 7e 48 d6 a2 ea e8 97 |;.Y..[..$~H.....|
000000d0 cd 54 aa 64 0e c4 98 8e 2e 4b ad 09 56 59 ab fd |.T.d.....K..VY..|
000000e0 53 e4 94 b2 22 cc 87 9e c2 0a |S...".....|
000000ea
etcd にも格納されていることがわかりました。
k8s:enc:aescbc:v1:key1
という部分がありますが、
etcd の中では、 aescbc
という暗号化プロバイダーが key1
というキーで暗号化した、ということだそうです。
...残念ながらよくわかりませんね。
「第8回 Encryption Config 」で etcd 用の Encryption Config を作成したのを覚えているでしょうか?
その時の yaml を再掲します。
kind: EncryptionConfig
apiVersion: v1
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: 77ditR6J(略)pKZL+AQj0=
- identity: {}
きちんと key1
という名前がついていますね!
こんなところにつながっているわけですね。 (つながっていないと困る)
#Deployments
検証用に nginx の deployment を作成します。
# kubectl create deployment nginx --image=nginx
deployment.apps/nginx created
# kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-86c57db685-tqhnw 1/1 Running 0 2m35s
##Port Forwarding
Port Forward を行って、外部からアクセスできるようにします。
kubernetes the hard way では、 80 -> 8080 に forward していますが、
ここでは 80 -> 8081 で実施しています。
(理由の詳細は後述)
kubernetes 1.18 以降であれば、別に 8080 で問題ありませんのでどちらでもOKです。
なお、ここではターミナルを2枚使います。
1枚で Port Forward を行い、もう1枚で アクセスのテストを行います。
1枚目のターミナルで POD 名を取得します。 jsonpath が登場ですね。
Pod インスタンス配列の 0番目(つまり先頭)の metadata.name を取得しています。
# POD_NAME=$(kubectl get pods -l app=nginx -o jsonpath="{.items[0].metadata.name}")
# echo $POD_NAME
nginx-86c57db685-tqhnw
同じターミナルで Port Forward します。(ここでは8081 を使います)
# kubectl port-forward $POD_NAME 8081:80
Forwarding from 127.0.0.1:8081 -> 80
ここで、 ターミナルにプロンプトが返ってこない状態になるはずです。
この状態で、もう1枚のターミナルからアクセスします。
まぁ、一応先に 8081 が Listen されているかを確認します。念のためです。
私のケースではここまで来るまでにだいぶ回り道をしていますので…
# ss -nat | grep 8081
LISTEN 0 4096 127.0.0.1:8081 0.0.0.0:*
大丈夫ですね、 次に curl でアクセスします。
# curl --head http://127.0.0.1:8081
HTTP/1.1 200 OK
Server: nginx/1.17.10
Date: Sun, 10 May 2020 13:14:25 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 14 Apr 2020 14:19:26 GMT
Connection: keep-alive
ETag: "5e95c66e-264"
Accept-Ranges: bytes
無事に 200 OK が返ってきています。これで確認完了です。
この時、 Port Forward したターミナルに、下記のようなログが出ているかと思います。
Handling connection for 8081
確認できれば Port Forward のターミナルで Ctrl + c で Port Forward を終了します。
次に、nginx のログを確認しておきます。
# kubectl logs ${POD_NAME}
127.0.0.1 - - [10/May/2020:13:14:25 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.68.0" "-"
きちんとログが出力されていますね。
##Exec
コンテナ内でコマンドを実行させます。
これはよく使いますので自然と覚えているのではないかと思います。
# kubectl exec -ti $POD_NAME -- nginx -v
nginx version: nginx/1.17.10
コンテナ内で nginx -v が実行され、応答が返されています。
#Services
サービスですね。外部公開するためのものです。
ここでは NodePort を使って nginx のポート (80) を外部公開します。
オプション等を指定せず簡単に expose だけしているため、 ポートは自動的に 31919 が採用されています。
# kubectl expose deployment nginx --port 80 --type NodePort
service/nginx exposed
# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.32.0.1 <none> 443/TCP 31h <none>
nginx NodePort 10.32.0.225 <none> 80:31919/TCP 21s app=nginx
一応、kubernetes the hard way に沿って、 json でも確認しておきます。
jsonpath も使えるようになった方が便利なんでしょうかね。
# kubectl get svc nginx -o jsonpath='{range .spec.ports[0]}{.nodePort}'
31919
ここがなぜ range となっているのかは、調べる気力が無くわかりません。。。
さてアクセスです。
# curl -I http://192.168.199.210:31919
HTTP/1.1 200 OK
Server: nginx/1.17.10
Date: Sun, 10 May 2020 13:20:08 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 14 Apr 2020 14:19:26 GMT
Connection: keep-alive
ETag: "5e95c66e-264"
Accept-Ranges: bytes
無事に応答が返っていますね。 IP アドレスの部分を 他のワーカーノードの IP にしても同じようにアクセスできますので
ためしてみてください。
これで作業は終了です! お疲れ様でした!!
...が、 トラブル説明のコーナーがまだでしたね。。。
#エラー並びに調査・対応方法
さて、今回のこの記事では、 Port Forward に 8081 番を利用しています。
kubernetes the hard way では 8080 を用いています。
なぜか、、、
エラーが出たからです! なお、 kubernetes 1.18 では出ませんのでもう無視していただいてもOKです。
# kubectl port-forward $POD_NAME 8080:80
Unable to listen on port 8080: Listeners failed to create with the following errors: [unable to create listener: Error listen tcp4 127.0.0.1:8080: bind: address already in use unable to create listener: Error listen tcp6 [::1]:8080: socket: address family not supported by protocol]
error: unable to listen on any of the requested ports: [{8080 80}]
もうほぼ終了、というところでのエラーはつらいですね。
address already in use
だそうです。誰だよ。
ポートの利用状況を調べます。
# lsof -n -i:8080
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
kube-apis 688 root 5u IPv4 33307 0t0 TCP 127.0.0.1:http-alt (LISTEN)
少し短く省略されて kube-apis
などと表示されていますが、 kube-apiserver ですね。 6443 じゃないのか!
一応、 PID も確認しておきます。
# ps -ef | grep kube-apiserver
root 688 1 2 01:55 ? 00:15:46 /usr/local/bin/kube-apiserver --advertise-address=192.168.199.200 --allow-privileged=true --apiserver-count=3 --audit-log-maxage=30 --audit-log-maxbackup=3 --audit-log-maxsize=100 --audit-log-path=/var/log/audit.log --authorization-mode=Node,RBAC --bind-address=0.0.0.0 --client-ca-file=/var/lib/kubernetes/ca.pem --enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota --etcd-cafile=/var/lib/kubernetes/ca.pem --etcd-certfile=/var/lib/kubernetes/kubernetes.pem --etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem --etcd-servers=https://192.168.199.200:2379,https://192.168.199.201:2379,https://192.168.199.202:2379 --event-ttl=1h --encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem --kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem --kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem --kubelet-https=true --runtime-config=api/all --service-account-key-file=/var/lib/kubernetes/service-account.pem --service-cluster-ip-range=10.32.0.0/24 --service-node-port-range=30000-32767 --tls-cert-file=/var/lib/kubernetes/kubernetes.pem --tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem --v=2
kube-apiserver の設定か何かに 8080 が入っているのか?ということで、確認します。
kubernetes the hard way では kube-apiserver は systemd 配下のサービスですので、
systemctl
で確認します。
# systemctl cat kube-apiserver
# /etc/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
[Service]
ExecStart=/usr/local/bin/kube-apiserver \
--advertise-address=192.168.199.200 \
--allow-privileged=true \
--apiserver-count=3 \
--audit-log-maxage=30 \
--audit-log-maxbackup=3 \
--audit-log-maxsize=100 \
--audit-log-path=/var/log/audit.log \
--authorization-mode=Node,RBAC \
--bind-address=0.0.0.0 \
--client-ca-file=/var/lib/kubernetes/ca.pem \
--enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \
--etcd-cafile=/var/lib/kubernetes/ca.pem \
--etcd-certfile=/var/lib/kubernetes/kubernetes.pem \
--etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \
--etcd-servers=https://192.168.199.200:2379,https://192.168.199.201:2379,https://192.168.199.202:2379 \
--event-ttl=1h \
--encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \
--kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \
--kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \
--kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \
--kubelet-https=true \
--runtime-config=api/all \
--service-account-key-file=/var/lib/kubernetes/service-account.pem \
--service-cluster-ip-range=10.32.0.0/24 \
--service-node-port-range=30000-32767 \
--tls-cert-file=/var/lib/kubernetes/kubernetes.pem \
--tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem \
--v=2
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
8080 は記載がないです。 ただ、たまたまですが NodePort の Range が 30000-32767 であることがわかりました。
kube-apiserver が犯人であることは間違いなさそうですが、
一応 kube-apiserver を停止して 8080 ポートが解放されるか確認します。
# systemctl stop kube-apiserver
# echo $?
0
# lsof -n -i:8080
#
# systemctl start kube-apiserver
# lsof -n -i:8080
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
kube-apis 2478 root 5u IPv4 632782 0t0 TCP 127.0.0.1:http-alt (LISTEN)
kube-apiserver を止めると 8080 を Listen しなくなるので
犯人は kube-apiserver で確定です。
興味本位ではありますが、 kube-apiserver は 8080 ポートで何をしてみるのか
少しのぞいてみます。
# curl 127.0.0.1:8080
"/apis/apiregistration.k8s.io",
"/apis/apiregistration.k8s.io/v1",
... (略)
API がずらりとリストされました。 https ではなくてもアクセスできるんですね。(!)
API Server のログを確認してみます。
# journalctl -u kube-apiserver | grep 8080
May 09 05:46:22 k8smaster0 kube-apiserver[2625]: I0509 05:46:22.643275 2625 flags.go:33] FLAG: --insecure-port="8080"
May 09 05:46:22 k8smaster0 kube-apiserver[2625]: I0509 05:46:22.643736 2625 flags.go:33] FLAG: --port="8080"
May 09 05:46:27 k8smaster0 kube-apiserver[2625]: I0509 05:46:27.454254 2625 deprecated_insecure_serving.go:53] Serving insecurely on 127.0.0.1:8080
May 09 15:07:28 k8smaster0 kube-apiserver[2625]: I0509 15:07:28.261738 2625 secure_serving.go:167] Stopped listening on 127.0.0.1:8080
--insecure-port="8080"
となっていますね。
念のため、API Server 再起動の際のログを丁寧に見てみると、確かに指定があります。
May 10 11:51:21 k8smaster0 systemd[1]: Stopped Kubernetes API Server.
May 10 11:51:36 k8smaster0 systemd[1]: Started Kubernetes API Server.
May 10 11:51:36 k8smaster0 kube-apiserver[2478]: I0510 11:51:36.195254 2478 flags.go:33] FLAG: --add-dir-header="false"
May 10 11:51:36 k8smaster0 kube-apiserver[2478]: I0510 11:51:36.195362 2478 flags.go:33] FLAG: --address="127.0.0.1"
...(略)...
May 10 11:51:36 k8smaster0 kube-apiserver[2478]: I0510 11:51:36.196314 2478 flags.go:33] FLAG: --insecure-bind-address="127.0.0.1"
May 10 11:51:36 k8smaster0 kube-apiserver[2478]: I0510 11:51:36.196325 2478 flags.go:33] FLAG: --insecure-port="8080"
...(略)...
ネットで調べてみると 「12 Kubernetes configuration best practices」というサイトで上記のオプションの記載がありました。
--insecure-bind-address argument isn’t there. This configuration will prevent the API Server from binding to an insecure address, preventing non-authenticated and unencrypted access to your master node, which minimizes your risk of attackers potentially reading sensitive data in transit.
--insecure-port argument shows as 0. This setting will prevent the API Server from serving on an insecure port, which would prevent unauthenticated and unencrypted access to the master node and minimize the risk of an attacker taking control of the cluster.
また、 下記の issue を見ると、 default は 8080 であることがわかりました。
Enabling insecure port for API Server #49933
The default value of --insecure-port is 8080, which can be omitted. Also for insecure requests, no authentications and authorizions will be performed.
ちなみに、 kubernetes 1.18 では、 --insecure-port=0
が指定され、無効化されているようです。
( kubeadm でクラスター構成をした場合。 デフォルトが 0 に変わったのか、 kubeadm が 0 に設定したのかは不明。
マニュアル上からは --insecure-port
の記載は消えています(deprecatedのため))
v1.18 では kube-apiserver のログにも出ています。
Flag --insecure-port has been deprecated, This flag will be removed in a future version.
ということで、 前にもバグに引っ掛かりましたが、 v1.16 でやったのがすべての間違いでした。。。
これで、kubernetes the hard way on PC は終了です。
ここまでお付き合いいただいたみなさま、ありがとうございました。
情報発信自体は続けていきたいので、またよろしくお願いいたします。
← 14.CoreDNS
↑ 目次