数个小时内彻底理解!非常拥有实力的Kubernetes实践教程!
因为在公司内进行了Kubernetes实操,所以我想和大家分享一下。在6名参与者积极提问的情况下,我们解答问题并顺利完成,大约花费了4个小时的时间。
我也将资料上传到了SpeakerDeck。
(2019/07/11追記)
我写了续集!这次不会太复杂了吧!?“相对来说复杂的Kubernetes实践”,然后是
实践的目标
-
- Kubernetesとお友達になる
イメージを掴む
触ってみる(ローカル・EKS・ちょっとGKE)
構築・運用ができるような気分になる
巷にあふれるKubernetesの記事・スライドが理解できるようになる
由于公司业务主要使用AWS,因此EKS是主要选择。
如果只是纯粹想学习Kubernetes,推荐使用GKE。
但是,由于大部分内容都是关于一般的Kubernetes讨论,所以无论选择哪个平台阅读这篇文章都可以。
前置条件及准备
-
- Docker / Docker Composeは前提知識として扱います。
-
- 以下が用意されている前提で進めます
Docker for Desktop
kubectl
AWS CLI(IAMユーザー作ってprofile設定済み)
aws-iam-authenticator
eksctl
$ docker -v
Docker version 18.09.2, build 6247962
$ kubectl version --short --client
Client Version: v1.13.4
$ aws --version
aws-cli/1.16.125 Python/2.7.14 Darwin/18.2.0 botocore/1.12.115
$ aws-iam-authenticator help
(省略)
$ eksctl version
[ℹ] version.Info{BuiltAt:"", GitCommit:"", GitTag:"0.1.31"}
Kubernetes是什么?
总的说来
容器编排系统
-
- たくさんのサーバーに
-
- たくさんのコンテナを置いて
-
- 連携させるようなアプリケーションを
- デプロイ・管理・スケールとかさせるやーつ
默认标准 (mō,
2014年,谷歌以开放源代码形式发布了一个名为”Cadvisor”的项目。据说其中蕴含了谷歌多年积累的容器化运维经验。目前,CNCF(云原生计算基金会)负责管理该项目。虽然有Docker Swarm作为竞争产品存在,但Docker也正式提供对Cadvisor的支持。?
在不仅限于GCP的情况下,这个动作环境也可以在自己的数据中心或其他云平台上运行。
在各种公共云平台上,即使不需要自己构建,也可以找到托管服务。
-
- GCP: GKE
-
- AWS: EKS (ECSあるのに!?)
-
- Azure: AKS
-
- IBM Cloud(旧Bluemix): IKS
- Alibaba: Container Service for Kubernetes
现在虽然还处于引入阶段的知识被炒作的阶段,但再过一段时间,它可能会变成一个理所当然的技术。
Kubernetes有什么好处?
-> Docker真的很方便呢。
-> 在生产环境中想使用它呢。
-> 不过Docker基本上是一个容器一个功能呢。
-> 如果要构建复杂的应用程序,就需要多个容器呢。
-> 那么,该如何构建呢…?
-
- コンテナが1台のサーバーに収まらないだろうから、どのコンテナがどのサーバーにあるか管理しなくちゃ
-
- コンテナが死んだときに気づけるようにしないと。回復方法も用意せねば
-
- サーバーを跨いだコンテナ間通信って結構めんどくさい
-
- コンテナを更新するとき、コンテナ間の依存関係とかデプロイ順序とか考えなくちゃ
-
- 複数サーバーにコンテナがあるんなら、ロードバランシングも考えなくちゃ
-
- サーバーのスケーリングをしないといけないこともあるよね…
-
- 各コンテナからログが出てくるけど、ちゃんと管理しておかないと運用辛いよね
- etc…
这对人类来说太复杂了,不是吗。。
可以做到的。
是的,如果是Kubernetes的话。
摸清 Kubernetes 的概念
基本概念
如果有一个可以很好地管理容器的系统,那就太好了。这是基本的想法。
我们希望将这种好的系统的运营方式通过注文书传达出去,然后系统就会很棒地为我们处理。
而能够为我们做到这一点的就是Kubernetes。
-
- いい感じに場所を判断してコンテナを配置
-
- コンテナが死んだら自動回復
-
- サーバー間ネットワークもいい感じに
-
- ローリングアップデート / Blue/Greenデプロイメントもお手の物
-
- ロードバランシングもやってくれる
-
- サーバーのオートスケーリングも(クラウドなら)設定可能
- ログ収集もカンタンに
让我们把它转换成Kubernetes的术语。
我会先将之前图中的元素转换成Kubernetes术语。
-
- いい感じのシステム: Master(ControlPlaneとも)
-
- コンテナが配置されるサーバー: Node
-
- Nodeの集合: DataPlane
-
- MasterとDataPlaneを合わせて: Cluster
-
- Masterに投げる注文書: マニフェストファイル
-
- Masterに注文書を投げつけるやつ: kubectl
CLIツール
注文書を投げつけるだけでなくいろんな操作ができる
我們將開展該項目。 .)
清单文件
由于在Kubernetes中,清单文件的概念非常独特,因此我将对其进行讨论。
「宣言的設定」( <-> 「命令的設定」)を特徴とするマニフェストファイルは、yaml(またはjson)で記述され、システムの理想的な状態を示しています。
如果要打个比方的话,命令式配置就像一家普通的拉面店。
你点了一碗素面,拿到面后,拉面店就不再参与接下来的事情了。
如果你想再点别的菜,就需要再次召唤店员来点餐。
这类工具就像 Ansible、Terraform、CloudFormation。
宣言是一碗狗麵。
狗麵的設置應該是在碗中放置一碗麵的狀態,麵攤一直監視著碗。
當吃完麵使碗中麵消失時,它會自動添加麵進去。
在Kubernetes中,它會在應用了Manifest文件後,為容器提供自動恢復等功能,以保持其內容的穩定。
概括来说
-
- 宣言的に書いたマニフェストを
-
- kubectlを使ってmasterに渡すと
-
- 各nodeにコンテナをデプロイしたりしてくれて
- その後はいい感じに監視・維持をしてくれるやーつ
在本地试用Kubernetes
好了,从这里开始实际操作吧。首先让我们试试看。
启用Docker Desktop的Kubernetes
打开“Preference”画面,并进行以下设置。
首次启动可能需要约5分钟才能生效。
如果以前使用过的人,可能最好将其重置一下以避免遇到问题。
kubectl的配置設定
将使用kubectl进行操作的Kubernetes集群转换为docker-for-desktop。
$ kubectl config use-context docker-for-desktop
我会检查一下是否正确设置了。
$ kubectl config current-context
docker-for-desktop
确认动作
有很多事情正在发生。
$ kubectl get pods --namespace=kube-system
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system etcd-docker-for-desktop 1/1 Running 0 1m
kube-system kube-apiserver-docker-for-desktop 1/1 Running 0 1m
kube-system kube-controller-manager-docker-for-desktop 1/1 Running 0 1m
kube-system kube-dns-86f4d74b45-xb4qh 3/3 Running 0 2m
kube-system kube-proxy-8r45p 1/1 Running 0 2m
kube-system kube-scheduler-docker-for-desktop 1/1 Running 0 1m
准备好
为了使docker-for-desktop能够使用ingress(如下所述),需要进行预备工作。由于这仅是本地操作,所以不必太过担心,没问题的。
$ kubectl create namespace ingress-nginx
$ cat << EOF > kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: ingress-nginx
bases:
- github.com/kubernetes/ingress-nginx/deploy/cluster-wide
- github.com/kubernetes/ingress-nginx/deploy/cloud-generic
EOF
$ kubectl apply -k .
部署示例应用
取得示例应用程序的清单文件。
$ git clone git@github.com:kubernetes/examples.git
由于Docker桌面版的Kubernetes对系统负荷很高,因此需要调整所建立的容器数量。
$ vi examples/guestbook/frontend-deployment.yaml
10行目 replicas: 3 <- これを1に変更
$ vi examples/guestbook/redis-slave-deployment.yaml
11行目 replicas: 2 <- これを1に変更
部署!将examples/guestbook文件夹下的所有清单文件部署。
$ kubectl apply -f examples/guestbook/
尽管示例应用程序已经开始运行,但还需要一点额外的工作才能从外部进行访问。
$ cat << 'EOT' >./guestbook-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: guestbook-ingress
spec:
rules:
- http:
paths:
- path: /
backend:
serviceName: frontend
servicePort: 80
EOT
我会把这个部署上线。
$ kubectl apply -f guestbook-ingress.yaml
等待直到地址(ADDRESS)变成本地主机(localhost)。
$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
guestbook-ingress * localhost 80 24m
当你访问http://localhost时,会显示示例应用程序的界面。由于端口是80,根据电脑的情况可能无法成功访问。据说使用http://127.0.0.1有时也可以正常工作。
发生了什么事情?
这个可以用图形来表示。
请冷静下来。
将要点提取出来看,这只是一个仅包含前端和Redis的简单应用。
首先,让我们能够理解这个图。
播客
-
- Kubernetesの最小デプロイ単位
-
- 1つ以上のコンテナとストレージボリュームの集まり
-
- 同一Pod内のコンテナは同一Nodeに配置される
「同一Nodeで動作する必要があるか?」がPod構成の一つの基準
1つのPod内のコンテナは同じIPアドレスとポートを使用する
Pod内のコンテナ間の通信はプロセス間通信として行う
复制集合
-
- 同じ仕様のPodが指定した数だけ存在するよう生成・管理する
Podが死んだときも指定した数になるよう自動回復してくれる
PodとReplicaSetは疎結合
“Label”というメタデータを使って都度検索している
手動でPodのLabelを書き換えれば、
ReplicaSetから切り離してデバッグするといったことも可能
部署 (bù shǔ)
-
- 新しいバージョンのリリースを管理するための仕組み
ReplicaSetの変更を安全に反映させる / 世代管理する
Podのスケール、コンテナの更新、ロールバックetc…
2つのDeployment戦略
Recreate
RollingUpdate
ReplicaSetとDeploymentも疎結合
在Deployment的清单文件中,还会包含有有关ReplicaSet和Pod的信息。
(虽然可以编写ReplicaSet和Pod单独的清单文件,但一般并不常做。)
让我们尝试查看示例应用程序的frontend-deployment.yaml文件。redis-master-deployment.yaml和redis-slave-deployment.yaml也大致相同。
apiVersion: apps/v1 # apply時に使用するAPIの種別。リソース(kind)によって決まる
kind: Deployment # Deploymentのマニフェスト
metadata:
name: frontend # Deploymentリソースの名前。「metadata.name + ランダム文字列」の名前でReplicaSetが生成される
spec:
selector:
matchLabels: # ReplicaSetがPodを検索するときのLabel
app: guestbook
tier: frontend
replicas: 1 # ReplicaSetが生成・管理するPodの数
template: # ---ここからPodの定義--------------------------------------------
metadata:
labels: # PodのLabel。ReplicaSetが管理下のPodを検索するときに使う
app: guestbook
tier: frontend
spec:
containers:
- name: php-redis # コンテナ名
image: gcr.io/google-samples/gb-frontend:v4 # コンテナイメージ
resources: # 使用するCPU, Memoryの指定
requests:
cpu: 100m
memory: 100Mi
env: # 環境変数
- name: GET_HOSTS_FROM
value: dns
ports: # EXPOSEするポートの指定
- containerPort: 80
服务 (fú wù)
-
- Podの集合(主にReplicaSet)に対する経路やサービスディスカバリを提供
クラスタ内DNSで、<Service名>.<Namespace名>で名前解決可能に
同じNamespace内なら<Service名>だけでOK
ここでもLabelによって対象のPodが検索される
対象のPodが動的に入れ替わったりしても、Labelさえついていれば一貫した名前でアクセスできる
以下是Service的清单文件。
frontend-service.yaml是专门用于前端的,redis-*-service.yaml是专门用于后端的,因此Service的类型是不同的。
apiVersion: v1
kind: Service # Serviceのマニフェスト
metadata:
name: frontend # Serviceリソースの名前
labels: # ServiceにつけるLabel
app: guestbook
tier: frontend
spec:
type: NodePort # Serviceの種別。NodePortはクラスタ外からアクセスできるやつ
ports:
- port: 80 # アクセスを受け付けるポート
selector: # 対象のPodを検索するときのLabel
app: guestbook
tier: frontend
apiVersion: v1
kind: Service
metadata:
name: redis-master
labels:
app: redis
role: master
tier: backend
spec:
# 省略されているけどtypeはデフォルトの"ClusterIP"。クラスタ上の内部IPアドレスにServiceを公開
ports:
- port: 6379
targetPort: 6379
selector:
app: redis
role: master
tier: backend
入口
-
- Serviceをクラスタ外に公開
-
- NodePortタイプのServiceと違い、パスベースで転送先のServiceを切り替えるといったことも可能
Service(NodePort) : L4層レベルでの制御
Ingress : L7層レベルでの制御
如果在云端进行操作,可以通过将服务的类型设置为负载均衡器来使用各个云的负载均衡器,因此可能没有太多的使用机会。
暂时先看一下刚才使用的guestbook-ingress.yaml文件大概是这样的。
apiVersion: extensions/v1beta1
kind: Ingress # Ingressのマニフェスト
metadata:
name: guestbook-ingress # Ingressリソースの名前
spec:
rules: # ルーティングのルールの配列
- http:
paths:
- path: /
backend: # "frontend"Serviceの80番ポートにアクセス
serviceName: frontend
servicePort: 80
我們來看看裡面吧。
让我们先了解结构,然后再查看其内部是如何的。
通过使用kubectl get [资源类型]可以获取列表,使用kubectl describe [资源类型] [资源名称]可以查看详细信息。
部署
部署概览(”-o wide” 是用于查看详细信息的选项)
$ kubectl get deploy -o wide
NAME (略) SELECTOR
frontend ... app=guestbook,tier=frontend
redis-master ... app=redis,role=master,tier=backend
redis-slave ... app=redis,role=slave,tier=backend
在SELECTOR的选项中,列出的是用于搜索ReplicaSet的选择器。正如之前所述,Deployment和ReplicaSet是松散耦合的,也就是说它们之间的关联是这样的。
如果要在手中进行搜索,可以按以下方式进行。
$ kubectl get rs -l app=guestbook,tier=frontend
详细部署(太长了,建议在手边查看!)
$ kubectl describe deploy frontend
复制集
复制集列表
$ kubectl get rs -o wide
NAME (略) SELECTOR
frontend-5c548f4769 ... app=guestbook,pod-template-hash=1710490325,tier=frontend
redis-master-55db5f7567 ... app=redis,pod-template-hash=1186193123,role=master,tier=backend
redis-slave-584c66c5b5 ... app=redis,pod-template-hash=1407227161,role=slave,tier=backend
名字是部署名称 + 随机字符串。
另外,在SELECTOR中有一个叫做”pod-template-hash”的东西。为了确保即使存在多个混合的ReplicaSet来管理同一模板的Pod(如RollingUpdate等),Kubernetes会自动添加一个具有特定值的标签作为唯一标识。
複製設定的詳細資訊(太長了,請自己看!)
$ kubectl describe rs frontend-5c548f4769
博客
Pod列表
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
frontend-5c548f4769-xhpxz 1/1 Running 0 1d 10.1.1.65 docker-for-desktop
redis-master-55db5f7567-2n4qp 1/1 Running 0 1d 10.1.1.67 docker-for-desktop
redis-slave-584c66c5b5-z2fvj 1/1 Running 0 1d 10.1.1.66 docker-for-desktop
这次的名字变成了ReplicaSet名+随机字符串,是吧。
请查看Pod详情(内容较长,请查看手头文档!)可查看设置、启动时间、状态、事件等。
$ kubectl describe pod frontend-5c548f4769-xhpxz
你可以在Pod上查看日志(由于日志内容较长,请在自己手边查看!)
$ kubectl logs frontend-5c548f4769-xhpxz
服务
服务清单
$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
frontend NodePort 10.102.204.76 <none> 80:30590/TCP 1d app=guestbook,tier=frontend
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d <none>
redis-master ClusterIP 10.98.133.213 <none> 6379/TCP 1d app=redis,role=master,tier=backend
redis-slave ClusterIP 10.107.141.173 <none> 6379/TCP 1d app=redis,role=slave,tier=backend
由于前端的类型是NodePort,所以外部有一个端口开放。
刚刚我们使用了Ingress进行访问,但实际上也可以通过localhost:30590(端口号会随时变化)进行访问。
但请注意,这是一个服务,受到L4控制。
详细服务(请手头查阅,内容较长!)
$ kubectl describe svc frontend
入口
Ingress列表
$ kubectl get ing
NAME HOSTS ADDRESS PORTS AGE
guestbook-ingress * localhost 80 1d
Ingress详细信息(太长了,请你自己看一下!)
$ kubectl describe ing guestbook-ingress
让我们尝试扩大规模
我尝试将前端的Pod数量增加到两个。
$ vi examples/guestbook/frontend-deployment.yaml
10行目 replicas: 1 <- これを2に変更
部署
$ kubectl apply -f examples/guestbook/frontend-deployment.yaml
涨了!
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
frontend-5c548f4769-vltkv 1/1 Running 0 15s
frontend-5c548f4769-xhpxz 1/1 Running 0 1d
redis-master-55db5f7567-2n4qp 1/1 Running 0 1d
redis-slave-584c66c5b5-z2fvj 1/1 Running 0 1d
让我们尝试自动回复一下。
让我们有意删除Pod。
让我们尝试删除刚刚新增的第二个Pod。
$ kubectl delete pod frontend-5c548f4769-vltkv
过了一会儿,新的pod被创建了!
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
frontend-5c548f4769-ns5q2 1/1 Running 0 8s
frontend-5c548f4769-xhpxz 1/1 Running 0 1d
redis-master-55db5f7567-2n4qp 1/1 Running 0 1d
redis-slave-584c66c5b5-z2fvj 1/1 Running 0 1d
观察一下Deployment的部署管理情况
由于不更改Pod,不会记录历史记录(无法通过规模解决问题),所以试着改变使用的内存。
$ vi examples/guestbook/frontend-deployment.yaml
23行目 memory: 100Mi <- これを120Miに変更
部署
$ kubectl apply -f examples/guestbook/frontend-deployment.yaml
逐渐转变。
# 徐々に切り替わっている!
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
frontend-5c548f4769-ns5q2 1/1 Running 0 11m
frontend-5c548f4769-xhpxz 1/1 Running 0 1d
frontend-68dd74b969-ztcdw 0/1 ContainerCreating 0 5s
redis-master-55db5f7567-2n4qp 1/1 Running 0 1d
redis-slave-584c66c5b5-z2fvj 1/1 Running 0 1d
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
frontend-5c548f4769-xhpxz 1/1 Running 0 1d
frontend-68dd74b969-6shhj 0/1 ContainerCreating 0 6s
frontend-68dd74b969-ztcdw 1/1 Running 0 21s
redis-master-55db5f7567-2n4qp 1/1 Running 0 1d
redis-slave-584c66c5b5-z2fvj 1/1 Running 0 1d
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
frontend-68dd74b969-6shhj 1/1 Running 0 26s
frontend-68dd74b969-ztcdw 1/1 Running 0 41s
redis-master-55db5f7567-2n4qp 1/1 Running 0 1d
redis-slave-584c66c5b5-z2fvj 1/1 Running 0 1d
让我们查看一下通过Deployment进行管理的历史记录。
$ kubectl rollout history deployments frontend
deployment.extensions/frontend
REVISION CHANGE-CAUSE
1 <none>
2 <none>
REVISION的值越大,表示它是新的。
CHANGE-CAUSE会在清单文件中添加名为“Annotation”的信息,但这次我们将无视它。
让我看看REVISION=2的详细信息(因为内容很长,请让我亲自查看!)
$ kubectl rollout history deployments frontend --revision=2
让我们回滚到先前的版本。
虽然这次指定了版本,但当回退到前一个版本时,”–to-revision”可以省略。
$ kubectl rollout undo deployments frontend --to-revision=1
当再次查看历史记录时,REVISION=1已经消失。
即使进行回滚操作,也会有一个新的版本,但是相同内容的版本将会从历史记录中被删除。
$ kubectl rollout history deployments frontend
deployment.extensions/frontend
REVISION CHANGE-CAUSE
2 <none>
3 <none>
顺便问一下,Master在做什么呢?
最終解释下来,Master主要是由Pod集群组成的。
在kube-system命名空间中,归属于Master的Pod存在。
$ kubectl get pods --namespace=kube-system
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system etcd-docker-for-desktop 1/1 Running 0 1m
kube-system kube-apiserver-docker-for-desktop 1/1 Running 0 1m
kube-system kube-controller-manager-docker-for-desktop 1/1 Running 0 1m
kube-system kube-dns-86f4d74b45-xb4qh 3/3 Running 0 2m
kube-system kube-proxy-8r45p 1/1 Running 0 2m
kube-system kube-scheduler-docker-for-desktop 1/1 Running 0 1m
etcd
- クラスタ内のさまざまなデータを保存している一貫性のある高可用性のKVS
Kubernetes 的 API 服务器
-
- クラスタに対する全ての操作を司るAPIサーバー
- 認証や認可の処理なども行う
Kubernetes调度器
-
- PodのNodeへの割り当てを行うスケジューラー
- Podを配置するNodeの選択も行う
Kubernetes控制器管理器
- 各種Kubernetesオブジェクトのコントローラーを起動し管理するマネージャー
在Node中也包含与Master协同工作的元素。
kubelet 翻译为“kubelet”。
- Nodeのメイン処理であるPodの起動・管理を行うエージェント
kube-proxy (Kubernetes代理)
- Serviceが持つ仮想的なIPアドレス(ClusterIP)へのアクセスをルーティングする
以图表表示的话,情况会是这样的。
整理
请删除客户留言本应用程序。
$ kubectl delete -f examples/guestbook/
不久之后将被删除
$ kubectl get pod
No resources found.
让我们尝试使用EKS创建一个集群。
EKS 是什么?
-
- 複数AZでmasterを冗長構成して実行
-
- masterの監視・自動回復
-
- 自動アップグレード・パッチ適用
-
- 他のAWSサービスとの統合
-
- DataPlane(EC2)は自前で用意する必要がある
- ざっくり費用感 : $144/月 (2019/04現在・東京リージョン : EC2費用は別途)
让我们来创建一个集群吧!
我认为尽管如此,这次我们只需要确认步骤即可。
虽然知道哪些步骤是必要的很好,但有更好的方法,所以实际上去做会浪费时间。
在幻灯片上也写有步骤,如果想尝试的话,请参考那里。
用更好的方法
eksctl可以用中文翻译为:
扩展是亚马逊弹性容器服务(Amazon Elastic Kubernetes Service,简称EKS)的一个工具。
-
- 非公式・デファクトスタンダード
-
- https://eksctl.io/
- コマンド一つでクラスタ構築
快速开始
-
- 公式・最近出た
-
- https://aws.amazon.com/jp/quickstart/architecture/amazon-eks/
-
- https://dev.classmethod.jp/cloud/aws/eks-quickstart/
-
- Cfnを使ってクラスタ構築
- ベストプラクティスに従っているので結構豪華な構成
让我们尝试使用eksctl。
eksctl是什么?
-
- コマンド一つでEKSのClusterができちゃうツール
-
- CloudFormationのテンプレートを自動生成して構築してる
-
- nodeのオートスケーリングなど、便利な機能も
-
- とはいえまだ発展途上
- ロゴを見てみると分かると思いますが、goでできています
让我们试试看吧!
只需一个命令,即可创建集群!
$ eksctl create cluster \
--name eksctl-handson \
--region ap-northeast-1 \
--nodes 3 \
--nodes-min 3 \
--nodes-max 3 \
--node-type t2.medium \
--ssh-public-key <キーペア名>
然而,构建完成需要大约15分钟…
选择旧AWS账户的ap-northeast-1b也需要指定AZ。
请参考其他选项以获取帮助。
$ eksctl create cluster -h
当构建完成后,会有以下的感觉。
暂时部署吧
根据已经克隆的内容恢复原状。
$ cd examples
$ git reset --hard
$ cd ../
将 frontend-service 的类型更改为 LoadBalancer。
这是与云负载均衡器(在这里是 ELB)进行合作的类型。
$ vi examples/guestbook/frontend-service.yaml
9-13行目
# comment or delete the following line if you want to use a LoadBalancer
type: NodePort <- ここをコメントアウト
# if your cluster supports it, uncomment the following to automatically create
# an external load-balanced IP for the frontend service.
# type: LoadBalancer <- ここをアンコメント
申请吧! ba!)
$ kubectl apply -f examples/guestbook/
过一会儿就会完成建设。
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/frontend-56f7975f44-2vtbr 1/1 Running 0 8s
pod/frontend-56f7975f44-j25zn 1/1 Running 0 8s
pod/frontend-56f7975f44-mss7q 1/1 Running 0 8s
pod/redis-master-6b464554c8-wrjrp 1/1 Running 0 8s
pod/redis-slave-b58dc4644-ft2fd 1/1 Running 0 7s
pod/redis-slave-b58dc4644-p59fk 1/1 Running 0 7s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/frontend LoadBalancer 10.100.61.11 xxxxxx.ap-northeast-1.elb.amazonaws.com 80:31673/TCP 8s
service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 23m
service/redis-master ClusterIP 10.100.137.217 <none> 6379/TCP 7s
service/redis-slave ClusterIP 10.100.217.57 <none> 6379/TCP 7s
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment.apps/frontend 3 3 3 3 8s
deployment.apps/redis-master 1 1 1 1 8s
deployment.apps/redis-slave 2 2 2 2 7s
NAME DESIRED CURRENT READY AGE
replicaset.apps/frontend-56f7975f44 3 3 3 8s
replicaset.apps/redis-master-6b464554c8 1 1 1 8s
replicaset.apps/redis-slave-b58dc4644 2 2 2 7s
因为service/frontend的EXTERNAL-IP上附有ELB的域名,所以我们尝试访问那里。
让我们试试安装仪表板。 .)
应用适用于仪表板的Pod。
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml
获取登录仪表盘所需的令牌
$ aws-iam-authenticator token -i eksctl-handson | jq -r '.status.token'
通过代理访问仪表盘
$ kubectl proxy --port=8000 --address='0.0.0.0' --disable-filter=true
当访问 http://localhost:8000/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/ 时,将进入仪表盘的登录页面。请键入令牌以登录。
让我们尝试收集日志
我会将Kubernetes的日志放入CloudWatchLogs中尝试。
云监控容器洞察
然而在实施 Hands-On 后宣布了 “CloudWatch Container Insights”。
它是一项能够提供日志和度量数据的托管服务。
虽然目前仍处于公共预览阶段,但在使用 AWS 时,它将成为标准配置。
暫時在這裡,先簡述一下DaemonSet的概念,然後再寫下傳統的步驟。
Docker容器的日志
在Docker容器中,标准输出被视为日志。默认情况下,日志以JSON格式保存为文件。
本次我们将在每个节点上放置一个fluentd的Pod,以收集节点内的Pod生成的日志文件并将其发送到CloudWatch。
DaemonSet : 守护进程集
现在,在配置Pod时,无法选择Node。Kubernetes会为我们做好。像这次一样,当想要在每个Node上启动一个Pod时,可以使用DaemonSet。
那么,让我们试一试吧。
部署
获取绑定到节点 EC2 的 IAM 角色的名称。
$ INSTANCE_PROFILE_NAME=$(aws iam list-instance-profiles | jq -r '.InstanceProfiles[].InstanceProfileName' | grep nodegroup)
$ ROLE_NAME=$(aws iam get-instance-profile --instance-profile-name $INSTANCE_PROFILE_NAME | jq -r '.InstanceProfile.Roles[] | .RoleName')
我要为IAM角色添加用于日志收集的内嵌策略。
$ cat << "EoF" > ./k8s-logs-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*",
"Effect": "Allow"
}
]
}
EoF
$ aws iam put-role-policy --role-name $ROLE_NAME --policy-name Logs-Policy-For-Worker --policy-document file://k8s-logs-policy.json
获取Fluentd的清单文件
$ wget https://eksworkshop.com/intermediate/230_logging/deploy.files/fluentd.yml
更改集群名称
$ vi fluentd.yml
197行目
value: us-east-1 <- ap-northeast-1 に変更
199行目
value: eksworkshop-eksctl <- eksctl-handson に変更
部署Fluentd
$ kubectl apply -f fluentd.yml
过一会儿,日志就会上传到CloudWatch。
看一下清单文件
看一下fluentd.yaml文件,可以看出已经配置了DaemonSet。
(略)
---
apiVersion: extensions/v1beta1
kind: DaemonSet # DaemonSetのマニフェスト
metadata:
name: fluentd-cloudwatch
namespace: kube-system
labels:
k8s-app: fluentd-cloudwatch
spec:
template: # ---ここからPodの定義--------------------------------------------
metadata:
labels:
k8s-app: fluentd-cloudwatch
spec:
serviceAccountName: fluentd
terminationGracePeriodSeconds: 30
# Because the image's entrypoint requires to write on /fluentd/etc but we mount configmap there which is read-only,
# this initContainers workaround or other is needed.
# See https://github.com/fluent/fluentd-kubernetes-daemonset/issues/90
initContainers:
- name: copy-fluentd-config
image: busybox
command: ['sh', '-c', 'cp /config-volume/..data/* /fluentd/etc']
volumeMounts:
- name: config-volume
mountPath: /config-volume
- name: fluentdconf
mountPath: /fluentd/etc
(略)
让我们试试使用Helm。
Helm是什么?
-
- Kubernetes用のパッケージ管理ツール
パッケージは”Chart”と呼ばれ、マニフェストファイルのテンプレートが含まれる
“Tiller”と呼ばれるサーバーアプリケーション(これもPod)を介してクラスタ内にパッケージをインストール
ちなみに”helm”は兜ではなく船の舵、”chart”は海図、”tiller”は舵柄の意味
基于角色的访问控制(RBAC)
-
- Kubernetesの権限制御の仕組み
Kubernetesのリソースへのアクセスをロールによって制御
ユーザーとロールをBindingによって紐付けることによって機能する
ユーザー種別
認証ユーザー・グループ : クラスタ外からKubernetes APIを操作するためのユーザー
ServiceAccount : PodがKubernetes APIを操作するためのユーザー
ロール種別
Role : 指定のnamespace内でのみ有効
ClusterRole : クラスタ全体で有効
HelmにもRBACを有効にできるChartが多く管理されている
安装Helm
-
- https://helm.sh/docs/using_helm/#installing-helm
-
- Macの場合
brew install kubernetes-helm
创建用于Tiller服务账号的清单文件
“cluster-admin”是默认存在的ClusterRole。
$ cat <<EoF > tiller_rbac.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system
EoF
创建用于Tiller的服务账号
$ kubectl apply -f tiller_rbac.yaml
指定Tiller的服务账号安装Helm到集群中,
这样Tiller的Pod将被部署到kube-system命名空间中。
$ helm init --service-account tiller
安装Jenkins
创建CustomValue文件。
可以通过”helm inspect values stable/jenkins”命令查看参数的详细信息。
$ cat <<EoF > jenkins.yaml
rbac:
create: true
master:
service_port: 8080
persistence:
size: 1Gi
EoF
安装Jenkins
$ helm install -f jenkins.yaml --name jenkins stable/jenkins
过一会儿就完成部署(大约2-3分钟)。
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/jenkins-f65b9477-89s69 1/1 Running 0 33m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/jenkins LoadBalancer 10.100.22.208 xxxxxx.ap-northeast-1.elb.amazonaws.com 8081:30196/TCP 33m
service/jenkins-agent ClusterIP 10.100.58.26 <none> 50000/TCP 33m
service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 2h
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment.apps/jenkins 1 1 1 1 33m
NAME DESIRED CURRENT READY AGE
replicaset.apps/jenkins-f65b9477 1 1 1 33m
获取密码(安装日志中记录了获取方法的那个)
$ printf $(kubectl get secret --namespace default jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo
XXXXXXXX
获取登录网址(安装时在日志中写有获取方法的那个)
$ export SERVICE_IP=$(kubectl get svc --namespace default jenkins --template "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}")
$ echo http://$SERVICE_IP:8080/login
http://xxxxxx.ap-northeast-1.elb.amazonaws.com:8080/login
登录后,访问页面会显示如下内容:
用户名:admin
密码:通过命令获取的那个
卸载Jenkins
— purge是一个选项。如果不使用该选项,修订记录将保留,可以进行回滚操作。
$ helm delete --purge jenkins
让我们设法进行监视
我认为这也可能会被”CloudWatch Container Insights”替代,但我还是写下来为好。
这次我们将使用Prometheus/Grafana。
-
- Prometheus
OSSのリソース監視ツール
導入がカンタン、いい感じに通知くれる、高性能などで人気が高い
ただ、データの可視化が本業ではないので力不足
Grafana
OSSのログ・データ可視化ツール
Prometheusが収集したデータをかっこよく表示できる
安装Prometheus
使用中文进行重述:
创建一个 CustomValue 文件。
可以通过运行命令 “helm inspect values stable/prometheus” 来查看参数的详细信息。
$ cat <<EoF > prometheus.yaml
alertmanager:
persistentVolume:
size: 1Gi
storageClass: "gp2"
server:
persistentVolume:
size: 1Gi
storageClass: "gp2"
retention: "12h"
pushgateway:
enabled: false
EoF
安装 Prometheus。
$ kubectl create namespace prometheus
$ helm install -f prometheus.yaml --name prometheus --namespace prometheus stable/prometheus
等待部署完成后尝试访问(启动需要几分钟时间)
下面是安装时出现的日志。
$ export POD_NAME=$(kubectl get pods --namespace prometheus -l "app=prometheus,component=server" -o jsonpath="{.items[0].metadata.name}")
$ kubectl --namespace prometheus port-forward $POD_NAME 9090
当访问http://localhost:9090/targets时,可以看到Prometheus的界面。
安裝Grafana
创建CustomValue文件
可以通过”helm inspect values stable/grafana”命令来查看参数的详细信息。
$ cat <<EoF > grafana.yaml
persistence:
storageClassName: gp2
adminPassword: password
datasources:
datasources.yaml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
url: "http://prometheus-server.prometheus.svc.cluster.local"
access: proxy
isDefault: true
service:
type: LoadBalancer
EoF
安装Grafana
$ kubectl create namespace grafana
$ helm install -f grafana.yaml --name grafana --namespace grafana stable/grafana
等待部署完成后,尝试访问(启动可能需要几分钟时间)
以下是安装过程中显示的日志
$ export ELB=$(kubectl get svc -n grafana grafana -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
$ echo "http://$ELB"
http://xxxxxx.ap-northeast-1.elb.amazonaws.com
登录后,您将进入以下屏幕:
– 用户名:admin
– 密码:password(已在grafana.yaml中记录)
制作仪表板
让我们尝试从导入界面中引入公开的模板。
模板编号为3131。
有一种东西出现得有些像那个。
在中文中,可以这样表达:如果是3146,就会变成这样。
整理整理
请注意,在使用eksctl删除集群时,如果不清理集群内的资源,将会在CloudFormation中报错。
删除Prometheus和Grafana。
$ helm delete --purge prometheus
$ helm delete --purge grafana
删除 Fluentd
$ kubectl delete -f fluentd.yml
删除仪表板
$ kubectl delete -f https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.1/src/deploy/recommended/kubernetes-dashboard.yaml
请卸载留言板应用。
$ kubectl delete -f examples/guestbook/
解除IAM Role上附加的用于日志收集的策略。
$ INSTANCE_PROFILE_NAME=$(aws iam list-instance-profiles | jq -r '.InstanceProfiles[].InstanceProfileName' | grep nodegroup)
$ ROLE_NAME=$(aws iam get-instance-profile --instance-profile-name $INSTANCE_PROFILE_NAME | jq -r '.InstanceProfile.Roles[] | .RoleName')
$ aws iam delete-role-policy --role-name $ROLE_NAME --policy-name Logs-Policy-For-Worker
建议在AWS控制台上确认集群是否真的已被删除,因为我只能看到部分过程,无法确认删除的结果。
$ eksctl delete cluster --name eksctl-handson
让我们用GKE创建一个集群(附加)。
既然如此,还是看看原作吧,只需要一点点时间。
假设CLI和GCP项目的设置已经完成。
让我们试试看
只需要一个命令,可以创建一个包括Node在内的集群。
而且只需要大约3分30秒!!
$ gcloud container clusters create gke-handson --cluster-version=1.12.7-gke.10 --machine-type=n1-standard-1 --num-nodes=3
kubectl的配置已经正确更改了!
$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
docker-for-desktop docker-for-desktop-cluster docker-for-desktop
* xxxxxx_asia-northeast1-a_gke-handson xxxxxx_asia-northeast1-a_gke-handson xxxxxx_asia-northeast1-a_gke-handson
默认情况下,fluentd和prometheus已经安装好了!
$ kubectl get pods --namespace=kube-system
NAME READY STATUS RESTARTS AGE
event-exporter-v0.2.3-f9c896d75-52nkr 2/2 Running 0 2m5s
fluentd-gcp-scaler-69d79984cb-zm56b 1/1 Running 0 113s
fluentd-gcp-v3.2.0-5mncb 2/2 Running 0 63s
fluentd-gcp-v3.2.0-9sdg7 2/2 Running 0 74s
fluentd-gcp-v3.2.0-t59sr 2/2 Running 0 54s
heapster-v1.6.0-beta.1-6fc8df6cb8-54qrk 3/3 Running 0 85s
kube-dns-autoscaler-76fcd5f658-22l8j 1/1 Running 0 104s
kube-dns-b46cc9485-5kspm 4/4 Running 0 92s
kube-dns-b46cc9485-j8fmn 4/4 Running 0 2m5s
kube-proxy-gke-gke-handson-default-pool-d757b1ec-9ld7 1/1 Running 0 108s
kube-proxy-gke-gke-handson-default-pool-d757b1ec-lcl8 1/1 Running 0 110s
kube-proxy-gke-gke-handson-default-pool-d757b1ec-z2m6 1/1 Running 0 110s
l7-default-backend-6f8697844f-s8lgv 1/1 Running 0 2m6s
metrics-server-v0.3.1-5b4d6d8d98-tn8cw 2/2 Running 0 87s
prometheus-to-sd-4v26j 1/1 Running 0 111s
prometheus-to-sd-k4jj7 1/1 Running 0 110s
prometheus-to-sd-mhs8h 1/1 Running 0 110s
用这个,你可以创建一个由3个节点组成的集群。
当然可以使用以前使用过的清单文件进行部署,让我们试一试。
$ kubectl apply -f examples/guestbook/
整理
删除操作只需大约3分30秒左右就可以完成!
$ gcloud container clusters delete gke-handson
kubectl的配置也已经正确删除了。
$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
docker-for-desktop docker-for-desktop-cluster docker-for-desktop
为了确保,可以在控制台上检查是否还有可用资源。
EKS和GKE
-
- 柔軟性
EKSはユーザーによるカスタマイズの幅が大きい
でもやっぱり面倒なのでeksctlとかが誕生していたりする…
GKEはいい感じにしてくれる
立ち上がりの速さ
EKSは(eksctl利用で)15分ぐらい
GKEは3分半ぐらい
費用
EKSはmasterに費用がかかる(東京リージョンで$144/月ぐらい)
GKEはmasterに費用がかからない!
どっちを選ぼう?
純粋にKubernetesを使いたいだけなら圧倒的にGKE
AWSのもろもろのサービスと合わせて使いたいならEKS
其他各種其他事項
没有介绍过的资源
在实践中,我会介绍一些关键资源,这些资源在实践过程中并没有提及。
工作
-
- 単発の処理を管理
指定した数だけPodを作成して処理を実行
apiVersion: batch/v1
kind: Job # Jobのマニフェスト
metadata:
name: example_job
labels:
app: example
spec:
parallelism: 3 # 同時に実行するPodの数
template: # ---ここからPodの定義--------------------------------------------
metadata:
labels:
app: example
spec:
(略)
定期任务
-
- 定期実行する処理を管理
スケジュールに沿ってPodを作成して処理を実行
コンテナ内でCronを設定しなくてよくなるので便利!
apiVersion: batch/v1beta1
kind: CronJob # CronJobのマニフェスト
metadata:
name: example_job
labels:
app: example
spec:
schedule: "*/1 * * * *" # 起動スケジュールをcronと同じ型式で定義
jobTemplate:
spec:
template: # ---ここからPodの定義--------------------------------------------
metadata:
labels:
app: example
spec:
(略)
配置映射
-
- アプリケーションの設定情報を定義してPodに提供
環境変数として提供
Volumeとして提供
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-example
data: # key-value型式で設定情報を書いていく
EXAMPLE: this_is_example
example.txt: |
this
is
example
# Podの定義中
(略)
env: # 環境変数として提供
- name: EXAMPLE
valueFrom:
configMapKeyRef:
name: cm-example
key: EXAMPLE
(略)
containers: # Volumeとして提供。これで /config/example.txt が扱えるようになる
- image: alpine
(略)
volumeMounts: ## コンテナ内でのVolumeのマウント設定
- name: cm-volume
mountPath: /config
volumes: ## Volumeの定義
- name: cm-volume
configMap:
name: cm-example
(略)
秘密
-
- アプリケーションの機密情報を定義してPodに提供
環境変数として提供
Volumeとして提供
ConfigMapとの違い
文字列をBase64エンコードした状態で扱う(バイナリデータを扱えるように)
もちろん暗号化目的じゃないので、そのままGithubに上げたりしたらダメ
(設定によって)etcd上に暗号化した状態で保存される
NodeではPodのtmpfs(要するにメモリ)上に保存される
Nodeは割り当てられたPodが参照するSecret以外はアクセスできない
いくつかTypeがある
Opaque: ConfigMapと同じ構造化されてないKey/Value形式
kubernetes.io/tls: TLSの秘密鍵と公開鍵を格納
kubernetes.io/service-account-token: Kubernetesのサービスアカウントのクレデンシャル
apiVersion: v1
kind: Secret
metadata:
name: secret-example
stringData:
password: xxxxxxxxxxxx # Base64エンコードされた文字列
credential.txt: |
xxxxxxxxxxxxx
xxxxxxxxxxxxx
xxxxxxxxxxxxx
# Podの定義中
(略)
env: # 環境変数として提供
- name: PASSWORD
valueFrom:
secretKeyRef:
name: secret-example
key: password
(略)
containers: # Volumeとして提供。これで /secrets/credential.txt が扱えるようになる
- image: alpine
(略)
volumeMounts: ## コンテナ内でのVolumeのマウント設定
- name: secret-volume
mountPath: /secrets
volumes: ## Volumeの定義
- name: secret-volume
secret:
secretName: secret-data
(略)
存储相关的各种事务
-
- PersistentVolume
ストレージの実体
AWSならNodeのEC2が持つEBS
PersistentVolumeClaim
ストレージを論理的に抽象化したリソース
PersistentVolumeに対して必要な容量を動的に確保
StorageClass
PersistentVolumeが確保するストレージの種類を定義
AWSなら io1/ gp2/sc1/st1
StatefulSet
継続的にデータを永続化するステートフルなアプリケーションの管理に向いたリソース
管理下のPodには連番の識別子が付与され、再作成されても同じ識別子であれば同じストレージを参照する
如果在云上使用Kubernetes,基本上会使用托管的数据库服务等,所以可能自己使用的机会不多。
可能会在外部提供的清单文件中进行记录,所以希望了解并知道它的存在。
调查清单文件的格式
当浏览Kubernetes相关信息的时候,无论是在书本还是在网页上,都可以找到大量的Manifest文件示例。然而,由于参数过多,能够完全解释的只有官方手册之类的资源,所以需要学会如何查阅这些资源。
https://qiita.com/Kta-M/items/039cee72e82590a0c4f4
使用时的基本结构概念
虽然我从未尝试过实际操作,但应该会有类似这样的布局。
总结
进行实践的目标(再次提及)。
-
- Kubernetesとお友達になる
イメージを掴む
触ってみる(ローカル・EKS・ちょっとGKE)
構築・運用ができるような気分になる
巷にあふれるKubernetesの記事・スライドが理解できるようになる
虽然这样说…
Kubernetes在大规模且复杂的系统中展现其真正价值。
如果是简单的系统,例如Fargate或ECS可能更好。
根据情况,甚至使用EC2的传统配置也足够了。
然而,数年后
我认为它很有可能成为司空见惯的技术。并且有充分的可能变得更加简单易用!比如说,是否有EKS的Fargate支持……。
无论如何,现阶段自己的选择增多是件好事,对吧!
所以
如果我们能够达到目标,那就太幸运了?
但是,我们刚刚踏入了Kubernetes的世界。这次无法完全介绍的术语和生态系统都是庞大的……
我们的战斗才刚刚开始!