tjtjtjのメモ

自分のためのメモです

kubernetes 学習 ExternalIP

クラスタ外から指定したNodeIPを通してpodへアクセスできるようにする。 指定したip の node がダウンしている場合は疎通できない。nodeportも同様

kbhello-service-eip.yaml

apiVersion: v1
kind: Service
metadata:
  name: kbhello-service-eip
spec:
  selector:
    app: kbhello
  type: ClusterIP
  externalIPs:
  - 192.168.0.101
  ports:
  - port: 8080
$ kubectl get pod -o wide
NAME                                  READY   STATUS    RESTARTS   AGE   IP            NODE   NOMINATED NODE   READINESS GATES
kbhello-deployment-664dd576d4-29pkt   1/1     Running   0          5d    10.244.1.90   kb2    <none>           <none>
kbhello-deployment-664dd576d4-l64ft   1/1     Running   0          14s   10.244.2.39   kb3    <none>           <none>
kbhello-deployment-664dd576d4-zztrq   1/1     Running   0          14s   10.244.2.38   kb3    <none>           <none>
$ curl 192.168.0.101:8080
Hello Docker World kbhello-deployment-664dd576d4-zztrq
Hello Docker World kbhello-deployment-664dd576d4-29pkt
Hello Docker World kbhello-deployment-664dd576d4-l64ft

kubernetes 学習 NodePort

前回は、クラスタ内部間の通信を調べた。 クラスタ外部 -> サービス -> pod の経路を調べたい。

確認

kb2 に pod:29pkt, pod:nrj9j。kb3 に pod:n9t8t。

$ kubectl get pod -o wide
NAME                                  READY   STATUS    RESTARTS   AGE   IP            NODE   NOMINATED NODE   READINESS GATES
kbhello-deployment-664dd576d4-29pkt   1/1     Running   0          24h   10.244.1.90   kb2    <none>           <none>
kbhello-deployment-664dd576d4-n9t8t   1/1     Running   0          24h   10.244.2.37   kb3    <none>           <none>
kbhello-deployment-664dd576d4-nrj9j   1/1     Running   0          24h   10.244.1.91   kb2    <none>           <none>

EXTERNAL-IP にip設定すればいいのかな?

$ kubectl get service
NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kbhello-service-cip   ClusterIP   10.104.228.167   <none>        8080/TCP   3d22h
kubernetes            ClusterIP   10.96.0.1        <none>        443/TCP    43d

LoadBalancer を試す

apiVersion: v1
kind: Service
metadata:
  name: kbhello-service-lb
spec:
  selector:
    app: kbhello
  type: LoadBalancer
  ports:
  - port: 8080
  loadBalancerIP: 192.168.0.111

やっぱり失敗。EXTERNAL-IP:pending。LoadBalancer にipを渡す人が必要なのか。

$ kubectl get service
NAME                  TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kbhello-service-cip   ClusterIP      10.104.228.167   <none>        8080/TCP         3d23h
kbhello-service-lb    LoadBalancer   10.111.236.45    <pending>     8080:30202/TCP   97s
kubernetes            ClusterIP      10.96.0.1        <none>        443/TCP          43d

CLUSTER-IP のcurl はOK

$ curl 10.111.236.45:8080
Hello Docker World kbhello-deployment-664dd576d4-n9t8t
Hello Docker World kbhello-deployment-664dd576d4-29pkt
Hello Docker World kbhello-deployment-664dd576d4-nrj9j

NodePort を試す

NodePort。ノードポート。node内のpodには行けても、別nodeのpodには行けない感。

NodePortは、全てのKubernetes NodeのIP:Portで受けたトラフィックをコンテナに転送する形で、外部疎通性を確立します。 https://thinkit.co.jp/article/13738?page=0%2C1

NodePortは各NodeのIPでポートを公開します。これにより、クラスタの外からServiceにアクセスできるようになります。 https://codezine.jp/article/detail/10523

試してみる。

$ kubectl expose deployment kbhello-deployment --type=NodePort --port 8080
service/kbhello-deployment exposed
$ kubectl get service
NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kbhello-deployment    NodePort    10.110.104.66    <none>        8080:30199/TCP   18s
kbhello-service-cip   ClusterIP   10.104.228.167   <none>        8080/TCP         3d23h
kubernetes            ClusterIP   10.96.0.1        <none>        443/TCP          44d

node:kb2 にcurl。node3のレスポンスもあった。

$ curl 192.168.0.102:8080
curl: (7) Failed to connect to 192.168.0.102 port 8080: Connection refused

$ curl 192.168.0.102:30199
Hello Docker World kbhello-deployment-664dd576d4-29pkt
Hello Docker World kbhello-deployment-664dd576d4-nrj9j
Hello Docker World kbhello-deployment-664dd576d4-29pkt
Hello Docker World kbhello-deployment-664dd576d4-n9t8t <--- node:kb3
Hello Docker World kbhello-deployment-664dd576d4-nrj9j

node:kb3 にcurl。node2のレスポンスもあった。

$ curl 192.168.0.103:8080
curl: (7) Failed to connect to 192.168.0.103 port 8080: Connection refused

$ curl 192.168.0.103:30199
Hello Docker World kbhello-deployment-664dd576d4-n9t8t
Hello Docker World kbhello-deployment-664dd576d4-29pkt <--- node:kb2
Hello Docker World kbhello-deployment-664dd576d4-29pkt <--- node:kb2
Hello Docker World kbhello-deployment-664dd576d4-29pkt <--- node:kb2
Hello Docker World kbhello-deployment-664dd576d4-nrj9j <--- node:kb2

pod を1個にしてcurl してみる。

podがないnodeを作ったらどうなる?

$ kubectl edit deployment kbhello-deployment
:
 replicas: 3
↓ 
 replicas: 1

node:kb2 だけになったことを確認。

$ kubectl get pod -o wide
NAME                                  READY   STATUS    RESTARTS   AGE   IP            NODE   NOMINATED NODE   READINESS GATES
kbhello-deployment-664dd576d4-29pkt   1/1     Running   0          24h   10.244.1.90   kb2    <none>           <none>

curl。podがない kb3(103) の port からもレスポンスがある。なるほど、欲しかったのはNodePortだったのか。

core@kb1 ~ $ curl 192.168.0.102:30199
Hello Docker World kbhello-deployment-664dd576d4-29pkt

core@kb1 ~ $ curl 192.168.0.103:30199
Hello Docker World kbhello-deployment-664dd576d4-29pkt

NodePortだと別nodeのpodには行けない、は勘違いでした。

NodePortを指定する

さっきのやり方ではNodePort の番号を指定できないようなのでマニフェストにしてみた。

kbhello-service-np.yaml

apiVersion: v1
kind: Service
metadata:
  name: kbhello-service-np
spec:
  selector:
    app: kbhello
  type: NodePort
  ports:
  - port: 8080
    nodePort: 30080

マニフェスト適用。

$ kubectl apply -f kbhello-service-np.yaml
service/kbhello-service-np created

$ kubectl get svc
NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kbhello-deployment    NodePort    10.110.104.66    <none>        8080:30199/TCP   28m
kbhello-service-cip   ClusterIP   10.104.228.167   <none>        8080/TCP         4d
kbhello-service-np    NodePort    10.99.42.155     <none>        8080:30080/TCP   8s
kubernetes            ClusterIP   10.96.0.1        <none>        443/TCP          44d

curlで確認。podが1コしかないけど想定通りの結果が得られた。

$ curl 192.168.0.102:30080
Hello Docker World kbhello-deployment-664dd576d4-29pkt
Hello Docker World kbhello-deployment-664dd576d4-29pkt

$ curl 192.168.0.103:30080
Hello Docker World kbhello-deployment-664dd576d4-29pkt
Hello Docker World kbhello-deployment-664dd576d4-29pkt

kubernetes 学習 dns

service, pod の名前解決を調べた。

最初に確認

kbhello でHOSTNAMEを表示するようにした。 kbhello-deployment が 3podあり、kb2 kb3 の2node で実行中。

$ kubectl get pod -o wide
NAME                                  READY   STATUS    RESTARTS   AGE   IP            NODE   NOMINATED NODE   READINESS GATES
kbhello-deployment-664dd576d4-29pkt   1/1     Running   0          15m   10.244.1.90   kb2    <none>           <none>
kbhello-deployment-664dd576d4-n9t8t   1/1     Running   0          33s   10.244.2.37   kb3    <none>           <none>
kbhello-deployment-664dd576d4-nrj9j   1/1     Running   0          15m   10.244.1.91   kb2    <none>           <none>

ClusterIP 確認

$ kubectl get service
NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kbhello-service-cip   ClusterIP   10.104.228.167   <none>        8080/TCP   2d23h
kubernetes            ClusterIP   10.96.0.1        <none>        443/TCP    42d

curl。ip:10.104.228.167 が3pod(2node) に対応していることが分かる。

$ curl 10.104.228.167:8080
Hello Docker World kbhello-deployment-664dd576d4-n9t8t
$ curl 10.104.228.167:8080
Hello Docker World kbhello-deployment-664dd576d4-nrj9j
$ curl 10.104.228.167:8080
Hello Docker World kbhello-deployment-664dd576d4-29pkt

ClusterIPの名前解決

kbhello がクラスタ内のapiサービスだとしたら、このapiにどうやってアクセスしたらいい? ipアドレスはサービス作成毎に変わってしまうから名前で引きたい。

DNS for Services and Pods

「通常の」(ヘッドレスではない)サービスには、フォームの名前のDNS Aレコードが割り当てられています。 「my-svc.my-namespace.svc.cluster.local」 これはサービスのクラスタIPに解決されます。

ということは、こうなる。

kbhello-service-cip.default.svc.cluster.local

pod から ping してみる。なるほど。

$ kubectl exec -it kbhello-deployment-664dd576d4-n9t8t ping kbhello-service-cip.default.svc.cluster.local
PING kbhello-service-cip.default.svc.cluster.local (10.104.228.167): 56 data bytes
^C
--- kbhello-service-cip.default.svc.cluster.local ping statistics ---
3 packets transmitted, 0 packets received, 100% packet loss
command terminated with exit code 1

$ kubectl exec -it kbhello-deployment-664dd576d4-nrj9j ping kbhello-service-cip.default.svc.cluster.local
PING kbhello-service-cip.default.svc.cluster.local (10.104.228.167): 56 data bytes
:
$ kubectl exec -it kbhello-deployment-664dd576d4-29pkt ping kbhello-service-cip.default.svc.cluster.local
PING kbhello-service-cip.default.svc.cluster.local (10.104.228.167): 56 data bytes
:

名前解決はしているが、レシーブせず100%ロスしている。 nslookup してみた。

$ kubectl exec -it kbhello-deployment-664dd576d4-29pkt nslookup kbhello-service-cip.default.svc.cluster.local
nslookup: can't resolve '(null)': Name does not resolve

Name:      kbhello-service-cip.default.svc.cluster.local
Address 1: 10.104.228.167 kbhello-service-cip.default.svc.cluster.local

/etc/resolv.conf を確認。

$ kubectl exec -it kbhello-deployment-664dd576d4-29pkt cat /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

完全修飾でなく サービス名:kbhello-service-cip でも引けそうだ。引けた。

$ kubectl exec -it kbhello-deployment-664dd576d4-29pkt nslookup kbhello-service-cip
core@kb1 ~ $ kubectl exec -it kbhello-deployment-664dd576d4-29pkt nslookup kbhello-service-cip
nslookup: can't resolve '(null)': Name does not resolve

Name:      kbhello-service-cip
Address 1: 10.104.228.167 kbhello-service-cip.default.svc.cluster.local

逆に完全修飾ならnamepsaceを超えられるのか?

/etc/hosts を確認。10.244.1.90 はこのpod のipアドレス。kbhello-deployment-664dd576d4-29pkt はこのポッドの名前。

$ kubectl exec -it kbhello-deployment-664dd576d4-29pkt cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.244.1.90     kbhello-deployment-664dd576d4-29pkt

Podの名前解決

podも名前解決できるようだ。

ポッドにはDNS Aレコードが「pod-ip-address.my-namespace.pod.cluster.local」の形式で割り当てられます。

ということは、こうか。

10-244-1-90.default.pod.cluster.local
10-244-2-37.default.pod.cluster.local
10-244-1-91.default.pod.cluster.local

ping してみる。こちらはレシーブしている。

$ kubectl exec -it kbhello-deployment-664dd576d4-29pkt ping 10-244-1-90.default.pod.cluster.local
PING 10-244-1-90.default.pod.cluster.local (10.244.1.90): 56 data bytes
64 bytes from 10.244.1.90: seq=0 ttl=64 time=0.048 ms
:
$ kubectl exec -it kbhello-deployment-664dd576d4-29pkt ping 10-244-1-91.default.pod.cluster.local
PING 10-244-1-91.default.pod.cluster.local (10.244.1.91): 56 data bytes
64 bytes from 10.244.1.91: seq=0 ttl=63 time=0.116 ms
:
$ kubectl exec -it kbhello-deployment-664dd576d4-29pkt ping 10-244-2-37.default.pod.cluster.local
PING 10-244-2-37.default.pod.cluster.local (10.244.2.37): 56 data bytes
64 bytes from 10.244.2.37: seq=0 ttl=62 time=0.575 ms
:

podの名前解決が必要なケースはなんだろう。思いつかない。

kubernetes 学習 service/ClusterIP

これまなんとなく雰囲気でサービス・ロードバランサを作っていた。serviceはたぶん pod/deployment の前段にあたる概念だよなーと考えていた。

Serviceとは?

K8sのサービスは、論理的なPodのセットとそれと通信するためのポリシーを定義する抽象的なものです。これはマイクロサービスと呼ばれることもあります。 サービスを介して通信する一連のポッドは、通常、ラベル・セレクタによって決定されます。

https://kubernetes.io/docs/concepts/services-networking/service/ https://qiita.com/kouares/items/94a073baed9dffe86ea0

なるほど、大体間違っていなかったようだ。そして「ラベル・セレクタ」などによってターゲットpodsを指定すると。

雰囲気で作っていた

いままで、なんとなくこんな感じでロードバランサを作成していた。

kubectl expose deployment kbhello-deployment --type=LoadBalancer --name=kbhello-service

残骸serviceが残っていたので yaml を確認してみる。

$ kubectl get service kbhello-service -o yaml
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: "2019-03-04T12:14:50Z"
  name: kbhello-service
  namespace: default
  resourceVersion: "2306882"
  selfLink: /api/v1/namespaces/default/services/kbhello-service
  uid: 1c5a9ac4-3e77-11e9-8f5d-9ca3ba319985
spec:
  clusterIP: 10.98.159.32
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 31872
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: kbhello
  sessionAffinity: None
  type: LoadBalancer
status:
  loadBalancer: {}

お掃除

kubectl get allk8s の ClusterIP しかない状態にする。そう、この ClusterIP ってなんだろとは思っていた。

$ kubectl delete service quarkusstarted-service
service "quarkusstarted-service" deleted
$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   40d
$ kubectl get all
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   40d

続けてテキトーなdeployment作成。

$ kubectl apply -f kbhello-deployment.yaml
deployment.apps/kbhello-deployment created
$ kubectl get pod -o wide
NAME                                  READY   STATUS    RESTARTS   AGE   IP            NODE   NOMINATED NODE   READINESS GATES
kbhello-deployment-7cd47cbb58-4dlk8   1/1     Running   0          84s   10.244.1.89   kb2    <none>           <none>
kbhello-deployment-7cd47cbb58-jxjsx   1/1     Running   0          84s   10.244.1.88   kb2    <none>           <none>
$ curl 10.244.1.89:8080
Hello Docker World
$ curl 10.244.1.88:8080
Hello Docker World

ClusterIP 作成

Service にはいくつか種類があるが、ClusterIP を作ってみる。 selector=app:kbhello って感じか。

kbhello-service-cip.yaml

apiVersion: v1
kind: Service
metadata:
  name: kbhello-service-cip
spec:
  selector:
    app: kbhello
  type: ClusterIP
  ports:
  - port: 8080

apply して get。表示されたip にcurl。なるほど。クラスタ内通信ならLoadBalancerでなく、外部ipが不要なClusterIPで十分ってことか。LoadBalancer の EXTERNAL-IPが pending になっていたのはipが取得できなかったのだ。

$ kubectl apply -f kbhello-service-cip.yaml
service/kbhello-service-cip created
$ kubectl get svc
NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kbhello-service-cip   ClusterIP   10.104.228.167   <none>        8080/TCP   6s
kubernetes            ClusterIP   10.96.0.1        <none>        443/TCP    40d
$ curl 10.104.228.167:8080
Hello Docker World

Quarkus - Creating Your First Application

java コンテナイメージのサイズに悩んでいたところ、こんなニュースがあった。 高速起動にはあまり興味がないが、Quarkus のイメージサイズに興味を覚えた。

Quarkus ってなに?

https://yoshio3.com/2019/03/11/try-quarkus/ より

Quarkus を簡単にご説明すると、Javaソースコードを GraalVM を利用して Linux の Native バイナリを作成し、その Linux バイナリをコンテナ上で起動することにより、今まで Java アプリの課題であった起動時間を大幅に短縮することができる技術です。

GraalVM ってなに?

https://www.graalvm.org/ google翻訳

GraalVMは、JavaScriptPythonRuby、R、JavaScala、Kotlin、ClojureなどのJVMベースの言語、およびCやC ++などのLLVMベースの言語で作成されたアプリケーションを実行するための汎用仮想マシンです。

graalvm いれる

  • https://www.graalvm.org/downloads/
  • wget
  • tar xvf
  • mv graalvm-ce-1.0.0-rc13 /opt
  • export PATH=/opt/graalvm-ce-1.0.0-rc13/bin:$PATH
  • java -version openjdk version "1.8.0_202" OpenJDK Runtime Environment (build 1.8.0_202-20190206132807.buildslave.jdk8u-src-tar--b08) OpenJDK GraalVM CE 1.0.0-rc13 (build 25.202-b08-jvmci-0.55, mixed mode)

Quarkus - Creating Your First Application

チュートリアル 通りにやってみる。

getting-started ディレクトリ下にプロジェクトが生成される。

mvn io.quarkus:quarkus-maven-plugin:0.11.0:create \
    -DprojectGroupId=jp.acme \
    -DprojectArtifactId=getting-started \
    -DclassName="org.acme.quickstart.GreetingResource" \
    -Dpath="/hello"

アプリケーション起動(devモード)

$ cd ./getting-started
$ mvn compile quarkus:dev

アプリケーション確認

$ curl http://localhost:8080
> Congratulations, you have created a new Quarkus application.
> とかなんとか

$ curl http://localhost:8080/hello
hello
  1. Using injection

今度試す

  1. Development Mode バックグラウンドコンパイルによるホットデプロイが可能

なるほどいいね。次も今度試す。

  1. Testing
  2. Packaging and run the application
  3. Async

docker してみる

src/main/docker/Dockerfile を確認。イメージ生成手順が載っています。 target/*-runner を実行することが分かる。

####
# Before building the docker image run:
#
# mvn package -Pnative -Dnative-image.docker-build=true
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile -t quarkus/getting-started .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/getting-started
#
###
FROM registry.fedoraproject.org/fedora-minimal
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

target ディレクトリも見てみる。 target/*-runner は20MB

$ ls -alh target
合計 20M
drwxrwxr-x 14 tjtjtj tjtjtj 4.0K  3月 18 20:50 .
drwxrwxr-x  4 tjtjtj tjtjtj 4.0K  3月 18 22:50 ..
drwxrwxr-x  4 tjtjtj tjtjtj 4.0K  3月 18 19:59 classes
drwxrwxr-x  3 tjtjtj tjtjtj 4.0K  3月 18 19:39 generated-sources
drwxrwxr-x  3 tjtjtj tjtjtj 4.0K  3月 18 19:59 generated-test-sources
-rwxr-xr-x  1 root   root    20M  3月 18 20:50 getting-started-1.0-SNAPSHOT-runner
-rw-r--r--  1 root   root    36K  3月 18 20:04 getting-started-1.0-SNAPSHOT-runner.jar
-rw-rw-r--  1 tjtjtj tjtjtj 5.3K  3月 18 20:04 getting-started-1.0-SNAPSHOT.jar
drwxrwxr-x  2 tjtjtj tjtjtj 4.0K  3月 18 20:04 lib
drwxrwxr-x  2 tjtjtj tjtjtj 4.0K  3月 18 19:59 maven-archiver
drwxrwxr-x  3 tjtjtj tjtjtj 4.0K  3月 18 19:39 maven-status
-rw-rw-r--  1 tjtjtj tjtjtj 3.5K  3月 18 20:04 quarkus.log
drwxr-xr-x  2 root   root   4.0K  3月 18 20:33 reports
drwxrwxr-x  2 tjtjtj tjtjtj 4.0K  3月 18 19:59 surefire-reports
drwxrwxr-x  6 tjtjtj tjtjtj 4.0K  3月 18 19:59 test-classes
drwxrwxr-x  2 tjtjtj tjtjtj 4.0K  3月 18 19:59 transformed-classes
drwxrwxr-x  6 tjtjtj tjtjtj 4.0K  3月 18 19:59 wiring-classes
drwxrwxr-x  4 tjtjtj tjtjtj 4.0K  3月 18 19:39 wiring-devmode

ネイティブバイナリ生成。これが噂のlinuxに最適化されたバイナリ生成か。しっかし時間かかったね。ショボvm のせいもあり参考になさらず。

# mvn package -Pnative -Dnative-image.docker-build=true
:
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  48:16 min
[INFO] Finished at: 2019-03-18T20:50:50+09:00
[INFO] ------------------------------------------------------------------------

イメージサイズ。125MBか。openjdk:8-jdk-alpine の121MB とあまり変わらないサイズ。

# docker images
REPOSITORY               TAG     IMAGE ID      CREATED             SIZE
quarkus/getting-started  latest  2bde28a5057f  About a minute ago  125MB

docker build

# docker build -f src/main/docker/Dockerfile -t quarkus/getting-started .

docker run。確かに一瞬で起動する。バイナリ生成時間をとるか、起動時間をとるか。

# docker run -i --rm -p 8080:8080 quarkus/getting-started

動作確認

$ curl http://localhost:8080/hello
hello

kubernetes してみる

タギング, docker-login, タグpush

docker tag quarkus/getting-started:latest 192.168.0.1:5000/quarkus/getting-started:latest
docker login 192.168.0.1:5000
docker push 192.168.0.1:5000/quarkus/getting-started:latest

イメージ確認

# docker image ls
REPOSITORY                                  TAG                          IMAGE ID            CREATED             SIZE
192.168.0.1:5000/quarkus/getting-started    latest                       2bde28a5057f        About an hour ago   125MB
quarkus/getting-started                     latest                       2bde28a5057f        About an hour ago   125MB

カタログ確認

# curl localhost:5000/v2/_catalog
{"repositories":["kbhello","quarkus/getting-started"]}

quarkusstarted-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: quarkusstarted-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: quarkusstarted
  template:
    metadata:
      labels:
        app: quarkusstarted
    spec:
      containers:
      - name: quarkusstarted
        image: 192.168.0.1:5000/quarkus/getting-started
        ports:
        - containerPort: 8080

デプロイ

$ kubectl apply -f quarkusstarted-deployment.yaml
deployment.apps/quarkusstarted-deployment created
$ kubectl get pod
NAME                                         READY   STATUS    RESTARTS   AGE
quarkusstarted-deployment-7896c77d8c-h7k4n   1/1     Running   0          19s
quarkusstarted-deployment-7896c77d8c-vh42p   1/1     Running   0          19s

ロードバランサ作成

$ kubectl expose deployment quarkusstarted-deployment --type=LoadBalancer --name=quarkusstarted-service
service/quarkusstarted-service exposed
$ kubectl get service
NAME                     TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)              AGE
kubernetes               ClusterIP      10.96.0.1       <none>        443/TCP              33d
quarkusstarted-service   LoadBalancer   10.103.221.38   <pending>     8080:30423    /TCP   14s

確認

$ curl http://10.103.221.38:8080/hello
hello

kubernetes 学習 logging その1

Logging Architecture を試す。

  • コンテナ化されたアプリケーションの最も簡単で包括的なロギング方法は、標準出力と標準エラーストリームに書き込むこと
  • クラスタレベルのログ記録では、ログを保存、分析、およびクエリするために別のバックエンドが必要
  • Kubernetesはログデータ用のネイティブストレージソリューションを提供しない
  • Kubernetesクラスタに既存のロギングソリューションを多数統合できる

Kubernetesでの基本的なログ記録

1秒に1回テキストを標準出力するポッド

debug/counter-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']
$ kubectl apply -f debug/counter-pod.yaml
pod/counter created
$ kubectl get pod -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP            NODE   NOMINATED NODE   READINESS GATES
counter   1/1     Running   0          14s   10.244.1.84   kb2    <none>           <none>
$ kubectl describe pod counter
Name:               counter
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               kb2/192.168.0.102
Start Time:         Mon, 11 Mar 2019 19:44:53 +0900
Labels:             <none>
Annotations:        cni.projectcalico.org/podIP: 10.244.1.84/32
                    kubectl.kubernetes.io/last-applied-configuration:
                      {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"counter","namespace":"default"},"spec":{"containers":[{"args":["/bin/...
Status:             Running
IP:                 10.244.1.84
Containers:
  count:
    Container ID:  docker://5e5c7d509ce1bfadac97749fb855f5771042fce001972efb7b1874cf769fbb2f
    Image:         busybox
    Image ID:      docker-pullable://busybox@sha256:061ca9704a714ee3e8b80523ec720c64f6209ad3f97c0ff7cb9ec7d19f15149f
    Port:          <none>
    Host Port:     <none>
    Args:
      /bin/sh
      -c
      i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done

pod:counter のログを確認

$ kubectl log counter
log is DEPRECATED and will be removed in a future version. Use logs instead.
0: Mon Mar 11 10:44:59 UTC 2019
1: Mon Mar 11 10:45:00 UTC 2019
2: Mon Mar 11 10:45:01 UTC 2019
3: Mon Mar 11 10:45:02 UTC 2019
4: Mon Mar 11 10:45:03 UTC 2019
5: Mon Mar 11 10:45:04 UTC 2019
6: Mon Mar 11 10:45:05 UTC 2019
7: Mon Mar 11 10:45:06 UTC 2019
8: Mon Mar 11 10:45:07 UTC 2019
9: Mon Mar 11 10:45:08 UTC 2019
10: Mon Mar 11 10:45:09 UTC 2019
:

ノード上のログを確認してみる

kb2 の /var/log/containers を確認。counter_default_count-5e5c7d50... を追跡する。/var/log/pods/b460364c-43ea-11e9-8f5d-9ca3ba319985/count/0.log. にリンクしている

$ ls -al /var/log/containers
total 80
drwxr-xr-x. 2 root root 12288 Mar 11 19:44 .
drwxr-xr-x. 7 root root  4096 Jan 25 20:38 ..
lrwxrwxrwx. 1 root root    68 Feb 28 23:58 calico-node-jdbbb_kube-system_calico-node-44f1f3e3277039d626404d7f4f4f9bdb97349b762ba38ce6e4fac50cf21a1c2b.log -> /var/log/pods/a206ccdc-2f85-11e9-83f4-9ca3ba319985/calico-node/3.log
lrwxrwxrwx. 1 root root    68 Feb 24 16:03 calico-node-jdbbb_kube-system_calico-node-80765d945664af23866537c55b7dcfead8a0ce3e8e9649494621f35104fd44c9.log -> /var/log/pods/a206ccdc-2f85-11e9-83f4-9ca3ba319985/calico-node/2.log
lrwxrwxrwx. 1 root root    68 Feb 24 16:03 calico-node-jdbbb_kube-system_install-cni-3c4e153bae5aa417322473e704275c3ec5a2e541a4fdafa022e2d92009183bfe.log -> /var/log/pods/a206ccdc-2f85-11e9-83f4-9ca3ba319985/install-cni/2.log
lrwxrwxrwx. 1 root root    68 Feb 28 23:58 calico-node-jdbbb_kube-system_install-cni-4799ede6da39807478b6316ce18957d255575132d620d524a06b6e98566e8948.log -> /var/log/pods/a206ccdc-2f85-11e9-83f4-9ca3ba319985/install-cni/3.log
lrwxrwxrwx. 1 root root    62 Mar 11 19:44 counter_default_count-5e5c7d509ce1bfadac97749fb855f5771042fce001972efb7b1874cf769fbb2f.log -> /var/log/pods/b460364c-43ea-11e9-8f5d-9ca3ba319985/count/0.log
lrwxrwxrwx. 1 root root    67 Feb 28 23:58 kube-proxy-x6gbc_kube-system_kube-proxy-2acc29cfc69b74874faa11d4c9a793a7a43b9925575c112a4015f7af39ee09c8.log -> /var/log/pods/a204dab0-2f85-11e9-83f4-9ca3ba319985/kube-proxy/4.log
lrwxrwxrwx. 1 root root    67 Feb 24 16:03 kube-proxy-x6gbc_kube-system_kube-proxy-942c1bd44c48442bfe17c9add49cbad796ac224f00fb89b2e2eabe834739de05.log -> /var/log/pods/a204dab0-2f85-11e9-83f4-9ca3ba319985/kube-proxy/3.log

/var/log/pods には b460364c-43ea-11e9-8f5d-9ca3ba319985 があって...

$ ls -al /var/log/pods
total 40
drwxr-xr-x. 5 root root 4096 Mar 11 19:44 .
drwxr-xr-x. 7 root root 4096 Jan 25 20:38 ..
drwxr-xr-x. 3 root root 4096 Feb 13 20:51 a204dab0-2f85-11e9-83f4-9ca3ba319985
drwxr-xr-x. 4 root root 4096 Feb 13 20:51 a206ccdc-2f85-11e9-83f4-9ca3ba319985
drwxr-xr-x. 3 root root 4096 Mar 11 19:44 b460364c-43ea-11e9-8f5d-9ca3ba319985

/var/log/pods/b460364c-.../ には count があって...

$ ls -al /var/log/pods/b460364c-43ea-11e9-8f5d-9ca3ba319985
total 24
drwxr-xr-x. 3 root root 4096 Mar 11 19:44 .
drwxr-xr-x. 5 root root 4096 Mar 11 19:44 ..
drwxr-xr-x. 2 root root 4096 Mar 11 19:44 count

/var/log/pods/b460364c-.../count/ には 0.log があって /var/lib/docker/containers/5e5c7d5.../5e5c7d5...-json.log にリンクしている

$ ls -al /var/log/pods/b460364c-43ea-11e9-8f5d-9ca3ba319985/count
total 24
drwxr-xr-x. 2 root root 4096 Mar 11 19:44 .
drwxr-xr-x. 3 root root 4096 Mar 11 19:44 ..
lrwxrwxrwx. 1 root root  165 Mar 11 19:44 0.log -> /var/lib/docker/containers/5e5c7d509ce1bfadac97749fb855f5771042fce001972efb7b1874cf769fbb2f/5e5c7d509ce1bfadac97749fb855f5771042fce001972efb7b1874cf769fbb2f-json.log

/var/lib/docker/containers/5e5c7d5.../5e5c7d5...-json.log の内容を確認。json で log フィールドに記録されていた。

$ sudo tail /var/lib/docker/containers/5e5c7d509ce1bfadac97749fb855f5771042fce001972efb7b1874cf769fbb2f/5e5c7d509ce1bfadac97749fb855f5771042fce001972efb7b1874cf769fbb2f-json.log
{"log":"707: Mon Mar 11 10:56:47 UTC 2019\n","stream":"stdout","time":"2019-03-11T10:56:47.219187708Z"}
{"log":"708: Mon Mar 11 10:56:48 UTC 2019\n","stream":"stdout","time":"2019-03-11T10:56:48.220973929Z"}
{"log":"709: Mon Mar 11 10:56:49 UTC 2019\n","stream":"stdout","time":"2019-03-11T10:56:49.222537805Z"}
{"log":"710: Mon Mar 11 10:56:50 UTC 2019\n","stream":"stdout","time":"2019-03-11T10:56:50.223903951Z"}
{"log":"711: Mon Mar 11 10:56:51 UTC 2019\n","stream":"stdout","time":"2019-03-11T10:56:51.225434989Z"}
{"log":"712: Mon Mar 11 10:56:52 UTC 2019\n","stream":"stdout","time":"2019-03-11T10:56:52.226873482Z"}
{"log":"713: Mon Mar 11 10:56:53 UTC 2019\n","stream":"stdout","time":"2019-03-11T10:56:53.22864989Z"}
{"log":"714: Mon Mar 11 10:56:54 UTC 2019\n","stream":"stdout","time":"2019-03-11T10:56:54.230080744Z"}
{"log":"715: Mon Mar 11 10:56:55 UTC 2019\n","stream":"stdout","time":"2019-03-11T10:56:55.231569155Z"}
{"log":"716: Mon Mar 11 10:56:56 UTC 2019\n","stream":"stdout","time":"2019-03-11T10:56:56.233325322Z"}

springboot+docker イメージのサイズ縮小

前回の続き。イメージのサイズの縮小を試す。今回は MultiStageBuild の必要性が理解できている。

hirokimatsumoto/alpine-openjdk-11 + alpine:3.8

ここ を参考にした。

イメージサイズ: 78.7MB。これは小さい。

Dockerfile.hiroki

FROM hirokimatsumoto/alpine-openjdk-11:latest as jlink-package
# First: generate java runtime module by jlink.

RUN jlink \
     --module-path /opt/java/jmods \
     --compress=2 \
     --add-modules jdk.jfr,jdk.management.agent,java.base,java.logging,java.xml,jdk.unsupported,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument \
     --no-header-files \
     --no-man-pages \
     --output /opt/jdk-11-mini-runtime

# Second: generate run image.
FROM alpine:3.8

ENV JAVA_HOME=/opt/jdk-11-mini-runtime
ENV PATH="$PATH:$JAVA_HOME/bin"

COPY --from=jlink-package /opt/jdk-11-mini-runtime /opt/jdk-11-mini-runtime

RUN mkdir /app
COPY ./build/libs/kbhello-0.1.0.jar /app
EXPOSE 8080
ENTRYPOINT [ "java", "-jar", "/app/kbhello-0.1.0.jar" ]
docker build -t hiroki .
docker run -it --rm -p 8080:8080 hiroki

openjdk:8-jdk-alpine

イメージサイズ: 121MB

Dockerfile.openjdk8alp

FROM openjdk:8-jdk-alpine
RUN mkdir /app
COPY ./build/libs/kbhello-0.1.0.jar /app
ENTRYPOINT [ "java", "-jar", "/app/kbhello-0.1.0.jar"]
docker build -t openjdk8alp .
docker run -it --rm -p 8080:8080 openjdk8alp

openjdk:11-jdk-slim

イメージサイズ: 490MB

Dockerfile.openjdk11slim

FROM openjdk:11-jdk-slim
RUN mkdir /app
COPY ./build/libs/kbhello-0.1.0.jar /app
ENTRYPOINT [ "java", "-jar", "/app/kbhello-0.1.0.jar"]
docker build -t openjdk11slim .
docker run -it --rm -p 8080:8080 openjdk11slim

adoptopenjdk/openjdk11

ここadoptopenjdk を知る。 まず How to use this Image のようにイメージ作成。 イメージサイズ: 458MB

Dockerfile

FROM adoptopenjdk/openjdk11:latest

RUN mkdir /app
COPY ./build/libs/kbhello-0.1.0.jar /app
ENTRYPOINT [ "java", "-jar", "/app/kbhello-0.1.0.jar"]
docker build -t adopt1 .
docker run -it --rm -p 8080:8080 adopt1

adoptopenjdk/openjdk11:x86_64-alpine-jre-11.0.2.9

イメージサイズ: 153MB

Dockerfile.adoptjre11alp

FROM adoptopenjdk/openjdk11:x86_64-alpine-jre-11.0.2.9

RUN mkdir /app
COPY ./build/libs/kbhello-0.1.0.jar /app
ENTRYPOINT [ "java", "-jar", "/app/kbhello-0.1.0.jar"]
docker build -t adoptjre11alp .
docker run -it --rm -p 8080:8080 adoptjre11alp

サイズ比較

$ docker images
REPOSITORY    SIZE
hiroki        78.7MB
openjdk8alp   121MB
openjdk11slim 490MB
adopt1        458MB
adoptjre11alp 153MB

参考

JDK11難民は続く。