LoginSignup
33
21

More than 5 years have passed since last update.

開発用マシンからKubernetesのクラスタ内ネットワークに透過的にアクセスする

Last updated at Posted at 2017-06-13

モチベーション

  • マイクロサービス化を進めたり、サービスで利用しているミドルウェアが多くなると、ローカル環境にすべての依存サービス、ミドルウェアを同居させることがリソース的にきびしくなります
    • 基本的にすべてをAWS等のクラウドプロバイダ上にデプロイして、開発対象のサービスだけ置き換えられるといいですよね。しかも高速に。
    • Kubernetesならできます。

TL;DR;

  • minikubeとそれ以外でハマりどころが違いました。
  • ややこしいことに、K8Sと開発マシン、どちらからどちらへの通信を可能にするのか、もしくは両方なのか、によってやることがが変わります。
  • 以下では特に説明できていませんが、通信だけでなく、K8Sクラスタ内にあるVolumeをマウントしてローカルプロセスから参照したい・・・というような場合は、Telepresence一択です

この記事で説明しないこと

登場するOSS

基本的な考え方

VPN

最もスタンダードなのは、K8Sクラスタ内ネットワークと開発用マシンのネットワークをつなぐためにVPNを使うというものです。

minikube以外

your-laptop <---vpn over internet---> K8S cluster-internal net

minikube

your-laptop <---vpn over private network---> K8S cluster-internal network

ホストオンリーネットワーク

一方で、例えば、VirtualBoxの場合はVirtualBoxが作ったvboxnetに繋がるvboxnet<N>というインタフェース経由でminikubeVMのネットワークにアクセスできます。

your-laptop <---vboxnet---> VirtualBox machine

minikubeVMはkube-proxyによって設定されたiptablesによって、VM→K8Sクラスタ内ネットワークへのルートが設定されています。かつ、minikubeVMはcat /proc/sys/net/ipv4/ip_forward1となることからわかるようにIPフォワードが有効化されています。

そのため、ホストマシンに、K8SクラスタのServiceやPod IPのCIDRへのパケットをminikubeVM経由でルーティングするようなルートを設定してやれば、結果的にホストマシンからK8Sクラスタ内ネットワークにアクセスすることができます。

macOS --route--> minikubeVM --route--> K8S

iptables + sshポートフォワード = sshuttle

K8Sでは、kubectlでK8SのAPIサーバに繋がる状態でありさえすれば、kubectl port-forwardコマンドでローカルポートフォワードができます。つまり、ローカルの特定ポートへのパケットをK8Sクラスタの特定podの特定ポートに転送することができます。

これを応用して、特定podをssh serverにして、開発用マシンからフォワードされたポート経由でSSHトンネルを作成し、iptablesを使ってK8Sネットワーク宛のパケットをSSHトンネル経由とpod経由でK8Sネットワークに送るようにすれば、あたかもローカルプロセスがK8Sクラスタのネットワークに繋がったような状態にできます。
sshuttleというOSSがこれを実装していて、telepresenceの-m vpn-tcpはこれを利用しています。

参考: Telepresence - How it works

minikube以外の場合

開発マシン→K8S, K8S→開発マシン,

telepresence

以下の欠点に目を瞑れるのであればtelepresenceを使いましょう。

  • telepresenceの設計思想的に、また実際telepresenceのスタンダードなメソッド(inject-tcp)は特に、特定プロセスからK8Sクラスタ内のIPアドレスやDNS名が見えるようにします。
    • 結果的に、「開発機で動いているWebアプリを、クラスタ内のDNS名でChromeからアクセス」というようなことはできません。
    • IngressなりNodePort and/or LoadBalancerを使うワークアラウンドもありですが、ちょっと面倒かもしれません

kube-openvpn

TODO

minikubeの場合

VirtualBox前提で説明しますが、原理的にはVMWareなどでも一緒なはず…?

kube-openvpn

開発マシン・K8S間の双方向通信ができます。

開発マシン→K8S

K8Sクラスタ外のDNS名前解決ができなくなるのでNG

試してだめだったこと

- OpenVPNサーバ側の設定
- push "redirect-gateway"を追加してみた
- push "block-outside-dns"を追加してみた

おそらく、telepresence同様、DNSルックアップがループしている?

気をつけることとしては、あえてSplit DNSにすることと、K8Sクラスタ内ネットワークのドメイン配下の名前解決をするときだけK8SのDNSを使うように、開発用マシン側の設定を変更すること。

あえてSplit DNSにする

具体的には、OpenVPN Server側の設定で以下のようにpush "block-outside-dns"push "dhcp-options DNS ..."を記述しないようにする。

前者はVPNクランアント側でホストに元々設定されていたDNSを使わないようにする(VPNサーバ側のDNSの使用を矯正する)、後者はDHCP Options経由でVPNクライアント側がVPN接続後に名前解決に使うDNSサーバの場所を強制するという設定。

どちらを設定しても、VPN接続後にホストOSからはminikubeVMから直接見えるホスト名しか解決できなくなってしまう。また、minikubeVMはminikubeVMから直接見えないホスト名についてはホストOSのDNSレゾルバーに委譲するので、ホストOSのDNSリゾルバーをこのように無効化してしまうと、minikubeVMから直接見えるホスト名以外解決できなくなってしまう。例えば、google.comがホストOSからもminikubeVMからも解決できなくなってしまう。

server ${OVPN_NETWORK_ROUTE}
topology subnet
verb ${OVPN_VERB}

# Filled by Secrets object. Use generic names
key ${EASYRSA_PKI}/private.key
ca ${EASYRSA_PKI}/ca.crt
cert ${EASYRSA_PKI}/certificate.crt
dh ${EASYRSA_PKI}/dh.pem
tls-auth ${EASYRSA_PKI}/ta.key

key-direction 0
keepalive 10 60
persist-key
persist-tun
#push "block-outside-dns"

proto ${OVPN_PROTO}
cipher ${OVPN_CIPHER}
tls-cipher ${OVPN_TLS_CIPHER}

# Rely on scheduler to do port mapping, internally always 1194
port 1194
dev tun0

user nobody
group nogroup

push "dhcp-option DOMAIN ${OVPN_K8S_DOMAIN}"
#push "dhcp-option DNS ${OVPN_K8S_DNS}"

参考: Push DNS for only a domain OpenVPN - Server Fault
Mac OS X 10.8.4 で VPNを使うときに知っておきたいこと | hiro345

開発用マシン側で、K8Sクラスタ内のDNS名を解決するときだけK8SのDNSを使う

ここまでの手順でK8S ServiceのCluster IPにVPN経由でアクセスできるようになる。
ただし、まだK8SのServiceのDNS名などは解決できる状態にははなっていない。あえてVPNサーバ側のDNS設定を利用しないようにしたから。

VPN経由でK8S ServiceのIPにアクセスできるということは、kube-dnsのIPにもアクセスできるということ。
この状態で、cluster.local以下の名前解決のためのDNSクエリがkube-dnsにフォワードされるようにすれば名前解決もできるようになる。

/etc/resolver/cluster.local

nameserver 10.0.0.10

K8S→開発マシン

kube-openvpnのRouting back to the client参照

開発マシン→K8S

telepresenceのようにLD_PRELOADを乗っ取ったりVPN over SSHを使わなくてもできる。

KubernetesのService CIDRへの静的ルート追加

sudo route -n add 10.0.0.0/24 $(minikube ip)

これでkube-dnsはじめ、K8S ServiceのCluster IPにminikubeVM経由でアクセスできるようになる。
この状態で、cluster.local以下の名前解決のためのDNSクエリがkube-dnsにフォワードされるようにすればよい。

/etc/resolver/cluster.local

nameserver 10.0.0.10

テスト

nslookupはcurlなどが使うMacOSのDNSスタックを使わないので、このアプローチのテストには使えない。以下のようにdns-sdを使う。

$ dns-sd -G v4 nginx.default.svc.cluster.local
DATE: ---Tue 13 Jun 2017---
11:30:07.937  ...STARTING...
Timestamp     A/R Flags if Hostname                               Address                                      TTL
11:30:07.939  Add     2  0 nginx.default.svc.cluster.local.       10.0.0.242                                   39

dns-sdが通るなら、curlなどでも通るはず。
試しにkubectl run & exposeで起動したnginxにアクセスしてみる。

$ curl nginx.default.svc.cluster.local
<!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>

Chromeでアクセスすることも可能。

image.png

参考リンク集

33
21
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
33
21