OCI ことはじめ : OCIR に Push した Docker イメージを OKE クラスタ上で動かしてブラウザからアクセスするまで
OCI : Oracle Cloud Infrastructure という IaaS を使ってみる。OCI には OCIR : Oracle Cloud Infrastructure Registry と呼ばれるプライベート Docker レジストリと、OKE : Oracle Container Engine for Kubernetes という Kubernetes マネージド・サービスが提供されているので、DockerHub には Push したくないプライベートな Docker イメージを置いておけるし、その Docker イメージを使って Kubernetes クラスタに Pod をデプロイしたりできる。
今回はこの OCI が提供する OKE と OCIR を活用して、最終的にブラウザからアクセスできる Web サーバを立ち上げてみたいと思う。
事前準備が多いので、順を追って説明する。今回は MacOS Mojave 環境で構築した。
目次
- Docker のインストール
- Kubernetes のインストール
- OCI 管理コンソールに移動する
- Compartment・Group・User・Policy を作成する
- OCI CLI のインストール
- OCI CLI の初期設定
- 作成した User に API キーの公開鍵を登録する
- OKE Cluster を作成する
- Kubernetes 環境を設定する
- Docker イメージを OCIR へ Push するための準備
- Docker イメージを OCIR へ Push する
- OKE Cluster で OCIR 上の Docker イメージを使えるように設定する
- OKE Cluster に Docker イメージをデプロイする
- Load Balancer を作成する
- 以上
Docker のインストール
開発マシンに Docker Desktop をインストールしておく。今回は細かな手順は省略。
ターミナルで $ docker
コマンドを実行し、Docker が利用可能な状態にしておく。
Kubernetes のインストール
コチラも細かな手順は省略。以下のサイトより、OS に合う方法でインストールする。
MacOS の場合、Homebrew を利用してインストールできたので、$ brew install kubernetes-cli
だけで OK。ターミナルで $ kubectl
コマンドが叩ける状態にしておく。
OCI 管理コンソールに移動する
Oracle Cloud My Services ダッシュボードにログインしたら、左メニューから「サービス」→「Compute」と進む。
Compartment・Group・User・Policy を作成する
- OCI における、各種「リソース」を管理する枠となるのが Compartment (コンパートメント)。
- 「リソース」というのは、VM のインスタンスとか、VCN とか、Kubernetes クラスタとか、OCI 内で操作できるモノの総称。
- そのリソースをどこまで操作できるようにするか、という権限設定が Policy (ポリシー) で、Policy の適用範囲は Group (グループ) 単位などで指定できる。
- その Group には User (ユーザ) が属する。この User は、OCI 用のアカウントとして発行して普通に人間がログインして利用することもできるが、プログラム内でリソースを操作する際にも、この作成した User を使ったりする。「SYSTEM」ユーザ、みたいな感覚かな。
そんなワケで、OCIR や OKE を使うためのアレコレを作っていく。
- Compartment を作成する
- OCI コンソールの左上ハンバーガーメニューアイコン → Identify → Compartments を選択する
- 「Create Compartment」ボタンを押下する
- Name : コンパート名を設定する・あとで変更可能 (命名については後述)
- Description : 任意・あとで変更可能
- Tags : 任意・今回は指定しない
- 入力したら「Create Compartment」ボタンを押下する
- OKE・OCIR を使用するユーザ向けの Group を作成する
- OCI コンソールメニュー → Identify → Groups と移動する
- 「Create Group」ボタンを押下する
- Name : グループ名を設定する・あとでの変更は不可
- Description : 任意・変更不可
- 入力したら「Submit」ボタンを押下する
- OKE・OCIR 操作時に使用する API キー設定および Auth Token 発行用の User を作成する
- OCI コンソールメニュー → Identify → Users と移動する
- 「Create User」ボタンを押下する
- Name : ユーザ名を設定する・変更不可。以降では分かりやすくするため
my-app-manager-user
と呼ぶことにする。 - Description : 任意・あとで変更可能
- 入力したら「Create」ボタンを押下する
- 作成した User を先程の Group に所属させる
- 作成した User (
my-app-manager-user
) の詳細画面に移動する - 左カラム Resources メニュー → Groups を選択する
- 「Add User to Group」ボタンを押下する
- Groups : 「Select a Group」セレクトボックスより、先程作成した Group を選択する
- 「Add」ボタンを押下する
- 別の操作手順 (どちらでも同じ)
- Group 詳細画面に移動し、「Add User to Group」ボタンを押下する
- User : 「Select a User」セレクトボックスより User (
my-app-manager-user
) を選択し「Add」ボタンを押下する
- 作成した User (
- OKE を使用するための Policy を作成する
- OCI コンソールメニュー → Identify → Policies と移動する
- 左カラム Compartment メニュー → 「Pick a compartment」セレクトボックスより、ルート Compartment を選択する
- 「Create Policy」ボタンを押下する
- Name : ポリシー名を設定する・変更不可
- Description : 任意・変更不可
- Policy Versioning : Keep Policy Current を選択 (初期状態のまま)
- Policy Statements : 以下のとおりに入力する
allow service OKE to manage all-resources in tenancy
- 入力したら「Create」ボタンを押下する
- 作成した Group に所属する User が OCIR を使用するための Policy を作成する
- 先程と同様の手順で、ルート Compartment を選択した状態で「Create Policy」ボタンを押下する
- Name : ポリシー名を設定する
- Description : 任意
- Policy Versionin : Keep Policy Current を選択 (初期状態のまま)
- Policy Statements : 以下の要領で入力する
allow group 【作成した Group 名】 to manage repos in tenancy
- 入力したら「Create」ボタンを押下する
命名規則のオススメ
Compartment・Group・User・Policy の名称は、半角英数字とハイフンあたりが使える。大文字も使えるが、個人的なオススメは小文字のみのハイフンケース。この時点で4種類のリソースが登場しているので、何のリソースに名前を付けたのか分かるように、リソース種別も名前に含めてしまうと後で分かりやすいかも。
要するにこんな感じ。
- システム名を
my-app
とする - Compartment :
my-app-compartment
- Group :
my-app-managers-group
- あとで付与する Policy の Verb 部分に沿った Target User の種別として
managers
を使ってみた。 - 参考 : Policy Reference … Policy Statement の構文説明の中の、Verb 部分の解説。
inspect
・read
→auditors
あたり、use
→users
あたり、manage
→managers
・administrators
あたりが分かりやすいかと
- あとで付与する Policy の Verb 部分に沿った Target User の種別として
- User :
my-app-manager-user
- 複数ユーザ作る時は「どんなマネージャーなの?」が分かるように工夫…
- Policy :
my-app-managers-policy
Policy の Statement は、普通の英文に見えるかもしれないが、決まった構文がある。以下を参考にされたし。
- 参考 : OKE、OCIRを使うときに権限制御するための設定まとめ - チャーリー!のテクメモ
- 参考 : Oracle Cloud Infrastructure Registry (OCIR)を使ってみよう - Qiita
OCI CLI のインストール
OKE で構成した kubernetes クラスタの操作には、kubectl
コマンドを使うワケだが、その接続に必要な環境情報を得るため、まずは OCI CLI というコマンドラインツールをインストールする。
OCI CLI のインストールに際しては Python3・pip3 が必要になるため、未インストールの場合は Homebrew などでインストールしておく。
# Homebrew で Python3 をインストールする
$ brew install python
# Python3 がインストールされたことを確認する
$ python3 -V
Python 3.7.2
# pip3 も同時にインストールされたことを確認する
$ pip3 -V
pip 18.1 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
# `python` コマンドで v3 系が使用されるよう PATH を設定する (brew info python で紹介されている)
echo 'PATH="/usr/local/opt/python/libexec/bin:$PATH"' >> ~/.bash_profile
続いて、以下のコマンドで OCI CLI のインストールを開始する。
$ bash -c "$(curl -L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh)"
# 色々質問されるので、それぞれ次のように回答して進めていく
===> In what directory would you like to place the install? (leave blank to use '/Users/【ユーザ】/lib/oracle-cli'): # ★
# → 空白のまま Enter を押下する。ライブラリが `~/lib/oracle-cli/` 配下にインストールされる
===> In what directory would you like to place the 'oci' executable? (leave blank to use '/Users/【ユーザ】/bin'): # ★
# → 空白のまま Enter を押下する。コマンドが `~/bin/` 配下にインストールされる
===> In what directory would you like to place the OCI scripts? (leave blank to use '/Users/【ユーザ】/bin/oci-cli-scripts'): # ★
# → 空白のまま Enter を押下する。OCI CLI スクリプトが `~/bin/oci-cli-scripts/` 配下にインストールされる
===> Modify profile to update your $PATH and enable shell/tab completion now? (Y/n): Y # ★
# → PATH にタブ補完用のスクリプトを追記するかどうか。指示どおり `Y` を入力する
===> Enter a path to an rc file to update (leave blank to use '/Users/【ユーザ】/.bash_profile'): # ★
# → タブ補完用の PATH を追記するファイルを問われている。空白のまま Enter を押下し、`~/.bash_profile` に追記させる
ターミナルを再起動 ($ exec -l $SHELL
) し、oci
コマンドが動作することを確認する。
~/.bash_profile
末尾には以下のような1行が追記されている。コレは、OCI CLI コマンドの Tab 補完用のスクリプト読み込み処理。
[[ -e "/Users/【ユーザ】/lib/oracle-cli/lib/python3.7/site-packages/oci_cli/bin/oci_autocomplete.sh" ]] && source "/Users/【ユーザ】/lib/oracle-cli/lib/python3.7/site-packages/oci_cli/bin/oci_autocomplete.sh"
インストール中、command 'clang' failed with exit status 1
みたいなエラーが発生してインストールに失敗した場合は、以下のような対処で何とかなるかも。
brew install ta-lib
を実行してみる~/.bash_profile
に以下の行を追記してやり直す
export LDFLAGS="-L/usr/local/opt/openssl/lib"
export CPPFLAGS="-I/usr/local/opt/openssl/include"
export PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig"
この他にも、環境によってインストール時にエラーが発生しやすい。python
コマンドで Python3、pip
コマンドで pip3 が利用できるよう PATH を通してあるかを中心に環境を確認すると良い。
OCI CLI の初期設定
ターミナルで $ oci setup config
コマンドを実行し、OCI CLI の初期設定を行う。
途中、OKE・OCIR 使用時の API キーを生成する。
oci
コマンドから対象のテナンシーにアクセスできるよう初期設定する。
$ oci setup config
# またいくつか質問されるので答えていく
Enter a location for your config [/Users/【ユーザ】/.oci/config]: # ★
# → 空白のまま Enter
Enter a user OCID: ocid1.user.xxxxxxxxxx # ★
# → 先程作成した User (my-app-manager-user) の OCID を入力する
Enter a tenancy OCID: ocid1.tenancy.xxxxxxxxxx # ★
# → Tenancy の OCID
Enter a region (e.g. eu-frankfurt-1, uk-london-1, us-ashburn-1, us-phoenix-1): us-ashburn-1 # ★
# → Tenancy のホームリージョンを指定する。ココでは us-ashburn-1 を指定したテイ
Do you want to generate a new RSA key pair? (If you decline you will be asked to supply the path to an existing key.) [Y/n]: Y # ★
# → API キー (秘密鍵) を生成するため `Y` を入力する
Enter a directory for your keys to be created [/Users/【ユーザ】/.oci]: # ★
# → 空白のまま Enter・生成先ディレクトリパスの指定
Enter a name for your key [oci_api_key]: # ★
# → 空白のまま Enter・生成する API キーのファイル名
Public key written to: /Users/【ユーザ】/.oci/oci_api_key_public.pem
Enter a passphrase for your private key (empty for no passphrase): # ★
# → 空白のまま Enter・API キーにパスフレーズを付けられるが、今回は付けない
Private key written to: /Users/【ユーザ】/.oci/oci_api_key.pem
Fingerprint: xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx
Config written to /Users/【ユーザ】/.oci/config
以上の初期設定で、以下の3ファイルが生成された。
~/.oci/config
:oci
コマンドが接続する先を示す設定ファイル~/.oci/oci_api_key.pem
:oci
コマンドで使用する API 秘密鍵。コレを後で OKE・OCIR 利用時にも流用することにする~/.oci/oci_api_key_public.pem
: API 秘密鍵に対応する公開鍵
作成した User に API キーの公開鍵を登録する
先程作成した my-app-manager-user
に、~/.oci/oci_api_key_public.pem
ファイルに書き出された API 公開鍵を紐付ける。
- User の詳細画面に移動する
- 左カラム Resources メニュー → API Keys を選択する
- 「Add Public Key」ボタンを押下する
- Public Key 欄に、前述の公開鍵
oci_api_key_public.pem
の内容をコピー & ペーストするBEGIN PUBLIC KEY
からEND PUBLIC KEY
の記載がある行まで全てを貼り付ける
- 貼り付けたら「Add」ボタンを押下する
- 登録した API キーの「Fingerprint」を確認する。
oci setup config
コマンドの最後や、~/.oci/config
ファイルの中に記載の Fingerprint と一致していることが確認できる
OKE Cluster を作成する
いよいよ Kubernetes クラスタを作成する。
- OCI コンソールメニュー → Developer Services → Container Clusters (OKE) と選択する
- 左カラム Compartment メニュー → セレクトボックスより作成した Compartment を選択する
- 「Create Cluster」ボタンを押下する
- Name : 任意で決める
- Kubernetes Version : v1.11.5 (初期状態のまま)
- ラジオボタン : Quick Create を選択 (初期状態のまま) → コレによりインスタンスや VCN などを自動生成してくれる
- その他 : 初期状態のまま
- 入力したら「Create」ボタンを押下する
- VCN や Subnet などが自動生成されるので、「Close」ボタンを押下する
- Cluster 詳細画面に移動し、「Cluster Status」が「Creating」から「Active」になるまで10分程度待つ
Kubernetes 環境を設定する
先程作成した OKE クラスタに、kubectl
コマンドでアクセスできるようにするための設定。
- 作成した Cluster の詳細画面に移動する
- 「Access Kubeconfig」ボタンを押下する
- 表示された次のようなスクリプトをコピーする
mkdir -p $HOME/.kube
oci ce cluster create-kubeconfig --cluster-id ocid1.cluster.xxxxxxxxxx --file $HOME/.kube/config --region us-ashburn-1
このスクリプトをターミナルで実行すると、~/.kube/config
ファイルが生成され、kubectl
コマンドで OKE クラスタに接続できるようになる。この KubeConfig と呼ばれるファイルの中身は YAML 形式の設定ファイルだ。
次のコマンドで、kubectl
を用いて実際に OKE Cluster にアクセスできるか確認する
$ kubectl get all
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11m
Docker イメージを OCIR へ Push するための準備
OKE クラスタが構築でき、コマンドラインから操作できるようになったので、次は OCIR (プライベートレジストリ) にオリジナルの Docker イメージをプッシュしておこう。あとでコレを OKE クラスタにデプロイする。
- 作成した User
my-app-manager-user
より、OCIR に Docker イメージを Push するための Auth Token を発行する- OCI コンソールにて、User の詳細画面に移動する
- 左カラム Resources メニュー → Auth Tokens を選択する
- 「Generate Token」ボタンを押下する
- Description : 任意
- 入力したら「Generate」ボタンを押下する
- 次画面の「Generated Token」欄に表示されているトークン文字列をコピーする。この画面を閉じると二度と確認できないため、必ずコピーして控えておく。
- トークン文字列がコピーできたら「Close」ボタンを押下する
- Docker コマンドで OCIR にログインする
- 以下のように、
echo
コマンドに Auth Token を指定し、docker login
コマンドの--password-stdin
オプションにパイプで渡す docker login
コマンドでログインするドメイン名は、リージョンにあわせて指定する-u
オプションで指定するユーザ名は、Tenancy/User
の形式で指定する
- 以下のように、
$ echo 'XXXxxx(XXXX#00XXXXXX' | docker login iad.ocir.io -u my-app-tenancy/my-app-manager-user --password-stdin
Login Succeeded
Docker イメージを OCIR へ Push する
任意の Dockerfile
を用意して、docker build
コマンドで Docker イメージを作成しておこう。今回は詳しい説明は省略するが、とりあえず何か Web サーバっぽいモノが動いている想定。
コレを OCIR にプッシュするには、Push 用のタグを付けてから Push する必要がある。
# Push 用のタグを付与する : 規則は 【リージョン別ドメイン】/【Tenancy】/【任意のリポジトリ名】/【任意の Docker イメージ名】
$ docker image tag my-app-docker-image:v1 iad.ocir.io/my-app-tenancy/my-app-repository/my-app-docker-image:v1
# タグ付けした Docker イメージを Push する
$ docker push iad.ocir.io/my-app-tenancy/my-app-repository/my-app-docker-image:v1
Push できたら、OCI コンソールより、OCIR に Docker イメージが Push されているか確認してみよう。
OCI コンソールメニュー → Developer Services → Registry (OCIR) に移動し、一覧にリポジトリとイメージが存在していれば OK。
OKE Cluster で OCIR 上の Docker イメージを使えるように設定する
OKE クラスタで、OCIR 上の Docker イメージを使えるようにするには、Docker Registry Secret というクレデンシャル情報 (秘密情報) を Kubernetes に登録する必要がある。秘密情報は開発マシンに保存されるのではなく、Kubernetes クラスタ内に保持される。
- 参考 : Kubernetes Secrets の紹介 – データベースのパスワードやその他秘密情報をどこに保存するか? – ゆびてく
- 参考 : KubernetesのSecretは本当に安全か - Qiita
ではその登録手順。ターミナルで、以下のようにコマンドを実行する。
$ kubectl create secret docker-registry my-app-ocir-secret \
--docker-server=iad.ocir.io \
--docker-username='my-app-tenancy/my-app-manager-user' \
--docker-password='XXXxxx(XXXX#00XXXXXX' \
--docker-email='example@example.com'
secret/my-app-ocir-secret created
kubectl create secret docker-registry
の後に、任意の Docker Registry Secret 名を指定する。この Docker Secret Registry 名は、この後 Kubernetes のdeployment.yaml
に記載する--docker-server
はホームリージョンに合わせて指定する--docker-username
はTenancy/User
の形式で記述する--docker-password
は先程 User から発行した Auth Token を指定する--docker-email
は、使用しないが必須入力であるため、適当に入力する
Secret が生成できたら、以下のコマンドで確認できる。
$ kubectl get secret
NAME TYPE DATA AGE
default-token-hrv9f kubernetes.io/service-account-token 3 41m
my-app-ocir-secret kubernetes.io/dockerconfigjson 1 5s
OKE Cluster に Docker イメージをデプロイする
Kubernetes に Docker イメージを冗長構成でデプロイするための deployment.yaml
を作成する。
apiVersion: apps/v1 # Kubernetes のバージョンに依存して決まる
kind: Deployment
metadata:
name: my-app-deployment
spec:
selector: # Label Selector : 指定の labels と完全一致した Pods が紐付く
matchLabels: # 完全一致なので、spec.template.metadata.labels: セクションと全く同じ内容を書く
app: my-app
replicas: 2 # レプリカ数
template: # Pod Template
metadata:
labels:
app: my-app # Pod 名。Key・Value 形式で、Key は何でも良い
spec:
containers:
- name: my-app
image: iad.ocir.io/my-tenancy/my-app-repository/my-app-docker-image:v1 # 使用する Docker イメージ名
imagePullPolicy: Always
ports:
- containerPort: 8080 # コンテナが公開したいポート・Dockerfile の EXPOSE 命令に書いたポートと合わせておく
imagePullSecrets:
- name: my-app-ocir-secret # Docker Registry Secret 名を記述する
deployment.yaml
の書き方は Kubernetes の使い方の中で調べてみてほしい。ココでは、Docker イメージ内で動作する Web サーバが 8080 ポートを使っている前提で containerPort
を指定している。Node.js Express サーバなんかだと、80番のようないわゆる「Well-known Port」を使用するにはひと手間必要だったりするので、こんな風にした次第。
そしたらこのファイルを適用して、Pod をデプロイさせる。
$ kubectl apply -f deployment.yaml
deployment.apps/my-app-deployment created
# 少し待って Pod が生成されていることを確認する
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
my-app-deployment-xxxxxxxxxx-yyyyy 1/1 Running 0 37s
my-app-deployment-xxxxxxxxxx-zzzzz 1/1 Running 0 1m
# 各 Pod の直近の標準出力を確認して、起動しているか見てみる
$ kubectl logs -lapp=my-app
コレで、Kubernetes クラスタ内で Pod が稼動し始めた。
Load Balancer を作成する
このままでは稼動している Pod に外部からアクセスできないので、Load Balancer を作成する。
以下のような service.yaml
を用意する。
apiVersion: v1
kind: Service
metadata:
name: my-app-service
labels:
app: my-app
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- name: http
port: 8080
targetPort: 8080
- name: httpwell
port: 80
targetPort: 8080
先程も書いたとおり、Docker イメージが実際に使用するのは 8080 ポートなので、8080 ポートで直接アクセスできる口も作ってあって、さらに80番ポートでアクセスした時に Pod には 8080 ポートで転送されるような name: httpwell
指定も入れている。あんまりやたらとポートを開けておかない方が安全なので、本番利用時は80番 (HTTP) と443番 (HTTPS) くらいに絞った方が良いかと。
さて、コレを次のようなコマンドで実行し、Load Balancer を生成させる。
$ kubectl apply -f service.yaml
service/my-app-service created
# Load Balancer 作成中。パブリック IP (`EXTERNAL-IP`) の発行を数分待つ
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 49m
my-app-service LoadBalancer 10.96.200.00 <pending> 8080:31779/TCP,80:32202/TCP 5s
# パブリック IP が発行された
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 50m
my-app-service LoadBalancer 10.96.200.00 128.200.190.10 8080:31779/TCP,80:32202/TCP 57s
ココで確認できる EXTERNAL-IP
が、実際に外部から接続できるパブリック IP アドレスである。というワケで、http://128.200.190.10/
といった URL でブラウザからアクセスしてみよう。実際に疎通できるようになっているはずだ。
ココで生成した Load Balancer や Public IP は、OCI コンソールメニュー → Networking → Load Balancers および Public IPs で確認できる。kubectl
の service
を変更しても上手く反映されていないように見える場合は、OCI コンソールで状況を確認してみよう。
以上
初期設定が物凄く長かった…。Terraform とか使ったらもう少しスクリプト化できるのかな…。
ひとまずこんな流れで、OCI を初めて触った人間が、OKE と OCIR を活用して、ブラウザからアクセスできる Web サーバを構築できた。