2. 使用Kubernetes和Docker
- 2019 年 10 月 10 日
- 筆記
1. 内容
- 使用Docker创建、运行及共享容器镜像
- 在本地部署单节点的Kubernetes集群
- 配置和使用命令行客户端——kubectl
- 在Kubernetes上部署应用并进行水平伸缩
2. 创建、运行及共享容器镜像
介绍
深入学习前,先看看如何创建一个简单的应用、打包成容器镜像、在远程集群或本地集群运行
步骤简介
- 安装Docker并运行第一个“Hello World”容器
- 创建一个简单的php应用并部署在Kubernetes
- 把应用打包成可以独立运行的容器镜像
- 基于镜像运行容器
- 把镜像推送到Docker Hub,这样任何人在任何地方都可以使用
安装并运行Docker
- 安装:https://docs.docker.com/docker-for-mac/install/
- 运行Hello World容器
- busybox是一个集成最常用linux命令的linux系统容器镜像
- 使用docker run命令指定镜像名字、执行命令(可选),如图所示
- 通过一个命令就运行了一个完整的“应用”,而不用做其它的事情
- 重要的是应用是在容器内部执行的,完全独立于基于其他主机上运行的进程
- 背后原理
- 执行docker run命令(
docker run busybox echo "Hello World"
)后,docker会检查busybox:latest镜像是否已经存在本机,如果没有会从http://docker.io的镜像中心拉取 - 镜像下载到本机后,docker基于这个镜像创建一个容器并在容器中运行命令
- echo命令输出后,进程终止,容器停止运行
- 执行docker run命令(
- 运行其它镜像
- 如果想运行其它镜像可以在hub.docker.com网站搜索
- 然后像这样运行镜像
docker run <image>
- 容器镜像的版本管理
- docker支持同一镜像的多个版本,每个版本必须有唯一的tag名,当没有显式指定tag时,docker默认指定为latest
- 运行别的版本镜像
docker run <image>:<tag>
创建一个简单的php应用
- 这个应用程序输出当前时间
- 应用运行在容器中,看到的是自己的主机名而不是宿主机名,即使它像其它进程一样运行在宿主机上
- 这在后面非常有用,当应用部署在Kubernetes上进行伸缩时(复制应用到多个节点),它的请求切换到了应用的不同实例上

为镜像创建Dockerfile
- 把应用打包成镜像,首先需要创建名为Dockerfile的文件,它包含了一系列构建镜像时执行的指令
- Dockerfile和index.php在同一目录
- Dockerfile内容如下

- FROM定义了基础镜像,使用了7-zts-alpine3.9版本
- ADD是把index.php从本地文件夹添加到镜像的/目录
- EXPOSE声明暴露的端口号
- WORKDIR指定默认工作目录
- CMD默认执行的命令,意思是用php启动一个web服务器,端口为8080
构建容器镜像
- 运行docker命令(
docker build -t php:local .
)来构建镜像 - 构建过程不是用Docker客户端完成,而将整个目录上传到Docker守护进程(Docker客户端和守护进程可以不在一台机器上)
- 构建过程中,Docker首次会从公开镜像仓库(Docker Hub)拉取基础镜像(php:7-zts-alpine3.9)
- 最后一个
.
是告诉Docker是基于当前目录,构建名为php、标签为local的镜像;Docker会在目录中寻找Dockerfile,然后基于其中指令构建镜像
使用docker images命令查看生成的镜像 $ docker images php local b26e7de6427c 9 hours ago 64.7MB php 7-zts-alpine3.9 0adbdb1b2250 2 weeks ago 64.7MB
- 构建镜像过程

- 镜像分层
- 镜像不是一个大的二进制块,而由多层组成的,在busybox例子中,每一层有一行Pull complete,不同镜像可能会共享分层,这会让存储和传输变得更高效
- Dockerfile每一条单独的指令都会创建一个新层
运行容器镜像
- 运行应用容器
- 运行命令
docker run --name php-container -p 8000:8000 -d php:local
- 这条命令告知Docker是基于php:local镜像创建一个名为php-container的容器,本机8000端口映射到容器的8000的端口,-d表示后台运行
- 运行命令
- 访问应用
- 运行命令
$ curl http://localhost:8000
- 通过http://localhost:8000访问应用,输出当前运行时间。应用是运行在容器中,与其他应用隔离
- 运行命令
- 列出所有运行中的容器
- 运行命令
docker ps
- 运行命令
命令结果 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2b3eb8cbab22 php:local "docker-php-entrypoi…" 2 minutes ago Up 2 minutes 0.0.0.0:8000->8000/tcp php-container
- 有一个容器正在运行,显示容器的id、名称、运行的镜像、命令等信息
- 获取容器信息
- 运行命令
docker inspect php-container
会打印出包含容器底层信息的长json
- 运行命令
探索运行容器的内部
- 在已有的容器内部运行shell
- 在php-container容器执行ls -al /,命令和主容器进程有相同的命名空间
- -i:确保标准输入流保持开放,需要在shell中输入命令
- -t:分配一个伪终端(TTY)
- 运行命令
docker exec -it php-container ls -al /
输出结果 $ docker exec -it php-container ls -al / total 68 drwxr-xr-x 1 root root 4096 May 28 23:13 . drwxr-xr-x 1 root root 4096 May 28 23:13 .. -rwxr-xr-x 1 root root 0 May 28 23:13 .dockerenv drwxr-xr-x 1 root root 4096 May 11 03:27 bin drwxr-xr-x 5 root root 340 May 28 23:13 dev drwxr-xr-x 1 root root 4096 May 28 23:13 etc drwxr-xr-x 1 root root 4096 May 11 03:04 home -rw-r--r-- 1 root root 33 May 28 23:12 index.php
- 参数介绍
- 从内部探索容器
- 进入容器命令
docker exec -it php-container sh
- 进入容器命令
查看容器里运行的进程 # ps -ef PID USER TIME COMMAND 1 root 0:00 /usr/local/bin/php -S 0.0.0.0:8000 7 root 0:00 sh 12 root 0:00 ps -ef
- 容器的文件系统也是独立的
执行ls -al /命令查看 # ls -al / total 68 drwxr-xr-x 1 root root 4096 May 29 03:58 . drwxr-xr-x 1 root root 4096 May 29 03:58 .. -rwxr-xr-x 1 root root 0 May 29 03:58 .dockerenv drwxr-xr-x 1 root root 4096 May 11 03:27 bin drwxr-xr-x 5 root root 340 May 29 03:58 dev drwxr-xr-x 1 root root 4096 May 29 03:58 etc drwxr-xr-x 1 root root 4096 May 11 03:04 home -rw-r--r-- 1 root root 33 May 28 23:12 index.php drwxr-xr-x 1 root root 4096 May 11 03:27 lib drwxr-xr-x 5 root root 4096 May 9 20:49 media drwxr-xr-x 2 root root 4096 May 9 20:49 mnt drwxr-xr-x 2 root root 4096 May 9 20:49 opt dr-xr-xr-x 262 root root 0 May 29 03:58 proc drwx------ 1 root root 4096 May 29 03:59 root drwxr-xr-x 2 root root 4096 May 9 20:49 run drwxr-xr-x 2 root root 4096 May 9 20:49 sbin drwxr-xr-x 2 root root 4096 May 9 20:49 srv dr-xr-xr-x 13 root root 0 May 29 03:58 sys drwxrwxrwt 1 root root 4096 May 11 03:27 tmp drwxr-xr-x 1 root root 4096 May 11 03:27 usr drwxr-xr-x 1 root root 4096 May 11 03:04 var
- 容器拥有完整的文件系统、进程、用户、主机名和网络接口
停止和删除容器
- 停止命令
- 执行
docker stop php-container
- 执行
使用docker ps -a查看容器,发现状态已经是Exited $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c113cc387a6d php:local "docker-php-entrypoi…" 8 minutes ago Exited(137) 56 seconds ago php-container
- 删除容器命令
docker rm php-container
向镜像仓库推送镜像
- 介绍
- 目前构建的镜像只能本机使用,为了在任何机器上都能使用,可以把镜像推送到外部的镜像仓库,如Docker Hub(http://hub.docker.com)
- 使用附加标签标注镜像
- 在Docker Hub注册个账号,把镜像重命名标签
- 重命名镜像标签
docker tag php:local yeedom/php:v1
查看镜像docker images $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE php local 9ed8b05c3334 13 hours ago 64.7MB yeedom/php v1 9ed8b05c3334 13 hours ago 64.7MB
- 两个镜像指向同一个镜像id
- 向Docker Hub推送镜像
推送前先登录 $ docker login 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: yeedom Password: Login Succeeded
推送镜像 国外网络可能不稳定,失败了可以多试几次 $ docker push yeedom/php:v1 The push refers to repository [docker.io/yeedom/php] d281f172ef70: Pushed 8a1be1ed4590: Mounted from library/php b39144a13ab7: Mounted from library/php 060efcf419e8: Mounted from library/php f38edf0edb30: Mounted from library/php 0b0394abec4c: Mounted from library/php 21f626200b4c: Mounted from library/php 414e112bbb2c: Mounted from library/php 3575e617b5f4: Mounted from library/php f1b5933fe4b5: Mounted from library/php v1: digest: sha256:448c8eeca87506bbc6d41190073acdcab094aa0b0bd3c2551c4ced947f422ebb size: 2409
- 在Docker Hub上查看镜像

- 在不同机器上运行镜像
运行命令$ docker run -p 8000:8000 -d yeedom/php:v1 $ docker run -p 8000:8000 -d yeedom/php:v1 Unable to find image 'yeedom/php:v1' locally v1: Pulling from yeedom/php Digest: sha256:448c8eeca87506bbc6d41190073acdcab094aa0b0bd3c2551c4ced947f422ebb Status: Downloaded newer image for yeedom/php:v1 a0d96dc65d6795ba5542754bd3a07e424da672960f545f5cf36e14fa02cac46f
- 这非常简单,最棒的是应用每次都运行在完全一致的环境中
删除镜像docker rmi yeedom/php:v1 $ docker rmi yeedom/php:v1 Untagged: yeedom/php:v1 Untagged: yeedom/php@sha256:448c8eeca87506bbc6d41190073acdcab094aa0b0bd3c2551c4ced947f422ebb
- 删除容器
docker ps -aq | xargs docker rm -f
- 推送完后,镜像就可以给任何人用了,只需要在机器上运行一条命令就能启动一个新容器了
- 模拟一台新电脑
- 在装有docker机器上运行这条命令启动新容器
3. 配置Kubernetes集群
用Minikube运行一个本地单节点Kubernetes集群
- 介绍
- 要在Kubernetes运行应用,首先要设置集群
- 使用Minikube是运行Kubernetes集群最简单、最快捷的方法,它是构建单节点集群的工具,对于测试Kubernetes和本地开发应用都非常有用
- Minikube在VM中通过VirtualBox、KVM或hyperkit来运行Kubernetes,所以启动Kubernetes集群之前,还需要安装VM
- 安装文档:因为minikube国内安装有点特殊,请参考文档
- 使用Minikube启动一个Kubernetes集群
- 运行命令
minikube start
- 运行命令
- 获取集群概览
- 每个节点运行着Docker、kubelet、kube-proxy
- Kubectl:向运行在主节点上的Kubernetes API服务器发出REST请求以与集群交互
- 交互图
- 列出集群节点
命令kubectl get nodes $ kubectl get nodes NAME STATUS ROLES AGE VERSION minikube Ready <none> 43m v1.14.2
- 查看对象更多信息查看对象更多信息查看对象更多信息查看对象更多信息
- 命令
$ kubectl describe node minikube
- 内容比较多,如输出节点的状态、CPU、内存数据、系统信息、运行容器的节点
- 如果不指定节点名称则会打印所有节点信息
为kubectl配置别名和命令行补齐
- 配置别名
- 使命令行补齐
- 使用
kube-shell
,带命令补齐和高亮
- 使用
4. 在Kubernetes上运行第一个应用
介绍
正常来说,部署一个Kubernetes程序需要包含部署的所有组件描述的配置文件,因为第一次使用,所以用最简单的方法运行Kubernetes程序
部署php应用
运行命令 kube-shell> kubectl run php-container --image=yeedom/php:v1 --port=8000 --generator=run/v1 replicationcontroller "php-container" created
- 部署应用最简单的方式是使用
kubectl run
命令,它会创建所有必要的组件而无需json或yaml文件,这样不需要深入了解每个组件对象的结构
- 参数解释
- —image:容器镜像
- —port:端口
- —generator:创建一个ReplicationController
- 介绍pod
- 一个pod是一组紧密相关的容器,运行在同一个工作节点和linux命名空间中
- 每个pod就像一个独立的逻辑机器,有自己的ip、主机名、进程等,运行一个独立的应用程序
- 一个pod的所有容器运行在同一个逻辑机器上,也可以出现在不同的节点上
- 和Docker启动容器不同,Kubernetes不直接处理单个容器,它使用多个共存容器的理念,这组容器叫作pod
- pod
- 关系图
- 列出pod
命令 $ kubectl describe pod php-container-mdkmq Name: php-container-mdkmq Namespace: default Priority: 0 PriorityClassName: <none> Node: minikube/10.0.2.15 Start Time: Thu, 30 May 2019 07:58:10 +0800 Labels: run=php-container Annotations: <none> Status: Running …………………………
- 介绍:不能列出单个容器,因为它们不是独立的Kubernetes对象,但可列出pod
命令
$ kubectl get pods NAME READY STATUS RESTARTS AGE php-container-mdkmq 0/1 ContainerCreating 0 7
- 列出所有pod
- 查看pod详情
- 幕后发生的事情
- ReplicationController对象创建一个新的pod
- 调度器将其调度到一个工作节点上
- kubectl看到pod被调度到节点上,就告知Docker拉取镜像
- 创建容器
- 构建镜像并推送到Docker Hub
- 运行
kubectl
命令时,向Kubernetes API服务器发送http请求在集群中创建一个新的ReplicationController对象
访问web应用
- 介绍:每个pod在集群内有自己的ip,要让pod从外部访问,需要通过服务对象公开它,要创建一个特殊的LoadBalance类型的服务
- 创建一个服务对象
- 命令
$ kubectl expose rc php-container --type=LoadBalancer --name php-service service "php-service" exposed
- rc是ReplicationController缩写
- 列出服务
- 命令
kubectl get service
- 命令
$ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 37d php-service LoadBalancer 10.105.206.79 localhost 8000:32040/TCP 6s
- 使用外部ip访问服务
- 命令
$ curl http://localhost:8000
- 查看服务
- 命令
$ kubectl get service php-service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE php-service LoadBalancer 10.105.206.79 localhost 8000:32040/TCP 3m
系统的逻辑部分
- ReplicationController、pod和服务如何组合在一起
- 如之前所说,Kubernetes没有直接创建和使用容器,它的基本构件是pod
- 但你也没有直接创建pod,而是通过
kubectl run
命令创建了ReplicationController,它用于创建pod实例 - 为了能使pod能从集群外部访问,需要创建一个服务对外暴露
- pod和它的容器
- 在系统中最重要最基本的组件是pod
- 它只包含一个容器,但是通常一个pod可包含任意数量的容器
- pod有自己的私有ip和主机名
- ReplicationController的角色
- 通常,rc用于创建pod多个副本并让它保持运行
- 如果pod有任何原因消失或停止,那么rc将拉起或重新创建新的pod
- 为什么需要服务
- 解决不断变化的pod ip地址,pod可能因为故障而停止,这时会有新pod替换
- 固定的ip和端口对外提供服务
- 服务表示一组或多组提供相同服务的pod,到达服务ip和端口的请求会转发到该服务的一个容器ip和端口
水平伸缩应用
- 增加期望的副本数
- Kubernetes的一个主要好处是可简单地处理部署,我们把运行实例数量增加到三个
- 查看rc
kubectl get rc NAME DESIRED CURRENT READY AGE php-container 1 1 1 52m
- 查看pod
kubectl get pod NAME READY STATUS RESTARTS AGE php-container-xlhzh 1/1 Running 0 53m
- 增加副本数
kubectl scale rc php-container --replicas=3 replicationcontroller "php-container" scaled
- 查看扩容结果 已经由1个变成3个pod了,可以看到给应用扩容非常简单
kubectl get pods NAME READY STATUS RESTARTS AGE php-container-f879k 1/1 Running 0 5s php-container-n2pwj 1/1 Running 0 5s php-container-xlhzh 1/1 Running 0 54m
- Kubernetes原则:不是告诉Kubernetes要执行什么,而是声明系统的期望状态,Kubernetes世界都是这样的
- 当切换到服务时请求切换到所有pod上
- 多次请求服务,会落到不同的pod上
$ curl http://localhost:8000 php-container-n2pwj 2019-05-30 13:23:38 $ curl http://localhost:8000 php-container-xlhzh 2019-05-30 13:23:50 $ curl http://localhost:8000 php-container-f879k 2019-05-30 13:23:54
- 服务会做负载均衡挡在多个pod前面
- 服务请求示例图

查看应用运行在哪个节点上
- 介绍:在Kubernetes世界中,pod运行在哪个节点上不重要,只要它被调度到一个可以提供pod正常运行所需的cpu和内存的节点就可以了
- 列出pod时显示pod IP和pod的节点:会显示pod的ip和运行的节点
kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE php-container-f879k 1/1 Running 0 50m 10.1.1.102 docker-for-desktop php-container-n2pwj 1/1 Running 0 50m 10.1.1.101 docker-for-desktop php-container-xlhzh 1/1 Running 0 1h 10.1.1.100 docker-for-desktop
- 使用kubectl describe查看pod的其它细节
kubectl describe pod php-container-f879k Name: php-container-f879k Namespace: default Node: docker-for-desktop/192.168.65.3 Start Time: Thu, 30 May 2019 21:10:32 +0800 Labels: run=php-container Annotations: <none> Status: Running IP: 10.1.1.102 Controlled By: ReplicationController/php-container
5. 小结
- 拉取、运行镜像
- 把应用打包到容器镜像,并且推送到公开镜像仓库让大家可以使用
- 进入运行中的容器并检查运行环境
- 为kubectl命令行工具设置别名和tab补全
- 在Kubernetes集群中列出查看节点、pod、服务和ReplicationController
- 在Kubernetes中运行容器并可以在集群外部访问
- 了解pod、ReplicationController和服务是关联的基础场景
- 通过改变ReplicationController的副本数对应用进行水平伸缩