ビビリフクロウの足跡

とあるインフラSEの勉強&備忘ブログ

Rancher Submarinerを動かしてみた

3/12にRancher公式ブログで発表された、「Submariner」という新しいOSSを動かしてみましたので、軽く所感を書きます。

Submarinerってなに?

複数のKubernetesクラスタのPodネットワークおよびServiceネットワークを繋げるプロダクトです。NodePortサービスを作って外部公開せずとも、複数Kubernetesクラスタ内のPod同士で通信することが可能になります。

公式では以下の用途にマッチするとしています。

利用手順概要

ここからは私が試した手順の概要を記載いたします。といっても公式GitHubが秀逸なので、特に面白い内容ではないですが。。。

submarinerを試すには最低限3つのKubernetesクラスタが必要です。2つは実際に接続したいクラスタ「west」と「east」、残りの1つは「broker」という、westとeastのネットワーク情報を同期する仲介役として動きます。 クラスタ要件として以下が求められるので、ユーザが自由に制御可能なバニラKubernetesで試すのがおすすめです。

  * 任意のクラスタ間でpodネットワークcidrおよびserviceネットワークcidrおよびcluster local domainが異なること
  * 任意のクラスタ間でインターネットを通じて直接通信できるか、同じネットワークに所属していること
  • brokerクラスタに対し、以下のコマンドを実行
# helmの初期化
kubectl -n kube-system create serviceaccount tiller
kubectl create clusterrolebinding tiller \
  --clusterrole=cluster-admin \
  --serviceaccount=kube-system:tiller
helm init --service-account tiller
helm repo update

# submariner-brokerのインストール
SUBMARINER_BROKER_NS=submariner-k8s-broker
helm install submariner-latest/submariner-k8s-broker \
--name ${SUBMARINER_BROKER_NS} \
--namespace ${SUBMARINER_BROKER_NS}
SUBMARINER_BROKER_URL=$(kubectl -n default get endpoints kubernetes -o jsonpath="{.subsets[0].addresses[0].ip}:{.subsets[0].ports[0].port}")
SUBMARINER_BROKER_CA=$(kubectl -n ${SUBMARINER_BROKER_NS} get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='${SUBMARINER_BROKER_NS}-client')].data['ca\.crt']}")
SUBMARINER_BROKER_TOKEN=$(kubectl -n ${SUBMARINER_BROKER_NS} get secrets -o jsonpath="{.items[?(@.metadata.annotations['kubernetes\.io/service-account\.name']=='${SUBMARINER_BROKER_NS}-client')].data.token}"|base64 --decode)
  • IPSec通信するための事前共有キーの作成
SUBMARINER_PSK=$(cat /dev/urandom | LC_CTYPE=C tr -dc 'a-zA-Z0-9' | fold -w 64 | head -n 1)
  • westクラスタに対し、以下のコマンドを実行
# eastクラスタに対する通信のゲートウェイとなるノードにラベル付け
kubectl label node <GATEWAY_NODE> "submariner.io/gateway=true"

# helmの初期化
kubectl -n kube-system create serviceaccount tiller
kubectl create clusterrolebinding tiller \
  --clusterrole=cluster-admin \
  --serviceaccount=kube-system:tiller
helm init --service-account tiller

# submarinerのインストール
helm install submariner-latest/submariner \
--name submariner \
--namespace submariner \
--set ipsec.psk="${SUBMARINER_PSK}" \
--set broker.server="${SUBMARINER_BROKER_URL}" \
--set broker.token="${SUBMARINER_BROKER_TOKEN}" \
--set broker.namespace="${SUBMARINER_BROKER_NS}" \
--set broker.ca="${SUBMARINER_BROKER_CA}" \
--set submariner.clusterId="west-cluster" \
--set submariner.clusterCidr="<westクラスタのpod network cidr>" \
--set submariner.serviceCidr="<westクラスタのservice network cidr>" \
--set submariner.natEnabled="false"
  • eastクラスタに対し、以下のコマンドを実行
# westクラスタに対する通信のゲートウェイとなるノードにラベル付け
kubectl label node <GATEWAY_NODE> "submariner.io/gateway=true"

# helmの初期化
kubectl -n kube-system create serviceaccount tiller
kubectl create clusterrolebinding tiller \
  --clusterrole=cluster-admin \
  --serviceaccount=kube-system:tiller
helm init --service-account tiller

# submarinerのインストール
helm install submariner-latest/submariner \
--name submariner \
--namespace submariner \
--set ipsec.psk="${SUBMARINER_PSK}" \
--set broker.server="${SUBMARINER_BROKER_URL}" \
--set broker.token="${SUBMARINER_BROKER_TOKEN}" \
--set broker.namespace="${SUBMARINER_BROKER_NS}" \
--set broker.ca="${SUBMARINER_BROKER_CA}" \
--set submariner.clusterId="east-cluster" \
--set submariner.clusterCidr="<eastクラスタのpod network cidr>" \
--set submariner.serviceCidr="<eastクラスタのservice network cidr>" \
--set submariner.natEnabled="false"
  • westクラスタ上にnginx deploymentとserviceを作る(動作確認)
kubectl run nginx --image nginx
kubectl expose deploy nginx --port 80
  • 作成したサービスのIPを取得(動作確認)
NGINX_SERVICE_IP=$(kubectl get svc nginx -o jsonpath="{.spec.clusterIP}")
kubectl run busybox --restart=Never --image busybox:1.28 -- sleep 3600 
kubectl exec -it busybox -- wget -q -O - ${NGINX_SERVICE_IP}

無事、nginxのスタートページが表示されればOKです。

まとめ

submarinerをざっと試してみましたが、そもそもKubernetesクラスタはプロジェクトやチームなど意味あるかたまりで分割されるであろうことから、そもそもpodネットワークやserviceネットワークをつなぎたい需要ってそんなにあるのかな? と疑問に思っています。 Rancherの提示している使い方以外に使えるとすれば、PVのクラスタ間移行なんかには便利に使える気はしますね。

現時点ではクラスタ超えのDNSには対応していないので、クラウドネイティブに使うとなるとちょっと物足りないところ。今後の発展に期待です!

Kubernetesの認定資格「CKA」を取得しました 〜これから受ける方へのアドバイス〜

この度、Kubernetesの認定資格「CKA(Certified Kubernetes Administrator)」を取得いたしました! 今後受ける方のお役に立てばと、受験レポートを残しておきます。

要約 〜忙しい人はここだけ読んでね〜

  • 試験に関する説明の書かれている英語のドキュメント「Handbook」「Important Tips」を熟読しよう!
  • Kubernetes完全ガイド」の4章から8章は可能な限り、9章から12章は余力と相談して押さえておこう!
  • Kubernetes The Hard Way」を1度実施し、各オペレーションの意味を理解しておこう!

受験動機

私の場合は、今まで培ってきたKubernetesの技術知識を試すことが目的でした。また副次的な目的で、社内でコンテナ技術推進活動をしているため、その発言に説得力をもたせる意味もありました。

Kubernetesの技術を身につけるきっかけにする」というパターンの方もいるかと思います。

自分がやったこと

自分が受験をするぞ!と決めてやったことは以下の4つです。私の場合は既にKubernetesを結構触ってしまっていたので役に立つかわからないですが、かけた時間を括弧付けで示しておきます。

  • パスポート取得(1週間)
  • 貸し会議室押さえ(30分)
  • 試験のHandbookとImportant Tipsに目を通す(6時間くらい)
  • Kubernetesの機能復習(8時間くらい)
パスポート取得

CKAを受験するには氏名がローマ字記載の写真付き身分証明書が必要になります。おそらくパスポート一択な気がします。

私は国際旅行等で海外に出たことが一回もなく、人生で初めてパスポートを取得しました。まさか資格試験のためにパスポートを取得することになろうとは…

パスポートの発行は私の居住する地域だと1週間程度かかりました。もし受験する熱意があるけどお持ちでない方は、受験予定日を決めたら早めに取得することをおすすめします。

ちなみに発行料は5年パスポートで1万円程度でした。

貸し会議室押さえ

CKAの受験は準備も含めて3時間半程度、誰もいない、誰も入ってこない、静かで、机・椅子以外ほぼ何もないスペースが必要になります。

私の場合は会社の会議室で試験を実施するのが難しい事情があったため、貸し会議室を予約し、受験することにしました。

試験日のスケジューリングをしてから空いている会議室を探すのが良いと思います。2000円くらいあれば借りられると思います。

試験のHandbookとImportant Tipsに目を通す

CKAは国際試験なので現状、試験に関する情報は英語になります。ですので、試験のレギュレーションを理解する時間をしっかり取りました。

Kubernetesの機能復習

先人の方々が残してくれた受験記はチラ見していましたが、なかなか実際の問題のイメージが膨らまなかったので、今まで触ってきた機能の復習は少々行いました。

具体的にはCyberAgentの青山さんが書いた本「Kubernetes完全ガイド」の4章〜12章くらいを見直したことと、Kubernetesクラスタをスクラッチで構築するハンズオン「 Kubernetes The Hard Way」を通しで1回、やり直してみました。

試験当日の対応

試験日当日の流れは以下のようになります。

  • Webカメラ有効化・デスクトップ共有
  • パスポート提示
  • 受験部屋の状態確認
  • Webカメラ位置調整
  • 受験開始
Webカメラ有効化・デスクトップ共有

試験当日は予約した会議室で試験サイトにアクセスし、試験官と英語のチャットでやり取りする形になります。まずはじめにWebカメラを有効化し、デスクトップを有効化するよう、指示されました。

パスポート提示

Webカメラが有効になったら、カメラ経由でパスポートの顔写真が写っているページを試験官に見せます。ここはすんなり通りました。

受験部屋の状態確認

次に試験を受験する部屋を一周くまなくカメラで見せるように指示があります。この際、特に机上を注意してみているようで、机上をゆっくり見せてほしいと指示がありました。

Webカメラ位置調整

試験中は基本的に休憩を申し出ない限りは、受験用端末のスクリーンを見続けていなければなりません。

顔がきれいに試験官に見えていなかったのか、Webカメラを少し下げるように指示されました。ディスプレイに付属のカメラを使う人は辛いディスプレイの角度で受験することになるかもしれないです。私は許容範囲でしたが…

許容できなければ外部Webカメラがあるとよいかも…?!

受験開始

以上の準備を終えて、試験官が試験ページを更新し、問題文とコンソールが表示されました。そしたら試験開始です。開始の合図は特にありませんでした。

試験に受かるために必要だと思うこと

私が考えるCKA合格に必要なことは以下の5点になります。

試験レギュレーションの事前理解

これが大前提になります。試験のルールは全て英語で記載されたHandbook、Important Tipsしかないので、抵抗がある方もいらっしゃるかと思いますが、禁止事項を理解しておかないと即時試験終了されてしまう恐れがあるので、HandbookとImportant Tipsの中身はよく理解するようにすると良いです。

特に、試験中に参照できるWebページの制限(k8sの公式ドキュメント、ブログ、公式GitHubのみ)には注意が必要です。Kubernetes公式ドキュメントからドキュメント検索をかけると、外部ページも引っかかるため、不意にそれらのページリンクをクリックしてしまうと不正行為になります。また、表示できるタブの数(試験サイト以外、一つのみ)も注意が必要なルールの一つです。

kubectlの便利機能の知識

kubectlを使ってオブジェクトの名前だけをリストしたり、特定のラベルを持つオブジェクトだけを検索したりする等、普段使っていない方もkubectlの効率的な使い方は理解しておいたほうが良いでしょう。青山さんの本「Kubernetes完全ガイド」だと4章を網羅的に理解しておけば十分でしょう。

基本的なKubernetesオブジェクトの知識

Deployment、ReplicaSet、Pod、各種Serviceなどの本当に基本的なオブジェクトの作り方から、マニフェストの書き方はしっかり押さえておくべきでしょう。マニフェストのサンプルはKubernetes公式ドキュメントから引っ張ってこれますし、kubectl run --dry-run -o yamlでも確認できるので、ゼロから作るというよりかは各パラメータの意味を理解してカスタマイズできるかが重要になります。

なおその他のオブジェクトについても、私が受けた感覚では「Kubernetes完全ガイド」の5章〜8章は可能な限り押さえ、余力があれば9章、10章を理解しておくぐらいで十分かなと思います。

Kubernetesクラスタの運用の知識

Kubernetesを運用するための知識として、「Kubernetes完全ガイド」の11章の知識は押さえておきましょう。またnode selectortainttolerationなどを用いたPodスケジューリングは覚えておいて損はないと思います。

ツールに頼らないKubernetesクラスタの構築知識

「kubeadm」などのツールを用いてしかKubernetesクラスタを構築したことがない方はぜひ一度Kubernetes The Hard Way」を1度実施してください。そしてオペレーションの一つ一つの意味を理解しておくほうが良いです。

結果 〜スコアと証書〜

上記準備で、無事CKAを取得することができましたが、スコアは80%と、合格ライン74%と比較してギリギリでした。一問以外はわからない問題ではなかったのですが、失点の理由が知りたいのがホンネ…

f:id:bbrfkr:20190306114113p:plain

本レポートが今後CKAを受験する方のお役に立つことを願っております!

KubernetesのHorizontalPodAutoscalerでハマった話

ふとしたきっかけで、deploymentを以下のように定義していたわけです。

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: v1
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: v1
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: v1
    spec:
      containers:
      - image: nginx
        name: nginx
        resources:
          requests:
            cpu: "100m"
            memory: 128Mi
          limits:
            cpu: "100m"
            memory: 256Mi

このマニフェストによるdeployment、kubectl autoscaleでもhpaが機能しません! 以下のように、appラベルはapp: <deployment名>である必要があるみたいです。。。

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        resources:
          requests:
            cpu: "100m"
            memory: 128Mi
          limits:
            cpu: "100m"
            memory: 256Mi

今日、1/3日ぐらいハマってしましました。。。

OpenStack Swiftの既存リングを拡張する

OpenStack Swiftで新しくストレージノードを構築し、既存リングを拡張する機会がありましたので、その際の手順をメモしておきます。

手順

proxyノードで以下のコマンドを叩きます。

# accountリングの拡張
swift-ring-builder account.builder add r<region num>z<zone num>-<storage node ip>:6202/<device name> 100
swift-ring-builder account.builder rebalance

# containerリングの拡張
swift-ring-builder container.builder add r<region num>z<zone num>-<storage node ip>:6201/<device name> 100
swift-ring-builder container.builder rebalance

# objectリングの拡張
swift-ring-builder object.builder add r<region num>z<zone num>-<storage node ip>/<device name> 100
swift-ring-builder object.builder rebalance

region番号とzone番号は既存リング情報から得ることができます。以下コマンドからどうぞ。

swift-ring-builder account.builder
swift-ring-builder container.builder
swift-ring-builder object.builder

Portusでプライベートなコンテナイメージレジストリを立ててみる

Dockerを使う際にまずお世話になるのがパブリックなコンテナイメージレジストリサービスであるDocker Hubかと思うのですが、業務で本格的にDockerを使うとなった際にはクローズドなネットワークセグメントでしかDockerホストを実行できなかったりして、プライベートなイメージレジストリが欲しくなりますよね?

そんなときのためにDocker側でプライベートレジストリサーバ用のコンテナイメージが用意されているのですが、UIがないのでイメージを一覧や削除などの管理をしようとすると、直にAPIを叩かなくてはならず辛みがあります。そこでUI付きのプライベートレジストリを探すと、プライベートGitリポジトリでも有名なGitLabが引っかかるのですが、あれってリポジトリだけでなく、CIやKubernetes連携など、いろいろな機能がついているじゃないですか。そこまで仰々しくしたくないんですよね…

シンプルにレジストリとUIと認証周りの機能がついていればいいのよ! というモチベーションで再度探すと、ありました! PortusというOSSが…!

が、例のごとく構築に少しつまずいたので、構築・設定方法を残しておきたいと思います。今後Portusを構築する人のために、つまづきポイントを共有できればと思います。

Portusとは

SUSEが開発している、プライベートレジストリに認証機能とWeb UIを提供するOSSです。 ざっくりとどんなものか知りたい方はCyberAgentの青山さんが記事にしてくれているので、そちらをご参照。

構築手順

では、本題に入っていきましょう。

前提

まず前提を示していきますが、以下のとおりです。

  • OS: CentOS 7
  • IP: 192.168.0.100
  • 作業ユーザ: root
  • 構築方法: docker-composeを利用

PortusもDockerコンテナで動作するので、パッケージの導入手順くらいを置き換えていただければ、Dockerが動作するどんなLinuxでも動作するかと思います。

Dockerのインストール

MinimalのCentOS 7のマシンにログインしたら、まずDockerをインストールします。

yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce docker-ce-cli containerd.io

インストールしたらサービスを設定しましょう。

systemctl enable docker
systemctl start docker

docker-composeのインストール

docker-composeを使ってPortusを構築するので、インストールしておきます。

curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/bin/docker-compose
chmod +x /usr/bin/docker-compose

Gitのインストール

Portusのソースコードの中にサンプルのdocker-compose.ymlが入っています。これをDLするためにGitをインストールします。

yum install -y git

Portusのソースコードを取得

Portusのソースコードを取得します。ソースコード/etc/docker/に置くことにします。

cd /etc/docker
git clone https://github.com/SUSE/Portus.git

今回はVer 2.4をデプロイしたいと思います。

cd Portus
git checkout remotes/origin/v2.4
git checkout -b v2.4

以降の作業は/etc/docker/Portus/example/composeで行いますので、移動しておきます。

cd /etc/docker/Portus/examples/compose

証明書群の作成

PortusのWeb UIおよびプライベートレジストリに使われるSSL証明書を作成します。今回は自己署名認証局(CA)を同ホスト内に作り、証明書を発行します。

まずはCAを作成します。

cd secrets
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=192.168.0.100" -days 7305 -out ca.crt

次にサーバの秘密鍵を作ります。

openssl genrsa -out portus.key 2048

CSRを発行して、CAで署名付き証明書を作ります。

cat <<EOF > csr.conf
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn

[ dn ]
C = JP
ST = Tokyo
L = Shinjuku
#O = <organization>
#OU = <organization unit>
CN = 192.168.0.100

[ req_ext ]
subjectAltName = @alt_names

[ alt_names ]
IP.1 = 192.168.0.100

[ v3_ext ]
authorityKeyIdentifier=keyid,issuer:always
basicConstraints=CA:FALSE
keyUsage=keyEncipherment,dataEncipherment
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=@alt_names
EOF

openssl req -new -key portus.key -out portus.csr -config csr.conf
openssl x509 -req -in portus.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out portus.crt -days 10000 -extensions v3_ext -extfile csr.conf
cd ..

.envファイルの修正

docker-composeに読み込ませる.envファイルを修正します。

cat <<EOF > .env
MACHINE_FQDN=192.168.0.100

SECRET_KEY_BASE=$(openssl rand -hex 64)
PORTUS_PASSWORD=password
DATABASE_PASSWORD=p@ssw0rd
EOF

nginx.confの修正

nginxの設定ファイルnginx.conf内のserver_nameパラメータを修正します。

sed -i 's/server_name.*/server_name 192.168.0.100;/g' nginx/nginx.conf

initスクリプトの修正

今回作成したCAをプライベートレジストリに信頼させるため、以下の記述をregistry/initに追加します。

#!/bin/sh

set -x

cp /secrets/portus.crt /usr/local/share/ca-certificates
# here!!
cp /secrets/ca.crt /usr/local/share/ca-certificates

update-ca-certificates
registry serve /etc/docker/registry/config.yml

Portusの起動

あとはdocker-composeで起動するだけです。

docker-compose up -d

以上で、構築は完了です。あとはPortus上で初期設定をしていきましょう。

設定手順

Portus上で設定を行うためにhttps://192.168.0.100にアクセスしましょう!

管理ユーザ設定

まずは管理ユーザであるadminユーザを作ります。

f:id:bbrfkr:20190202203426p:plain

レジストリサーバ登録

レジストリサーバ登録をして…

f:id:bbrfkr:20190202203457p:plain

ユーザ作成

一般ユーザbbrfkrを作ります。

f:id:bbrfkr:20190202203511p:plain f:id:bbrfkr:20190202203540p:plain

再ログイン

そして再ログイン。

f:id:bbrfkr:20190202203553p:plain f:id:bbrfkr:20190202203605p:plain f:id:bbrfkr:20190202203636p:plain

実際にイメージをプッシュしてみる

作ったレジストリサーバにログインするためにはCA証明書をクライアントマシンの適切な場所に置く必要があります。以下の場所に置きましょう。

/etc/docker/certs.d/192.168.0.100/ca.crt

その後レジストリサーバにログインして、nginx:latestをプライベートレジストリにプッシュしてみます。

docker login 192.168.0.100
docker pull nginx:latest
docker tag nginx:latest 192.168.0.100/bbrfkr/nginx:v1.0
docker push 192.168.0.100/bbrfkr/nginx:v1.0

プッシュが成功すると、Portus上で確認できるようになります。

f:id:bbrfkr:20190202203652p:plain

KubeVirtを使ってみる

KubeVirtをお試ししてみたので、その際のメモを残しておきます。

KubeVirtとは

KubeVirtはRed Hat社が主に開発している、CNCF MemberプロダクトのOSSです。KubeVirtを使うとKubernetes上で仮想マシンを管理することができるようになります。これによりコンテナよりも隔離レベルの高い仮想マシンを使いながら、KubernetesのCloud Nativeな機能であるPodネットワーク、Service、サービスディスカバリを仮想マシンに対し適用することができるようになります。

KubeVirtのインストール

インストールは至極簡単です。Kubernetesクラスタを用意したら、以下のコマンドを実行するだけです。

export VERSION=v0.11.0
kubectl create -f https://github.com/kubevirt/kubevirt/releases/download/$VERSION/kubevirt.yaml

もし、Workerノードが仮想化支援機構(IntelでいうVT-x)に対応しておらず、ハードウェアエミュレーションを使いたい場合は、事前に以下のコマンドを実行します。

kubectl create configmap -n kube-system kubevirt-config --from-literal debug.useEmulation=true

KubeVirtを使ってみる

KubeVirtで仮想マシンを操作するには、専用のCLIツールであるvirtctlがあると便利です。virtctlをインストールしましょう。

curl -L -o virtctl https://github.com/kubevirt/kubevirt/releases/download/$VERSION/virtctl-$VERSION-linux-amd64
chmod +x virtctl
sudo chown root:root virtctl
sudo mv virtctl /usr/bin

インストールしたらまずは超軽量OSであるCirrOSを起動してみましょう。仮想マシンオブジェクトをKubernetes上に作成します。

kubectl apply -f https://raw.githubusercontent.com/kubevirt/demo/master/manifests/vm.yaml

kubectl get vmコマンドを実行すると、以下のような出力が得られると思います。

NAME     AGE   RUNNING   VOLUME
testvm   6s    false

RUNNIGfalseとあるように、この時点ではまだ仮想マシンは起動していません。起動にはvirtctlコマンドを使って以下のようにします。

virtctl start testvm

すると、RUNNINGtrueに変わります。

NAME     AGE   RUNNING   VOLUME
testvm   1m    true

実は仮想マシンオブジェクトはコンテナで言うReplicaSetのようなもので、仮想マシンの起動定義情報が格納されているだけです。実際の仮想マシンのポインタ情報は「仮想マシンインスタンスオブジェクト」に格納されています。仮想マシンインスタンスオブジェクトを一覧するにはkubectl get vmiコマンドを実行します。

NAME     AGE   PHASE     IP           NODENAME
testvm   3m    Running   10.100.0.6   kubevirt-test-jbsl2irizcpz-minion-1

すると、上記のように仮想マシンの現在の状態やIP情報などを手に入れることができます。

仮想マシンのコンソール画面(CUIベース or GUIベース)にアクセスするには、またvirtctlコマンドを使います。

virtctl console testvm
virtctl vnc testvm

しかし、Cloudイメージを使う場合、通常パスワード認証できないので、これらのコマンドを使う機会は少ないでしょう。

仮想マシンSSHログインするにはコンテナと同様に仮想マシンを Service で外部公開する必要があります。これもvirtctlコマンドで簡単に行なえます。

virtctl expose vm testvm --name testvm --port 22 --type NodePort

kubectl get svc -o wideすると、確かにNodePort Serviceが作成されてますね!

NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE   SELECTOR
kubernetes   ClusterIP   10.254.0.1    <none>        443/TCP        18m   <none>
testvm       NodePort    10.254.20.5   <none>        22:30296/TCP   64s   kubevirt.io/domain=testvm,kubevirt.io/size=small

どこかのNodeの30296番ポートをsshで突っついてみましょう。ユーザ名はcirros、パスワードはgocubsgoです。

ssh -p 30296 cirros@<NodeのIP>
$

無事ログインできましたか?

仮想マシンの停止にもvirtctlを使います。

virtctl stop testvm 

仮想マシンの削除にはkubectl delete vmコマンドです。これを実行すると該当の仮想マシンインスタンスも一緒に消えます。

kubectl delete vm testvm

最後に

いかがでしたでしょうか。仮想マシンの隔離レベルを担保しなくてはいけないけれど、コンテナは使えないといったケースや、コンテナネットワークに仮想マシンを直接繋いでレイテンシを改善したい場合などに使えそうかなと思っています。 時間を見つけて任意の仮想マシンイメージを起動する方法も書こうと思いますので、お楽しみに!

Kubernetes the Hard Way on OpenStack

Kubernetesの構築をスクラッチで実践できるチュートリアルとしてKubernetes the Hard Wayがあります。来年のはじめくらいにCKAを取得したいと考えているので、本チュートリアルを自宅のOpenStack上で実施してみました。その際の実施手順と少しばかりの解説を残しておきたいと思います。

前提

OpenStackには次のコンポーネントがインストールされているとします。

  • Keystone
  • Glance
  • Nova
  • Neutron
  • Neutron LBaaSv2
  • Designate

次のインスタンスを用意し、HA構成のKubernetesを構成します。 ホスト名でFloatingIPに名前解決ができるようにDesignateを構成しておきます。

  • Masterサーバ x3

    • OS: CentOS 7 Minimal Install
    • vCPU: 2
    • Mem: 4GB
    • ホスト名: hardway-master-[0:2].bbrfkr.mydns.jp
    • IP: 10.0.0.[10:12]
    • FloatingIP: 192.168.0.[10:12]
  • Workerサーバ x3

    • OS: CentOS 7 Minimal Install
    • vCPU: 2
    • Mem: 4GB
    • ホスト名: hardway-worker-[0:2].bbrfkr.mydns.jp
    • IP: 10.0.0.[20:22]
    • FloatingIP: 192.168.0.[20:22]

また、FloatingIPに対して到達可能なネットワーク上に作業用端末を用意しておきます。作業用端末ではあらかじめ以下のコマンドが叩けるように準備しておきます。私の場合はWindows 10にWSLのUbuntuをインストールして、その上から叩けるようにしました。

  • cfssl
  • cfssljson
  • kubectl
  • openstack
  • neutron

Masterサーバのkube-apiserverに対するエンドポイントとしてNeutron LBaaSv2にてロードバランサを切っておきます。また、VIP portに対するセキュリティグループも適切に設定しておきましょう。

  • Neutron LBaaSv2 LB
    • 名前: kubernetes-the-hard-way
    • Internal VIP: 10.0.0.100
    • FloatingIP: 192.168.0.100
    • リスナーポート: 6443

手順

以降、Hard Wayな手順を示して行きます。作業対象は()付きでmasters、workers、bastion(作業用端末の意)で示していきます。

OS設定

  • (masters, workers) OSパッケージの更新とSELinuxの無効化をしておきます。これはKubernetesのインストール要件になります。
sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/g' /etc/selinux/config
sudo yum update -y
sudo reboot

CAおよび各種証明書の作成

ここではkube-apiserverとhttps通信するために必要な各種証明書を作成していきます。

  • (bastion) CAの署名における設定とCA証明書および鍵を作成します。
cat > ca-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "8760h"
    },
    "profiles": {
      "kubernetes": {
        "usages": ["signing", "key encipherment", "server auth", "client auth"],
        "expiry": "8760h"
      }
    }
  }
}
EOF

cat > ca-csr.json <<EOF
{
  "CN": "Kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "CA",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert -initca ca-csr.json | cfssljson -bare ca
  • (bastion) Kubernetesクラスタのadminユーザに対するクライアント証明書および鍵を作成します。Kubernetesのx509証明書認証においてはCN(Common Name)に指定した値がクラスタのユーザ名、O(Organization)に指定した値がグループ名として扱われます。system:mastersグループはデフォルトでKubernetesクラスタのフルコントロール権限を持つグループです。
cat > admin-csr.json <<EOF
{
  "CN": "admin",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:masters",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  admin-csr.json | cfssljson -bare admin
  • (bastion) kubeletがkube-apiserverと通信するときのクライアント証明書および鍵を作成します。ここでは厳密にはNode Authorizationというkube-apiserverの認可機能を使うために、CNにsystem:node:<インスタンス名>、Oにsystem:nodesという値を指定するのが肝となります。
for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; do
cat > ${instance}-csr.json <<EOF
{
  "CN": "system:node:${instance}",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:nodes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

EXTERNAL_IP=$(openstack server show -f json ${instance} | \
              jq -r .addresses | \
              awk -F= '{ print $2 }' | \
              sed 's/,//g' | \
              awk '{ print $2 }')

INTERNAL_IP=$(openstack server show -f json ${instance} | \
              jq -r .addresses | \
              awk -F= '{ print $2 }' | \
              sed 's/,//g' | \
              awk '{ print $1 }')

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=${instance},${EXTERNAL_IP},${INTERNAL_IP} \
  -profile=kubernetes \
  ${instance}-csr.json | cfssljson -bare ${instance}
done
  • (bastion) kube-controller-managerがkube-apiserverと通信するための証明書および鍵を作成します。
cat > kube-controller-manager-csr.json <<EOF
{
  "CN": "system:kube-controller-manager",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:kube-controller-manager",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager
  • (bastion) kube-proxyがkube-apiserverと通信するための証明書および鍵を作成します。
cat > kube-proxy-csr.json <<EOF
{
  "CN": "system:kube-proxy",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:node-proxier",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-proxy-csr.json | cfssljson -bare kube-proxy
  • (bastion) kube-schedulerがkube-apiserverと通信するための証明書および鍵を作成します。
cat > kube-scheduler-csr.json <<EOF
{
  "CN": "system:kube-scheduler",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:kube-scheduler",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-scheduler-csr.json | cfssljson -bare kube-scheduler
  • (bastion) kube-apiserver用のサーバ証明書および鍵を作成します。この証明書で許可されたCNやSAN(Subject Alternative Name)でアクセスしなければkube-apiservertとhttps通信できないので、登録するSAN(cfssl gencertコマンドの-hostnameオプションのところ)に注意します。
KUBERNETES_PUBLIC_ADDRESS=$(neutron floatingip-list | grep \
                          $(neutron lbaas-loadbalancer-show kubernetes-the-hard-way | \
                            grep vip_port_id | \
                            awk '{ print $4 }') | \
                          awk '{ print $8 }')
KUBERNETES_INTERNAL_ADDRESS=$(neutron lbaas-loadbalancer-show kubernetes-the-hard-way | \
                              grep vip_address | \
                              awk '{ print $4 }')
KUBERNETES_PUBLIC_DNS=kubernetes-the-hard-way.bbrfkr.mydns.jp

cat > kubernetes-csr.json <<EOF
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
MASTER_INTERNAL_IPS=${MASTER_INTERNAL_IPS}$(openstack server show -f json ${instance} | \
              jq -r .addresses | \
              awk -F= '{ print $2 }' | \
              sed 's/,//g' | \
              awk '{ print $1 }'),
done

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=10.32.0.1,${MASTER_INTERNAL_IPS}${KUBERNETES_PUBLIC_ADDRESS},${KUBERNETES_INTERNAL_ADDRESS},${KUBERNETES_PUBLIC_DNS},127.0.0.1,kubernetes.default \
  -profile=kubernetes \
  kubernetes-csr.json | cfssljson -bare kubernetes
  • (bastion) ServiceAccountのトークン発行用キーペアを作成します。
cat > service-account-csr.json <<EOF
{
  "CN": "service-accounts",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  service-account-csr.json | cfssljson -bare service-account
  • (bastion) 証明書群をMasterサーバ、Workerサーバに配布します。
for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; do
  scp ca.pem ${instance}-key.pem ${instance}.pem centos@${instance}.bbrfkr.mydns.jp:~/
done

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
  scp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
    service-account-key.pem service-account.pem centos@${instance}.bbrfkr.mydns.jp:~/
done

kubeconfig群の作成

ここではkube-apiserverと通信するために必要な認証ファイルであるkubeconfigファイル群を作成していきます。

  • (bastion) kubelet用のkubeconfigを作成します。
KUBERNETES_PUBLIC_ADDRESS=$(neutron floatingip-list | grep \
                          $(neutron lbaas-loadbalancer-show kubernetes-the-hard-way | \
                            grep vip_port_id | \
                            awk '{ print $4 }') | \
                          awk '{ print $8 }')

for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; do
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \
    --kubeconfig=${instance}.kubeconfig

  kubectl config set-credentials system:node:${instance} \
    --client-certificate=${instance}.pem \
    --client-key=${instance}-key.pem \
    --embed-certs=true \
    --kubeconfig=${instance}.kubeconfig

  kubectl config set-context default \
    --cluster=kubernetes-the-hard-way \
    --user=system:node:${instance} \
    --kubeconfig=${instance}.kubeconfig

  kubectl config use-context default --kubeconfig=${instance}.kubeconfig
done
  • (bastion) kube-proxy用のkubeconfigを作成します。
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \
  --kubeconfig=kube-proxy.kubeconfig

kubectl config set-credentials system:kube-proxy \
  --client-certificate=kube-proxy.pem \
  --client-key=kube-proxy-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-proxy.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:kube-proxy \
  --kubeconfig=kube-proxy.kubeconfig

kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
  • (bastion) kube-controller-manager用のkubeconfigを作成します。
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://127.0.0.1:6443 \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config set-credentials system:kube-controller-manager \
  --client-certificate=kube-controller-manager.pem \
  --client-key=kube-controller-manager-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:kube-controller-manager \
  --kubeconfig=kube-controller-manager.kubeconfig

kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig
  • (bastion) kube-scheduler用のkubeconfigを作成します。
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://127.0.0.1:6443 \
  --kubeconfig=kube-scheduler.kubeconfig

kubectl config set-credentials system:kube-scheduler \
  --client-certificate=kube-scheduler.pem \
  --client-key=kube-scheduler-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-scheduler.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:kube-scheduler \
  --kubeconfig=kube-scheduler.kubeconfig

kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig
  • (bastion) adminユーザ用のkubeconfigを作成します。
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://127.0.0.1:6443 \
  --kubeconfig=admin.kubeconfig

kubectl config set-credentials admin \
  --client-certificate=admin.pem \
  --client-key=admin-key.pem \
  --embed-certs=true \
  --kubeconfig=admin.kubeconfig

kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=admin \
  --kubeconfig=admin.kubeconfig

kubectl config use-context default --kubeconfig=admin.kubeconfig
  • (bastion) kubeconfig群をMasterサーバ、Workerサーバに配布します。
for instance in hardway-worker-0 hardway-worker-1 hardway-worker-2; do
  scp ${instance}.kubeconfig kube-proxy.kubeconfig centos@${instance}.bbrfkr.mydns.jp:~/
done

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
  scp admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig centos@${instance}.bbrfkr.mydns.jp:~/
done

データ暗号化用設定ファイルの作成

Kubernetesではetcdにクラスタ情報が格納されますが、ここではその格納データを暗号化するための設定を実施します。

  • (bastion) クラスタデータ暗号化用設定ファイルを作成し、Masterサーバに配布します。
ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
cat > encryption-config.yaml <<EOF
kind: EncryptionConfig
apiVersion: v1
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: ${ENCRYPTION_KEY}
      - identity: {}
EOF

for instance in hardway-master-0 hardway-master-1 hardway-master-2; do
  scp encryption-config.yaml centos@${instance}.bbrfkr.mydns.jp:~/
done

etcdクラスタの構築

ここではMasterサーバ上でetcdのHAクラスタを構築します。

  • (masters) etcdバイナリのダウンロードと適切な場所への配置を行います。
sudo yum -y install wget
wget "https://github.com/coreos/etcd/releases/download/v3.3.9/etcd-v3.3.9-linux-amd64.tar.gz"

tar -xvf etcd-v3.3.9-linux-amd64.tar.gz
sudo mv etcd-v3.3.9-linux-amd64/etcd* /usr/local/bin/
  • (masters) etcd用のsystemd unitファイルを作成し、サービスを有効化します。
sudo mkdir -p /etc/etcd /var/lib/etcd
sudo cp ca.pem kubernetes-key.pem kubernetes.pem /etc/etcd/

INTERNAL_IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
ETCD_NAME=$(hostname -s)
MASTER_0_HOSTNAME=hardway-master-0
MASTER_0_INTERNAL_IP=10.0.0.10
MASTER_1_HOSTNAME=hardway-master-1
MASTER_1_INTERNAL_IP=10.0.0.11
MASTER_2_HOSTNAME=hardway-master-2
MASTER_2_INTERNAL_IP=10.0.0.12
cat <<EOF | sudo tee /etc/systemd/system/etcd.service
[Unit]
Description=etcd
Documentation=https://github.com/coreos

[Service]
ExecStart=/usr/local/bin/etcd \\
  --name ${ETCD_NAME} \\
  --cert-file=/etc/etcd/kubernetes.pem \\
  --key-file=/etc/etcd/kubernetes-key.pem \\
  --peer-cert-file=/etc/etcd/kubernetes.pem \\
  --peer-key-file=/etc/etcd/kubernetes-key.pem \\
  --trusted-ca-file=/etc/etcd/ca.pem \\
  --peer-trusted-ca-file=/etc/etcd/ca.pem \\
  --peer-client-cert-auth \\
  --client-cert-auth \\
  --initial-advertise-peer-urls https://${INTERNAL_IP}:2380 \\
  --listen-peer-urls https://${INTERNAL_IP}:2380 \\
  --listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379 \\
  --advertise-client-urls https://${INTERNAL_IP}:2379 \\
  --initial-cluster-token etcd-cluster-0 \\
  --initial-cluster ${MASTER_0_HOSTNAME}=https://${MASTER_0_INTERNAL_IP}:2380,${MASTER_1_HOSTNAME}=https://${MASTER_1_INTERNAL_IP}:2380,${MASTER_2_HOSTNAME}=https://${MASTER_2_INTERNAL_IP}:2380 \\
  --initial-cluster-state new \\
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable etcd
sudo systemctl start etcd
  • (masters) etcdの正常動作確認をするには以下のコマンドを実行します。
sudo su - -lc \
"ETCDCTL_API=3 etcdctl member list \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/etcd/ca.pem \
--cert=/etc/etcd/kubernetes.pem \
--key=/etc/etcd/kubernetes-key.pem"

Masterサーバの構築

ここではMasterサーバの構築を実施します。

sudo mkdir -p /etc/kubernetes/config
  • (masters) Masterサーバ用コンポーネントのバイナリをダウンロードし、適切な場所に配置します。
wget \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-apiserver" \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-controller-manager" \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-scheduler" \
"https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kubectl"

chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl
sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/
  • (masters) kube-apiserver用の証明書群の配置とsystemd unitファイルを作成します。ここでKubernetesのServiceリソースで利用するIP帯10.32.0.0/24を指定します。
sudo mkdir -p /var/lib/kubernetes/
sudo mv ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
  service-account-key.pem service-account.pem \
  encryption-config.yaml /var/lib/kubernetes/

INTERNAL_IP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
MASTER_0_INTERNAL_IP=10.0.0.10
MASTER_1_INTERNAL_IP=10.0.0.11
MASTER_2_INTERNAL_IP=10.0.0.12

cat <<EOF | sudo tee /etc/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
  --advertise-address=${INTERNAL_IP} \\
  --allow-privileged=true \\
  --apiserver-count=3 \\
  --audit-log-maxage=30 \\
  --audit-log-maxbackup=3 \\
  --audit-log-maxsize=100 \\
  --audit-log-path=/var/log/audit.log \\
  --authorization-mode=Node,RBAC \\
  --bind-address=0.0.0.0 \\
  --client-ca-file=/var/lib/kubernetes/ca.pem \\
  --enable-admission-plugins=Initializers,NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
  --enable-swagger-ui=true \\
  --etcd-cafile=/var/lib/kubernetes/ca.pem \\
  --etcd-certfile=/var/lib/kubernetes/kubernetes.pem \\
  --etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \\
  --etcd-servers=https://${MASTER_0_INTERNAL_IP}:2379,https://${MASTER_1_INTERNAL_IP}:2379,https://${MASTER_2_INTERNAL_IP}:2379 \\
  --event-ttl=1h \\
  --experimental-encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
  --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
  --kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \\
  --kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \\
  --kubelet-https=true \\
  --runtime-config=api/all \\
  --service-account-key-file=/var/lib/kubernetes/service-account.pem \\
  --service-cluster-ip-range=10.32.0.0/24 \\
  --service-node-port-range=30000-32767 \\
  --tls-cert-file=/var/lib/kubernetes/kubernetes.pem \\
  --tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (masters) kube-controller-manager用のkubeconfigの配置とsystemd unitファイルを作成します。--cluster-cidrオプションにはPodが利用するIP帯172.17.0.0/16を指定しておきます。
sudo mv kube-controller-manager.kubeconfig /var/lib/kubernetes/
cat <<EOF | sudo tee /etc/systemd/system/kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-controller-manager \\
  --address=0.0.0.0 \\
  --cluster-cidr=172.17.0.0/16 \\
  --cluster-name=kubernetes \\
  --cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \\
  --cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \\
  --kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
  --leader-elect=true \\
  --root-ca-file=/var/lib/kubernetes/ca.pem \\
  --service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \\
  --service-cluster-ip-range=10.32.0.0/24 \\
  --use-service-account-credentials=true \\
  --allocate-node-cidrs=true \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (masters) kube-scheduler用の設定ファイルおよびkubeconfigの配置とsystemd unitファイルを作成します。
sudo mv kube-scheduler.kubeconfig /var/lib/kubernetes/
cat <<EOF | sudo tee /etc/kubernetes/config/kube-scheduler.yaml
apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
clientConnection:
  kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig"
leaderElection:
  leaderElect: true
EOF

cat <<EOF | sudo tee /etc/systemd/system/kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-scheduler \\
  --config=/etc/kubernetes/config/kube-scheduler.yaml \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (masters) Masterサーバの各種サービスを有効化します。
sudo systemctl daemon-reload
sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler
sudo systemctl start kube-apiserver kube-controller-manager kube-scheduler
  • (masters(hardway-master-0のみ)) kube-apiserverがWorkerサーバのkubeletに接続可能にするため、RBACの設定を入れておきます。
cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-apiserver-to-kubelet
rules:
  - apiGroups:
      - ""
    resources:
      - nodes/proxy
      - nodes/stats
      - nodes/log
      - nodes/spec
      - nodes/metrics
    verbs:
      - "*"
EOF

cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: system:kube-apiserver
  namespace: ""
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:kube-apiserver-to-kubelet
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: kubernetes
EOF

Workerサーバの構築

ここではWorkerサーバを構築し、Kubernetesクラスタに参加させます。

  • (workers) OS依存パッケージのインストールを行います。
sudo yum -y install socat conntrack ipset
  • (workers) Workerサーバ用コンポーネントのバイナリをダウンロードし、適切な場所に配置します。
sudo yum -y install wget
wget \
  https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.13.0/crictl-v1.13.0-linux-amd64.tar.gz \
    https://github.com/containernetworking/plugins/releases/download/v0.6.0/cni-plugins-amd64-v0.6.0.tgz \
  https://github.com/opencontainers/runc/releases/download/v1.0.0-rc5/runc.amd64 \
  https://github.com/containerd/containerd/releases/download/v1.2.0-rc.0/containerd-1.2.0-rc.0.linux-amd64.tar.gz \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kubectl \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kube-proxy \
  https://storage.googleapis.com/kubernetes-release/release/v1.13.1/bin/linux/amd64/kubelet

sudo mkdir -p \
  /var/lib/kubelet \
  /var/lib/kube-proxy \
  /var/lib/kubernetes \
  /var/run/kubernetes \
  /etc/cni/net.d \
  /opt/cni/bin

sudo mv runc.amd64 runc
chmod +x kubectl kube-proxy kubelet runc
sudo mv kubectl kube-proxy kubelet runc /usr/local/bin/
sudo tar -xvf crictl-v1.13.0-linux-amd64.tar.gz -C /usr/local/bin/
sudo tar -xvf cni-plugins-amd64-v0.6.0.tgz -C /opt/cni/bin/
sudo tar -xvf containerd-1.2.0-rc.0.linux-amd64.tar.gz
sudo mv bin/* /bin
sudo rmdir bin
  • (workers) CNI(Container Network Interface)のloopbackインタフェースに対する設定を実施します。
cat <<EOF | sudo tee /etc/cni/net.d/99-loopback.conf
{
    "cniVersion": "0.3.1",
    "type": "loopback"
}
EOF
  • (workers) コンテナランタイム containerdの設定ファイルとsystemd unitファイルを配置します。本家Kubernetes the Hard WayではgVisorにも対応した設定を行っていますが、CentOS 7のカーネルがデフォルトでは古く、gVisorに対応していないため、省いています。
sudo mkdir -p /etc/containerd/
cat << EOF | sudo tee /etc/containerd/config.toml
[plugins]
  [plugins.cri.containerd]
    snapshotter = "overlayfs"
    [plugins.cri.containerd.default_runtime]
      runtime_type = "io.containerd.runtime.v1.linux"
      runtime_engine = "/usr/local/bin/runc"
      runtime_root = ""
EOF

cat <<EOF | sudo tee /etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target

[Service]
ExecStartPre=/sbin/modprobe overlay
ExecStart=/bin/containerd
Restart=always
RestartSec=5
Delegate=yes
KillMode=process
OOMScoreAdjust=-999
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity

[Install]
WantedBy=multi-user.target
EOF
  • (workers) kubelet用の証明書群およびkubeconfigを配置し、設定ファイルとsystemd unitファイルを作成します。
WORKER_HOSTNAME=$(hostname -s)
sudo mv ${WORKER_HOSTNAME}-key.pem ${WORKER_HOSTNAME}.pem /var/lib/kubelet/
sudo mv ${WORKER_HOSTNAME}.kubeconfig /var/lib/kubelet/kubeconfig
sudo mv ca.pem /var/lib/kubernetes/

cat <<EOF | sudo tee /var/lib/kubelet/kubelet-config.yaml
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true
  x509:
    clientCAFile: "/var/lib/kubernetes/ca.pem"
authorization:
  mode: Webhook
clusterDomain: "cluster.local"
clusterDNS:
  - "10.32.0.10"
resolvConf: "/etc/resolv.conf"
runtimeRequestTimeout: "15m"
tlsCertFile: "/var/lib/kubelet/${WORKER_HOSTNAME}.pem"
tlsPrivateKeyFile: "/var/lib/kubelet/${WORKER_HOSTNAME}-key.pem"
EOF

cat <<EOF | sudo tee /etc/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=containerd.service
Requires=containerd.service

[Service]
ExecStart=/usr/local/bin/kubelet \\
  --config=/var/lib/kubelet/kubelet-config.yaml \\
  --container-runtime=remote \\
  --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock \\
  --image-pull-progress-deadline=2m \\
  --kubeconfig=/var/lib/kubelet/kubeconfig \\
  --network-plugin=cni \\
  --register-node=true \\
  --hostname-override=${WORKER_HOSTNAME}
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (workers) kube-proxy用のkubeconfigを配置し、設定ファイルとsystemd unitファイルを作成します。
sudo mv kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig

cat <<EOF | sudo tee /var/lib/kube-proxy/kube-proxy-config.yaml
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
clientConnection:
  kubeconfig: "/var/lib/kube-proxy/kubeconfig"
mode: "iptables"
clusterCIDR: "172.17.0.0/16"
EOF

cat <<EOF | sudo tee /etc/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Kube Proxy
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-proxy \\
  --config=/var/lib/kube-proxy/kube-proxy-config.yaml
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
  • (workers) Workerサーバのサービスを有効化します。同時にKubernetesクラスタへWorkerサーバ登録が行われます。
sudo systemctl daemon-reload
sudo systemctl enable containerd kubelet kube-proxy
sudo systemctl start containerd kubelet kube-proxy

CNI flannelとサービスディスカバリ corednsの導入

ここでは、作業用端末のkubectlを作成したKubernetesクラスタに接続できるよう設定し、CNIとサービスディスカバリを導入して最低限、Kubernetesが機能するようにします。

kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://kubernetes-the-hard-way.bbrfkr.mydns.jp:6443

kubectl config set-credentials admin \
  --client-certificate=admin.pem \
  --client-key=admin-key.pem

kubectl config set-context kubernetes-the-hard-way \
  --cluster=kubernetes-the-hard-way \
  --user=admin

kubectl config use-context kubernetes-the-hard-way
  • (bastion) CNI flannelをデプロイします。
wget https://raw.githubusercontent.com/coreos/flannel/bc79dd1505b0c8681ece4de4c0d86c5cd2643275/Documentation/kube-flannel.yml
sed -i 's@10.244.0.0/16@172.17.0.0/16@g' kube-flannel.yml
kubectl apply -f kube-flannel.yml
  • (bastion) サービスディスカバリ corednsをデプロイします。
kubectl apply -f https://storage.googleapis.com/kubernetes-the-hard-way/coredns.yaml