KubernetesのServiceを触って学ぶ

仕事でKubernetesを扱うことが不可欠になりつつあるので、最近重点的に学んでいる。Podの立て方は理解したが、Serviceというものが何をするのかわからなかったので、実際に動かして学んでみる。

今回使っているのは、Docker DesktopのKubernetes。Docker DesktopにはKubernetesが付属されており、 Preference 内の Kubernetes から enable Kubernetes を選べばよい。Docker Desktopが起動する際に、KubernetesのClusterも立ち上がるようになる。

f:id:Udomomo:20191117175710p:plain

また kubectl コマンドも同梱されており、 初期状態では docker-desktop というローカルのClusterを指している。

kubectl config get-contexts
CURRENT   NAME                 CLUSTER          AUTHINFO         NAMESPACE
*         docker-desktop       docker-desktop   docker-desktop 

Serviceの役割

Serviceとは、PodやReplicaSetに対するサービスディスカバリを提供である。KubernetesのPodはそれぞれ独自のIPアドレスを持つが、Podは一度死んだら復活せず、新しく別のPodが作られIPアドレスも変わる。その場合でも、Pod間での接続を担保するためにServiceがある。
Serviceは、Podの論理的集合に対してCluserIPという仮想のIPアドレスを割り当てる。そのIPアドレスにアクセスすることで、リクエストがkube-proxyを通じて各Podに到達できるようになる。仮にPodが死んで新しくなってもClusterIPは変わらないので、相手となるPodの状態を気にせずにリクエストを送ることができる。

Serviceを作る

まずはPodとServiceを立ててみる。設定ファイルはこちらの記事のものをお借りしつつ、少し変えている。

qiita.com

# php-apache-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-web-pod
  labels:
    app: http-app
spec:
  containers:
    - name: web-container
      image: php:7.0-apache
      ports:
        - name: http-port
          containerPort: 80
      volumeMounts:
        - name: documentroot
          mountPath: /var/www/html
  volumes:
    - name: documentroot
      hostPath:
        path: /home/username/containers/web/html
#php-apache-service.yaml
kind: Service                                                               
apiVersion: v1
metadata:
  name: http-service
  labels:
    app: http-app
spec:
  selector:
    app: http-app
  ports:
    - name: "service-port"
      protocol: "TCP"
      port: 8080
      targetPort: http-port
  type: ClusterIP

Serviceの設定ファイル内では、 selector の部分でPodのLabelを指定している。 app: http-app というラベルは、Podの設定ファイルの metadata.labels で指定してある。
また、 targetPorthttp-port となっているが、これもPodの設定ファイルの中で定義した名前である。

これを使ってオブジェクトを作り、状態を見てみる。

kubectl create -f php-apache-pod.yaml 
pod/sample-web-pod created

kubectl create -f php-apache-service.yaml 
service/http-service created
kubectl get pod --show-labels
NAME             READY   STATUS    RESTARTS   AGE     LABELS
sample-web-pod   1/1     Running   0          3h34m   app=http-app

kubectl get svc         
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
http-service   ClusterIP   10.99.212.152   <none>        8080/TCP   6s
kubernetes     ClusterIP   10.96.0.1       <none>        443/TCP    5h41m

http-service が作られ、ClusterIPが 10.99.212.152 になっている。Cluster内部ではこのIPアドレスを使うことでPodにアクセスできる。
それを確認するために、さらに詳しく調べてみる。

kubectl describe service http-service
kubectl describe svc http-service
Name:              http-service
Namespace:         default
Labels:            app=http-app
Annotations:       <none>
Selector:          app=http-app
Type:              ClusterIP
IP:                10.99.212.152
Port:              service-port  8080/TCP
TargetPort:        http-port/TCP
Endpoints:         10.1.0.5:80
Session Affinity:  None
Events:            <none>

ubectl get pods -o wide
NAME             READY   STATUS    RESTARTS   AGE     IP         NODE             NOMINATED NODE   READINESS GATES
sample-web-pod   1/1     Running   0          5h49m   10.1.0.5   docker-desktop   <none>           <none>

Serviceの Endpoints が、PodのIPアドレスに等しいことがわかる。

これをふまえて、Podの中にいるApacheサーバにアクセスしてみる。ClusterIPはClusterの中からしか使えないのでどうするか迷ったが、調べるとこのClusterの中に使い捨てのPodを立ててリクエストを送るやり方が一番楽そうだった。

kubectl run testpod --image=centos:6 --restart=Never -i --rm -- curl -s http://10.99.212.152:8080/
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /
on this server.<br />
</p>
<hr>
<address>Apache/2.4.25 (Debian) Server at 10.99.212.152 Port 8080</address>
</body></html>
pod "testpod" deleted

Apacheサーバの設定を何もしていないので403になってしまうが、リクエストを無事に届けることはできた。