これは、なにをしたくて書いたもの?
- Kubernetesから、Kubernetesクラスタ外へのアクセス方法を提供するServiceを試してみようと
なんとなく、それ用のServiceがあることはぼんやりと知っていたので、今回こちらをOKD/Minishiftで試してみました。
OKD(Kubernetes)の外部リソースへのアクセス方法を提供するService
ドキュメントでいくと、このあたりです。
Integrating External Services | Developer Guide | OKD 3.11
Defining a service / Services without selectors
外部リソースに対するServiceを定義することで、以下のようなことができるようになります。
- クラスタ外にあるサーバー、例えばデータベースなどに、KubernetesのServiceの名前でアクセスが可能になる
- 外部サーバーのエンドポイントを、アプリケーションなどに直接記述しなくてよくなる
とまあ、外部のリソースであっても、Kubernetes内でアクセスかのようにコントロールできるようになるわけですね。
外部リソースに対する、ロードバランサーを立てているようなものでしょうけれど、クラスタ内のService名で解決できるところが
ポイントですね、と。
ここで利用するServiceは、接続先への定義によって次の2種類を使います。
- 接続先をIPアドレスで指定する場合 … None-Selector Service(Service+Endpoints)
- 接続先を名前で定義する場合 … ExternalName Service
今回は、両方扱います。
お題
OKD(Minishift)が稼働するサーバーとは別のサーバーに、ApacheとMySQLを立てて、こちらに対してService越しにアクセス
させてみようと思います。
ApacheおよびMySQLが動作するサーバーの情報は、以下の通り。
- IPアドレス … 192.168.0.3
- 名前 … test.server
名前は、OKD(Minishift)が動作している側のホストの「/etc/hosts」に直接記載しました。
192.168.0.3 test.server
また、外部リソースへアクセスする際のService名は以下のようにします。
Serviceの定義方法に関わらず、この名前でいきます。つまり、パターンを切り替える場合は、いったんリソースを削除します。
環境
今回の環境は、こちら。
$ minishift version minishift v1.30.0+186b034 $ oc version oc v3.11.0+0cbc58b kubernetes v1.11.0+d4cacc0 features: Basic-Auth GSSAPI Kerberos SPNEGO Server https://192.168.42.198:8443 kubernetes v1.11.0+d4cacc0
Apacheは2.4.29、MySQLは8.0.14を使用します。
Apacheは、Ubuntuにaptでインストールできるデフォルトのものです。
サンプルアプリケーション
では、先にServiceを使ってアクセスするアプリケーションを書いてみます。Node.jsで書くことにしましょう。
Expressでサーバーを書くことにして、ApacheおよびMySQLにリクエストを投げるアプリケーションにしましょう。
$ npm install express promise-mysql request-promise request
インストールされたパッケージのバージョン。
"dependencies": { "express": "^4.16.4", "promise-mysql": "^3.3.1", "request": "^2.88.0", "request-promise": "^4.2.2" }
「/apache2」でApacheに向けてリクエストを投げて、その結果をそのままレスポンスとして返す、また「/mysql」でMySQLへ
接続し、現在時刻を返す簡単なアプリケーションを作成します。
server.js
const express = require("express"); const rp = require("request-promise"); const mysql = require("promise-mysql"); const app = express(); app.get("/apache2", async (req, res) => { try { rp("http://external-apache2-service") .then(html => res.send(html)); } catch (e) { console.log(e); } }); app.get("/mysql", async (req, res) => { try { const connection = await mysql.createConnection({ host: "external-mysql-service", port: 3306, user: "kazuhira", password: "password", database: "practice" }); const rows = await connection.query('select now() as message'); res.send(`${rows[0]['message']}`); } catch (e) { console.log(e); } }); app.listen(8080);
ところで、今回はNode.jsを使っているのに対してMySQLが8.0なので、認証時にエラーにならないようにユーザー作成時に
「mysql_native_password」をつけておきました。
※MySQLの設定ファイルに記載する方法でもよいと思います
CREATE USER ... IDENTIFIED WITH mysql_native_password BY ...;
あとは、こちらをOKDにデプロイします。Routeも作成。
$ oc new-app --name nodejs-external-service [Gitリポジトリへのパス] $ oc expose svc/nodejs-external-service
以上で、アプリケーションの準備は完了です。
外部リソースへのアクセスをIPアドレスで定義する(None-Selector Service/Service+Endpoints)
まずは、外部リソースへのアクセスをIPアドレスで行う方法で設定してみましょう。
作成したYAMLは、こんな感じ。Apacheへ直接アクセスするRouteもありますが、オマケです。 external-resource-endpoints.yml
--- ### Service(Apache) apiVersion: v1 kind: Service metadata: labels: app: external-apache2 name: external-apache2-service spec: ports: - name: external-apache2-http protocol: TCP port: 80 targetPort: 80 --- ### Service(MySQL) apiVersion: v1 kind: Service metadata: labels: app: external-mysql name: external-mysql-service spec: ports: - name: external-mysql protocol: TCP port: 3306 targetPort: 3306 --- ### Endpoints(Apache) apiVersion: v1 kind: Endpoints metadata: labels: app: external-apache2 name: external-apache2-service subsets: - addresses: - ip: 192.168.0.3 ports: - name: external-apache2-http port: 80 --- ### Endpoints(MySQL) apiVersion: v1 kind: Endpoints metadata: labels: app: external-mysql name: external-mysql-service subsets: - addresses: - ip: 192.168.0.3 ports: - name: external-mysql port: 3306 --- ### Route(http) apiVersion: v1 kind: Route metadata: labels: app: external-apache2 name: external-apache2-http-endpoint spec: port: targetPort: external-apache2-http to: kind: Service name: external-apache2-service
概ねこちらのドキュメントを読めばいいのですが、いくつかポイントがあります。
Integrating External Services | Developer Guide | OKD 3.11
ApacheのServiceとEndpointsを例にして、書いていきましょう。
まずは、Serviceを定義します。この時、selectorを定義しないようにします。
### Service(Apache) apiVersion: v1 kind: Service metadata: labels: app: external-apache2 name: external-apache2-service spec: ports: - name: external-apache2-http protocol: TCP port: 80 targetPort: 80
続いて、Endpointsを定義します。この時、「metadata.name」と「spec.ports.name」は、先に定義したServiceでの定義と
名前を一致させる必要があります。
apiVersion: v1 kind: Endpoints metadata: labels: app: external-apache2 name: external-apache2-service subsets: - addresses: - ip: 192.168.0.3 ports: - name: external-apache2-http port: 80
では、作成してみます。
$ oc apply -f external-resource-endpoints.yml
Serviceの確認。typeが「ClusterIP」、Selectorは設定されていません。
$ oc get svc -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR external-apache2-service ClusterIP 172.30.246.243 <none> 80/TCP 22s <none> external-mysql-service ClusterIP 172.30.179.61 <none> 3306/TCP 22s <none> nodejs-external-service ClusterIP 172.30.145.202 <none> 8080/TCP 10m app=nodejs-external-service,deploymentconfig=nodejs-external-service
Endpoints。
$ oc get ep -o wide NAME ENDPOINTS AGE external-apache2-service 192.168.0.3:80 25s external-mysql-service 192.168.0.3:3306 25s nodejs-external-service 172.17.0.6:8080 10m
確認してみましょう。
## Apache $ curl -I nodejs-external-service-myproject.192.168.42.198.nip.io/apache2 HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/html; charset=utf-8 Content-Length: 10918 ETag: W/"2aa6-B5k4N85/AnOmWyDbjumySCPafh4" Date: Sun, 27 Jan 2019 10:10:05 GMT Set-Cookie: f2a3c659b2659592c251c5e8fdf65988=71bc8243604809abfd59fec26feb237c; path=/; HttpOnly Cache-control: private ## MySQL $ curl nodejs-external-service-myproject.192.168.42.198.nip.io/mysql Sun Jan 27 2019 10:10:34 GMT+0000 (Coordinated Universal Time)
ちょっとわかりにくいですが、Node.jsのアプリケーション越しに、外部リソース上のApacheおよびMySQLにアクセスできました。
オマケ的に定義したRouteを使っても、アクセス可能です。
$ curl -I external-apache2-http-endpoint-myproject.192.168.42.198.nip.io HTTP/1.1 200 OK Date: Sun, 27 Jan 2019 10:11:27 GMT Server: Apache/2.4.29 (Ubuntu) Last-Modified: Sun, 27 Jan 2019 07:14:02 GMT ETag: "2aa6-5806b4dff8280" Accept-Ranges: bytes Content-Length: 10918 Vary: Accept-Encoding Content-Type: text/html Set-Cookie: c10e6db40be63af5d822808c349366a4=41d3256b7efc42ef62303ae9e497f809; path=/; HttpOnly Cache-control: private
では、ここでリソースを1度全部削除しておきます。
$ oc delete all --all
Endpointsは、同じ名前で定義したServiceを削除すると、一緒に削除されます。
また、次のためにアプリケーションを再度デプロイしておきます。
$ oc new-app --name nodejs-external-service [Gitリポジトリへのパス] $ oc expose svc/nodejs-external-service
外部リソースへのアクセスを名前で定義する(ExternalName Service)
続いて、ExternalName Serviceを使う場合。
作成したYAMLはこんな感じで、やっぱりApacheにアクセスするためのRouteも用意しました。
external-resource-externalname.yml
--- ### Service(Apache) apiVersion: v1 kind: Service metadata: labels: app: external-apache2 name: external-apache2-service spec: type: ExternalName externalName: test.server ports: - name: external-apache2-http protocol: TCP port: 80 targetPort: 80 --- ### Service(MySQL) apiVersion: v1 kind: Service metadata: labels: app: external-mysql name: external-mysql-service spec: type: ExternalName externalName: test.server ports: - name: external-mysql protocol: TCP port: 3306 targetPort: 3306 --- ### Route(http) apiVersion: v1 kind: Route metadata: labels: app: external-apache2 name: external-apache2-http-endpoint spec: port: targetPort: external-apache2-http to: kind: Service name: external-apache2-service
こちらは単純で、typeが「ExternalName」なServiceを定義し、「externalName」でアクセス先のサーバー名を設定してあげれば
OKです。
apiVersion: v1 kind: Service metadata: labels: app: external-apache2 name: external-apache2-service spec: type: ExternalName externalName: test.server ports: - name: external-apache2-http protocol: TCP port: 80 targetPort: 80
ドキュメントがそうなっていますが、「spec.ports」は書かなくてもOKです。その場合、ポートを絞らずにリクエストを
転送する感じになります。
では、リソースを定義します。
$ oc apply -f external-resource-externalname.yml
作成されたServiceを見てみます。typeは「ExternalName」、またclusterIPは設定されていませんね。代わりにexternalIPの
記述があります。
$ oc get svc -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR external-apache2-service ExternalName <none> test.server 80/TCP 21s <none> external-mysql-service ExternalName <none> test.server 3306/TCP 21s <none> nodejs-external-service ClusterIP 172.30.10.66 <none> 8080/TCP 6m app=nodejs-external-service,deploymentconfig=nodejs-external-service
同じくNode.jsからアクセスできますが、結果は先ほどのNone-Selector Serviceと同じなので省略します。
なんとか動かせましたね。
まとめ
OKD(Kubernetes)のクラスタ外にあるリソースへアクセスするService、None-Selector ServiceとExternalName Serviceを
試してみました。
ちょっと気になる機能だったので、試してみて正解でした。最初、None-Selector Serviceがちょっとわかりにくかったです…。