使用Helm将ASP.NET Core应用程序部署到Kubernetes容器集群
在《容器化單頁面應(yīng)用中RESTful API的訪問》以及《容器化單頁面應(yīng)用中Nginx反向代理與Kubernetes部署》兩篇文章中,我介紹了一套容器化ASP.NET Core應(yīng)用程序的方案,并對(duì)于Nginx反向代理的使用進(jìn)行了介紹。在《使用Rancher在Microsoft Azure上搭建Kubernetes集群》一文中,我介紹了一種基于Rancher搭建Kubernetes容器集群的方案,大家會(huì)發(fā)現(xiàn),使用Rancher來部署和管理Kubernetes容器集群非常方便。今天,我將結(jié)合這三篇文章的內(nèi)容,將案例程序name-list封裝成Helm Chart,然后部署到Kubernetes容器集群中。
要使用Helm來部署我們的案例程序,首先就是要安裝Helm。Helm安裝分兩個(gè)步驟,先安裝客戶端,然后安裝它的服務(wù)端部件Tiller。Tiller是運(yùn)行在Kubernetes集群中的,有關(guān)Helm和Tiller的基礎(chǔ)知識(shí)和基本概念,請(qǐng)參考官方文檔,本文不會(huì)做過多介紹。
安裝Helm客戶端非常簡(jiǎn)單,官網(wǎng)上提供了多種安裝方式。最簡(jiǎn)單的方式就是直接到Helm的Github repo,找到所需的版本下載后解壓,然后將路徑添加到系統(tǒng)的PATH環(huán)境變量,即可使用Helm客戶端。之后,我們可以使用下面的命令驗(yàn)證客戶端是否安裝成功:
1 2 3 | $ helm version Client: &version.Version{SemVer:"v2.14.3", GitCommit:"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085", GitTreeState:"clean"} Error: could not find tiller |
注意:在使用該命令之前,請(qǐng)先確保已經(jīng)成功部署了Kubernetes,并且~/.kube/config文件中設(shè)置了正確的context。有關(guān)Kubernetes的部署,請(qǐng)參考《使用Rancher在Microsoft Azure上搭建Kubernetes集群》一文。
上面的命令用于檢查Helm的版本,包括客戶端版本和服務(wù)端Tiller的版本。由于我們還沒有安裝服務(wù)端,因此,提示“Error: could not find tiller”的錯(cuò)誤信息。
使用下面的命令創(chuàng)建一個(gè)名為tiller的Service Account,然后將其賦予cluster-admin的角色,最后使用該Service Account安裝Tiller:
$ kubectl -n kube-system create serviceaccount tiller $ kubectl create clusterrolebinding tiller \ ??--clusterrole=cluster-admin \ ??--serviceaccount=kube-system:tiller $ helm init --service-account tiller |
待安裝成功后,再次運(yùn)行helm version,可以看到類似如下的信息,表示Helm以及Tiller安裝成功。
$ helm version Client: &version.Version{SemVer:"v2.14.3", GitCommit:"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085", GitTreeState:"clean"} Server: &version.Version{SemVer:"v2.14.3", GitCommit:"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085", GitTreeState:"clean"} |
Helm中有幾個(gè)比較關(guān)鍵的概念,Helm Chart指的是一套應(yīng)用程序部署的定義,比如一次Helm部署包含哪些Kubernetes的deployment以及service等等;Helm Release則表示一次應(yīng)用程序的部署。在之前介紹name-list案例的文章中,我使用了docker-compose來組織各個(gè)所需的服務(wù)及其之間的依賴關(guān)系,并使用docker-compose命令行工具來實(shí)現(xiàn)整個(gè)應(yīng)用程序的編譯、容器化以及容器運(yùn)行等任務(wù)。現(xiàn)在,我們?nèi)匀灰蕾囘@個(gè)docker-compose.yml文件來創(chuàng)建Helm Chart。
首先,到Kubernetes Kompose官方Github repo下載Kompose命令行工具,然后,在docker-compose.yml文件所在的目錄中,運(yùn)行:
1 | $ kompose convert -c -o helm |
此時(shí),會(huì)在docker-compose.yml文件所在目錄中,出現(xiàn)一個(gè)helm的子目錄,然后,在該目錄中,會(huì)包含Helm Chart相關(guān)的目錄和文件。以name-list案例為例:
helm:包含了兩個(gè)文件和一個(gè)子目錄:templates
Chart.yaml:Helm Chart的定義文件,里面可以指定Helm Chart的名稱、描述等
README.md:說明文件,可以無視
templates目錄:該目錄下包含了應(yīng)用程序部署的deployment、service以及ingress的描述文件,也是Helm Chart的主要部分
此外,helm目錄下還可以有values.yaml文件,用來定義一些用戶可以修改的變量,例如,可以在values.yaml中,使用“serviceTcpPort: 8080”來指定服務(wù)的端口號(hào)為8080,然后,在template中使用{{ .Values.serviceTcpPort }}來表示需要從values.yaml中讀取服務(wù)的端口號(hào)。在name-list案例中,沒有用到values.yaml。
默認(rèn)情況下,Kompose會(huì)將基本的template文件都創(chuàng)建好,然后,還需要根據(jù)應(yīng)用程序的情況進(jìn)行一些手工調(diào)整,比如,將Helm Release的名稱作為每個(gè)生成的deployment和service的名稱前綴就是一個(gè)不錯(cuò)的習(xí)慣,這就需要手工地對(duì)生成的template文件進(jìn)行調(diào)整。比如,在name-list案例中,前端應(yīng)用的service定義如下:
apiVersion: v1 kind: Service metadata: ??annotations: ????kompose.cmd: kompose convert -o k8s.deployment.yaml ????kompose.version: 1.18.0 (06a2e56) ??creationTimestamp: null ??labels: ????io.kompose.service: {{ .Release.Name }}-namelist-client ??name: {{ .Release.Name }}-namelist-client spec: ??ports: ??- name: "80" ????port: 80 ????targetPort: 80 ??selector: ????io.kompose.service: {{ .Release.Name }}-namelist-client status: ??loadBalancer: {} |
在高亮的幾行,使用{{ .Release.Name }}來表示當(dāng)前Helm Release的名稱。因此,對(duì)于name-list的前端應(yīng)用而言,在部署到Kubernetes之后,該前端服務(wù)的名稱就是{{ .Release.Name }}-namelist-client,雖然在Kubernetes集群中,可以通過這個(gè)前綴來區(qū)分不同的Helm Release,但對(duì)于Nginx來說,它將無法找到這個(gè)前端服務(wù),因?yàn)槟壳拔覀兊腘ginx配置如下:
events { ????worker_connections 1024; } http { ????server { ??????listen??????? 80; ??????server_name?? localhost; ??????resolver 127.0.0.11 ipv6=off; ??????include? /etc/nginx/mime.types; ??????location / { ??????} ??????location ~ ^/name-service/(.*)$ { ???????rewrite ^ $request_uri; ???????rewrite ^/name-service/(.*)$ $1 break; ???????return 400; ??????} ????} ????upstream namelistsvc { ?????server namelist-service:5000; ????} ????upstream namelistcli { ?????server namelist-client:80; ????} } |
可以看到,在Nginx配置中,前端服務(wù)的主機(jī)地址被寫死成namelist-client了,而在我們的Helm Release中,應(yīng)該是{{ .Release.Name }}-namelist-client。下面我們來解決這個(gè)問題。
解決Nginx中主機(jī)名稱的問題,可以使用dockerize工具,它的官方Github repo是:https://github.com/jwilder/dockerize。dockerize可以在容器啟動(dòng)的時(shí)候,實(shí)現(xiàn)模板替換,將容器環(huán)境變量的值替換到指定模板文件中。比如,對(duì)于Nginx的配置而言,我們可以首先定義一個(gè)nginx.conf.tmpl的文件,在其中使用一些模板變量,然后使用dockerize,使其在容器啟動(dòng)時(shí),使用環(huán)境變量來替換這些模板變量,于是,容器運(yùn)行時(shí)所使用的nginx.conf文件就是dockerize最終生成的文件。
仍然以name-list為例,首先,定義一個(gè)nginx.conf.tmpl文件,內(nèi)容如下:
server { ??listen??????? 80; ??server_name?? localhost; ??include? /etc/nginx/mime.types; ??location / { ??} ??location ~ ^/name-service/(.*)$ { ????rewrite ^ $request_uri; ????rewrite ^/name-service/(.*)$ $1 break; ????return 400; ??} } upstream namelistsvc { ??server {{ .Env.RELEASE_NAME }}-namelist-service:5000; } upstream namelistcli { ??server {{ .Env.RELEASE_NAME }}-namelist-client:80; } |
其中{{ .Env.RELEASE_NAME }}表示使用RELEASE_NAME環(huán)境變量來替換當(dāng)前位置的值。然后,修改Nginx所在容器的Dockerfile,內(nèi)容如下:
FROM ubuntu:14.04 ENV DOCKERIZE_VERSION=v0.6.1 # Install Nginx. # RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C300EE8C RUN apt-get update RUN apt-get install -y wget nginx RUN echo "daemon off;" >> /etc/nginx/nginx.conf RUN tar -C /usr/local/bin -xvzf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz ADD nginx.conf.tmpl /etc/nginx/sites-available/default.tmpl EXPOSE 80 CMD dockerize -template /etc/nginx/sites-available/default.tmpl:/etc/nginx/sites-available/default -stdout /var/log/nginx/access.log -stderr /var/log/nginx/error.log nginx |
這個(gè)Dockerfile的關(guān)鍵部分就是最后一行,它會(huì)調(diào)用dockerize命令,將之前編入容器的/etc/nginx/sites-available/default.tmpl文件替換并輸出為/etc/nginx/sites-available/default文件。
再看看Nginx的deployment.yaml中,是如何設(shè)置這個(gè)RELEASE_NAME環(huán)境變量的:
apiVersion: extensions/v1beta1 kind: Deployment metadata: ??annotations: ????kompose.cmd: kompose convert -c -o helm ????kompose.version: 1.18.0 (06a2e56) ??creationTimestamp: null ??labels: ????io.kompose.service: {{ .Release.Name }}-namelist-nginx ??name: {{ .Release.Name }}-namelist-nginx spec: ??replicas: 1 ??strategy: {} ??template: ????metadata: ??????creationTimestamp: null ??????labels: ????????io.kompose.service: {{ .Release.Name }}-namelist-nginx ????spec: ??????containers: ??????- image: daxnet/namelist-nginx ????????name: namelist-nginx ????????env: ????????- name: RELEASE_NAME ??????????value: {{ .Release.Name | quote }} ????????ports: ????????- containerPort: 80 ????????resources: {} ??????restartPolicy: Always status: {} |
由此可見,Helm Release的名稱,也就是{{ .Release.Name }}的值,被作為環(huán)境變量RELEASE_NAME的值,注入到daxnet/namelist-nginx容器中。
在Chart.yaml所在目錄,運(yùn)行下面的命令,將name-list部署到Kubernetes集群,我們將這次部署命名為myapp:
1 | $ helm upgrade --install myapp . |
運(yùn)行結(jié)果如下:
使用kubectl get pods命令查看所有pod是否正常運(yùn)行:
使用kubectl exec查看Nginx是否正確配置:
使用helm list命令,查看我們的Helm Releases:
在Microsoft Azure中,找到由Rancher創(chuàng)建的Kubernetes節(jié)點(diǎn)虛機(jī),確保80端口已在安全組中打開,然后訪問該虛擬機(jī)的IP地址,可以看到,我們的name-list案例已經(jīng)成功運(yùn)行在Kubernetes集群中:
本文主要介紹了使用Helm將ASP.NET Core應(yīng)用程序部署到Kubernetes容器集群的方法,并對(duì)其中遇到的問題進(jìn)行了概括性描述。事實(shí)上,本文所使用的name-list案例是一套集前端、后端以及Nginx于一體的完整案例,代碼完全免費(fèi)開源,地址是:https://github.com/daxnet/name-list。有需要的讀者歡迎查閱。
原文鏈接:https://sunnycoding.cn/2019/10/02/deploying-aspnetcore-apps-to-kubernetes-with-helm/
.NET社區(qū)新聞,深度好文,歡迎訪問公眾號(hào)文章匯總?http://www.csharpkit.com?
總結(jié)
以上是生活随笔為你收集整理的使用Helm将ASP.NET Core应用程序部署到Kubernetes容器集群的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .Net Core中间件和过滤器实现错误
- 下一篇: 微软如何利用机器学习改进Win 10更新