K8s上的應用是通過docker來布屬的,所以要將應用發佈到 K8s,前題是將應用製作成docker鏡像,並上傳到倉庫。
環境說明
- 開發環境:Mac、VSCode
- 布屬應用:.Net Core 與 Go(基於gin框架:github.com/gin-gonic/gin)開發的webapi應用
- 服務器環境:點些查看《搭建的K8s集羣》
# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-0001 Ready master 10d v1.17.2 192.168.1.26 <none> CentOS Linux 7 (Core) 3.10.0-1062.1.1.el7.x86_64 docker://1.13.1
k8s-0002 Ready <none> 10d v1.17.2 192.168.1.100 <none> CentOS Linux 7 (Core) 3.10.0-1062.1.1.el7.x86_64 docker://1.13.1
k8s-0003 Ready <none> 10d v1.17.2 192.168.1.85 <none> CentOS Linux 7 (Core) 3.10.0-1062.1.1.el7.x86_64 docker://1.13.1
k8s-0004 Ready <none> 10d v1.17.2 192.168.1.117 <none> CentOS Linux 7 (Core) 3.10.0-1062.1.1.el7.x86_64 docker://1.13.1
- 環境準備
- 點此查看《安裝docker》
- VS Code 添加Docker擴展
- 創建測試項目:test.webapi
項目爲最簡單的webapi項目,該示例中監聽了端口:5289
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseUrls("http://0.0.0.0:5289")
.UseStartup<Startup>();
- 製做、上傳鏡像並布屬到k8s
- 打開VS Code,同時按下 Command+ Shift+P;
- 點擊“>Docker:AddDocker Files to Workspace...”
- 選擇“ASP Net Core”,將自動創建DockerFile.內容如下:
注:如果是Go的應用通過自動創建的DockerFile,在運行時會報錯權限不足的錯誤,需重新編寫。點此查看《DockerFile詳解》
FROM mcr.microsoft.com/dotnet/core/aspnet:2.1 AS base
WORKDIR /app
EXPOSE 5289
FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
WORKDIR /src
COPY ["test.webapi.csproj", "./"]
RUN dotnet restore "./test.webapi.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "test.webapi.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "test.webapi.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "test.webapi.dll"]
從桌面上打開Docker(註冊帳號並登錄,所創建的倉庫將上傳至自已的帳號),運行後在右上角會有一個docker的icon
回到VS Code打開控制檯,通過docker build打包項目
$ docker build -t fengyily/webapi:v1 .
Sending build context to Docker daemon 32.26kB
Step 1/16 : FROM mcr.microsoft.com/dotnet/core/aspnet:2.1 AS base
---> d27433e73f8b
Step 2/16 : WORKDIR /app
---> Using cache
---> d21b3b3634cf
Step 3/16 : EXPOSE 5289
---> Using cache
---> 86f2dc533d61
Step 4/16 : FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build
---> be2b589b3992
Step 5/16 : WORKDIR /src
---> Using cache
---> 4d1c285b0dd2
Step 6/16 : COPY ["test.webapi.csproj", "./"]
---> Using cache
---> bb804e07f792
Step 7/16 : RUN dotnet restore "./test.webapi.csproj"
---> Using cache
---> 8bb314beb77e
Step 8/16 : COPY . .
---> 38bc90ce9a67
Step 9/16 : WORKDIR "/src/."
---> Running in 33e3305ccfc4
Removing intermediate container 33e3305ccfc4
---> f510bc0df788
Step 10/16 : RUN dotnet build "test.webapi.csproj" -c Release -o /app/build
---> Running in 51773af168d9
Microsoft (R) Build Engine version 16.2.37902+b5aaefc9f for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 597.56 ms for /src/test.webapi.csproj.
test.webapi -> /app/build/test.webapi.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:05.52
Removing intermediate container 51773af168d9
---> 8f77e942d5e2
Step 11/16 : FROM build AS publish
---> 8f77e942d5e2
Step 12/16 : RUN dotnet publish "test.webapi.csproj" -c Release -o /app/publish
---> Running in e7750061499d
Microsoft (R) Build Engine version 16.2.37902+b5aaefc9f for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Restore completed in 55.3 ms for /src/test.webapi.csproj.
test.webapi -> /src/bin/Release/netcoreapp2.1/test.webapi.dll
test.webapi -> /app/publish/
Removing intermediate container e7750061499d
---> fcbc2fac2f29
Step 13/16 : FROM base AS final
---> 86f2dc533d61
Step 14/16 : WORKDIR /app
---> Using cache
---> 477f20371de1
Step 15/16 : COPY --from=publish /app/publish .
---> Using cache
---> 9a9914a4a6e6
Step 16/16 : ENTRYPOINT ["dotnet", "test.webapi.dll"]
---> Using cache
---> 2e2ac48eb92e
Successfully built 2e2ac48eb92e
Successfully tagged fengyily/webapi:v1
通過後可運行docker run fengyily/findo.api:v1來測試鏡像是否可用
測試通過後,將鏡像上傳至倉庫
$ docker push fengyily/webapi:v1
The push refers to repository [docker.io/fengyily/webapi]
7ff696383b4c: Layer already exists
4854b085f893: Layer already exists
977691ddf529: Layer already exists
217f610b769d: Mounted from fengyily/findo
f19669fee5cc: Layer already exists
e0db3ba0aaea: Layer already exists
v1: digest: sha256:068ba4453ff88d8d7bb81940536405efbb1afb2fd1dd029325bba275e6e166d1 size: 1580
上傳成功,接下來我們就編寫webapi的yaml,布屬至K8s集羣中。
在k8s上創建develop名稱空間
kubectl create ns develop
testwebapi.yaml
# cat testwebapi.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
webapi: testwebapi
name: testapi
namespace: develop
spec:
replicas: 2
selector:
matchLabels:
webapi: testwebapi
template:
metadata:
labels:
webapi: testwebapi
spec:
containers:
- name: testwebapi
image: fengyily/webapi:v1
imagePullPolicy: Always
ports:
- containerPort: 5289
livenessProbe:
httpGet:
path: /api/values
port: 5289
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 5
periodSeconds: 5
testwebapi-svc.yaml
# cat testwebapi-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: testapi
namespace: develop
spec:
ports:
- protocol: TCP
port: 8889
targetPort: 5289
name: web
selector:
webapi: testwebapi
通過kubectl apply -f . 一次性創建好
# kubectl apply -f .
service/testapi
deployment.apps/testapi
檢查一下,基中testapi開頭的是通過剛纔制到的鏡像布屬的,go開頭的是go開發的應用,下一節將講解如何訪問k8s中的應用。
[root@k8s-0001 myapp]# kubectl get pods -n develop -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testapi-85664b498-vl749 1/1 Running 0 8d 10.244.2.13 k8s-0002 <none> <none>
testapi-85664b498-zxqml 1/1 Running 5 9d 10.244.3.13 k8s-0004 <none> <none>
go-findo-api-55c8ddd98d-cb2h8 1/1 Running 0 13h 10.244.2.26 k8s-0002 <none> <none>
go-findo-api-55c8ddd98d-pmpd8 1/1 Running 0 13h 10.244.1.43 k8s-0003 <none> <none>
go-findo-api-55c8ddd98d-vmgvz 1/1 Running 0 12h 10.244.3.22 k8s-0004 <none> <none>
[root@k8s-0001 myapp]# kubectl get svc -n develop -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
default-http-backend ClusterIP 10.102.58.113 <none> 80/TCP 6d9h app.kubernetes.io/name=default-http-backend,app.kubernetes.io/part-of=ingress-nginx
findo-svc ClusterIP 10.102.47.86 <none> 6660/TCP 6d9h webapi=findo-api
go-findo-svc ClusterIP 10.108.8.131 <none> 8080/TCP 28h webapi=go-findo-api
nginx-ingress-controller NodePort 10.101.0.193 <none> 80:30033/TCP,443:30241/TCP 6d9h app.kubernetes.io/name=ingress-nginx
testapi ClusterIP 10.108.208.200 <none> 8889/TCP 6d8h webapi=testwebapi
Go 應用對應的DockerFile,示例一:簡單實現,是將源碼COPY至鏡像,然後編譯運行,這樣的鏡像會非常大,實際上我們只需將編譯結果複製。
FROM golang:latest
ENV GO111MODULE on
WORKDIR /app
USER root
COPY . .
RUN go build main.go
CMD ["./main"]
示例二:已做優化
#build stage
FROM golang:alpine AS builder
WORKDIR /go/src/app
COPY . .
RUN go clean -cache
ENV GO111MODULE on
ENV GIN_MODE release
RUN go build -o /go/bin/app /go/src/app/main.go
#final stage
FROM alpine:latest
USER root
RUN apk add ca-certificates
COPY --from=builder /go/bin/app /app
COPY ./conf/app.ini.bak ./conf/app.ini
COPY ./views ./views
ENTRYPOINT ./app
LABEL Name=go.findo.api Version=0.0.1
EXPOSE 8080
優化主要爲以下兩點:
1、基礎系統採用alpine版本,只有 5M左右,golang:latest有 200M左右
2、編譯後,將編譯的文件、配置、視圖拷貝至鏡像(源代碼+框架+引用源碼:61M)
我們來看一下優化前後鏡像的大小,以及基礎OS的大小
以下是同一go應用採用不同基礎系統鏡大小的差異,可以看出基於alpine版的鏡像只有 23M,golang的alpine版 483M,latest版 927M
fengyideMacBook-Pro:~ fengyi$ docker images
REPOSITORY TAG OS IMAGE ID CREATED SIZE
fengyily/go.findo.api auto FROM alpine:latest 0d43ec73a46a 32 seconds ago 23.1MB
fengyily/go.findo.api alpine FROM golang:alpine 9aa dd792eef2 7 hours ago 483MB
fengyily/go.findo.api v1 FROM golang:latest 3a3508748fe2 8 hours ago 927MB
以下是net core應用鏡像大小(未優化)
REPOSITORY TAG IMAGE ID CREATED SIZE
fengyily/findo v1 099e276b2b89 10 days ago 264MB
fengyily/webapi v1 2e2ac48eb92e 10 days ago 254MB
f1api/test.webapi v1 bd056e05c29e 12 days ago 254MB
mcr.microsoft.com/dotnet/core/sdk 2.1 be2b589b3992 12 days ago 1.74GB
mcr.microsoft.com/dotnet/core/aspnet 2.1 d27433e73f8b 12 days ago 253MB
以下是各系統的基礎鏡像大小
REPOSITORY TAG IMAGE ID CREATED SIZE
debian latest a8797652cfd9 12 days ago 114MB
golang alpine 87eefb76f0a8 2 weeks ago 359MB
golang latest 6586e3d10e96 11 days ago 803MB
alpine latest e7d92cdc71fe 3 weeks ago 5.59MB
可以看出,採用alpine系統以及只差編譯後文件打包後,最終鏡像的大小爲 23.1M。