共計 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 行業資訊頻道,感謝各位的閱讀!