共计 9268 个字符,预计需要花费 24 分钟才能阅读完成。
如何进行 minikube 部署本地镜像实践与 k8s 网络架构分析,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
在 windows10 的 WSL2 里用 minikube 搭建了一个 k8s 环境,但是每次拉取镜像都很慢甚至失败(原因大家都懂的), 本文的方案的原理是, 先将镜像在宿主机上 f - q 拉取到本地, 再由 minikube 虚机中的 k8s 来拉取本地镜像, 这样即可解决之前的拉取镜像失败,也可提升拉取镜像的速度;
两种方案
把私有镜像仓库 docker registry 搭建在宿主机上, k8s 从本地宿主机上拉取镜像;
把本地镜像推送到 minikube 的私有镜像仓库上, k8s 从 minikube 的私有镜像仓库(k8s 的本地私有镜像仓库) 拉取镜像;
1、构建一个镜像
一个完整镜像通常包含应用本身和操作系统,当然还包含需要的依赖软件。
首先准备一个应用。新建一个本文文件,起名叫 app.py,写入下面的内容,实现一个简单的 web 应用:
from flask import Flask
import socket
import os
app = Flask(__name__)
html = h4 Hello {name}! /h4 \
b 主机名: /b {hostname} br/
return html.format(name=os.getenv( NAME , world), hostname=socket.gethostname())
if __name__ == __main__ :
app.run(host= 0.0.0.0 , port=8082)
在这段代码中,使用 Flask 框架启动了一个 Web 服务器,而它唯一的功能是:如果当前环境中有“NAME”这个环境变量,就把它打印在“Hello”后,否则就打印“Hello world”,最后再打印出当前环境的 hostname。
这个应用的依赖文件 requirements.txt 存在于与 app.py 同级目录中,内容是:
$ cat requirements.txt
Flask
将这样一个应用在容器中跑起来,需要制作一个容器镜像。Docker 使用 Dockerfile 文件来描述镜像的构建过程。在本文中,Dockerfile 内容定义如下:
# FROM 指令指定了基础镜像是 python:3.6-alpine,这个基础镜像包含了 Alpine Linux 操作系统和 python3.6
FROM python:3.6-alpine
# WORKDIR 指令将工作目录切换为 /app
WORKDIR /app
# ADD 指令将当前目录下的所有内容(app.py、requirements.txt)复制到镜像的 /app 目录下
ADD . /app
# RUN 指令运行 pip 命令安装依赖
RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
# EXPOSE 指令暴露允许被外界访问的 8082 端口
EXPOSE 8082
# ENV 指令设置环境变量 NAME
ENV NAME World
# CMD 指令设置容器内进程为:python app.py,即:这个 Python 应用的启动命令
CMD [python , app.py]
这个 Dockerfile 中用到了很多指令,把包括 FROM、WORKDIR、ADD、RUN、EXPOSE、ENV 和 CMD。指令的具体含义已经以注释的方式写在了 Dockerfile 中,大家可以查看。通常我们构建镜像时都会依赖一个基础镜像,基础镜像中包含了一些基础信息,我们依赖基础构建出来的新镜像将包含基础镜像中的内容。
需要再详细介绍一下 CMD 指令。CMD 指定了 python app.py 为这个容器启动后执行的进程。CMD [“python”,“app.py”] 等价于在容器中执行“python app.py”。
另外,在使用 Dockerfile 时,还有一种 ENTRYPOINT 指令。它和 CMD 都是 Docker 容器进程启动所必需的参数,完整执行格式是:“ENTRYPOINT CMD”。
默认情况下,Docker 会为你提供一个隐含的 ENTRYPOINT,即:/bin/sh -c。所以,在不指定 ENTRYPOINT 时,比如在我们这个例子里,实际上运行在容器里的完整进程是:/bin/sh -c“python app.py”,即 CMD 的内容就是 ENTRYPOINT 的参数。正是基于这样的原理,Docker 容器的启动进程为实际为 ENTRYPOINT,而不是 CMD。
需要注意的是,Dockerfile 里的指令并不都是只在容器内部的操作。就比如 ADD,它指的是把当前目录(即 Dockerfile 所在的目录)里的文件,复制到指定容器内的目录当中。
更多能在 Dockerfile 中使用的指令,可以参考官方文档:
https://docs.docker.com/engine/reference/builder/#dockerfile-reference。
根据前面的描述,现在我们的整个应用的目录结构应该如下这样:
$ ls
Dockerfile app.py requirements.txt
执行下面的指令可以构建镜像:
$ docker build -f /path/to/Dockerfile -t helloworld .
Sending build context to Docker daemon 4.608kB
Step 1/7 : FROM python:3.6-alpine
--- 5e7f84829665
Step 2/7 : WORKDIR /app
--- Using cache
--- dbb4a00a8f68
Step 3/7 : ADD . /app
--- fd33ac91c6c7
Step 4/7 : RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
--- Running in 6b82e863d802
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting Flask
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/f2/28/2a03252dfb9ebf377f40fba6a7841b47083260bf8bd8e737b0c6952df83f/Flask-1.1.2-py2.py3-none-any.whl (94 kB)
Collecting click =5.1
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/dd/c0/4d8f43a9b16e289f36478422031b8a63b54b6ac3b1ba605d602f10dd54d6/click-7.1.1-py2.py3-none-any.whl (82 kB)
Collecting Jinja2 =2.10.1
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/27/24/4f35961e5c669e96f6559760042a55b9bcfcdb82b9bdb3c8753dbe042e35/Jinja2-2.11.1-py2.py3-none-any.whl (126 kB)
Collecting itsdangerous =0.24
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl (16 kB)
Collecting Werkzeug =0.15
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/cc/94/5f7079a0e00bd6863ef8f1da638721e9da21e5bacee597595b318f71d62e/Werkzeug-1.0.1-py2.py3-none-any.whl (298 kB)
Collecting MarkupSafe =0.23
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz (19 kB)
Building wheels for collected packages: MarkupSafe
Building wheel for MarkupSafe (setup.py): started
Building wheel for MarkupSafe (setup.py): finished with status done
Created wheel for MarkupSafe: filename=MarkupSafe-1.1.1-py3-none-any.whl size=12629 sha256=1f965945354a52423078c573deb1a8116965e67b2467c3640264d7f02058b06d
Stored in directory: /root/.cache/pip/wheels/06/e7/1e/6e3a2c1ef63240ab6ae2761b5c012b5a4d38e448725566eb3d
Successfully built MarkupSafe
Installing collected packages: click, MarkupSafe, Jinja2, itsdangerous, Werkzeug, Flask
Successfully installed Flask-1.1.2 Jinja2-2.11.1 MarkupSafe-1.1.1 Werkzeug-1.0.1 click-7.1.1 itsdangerous-1.1.0
Removing intermediate container 6b82e863d802
--- d672a00c1a2f
Step 5/7 : EXPOSE 8083
--- Running in b9b2338da3f3
Removing intermediate container b9b2338da3f3
--- e91da5a22e20
Step 6/7 : ENV NAME World
--- Running in d7e5d19f3eed
Removing intermediate container d7e5d19f3eed
--- 4f959f34d486
Step 7/7 : CMD [python , app.py]
--- Running in 99a97bedace0
Removing intermediate container 99a97bedace0
--- 3bc3e537ebb7
Successfully built 3bc3e537ebb7
Successfully tagged helloworld:latest
其中,-t 的作用是给这个镜像加一个 Tag,即:起一个好听的名字。docker build 会自动加载当前目录下的 Dockerfile 文件,然后按照顺序执行 Dockerfile 文件中的指令。
上面的命令执行完成后,就生成了一个镜像。可以通过下面的指令查看:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
helloworld latest 3bc3e537ebb7 2 minutes ago 103MB
还可以通过 docker inspect helloworld:latest 查看镜像的元信息。
元信息中包含了镜像的全部信息,包括镜像的 tag,构建时间,环境变量等。
如果镜像不再需要了,可以通过 docker image rm 删除镜像。
有了镜像,就可以通过下面的指令来运行镜像得到容器了。
$ docker run -p 8082:8082 helloworld
* Serving Flask app app (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:8082/ (Press CTRL+C to quit)
上面命令中,镜像名 helloworld 后面,什么都不用写,因为在 Dockerfile 中已经指定了 CMD。否则,我就得把进程的启动命令加在后面:
$ docker run -p 8082:8082 helloworld python app.py
从现在看,容器已经正确启动,我们使用 curl 命令通过宿主机的 IP 和端口号,来访问容器中的 web 应用。
$ curl http://0.0.0.0:8082/
h4 Hello World! /h4 b 主机名: /b 59b607239c3a br/
从输出中可以看到容器的 ID,容器是基于哪个镜像的启动的,容器中的进程,容器的启动时间及端口映射情况,以及容器的名字。
使用 docker inspect 59b607239c3a 命令,可以查看容器的元数据,内容非常丰富。
2、k8s 从宿主机私有镜像仓库中拉取镜像
2.1 搭建宿主机私有镜像仓库;
sudo docker pull registry
sudo docker run -d -p 5000:5000 -v $(pwd):/var/lib/registry --restart always --name registry registry:2
2.2 验证私有镜像仓库
curl http://127.0.0.1:5000/v2/_catalog
2.3 推送镜像到私有镜像仓库
重启 docker 服务使配置生效: systemctl restart docker
172.19.242.79 是宿主机 ip
mac 配置路径: 工具栏– docker – Preferences – Daemon – basic – Insecure registries 加上一行: 172.19.242.79:5000
sudo vim /etc/docker/daemon.json
insecure-registries : [
172.19.242.79:5000
],
debug : true
2.4 推送到镜像仓库
sudo docker tag helloworld 172.19.242.79:5000/flaskhelloworld # 标记本地镜像,将其归入某一仓库
sudo docker push 172.19.242.79:5000/flaskhelloworld # 最开始 docker build -t 直接打入指定(远程)仓库可省去上一步
curl -X GET 172.19.242.79:5000/v2/_catalog
2.5 配置 minikube 并重启
为了使得 minikube 虚机中的 k8s 能拉取到宿主机私有镜像 需要上述两项配置 –registry-mirror 和 –insecure-registry
sudo minikube start --driver=none --insecure-registry= 172.19.242.79:5000 --image-mirror-country cn \
--iso-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/iso/minikube-v1.15.1.iso \
--registry-mirror=https:// 你注册的 aliyun 加速地址.mirror.aliyuncs.com
2.6 部署镜像与开放服务接口
sudo kubectl create deployment flaskhelloworld --image=172.19.242.79:5000/flaskhelloworld
sudo kubectl expose deployment flaskhelloworld --type=LoadBalancer --port=6789 --target-port=8082
sudo minikube service flaskhelloworld --url
#sudo kubectl port-forward flaskhelloworld-6b7695c6df-5hclc 8888:8082
#sudo kubectl create -f flaskhelloworld.yaml
#sudo kubectl describe pod flaskhelloworld-pod
#sudo kubectl port-forward flaskhelloworld-pod 8888:8080
#sudo kubectl run flaskhelloworld --image=172.19.242.79:5000/flaskhelloworld --port 3000
#sudo kubectl expose pod flaskhelloworld --name flaskhelloworld-http --type=LoadBalancer
#sudo minikube service flaskhelloworld-http
3、k8s 中 pod、service、ingress 网络架构分析
sudo minikube service flaskhelloworld
|-----------|-----------------|-------------|----------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|-----------------|-------------|----------------------------|
| default | flaskhelloworld | 6789 | http://172.19.242.79:30792 |
|-----------|-----------------|-------------|----------------------------|
???? 正通过默认浏览器打开服务 default/flaskhelloworld...
???? http://172.19.242.79:30792
每次访问的主机信息不一样,是因为我们 deployments 后,执行了
kubectl scale -n default deployment flaskhelloworld –replicas=3
集群有 3 个 pod
在 Kubernetes 中,部署一个 Deployment 是无法对外进行访问的,就是别的应用程序要想访问部署的 Deployment,找不到该怎么去访问,为什么这么讲,因为 Deployment 一般都是多副本部署的,有可能会分布在不同的节点之上,而且重建 Pod IP 也会变,重新发布一下也会改变,所以没有办法去固定去访问哪个 Pod,即使固定了,其他的 Pod 也访问不了,要想做到多个 Pod 都去提供服务,前面就必须要加一个负载均衡,提供一个访问入口,只有访问这个统一入口,才能转发到后端多个 Pod 上,只要访问这个 Cluster IP 就能转发到后端的 Pod 上。
Service
Service 定义了 Pod 的逻辑集合和访问这个集合的策略
Service 的引入为解决 Pod 的动态变化,提供了服务发现和负载均衡
使用 CoreDNS 解析 Service 名称
sudo kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
flaskhelloworld LoadBalancer 10.98.106.240 pending 6789:30792/TCP 31m
flink-jobmanager ClusterIP 10.100.169.253 none 6123/TCP,6124/TCP,8081/TCP 7d12h
flink-jobmanager-rest NodePort 10.103.71.37 none 8081:30424/TCP 7d12h
kubernetes ClusterIP 10.96.0.1 none 443/TCP 7d12h
因此,我们也可以用集群 IP 来访问:
services 暴露出去之后,也就是需要让用户去访问,比如搭建一个电商网站,让用户去访问,Ingress 相对于 Service,是一个互补的状态,Service 主要提供了集群内部的访问,也可以暴露一个 TCP/UDP 的端口,而 Ingress 主要是一个 7 层的转发,也就是提供一个统一的入口,只要访问 Ingress Controller,就能帮你转发你部署所有的项目,也就是所有的项目都使用域名去访问。
为了进一步了解网络架构,我们也可以直接用 pod ip + port 来访问
sudo kubectl get endpoints serviceName # 查看 Service backend pods(在 Kubernetes 对应多个 endpoint)
看完上述内容,你们掌握如何进行 minikube 部署本地镜像实践与 k8s 网络架构分析的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注丸趣 TV 行业资讯频道,感谢各位的阅读!