連載目次
- KubernetesクラスターでIPv4/IPv6 Dual-stackサポートを有効にする
- IPv6チェック用のイメージを作成する
- IPv4/IPv6でサービスを動かすコンテナイメージを使ってKubernetesで実行 (今回)
イメージを使ってアプリケーションを実行してみる
前回、テストに利用するイメージを作成してDockerで動作確認しました。 このイメージをKubernetesで使うには、イメージを何らかの方法で「共有」する必要があります。 今回はDocker Hubに登録する方法を試してみます。
Docker Desktopを使ったことがあるユーザーであれば、Dockerのアカウントは作成済みだと思います。 そのアカウントでDocker Hubサイトを開き、ログインしてAccess Tokensを作っておきましょう。 DockerアカウントのTwo-Factor Authenticationが未だ設定されていない場合は、設定しておいたほうが良いでしょう。
ユーザー名とパスワードを聞かれるので、ユーザー名を入力し、パスワードは作成しておいた「Access Tokens」を入力します。
% docker login
ログインできたら、あとは作成しておいたイメージをDocker HubにPushするだけです。
タグを設定時に、Docker Hubユーザー名/イメージ名:タグ名
のようにイメージにタグ付けします。
% docker image tag localhost/python-webserver ouruser/python-webserver:latest % docker image push ouruser/python-webserver:latest
上記は現在実行中のCPUアーキテクチャー向けにコンテナーイメージを作成してイメージを登録する例です。
例えばあなたのマシンがaarch64なマシンであり、上記のようにイメージをビルドして登録した場合、aarch64のCPUを備えたマシンを使っているユーザーしか利用できません。amd64やarm/v7などでも利用できるようにしたい場合は次のドキュメントを参考に、multi-archなイメージを作成します(docker buildxを使うと簡単です)。
作成したイメージ例:
作成したイメージを使ってPodを作成します。手元環境で動かす場合は、タグがlatestではない点にご注意ください。 なお、このイメージを使って起こった問題について、筆者は一切責任を負いません。
$ cat testpod2.yaml --- apiVersion: apps/v1 kind: Deployment metadata: name: ubi9app1 spec: replicas: 1 selector: matchLabels: app: ubi9app1 template: metadata: labels: app: ubi9app1 spec: containers: - name: ubi9app1 image: docker.io/ytooyama/ubi9-pythonweb-multiarch-example:buildx-latest ports: - containerPort: 8000 --- apiVersion: v1 kind: Service metadata: name: ubi9app1-np labels: app: ubi9app1 spec: type: NodePort ports: - port: 8000 targetPort: 8000 nodePort: 31800 selector: app: ubi9app1
Podとサービスを作成します。
$ kubectl create -f testpod2.yaml
確認します。今回はDeployment APIとService APIを使って、PodとNodePortサービスを作成しています。 それぞれエラーが出ていないことを確認します。
$ kubectl get -f testpod2.yaml NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/ubi9app1 1/1 1 1 17s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/ubi9app1-np NodePort 10.96.196.55 <none> 8000:31800/TCP 17s $ kubectl get pods NAME READY STATUS RESTARTS AGE ubi9app1-86d6858fc4-vkwbc 1/1 Running 0 2m22s
IPアドレスを確認します。PodにはIPv4とIPv6のアドレスが割り当てられています。
$ kubectl describe pods/ubi9app1-86d6858fc4-vkwbc ... IP: 10.244.78.136 IPs: IP: 10.244.78.136 IP: 2001:db8:42:c5:45e3:dcb6:dab7:e247
ホスト上であれば、curlコマンドを使ってアクセスできます。
$ curl http://10.244.78.136:8000 $ curl http://[2001:db8:42:c5:45e3:dcb6:dab7:e247]:8000
サービスを確認します。
$ kubectl get svc/ubi9app1-np NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ubi9app1-np NodePort 10.96.46.56 <none> 8000:31800/TCP 7m39s
何も書かない場合、IPv4のSingleStackでサービスが作られるようです。
$ kubectl describe svc/ubi9app1-np Name: ubi9app1-np Namespace: default Labels: app=ubi9app1 Annotations: <none> Selector: app=ubi9app1 Type: NodePort IP Family Policy: SingleStack IP Families: IPv4 IP: 10.96.46.56 IPs: 10.96.46.56 Port: <unset> 8000/TCP TargetPort: 8000/TCP NodePort: <unset> 31800/TCP Endpoints: 10.244.78.136:8000 Session Affinity: None External Traffic Policy: Cluster Events: <none>
そのため、サービスを通じてアプリケーションのアクセスをする場合、このままではIPv6ではアクセスできません。
IPv6を考慮したKubernetesのサービスの作り方は、Validate IPv4/IPv6 dual-stackにあります。
先程のサービス部分にipFamilies
を追加して、IPv6を指定してみます。
--- apiVersion: v1 kind: Service metadata: name: ubi9app1-np labels: app: ubi9app1 spec: ipFamilies: - IPv6 type: NodePort ports: - port: 8000 targetPort: 8000 nodePort: 31800 selector: app: ubi9app1
サービスを作ると、Cluster IPがIPv6アドレスが設定されていることがわかります。
$ kubectl get -f testpod2.yaml NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/ubi9app1 1/1 1 1 6s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/ubi9app1-np NodePort 2001:db8:42:1::d691 <none> 8000:31800/TCP 6s
サービスの詳細を見ると、IPv6のSingleStackなサービスが作られたことがわかります。
$ kubectl describe service/ubi9app1-np Name: ubi9app1-np Namespace: default Labels: app=ubi9app1 Annotations: <none> Selector: app=ubi9app1 Type: NodePort IP Family Policy: SingleStack IP Families: IPv6 IP: 2001:db8:42:1::d691 IPs: 2001:db8:42:1::d691 Port: <unset> 8000/TCP TargetPort: 8000/TCP NodePort: <unset> 31800/TCP Endpoints: [2001:db8:42:c5:45e3:dcb6:dab7:e24c]:8000 Session Affinity: None External Traffic Policy: Cluster Events: <none>
NodePortでアクセスすると、IPv4ではアクセスできずにIPv6ではアクセスできることが確認できます。
$ curl -4 http://k8s.example.com:31800 curl: (7) Failed to connect to k8s.example.com port 31800 after 0 ms: Connection refused $ curl -6 http://k8s.example.com:31800 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Directory listing for /</title> </head> <body> <h1>Directory listing for /</h1> <hr> <ul> <li><a href="afs/">afs/</a></li> <li><a href="bin/">bin@</a></li> <li><a href="boot/">boot/</a></li> <li><a href="dev/">dev/</a></li> <li><a href="etc/">etc/</a></li> <li><a href="home/">home/</a></li> <li><a href="lib/">lib@</a></li> <li><a href="lib64/">lib64@</a></li> <li><a href="lost%2Bfound/">lost+found/</a></li> <li><a href="media/">media/</a></li> <li><a href="mnt/">mnt/</a></li> <li><a href="opt/">opt/</a></li> <li><a href="proc/">proc/</a></li> <li><a href="root/">root/</a></li> <li><a href="run/">run/</a></li> <li><a href="sbin/">sbin@</a></li> <li><a href="srv/">srv/</a></li> <li><a href="sys/">sys/</a></li> <li><a href="tmp/">tmp/</a></li> <li><a href="usr/">usr/</a></li> <li><a href="var/">var/</a></li> </ul> <hr> </body> </html>
-v
をつけて実行すると、IPv6アドレスでアクセスされていることが確認できます(必要な部分のみ抜粋)。
$ curl -v -6 http://k8s.example.com:31800 * Trying fd66:292e:d1ca:e484:f864:d3ff:fe08:7480:31800... * Connected to k8s.example.com (fd66:292e:d1ca:e484:f864:d3ff:fe08:7480) port 31800 (#0) > GET / HTTP/1.1 > Host: k8s.example.com:31800
サービス部分をさらに次のように改変してみます。
--- apiVersion: v1 kind: Service metadata: name: ubi9app1-np labels: app: ubi9app1 spec: ipFamilyPolicy: PreferDualStack type: NodePort ports: - port: 8000 targetPort: 8000 nodePort: 31800 selector: app: ubi9app1
CLUSTER-IPはIPv4のIPアドレスがみられますが、describeで確認すると、サービスに対してIPv4とIPv6のIPアドレスが設定されていることがわかります。
$ kubectl get -f testpod3.yaml NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/ubi9app1 1/1 1 1 4s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/ubi9app1-np NodePort 10.96.18.75 <none> 8000:31800/TCP 4s $ kubectl describe service/ubi9app1-np Name: ubi9app1-np Namespace: default Labels: app=ubi9app1 Annotations: <none> Selector: app=ubi9app1 Type: NodePort IP Family Policy: PreferDualStack IP Families: IPv4,IPv6 IP: 10.96.18.75 IPs: 10.96.18.75,2001:db8:42:1::2469 Port: <unset> 8000/TCP TargetPort: 8000/TCP NodePort: <unset> 31800/TCP Endpoints: 10.244.78.143:8000 Session Affinity: None External Traffic Policy: Cluster Events: <none>
早速アクセスしてみましょう。今度はIPv4とIPv6のアドレスがサービスに設定されているため、どちらも成功するはずです。
$ curl -v -4 http://k8s.example.com:31800 $ curl -v -6 http://k8s.example.com:31800
curlのオプションに-v
と-4
や-6
をつけて、IPv4とIPv6のアドレスでアプリケーションにアクセスできていることを確認します。
$ curl -v -4 http://k8s.example.com:31800 * Trying 192.168.205.109:31800... * Connected to k8s.example.com (192.168.205.109) port 31800 (#0) > GET / HTTP/1.1 > Host: k8s.example.com:31800 > User-Agent: curl/7.81.0 > Accept: */* ... $ curl -v -6 http://k8s.example.com:31800 * Trying fd66:292e:d1ca:e484:f864:d3ff:fe08:7480:31800... * Connected to k8s.example.com (fd66:292e:d1ca:e484:f864:d3ff:fe08:7480) port 31800 (#0) > GET / HTTP/1.1 > Host: k8s.example.com:31800 > User-Agent: curl/7.81.0 > Accept: */* ...
今回、IPv4とIPv6アドレスをKubernetesで使う、IPv4/IPv6 dual-stackを試してみました。 今回、パブリックにある既存のイメージを使ってPodを使わなかった理由は、イメージによってIPv6での利用が想定されていないイメージが有り、そのようなイメージでは今回のようなテストができない可能性があるという情報があったためです。イメージの作成については、Red HatのUBI9イメージがPython3をバンドルしてくれていたのでそんなに手間がかからずなんとかなりました。
KubernetesでIPv4とIPv6のDualStack環境を作るのは公式ドキュメントがあるのでそこまで難しくはありませんが、この環境を作るには以下の考慮が必要になると思います。
- 利用するIPv6アドレスの範囲
- IPv6アドレスの配布、決定方法
- 利用するコンテナイメージのIPv6アドレス対応
- 利用するコンテナネットワークインターフェイス(CNI)プラグインはIPv6対応しているか、DualStackに対応しているか