はじめに
-
Amazon EC2でCentOSにKubernetesを構築する(前編)
Kubernetesのインストール~Dashboard導入まで -
Amazon EC2でCentOSにKubernetesを構築する(後編) ここ
Dockerイメージの作成~サービスの公開まで
今回はKubernetes上でサービスを公開したいと思います。構築方法については前編を見てください。
Kubernetesコンポーネントの簡単な説明
最初に、今回出てくるコンポーネントについて簡単に説明しておきます。
-
Pod
1つ以上のコンテナーをまとめたもの。Kubernetesがデプロイする単位であり、Kubernetesが管理する最小単位。「1つ以上」となっているのは、複数のコンテナーをアプリケーション要件から、必ず同一ノードで実行しなければならない場合のためです。 -
Service
0個以上のPodをまとめて、内部のPodの配置や内容に関わらず外部に同じ方法でアクセスするエンドポイントを提供するもの。内部のPodに対して、ロードバランシングをしたりFQDNを設定したりサービス検索(ディスカバリ)を提供したりします。0個PodのServiceという、いわゆる空のServiceに何の価値があるのか、と思うかもしれませんが、これは外部のリソースを参照したいときに使います。たとえば、Kubernetes外部にある構築済みのデータベースなどです。 -
Deployment
アプリケーション(≒Pod)のリリースを行うもの。PodとServiceが静的なものを対象にするのに対して、Deploymentは動的なものを扱います。アプリケーションをリリースするとき(Podをデプロイするとき)、デフォルトでは、新しいアプリケーションをデプロイする → Podを起動する → FQDNのあて先(IPアドレス)を変更する → 古いPodを停止する、というように行うが、その処理を定義できる。
PodとServiceの関係
Deploymentのイメージ
環境
サーバーがIPアドレスとなっていて分かりにくいので、載せておきます。
- サーバー構成
名前(兼hostname) | 役割 | IPアドレス |
---|---|---|
kube-master | Masterノード | 172.26.22.85 |
kube-node1 | Workerノード1 | 172.26.22.71 |
kube-node2 | Workerノード2 | 172.26.22.99 |
docker-repo | Docker private repository | 172.26.22.6 |
Dockerイメージを作成してからServiceを公開する
デプロイするWebコンテナーは何でもいいのですが、この記事では例として Apache HTTPD にしました。もちろんNginxでも問題ありませんし、手順も全く同じです。
Dockerイメージを作成
dockerレポジトリーから Apache HTTPDのイメージをpullして、前編で作成した private repository(172.26.22.6)へpushしておきます。KubernetesはPodをデプロイするとき、常にイメージを docker pull
するため、ローカルにイメージを置いておくだけではうまくいきません。またpushするときは、必ずtagを付けておく必要があります。
# docker pull docker.io/centos/httpd
...(snip)...
# docker run --privileged -d -p 80:80 --name my-httpd docker.io/centos/httpd /sbin/init
# docker exec -it my-httpd /bin/bash
$ systemctl enable httpd & systemctl start httpd
$ logout
# docker commit my-httpd 172.26.22.6:5000/my-repo/my-httpd:v0.01
# docker push 172.26.22.6:5000/my-repo/my-httpd:v0.01
dockerコンテナーの起動の仕方は、この記事ではコンテナー内に入って systemd
経由で行ったため特権モード (--privileged
) を付けましたが、/usr/sbin/httpd
を直接起動する方法でも良いです。
Deploymentの作成
Deploymentを作ってPodをデプロイします。
# kubectl run my-httpd \
--image=172.26.22.6:5000/my-repo/my-httpd:v0.01 \
--port=80 \
--labels="app=my-sample"
Serviceを作成
作成したDeploymentに対して、Serviceを作成します。
# kubectl expose deployment my-httpd
確認
まず、Podが起動できているかを確認してみましょう。
# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
my-httpd-55cb77848d-d5bvl 1/1 Running 0 20s 10.244.2.169 kube-node2
STATUS
が Running
になっていれば成功です。Kubernetesは、Podをどのノードに配置するかは勝手に決定します(どのノードに配置するかは、コントロールするものではありません)。今回は NODE
が kube-node2 になっているため、Workerノード2に配置されました。 また、Pod単位に内部ネットワークのIPアドレスが割り振られます(IP
の列)。
次に、Serviceを見てみます。
# kubectl get services -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
my-httpd ClusterIP 10.97.83.136 <none> 80/TCP 3s app=my-sample
PORT(S)
の 80/TCP
は内部ネットワークのLISTENポートであり、まだ外部に公開されていません。この段階で画面を確認するには、SSHポート転送を使ってアクセスします。ローカルPCの 80 ポートを Masterノード経由で Masterノードのlocalhost:80にポート転送し、ブラウザから http://localhost でアクセスしてみます。
It works! ではなく、Testing 123... が表示されると思います。
Serviceを外部に公開する
Serviceを外部のポートに転送しましょう。先ほど作成したServiceの設定を編集します。
# kubectl edit services my-httpd
spec.type
の値を ClusterIP
から NodePort
に変更して保存します。
Serviceを見てみましょう。
# kubectl get services -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
my-httpd NodePort 10.97.83.136 <none> 80:31419/TCP 1h app=my-sample
PORT(S)
が80:31419/TCP
に変わっています。これは、内部ネットワークの80ポートに対して、外部ネットワークの31419ポートから転送されます。ポート番号はKubernetesが自動で割り振ります。これで、SSHポート転送をしなくてもブラウザからアクセスできるようになります。ブラウザからMasterノード(172.26.22.85)に対して直接 http://172.26.22.85:31419/ でアクセスすると、再び Testing 123... が表示されると思います。
Serviceにpublic IPを割り振る (できない)
ユーザーがWebサービスにアクセスするのに、:31419
のようなポート番号を付けるのはおかしいので、プロキシーをしてくれるロードバランサーをServiceに設定しましょう。再びServiceを編集します。
# kubectl edit services my-httpd
spec.type
の値を NodePort
から LoadBalancer
に変更して保存します。Serviceを見てみます。
# kubectl get services -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
my-httpd LoadBalancer 10.97.83.136 <pending> 80:31419/TCP 1h app=my-sample
public IPが割り振られえると(時間が掛かりますが)、EXTERNAL-IP
にIPアドレスが表示されます。しかしEC2の場合は <pending> のままpublic IPが割り振られません。これはEC2インスタンスは、1インスタンスにつきIPアドレスが1つしか付与できないからです(だから、仮想IPも作れない)。仕方がないので、EC2の場合はNodePort
で動かすしかありません。Serviceの設定を編集して spec.type
の値を LoadBalancer
から NodePort
に戻しておきましょう。
設定をManifest(YAML)にしておく
DeploymentとServiceの作成をコマンドで行ってしまいましたが、今後の運用のために、設定はYAML(かJSON)で書いたほうが良いです。将来はKaaS(Kubernetes as a Service)が提供するコンソール画面やDashboardから、ボタンクリックでできるようになると思いますが、それはまだまだ遠い未来の話であり、今はYAML(かJSON)で頑張るしかありません。
現在設定されている Pod や Service といった情報のYAML化は、次のコマンドで可能です。
(Podの場合)
# kubectl get pods my-httpd-55cb77848d-d5bvl -o yaml
(Deploymentの場合)
# kubectl get deployments my-httpd -o yaml
(Serviceの場合)
# kubectl get services my-httpd -o yaml
今回作ったmy-httpdのYAMLは、次のようになります。
### deployment
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: my-sample
name: my-httpd
# namespace: my-test
spec:
replicas: 1
selector:
matchLabels:
app: my-sample
template:
metadata:
labels:
app: my-sample
spec:
containers:
- image: 172.26.22.6:5000/my-repo/my-httpd:v0.01
imagePullPolicy: IfNotPresent
name: my-httpd
ports:
- containerPort: 80
protocol: TCP
---
### service
apiVersion: v1
kind: Service
metadata:
labels:
app: my-sample
name: my-httpd
# namespace: my-test
spec:
ports:
- port: 80
protocol: TCP
name: http
selector:
app: my-sample
sessionAffinity: None
type: NodePort
---
コンテナーの新バージョンをリリースする
アプリケーションは一度リリースしたらそれっきり、ということはなく新しい実装やバグ修正などでリリースがあると思います。
リリースする
Deploymentを使うと、リリースは簡単です。先のYAML内にある、spec.template.image
の値を、新しいバージョンのDockerイメージに変更するだけです。
- image: 172.26.22.6:5000/my-repo/my-httpd:v0.01
↓
- image: 172.26.22.6:5000/my-repo/my-httpd:v0.02
Kubernetesに適用します。
# kubectl apply -f (YAMLファイル名)
これだけで自動的にロールアウトが実行されます。
リリースを取り消す(ロールバックする)
リリースしたアプリケーションに問題があったり、Dockerイメージに問題がありコンテナーが起動できなかったときなど、リリースを戻したいときがあります。Kuberentesは古いバージョンのDeploymentも保持しているため、戻すことができます。
1つ前に戻すには、次のコマンド1つを実行するだけです。
# kubectl rollout undo deployments my-httpd
ロールアウトの履歴を見ることもできます。
# kubectl rollout history deployments my-httpd
REVISION CHANGE-CAUSE
2 <node>
1 <none>
ロールアウトをロールバックすると、この履歴が1つ減るのではなく、さらに1つ追加されます。Gitで言うrevertをcommitするイメージでしょうか。そのため、もう一度ロールバックすると、さらに1つ前のバージョンになるのではなく、最初のバージョンになってしまいます。
特定のバージョンにロールバックしたい場合は、--to-revision
で番号を指定します。
# kubectl rollout undo deployments my-httpd --to-revision=(REVISIONの番号)
まとめ
これで何とか最低限のことができましたが、今回話ができたのはKubernetesのほんの一部の機能でしかありません(それでもQiita2回分になりました)。WebサービスはWebアプリケーションだけではなく、データベースも重要かと思います。データベースはWebアプリケーションと異なりステートフルのため、Webアプリケーションと同じように扱えません。Kubernetesはデータベースを扱うStatefulSetというコンポーネントも実装され(1.6ではexperimentalでしたが、1.9でGAになりました)、これも1つの大きな話になります。今回話したPod~Service~Deploymentもまだ一部しか話できていません(内部の仕組みの話は全く書けていませんし)。Kubernetesはいまや大きなプロダクトとなっているため、情報がまとめやすい書籍の出版が待ち望まれるところです。私? 書きませんよ?
参考文献
-
マニュアル(デプロイ)
マニュアルは英語ですが、一度は目を通しておいたほうが良いでしょう。 -
RedHat7上に構築
メモ書きレベルですが、UbuntuではなくRedHat7上で、かつKubernetes最新版の記述になっています。 -
flannel 0.90ではバグあり
私は最初から0.91なのでハマりませんでしたが、大きなバグがあるようです。 -
Kubernetesの本
O'ReillyからKubernetesの本の日本語訳がでました。内容は1.6ベースになっているようで、1.8や1.9の機能であるRoleやMulti Masterの記述はありませんでした。