這個文章的題目起的比較長,我想實現這樣一個產品:
前端是微信小程序,后端是基於docker運行的asp.net core webapi。webapi通過nginx實現的反向代理接入,nginx同樣基於docker來運行。開擼!
准備工作
1、微信小程序注冊賬號
微信小程序注冊有一個限制,一個郵箱只能注冊一個小程序。我用的qq郵箱,因為之前使用qq郵箱的默認賬號注冊過一個,但是qq郵箱可以針對同一個郵箱設置不同的賬戶(好像是三個),所以可以先設置一個額外的賬號,然后通過這個賬號來注冊小程序
2、購買服務器
你要建立api必須得有一台服務器,我從騰訊雲申請了一台免費的(3個月)2核心4G的服務器。
3、申請域名
申請域名是有一個原因的,你可以參考看看是不是否和你的條件,否則,你可以跳過這步。我的原因是首先小程序如果要接入api這個api必須是一個https的連接,https的連接涉及到ssl/tls證書,我采用let's encrypt來獲取證書。但是let's encrypt不允許使用ip地址來獲取證書,需要域名。所以,我又申請了一個域名。域名申請在騰訊雲官網完成,花了45(原價55,10塊的代金券)塊錢申請了一個域名,有效期一年。但是需要注意你申請域名的時候必須得先有一台服務器。服務器用於一個公網ip。
4、申請ssl/tls證書
當你完成購買服務器和申請域名的步驟后,可以開始申請證書了。我的證書是通過let's encrypt來申請的,let's encrypt發布了一個certbot的工具,通過certbot來對證書進行獲取和管理。certbot的網址是https://certbot.eff.org/
當然還有很多其他的工具,比如certbot-auto和acme.sh等,這里不做過多介紹,百度吧。
首先是安裝certbot,我是用的是centos7,certbot是包含在epel(extra packges for enterprise linux)中的,所以需要安裝epel包源,另外,需要python的運行環境。在centos7中,這些默認都有,所以,我直接運行
sudo yum install certbot
就完成了安裝。
關於epel的官網在這里:https://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F 你可以對照你的操作系統來查看如何安裝這個包源。
關於certbot的官網在這里:https://certbot.eff.org/lets-encrypt/centosrhel7-nginx 你可以查看相關文檔。
安裝好之后就是獲取證書的環節了:
certbot certonly
鍵入上述命令后,根據提示操作,很快就會完成,然后在/etc/letsencrypt/live/you.domain.com
下面會有你的證書和證書對應的key證書和key都是pem文件,證書的名字是fullchain.pen,key的名字是privkey.pem.
5、安裝docker
docker:https://docs.docker.com/install/
windows開發機上的docker安裝過程省略。下面是服務器(centos7.2)的安裝步驟
centos安裝docker的條件是系統在7以上,並且開通了centos-extras包源,它默認是開啟的。從https://wiki.centos.org/AdditionalResources/Repositories上面可以查看centos關於這方面的一些信息。確認完上述信息后,按照一下步驟來完成安裝:
sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2
yum-utils
提供了 yum-config-manager
這個工具,而device-mapper-persistent-data 和lvm2對於devicemapper
存儲驅動程序來說是必須的。然后配置包源:
sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
然后安裝docker:
sudo yum install docker-ce docker-ce-cli containerd.io
安裝文檔在這里:https://docs.docker.com/install/linux/docker-ce/centos/ 不細說了。
6、安裝nginx
我的nginx是通過docker來安裝的。
docker pull nginx
上述命令會安裝最新版的nginx docker鏡像。
關於nginx的docker鏡像可以參考我翻譯的一篇文章。
nginx的主配置文件是/etc/nginx/nginx.conf,子配置文件在/etc/nginx/conf.d/default.conf主配置文件會使用include指令來調用子配置文件。
當使用nginx鏡像時,我們在宿主系統上寫好配置,然后通過將容器的目錄掛載到宿主的相應目錄上即可。
7、創建asp.net core api應用並制作鏡像
我創建了一個asp.net core 的webapi,這個web服務監聽了5000端口:
它的本地目錄是這樣的:
Source目錄是這樣:
列出目錄的主要目的是要書寫Dockerfile。Dockerfile里面有對路徑的操作。Source目錄里面放的就是api的工程文件了。Source目錄的上一級放了Dockerfile文件,Dockerfile文件內容如下:
FROM microsoft/dotnet:2.2-sdk-alpine AS dotnetcore-sdk #以這個鏡像為基礎鏡像 WORKDIR /source #定義工作路徑,下面的命令都是以這個工作路徑為基礎 #復制工程文件 COPY Source/Widget.Application/Widget.Application.csproj ./Widget.Application/ #COPY命令有兩個參數,第一個參數是宿主的目錄,第二個參數是鏡像上面的目錄,如果是相對路徑,那都是相對於WORKDIR的 COPY Source/Widgets.Core/Widgets.Core.csproj ./Widgets.Core/ COPY Source/Widgets.Domain/Widgets.Domain.csproj ./Widgets.Domain/ COPY Source/Widgets.Infrastructure/Widgets.Infrastructure.csproj ./Widgets.Infrastructure/ COPY Source/Widgets.UI/Widgets.UI.csproj ./Widgets.UI/ #執行命令 RUN dotnet restore ./Widgets.UI/Widgets.UI.csproj #將Source目錄復制到鏡像中(WORKDIR指定的目錄下) COPY Source . #構建和發布 FROM dotnetcore-sdk as dotnetcore-publish #以上一個FROM子句制作好的鏡像為基礎鏡像,並給了這個鏡像一個新的命名 RUN dotnet publish ./Widgets.UI/Widgets.UI.csproj -c Release -o /publish #執行publish命令並將發布的內容放到容器內的/publish目錄下 #ASP.NET CORE RUNTIME 用上一步發布好的dotnet core鏡像作為基礎鏡像 FROM microsoft/dotnet:2.2-aspnetcore-runtime-alpine AS aspnetcore-runtime WORKDIR /app #定義工作目錄 COPY --from=dotnetcore-publish /publish . #--from=dotnetcore-publish這個參數說明COPY的第一個參數是從哪里復制的 EXPOSE 5000 #對外暴露5000端口 ENTRYPOINT [ "dotnet","Widgets.UI.dll" ] #鏡像啟動時執行的命令
上面就是這個dockerfile做的工作。里面有詳細說明。我們從這個dockerfile構建一個鏡像出來:
docker build -t widgets:1.0 .
上面的命令是在dockerfile這個目錄下執行的,必須從這里執行,最后面有一個點,表示的就是執行路徑。-t參數指明了鏡像的名稱和標簽。
構建完鏡像后,需要吧鏡像上傳到服務器,我沒有構建私有倉庫,我采用docker image save的方式將鏡像保存成tar文件:
docker image save widgets:1.0 C:\\registry\widgets.tar
我將這個鏡像保存成一個tar文件,然后上傳到雲服務器,然后在雲服務器上面,使用下面命令將tar文件還原成鏡像:
docker image load -i /path/to/widgets.tar
這樣就從tar文件還原了一個鏡像,然后之后后續的步驟。
8、啟動應用和nginx
我是分別做了兩個鏡像,一個是nginx的,一個是asp.net core的,nginx作為asp.net core的反向代理來使用。這兩個鏡像運行后得到兩個容器。因為nginx容器要和asp.net core容器進行交互,所以我需要一種方式來實現這個需求,我采用的是docker network命令來創建一個橋,然后吧這兩個應用都運行在這個橋上。具體如下:
首先是nginx:
docker run -d -p 443:443 --name nginx \ --network=widgetsweb \ --network-alias=nginx \ -v /home/wallee/nginx/conf.d:/etc/nginx/conf.d \ -v /home/wallee/nginx/nginx.conf:/etc/nginx/nginx.conf \ -v /home/wallee/nginx/log:/var/log/nginx \
-v /etc/letsencrypt:/etc/letsencrypt \ nginx
解釋一下這個命令的參數:
- -d:后台運行
- -p端口映射,從容器外部到容器內部的映射
- --name指定容器名稱
- --network指定容器運行在那個橋,橋使用docker network create來創建的,這里橋的名稱是widgetsweb
- --network-alias指定容器運行的橋的別名,這個別名在nginx的配置中有用
- 第一個-v 將宿主上的conf.d掛載到容器的conf.d上,包含了default.conf這個子配置文件
- 第二個-v將宿主上的nginx.conf掛載到容器的的nginx.conf文件中,包含了nginx的主配置文件
- 第三個-v將宿主上的log文件掛載到容器的log,包含了access.log和error.log
- 第四個-v將宿主上的/etc/letsencrypt掛載到容器的/etc/letsencrypt上,包含了證書(ssl)的相關信息
然后啟動asp.net core容器:
docker run -d -p 5000:5000 --name widgetsweb \ --network=widgets \ --network-alias=widgetsweb \ widgets:1.0
我將容器內的5000端口映射到外部的5000端口,並將容器運行到widgets這個橋上,這樣,asp.net core容器和nginx容器都在一個橋上,實現了互通信息的目的。然后,我給asp.net core 容器的橋起了一個別名,這個別名在nginx的配置中有用,下面來看看nginx的配置文件是怎楊的:
user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; include /etc/nginx/conf.d/*.conf; }
這個nginx的主配置文件分了幾個模塊:
events模塊主要定義了工作者進程的連接數,https模塊定義了一些在http模塊上下文的全局參數如日志格式,長連接過期時間等,然后它里面有一個include指令,這個指令將conf.d文件夾下的defualt.conf文件包含了進來,default.conf文件的內容如下:
upstream widgets{ server widgetsweb:5000; } server{ listen 443 ssl; server_name www.bankskit.com; ssl_certificate /etc/letsencrypt/live/www.bankskit.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/www.bankskit.com/privkey.pem; location / { proxy_pass http://widgets; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
upstream模塊定義了上游服務器的一些信息,upstream主要用來做負載均衡,這里面只定義了一台服務器,這個服務器就是asp.net core容器運行的服務。注意看這句:
server widgetsweb:5000
widgetsweb這個就是運行asp.net core容器時給這個容器的橋器的別名。這里配置成這樣是生效的。
在server模塊中定義了監聽的地址(https的標准端口)和ssl證書以及證書的密鑰。location模塊中定義了代理的地址,proxy_pass http://widgets這句話中的widgets是upstream后面定義的名稱。
到此主要的配置已經完成。
然后在瀏覽器上訪問你的網站已經成功。
然后就需要在微信小程序中將你申請好的域名配置到里面,然后你的小程序就能訪問你建立好的api了