マネージドサービスとフルマネージドサービスの違い
マネージドサービス
本来のサービスに付随する作業を、サービス事業者が請け負ってくれるサービス。
例)
- 本来のサービス:サーバホスティングサービス、付随する作業:サーバ設定、運用監視等
→ 後者のサーバの利用に付随する作業をサービス利用者側で実施しなくてよい。
フルマネージドサービス
マネージドサービスより、請け負ってくれる範囲が広いサービスで、利用者はサービスに付随する技術的な対応や管理をほぼしなくてよいもの。
例)
→ 後者のDBMS利用に付随する作業をサービス利用者側でしなくてよい。当然、DBがホストされているサーバの運用監視はやってくれるので、マネージドサービスよりもやってくれる範囲が広いといえる*1。
厳密な違いはあるのか?
マネージドサービスとフルマネージドサービスは概ね先述した違いを指すが、厳密な定義があるわけではない。したがってクラウドベンダなどのサービス事業者が「マネージドサービス」「フルマネージドサービス」と言っていても実際に利用する際は、どこまでを利用者が実施する必要があり、どこまでをサービス事業者がやってくれるかを具体的に確認するのが望ましい。自分が想像する「フルマネージドサービス」より、提供されている範囲が狭いかもしれない。
参考にしたもの
コンテナ内でコマンド実行する方法
目的
コンテナ内でコマンド実行する方法について簡単に整理する。
背景
コンテナの使い方
コンテナは仮想マシンと異なり、汎用的なOSを提供するのではなく、特定の仕事(例えばWebサービスの提供)を実行する目的で存在する*1。1コンテナ1プロセスが基本である。
では、コンテナ内で実行するプロセスはどのように指定されるのであろうか。以降でdockerを例に記載する。
コンテナ実行までの流れ
まず、実行するdockerコンテナがどのように作られるかの流れをおさらいする。
まず、Docker Hub等のコンテナレジストリから、イメージを取得する。取得したイメージをそのまま使うケースもあるが、実際にはカスタマイズがしたいので、Dockerfileと呼ばれる指示書のようなもので、そのイメージに対し各種コマンドを実行し、コンテナイメージを作成する。そのイメージをdocker run
コマンドで実行する。
コンテナ内で実行するプロセスの指定
コンテナ内で実行するプロセスは以下のいずれかの方法で指定できる。
- Dockerfile内で命令を書く
- docker run
コマンド実行時のオプションで指定する
実際は上記を組み合わせて使うことで、より柔軟に実行するプロセスを指定できる。
Dockerfileの命令とdocker run
命令のオプション
大雑把には、次のように理解できる。
- ENTRYPOINT
:docker run
実行時に行われるコマンド*2
- CMD
:コマンドのデフォルト引数
が、docker run
コマンド実行時のオプション指定と組合せると上記の表現では理解できない挙動があるのでもう少し書く。ENTRYPOINT
とCMD
の有無で場合わけする。
ここからわかる通り、基本はコマンドは1つしか実行できない。1コンテナ1プロセスという考え方ともマッチする。とはいえ、複数実行したいケースも当然ある。その方法は別記事にて記載する。
RUN
命令について
なお、DockerfileにはRUN
命令もあるが、毛色が違うので分けて考えておくのがよい。これもコンテナ内で実行するコマンドを指定しているという面では似ているが、apt-get
やyum
等、コンテナ実行のために必要なパッケージを準備するものとして使われる。コンテナが担う主のタスクそのものを実行することはできない。つまりdocker run
時には実行されない。
Kubernetesにおけるコンテナの実行
Kubernetesのpodやdeploymentのmanifestにおいて、コンテナについてcontainers:
フィールド内で記述できるが、dockerにおけるENTRYPOINTとCMDに対応するフィールドがある。
ENTRYPOINT
に対応するのがcommand:
で、CMD
に対応するのがargs:
である。CMD
とcommand
が対応するではないので注意(紛らわしい)。Kubernetesでは、コンテナを実行するときは例えばkubectl exec ポッド名/コンテナ名 -it -- 引数(コマンドやオプション)
で行うが、このときの引数部とcommand:
やargs:
の関係はdockerと同じである。
例:podのmanifest
apiVersion: v1 kind: Pod metadata: name: command-demo spec: containers: - name: command-demo-container image: ubuntu command: ["ls", "-l"] args: ["-a", "--color"]
例:実行例(ls -l -f
が実行される)
$ kubectl exec command-demo -it -- -f
参考にしたもの
Kubernetes完全ガイド impress top gearシリーズ | 青山 真也 | 工学 | Kindleストア | Amazon
Udemy - Certified Kubernetes Administrator(CKA)with Practice Tests
Docker入門(第四回)~Dockerfileについて~ | さくらのナレッジ
DockerfileのCMDとENTRYPOINTを改めて解説する - Qiita
KubernetesのServiceにおけるDNS動作の確認方法
背景・目的
Kubernetesではserviceリソースをpodやdeploymentから作成すると、そのKubernetesクラスタ内のコンテナから名前解決ができるようになる。名前解決ができることの確認方法を整理する。
準備:podとserviceの作成
まず、適当なpodを作成する。podのIPが10.244.1.6
と割り振られていることがわかる(この値は後で使う)。serviceはデフォルト(タイプはClusterIPとなる)で、ポートは適当に80番で公開することとする。
master $ kubectl run nginx-pod --image=nginx pod/nginx-pod created master $ kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-pod 1/1 Running 0 8s 10.244.1.6 node01 <none> <none> master $ kubectl expose pod nginx-pod --name=nginx-svc --port=80 service/nginx-svc exposed master $ kubectl describe svc nginx-svc Name: nginx-svc Namespace: default Labels: run=nginx-pod Annotations: <none> Selector: run=nginx-pod Type: ClusterIP IP: 10.107.129.2 Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 10.244.1.6:80 Session Affinity: None Events: <none>
serviceの名前解決の確認
確認のためのクライアントとしてbusyboxのコンテナを作成し、そのコンテナの中からnslookup
を使って名前解決を試みる。
master $ kubectl run test --image=busybox:1.28 -it -- sh If you don't see a command prompt, try pressing enter. / # nslookup nginx-svc Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: nginx-svc Address 1: 10.107.129.2 nginx-svc.default.svc.cluster.local
DNSに渡すクエリとしてサービス名のnginx-svc
だけ渡せば名前解決ができる。DNSサーバのアドレスが10.96.0.10
となっていることがわかる。これが何ものかは別記事で触れる。
なぜ、サービス名だけでよいのかは/etc/resolv.conf
を確認するとわかる。search default.svc.cluster.local svc.cluster.local cluster.local
と書かれており、補完が効くようになっている。
/ # cat /etc/resolv.conf nameserver 10.96.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5
よって、名前を途中まで入れても同様に名前解決ができる。以下では試していないが、nginx-svc.default.svc.cluster.local
とフルで入れても問題ない。
/ # nslookup nginx-svc.default Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: nginx-svc.default Address 1: 10.107.129.2 nginx-svc.default.svc.cluster.local / # nslookup nginx-svc.default.svc Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: nginx-svc.default.svc Address 1: 10.107.129.2 nginx-svc.default.svc.cluster.local
podの名前解決
pod作成時に付与されたIPアドレスがX.Y.Z.Wだとすると、X-Y-Z-W.default.podでpodに対し名前解決ができる*1。今回はpodのIPが10.244.1.6
なので、10-244-1-6.default.pod
をクエリとして投げると、podのIPが返ってきていることがわかる。
/ # nslookup 10-244-1-6.default.pod Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: 10-244-1-6.default.pod Address 1: 10.244.1.6 10-244-1-6.nginx-svc.default.svc.cluster.local
↑の一番下の行を見ると、10-244-1-6.nginx-svc.default.svc.cluster.local
がフルの名前であるようである。実際、10-244-1-6.nginx-svc
等、/etc/resolv.conf
のsearch
で補完できる投げ方ならば同様に名前解決が可能である。
/ # nslookup 10-244-1-6.nginx-svc Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: 10-244-1-6.nginx-svc Address 1: 10.244.1.6 10-244-1-6.nginx-svc.default.svc.cluster.local
補完できない形で名前解決を試みても失敗する。以下、なんとなくできそうだができない例である。
/ # nslookup 10-244-1-6 Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local nslookup: can't resolve '10-244-1-6' / # nslookup nginx-pod.nginx-svc Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local nslookup: can't resolve 'nginx-pod.nginx-svc'
逆引き
IPアドレスから逆引きすることもできる。
/ # nslookup 10.244.1.6 Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: 10.244.1.6 Address 1: 10.244.1.6 10-244-1-6.nginx-svc.default.svc.cluster.local / # nslookup 10.107.129.2 Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: 10.107.129.2 Address 1: 10.107.129.2 nginx-svc.default.svc.cluster.local
シンプルなpod / deploymentをコマンドラインで作る手順
目的
シンプルなpod / deploymentをコマンドラインで作る手順を整理する。複雑なことをしたいときは-o yaml
オプションでYAMLファイルに書き出して編集し、kubectl create -f 作成したYAMLファイル名
*1したほうがやりやすいことが多い。
バージョン
Kubernetesの1.18で確認。
master $ kubectl version Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.0", GitCommit:"9e991415386e4cf155a24b1da15becaa390438d8", GitTreeState:"clean", BuildDate:"2020-03-25T14:58:59Z", GoVersion:"go1.13.8", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.0", GitCommit:"9e991415386e4cf155a24b1da15becaa390438d8", GitTreeState:"clean", BuildDate:"2020-03-25T14:50:46Z", GoVersion:"go1.13.8", Compiler:"gc", Platform:"linux/amd64"}
kubectl runコマンド
普通にkubectl run ポッド名 --image=イメージ名
でPodを作成できる。これも以前のバージョンではエラーが出たような気がする*2
master $ kubectl run mypod --image=nginx pod/mypod created master $ kubectl get po NAME READY STATUS RESTARTS AGE mypod 1/1 Running 0 50s
--generator=run-pod/v1
をつけても同じことができる。ただし、将来的にdepricatedになる旨の警告が出る。
master $ kubectl run mypod-2 --generator=run-pod/v1 --image=nginx Flag --generator has been deprecated, has no effect and will be removed in the future. pod/mypod-2 created master $ kubectl get po NAME READY STATUS RESTARTS AGE mypod 1/1 Running 0 3m39s mypod-2 1/1 Running 0 88s
レプリカ数(deploymentに含まれるpodの数*3)をオプションで指定すれば自動でdeploymentにしてくれるかと思いきや、--replicasのオプション自体がないようである。
master $ kubectl run mydeploy --image=nginx --re(tabを押下) --record --recursive --requests --requests= --request-timeout --request-timeout= --restart --restart=
kubectl createコマンド
kubectl run
ではなくkubectl create
を使う方法もある。これで作れるリソースは下記の通りである。podは作れない。
master $ kubectl create(tabを押下) clusterrole configmap deployment namespace priorityclass role secret serviceaccount clusterrolebinding cronjob job poddisruptionbudget quota rolebinding service
いきなりレプリカ数が複数のdeploymentは作れないようである。
master $ kubectl create deployment mydeploy --image=nginx --replicas=2 Error: unknown flag: --replicas See 'kubectl create deployment --help' for usage.
したがって、レプリカ数が1のdeploymentを作成し、しかるのちにレプリカ数をkubectl scale
コマンドで増やす等の二段階のやり方になる。
master $ kubectl create deployment mydeploy --image=nginx deployment.apps/mydeploy created master $ kubectl describe deployments.apps mydeploy Name: mydeploy Namespace: default CreationTimestamp: Thu, 30 Jul 2020 03:32:06 +0000 Labels: app=mydeploy Annotations: deployment.kubernetes.io/revision: 1 Selector: app=mydeploy Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable (略)
master $ kubectl scale deployment mydeploy --replicas=2 deployment.apps/mydeploy scaled master $ kubectl describe deployments.apps mydeploy Name: mydeploy Namespace: default CreationTimestamp: Thu, 30 Jul 2020 03:32:06 +0000 Labels: app=mydeploy Annotations: deployment.kubernetes.io/revision: 1 Selector: app=mydeploy Replicas: 2 desired | 2 updated | 2 total | 1 available | 1 unavailable
いきなりレプリカ数が複数のdeploymentを作る方法だが、kubectl Cheat Sheet | Kubernetes を見ても特になさそうである。
まとめ
- podを作成する:kubectl run pod名 --image=イメージ名
- deploymentを作成する:kubectl create deployment deployment名 --image=イメージ名
ほとんどのリソースはkubectl create
で作れそうなので、podだけrunで作る、と覚えておけばよさそう。
KubernetesにおけるデフォルトのDockerレジストリ
コンテナイメージの取得元
背景
Kubernetesをインストールした状態で、pod(や、deployment)を作成することができる。このときに、コンテナのイメージは手動であらかじめダウンロードしていない。Kubernetesのインストールパッケージの中に膨大なコンテナイメージが含まれているはずもないので、インターネット越しに取ってきているはずである。どこから持ってきているのだろうか。
[root@master01 ~]# kubectl run nginx-pod --image=nginx W0729 10:19:46.316434 519 helpers.go:535] --dry-run is deprecated and can be replaced with --dry-run=client. pod/nginx-pod created
Docker Hub
結論から言うと、Docker Hubというレジストリ・サービスがあり、特に取得するレジストリを指定しない場合はそこから取ってくる*1。
Docker Hub以外のレジストリから取得する場合は明示的に指定する。試しに適当なイメージでPodを作成してみたのが次に示す例である*2。
[root@master01 ~]# kubectl run test-pod --image=quay.io/coreos/flannel:v0.12.0-amd64 pod/test-pod created [root@master01 ~]# kubectl get pod test-pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES test-pod 1/1 Running 0 31s 10.0.30.83 worker02 <none> <none>
quay.io/coreos/
というレジストリの場所を示す文字列が入っている。Docker Hubから取得する場合は--image=nginx
等のレジストリの場所を示す文字列は省略可能である。
配置対象のワーカノードにもイメージがpullされているのがわかる。REPOSITORY
の列を見るとquay.io/coreos/
と取得元のレジストリがわかるようになっている。
[root@worker02 ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE (略) quay.io/coreos/flannel v0.12.0-amd64 4e9f801d2217 4 months ago 52.8MB (略)
参考にしたもの
*1:直接的に書かれているものが見つけられなかったので、傍証より確認した。参考リンクの1.より「kubectlコマンドでPodを作成するには、作成するPodの情報を記述したYAML形式ファイルを用意し、それを「kubectl create」コマンドの引数として与えれば良い。たとえば、「httpd」(DockerHubで公開されている、Docker公式のApache HTTP Serverを含んだコンテナイメージ)というイメージからコンテナを作成し、そのコンテナの80番ポートに外部からアクセスできるようにするには、以下のような設定ファイル(「pod-httpd.yaml」)を用意する。」という記載があった。直接的に調べるには、kubectl runの内部的な仕組みを知る必要があるが今回は割愛した。kubectl runがdocker runを呼び出し実行しているなら、ソースコードまで追わなくてもdocker側の仕様からわかる。参考リンク2.ではP.49にdocker pullを行うとDocker Hubからイメージを取ってくる図が記載されている
*2:実際にflannelをインストールする場合はflannelのイメージをただpodとして動かすのはダメなので注意。あくまでこれは適当なコンテナイメージを指定したかったためである。flannelを入れたければ、kurbenetes.io等にある手順に従い、manifestファイルをkubectl apply -fすることでインストールする必要がある。
KubernetesのServiceで出てくる各種ポートの意味
本記事の目的
KubernetesでServiceを作成するとPort
、TragetPort
、NodePort
といろいろなポートが出てくる。忘れがちなのでメモする。
[root@master01 ~]# kubectl describe svc nginx Name: nginx Namespace: default Labels: app=nginx Annotations: <none> Selector: app=nginx Type: NodePort IP: 10.108.101.153 Port: <unset> 8080/TCP TargetPort: 80/TCP NodePort: <unset> 30082/TCP Endpoints: 10.0.1.21:80 Session Affinity: None External Traffic Policy: Cluster Events: <none>
NodePort
KuberntesのServiceにはいくつかType(種類)がある。NodePort Serviceはその一つである。NodePort Serviceを作成すると、NodePortというポート番号が設定できる。
このとき、Kubernetesの各ノードのIPにアクセスできるネットワークから、以下のURLでServiceにアクセスできる*1。
http://<各ワーカノードのIP(複数ある場合、どのワーカでもよい)>:<NodePortのポート番号>
以下に例を示す。192.168.0.102
と192.168.0.103
はそれぞれワーカノードのIPアドレスである。アクセス元はワーカノードへの疎通性があるところなら何でもよいので、ここではマスターノードからアクセスする。nginxのデフォルトページの応答が返ってきていることがわかる。
[root@master01 ~]# curl http://192.168.0.102:30082 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
[root@master01 ~]# curl http://192.168.0.103:30082 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
それぞれのワーカノードで、NodePortとして設定されているポート番号(この場合は30082
)の待ち受け状況を確認する。kube-proxという文字が見えるのでkube-proxyというプロセスがNodePortのポート番号で待ち受けているのが実態とわかる。
ワーカノード1で確認。
[root@worker01 ~]# ss -antu | grep 30082 tcp LISTEN 0 128 *:30082 *:* [root@worker01 ~]# lsof -i :30082 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME kube-prox 3270 root 8u IPv4 69766 0t0 TCP *:30082 (LISTEN)
ワーカノード2で確認。
[root@worker02 ~]# ss -antu |grep 30082 tcp LISTEN 0 128 *:30082 *:* [root@worker02 ~]# lsof -i :30082 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME kube-prox 2369 root 8u IPv4 43557 0t0 TCP *:30082 (LISTEN)
Port
KubernetesでServiceを作成するとデフォルトではClusterIP*2というIPが生成される。Portは作成したServiceへClusterIP経由でアクセスする際のポート番号である。ClusterIPはKubernetesクラスター内のコンテナからアクセスできる。以下のURLでアクセス可能。
http://<ClusterIP>:<Portで指定のポート番号>
以下に例を示す。コンテナ内に入り、コンテナ内からnginxの応答が返ってきていることがわかる。この例だとServiceのアクセス先とアクセス元のコンテナが同じなのでわかりにくいが、別に他のコンテナでも同じ方法でアクセスできる。
[root@master01 ~]# kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 119d nginx NodePort 10.108.101.153 <none> 8080:30082/TCP 11h [root@master01 ~]# kubectl get pod nginx-f89759699-pksbl NAME READY STATUS RESTARTS AGE nginx-f89759699-pksbl 1/1 Running 0 2d22h [root@master01 ~]# kubectl exec nginx-f89759699-pksbl -it -- /bin/sh # curl http://10.108.101.153:8080 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
TargetPort
Serviceとして公開されたアプリケーションの処理の実体を担うコンテナがアクセスを待ち受けているポート番号である。このポートは外部からアクセスするためのURLになるものではない。http://<ClusterIP>:<Portで指定のポート番号>
でアクセスを受けたとき、コンテナから見るとTargetPortでアクセスを受けたものとして処理される*3。
確認するには、コンテナに入りhttp://localhost:<TargetPortで指定のポート番号>
にアクセスしてみればよい。
[root@master01 ~]# kubectl exec nginx-f89759699-pksbl -it -- /bin/sh # curl http://localhost:80 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
なお、アプリケーション側の設定も正しい必要がある。例えばnginxではnginxのアプリケーションが待ち受けるポートの設定が記述されている。以下の通り、80
番ポートでリッスンしており、KubernetesのServiceのTargetPortと一致していることがわかる。
# cat /etc/nginx/conf.d/default.conf server { listen 80; listen [::]:80; server_name localhost; #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} }