CDO (Custom Domain Operator) とは
また RSOA 4.14 (2024年10月GA) からは、OpenShift 標準の IngressController によってユーザーの独自ドメイン用の IngressController を作成するようになりました。
また、この記事は ROSA Classic と呼ばれるタイプのアーキテクチャーの ROSA の記事です。ROSA HCP は OCP 4.14 以降を採用していますので、CDO (Custom Domain Operator)は存在していません。
CDOの代わりに標準のIngressControllerを使って、同様の事をする記事をこちらに書きました。
ROSA では、2つのタイプの ingress (ユーザーのHTTP/S トラフィックを Service まで導く仕組み) が提供されています。
ingress
と言うと、Kubernetes のリソースの定義としての kind: Ingress
の意味もありますが、ここでは HTTP/S トラフィックを誘導する機能としての ingress という意味で、頭を小文字にして igress
という言葉を使用します。
ROSA デフォルトのingress
1つめの Ingress は、単純に "Ingress"と呼ばれていて、特別な呼び名はありません。Cluster作成時に最低でも1つがデフォルトで存在し"default" という名前が付いてます。もう一つ別の名前を付けて追加する事ができます。これらはSREが管理しているオブジェクトです。
*.apps.<ユーザーが付けたROSAクラスター名>.<ランダム文字>.<ランダム文字>.openshiftapps.com
例:*.apps.mycluster.dzfa.p1.openshiftapps.com
のようなフォーマットになります。openshiftapps.com
は、Red Hat が取得管理しているドメイン名で、そのサブドメインを ROSAサービスを購入しているユーザーに割り当てている形になります。
ユーザーアプリを外部公開する時は、この *
の部分を好きな文字列にして提供できます。DNS (Route53)上に *
で登録されているのでユーザーは個別の DNSレコードを登録する必要はありません。
OpenShift のコンソールや、OAuth サーバーなどの基本機能もこのドメインで提供されます。
このデフォルトの ingress は、rosa
コマンドで以下のように表示する事ができます。(CDOはできません)
$ rosa list ingress -c rosa-cluster
ID APPLICATION ROUTER PRIVATE DEFAULT ROUTE SELECTORS
z9l9 https://apps.rosa-cluster.70d7.p1.openshiftapps.com no yes
$
CDO(Custom Domain Operator)によるingress
2つ目の Ingressは、CDO (Custom Domain Operator)と呼ばれています。ドキュメント上はアプリケーションのカスタムドメインの設定で言及されている部分になります。
この ingress のドメイン名は、*.example.com
のように独自ドメイン(ユーザーが取得したドメイン)が使われます。
ここで使用するドメイン名はユーザーが取得したものを使用する前提になります。
ユーザーが作成し、外部に公開するアプリは test1
.example.com、test2
.example.com などのドメインになります。同じベースドメインで複数のアプリを公開する時に便利です。ワイルドカード証明書を使用するため、アプリ毎に証明書を管理しなくて良いというメリットもあります。
CDOを使用するに当たって以下が必要になります。
-
example.com
のような独自ドメインの取得 - 上記の独自ドメインに対する証明書
- 作業用の dedicated-admin 権限を持つユーザーアカウント
dedicated-admin
権限は ROSAクラスターの管理者権限です。
この作業では、管理者が提供する独自ドメインを選定し、そのドメインを * (ワイルドカード) で使用できるようにした ingress 環境を作成します。アプリケーション開発者は、*の部分を好きな名前にしたアプリをHTTPSで提供できるようになります。
ちょっと細かいですが、ROSA の default ingress と、CDO(Custom Domain Operator) による ingress の違いを図示したものが以下になります。薄い緑色の枠で塗っている部分がユーザーが何らかの作成作業をしなければいけない部分を示しています。それ以外の部分は自動で生成される部分です(オレンジは AWSリソース、薄い青は ROSAのリソース)。
上図では、*.apps.<ユーザーが付けたROSAクラスター名>.<ランダム文字>.<ランダム文字>.openshiftapps.com
は、長いので *.apps.<cluster-domain>
と省略しています。
2023/03/29 追記: 現在 CDO の内部で使用する ELB の種類として CLB の他に NLB が選択できるようになりました。 CDOの使い勝手を広げる機能追加なので別途、解説記事を書ければ...と思っています。
CDO のインストール
ROSA では、CDO(Custom Domain Operator) はデフォルトでインストールされているため、デフォルトでは Operator 自体のインストール作業は必要ありません。
ただし、ingress オブジェクトを生成するには、CustomDomain
という kind
の Custom Resrouce を作成する必要があります。
Custom Domain Operator がこの CustomDomain
リソースの作成を検知して、実際の ingress 関連のオブジェクトを作成してくれる仕組みになっています。
独自ドメインの取得
example.com
は取得する事はできないので、ここでは、ocp4.work
というドメインを お名前.com で取得しました。そのため、取得したドメインは、最初は、お名前.com の DNSサーバーによって管理されています。
そのままでも良いのですが、Route53ユーザーが多いと思うので、お名前.com から Route53 にドメイン(Zone) の管理を移管します。
まず、AWS Route53 に Zone を作成してホストする事にします。詳細な手順は省略しますが、Route53 上に取得した自分のドメインの Zone を作成します。
Route53上でゾーン(ドメインの入れ物) を作成すると、画面に、そのゾーンを管理する NSレコード (DNSサーバーのアドレス) が表示されます。上記の表示の ns-711.awsdns-24.net
ns-1066.awsdns-05.org
.... がそれです。可用性を保つために複数のDNSサーバーが提供されます。
Route53で提供された DNSサーバー名をお名前.com 側の管理ツールに入力して、暫く待ちます。
以下は お名前.com の DNSサーバーの設定変更画面です。
以下のコマンドを指定して、NSレコードの状態を確認できます。
$ dig <取得したドメイン名> ns +short
指定した DNSサーバーに変更されるまで待ちます。
親となるドメインの複数のDNSサーバーに対して情報更新を行う(しかもその親は複数のドメインの親である)ので、暫く時間がかかる事があります。私の場合は1時間ほどで更新されました。
$ dig ocp4.work ns +short
01.dnsv.jp. # 初期状態。ドメイン取得元(お名前.com) の NS サーバーが表示されている。
03.dnsv.jp.
02.dnsv.jp.
04.dnsv.jp.
$
<お名前.com 側の Interface で、Route53から提供された DNSサーバー名を入力>
<待つ事およそ一時間 (管理コンソールのメッセージによると24時間くらいかかる事もあるとの事) >
$ dig ocp4.work ns +short
ns-1781.awsdns-30.co.uk. # AWSが提供する DNSサーバーが表示されるようになる。
ns-711.awsdns-24.net.
ns-51.awsdns-06.com.
ns-1066.awsdns-05.org.
$
Route53 の DNSサーバーの値が NS レコードとして返ってくれば Route53での取得ドメインのホストが開始されています。
dig
コマンドの +short
というオプションは出力を簡略化してくれるオプションなので覚えて置くと便利です。
証明書の取得
今回は、Let's Encrypt で証明書を作成します。
AWS環境だと ACM (AWS Certificate Manager) で、証明書が発行をしたくなりますが、ACMは、AWSのサービスである ELBやCloudFront 等のサービスに対してのみ発行できる仕組みです。EC2上等で稼働する一般のアプリケーションに対して証明書を発行(証明書と秘密鍵をファイルとして Export する)機能は持っていません。
作業環境を整える
後のコマンドがコピペで済むようにするため、必要な情報を環境変数にセットします。
この例では、ocp4.work
という独自ドメインを取得したので、それを使用します。
$ CERT_DIR=~/mycert
$ mkdir -p $CERT_DIR
$ EMAIL="myemail@test.com" # 有効な email アドレス
$ DOMAIN="ocp4.work" # 取得した独自ドメイン
Let's Enrypt証明書の取得に必要なソフトウェアのインストール
certbot と呼ばれる Let's Encrypt のモジュールを適当な環境にインストールします。インターネットに接続できる環境である必要があります。作業手順を考えると oc コマンドが実行でき ROSAクラスターにアクセスできるようにセットアップされた環境が便利です。
私の環境は Amazon Linux 2 の環境なので、こちらの公式ガイドを見ながら以下のコマンドを実行しました。
$ sudo wget -r --no-parent -A 'epel-release-*.rpm' https://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/
$ sudo rpm -Uvh dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-*.rpm
$ sudo amazon-linux-extras install epel -y
$ sudo yum install -y certbot python2-certbot-apache
証明書取得の開始(certbot コマンドの実行)
certbot の実行。証明書は *.
でワイルドカード証明書を取得しています。
certbot certonly --manual \
--preferred-challenges=dns \
--email $EMAIL \
--agree-tos \
--config-dir "$CERT_DIR/config" \
--work-dir "$CERT_DIR/work" \
--logs-dir "$CERT_DIR/logs" \
-d "*.$DOMAIN"
テストで何度も証明書を発行を繰り返す可能性がある場合の注意
一週間に5回以上、同じリクエストをすると Rate Limit にひっかかり、以下のようなエラーがでる事があります。
too many certificates (5) already issued for this exact set of domains in the
last 168 hours: example.com login.example.com: see https://letsencrypt.org/docs/duplicate-certificate-limit
この事象については、こちらのブログ に記載があります。
発行対象の名前を変更する方法もありますが、回避策の一つとして、テスト用に何度も発行する事が想定する時は、 --test-cert
オプションを付けておく方法があります。
certbot certonly --manual \
--preferred-challenges=dns \
--email $EMAIL \
--agree-tos \
--config-dir "$CERT_DIR/config" \
--work-dir "$CERT_DIR/work" \
--logs-dir "$CERT_DIR/logs" \
-d "*.$DOMAIN" \
--test-cert
ただし、この場合の Root CA は通常のブラウザなどには含まれてないものになりますので、curl だと -k オプションを使用するか、Root CA の証明書をダウンロードしておく必要があります。こちらの記事 に詳細の解説があります。
certbot コマンドを実行すると以下のような出力が出てきます。慌てて Enter を押さず、一回止まって指示を良く読みます。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y # ここは お好みで。
Account registered.
Saving debug log to /home/ec2-user/mycert/logs/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Requesting a certificate for *.ocp4.work
Performing the following challenges: # Challenge を実行して下さいと指示をしている。
dns-01 challenge for ocp4.work
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name # DNS TXT レコードを設定しなさいと指示が出ている。
_acme-challenge.ocp4.work with the following value: # _acme-challenge.ocp4.work という TXT レコードに
L_BVE3S_cAeHEnodB2xwLCcZYxStOeUutxlTevhX2NQ # L_BVE3S_cAeHEnodB2xwLCcZYxStOeUutxlTevhX2NQ という値を設定
Before continuing, verify the record is deployed. # これ以上進む前に、TXTレコードがデプロイされた確認する。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue
上記のメッセージを解読すると DNS 上に _acme-challenge.ocp4.work
という TXT レコードを作成し、そのレコードにL_BVE3S_cAeHEnodB2xwLCcZYxStOeUutxlTevhX2NQ
という値を設定しな、レコードがインターネット上から解決できる状態になったら、Enter をクリックして下さい。と言っています。
ドメインを管理している DNS に、指定された値を追加する事で、このドメインの編集権限がある持ち主である事を証明しています。証明書の発行母体(この場合 Let's Encrypt)から与えられるこの課題を Challenge
と呼びます。証明書を発行してもらうには、このChallenge
に応える必要があります。
独自ドメインをホストするDNSサーバーは、闇で立てたDNSサーバーのようなものは使う事はできず、ドメインを購入したレジストラ等で「このDNSサーバーでホストします」登録する必要があります。[example.com]を取得した場合は、上位の[.com] の DNS権威サーバーに、子ゾーンである [example.com] の DNS権威サーバーのアドレスを登録する必要があります。
今回の例では、お名前.com(レジストラ)の設定画面で、Route53 の DNSサーバー名を登録した作業がこれに当たります。[ocp4.work]というドメインを取得しましたが、[.work]の権威DNSサーバーに[ocp4.work]をホストする権威DNSサーバーとして、Route53のDNSサーバーを登録した事になります。
DNSへのレコードの登録
今回の場合は、独自ドメインocp4.work
の権威DNSサーバーを、Route53にしているので、Route53の該当画面に飛び、指定された TXT レコードを登録します。
レコードを登録したら、以下のコマンドで値が返ってくるまで待ちます。
dig (_acme-challenge.ではじまる指定されたドメイン名) TXT +short
どのくらいでレコードが反映されるかは、事業者毎によって違います。Route53の場合は、通常1分程度 で反映されるようです。
$ dig _acme-challenge.ocp4.work TXT +short # TXT レコードを指定
$ # 何も出てこないので暫く待つ。
<暫く待つ>
$ dig _acme-challenge.ocp4.work TXT +short
"45D1lL4FxRuV__fBdgApFH9uNtNixDiSLzSs0duC0Lk" # 登録した TXT レコードが表示される (状況によりますが、今回は1分程度で出てきました)
certbot コマンドの実行画面に戻る
TXT レコードが表示されるようになったら、certbotコマンドを実行していた画面で Enter を実行します。
端末によって DNSレコードの反映に少し時間差がある場合もあるので、あまり急いでテキパキと操作をしない方が良いです。(challenge に失敗すると certbot の実行からやり直しになり、DNSへの登録もやり直しになります)
<省略>
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name # DNS TXT レコードを設定しなさいと指示が出ている。
_acme-challenge.ocp4.work with the following value: # _acme-challenge.ocp4.work という TXT レコードに
vl-6-7guKT4ShQS0OtNLBp1VtNgHQdiu1VpbFVeVC6I # vl-6-7guKT4ShQS0OtNLBp1VtNgHQdiu1VpbFVeVC6I という値を設定
Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue
Waiting for verification...
Cleaning up challenges
Non-standard path(s), might not work with crontab installed by your operating system package manager
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at: # Challenge 成功です。
/home/ec2-user/mycert/config/live/ocp4.work/fullchain.pem # 証明書は左のパスに保存されています。
Your key file has been saved at:
/home/ec2-user/mycert/config/live/ocp4.work/privkey.pem # 秘密鍵は左のパスに保存されています。
Your certificate will expire on 2023-03-27. To obtain a new or
tweaked version of this certificate in the future, simply run
certbot again. To non-interactively renew *all* of your
certificates, run "certbot renew"
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
$
これで証明書(=公開鍵を含んだ物体)と秘密鍵が生成されました。
念のため、生成された書かれているディレクトリを確認してみます。
$ ls $CERT_DIR/config/live/$DOMAIN
README cert.pem chain.pem fullchain.pem privkey.pem
$
それっぽいものが生成されているのがわかります。
ここのファイルが何かを解説するには、Webの証明書の仕組みを解説する必要があるので、説明は省略しますが、私達が注目すべきは、fullcahin.pem
と privkey.pem
の2つのファイルです。
CustomDomain オブジェクトの作成
作業環境を整える
ドメインと証明書が準備できたので、いよいよ CustomDomain の作成に入ります。まずは、作業用に my-custom-domain
という新しいプロジェクトを作成します。
$ oc new-project my-custom-domain
後々のコマンドを楽にするために、変数に証明書があるパスを設定しておきます。
$ MY_CERT=$CERT_DIR/config/live/$DOMAIN
Secretの作成
証明書を入れた Secret を作成します。
$ oc create secret tls my-tls --cert=$MY_CERT/fullchain.pem --key=$MY_CERT/privkey.pem -n my-custom-domain
secret/my-tls created
$
CustomDomain のオブジェクトの作成
作成した Secret を内部に指定する形で、Kind : CusutomDomain
のリソースのオブジェクト作成します。
ここでは、CusutomDomain
オブジェクトの名前は acme
と付けました。
(ACME(Automatic Certificate Management Environment) は、Let's Encrypt の証明書発行するための環境を指す言葉です。)
ヒアドキュメントで書いた定義をそのまま apply してしまいます。
cat << EOF | oc apply -f -
apiVersion: managed.openshift.io/v1alpha1
kind: CustomDomain
metadata:
name: acme
spec:
domain: $DOMAIN
certificate:
name: my-tls
namespace: my-custom-domain
EOF
暫く待つと以下のように Ready
になります。以上で完了です。
$ oc get customdomains
NAME ENDPOINT DOMAIN STATUS
acme qgeenq.acme.singleaz.dzfa.p1.openshiftapps.com ocp4.work Ready
$
CustomDomain リソースの作成で生成されたものを確認してみる
すこしアドバンスな内容なのではじめのうちは気にしなくて良いですが、CustomDomain オブジェクトを作成すると、裏でいろいろなオブジェクトが作成されています。
どのようなオブジェクトが生成されているのかリストしてみました。内部構造を知りたくなった時にこの部分を参照して見て下さい。
CustomDomain リソース作成により、生成されたオブジェクト群
Service
まずは、どのような Service リソースのオブジェクトが作成されたか確認してみます。
openshift-ingress
プロジェクト内に、Service type=LoadBalancer
と、Server type=ClusterIP
のオブジェクトが作成されています。(NAME に -acme と入っているのがそれです。それ以外は元から存在するものですが、環境によって異なります)
Service type=LoadBalancer
の EXTERNAL-IP部分は、afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com
と AWS の LoadBalancer のドメインが紐付いてる事がわかります。この LoadBalancer (CLB) も CustomDomain リソースの作成で生成されたものです。
$ oc get svc -n openshift-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
router-acme LoadBalancer 172.30.85.219 afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com 80:32618/TCP,443:31941/TCP 2m28s # 新規に作成された
router-default LoadBalancer 172.30.237.168 internal-a93f5c9ec983f4c87aca4abbedf0ba3b-1240512026.ap-northeast-1.elb.amazonaws.com 80:31404/TCP,443:32229/TCP 7h47m
router-internal-acme ClusterIP 172.30.205.43 <none> 80/TCP,443/TCP,1936/TCP 2m28s # 新規に作成された
router-internal-default ClusterIP 172.30.25.151 <none> 80/TCP,443/TCP,1936/TCP 7h47m
$
Pod
以下のPod は OpenShift 用語で router Pod と呼ばれる、ingress の Pod です。
新規に2つの Pod が作成されています。-acme- と NAME 欄に入っているもがそれです。それ以外の router Pod はデフォルトで作成される router pod です。
$ oc get pods -n openshift-ingress
NAME READY STATUS RESTARTS AGE
router-acme-857b888458-5nfxb 1/1 Running 0 8m24s # 新規に作成されたもの
router-acme-857b888458-9sv7f 1/1 Running 0 8m24s # 新規に作成されたもの
router-default-b8cfbc448-t928m 1/1 Running 0 7h
router-default-b8cfbc448-zggxj 1/1 Running 0 7h3m
$
ingresscontroller
こちらは ingresscontroller オブジェクトです。NAME の所に acme と書かれたものが新しく作成されたものです。OpenShift の場合は、実際にトラフィックを処理する Ingerss Pod(=Router Pod)とそれを管理する Ingresscontroller にオブジェクトが別れています。実装によっては Ingresscontroller が Podとして直接トラフィックを処理するものもあります。
$ oc get ingresscontroller -A
NAMESPACE NAME AGE
openshift-ingress-operator acme 9m12s # 新規に作成されたもの
openshift-ingress-operator default 8h
$
DNSレコード
黄色のハイライト部分が新規にレコードとして追加されています。*.acme.<cluster domain名>
のエイリアス先は、CLB のドメイン、afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com
になっています。
CLB(Classic Load Balancer)
CLB(Classic Load Blanacer)が新規に追加されています。これは前述のDNSレコードに登録されているものです。
真ん中の行の DNS名 afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com
の部分がそれにあたります。
独自ドメインをCDOで生成された CLB のドメインに CNAMEする
CustomDomain
オブジェクトに acme
という名前を付けたので、acme
という名前が入った kind:Service Type=LoadBalancer
が生成されているはずです。LoadBalancer で使われているドメイン名を取得します。
この Service オブジェクトは、openshift-ingress プロジェクト内に作成されています。
$ oc get svc -n openshift-ingress | grep acme | grep LoadBalancer
router-acme LoadBalancer 172.30.85.219 afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com 80:32618/TCP,443:31941/TCP 17h
$
CLB ドメインが afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com
である事がわかります。
情報の整理
この作業で使うドメインは以下の2つです。
[独自ドメイン]: ocp4.work
お名前.com で取得し、Route53 に管理を移管したドメイン。
[CDOによって作られたドメイン(CLB名)]: afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com
CNAME
独自ドメイン *.ocp4.work
へのアクセスを、CDOによって作られたCLBのドメイン afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com
に誘導するには CNAMEという作業をします。
Route53 の ocp4.work
ゾーンに以下の黄色でハイライトしている CNAMEレコードを追加します。
Route53 の独自機能でエイリアスという機能があり、それを使用しても上記の CNAMEと同様な事が実現可能です。が、ここでは RFCで定められた標準的な CNAMEを使って設定を行っています。
1分程度したら、*.ocp4.work の * の所を適当な文字列にして、dig で状況を確認してみます。DNS上にレコードとして*
で登録しているので、どんな文字列でもDNSレコードにマッチして解決されるはずです。(abc . def . ocp4.work のようなものは . が2つあるのでマッチしません)
ここでは、test.ocp4.work
に対して dig を実行してみます。
$ dig test.ocp4.work
<省略>
;; ANSWER SECTION:
test.ocp4.work. 291 IN CNAME afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com.
afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com. 51 IN A 13.113.63.29
afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com. 51 IN A 18.176.74.91
<省略>
test.ocp4.work
は、先ほど設定した CNAME により afb4ab5e4fcb8450e9705d2f4b4588c1-143380427.ap-northeast-1.elb.amazonaws.com
(CLBのドメイン名) に誘導され、さらに CLBドメイン名がロードバランシング用の2つのIPアドレスに解決されている事がわかります。
この CLIの画面からはわかりませんが、CLBの2つのIPに到達したトラフィックは、さらに OpenShift 上の Router Pod (= Ingress Pod : 実際のHTTP/Sトラフィックの処理を行うPod) にフォワードされるように設定されています。
Router Podでの処理
前述のように[独自ドメイン]->[CLBドメイン]->(CLB IPアドレス) -> (Router Pod IPアドレス)
と言う流れでトラフィックの宛先が変わっていきます。
CustomDomain オブジェクトを作成した時に、証明書の Secret を指定しましたが、この証明書は、Router Pod に渡されています。そのため Router Pod はHTTPSを復号化する事ができます。
Route Pod は、HTTP/Sのリクエストと一緒にやってきた宛先のドメインの情報を見て、どのユーザーの Service (Kbuerentes で定義されているリソース) にフォワードするか決定します。そのためには、ドメイン名とユーザーのService 名の紐付けの設定が書かれたオブジェクトが、アプリケーション毎に別途必要です。これを OpenShift では、Route
リソースと呼んでいます。
Route
リソースは、Kuberentes の Ingerss
リソースの元になったもので、YAMLの記述内容も Ingress とかなり似たものになっています。
OpenShiftでもIngress
リソースが使えるのですが、歴史的に Route
が先に作成された事もあり、OpenShift の各種ドキュメントは、今でも基本Route
が前提とした書き方になっています。そのため、敢えて変えるのも混乱するのでRoute
をそのまま使うのがおすすめです。
このRoute
は、ユーザーアプリ(PodやService)と一緒に、ユーザーが作成する必要があるリソースになります。次のステップでは実際にサンプルアプリと一緒にRoute
を作成します。
ここで Ingress と呼んでいるのは、kind: Ingress (Ingress リソース/リソースの定義) の事で、L7(HTTP/S)トラフィックを取り扱う機能としての ingress の事ではない事に注意して下さい。
Ingress リソースは、ベンダー毎に様々な機能拡張がされ、同じ Ingress リソース (kind: Ingress) もできる事が違っているというのが実態です。もはや Ingress という標準化は意味をなしておらず、個人的には無理に Route を Ingress に置き換える意味もほぼ無いだろうと思っています。OpenShift が Ingress リソースをサポートしつつも、未だに Route をメインに据えているのもこの辺りの背景があるのかもしれません。
また様々な形に派生してしまった Ingress リソースをそのまま発展させていくのも難しいため、それに変わるものとして Gateway API というものが Kubernetes の SIG(Special Interest Group) で開発されており、様々なベンダーがその実装を公開しはじめています。
テスト用のサンプルアプリをデプロイしてアクセスを確認する
アクセスすると "Hello OpenShift!" とテキストを返すシンプルな HTTPS アプリをデプロイしてみます。
イメージは、docker.io/openshift/hello-openshift
に置かれています。
Projectの作成
テスト用に新しい test-app
という新しい Project を作成します。
$ oc new-project test-app
hello-openshift アプリのデプロイとクラスター内への公開
oc new-app
コマンドに、イメージ名を指定して、テスト用のコンテナ (hello-openshift)を Pod としてデプロイします。
イメージ名を指定するだけで、 Service
を作成してクラスター内にコンテナを公開する所までやってくれます。
oc newa-app --image=<イメージ名>
実際のコマンドは以下の通りです。
$ oc new-app --image=docker.io/openshift/hello-openshift
$ oc get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-openshift ClusterIP 172.30.80.122 <none> 8080/TCP,8888/TCP 4m18s
$
通常コンテナイメージを Kubernetes (OpenShift)上にデプロイするには、Pod 化するために Deployment のYAMLを書いたり、Service を作成する必要がありますが、oc new-app は、イメージか自動的にデプロイに必要なオブジェクトを作成し Service として公開してくれます。
hello-openshift アプリのクラスター外への公開
上記の Service hello-openshit
を、クラスター外部に公開します。OpenShift では、oc create route edge
に Service 名を引数指定する事で公開できます。
route
の主な役割の一つは、Router Pod がHTTP/Sトラフィックを受けた取った時に、受け取ったドメイン名に対して、紐付けされている Service を教える事です。
oc create route edge --service=<サービス名> <作成するルート名> --hostname <公開ホスト名>
--hostname
オプションを使用してクラスター外部に公開するドメイン名を指定します。ここに独自ドメインを使ったホスト名を指定します。
今回の例では *.ocp4.work
にマッチするドメイン名であれば、--hostname
に、どんな値も指定できます。ここでは、サービス名と同じ hello-openshift
を使用して hello-openshift.ocp4.work
というドメイン名で公開する事にします。
ここでさりげなく edge
と指定していますが、これは Ingress Pod (=Router Pod)で HTTPSを終端しそこから先は暗号化しない方法です。この手順の中で作成して Secret に格納した証明書は、内部的に Router Pod に渡されており、Router Pod はそれを使用してHTTPSを終端します。
Router Pod と、ユーザーのアプリ(Pod)の間はクラスター内のプライベートネットワークなので外部に公開されないネットワークです。もし、Route Pod から先も暗号化したい場合は、個別のユーザーの Pod に証明書を入れて別途管理する必要があります。
edge 以外にも、re-encrypt や passthrough というオプションがあります。これらの方法は、いずれもユーザーPod側で別途証明書を組み込む必要があります。
実際のコマンドの実行結果は以下のようになります。
$ oc create route edge --service=hello-openshift hello-openshift-tls --hostname hello.$DOMAIN
route.route.openshift.io/hello-openshift-tls created
$ oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
hello-openshift-tls hello.ocp4.work ... 1 more hello-openshift 8080-tcp edge None
$
独自ドメイン名でのアクセス確認
curl で、 hello-openshift.ocp4.work
にアクセスして確認してみます。
$ curl https://hello.$DOMAIN
Hello OpenShift!
$
無事、独自ドメイン名で HTTPS を使用してサンプルアプリにアクセスできました。
サンプルアプリでは、hello
.ocp4.work という名前にしていますが、証明書は *
で取得していて、DNSへのドメイン登録も*
で登録されているので、*
.ocp4.work にマッチすればどんな名前でも使用可能です。
サンプルアプリの削除
プロジェクト毎そこに含まれるサンプルアプリを廃棄するのが一番簡単です。以下のコマンドを実行します。
oc delete project <プロジェクト名>
実際のコマンドの実行結果は以下のようになります。
$ oc delete project my-app
project.project.openshift.io "my-app" deleted
$
routeSelector と namespaceSelector
2022/10 月頃に routeSelector と namespaceSelector が、kind: CustomDomain に実装されていました。
これにより CDO で作られた ingress の対象となる Route オブジェクトや、namespace を選択できるようになりました。
apiVersion: managed.openshift.io/v1alpha1
kind: CustomDomain
metadata:
name: <company_name>
spec:
domain: apps.companyname.io
scope: External
certificate:
name: <name>-tls
namespace: <my_project>
routeSelector:
matchLabels:
route: acme # このラベルにマッチする Route を拾う
この機能は、ingress を経由するユーザーアプリへのトラフィックの向け先を制御できるので sharding (負荷分散) に使用するために使用する事ができます。(尚、routeSelector をテストしてみたのですが、作成していた CustomDomain オブジェクトを一度削除して再作成する必要がありました)
構成によっては ROSA上に、インターネット向けのドメインを持ったアプリだけでなく、プライベートネットワーク向けのドメインを持ったアプリも同時に作成する事もできます。
router pod は、特に何も指定しない場合、ROSA (OpenShift) 上の全ての Route オブジェクトを検索して、外部からのリクエストにマッチするドメイン名を持った Route オブジェクトを探します。
このような場合、「Spoofing」と呼ばれる手法を使って、インターネットから、内向けのドメインへのアクセスを探る事が技術的に可能です。
プライベートネットワーク向けのドメイン名をある程度把握してないと内部向けのドメインを突き止める事は困難ですが、セキュリティリスクにならないように、インターネットに公開されている ingress には、特定の Route (インターネット公開ドメインを持つ Route)だけにしかアクセスできないように routeSelector を使って制御する事をおすすめします。
証明書に関する運用上の注意
この記事の中では触れていませんが、証明書には期限があります。そのため独自の証明書を使用した場合は、その証明書を更新し続ける必要があります。
ROSA標準の ingress で使用されている証明書は、SREにより自動的に証明書が期限前に更新されますが、CDOで作成された ingressの証明書はユーザーが取得したものなので、ユーザーが更新する必要があります。
証明書が含まれている Secret を自動更新する Certification Manager というOperator が存在しているので、それについては別記事で解説しています。