1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kubernetesで作ったサービスを公開する3つの方法

1
Last updated at Posted at 2026-01-25

はじめに

オンプレや仮想マシンに構築したKubernetesのサービスをクラスターのネットワーク外に公開したい場合に、その方法がすぐに思いつかなかったり、適切な方法がわからなかったりすることが多いと思います。本記事ではそのような場合になるべく迷わないように、Kubernetesで作ったサービスを外部公開する方法をまとめました。

要約

本記事で紹介する3つの公開方法のメリットとデメリットを、表にまとめます。

方法 長所 短所
NodePort type:NodePort に指定すればよく、設定が簡単 使えるポート番号が30000-32767に限られる
externalIPs 公開するIPアドレスをマニフェストに書いて対応可能 IPアドレスを誤って設定するとトラブルになりかねない
hostNetwork Pod系リソースに設定すればよく、Serviceリソースが不要 ポート競合、ノードIP変更の可能性。capabilities追加が必要になる場合もある

これ以外でも、MetalLBやIngress Controller、Gateway APIを使う方法がある。

Kubernetesネットワークのおさらい

最初に、Kubernetesのネットワークについて簡単におさらいしておきます。

Kubernetes には、主に2種類のネットワークがあります。一つは各PodにIPアドレスを割り当てて、Pod同士が直接通信するための「Podネットワーク」、もう一つはServiceリソースに仮想的なIPアドレス(ClusterIP)を割り当て、Podの集合に対して安定した通信経路を提供する「Serviceネットワーク」(ClusterIPネットワークとも言う)です。

例えばDeploymentリソースを作るとその背後にPodが作られて、Podには指定されたCIDRからIPアドレスが自動で付与されます。さらにServiceリソースを作りPodへルーティングすると、ServiceにもClusterIPのCIDRに基づいてIPアドレスが自動で付与されます。

これにより、Kubernetesクラスターの内部ではClusterIP(またはDNSで解決される名前)やPodのIPを使って、Podへ通信できるようになります。

しかし、このままではクラスター外からはアクセスできません。仮にクラスターがローカルネットワーク上に構築されていても、そのネットワークからClusterIPやPodのIPでService/Podへアクセスしようとしても到達できません。これらのネットワークはクラスター内部にのみ存在するためです。

以下の図は、その様子を表したものです。

k8s-network-review-前提.png

このため、サービスを外部公開するには「ローカルネットワークからサービスにアクセスするための設定」が必要となります。

方法1:NodePortを使う

一つの方法は、ServiceリソースのtypeをNodePortに設定することです。NodePortのServiceを作ると、クラスター内の各ノードに対して30000–32767の範囲のポートが(自動or手動で)割り当てられ、ノードのIP:NodePortでアクセスできるようになります。

試しにkubectlコマンドで作ってみましょう。

kubectl create deployment nginx \
  -n default \
  --image=nginx \
  --replicas=2
kubectl expose deployment nginx \
  -n default \
  --type=NodePort \
  --port=80 \
  --target-port=80

以下のように、NodePortのServiceリソースが作られました。

$ kubectl get svc -n default -l app=nginx
NAME    TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
nginx   NodePort   10.106.103.249   <none>        80:31980/TCP   53s

今回はポート番号を指定しなかったので31980のポートが割り当てられていますが、 30000–32767の範囲で値を指定して固定にすることも可能です。

例えばノードが2つあり、それぞれローカルネットワーク上のIPアドレスを 192.168.1.4192.168.1.5 とすると、このサービスには次のようにアクセス可能です。

$ curl -I http://192.168.1.4:31980 2>/dev/null | head -n1
HTTP/1.1 200 OK

$ curl -I http://192.168.1.5:31980 2>/dev/null | head -n1
HTTP/1.1 200 OK

以下は、nodePortでつながる様子を表したものです。
k8s-network-review-nodePort.png

方法2:externalIPsを使う

2つ目は、ServiceリソースのexternalIPsを使って、クラスター外からアクセスするIPアドレスを直接指定する方法です。マニフェストでは、以下のように書きます。

external-ips.svc.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx-ext
  name: nginx-ext
  namespace: default
spec:
  externalIPs: # アクセスするIPのリストを書く
  - 192.168.1.4
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx

このマニフェストをもとに、Serviceリソースを作成します。

kubectl apply -f external-ips.svc.yaml

次のようなServiceリソースが作成されます。

$ kubectl get svc -n default -l app=nginx-ext
NAME        TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
nginx-ext   ClusterIP   10.108.27.41   192.168.1.4   80/TCP    33s

NodePortと異なる点は、以下です。

  • TYPEがClusterIPである
  • EXTERNAL-IPが付与されている
  • ポートはServiceと同じ80が使える

externalIPsに記載したIP 192.168.1.4 でアクセスすると、以下のように80番ポートでアクセスできます。

curl -I http://192.168.1.4:80 2>/dev/null | head -n1
HTTP/1.1 200 OK

しかしながら、NodePortでアクセスできていた 192.168.1.5externalIPs に記載していないので、アクセスできなくなります。

$ curl -I http://192.168.1.5:80
curl: (7) Failed to connect to 192.168.1.5 port 80 after 17 ms: Couldn't connect to server

192.168.1.5 からも到達可能にするには、externalIPsに追加すればOKです。

spec:
  externalIPs: # アクセスするIPのリストを書く
  - 192.168.1.4
  - 192.168.1.5 # 追加

以下が、追加したマニフェストをapplyした後の結果です。

curl -I http://192.168.1.5:80 2>/dev/null | head -n1
HTTP/1.1 200 OK

以下は、externalIPs でつながる様子を表したものです。
k8s-network-review-externalIPs.png

問題:実在しないホストのIPでもつながってしまう

試しに externalIPs に、どのホストにも割り当てられていないでたらめなIPアドレスを追加してみましょう。

spec:
  externalIPs: # アクセスするIPのリストを書く
  - 192.168.1.4
  - 192.168.2.4 # 実在しないIP

このマニフェストをapplyすると、Kubernetesのノードが存在するホストからに限りますが、なんと実在しないIPにつながってしまいます。

curl -I http://192.168.2.4:80 2>/dev/null | head -n1
HTTP/1.1 200 OK

原因は、iptablesのルーティングに関する設定で、以下のように 192.168.2.4 が書かれているからのようです。

$ sudo iptables -t nat -nL | grep 192.168.2.4
KUBE-EXT-E3ZCWTDGY4D3CK2O  6    --  0.0.0.0/0            192.168.2.4          /* default/nginx-ext external IP */ tcp dpt:80

このように、ローカルネットワークに実在しないIPに到達できてしまうとネットワークトラブルの原因になり得るため、externalIPsはリスクを理解したうえで使ったほうがよさそうです。

方法3:hostNetworkを使う

3つ目は、PodのマニフェストでhostNetworkを有効にする方法です。以下のマニフェストのように、spec.hostNetworktrueに設定します。

hostnetwork.pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-hostnetwork
  namespace: default
  labels:
    app: nginx-hostnetwork
spec:
  hostNetwork: true  # ここで設定
  dnsPolicy: ClusterFirstWithHostNet
  containers:
    - name: nginx
      image: nginx
      ports:
        - containerPort: 80
          protocol: TCP

このマニフェストを kubectl apply -f hostnetwork.pod.yaml でデプロイすると、PodにはローカルネットワークのノードのIP 192.168.1.4 が割り当てられます。

$ kubectl get po -n default nginx-hostnetwork -owide
NAME                READY   STATUS    RESTARTS   AGE   IP            NODE      NOMINATED NODE   READINESS GATES
nginx-hostnetwork   1/1     Running   0          13m   192.168.1.4   node01   <none>           <none>

この場合、Serviceリソースを使わずに、クラスターの外から直接リクエストが届きます。

$ curl -I http://192.168.1.4 2>/dev/null | head -n1
HTTP/1.1 200 OK

以下は、hostNetwork でつながる様子を表したものです。
k8s-network-review-hostNetwork.png

注意点

ポート競合や、ノードIPの変更の可能性

hostNetworkを使うPodをDeploymentで作成すると、一つのノードに複数のPodが作成されてポートの競合を起こしてしまう可能性があるため、replica数を1にする等の対策が必要です。また、外部からアクセスできるのはPodがデプロイされたノードのIPに限られ、再起動等でノードが変わるとアクセス先IPも変わる可能性があります。IPアドレスを固定するには、マニフェストで nodeSelector を設定する等の工夫が必要になります。

これらの問題をシンプルに避けるなら、DaemonSetが有効です。DaemonSetによって全ノードにPodを配置できるため、どのノードのIPからもアクセス可能になります。

ポート0-1023を使うのに、capabilities追加が必要になる可能性

先ほどの例では、nginxのコンテナがroot権限で起動して問題なく80番ポートを利用できましたが、一般ユーザーで起動するコンテナの場合、Well-Knownポートの0-1023番が使えない可能性が高いです。この場合、コンテナの securityContext に、以下のように NET_BIND_SERVICE を追加する必要があります。

securityContext:
  ...
  capabilities:
    add: ["NET_BIND_SERVICE"] # 追加
    drop: ["ALL"]

この内容はWebのみで調査したもので、実際の動作は未確認です。

(参考)その他の方法

それ以外だと、以下の方法でサービスの外出しが可能です。

  • type: LoadBalancer のServiceリソースを利用
    • MetalLB など
  • Ingress Controllerを利用
    • NGINX Ingress Controller
    • Traefik Kubernetes Ingress provider など
  • Gateway APIを利用
    • Envoy Gateway
    • Istio など

設定方法や仕組みは使うツールによって異なるので、この記事では説明を割愛いたします。

インターネットに公開するには?

これまでに説明した方法で、ローカルネットワークからKubernetesのサービスへアクセスできるようになりますが、インターネットに公開するには、クラスターの外側(ルーター/ファイアウォール/クラウド側)で外部からの通信を受けてクラスターへ転送する設定が別途必要です。

例えば自宅LANなら、ルーターでポートフォワーディング(NAT)を設定し、グローバルIPの特定ポートへの通信をクラスター内のノードのIPへ転送します。クラウド上のVMなら、セキュリティグループ/ファイアウォールで必要なポートを許可し、外部からVM(=ノード)へ到達できるようにします。あるいは、クラウドのマネージドなLoadBalancerを前段に置いて外部からの入口を用意する方法もあります。

※ 余談ですが、私の自宅LAN環境ではインターネット公開が簡単にできないことが判明しました。。。理由は、インターネット通信する際に使われるパブリックIPが、複数ユーザーで共有される「MAP-E」という方式で割り当てられているためです。こちらの記事を参考に、頑張ってVPN構築すれば突破できるかもしれません。

おわりに

私自身、このあたりの知識が曖昧だったので整理のために書きました。読んだ方にも参考になれば幸いです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?