使用Helm将ASP.NET Core应用程序部署到Kubernetes容器集群
在《容器化單頁面應用中RESTful API的訪問》以及《容器化單頁面應用中Nginx反向代理與Kubernetes部署》兩篇文章中,我介紹了一套容器化ASP.NET Core應用程序的方案,并對于Nginx反向代理的使用進行了介紹。在《使用Rancher在Microsoft Azure上搭建Kubernetes集群》一文中,我介紹了一種基于Rancher搭建Kubernetes容器集群的方案,大家會發現,使用Rancher來部署和管理Kubernetes容器集群非常方便。今天,我將結合這三篇文章的內容,將案例程序name-list封裝成Helm Chart,然后部署到Kubernetes容器集群中。
要使用Helm來部署我們的案例程序,首先就是要安裝Helm。Helm安裝分兩個步驟,先安裝客戶端,然后安裝它的服務端部件Tiller。Tiller是運行在Kubernetes集群中的,有關Helm和Tiller的基礎知識和基本概念,請參考官方文檔,本文不會做過多介紹。
安裝Helm客戶端非常簡單,官網上提供了多種安裝方式。最簡單的方式就是直接到Helm的Github repo,找到所需的版本下載后解壓,然后將路徑添加到系統的PATH環境變量,即可使用Helm客戶端。之后,我們可以使用下面的命令驗證客戶端是否安裝成功:
1 2 3 | $ helm version Client: &version.Version{SemVer:"v2.14.3", GitCommit:"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085", GitTreeState:"clean"} Error: could not find tiller |
注意:在使用該命令之前,請先確保已經成功部署了Kubernetes,并且~/.kube/config文件中設置了正確的context。有關Kubernetes的部署,請參考《使用Rancher在Microsoft Azure上搭建Kubernetes集群》一文。
上面的命令用于檢查Helm的版本,包括客戶端版本和服務端Tiller的版本。由于我們還沒有安裝服務端,因此,提示“Error: could not find tiller”的錯誤信息。
使用下面的命令創建一個名為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 |
待安裝成功后,再次運行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中有幾個比較關鍵的概念,Helm Chart指的是一套應用程序部署的定義,比如一次Helm部署包含哪些Kubernetes的deployment以及service等等;Helm Release則表示一次應用程序的部署。在之前介紹name-list案例的文章中,我使用了docker-compose來組織各個所需的服務及其之間的依賴關系,并使用docker-compose命令行工具來實現整個應用程序的編譯、容器化以及容器運行等任務?,F在,我們仍然依賴這個docker-compose.yml文件來創建Helm Chart。
首先,到Kubernetes Kompose官方Github repo下載Kompose命令行工具,然后,在docker-compose.yml文件所在的目錄中,運行:
1 | $ kompose convert -c -o helm |
此時,會在docker-compose.yml文件所在目錄中,出現一個helm的子目錄,然后,在該目錄中,會包含Helm Chart相關的目錄和文件。以name-list案例為例:
helm:包含了兩個文件和一個子目錄:templates
Chart.yaml:Helm Chart的定義文件,里面可以指定Helm Chart的名稱、描述等
README.md:說明文件,可以無視
templates目錄:該目錄下包含了應用程序部署的deployment、service以及ingress的描述文件,也是Helm Chart的主要部分
此外,helm目錄下還可以有values.yaml文件,用來定義一些用戶可以修改的變量,例如,可以在values.yaml中,使用“serviceTcpPort: 8080”來指定服務的端口號為8080,然后,在template中使用{{ .Values.serviceTcpPort }}來表示需要從values.yaml中讀取服務的端口號。在name-list案例中,沒有用到values.yaml。
默認情況下,Kompose會將基本的template文件都創建好,然后,還需要根據應用程序的情況進行一些手工調整,比如,將Helm Release的名稱作為每個生成的deployment和service的名稱前綴就是一個不錯的習慣,這就需要手工地對生成的template文件進行調整。比如,在name-list案例中,前端應用的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 }}來表示當前Helm Release的名稱。因此,對于name-list的前端應用而言,在部署到Kubernetes之后,該前端服務的名稱就是{{ .Release.Name }}-namelist-client,雖然在Kubernetes集群中,可以通過這個前綴來區分不同的Helm Release,但對于Nginx來說,它將無法找到這個前端服務,因為目前我們的Nginx配置如下:
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配置中,前端服務的主機地址被寫死成namelist-client了,而在我們的Helm Release中,應該是{{ .Release.Name }}-namelist-client。下面我們來解決這個問題。
解決Nginx中主機名稱的問題,可以使用dockerize工具,它的官方Github repo是:https://github.com/jwilder/dockerize。dockerize可以在容器啟動的時候,實現模板替換,將容器環境變量的值替換到指定模板文件中。比如,對于Nginx的配置而言,我們可以首先定義一個nginx.conf.tmpl的文件,在其中使用一些模板變量,然后使用dockerize,使其在容器啟動時,使用環境變量來替換這些模板變量,于是,容器運行時所使用的nginx.conf文件就是dockerize最終生成的文件。
仍然以name-list為例,首先,定義一個nginx.conf.tmpl文件,內容如下:
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環境變量來替換當前位置的值。然后,修改Nginx所在容器的Dockerfile,內容如下:
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 |
這個Dockerfile的關鍵部分就是最后一行,它會調用dockerize命令,將之前編入容器的/etc/nginx/sites-available/default.tmpl文件替換并輸出為/etc/nginx/sites-available/default文件。
再看看Nginx的deployment.yaml中,是如何設置這個RELEASE_NAME環境變量的:
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 }}的值,被作為環境變量RELEASE_NAME的值,注入到daxnet/namelist-nginx容器中。
在Chart.yaml所在目錄,運行下面的命令,將name-list部署到Kubernetes集群,我們將這次部署命名為myapp:
1 | $ helm upgrade --install myapp . |
運行結果如下:
使用kubectl get pods命令查看所有pod是否正常運行:
使用kubectl exec查看Nginx是否正確配置:
使用helm list命令,查看我們的Helm Releases:
在Microsoft Azure中,找到由Rancher創建的Kubernetes節點虛機,確保80端口已在安全組中打開,然后訪問該虛擬機的IP地址,可以看到,我們的name-list案例已經成功運行在Kubernetes集群中:
本文主要介紹了使用Helm將ASP.NET Core應用程序部署到Kubernetes容器集群的方法,并對其中遇到的問題進行了概括性描述。事實上,本文所使用的name-list案例是一套集前端、后端以及Nginx于一體的完整案例,代碼完全免費開源,地址是:https://github.com/daxnet/name-list。有需要的讀者歡迎查閱。
原文鏈接:https://sunnycoding.cn/2019/10/02/deploying-aspnetcore-apps-to-kubernetes-with-helm/
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?
總結
以上是生活随笔為你收集整理的使用Helm将ASP.NET Core应用程序部署到Kubernetes容器集群的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .Net Core中间件和过滤器实现错误
- 下一篇: asp.net ajax控件工具集 Au