系列文章:
容器 & 服务:开篇,压力与资源
容器 & 服务:Jenkins 本地及 docker 安装部署
容器 & 服务:Jenkins 构建实例
容器 & 服务:一个 Java 应用的 Docker 构建实战
容器 & 服务:Docker 应用的 Jenkins 构建
容器 & 服务:Docker 应用的 Jenkins 构建 (二)
容器 & 服务:K8s 与 Docker 应用集群 (一)
关注公众号:程序员架构进阶,获取丰富资料~
一 概述
容器 & 服务:K8s 与 Docker 应用集群 (一)中,我们通过解决之前的一个遗留问题,初步了解了k8s的一些基础命令,做了一个应用部署。本篇将继续介绍k8s的一些原理,并优化应用demo。
二 K8s Pods与工作节点
为保证内容的准确性,本章内容主要来自kubernetes的官方文档。
2.1 kubernetes pods
与docker直接创建启动容器不同,Kubernates添加了一个Pod来托管我们的应用实例。Pod是K8s抽象出来的,用于表示一组一个,或一组多个应用程序容器(例如Docker),一级这些容器的一些共享资源。这些资源包括:
- 共享存储,当做卷
- 网络,作为唯一的集群IP地址
- 有关每个容器如何运行的信息,例如容器映像版本或要使用的特定端口
Pod为特定于应用程序的『逻辑主机』建模,并且可以包含相对紧耦合的不同应用容器。例如,Pod 可能既包含带有 Node.js 应用的容器,也包含另一个不同的容器,用于提供 Node.js 网络服务器要发布的数据。Pod 中的容器共享 IP 地址和端口,始终位于同一位置并且共同调度,并在同一工作节点上的共享上下文中运行。
Pod是 Kubernetes 平台上的原子单元。 当我们在 Kubernetes 上创建 Deployment 时,该 Deployment 会在其中创建包含容器的 Pod (而不是直接创建容器)。每个 Pod 都与调度它的工作节点绑定,并保持在那里直到终止(根据重启策略)或删除。 如果工作节点发生故障,则会在群集中的其他可用工作节点上调度相同的 Pod。
几个Pods示例:
2.2 k8s 工作节点
一个 pod 总是运行在 工作节点。工作节点是 Kubernetes 中的参与计算的机器,可以是虚拟机或物理计算机,具体取决于集群。每个工作节点由主节点管理。工作节点可以有多个 pod ,Kubernetes 主节点会自动处理在群集中的工作节点上调度 pod 。 主节点的自动调度考量了每个工作节点上的可用资源。
每个 Kubernetes 工作节点至少运行:
- Kubelet,负责 Kubernetes 主节点和工作节点之间通信的过程; 它管理 Pod 和机器上运行的容器。
- 容器运行时(如 Docker)负责从仓库中提取容器镜像,解压缩容器以及运行应用程序。
工作节点示例:
三 k8s部署demo应用
前面容器 & 服务:Docker 应用的 Jenkins 构建 (二)中,我们在github上提交过一个demo,通过docker run 、 docker-compose up 和 docker stack deploy 命令分别部署过应用。这里我们尝试使用k8s来执行部署。
3.1 使用本地镜像
3.1.1 查看并使用本地镜像部署
代码语言:javascript复制bogon xxx$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
dockerdemoapplication1 latest a0ea39905467 8 days ago 122MB
nginx latest f6d0b4767a6c 7 weeks ago 133MB
flamingskys/java-demo latest 8c8971d2d351 5 months ago 406MB
k8s.gcr.io/kube-apiserver v1.14.8 1e94481e8f30 16 months ago 209MB
k8s.gcr.io/kube-proxy v1.14.8 849af609e0c6 16 months ago 82.1MB
k8s.gcr.io/kube-controller-manager v1.14.8 36a8001a79fd 16 months ago 158MB
k8s.gcr.io/kube-scheduler v1.14.8 f1e3e5f9f93e 16 months ago 81.6MB
kubernetesui/metrics-scraper v1.0.1 709901356c11 19 months ago 40.1MB
docker/kube-compose-controller v0.4.23 a8c3d87a58e7 21 months ago 35.3MB
docker/kube-compose-api-server v0.4.23 f3591b2cb223 21 months ago 49.9MB
openjdk 8u201-jdk-alpine3.9 3675b9f543c5 23 months ago 105MB
k8s.gcr.io/coredns 1.3.1 eb516548c180 2 years ago 40.3MB
k8s.gcr.io/etcd 3.3.10 2c4adeb21b4f 2 years ago 258MB
k8s.gcr.io/pause 3.1 da86e6ba6ca1 3 years ago 742kB
能看到我们之间创建的镜像还在:dockerdemoapplication1,那么根据上一篇的kubectl create命令创建pod:
代码语言:javascript复制bogon xxx$ kubectl create deployment dockerdemoapplication1 --image=dockerdemoapplication1
deployment.apps/dockerdemoapplication1 created
暴露端口:
代码语言:javascript复制bogon xxx$ kubectl expose deployment/dockerdemoapplication1 --type="NodePort" --port 8088
service/dockerdemoapplication1 exposed
查看pod:
额,又是ImagePullBackOff?什么鬼,这是一个本地镜像啊。
用docker run 简单粗暴验证一下试试:
代码语言:javascript复制bogon xxx$ docker run -d -t -p 18081:8080 --name dockerdemoapplication1 dockerdemoapplication1
Unable to find image 'dockerdemoapplication1:latest' locally
找不到镜像。。
3.1.2 失败原因分析
怀疑是否使用方式不对,但docker也无法运行,那么可能是数据丢失导致镜像失效。重新构建镜像试一下:
代码语言:javascript复制bogon xxx$ docker build -t dockerdemoapplication1 .
Sending build context to Docker daemon 17MB
Step 1/7 : FROM openjdk:8u201-jdk-alpine3.9
---> 3675b9f543c5
Step 2/7 : MAINTAINER flamingstar <575912990@qq.com>
---> Running in fe92c7cca0e8
Removing intermediate container fe92c7cca0e8
---> 5eb4b2e2c65e
Step 3/7 : ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
---> Running in d6bef87ba5c8
Removing intermediate container d6bef87ba5c8
---> 4bb0bc8d3bed
Step 4/7 : VOLUME /tmp
---> Running in 4ce6a5581444
Removing intermediate container 4ce6a5581444
---> c677b3d055f0
Step 5/7 : ADD target/dockerdemo-1.0.0-SNAPSHOT.jar dockerdemo.jar
---> 6b6bc359bc3f
Step 6/7 : ENTRYPOINT ["java","-jar","/dockerdemo.jar"]
---> Running in c2d3347ef496
Removing intermediate container c2d3347ef496
---> a656e3f3f503
Step 7/7 : EXPOSE 8080
---> Running in ab53e5610ead
Removing intermediate container ab53e5610ead
---> a9e63d4e9118
Successfully built a9e63d4e9118
Successfully tagged dockerdemoapplication1:latest
再次docker run:
代码语言:javascript复制bogon xxx$ docker run -d -t -p 18081:8080 --name dockerdemoapplication1 dockerdemoapplication1
53ac95a804592adc4c94688a925b905da39a5838ae0f38f95e3839499eaff367
docker运行成功。停掉docker容器,再次用k8s部署:
代码语言:javascript复制bogon xxx$ kubectl create deployment dockerdemoapplication1 --image=dockerdemoapplication1
deployment.apps/dockerdemoapplication1 created
再次查看pod:
还是失败。
3.1.3 再次分析
查看pod详情: kubectl describe pod dockerdemoapplication1-5549fdf7fd-zxfbx ,输出信息如下,重点在Events部分:
kubelet, docker-desktop Failed to pull image "dockerdemoapplication1": rpc error: code = Unknown desc = Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
Normal SandboxChanged 6m43s kubelet, docker-desktop Pod sandbox changed, it will be killed and re-created.
kubelet, docker-desktop Failed to pull image "dockerdemoapplication1": rpc error: code = Unknown desc = Error response from daemon: pull access denied for dockerdemoapplication1, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
由此可看到错误信息。尽管我们意图是使用本地镜像,但kubelet实际上是从仓库中拉取: https://registry-1.docker.io/v2/;
repository does not exist or may require 'docker login': denied: requested access to the resource is denied
这个信息就更加明确,仓库不存在,或需要docker login,被拒绝。
3.1.4 解决
既然是打开方式不对,那么先考虑把镜像push到仓库就可以了。
3.1.4.1 推送命令
推送镜像规范
代码语言:javascript复制docker push 注册用户名/镜像名
1)执行推送
代码语言:javascript复制bogon xxx$ docker push dockerdemoapplication1:latest
The push refers to repository [docker.io/library/dockerdemoapplication1]
62e73adb4aec: Preparing
ee610d18148e: Preparing
dee6aef5c2b6: Preparing
a464c54f93a9: Preparing
denied: requested access to the resource is denied
失败,报了denied。 一般是没有在本地终端执行docker login导致,所以进行
2)登录操作
代码语言:javascript复制bogon xxx$ docker login
Authenticating with existing credentials...
Login did not succeed, error: Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username (flamingskys): flamingskys
Password:
Login Succeeded
3)登录成功后再次执行1)
4)重新构建镜像
错误不意外,因为按照规范,我们push要指定仓库,这点和github类似。而上面的镜像没有指定默认仓库(账号),直接裸传出错是正常的。所以重新构建镜像(注意别忘了,在工程所在的目录下执行):
代码语言:javascript复制docker build -t flamingskys/dockerdemoapplication1 .
5)正式push镜像
代码语言:javascript复制bogon xxx$ docker push flamingskys/dockerdemoapplication1:latest
The push refers to repository [docker.io/flamingskys/dockerdemoapplication1]
62e73adb4aec: Pushed
ee610d18148e: Pushed
dee6aef5c2b6: Pushed
a464c54f93a9: Pushed
latest: digest: sha256:afed6ab41808512962a50b0521ca9f5d870f4c6281114656e396f44d85c0019f size: 1159
6)kubectl部署
有了远程镜像,我们再次尝试创建deployment(记得要先删除之前创建失败的deployment):
然后查看pod状态,终于成功了! ————————这只是内心OS,然而并没有。。。查看pod详情,发现报错信息如下:
关键错误信息:
Get https://registry-1.docker.io/v2/flamingskys/dockerdemoapplication1/manifests/latest: net/http: TLS handshake timeout
Get https://registry-1.docker.io/v2/: net/http: TLS handshake timeout
网络TLS握手超时,看起来是网络的问题,但可以确定,是从我们指定的位置拉取镜像了,只是家里的破网不够给力。ok,删掉deployment再重试一次:
容器还在创建中,describe一下pod详情:
代码语言:javascript复制bogon xxx$ kubectl describe pod dockerdemoapplication1-648f767cff-dd5t7
Name: dockerdemoapplication1-648f767cff-dd5t7
Namespace: default
Priority: 0
PriorityClassName: <none>
Node: docker-desktop/192.168.65.3
Start Time: Fri, 05 Mar 2021 22:02:27 0800
Labels: app=dockerdemoapplication1
pod-template-hash=648f767cff
Annotations: <none>
Status: Pending
IP:
Controlled By: ReplicaSet/dockerdemoapplication1-648f767cff
Containers:
dockerdemoapplication1:
Container ID:
Image: flamingskys/dockerdemoapplication1
Image ID:
Port: <none>
Host Port: <none>
State: Waiting
Reason: ContainerCreating
Ready: False
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-62ccr (ro)
Conditions:
Type Status
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
default-token-62ccr:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-62ccr
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 20s default-scheduler Successfully assigned default/dockerdemoapplication1-648f767cff-dd5t7 to docker-desktop
Normal Pulling 19s kubelet, docker-desktop Pulling image "flamingskys/dockerdemoapplication1"
最后Events 中的Successfully,终于成功。
别高兴过早,还有一个步骤没做呢。
7)暴露服务
使用18081端口吧,避免冲突:
代码语言:javascript复制bogon xxx$ kubectl expose deployment/dockerdemoapplication1 --type="NodePort" --port 18081
Error from server (AlreadyExists): services "dockerdemoapplication1" already exists
services已存在,是之前发布的遗留问题,先执行删除后再暴露一次:
代码语言:javascript复制bogon xxx$ kubectl delete service dockerdemoapplication1
service "dockerdemoapplication1" deleted
bogon xxx$ kubectl expose deployment/dockerdemoapplication1 --type="NodePort" --port 18081
service/dockerdemoapplication1 exposed
至此,应用部署成功。
四 总结
本篇介绍了kubernete的关键概念:pods 和 工作节点,描述了大概的架构和运行时结构。然后,基于上一篇的基础,重新使用k8s的kubectl命令部署我们自己的demo应用,并分析解决过程中遇到的问题。下一张将会进一步阐述原理,并对demo进行丰富。