查看Kubernetes
Kubernetes是什么?
Kubernetes是一种开源的容器编排系统,用于部署、扩展和管理容器化的应用程序。
Kubernetesが何をもたらしてくれるのかは、書籍Cloud Native DevOps with Kubernetesにある文章が端的であった。
https://www.oreilly.com/library/view/cloud-native-devops/9781492040750/ch01.html (いずれアクセスできなくなるかも)
根据Kubernetes社区的”传奇”人物Google的Kelsey Hightower所说,
Kubernetes does the things that the very best system administrator would do: automation, failover, centralized logging, monitoring.
Kubernetesは、自動化やフェイルオーバー、ログの中央管理、モニタリングのような、システム管理者の業務に対してベストなシステムを提供してくれる。
これに加えて、Kubernetesがもたらす価値は何か?という問いに対しては、
Many of the traditional sysadmin tasks like upgrading servers, installing security patches, configuring networks, running backups, and so on, are less of a concern in the cloud native world. Kubernetes can automate these things for you so that your team can concentrate on doing its core work.
サーバーをアップグレードしたり、セキュリティパッチのインストール、ネットワーク設定、バックアップを走らせる、などのシステム管理者の伝統的なタスクは、クラウドネイティブの世界では大して心配事ではなくなる。Kubernetesはこれらを自動化してくれるので、君たちはコアな仕事に集中できるのだ。
关于Kubernetes的学习
学习的困难之处
上記でKubernetesとは?の解答を引用したけれど、わかったようでわかった気にならないのではと思う。私はそうでした。
Kubernetesは巨大で、できることが多い。またKubernetesを説明するために必要な固有名詞も多い。Kubernetesを利用する開発者とKubernetesを運用する人によって語り口も異なる。Kubernetesは複数のコンポーネントが組み合わさったものを指すので、以下のようにアーキテクチャ自体も複雑である。
こうしたことから、「Kubernetes とは」でググって1時間ほど粘ったけれど、全体像が見えず、なんともわからない、といった経験をする。
学习Kubernetes的感觉类似于学习一个框架。就像被问到“什么是Spring?”而无法回答一样,被问到“什么是Kubernetes?”也无法用一句话解释。
さらに学習を困難にさせるのが、Kubernetes周辺の話題である。Kubernetes単体を導入するということはほぼ無く、例えばKubernetesのパッケージマネージャであるHelmや、CI/CDを提供するConcourseやJenkins、ログ収集基盤のDatadogやELK、モニタリング基盤を提供するPrometheusなど、これらすべてを同時に考慮することになる。また設計や開発に関しては、microserviceといったものや、DevOpsという文脈からはBlue/Greenデプロイ、カナリヤリリース、AgileやScrumにも言及される。「Kubernetesを学習しよう」という言葉には、こうしたKubernetes以外のものにも言及することが暗に含まれている。
そんな中で、これからKubernetesについて数回に分けて説明を試みるけれど、全てに触れることはない。個人的に”主要な概念”と思うことを書いていく。
如何进行学习呢?就像学习框架一样,要学习Kubernetes,首先需要掌握其基础知识。
-
- 実行環境を準備して、
- とりあえずチュートリアルに沿って動かしてみる、
另外,
- Kubernetesを説明している書籍を通読する、
王道究竟是什么呢?
K8s的执行环境
在本地服务器环境中构建Kubernetes的方法有多种,其中之一是通过kubeadm。按照这个设置步骤,可以相对容易地进行构建。作为环境要求,至少需要两台服务器。其中一台是Kubernetes Master服务器,用于执行在Kubernetes上运行容器等操作。另外一台是Kubernetes Node服务器,在这里实际运行容器。
作为更方便的 Kubernetes 执行环境,首先想到的是 minikube。minikube 是一个本地用于尝试的 Kubernetes 环境。据说它可以在 Windows 上通过 VirtualBox 运行(我没有安装过,所以不确定)。
如果你想要立即接触Kubernetes,那么有一个Kubernetes的playground供你使用。这篇文章非常易懂,按照它的指导你可以轻松地在Kubernetes上进行操作。(实际上,这与使用kubeadm进行配置的步骤并没有太大差异)
如果你想要一个可以自由使用且便捷的环境,那么最好选择订阅和使用Google的原生Kubernetes Engine(GKE)。虽然需要花一些钱,但一个月大约1000日元左右就可以享受了(具体取决于使用方式,不要完全相信这个数字)。
Kubernetes的教程
在Kubernetes的官方网站上有一个页面,上面汇集了许多教程。
Udemy上有各种各样的课程(link),Youtube上也有大量的视频。按照这些准备好的实际环境进行测试,我认为学习会取得进展。
k8s的图书
有许多书籍可供选择,但是如果选择一些稍微过时的书籍,可能会出现一些已经不再使用的术语,导致混淆不清,因此最好选择最新的书籍。我认为最好的方法是浏览官方文档 -> https://kubernetes.io/docs/concepts/。
博客
现在开始进入对Kubernetes具体功能的解释。
首先要理解”Pod”一词的含义。您可以参考此链接了解详细信息:https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/
Pod是一组容器的集合,对于管理容器的Kubernetes来说,它是最基础的概念。
在Kubernetes上运行容器的行为可以解释为部署Pod。
首先,让我们试着在Kubernetes上运行nginx的容器。
登录到Kubernetes Master服务器并在当前目录下创建一个名为Manifest的yaml文件,内容如下。在本环境中,服务器的主机名为node1,它是Master服务器。
apiVersion: v1
kind: Pod
metadata:
name: mynginx-pod
labels:
app: mynginx
spec:
containers:
- name: mynginx-container
image: nginx:1.12.0
忽略YAML的内容,这个文件主张的是”部署一个Nginx 1.12.0版本的容器”。
要在Kubernetes上应用此文件中的内容,请执行以下命令:
[node1 ~]$ kubectl apply -f mynginx-pod.yaml
pod/mynginx-pod created
然后,Pod将被部署。
[node1 ~]$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
mynginx-pod 1/1 Running 0 1m 10.46.0.1 node3 <none>
我想要关注的是NODE的列。在这里,这个Pod在被称为node3的节点上运行。在这个环境中,Kubernetes节点由两台服务器组成,分别是node2和node3。这次,确定指定的Pod在哪个节点上运行是由Master决定的。当然,也会有一些Pod希望根据节点的规格来运行。例如,希望将收集服务器系统日志的容器分别部署在每台服务器上。我不会在这里举例说明,但是是可以做到的。
像使用docker exec命令一样,登录到容器并检查Nginx的版本。
[node1 ~]$ kubectl exec -it mynginx-pod /bin/sh
# nginx -V
nginx version: nginx/1.12.0
built by gcc 6.3.0 20170205 (Debian 6.3.0-6)
built with OpenSSL 1.1.0e 16 Feb 2017 (running with OpenSSL 1.1.0f 25 May 2017)
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fdebug-prefix-map=/data/builder/debuild/nginx-1.12.0/debian/debuild-base/nginx-1.12.0=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector-strong -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-specs=/usr/share/dpkg/no-pie-link.specs -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie'
指定したVersion 1.12.0が立ち上がっている。
ここでkubectl execというコマンドでContainerの中に入ったけれど、ここでうれしいのは、Containerがどこで立ち上がっているかに依らず、Masterからであれば一律このコマンドでContainerの中に入れるところにある。同様にkubectl logsというコマンドで、Containerがどこで動いているかに依らずログを参照できる。
只要执行 kubectl delete 命令,该Pod就会消失。
[node1 ~]$ kubectl delete pods mynginx-pod
pod "mynginx-pod" deleted
[node1 ~]$ kubectl get pods
No resources found.
ここで、最初に「PodとはContainerの集まりである」と述べた。上記の例ではPod一つにあたりContainer一つ、の構成となっている。例えばPod一つにあたり、nginxとredisを含める場合には、以下のようにManifestファイルを記述する。
apiVersion: v1
kind: Pod
metadata:
name: nginx-redis
labels:
app: myapp
spec:
containers:
- name: mynginx
image: nginx:latest
- name: myredis
image: redis:latest
kubectl applyで適用してkubectl podsでデプロイされた様子を見てみる。
[node1 ~]$ kubectl apply -f nginx-redis.yaml
pod/nginx-redis created
[node1 ~]$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-redis 2/2 Running 0 8s
请注意,READY栏已经变为2/2。
このように、KubernetesにとってはPodが操作の基本単位となる。今回は例示のためにnginxとredisを同一Pod内に含めたが、実際はこうした構成は意味がないかもしれない。実際はPodには一つのContainerを含むことがほとんどであろうが、Podに複数のContainerを含むようなデザインパターンも存在する。例えばSidecar-PatternやAmbassador-Patternといった具合に。以下の記事が非常にわかりすい -> https://qiita.com/MahoTakara/items/03fc0afe29379026c1f3
部署
上記で例示したPodのManifestファイルでは、例えばそのPodが動いているノードが落ちたとき、そのPodも落ちる。これだけだとdockerコマンドで動かしているのと対して変わらない。
Kubernetesはこれまでも何度か述べたように、セルフヒーリング機能がある。つまり、あるPodのノードが動かなくなった場合に、それを検知し、別のノードで勝手に動かしてくれる。このあたりを定義するのがDeploymentである。
写一个类似以下的Manifest文件。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
无论yaml的内容如何,这句话的意思是“部署三个包含最新版本(latest)nginx容器的Pod”。 “Pod数量为三个”表示的是在Kubernetes上使用replicas:3这个参数进行定义,这在Kubernetes中被称为ReplicaSets。
应用这个,并观察 Pod 的情况。
[node1 ~]$ kubectl apply -f mynginx-deployment.yaml
deployment.apps/my-nginx configured
[node1 ~]$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
my-nginx-b685dc965-cnwb5 1/1 Running 0 16s 10.36.0.1 node2 <none>
my-nginx-b685dc965-nrrgl 1/1 Running 0 16s 10.44.0.1 node3 <none>
my-nginx-b685dc965-nwn9j 1/1 Running 0 16s 10.44.0.2 node3 <none>
有三个Pod正在运行。
ここでmy-nginx-b685dc965-cnwb5のPodを削除してみる。
[node1 ~]$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
my-nginx-b685dc965-5gpl8 1/1 Running 0 7s 10.36.0.1 node2 <none>
my-nginx-b685dc965-cnwb5 0/1 Terminating 0 1m 10.36.0.1 node2 <none>
my-nginx-b685dc965-nrrgl 1/1 Running 0 1m 10.44.0.1 node3 <none>
my-nginx-b685dc965-nwn9j 1/1 Running 0 1m 10.44.0.2 node3 <none>
然后,另一个 Pod 很快启动了起来。
Deployment默认具有滚动更新功能。例如,假设要将nginx的版本从latest更改为1.12.0。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.12.0
随后,开始启动新的Pod。
[node1 ~]$ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-nginx-5d5b7b9c8b-h2ddw 0/1 ContainerCreating 0 5s
my-nginx-b685dc965-5gpl8 1/1 Running 0 2m
my-nginx-b685dc965-nrrgl 1/1 Running 0 3m
my-nginx-b685dc965-nwn9j 1/1 Running 0 3m
尽管在文字中难以表达,但我在上面执行了一个操作,即部署了新版本(1.12.0)的一个Pod,然后删除了一个旧版本的Pod,再次部署了一个新版本的Pod,然后删除了一个旧版本的Pod…如此往复进行。
这种部署策略可以更加详细地进行设定。
服务
我们已经部署了到目前为止的nginx Pod,但如果无法在此处进行HTTP请求,就没有意义。作为管理这些网络的一种方式,我们介绍Service。
如果想要对上面提到的3个nginx进行负载均衡,Service将提供负载均衡功能,需要编写以下Manifest文件。
apiVersion: v1
kind: Service
metadata:
name: my-nginx-svc
labels:
app: nginx
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: 80
selector:
app: nginx
这句话主要表达了对于使用selector指定为nginx的Pod集合,将对8080端口的访问负载均衡并转发到Pod的80端口。
应用这个并进行确认。
[node1 ~]$ kubectl apply -f mynginx-service.yaml
service/my-nginx-svc created
[node1 ~]$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 20m
my-nginx-svc ClusterIP 10.106.9.75 <none> 8080/TCP 5s
尝试访问在这里自动生成的CLUSTER-IP,即10.106.9.75。
[node1 ~]$ curl -s 10.106.9.75:8080 | head -n4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
返回了nginx的欢迎页面。
观察负载均衡的情况。抛出以下10个不同请求,并查看每个 Pod 中 nginx 容器的访问日志。
[node1 ~]$ for i in `seq 10`; do curl 10.106.9.75:8080/${i[@]}; done
[node1 ~]$ for i in `kubectl get pod -o name`; do echo **********${i[@]}**********; kubectl logs ${i[@]}; done
**********pod/my-nginx-5d5b7b9c8b-4cpdl**********
10.32.0.1 - - [03/Mar/2019:03:33:58 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"10.32.0.1 - - [03/Mar/2019:03:34:04 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.32.0.1 - - [03/Mar/2019:03:34:09 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"10.32.0.1 - - [03/Mar/2019:03:35:35 +0000] "GET /1 HTTP/1.1" 404 169 "-" "curl/7.29.0" "-"
2019/03/03 03:35:35 [error] 8#8: *4 open() "/usr/share/nginx/html/1" failed (2: No such file or directory), client: 10.32.0.1, server: localhost, request: "GET /1
HTTP/1.1", host: "10.106.9.75:8080"10.32.0.1 - - [03/Mar/2019:03:35:35 +0000] "GET /2 HTTP/1.1" 404 169 "-" "curl/7.29.0" "-"
2019/03/03 03:35:35 [error] 8#8: *5 open() "/usr/share/nginx/html/2" failed (2: No such file or directory), client: 10.32.0.1, server: localhost, request: "GET /2
HTTP/1.1", host: "10.106.9.75:8080"
2019/03/03 03:35:35 [error] 8#8: *6 open() "/usr/share/nginx/html/5" failed (2: No such file or directory), client: 10.32.0.1, server: localhost, request: "GET /5
HTTP/1.1", host: "10.106.9.75:8080"
10.32.0.1 - - [03/Mar/2019:03:35:35 +0000] "GET /5 HTTP/1.1" 404 169 "-" "curl/7.29.0" "-"2019/03/03 03:35:35 [error] 8#8: *7 open() "/usr/share/nginx/html/9" failed (2: No such file or directory), client: 10.32.0.1, server: localhost, request: "GET /9
HTTP/1.1", host: "10.106.9.75:8080"10.32.0.1 - - [03/Mar/2019:03:35:35 +0000] "GET /9 HTTP/1.1" 404 169 "-" "curl/7.29.0" "-"
**********pod/my-nginx-5d5b7b9c8b-c6ckv**********
10.32.0.1 - - [03/Mar/2019:03:33:55 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
10.32.0.1 - - [03/Mar/2019:03:35:35 +0000] "GET /4 HTTP/1.1" 404 169 "-" "curl/7.29.0" "-"
2019/03/03 03:35:35 [error] 7#7: *2 open() "/usr/share/nginx/html/4" failed (2: No such file or directory), client: 10.32.0.1, server: localhost, request: "GET /4HTTP/1.1", host: "10.106.9.75:8080"
2019/03/03 03:35:35 [error] 7#7: *3 open() "/usr/share/nginx/html/10" failed (2: No such file or directory), client: 10.32.0.1, server: localhost, request: "GET /1
0 HTTP/1.1", host: "10.106.9.75:8080"
10.32.0.1 - - [03/Mar/2019:03:35:35 +0000] "GET /10 HTTP/1.1" 404 169 "-" "curl/7.29.0" "-"
**********pod/my-nginx-5d5b7b9c8b-h2ddw**********
10.32.0.1 - - [03/Mar/2019:03:33:46 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
2019/03/03 03:35:35 [error] 13#13: *2 open() "/usr/share/nginx/html/3" failed (2: No such file or directory), client: 10.32.0.1, server: localhost, request: "GET /3 HTTP/1.1", host: "10.106.9.75:8080"
10.32.0.1 - - [03/Mar/2019:03:35:35 +0000] "GET /3 HTTP/1.1" 404 169 "-" "curl/7.29.0" "-"10.32.0.1 - - [03/Mar/2019:03:35:35 +0000] "GET /6 HTTP/1.1" 404 169 "-" "curl/7.29.0" "-"
2019/03/03 03:35:35 [error] 13#13: *3 open() "/usr/share/nginx/html/6" failed (2: No such file or directory), client: 10.32.0.1, server: localhost, request: "GET /
6 HTTP/1.1", host: "10.106.9.75:8080"
10.32.0.1 - - [03/Mar/2019:03:35:35 +0000] "GET /7 HTTP/1.1" 404 169 "-" "curl/7.29.0" "-"
2019/03/03 03:35:35 [error] 13#13: *4 open() "/usr/share/nginx/html/7" failed (2: No such file or directory), client: 10.32.0.1, server: localhost, request: "GET /7 HTTP/1.1", host: "10.106.9.75:8080"
2019/03/03 03:35:35 [error] 13#13: *5 open() "/usr/share/nginx/html/8" failed (2: No such file or directory), client: 10.32.0.1, server: localhost, request: "GET /8 HTTP/1.1", host: "10.106.9.75:8080"
10.32.0.1 - - [03/Mar/2019:03:35:35 +0000] "GET /8 HTTP/1.1" 404 169 "-" "curl/7.29.0" "-"
已实现负载均衡。
宣言的定义
本次分享的示例,主要涉及到最基本的Pod、Deployment和Service。
回想起来,所有的操作只有以下的选择:
-
- Manifestファイルを記述して、
kubectl applyコマンドを適用する。
在这里,体现了Kubernetes的声明式配置思想。用户可以在清单文件中声明所期望的状态。一旦应用这些声明,无论之前的状态如何,都会达到声明的状态。此外,能够通过文本来定义这些状态也是一个好处。例如,通过Git等工具,可以将基础设施的状态作为源代码进行管理。这在基础设施即代码(Infrastructure as Code)的背景下经常被讨论。
还有Pod的数量和负载均衡设置也可以使用kubectl apply命令来反映。通过这种统一的操作能够反映各种类型的设置,或许可以想象与其他CI/CD工具的兼容性如何。