投稿日:2020年10月15日
KubernetesではPod間の通信や外部へのアプリケーションの公開にServiceというリソースを使用します。Serviceにはいろいろなタイプがあります。この記事ではServiceについて実践を交えつつ解説していきます。
KubernetesではPod間の通信や外部へのアプリケーションの公開にServiceというリソースを使用します。Serviceにはいろいろなタイプがあります。
この記事ではServiceについて実践を交えつつ解説していきます。
まずは一番基本的なタイプのClusterIPのServiceを作成します。このサービスを作成することでPodからPodへアクセスするための内部IPを定義することができます。
これによって複数のPodに対してクラスター内部のPodのみ単一のIPアドレスでアクセスできます。
例えばmysqlサーバーやRedisサーバーなどは外部から直接アクセスすることはなく、アプリケーションサーバーなどからのみアクセスできるようにしておけば良いためこのClusterIpのServiceを使用します。
yamlファイルを作成します。このyamlファイルではServiceとDeploymentの定義を一つのyamlファイルで定義します(詳しくは【【Kubernetes】1つのyamlファイルで複数のリソースの定義をする方法】)。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: webapp1-clusterip-pod
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp1-clusterip-deployment
spec:
replicas: 2
selector:
matchLabels:
app: webapp1-clusterip-pod
template:
metadata:
labels:
app: webapp1-clusterip-pod
spec:
containers:
- name: webapp1-clusterip-pod
image: nginx:1.18.0-alpine
ports:
- containerPort: 80
まずはyamlの後半部分ではapp=webapp1-clusterip-podというlabelを持ったPodのReplicaSetとそれを管理するDeploymentを作成する定義です。
前半部分の定義ではDeploymentで管理されるPodのポート80へのプロキシのServiceを定義しています。
ここからリソースを作成します。
$ kubectl apply -f clusterip.yaml
確認してみます。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 21m
my-service ClusterIP 10.107.205.136 <none> 80/TCP 16m
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
webapp1-clusterip-deployment-6cff5d45 2 2 2 16m
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
webapp1-clusterip-deployment-6cff5d45-6795q 1/1 Running 0 16m
webapp1-clusterip-deployment-6cff5d45-7dbbm 1/1 Running 0 16m
ServiceのTypeは複数存在しますが、今回の様に.spec.typeに何も指定しなかった場合には今回の様にClusterIPタイプのサービスが作成されます。
公式の定義によると
クラスター内部のIPでServiceを公開します。このタイプではServiceはクラスター内部からのみ疎通性があります。
このServiceを通じてPodに、Minikubeのホストからアクセスしてみます。
$ minikube ssh
$ curl 10.107.205.136
<!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のサンプルのレスポンスを受け取ることができました。
sshから退出してServiceとDeploymentを削除しておきます。
$ exit
$ kubectl delete -f clusterip.yml
次にNodePortタイプのServiceを作成してみます。NodePortタイプについて公式ドキュメントでは以下のように説明されています。
【公式の説明】
各NodeのIPにて、静的なポート(NodePort)上でServiceを公開します。そのNodePort のServiceが転送する先のClusterIP Serviceが自動的に作成されます。<NodeIP>:<NodePort>にアクセスすることによってServiceにアクセスできるようになります。
つまり基本的に外部に公開するPodへのアクセスを定義するServiceです。
ClusterIPが自動的に作成されるため、内部からのアクセスも外部からのアクセスもどちらも可能です。
例えばDjangoなどのアプリケーションサーバーなどの公開にはこれを使用します。
実際にNodePortサービスのyamlファイルを作ってみます。
apiVersion: v1
kind: Service
metadata:
name: webapp1-nodeport-svc
labels:
app: webapp1-nodeport-svc
spec:
type: NodePort
ports:
- port: 80
nodePort: 30080
selector:
app: webapp1-nodeport-pod
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp1-nodeport-deployment
spec:
replicas: 2
selector:
matchLabels:
app: webapp1-nodeport-pod
template:
metadata:
labels:
app: webapp1-nodeport-pod
spec:
containers:
- name: webapp1-nodeport-container
image: nginx:1.18.0-alpine
ports:
- containerPort: 80
---
まずはyamlの後半部分ではapp=webapp1-nodeport-podというlabelを持ったPodのReplicaSetとそれを管理するDeploymentを作成する定義です。
前半部分の定義ではDeploymentで管理されるPodのポート80にプロキシするNodePortタイプのServiceを定義しています。
これを適用させることでNodeの30080番ポートに対してapp= webapp1-nodeport-podというラベルのPodを公開できます。
$ kubectl apply -f nodeport.yaml
確認してみましょう。
$ kubectl apply -f nodeport.yaml
service/webapp1-nodeport-svc configured
deployment.apps/webapp1-nodeport-deployment unchanged
(base) ogihara@ubuntu:~/kubernetes_sample/networking$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
webapp1-nodeport-deployment 2/2 2 2 12m
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
webapp1-nodeport-deployment-675d7896c6 0 0 0 12m
webapp1-nodeport-deployment-68fddbcc5f 2 2 2 8m21s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
webapp1-nodeport-deployment-68fddbcc5f-cfk5m 1/1 Running 0 8m23s
webapp1-nodeport-deployment-68fddbcc5f-vhp5m 1/1 Running 0 8m28s
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 103m
webapp1-nodeport-svc NodePort 10.103.32.197 <none> 80:30080/TCP 8m37s
それぞれのリソースは問題なく動いているようです。
実際にサービスにアクセスできるかどうか確認してみます。
NodePortのServiceには<NodeのIPアドレス>:<NodePort>でアクセスできます。これら値の確認方法について詳しくは【Kubernetes】ServiceのIPアドレスとポートについてを参照してください。
ブラウザでアクセスしてみると以下のようなNginxのデフォルトページが表示されます。
最後にServiceとDeploymentを削除しておきます。
$ kubectl delete -f nodeport.yaml
複数のNodeクラスターがある場合は、ClusterIPタイプのIPのExternalIPを使用することでもServiceを公開させることができます。
ExternalIPにはNodeのIPを指定します。
今回使用しているMinikubeは仮想的マシンで単一ノードのKubernetesクラスターを形成しているため、まずはローカルにおけるこのNodeのローカルIPアドレスを取得します。
$ minikube ip
192.168.39.91
次にyamlファイルを作成します。
apiVersion: v1
kind: Service
metadata:
name: webapp1-externalip-svc
labels:
app: webapp1-externalip-svc
spec:
type: ClusterIP
ports:
- port: 80
externalIPs:
- 192.168.39.91
selector:
app: webapp1-externalip-pod
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp1-externalip-deployment
spec:
replicas: 2
selector:
matchLabels:
app: webapp1-externalip-pod
template:
metadata:
labels:
app: webapp1-externalip-pod
spec:
containers:
- name: webapp1-externalip-app
image: nginx:1.18.0-alpine
ports:
- containerPort: 80
---
基本的にClusterIPの節で解説した内容とほとんど同じです。
ここでは新しくServiceのディレクティブの.spec.externalIPsでNodeのIPアドレスを指定しています。もしここで複数のNodeのIPアドレスを指定した場合はそれらのアドレスで公開することができます。
$ kubectl describe svc webapp1-externalip-svc
Name: webapp1-externalip-svc
Namespace: default
Labels: app=webapp1-externalip-svc
Annotations: <none>
Selector: app=webapp1-externalip-pod
Type: ClusterIP
IP: 10.105.182.125
External IPs: 192.168.39.91
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 172.17.0.3:80,172.17.0.4:80
Session Affinity: None
Events: <none>
ローカルのブラウザから確認してみるとnginxのサンプルページが確認できます。
よくクラウドのGCPのComputeEngineやAWSのEC2インスタンスでサーバーを立てた際に、クラウドのロードバランサーサービスを通して公開したりします。KubernetesのServiceにはLoadBalacerタイプが用意されています。GKEなどのクラウドのKubernetesエンジンではこのタイプが指定された際にはクラウドのロードバランサーが自動的に使用されます。
今回はそのままローカルでロードバランサーを作成してみます。
apiVersion: v1
kind: Service
metadata:
name: webapp1-loadbalancer-svc
labels:
app: webapp1-loadbalancer-svc
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
selector:
app: webapp1-loadbalancer-pod
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp1-loadbalancer-deployment
spec:
replicas: 2
selector:
matchLabels:
app: webapp1-loadbalancer-pod
template:
metadata:
labels:
app: webapp1-loadbalancer-pod
spec:
containers:
- name: webapp1-loadbalancer-app
image: nginx:1.18.0-alpine
ports:
- containerPort: 80
---
$ kubectl apply -f loadbalancer.yaml
確認してみます。
$ kubectl describe svc webapp1-loadbalancer-svc
Name: webapp1-loadbalancer-svc
Namespace: default
Labels: app=webapp1-loadbalancer-svc
Annotations: <none>
Selector: app=webapp1-loadbalancer-pod
Type: LoadBalancer
IP: 10.110.242.134
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30515/TCP
Endpoints: 172.17.0.3:80,172.17.0.4:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
$ minikube service webapp1-loadbalancer-svc --url
http://192.168.39.91:30515
ローカルのマシンからはhttp://192.168.39.91:30515でアクセスできます。
今回はNetworkingについて学習しました。
一番よく使うことになるのはLoadBalancerタイプかな?クラウドで使う場合にはもう少し細かい設定があるみたいです。後々そのあたりも勉強していきます。