Docker 和 Kubernetes 从听过到略懂:给程序员的旋风教程

简介: 早在 Docker 正式发布几个月的时候,LeanCloud 就开始在生产环境大规模使用 Docker,在过去几年里 Docker 的技术栈支撑了我们主要的后端架构。这是一篇写给程序员的 Docker 和 Kubernetes 教程,目的是让熟悉技术的读者在尽可能短的时间内对 Docker 和 Kubernetes 有基本的了解,并通过实际部署、升级、回滚一个服务体验容器化生产环境的原理和好处。
早在 Docker 正式发布几个月的时候,LeanCloud 就开始在生产环境大规模使用 Docker,在过去几年里 Docker 的技术栈支撑了我们主要的后端架构。这是一篇写给程序员的 Docker 和 Kubernetes 教程,目的是让熟悉技术的读者在尽可能短的时间内对 Docker 和 Kubernetes 有基本的了解,并通过实际部署、升级、回滚一个服务体验容器化生产环境的原理和好处。本文假设读者都是开发者,并熟悉 Mac/Linux 环境,所以就不介绍基础的技术概念了。命令行环境以 Mac 示例,在 Linux 下只要根据自己使用的发行版和包管理工具做调整即可。

Docker 速成

首先快速地介绍一下 Docker:作为示例,我们在本地启动 Docker 的守护进程,并在一个容器里运行简单的 HTTP 服务。先完成安装:


$ brew cask install docker

上面的命令会从 Homebrew 安装 Docker for Mac,它包含 Docker 的后台进程和命令行工具。Docker 的后台进程以一个 Mac App 的形式安装在 /Applications 里,需要手动启动。启动 Docker 应用后,可以在 Terminal 里确认一下命令行工具的版本:
$ docker --version
Docker version 18.03.1-ce, build 9ee9f40

上面显示的 Docker 版本可能和我的不一样,但只要不是太老就好。我们建一个单独的目录来存放示例所需的文件。为了尽量简化例子,我们要部署的服务是用 Nginx 来 serve 一个简单的 HTML 文件 html/index.html。
$ mkdir docker-demo
$ cd docker-demo
$ mkdir html
$ echo '<h1>Hello Docker!</h1>' > html/index.html

接下来在当前目录创建一个叫 Dockerfile 的新文件,包含下面的内容:
FROM nginx
COPY html/* /usr/share/nginx/html

每个 Dockerfile 都以 FROM ... 开头。 FROM nginx 的意思是以 Nginx 官方提供的镜像为基础来构建我们的镜像。在构建时,Docker 会从 Docker Hub 查找和下载需要的镜像。Docker Hub 对于 Docker 镜像的作用就像 GitHub 对于代码的作用一样,它是一个托管和共享镜像的服务。使用过和构建的镜像都会被缓存在本地。第二行把我们的静态文件复制到镜像的 /usr/share/nginx/html 目录下。也就是 Nginx 寻找静态文件的目录。Dockerfile 包含构建镜像的指令,更详细的信息可以参考 这里

然后就可以构建镜像了:
$ docker build -t docker-demo:0.1 .

请确保你按照上面的步骤为这个实验新建了目录,并且在这个目录中运行 docker build。如果你在其它有很多文件的目录(比如你的用户目录或者 /tmp)运行,Docker 会把当前目录的所有文件作为上下文发送给负责构建的后台进程。

这行命令中的名称 docker-demo 可以理解为这个镜像对应的应用名或服务名,0.1 是标签。Docker 通过名称和标签的组合来标识镜像。可以用下面的命令来看到刚刚创建的镜像:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
docker-demo 0.1 efb8ca048d5a 5 minutes ago 109MB

下面我们把这个镜像运行起来。Nginx 默认监听在 80 端口,所以我们把宿主机的 8080 端口映射到容器的 80 端口:
$ docker run --name docker-demo -d -p 8080:80 docker-demo:0.1

用下面的命令可以看到正在运行中的容器:
$ docker container ps
CONTAINER ID IMAGE ... PORTS NAMES
c495a7ccf1c7 docker-demo:0.1 ... 0.0.0.0:8080->80/tcp docker-demo

这时如果你用浏览器访问 http://localhost:8080 ,就能看到我们刚才创建的「Hello Docker!」页面。

在现实的生产环境中 Docker 本身是一个相对底层的容器引擎,在有很多服务器的集群中,不太可能以上面的方式来管理任务和资源。所以我们需要 Kubernetes 这样的系统来进行任务的编排和调度。在进入下一步前,别忘了把实验用的容器清理掉:
$ docker container stop docker-demo
$ docker container rm docker-demo

安装 Kubernetes

介绍完 Docker,终于可以开始试试 Kubernetes 了。我们需要安装三样东西:Kubernetes 的命令行客户端 kubctl、一个可以在本地跑起来的 Kubernetes 环境 Minikube、以及给 Minikube 使用的虚拟化引擎 xhyve。
$ brew install kubectl
$ brew cask install minikube
$ brew install docker-machine-driver-xhyve

Minikube 默认的虚拟化引擎是 VirtualBox,而 xhyve 是一个更轻量、性能更好的替代。它需要以 root 权限运行,所以安装完要把所有者改为 root:wheel,并把 setuid 权限打开:
$ sudo chown root:wheel /usr/local/opt/docker-machine-driver-xhyve/bin/docker-machine-driver-xhyve
$ sudo chmod u+s /usr/local/opt/docker-machine-driver-xhyve/bin/docker-machine-driver-xhyve

然后就可以启动 Minikube 了:
$ minikube start --vm-driver xhyve

你多半会看到一个警告说 xhyve 会在未来的版本被 hyperkit 替代,推荐使用 hyperkit。不过在我写这个教程的时候 docker-machine-driver-hyperkit 还没有进入 Homebrew, 需要手动编译和安装,我就偷个懒,仍然用 xhyve。以后只要在安装和运行的命令中把 xhyve 改为 hyperkit 就可以。

如果你在第一次启动 Minikube 时遇到错误或被中断,后面重试仍然失败时,可以尝试运行 minikube delete 把集群删除,重新来过。

Minikube 启动时会自动配置 kubectl,把它指向 Minikube 提供的 Kubernetes API 服务。可以用下面的命令确认:
$ kubectl config current-context
minikube

Kubernetes 架构简介

典型的 Kubernetes 集群包含一个 master 和很多 node。Master 是控制集群的中心,node 是提供 CPU、内存和存储资源的节点。Master 上运行着多个进程,包括面向用户的 API 服务、负责维护集群状态的 Controller Manager、负责调度任务的 Scheduler 等。每个 node 上运行着维护 node 状态并和 master 通信的 kubelet,以及实现集群网络服务的 kube-proxy。

作为一个开发和测试的环境,Minikube 会建立一个有一个 node 的集群,用下面的命令可以看到:
$ kubectl get nodes
NAME STATUS AGE VERSION
minikube Ready 1h v1.10.0

部署一个单实例服务

我们先尝试像文章开始介绍 Docker 时一样,部署一个简单的服务。Kubernetes 中部署的最小单位是 pod,而不是 Docker 容器。实时上 Kubernetes 是不依赖于 Docker 的,完全可以使用其他的容器引擎在 Kubernetes 管理的集群中替代 Docker。在与 Docker 结合使用时,一个 pod 中可以包含一个或多个 Docker 容器。但除了有紧密耦合的情况下,通常一个 pod 中只有一个容器,这样方便不同的服务各自独立地扩展。

Minikube 自带了 Docker 引擎,所以我们需要重新配置客户端,让 docker 命令行与 Minikube 中的 Docker 进程通讯:
$ eval $(minikube docker-env)

在运行上面的命令后,再运行 docker image ls 时只能看到一些 Minikube 自带的镜像,就看不到我们刚才构建的 docker-demo:0.1 镜像了。所以在继续之前,要重新构建一遍我们的镜像,这里顺便改一下名字,叫它 k8s-demo:0.1。
$ docker build -t k8s-demo:0.1 .

然后创建一个叫 pod.yml 的定义文件:
apiVersion: v1
kind: Pod
metadata:
name: k8s-demo
spec:
containers:
- name: k8s-demo
 image: k8s-demo:0.1
 ports:
 - containerPort: 80

这里定义了一个叫 k8s-demo 的 Pod,使用我们刚才构建的 k8s-demo:0.1 镜像。这个文件也告诉 Kubernetes 容器内的进程会监听 80 端口。然后把它跑起来:
$ kubectl create -f pod.yml
pod "k8s-demo" created

kubectl 把这个文件提交给 Kubernetes API 服务,然后 Kubernetes Master 会按照要求把 Pod 分配到 node 上。用下面的命令可以看到这个新建的 Pod:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
k8s-demo 1/1 Running 0 5s

因为我们的镜像在本地,并且这个服务也很简单,所以运行 kubectl get pods 的时候 STATUS 已经是 running。要是使用远程镜像(比如 Docker Hub 上的镜像),你看到的状态可能不是 Running,就需要再等待一下。

虽然这个 pod 在运行,但是我们是无法像之前测试 Docker 时一样用浏览器访问它运行的服务的。可以理解为 pod 都运行在一个内网,我们无法从外部直接访问。要把服务暴露出来,我们需要创建一个 Service。Service 的作用有点像建立了一个反向代理和负载均衡器,负责把请求分发给后面的 pod。

创建一个 Service 的定义文件 svc.yml:
apiVersion: v1
kind: Service
metadata:
name: k8s-demo-svc
labels:
app: k8s-demo
spec:
type: NodePort
ports:
- port: 80
 nodePort: 30050
selector:
app: k8s-demo

这个 service 会把容器的 80 端口从 node 的 30050 端口暴露出来。注意文件最后两行的 selector 部分,这里决定了请求会被发送给集群里的哪些 pod。这里的定义是所有包含「app: k8s-demo」这个标签的 pod。然而我们之前部署的 pod 并没有设置标签:
$ kubectl describe pods | grep Labels
Labels: <none>

所以要先更新一下 pod.yml,把标签加上(注意在 metadata: 下增加了 labels 部分):
apiVersion: v1
kind: Pod
metadata:
name: k8s-demo
labels:
app: k8s-demo
spec:
containers:
- name: k8s-demo
 image: k8s-demo:0.1
 ports:
 - containerPort: 80

然后更新 pod 并确认成功新增了标签:
$ kubectl apply -f pod.yml
pod "k8s-demo" configured
$ kubectl describe pods | grep Labels
Labels: app=k8s-demo

然后就可以创建这个 service 了:
$ kubectl create -f svc.yml
service "k8s-demo-svc" created

用下面的命令可以得到暴露出来的 URL,在浏览器里访问,就能看到我们之前创建的网页了。
$ minikube service k8s-demo-svc --url
http://192.168.64.4:30050

横向扩展、滚动更新、版本回滚

在这一节,我们来实验一下在一个高可用服务的生产环境会常用到的一些操作。在继续之前,先把刚才部署的 Pod 删除(但是保留 service,下面还会用到):
$ kubectl delete pod k8s-demo
pod "k8s-demo" deleted

在正式环境中我们需要让一个服务不受单个节点故障的影响,并且还要根据负载变化动态调整节点数量,所以不可能像上面一样逐个管理 pod。Kubernetes 的用户通常是用 Deployment 来管理服务的。一个 deployment 可以创建指定数量的 pod 部署到各个 node 上,并可完成更新、回滚等操作。

首先我们创建一个定义文件 deployment.yml:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: k8s-demo-deployment
spec:
replicas: 10
template:
metadata:
 labels:
 app: k8s-demo
spec:
 containers:
 - name: k8s-demo-pod
 image: k8s-demo:0.1
 ports:
 - containerPort: 80

注意开始的 apiVersion 和之前不一样,因为 Deployment API 没有包含在 v1 里,replicas: 10 指定了这个 deployment 要有 10 个 pod,后面的部分和之前的 pod 定义类似。提交这个文件,创建一个 deployment:
$ kubectl create -f deployment.yml
deployment "k8s-demo-deployment" created

用下面的命令可以看到这个 deployment 的副本集(replica set),有 10 个 Pod 在运行。
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
k8s-demo-deployment-774878f86f 10 10 10 19s

假设我们对项目做了一些改动,要发布一个新版本。这里作为示例,我们只把 HTML 文件的内容改一下, 然后构建一个新版镜像 k8s-demo:0.2:
$ echo '<h1>Hello Kubernetes!</h1>' > html/index.html
$ docker build -t k8s-demo:0.2 .

然后更新 deployment.yml:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: k8s-demo-deployment
spec:
replicas: 10
minReadySeconds: 10
strategy:
type: RollingUpdate
rollingUpdate:
 maxUnavailable: 1
 maxSurge: 1
template:
metadata:
 labels:
 app: k8s-demo
spec:
 containers:
 - name: k8s-demo-pod
 image: k8s-demo:0.2
 ports:
 - containerPort: 80

这里有两个改动,第一个是更新了镜像版本号 image: k8s-demo:0.2,第二是增加了 minReadySeconds: 10 和 strategy 部分。新增的部分定义了更新策略:minReadySeconds: 10 指在更新了一个 pod 后,需要在它进入正常状态后 10 秒再更新下一个 pod;maxUnavailable: 1 指同时处于不可用状态的 pod 不能超过一个;maxSurge: 1 指多余的 Pod 不能超过一个。这样 Kubernetes 就会逐个替换 service 后面的 pod。运行下面的命令开始更新:
$ kubectl apply -f deployment.yml --record=true
deployment "k8s-demo-deployment" configured

这里的 --record=true 让 Kubernetes 把这行命令记到发布历史中备查。这时可以马上运行下面的命令查看各个 Pod 的状态:
$ kubectl get pods
NAME READY STATUS ... AGE
k8s-demo-deployment-774878f86f-5wnf4 1/1 Running ... 7m
k8s-demo-deployment-774878f86f-6kgjp 0/1 Terminating ... 7m
k8s-demo-deployment-774878f86f-8wpd8 1/1 Running ... 7m
k8s-demo-deployment-774878f86f-hpmc5 1/1 Running ... 7m
k8s-demo-deployment-774878f86f-rd5xw 1/1 Running ... 7m
k8s-demo-deployment-774878f86f-wsztw 1/1 Running ... 7m
k8s-demo-deployment-86dbd79ff6-7xcxg 1/1 Running ... 14s
k8s-demo-deployment-86dbd79ff6-bmvd7 1/1 Running ... 1s
k8s-demo-deployment-86dbd79ff6-hsjx5 1/1 Running ... 26s
k8s-demo-deployment-86dbd79ff6-mkn27 1/1 Running ... 14s
k8s-demo-deployment-86dbd79ff6-pkmlt 1/1 Running ... 1s
k8s-demo-deployment-86dbd79ff6-thh66 1/1 Running ... 26s

从 AGE 列就能看到有一部分 Pod 是刚刚新建的,有的 Pod 则还是老的。下面的命令可以显示发布的实时状态:
$ kubectl rollout status deployment k8s-demo-deployment
Waiting for rollout to finish: 1 old replicas are pending termination...
Waiting for rollout to finish: 1 old replicas are pending termination...
deployment "k8s-demo-deployment" successfully rolled out

由于我输入得比较晚,发布已经快要结束,所以只有三行输出。下面的命令可以查看发布历史,因为第二次发布使用了 --record=true 所以可以看到用于发布的命令。
$ kubectl rollout history deployment k8s-demo-deployment
deployments "k8s-demo-deployment"
REVISION CHANGE-CAUSE
1 <none>
2 kubectl apply --filename=deploy.yml --record=true

这时如果刷新浏览器,就可以看到更新的内容「Hello Kubernetes!」。假设新版发布后,我们发现有严重的 bug,需要马上回滚到上个版本,可以用这个很简单的操作:
$ kubectl rollout undo deployment k8s-demo-deployment --to-revision=1
deployment "k8s-demo-deployment" rolled back

Kubernetes 会按照既定的策略替换各个 Pod,与发布新版本类似,只是这次是用老版本替换新版本:
$ kubectl rollout status deployment k8s-demo-deployment
Waiting for rollout to finish: 4 out of 10 new replicas have been updated...
Waiting for rollout to finish: 6 out of 10 new replicas have been updated...
Waiting for rollout to finish: 8 out of 10 new replicas have been updated...
Waiting for rollout to finish: 1 old replicas are pending termination...
deployment "k8s-demo-deployment" successfully rolled out

在回滚结束之后,刷新浏览器就可以确认网页内容又改回了「Hello Docker!」。

结语

我们从不同层面实践了一遍镜像的构建和容器的部署,并且部署了一个有 10 个容器的 deployment, 实验了滚动更新和回滚的流程。Kubernetes 提供了非常多的功能,本文只是以走马观花的方式做了一个快节奏的 walkthrough,略过了很多细节。虽然你还不能在简历上加上「精通 Kubernetes」,但是应该可以在本地的 Kubernetes 环境测试自己的前后端项目,遇到具体的问题时求助于 Google 和官方文档即可。在此基础上进一步熟悉应该就可以在别人提供的 Kubernetes 生产环境发布自己的服务。

LeanCloud 的大部分服务都运行在基于 Docker 的基础设施上,包括各个 API 服务、中间件、后端任务等。大部分使用 LeanCloud 的开发者主要工作在前端,不过云引擎是我们的产品中让容器技术离用户最近的。云引擎提供了容器带来的隔离良好、扩容简便等优点,同时又直接支持各个语言的原生依赖管理,为用户免去了镜像构建、监控、恢复等负担,很适合希望把精力完全投入在开发上的用户。

本文转自DockOne-Docker 和 Kubernetes 从听过到略懂:给程序员的旋风教程

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
12月前
|
Kubernetes Docker Python
Docker 与 Kubernetes 容器化部署核心技术及企业级应用实践全方案解析
本文详解Docker与Kubernetes容器化技术,涵盖概念原理、环境搭建、镜像构建、应用部署及监控扩展,助你掌握企业级容器化方案,提升应用开发与运维效率。
1413 108
|
11月前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
2544 10
|
10月前
|
应用服务中间件 网络安全 nginx
手把手教你使用 Docker 部署 Nginx 教程
本文详解Nginx核心功能与Docker部署优势,涵盖镜像拉取、容器化部署(快速、挂载、Compose)、HTTPS配置及常见问题处理,助力高效搭建稳定Web服务。
4153 4
|
11月前
|
Kubernetes Devops Docker
Kubernetes 和 Docker Swarm:现代 DevOps 的理想容器编排工具
本指南深入解析 Kubernetes 与 Docker Swarm 两大主流容器编排工具,涵盖安装、架构、网络、监控等核心维度,助您根据团队能力与业务需求精准选型,把握云原生时代的技术主动权。
892 115
|
文件存储 数据安全/隐私保护 开发者
群晖NAS Docker镜像源加速配置教程
本教程介绍了群晖NAS用户如何通过配置轩辕镜像加速服务提升Docker镜像拉取速度。内容包括配置前准备、详细设置步骤及日常使用说明,帮助用户快速完成配置并享受高效稳定的镜像下载体验。
6679 60
|
10月前
|
存储 搜索推荐 数据库
🚀 RAGFlow Docker 部署全流程教程
RAGFlow是开源的下一代RAG系统,融合向量数据库与大模型,支持全文检索、插件化引擎切换,适用于企业知识库、智能客服等场景。支持Docker一键部署,提供轻量与完整版本,助力高效搭建私有化AI问答平台。
8379 8
|
存储 Kubernetes 监控
Docker与Kubernetes集成挑战及方案
面对这些挑战,并不存在一键解决方案。如同搭建灌溉系统需要考虑多种因素,集成Docker与Kubernetes也需要深思熟虑的规划、相当的技术知识和不断的调试。只有这样,才能建立起一个稳定、健康、高效的Docker-Kubernetes生态,让你的应用像花园中的植物一样繁荣生长。
480 63
|
12月前
|
缓存 Ubuntu Docker
Ubuntu环境下删除Docker镜像与容器、配置静态IP地址教程。
如果遇见问题或者想回滚改动, 可以重启系统.
667 16
|
运维 关系型数据库 MySQL
宝塔面板 Docker 镜像加速教程
本文介绍了如何为宝塔面板中的 Docker 配置轩辕镜像加速。内容包括:安装 Docker 管理器、获取镜像加速地址、配置加速 URL 以及验证设置等步骤。通过使用免费版或专业版加速地址(如 docker.xuanyuan.me 或 xuanyuan.cloud),可显著提升镜像拉取速度。同时,还提供了日常使用方法和注意事项,帮助用户顺利完成配置并保障 Docker 服务稳定运行。
949 0

热门文章

最新文章