尝试手动搭建 Kubernetes 集群(1.7 版)
今年も末になりました。なんだか最後の3ヶ月くらいが随分ドタバタしてたなぁってくらいしか、現場での記憶がないのはどうしたことか・・・。
今の現場では、どちらかと言うとレガシーよりのアーキテクチャ+AWSも自由がない、というところで、中々新しいことを実験したりするのが難しいです。
しかし、なんか新しいこと(自分にとって)をやりたいなーって思っていたところ、今年のRe:Inventで AWS上でのManaged Kubernetes が発表されました。
Google Cloud PlatformのGKEは前から知っていたし、Kubernetesというのがコンテナクラスタ管理のデファクトになったということも聞いていました。興味はあったんですが、どうにも食指が動かず・・・。
しかし、AWS上で KubernetesのManaged Service が出来たことで、仕事で利用する可能性も上がり、かつ最近Dockerを結構触っていることもあり、コンテナの管理に興味がでたところだったので、kubernetesを触ってみることにしました。
Tutorialが普通じゃない
大多数情况下,这种管理工具的构建是困难的(安装时间很长),但当我看到 Kubernetes 的设置时感到惊讶。
何が驚いたかって、まず最初っから手動での構築はやめとけと言わんばかりに、Managed Serviceや構築済みの環境https://github.com/kubernetes/minikube を勧めてきます。
しかし、個人的にはこういうものは作りながら仕組みを知りたい派閥なのと、勉強になるだろーってことで、 スクラッチで構築してみる ことにしました。
ここからは、実際に手を動かしてKubernetesを構築してみた履歴です。
顺便一提,我后来查了一下,发现有人也在做类似的事情,称之为“Hard Way”。实际体验后,我真的深刻地意识到了它有多么艰辛……每个人都渴望拥有托管服务。
手动设置记录
我們將進行的設置前提如下。
-
- Ubuntu 16.04上
単純に困った時に情報量が多いってだけです
Kubernetes 1.7
やってる間に1.8が正式リリースされたようです
为了暂时能够正常运行,我们选择使用Vagrant + Virtualbox的组合来进行操作。
では行ってみましょう。
1. 使用Virtualbox创建虚拟机
只需要一个选项:创建上述虚拟机。在Vagrant的条件下,如果满足以下条件就可以了。
-
- 2core/2GB
etcd/kube-proxyの動作に1core/1GBのメモリが必要
これくらいで30個くらいのコンテナを制御できるらしいです
host only network
後述するmaster nodeの設定のために必要です。無いとネットワーク設定がものすごく面倒になります。
2. 最基本的的网络设置 (Zuì de
需要确定集群的 IP 范围。这次我们设定为 10.1.0.0/16。
此外,还需要就以下事项做出决定。
-
- kubectlからアクセスするmaster nodeのIP
今回はVagrant自身なのですが、これをhost only networkのIPにしておきます
MASTERIPはローカルホスト以外である必要があるので
80/443が開いている
sysctlでnet.ipv4.ipforward = 1
在之后的步骤中,假定将master节点的IP地址存储在名为MASTER_IP的环境变量中。如果在文件中或其他地方出现了MASTER_IP,那么就表示指的是master节点的IP地址。
3.最基本的的预设
在之前提到的参考页面中,使用了/srv/kubernetes,但是通过一个名为kubeadm的工具创建的东西会被放在/etc/kubernetes中。通常/etc是用于存放配置文件的位置,所以将其创建在这里。
\# mkdir -p /etc/kubernetes
安装软件在主节点上。
从这里开始就是真正的演出了。Kubernetes的主节点需要以下软件。
-
- etcd
ただし、container runner上で動かすのが奨励されていたので、aptとかでは入れません
container runner
docker/rktといったものを導入できますが、ここではdockerにしておきます
Kubernetesの各種ツール
全部まとめてgithubにreleaseから取得できます。今回はv1.7.11を使います。
https://github.com/kubernetes/kubernetes/releases/tag
実際には、以下のツールを利用します。全部まとめてダウンロードされてくるので心配ありません。
kube-apiserver
kubelet
kube-proxy
kube-controller-manager
kube-scheduler
在中文中,可以这样表达:以下是一种方法进行整合。此外,建议在此之后基本上使用root进行操作。
# Dockerの導入はhttps://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/#set-up-the-repositoryを参考
# Kubernetesのバイナリをダウンロード。450MB(!)くらいあります
$ curl -LO https://github.com/kubernetes/kubernetes/releases/tag/v1.17.11
$ tar xf kubernetes.tar.gz
$ ./kubernetes/cluster/get-kube-binaries.sh
$ tar xf kubernetes/client/kubernetes-client-linux-amd64.tar.gz
$ tar xf kubernetes/server/kubernetes-server-linux-amd64.tar.gz
# これで、 kubernetes/client/bin と kubernetes/server/bin にそれぞれバイナリが入ります
创建一个中间件的Docker镜像。
关于kube-apiserver/kube-controller-manager/kube-scheduler/etcd,据说可以在docker pull或kubernetes运行时获取或创建,但也可以自行创建。本次将自行创建。
$ docker image load -i kubernetes/server/bin/kube-apiserver.tar
$ docker image load -i kubernetes/server/bin/kube-scheduler.tar
$ docker image load -i kubernetes/server/bin/kube-controller-manager.tar
$ cd kubernetes/cluster/image/etcd; make
# v1.17.11ではこれが普通に動かない・・・ということで諦めて普通にpullしました
$ docker image pull gcr.io/google-containers/etcd:3.0.17
6. 安全模型和预设设置
虽然似乎可以使用HTTP进行连接,但基本上推荐全部使用HTTPS,虽然麻烦,但我会做各种调整。最终必须通过HTTPS进行连接,虽然麻烦,但我们假设以HTTPS为前提。
CAを作る
作る場所は /etc/kubernetes/pki にします。まずは必要なprivate keyとcrtを作成します。
# ca.keyを生成
$ openssl genrsa -out ca.key 2048
# ca.crtを生成
$ openssl req -x509 -new -nodes -key ca.key -subj “/CN=${MASTER_IP}” -days 10000 -out ca.crt
# server.keyを生成
$ openssl genrsa -out server.key 2048
Certificate Signed Request(CSR)の設定を作成する
以下の内容のファイルを、 /etc/kubernetes/pki/csr.conf として作成します。ファイル内のMASTERCLUSTERIPは、最初に決めたクラスタのIPレンジにおける、最初のアドレスです。今回の場合であれば 10.1.0.1/32 になります。
–service-cluster-ip-rangeの最初のIPアドレス。–service-cluster-ip-rangeはapiserverのオプション。
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[ dn ]
C =
ST =
L =
O =
OU =
CN =
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster
DNS.5 = kubernetes.default.svc.cluster.local
IP.1 =
IP.2 =
[ v3_ext ]
authorityKeyIdentifier=keyid,issuer:always
basicConstraints=CA:FALSE
keyUsage=keyEncipherment,dataEncipherment
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=@alt_names
server.keyにCSRを適用してx509を生成する
作成したCSRを適用します。正直この辺は知識が無くて何をやっているかよくわからなかったです・・・。
$ openssl req -new -key server.key -out server.csr -config csr.conf
# Server certificateを生成する
$ openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 10000 \
-extensions v3_ext -extfile csr.conf
# server certificateを表示する。これをそれぞれの設定におけるcertificateとして利用する
$ openssl x509 -noout -text -in ./server.crt
kubernetes自体のルート証明書を配置する
最後に、以下のようにしてkubernetesのrootとして設定しておきます。自己署名なので、こうしないと色々問題が出ます(多分)
$ cp ca.crt /usr/local/share/ca-certificates/kubernetes.crt
$ update-ca-certificates
Credentialを設定する
Kubernetesでは、Production readyということもあり、様々な認証方式が利用できます。user/passwordや、各ユーザーごとに証明書を作成して登録、とかも出来るようです。
ここでは簡単にやるためにtokenを使います。
作ったtokenは、 /etc/kube-apiserver/known_tokens.csv に保存します。knowntokens.csvの中身は次のようなフォーマットです。
token,user,uid,”group1,group2,group3″
$ TOKEN=$(dd if=/dev/urandom bs=128 count=1 2>/dev/null | base64 | tr -d “=+/” | dd bs=32 count=1 2>/dev/null)
$ echo “$TOKEN,admin,admin,\”system:masters\”” > /etc/kube-apiserver/known_tokens.csv
ここで指定system:mastersというのは、Kubernetesにデフォルトで用意されている、管理用のgroupのようです。このgroupにいないと何も出来ません・・・
credentialをclientに公開する
credentialは、kubeconfigとしてclientから利用できる形式にしないと、kubectlから制御することができません。
kubectlのset-credentialsコマンドを利用します。kubectlはどこかからダウンロードしておきます。
export CLUSTER_NAME=scratch
export CA_CERT=/etc/kubernetes/pki/ca.crt
export MASTER_IP=
export TOKEN=<生成してknown_tokens.csvに入れたtoken>
export USER=admin
export CONTEXT_NAME=scratch
kubectl config set-cluster $CLUSTER_NAME –certificate-authority=$CA_CERT –embed-certs=true –server=https://$MASTER_IP
kubectl config set-credentials $USER –client-certificate=$CLI_CERT –client-key=$CLI_KEY –embed-certs=true –token=$TOKEN
# Set your cluster as the default cluster to use:
kubectl config set-context $CONTEXT_NAME –cluster=$CLUSTER_NAME –user=$USER
kubectl config use-context $CONTEXT_NAME
kubelet/kube-proxyのkubeconfigを作る
kubeletは各nodeにおいて、containerの動作を管理するミドルウェアで、kube-proxyは、kubernetes内でのネットワークをproxyするもの(らしい)です。これらのツールもkubernetesのAPIにアクセスするため、kubeconfigが必要になります。
作る方法は大きく3つあるようです。
adminのcredentialをそのまま利用する
kubelet用のtoken/kubeconfigを全kubeletに利用する
各kubeletごとに作成する(現状すごいめんどくさい
今回はsimplestな1.を利用します。なお、MASTERIPの後についている6443ポートは、kube-apiserverのポートです。後で出てきます。
apiVersion: v1
kind: Config
users:
– name: kubelet
user:
token:
clusters:
– name: local
cluster:
certificate-authority: /etc/kubernetes/pki/ca.crt
server: ${MASTER_IP}:6443
contexts:
– context:
cluster: local
user: kubelet
name: service-account-context
current-context: service-account-context
こんな感じのkubeconfigを、 /etc/kubernetes に、 kube-proxy.conf と kubelet.conf として保存します。
7. 网络设置
在Kubernetes中,我们使用桥接来连接。安装Docker时,将自动创建一个名为docker0的桥接,但是为了避免干扰,我们需要删除它。请删除docker0桥接。
为了创建名为cbr0的桥接,需要按照以下步骤进行操作。需要注意的是,在设置桥接地址时,必须选择cluster地址空间中的第一个地址。
# ブリッジを作成する
$ ip link add name cbr0 type bridge
# MTUを設定する
$ ip link set dev cbr0 mtu 1460
# bridgeのアドレスを作成する
$ ip addr add 10.1.0.1/16 dev cbr0
# 有効にする
$ ip link set dev cbr0 up
8. 在主节点上设置和启动所需的中间件
只有在启动这个之后才能进行最低限的操作。所有操作都需要以root权限进行(docker使用systemctl)。
docker
今回のように単体でインストールされている場合、当然ですがkubernetes特有のオプション設定などはされていません。docker0というブリッジが作成されているので、先に削除しておく必要があります。
$ iptables -t nat -F
$ ip link set docker0 down
$ ip link delete docker0
Dockerのオプションとして以下の設定が必要です。systemctlを利用している場合は、以下と等価な内容を /etc/docker/daemon.json に書く必要があります。
- `--bridge=cbr0`
- `--iptables=false`
- `--ip-masq=false`
- 環境に依存するので要注意
- `--mtu=`
- Flannelを利用する場合は必要
- `--insecure-registry $CLUSTER_SUBNET`
- private registryを利用する場合で、すでにあるregistryがhttpの場合
systemctlからDockerを再起動して、普通に起動したらOKです。
kubelet
kubeletは、kubernetesクラスタの各node上で動いている必要があります。今回は全部同じnodeに載っているので、これも起動します。
以下のオプションを設定して起動します。ちなみにバイナリは kubernetes/server/bin/kubelet にあります。
- –kubeconfig=/etc/kubernetes/kubelet.conf
- –pod-manifest-path=/etc/kubernetes/manifests
kube-proxy
kube-proxyは、v1.17.1版では –config, –config-write-to、 –cleanup-iptables以外はdeprecatedになっています。参考のドキュメントとは全く異なる形で起動する必要があります。
設定ファイルを作成しないといけないのですが、デフォルトの設定は kubernetes/server/bin/kube-proxy –write-config-to <ファイルパス> を実行すると生成できますので、これをいじることにします。
生成されるファイル形式はyamlです。versionがalpha1とか書いているのがなんとも言えない感じがありますが・・・。
apiVersion: componentconfig/v1alpha1
bindAddress: 0.0.0.0
clientConnection:
acceptContentTypes: “”
burst: 10
contentType: application/vnd.kubernetes.protobuf
kubeconfig: /etc/kubernetes/kube-proxy.conf
qps: 5
clusterCIDR: 10.1.0.0/16
configSyncPeriod: 15m0s
conntrack:
max: 0
maxPerCore: 32768
min: 131072
tcpCloseWaitTimeout: 1h0m0s
tcpEstablishedTimeout: 24h0m0s
enableProfiling: false
featureGates: “”
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: “”
iptables:
masqueradeAll: false
masqueradeBit: 14
minSyncPeriod: 0s
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: “”
oomScoreAdj: -999
portRange: “”
resourceContainer: /kube-proxy
udpTimeoutMilliseconds: 250ms
この設定の中で、 kubeconfig の設定が必須です。後は clusterCIDR も設定しないと基本動きません。kube-proxyの起動時は、 –config オプションだけでOKです。
$ kubernetes/server/bin/kube-proxy –config /etc/kubernetes/kube-proxy.yml
9. 服务的启动 加速+
由于每个服务的manifest文件在参考文档中根本没有提到路径,所以发生了这种情况……无可奈何,只能使用 kubeadm init 来创建初始文件。
每个设置都将保存为/etc/kubernetes/manifests/<服务名称>.yaml。
这些设置会在kubelet启动时自动执行。
1.etcd
apiVersion: v1
kind: Pod
metadata:
annotations:
scheduler.alpha.kubernetes.io/critical-pod: “”
creationTimestamp: null
labels:
component: etcd
tier: control-plane
name: etcd
namespace: kube-system
spec:
containers:
– command:
– etcd
– –listen-client-urls=http://127.0.0.1:2379
– –advertise-client-urls=http://127.0.0.1:2379
– –data-dir=/var/lib/etcd
image: gcr.io/google_containers/etcd-amd64:3.0.17
livenessProbe:
failureThreshold: 8
httpGet:
host: 127.0.0.1
path: /health
port: 2379
scheme: HTTP
initialDelaySeconds: 15
timeoutSeconds: 15
name: etcd
resources: {}
volumeMounts:
– mountPath: /etc/ssl/certs
name: certs
– mountPath: /var/lib/etcd
name: etcd
– mountPath: /etc/kubernetes
name: k8s
readOnly: true
hostNetwork: true
volumes:
– hostPath:
path: /etc/ssl/certs
name: certs
– hostPath:
path: /var/lib/etcd
name: etcd
– hostPath:
path: /etc/kubernetes
name: k8s
status: {}
2.apiserver
kubeadmで作成したapiserver.yamlは、client certとかservice account certとかも全部入りのやつなので、一旦必要最小限のオプションで起動するようにします。
特に –advertise-address と –service-cluster-ip-range は必須です。
apiVersion: v1
kind: Pod
metadata:
annotations:
scheduler.alpha.kubernetes.io/critical-pod: “”
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
– command:
– kube-apiserver
– –secure-port=6443
– –experimental-bootstrap-token-auth=true
– –requestheader-allowed-names=front-proxy-client
– –token-auth-file=/etc/kubernetes/known_tokens.csv
– –insecure-port=0
– –admission-control=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota
– –kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
– –requestheader-extra-headers-prefix=X-Remote-Extra-
– –requestheader-username-headers=X-Remote-User
– –tls-cert-file=/etc/kubernetes/pki/server.crt
– –tls-private-key-file=/etc/kubernetes/pki/server.key
– –tls-ca-file=/etc/kubernetes/pki/ca.crt
– –allow-privileged=true
– –requestheader-group-headers=X-Remote-Group
– –service-cluster-ip-range=10.1.0.0/16
– –authorization-mode=Node,RBAC
– –advertise-address=192.168.33.10
– –etcd-servers=http://127.0.0.1:2379
image: gcr.io/google_containers/kube-apiserver-amd64:v1.7.11
livenessProbe:
failureThreshold: 8
httpGet:
host: 127.0.0.1
path: /healthz
port: 6443
scheme: HTTPS
initialDelaySeconds: 15
timeoutSeconds: 15
name: kube-apiserver
resources:
requests:
cpu: 250m
volumeMounts:
– mountPath: /etc/kubernetes
name: k8s
readOnly: true
– mountPath: /etc/ssl/certs
name: certs
hostNetwork: true
volumes:
– hostPath:
path: /etc/kubernetes
name: k8s
– hostPath:
path: /etc/ssl/certs
name: certs
status: {}
3.scheduler
デフォルトでOKのようです。
apiVersion: v1
kind: Pod
metadata:
annotations:
scheduler.alpha.kubernetes.io/critical-pod: “”
creationTimestamp: null
labels:
component: kube-scheduler
tier: control-plane
name: kube-scheduler
namespace: kube-system
spec:
containers:
– command:
– kube-scheduler
– –address=127.0.0.1
– –leader-elect=true
– –kubeconfig=/etc/kubernetes/scheduler.conf
image: gcr.io/google_containers/kube-scheduler-amd64:v1.7.11
livenessProbe:
failureThreshold: 8
httpGet:
host: 127.0.0.1
path: /healthz
port: 10251
scheme: HTTP
initialDelaySeconds: 15
timeoutSeconds: 15
name: kube-scheduler
resources:
requests:
cpu: 100m
volumeMounts:
– mountPath: /etc/kubernetes
name: k8s
readOnly: true
hostNetwork: true
volumes:
– hostPath:
path: /etc/kubernetes
name: k8s
status: {}
4.controller-manager
認証情報の部分以外はデフォルトで良さそうです。
apiVersion: v1
kind: Pod
metadata:
annotations:
scheduler.alpha.kubernetes.io/critical-pod: “”
creationTimestamp: null
labels:
component: kube-controller-manager
tier: control-plane
name: kube-controller-manager
namespace: kube-system
spec:
containers:
– command:
– kube-controller-manager
– –leader-elect=true
– –kubeconfig=/etc/kubernetes/controller-manager.conf
– –cluster-signing-key-file=/etc/kubernetes/pki/ca.key
– –address=127.0.0.1
– –controllers=*,bootstrapsigner,tokencleaner
– –root-ca-file=/etc/kubernetes/pki/ca.crt
– –service-account-private-key-file=/etc/kubernetes/pki/sa.key
– –cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
– –use-service-account-credentials=true
image: gcr.io/google_containers/kube-controller-manager-amd64:v1.7.11
livenessProbe:
failureThreshold: 8
httpGet:
host: 127.0.0.1
path: /healthz
port: 10252
scheme: HTTP
initialDelaySeconds: 15
timeoutSeconds: 15
name: kube-controller-manager
resources:
requests:
cpu: 200m
volumeMounts:
– mountPath: /etc/kubernetes
name: k8s
readOnly: true
– mountPath: /etc/ssl/certs
name: certs
hostNetwork: true
volumes:
– hostPath:
path: /etc/kubernetes
name: k8s
– hostPath:
path: /etc/ssl/certs
name: certs
status: {}
确认API服务器是否正在运行。
curl -s https://$MASTER_IP:6443/healthz
所以,如果收到OK的回覆的话,暂时就可以了。
curl -s https://$MASTER_IP:6443/api
如果返回的是JSON,那么kubectl就处于可用状态。如果执行kubectl get nodes之类的命令时,节点列表中没有出现自己,那么应该是某处出现了错误,需要边查看各种日志边努力解决。
手动设置的烦恼
我丢弃了冗长的部分,只写了最低限度的操作。实际上,我正在检查代理/ DNS的运作情况,但这将在后续日期进行确认。
手動でやってみてわかりましたが、Kubernetesは複数のミドルウェアをうまく組み合わせて構成しているという構成のため、それぞれの構成について冗長性を確保しなければならないと考えると、非常に辛いことは想像に容易いです。
これは確かにManagement serviceが欲しくなります。ただ、やはり手動で作成すると、ミドルウェアの構成であったり、ネットワークの構成であったりといったものは(いやでも)理解が進みます。
不管是通过困难的方式,还是在使用之前或之后,如果有时间,手动尝试制作一次如何?
如果有时间的话,我也会记录有关DNS配置等的内容。