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。