docker4dotnet #3 在macOS上使用VSC和Docker開發(fā)asp.net core+mysql應(yīng)用
今天,我們來看看如何在macOS上使用 Visual Studio Code 開發(fā) Asp.net Core 應(yīng)用,并使用Docker來進(jìn)行開發(fā)調(diào)試和部署。之前在使用 Visual Studio 的時(shí)候,我們看到了良好的集成性,IDE把所有的工具鏈全部配置好了,你要做的只是F5就可以了,今天我們來嘗試一下手工編寫Dockerfile和Docker-Compose文件。另外,既然要開源,我們就徹底一點(diǎn),這次我們不再使用sqlite或者sql server作為數(shù)據(jù)庫,而采用開源界最流行的mysql作為我們應(yīng)用的后臺(tái)數(shù)據(jù)庫。
準(zhǔn)備開發(fā)環(huán)境
首先我們需要下載和安裝幾個(gè)工具
1. Visual Studio Code 和 .Net Core
Visual Studio Code 是微軟為廣大開發(fā)人員提供的免費(fèi)開源的跨平臺(tái)代碼編輯器,和其它流行的代碼編輯器,如:Sublime, Atom一樣,它非常小,運(yùn)行速度快,同時(shí)通過各種插件支持不同開發(fā)語言的編寫。不同的地方在于,VSC的插件不僅僅提供靜態(tài)的語言高亮,自動(dòng)語法檢測(cè)和完成功能外;還提供更加高級(jí)的編譯器服務(wù)支持,這使得VSC可以在一定程度上替代IDE的功能,進(jìn)行代碼的編譯,調(diào)試和發(fā)布操作。
下載地址:https://code.visualstudio.com
(同樣大家可以在公眾號(hào)中輸入d4dtools獲取最新版本的code安裝包,提供Windows/Mac/Linux不同版本)
Asp.net Core 是一個(gè)跨平臺(tái)的 asp.net 開發(fā)環(huán)境,可以通過以下地址下載macOS上的安裝包
下載地址:http://dot.net
注:如果你之前安裝過asp.net 5或者 rc版的.net core,需要在運(yùn)行以上安裝命令之前先卸載,在d4dtools網(wǎng)盤中下載 dotnet-uninstall-pkgs.sh,運(yùn)行即可。
2. Node.JS和NPM,以及 bower, gulp 和 grunt 等前端工具
Node.js是一個(gè)javascript的運(yùn)行引擎,提供服務(wù)端的javascript運(yùn)行能力,同時(shí)也包含了npm這個(gè)包管理器,可以用來安裝 bower, glup,grunt等前端工具。
下載地址:http://nodejs.org
(d4dtools中包含v4.4.7 LTS安裝包的Windows和Mac版本)
安裝完成后,讓通過以下命令安裝前端工具
- npm install bower gulp grunt-cli -g
3. 安裝項(xiàng)目模版生成工具 yeoman 和 asp.net generator
YEOMAN是用來創(chuàng)建項(xiàng)目模版的工具,用慣了Visual Studio的開發(fā)人員一定都很喜歡新建項(xiàng)目的引導(dǎo)工具,選擇自己要用的項(xiàng)目類型就可以創(chuàng)建出一個(gè)可運(yùn)行的基本項(xiàng)目框架,這讓啟動(dòng)一個(gè)項(xiàng)目或者學(xué)習(xí)編程都變的非常容易。Yeman提供了同樣的功能。
運(yùn)行以下命令就可以完成yeoman的安裝
- npm install yo -g
在yeoman中提供了不同的generator(模版生成器)來提供不同類型的項(xiàng)目的生成,為了能夠生成asp.net core應(yīng)用,我們需要安裝aspnet generator
- npm install generator-aspnet -g
安裝好以后就可以創(chuàng)建項(xiàng)目了
你也可以自己創(chuàng)建generator,可以參考 aspnet generator 的 github 源代碼來學(xué)習(xí)。
https://github.com/omnisharp/generator-aspnet
4. Docker for Mac
與 Docker for Windows 一樣,我們可以在macOS上安裝Docker for Mac來支持Docker環(huán)境的管理。
下載地址:https://www.docker.com/products/docker
(d4dtools網(wǎng)盤:Docker.dmg)
至此,我們的開發(fā)環(huán)境就準(zhǔn)備完畢了。
創(chuàng)建asp.net core webapp
使用以上這些工具,我們就可以很順暢的建立應(yīng)用程序了
1. 創(chuàng)建項(xiàng)目模版
首先創(chuàng)建一個(gè)應(yīng)用目錄,源代碼目錄
- mkdir aspnet-mysql
- cd aspnet-mysql
- mkdir src
然后進(jìn)入src目錄使用yoman創(chuàng)建項(xiàng)目
- cd src
- yo aspnet
選擇 Web Application [without Membership and Authorization]作為項(xiàng)目類型,Bootstrap作為前端框架,并給出應(yīng)用名稱aspnet-mysql
回車后,yeoman創(chuàng)建項(xiàng)目中的代碼文件結(jié)構(gòu),并運(yùn)行 bower install 完成所需要的javascript/css的安裝
然后運(yùn)行以下命令,完成asp.net的nuget依賴包安裝
- cd aspnet-mysql
- dotnet restore
最后,鍵入以下命令打開 Visual Studio Code
- code .
這時(shí),VSC會(huì)自動(dòng)生成以下配置文件,用于配置VSC中的開發(fā)調(diào)試工具鏈
- .vscode/launch.json
- .vscode/task.json
現(xiàn)在,你就可以切換到調(diào)試視圖,并點(diǎn)擊運(yùn)行按鈕開始調(diào)試你的應(yīng)用了,你也可以在代碼中設(shè)置斷點(diǎn),像在Visual Studio中一樣進(jìn)行單步調(diào)試,查看變量數(shù)值的變化。
2. 創(chuàng)建mysql容器作為開發(fā)數(shù)據(jù)庫
數(shù)據(jù)庫的開發(fā)一般會(huì)要求我們先安裝一個(gè)數(shù)據(jù)庫引擎在自己的機(jī)器上,現(xiàn)在使用docker,我們可以在容器中運(yùn)行一個(gè)數(shù)據(jù)庫引擎。這樣做有很多好處,1)自己的機(jī)器可以很干凈,不用擔(dān)心各種程序之間互相沖突;2)數(shù)據(jù)庫隨用隨開,不用的時(shí)候就關(guān)掉,不必占用資源;3)可以每次都用干凈的數(shù)據(jù)庫進(jìn)行調(diào)試,不用操心恢復(fù)數(shù)據(jù)狀態(tài);如果需要的話,也可以把容器中的數(shù)據(jù)卷內(nèi)容同步到本機(jī)上。
要這樣做,首先你需要一臺(tái)容器化主機(jī),可以參考本系列第二篇 《docker4dotnet #2 容器化主機(jī)》中的做法
這里我使用了一臺(tái)運(yùn)行在本地的vmwarefusion中的docker主機(jī)
首先通過以下命令將docker命令重定向到這臺(tái)主機(jī)中
- eval $(docker-machine env {machine-name})
運(yùn)行以下命令啟動(dòng)一臺(tái)mysql數(shù)據(jù)庫容器,創(chuàng)建一個(gè)叫做ef的數(shù)據(jù),并將3306端口暴露給本地環(huán)境
- docker run --name mysql-dev -e MYSQL_ROOT_PASSWORD=P2ssw0rd -e MYSQL_DATABASE=ef -p 3306:3306 -d mysql
參數(shù)說明:
- name: 給容器起個(gè)名字叫做mysql-dev,這樣便于后邊管理用,如果不給名字的話docker會(huì)給一個(gè)隨機(jī)名字
- -e: 配置容器的環(huán)境變量,這里我配置了
- MYSQL_ROOT_PASSWORD : root用戶密碼
- MYSQL_DATABASE: 新數(shù)據(jù)庫名稱,mysql容器會(huì)按照要求創(chuàng)建一個(gè)ef為名稱的空數(shù)據(jù)庫
- -p: 暴露端口,將3306端口暴露出來,便于管理
如果你本地沒有mysql的鏡像,docker會(huì)到docker hub去下載,如果已經(jīng)有了鏡像那么啟動(dòng)真的是毫秒級(jí)的,啟動(dòng)完成后你就可以使用管理工具連接到這個(gè)容器上,這里我用的MySQL Workbench
你可以看到 ef 數(shù)據(jù)庫已經(jīng)創(chuàng)建好了。
3. 配置asp.net應(yīng)用使用mysql作為Entity Framework數(shù)據(jù)源
默認(rèn)的asp.net 應(yīng)用程序在Windows上使用sql server或者localdb作為數(shù)據(jù)源,在非windows系統(tǒng)上使用的是sqlite。這里,localdb和sqlite都只能作為開發(fā)調(diào)試用途,如果需要投入生產(chǎn)就需要使用sql server,對(duì)應(yīng)到開源產(chǎn)品,我們可以選用mysql作為sql server的替代品。在上一步中我們已經(jīng)配置好了用于開發(fā)的mysql服務(wù)器(容器),現(xiàn)在我們需要對(duì)應(yīng)用程序進(jìn)行配置,讓Entity Framework可以使用mysql作為數(shù)據(jù)源。
這里,我們使用的是由國內(nèi)開發(fā)人員貢獻(xiàn)的開源庫,github地址如下:
https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql
首先在 project.json 文件的 dependencies 配置節(jié)中加入以下引用:
- "Pomelo.EntityFrameworkCore.MySql": "1.0.0-prerelease-20160726"
同時(shí)添加一個(gè)nuget.config配置文件,并在里面添加Pomelo的源地址,這主要是因?yàn)檫@個(gè)nuget庫現(xiàn)在還沒有正式發(fā)布,發(fā)布以后就不用進(jìn)行這個(gè)配置了
- <?xml version="1.0" encoding="utf-8"?>
- <configuration>
- <packageSources>
- <add key="NuGet official package source" value="https://nuget.org/api/v2/" />
- <add key="PomeloMysql" value="https://www.myget.org/F/pomelo/api/v2/"/>
- </packageSources>
- </configuration>
然后再次運(yùn)行 dotnet restore,這次需要添加 –configfile nuget.config這個(gè)參數(shù)確保dotnet restore可以正確使用nuget源
- dotnet restore --configfile nuget.config
4. 添加 MVC Model到項(xiàng)目中
現(xiàn)在我們就可以在項(xiàng)目中創(chuàng)建我們的實(shí)體類了,并且為了演示方便,我在還添加了一些示例數(shù)據(jù)。
代碼文件 https://github.com/ups216/aspnet-mysql/blob/master/src/aspnet-mysql/Models/Blog.cs
然后在starup.cs中的ConfigureServices方法中添加依賴注入代碼
- services.AddDbContext(options=>
- options.UseMySql(Configuration.GetConnectionString("Mysql")));
在Configure方法中調(diào)用SampleData.InitDB來創(chuàng)建示例數(shù)據(jù)
- await SampleData.InitDB(app.ApplicationServices);
代碼文件:https://github.com/ups216/aspnet-mysql/blob/master/src/aspnet-mysql/Startup.cs
在 appsetting.json 中創(chuàng)建名為Mysql的連接字符串
- "ConnectionStrings": {
- "DefaultConnection": "Data Source=aspnetweb01.db",
- "Mysql": "Server={docker machine ip};database=ef;uid=root;pwd=P2ssw0rd;"
- }
代碼文件:https://github.com/ups216/aspnet-mysql/blob/master/src/aspnet-mysql/appsettings.json
確保 uid及pwd參數(shù)與docker run命令中的MYSQL_DATABASE,MYSQL_ROOT_PASSWORD一致
[docker machine ip]地址可以通過以下命令獲取
- docker-machine ip
現(xiàn)在,再次使用VSC啟動(dòng)應(yīng)用進(jìn)行調(diào)試,你可以看到在mysql中的ef數(shù)據(jù)中Blogs和Users兩個(gè)表已經(jīng)創(chuàng)建并且寫入了示例數(shù)據(jù)。
使用Docker打包發(fā)布應(yīng)用
以上我們已經(jīng)完成了asp.net應(yīng)用的創(chuàng)建,并且使用了一個(gè)運(yùn)行在容器中的mysql進(jìn)行開發(fā)調(diào)試,現(xiàn)在我們需要將這個(gè)應(yīng)用使用docker打包并運(yùn)行在容器中。
1. 創(chuàng)建Dockerfile
使用yoman創(chuàng)建的應(yīng)用本身就已經(jīng)包含了一個(gè)Dockerfile,我們只需要進(jìn)行簡(jiǎn)單修改即可
文件內(nèi)容如下
- FROM microsoft/dotnet:latest
- COPY . /app
- WORKDIR /app
- RUN ["dotnet", "restore", "--configfile", "nuget.config"]
- RUN ["dotnet", "build"]
- EXPOSE 5000/tcp
- ENTRYPOINT ["dotnet", "run", "--server.urls", "http://0.0.0.0:5000"]
代碼鏈接:https://github.com/ups216/aspnet-mysql/blob/master/src/aspnet-mysql/Dockerfile
這個(gè)文件很簡(jiǎn)單,我們來看看它都做了些什么:
- FROM microsoft/dotnet:latest 告訴docker build使用microsoft/dotnet這個(gè)鏡像的最新版作為我們的base image
- COPY . /app 將本機(jī)上Dockerfile所在目錄的所有文件拷貝到容器的/app目錄中
- WORKDIR /app 設(shè)置容器使用/app作為工作目錄,這樣后續(xù)的操作就都在這個(gè)目錄中進(jìn)行
- RUN dotnet restore 和 RUN dotnet build告訴docker build要執(zhí)行dotnet restore和dotnet build兩個(gè)命令,同時(shí)使用nuget.config作為restore的配置文件
- EXPOSE 5000/tcp 暴露5000端口
- ENTRYPOINT [“dotnet”, “run”, “–server.urls”, “http://0.0.0.0:5000”],設(shè)置容器入口為dotnet run命令,這個(gè)命令將啟動(dòng)我們應(yīng)用
2. 構(gòu)建容器鏡像并運(yùn)行容器
現(xiàn)在,我們就可以運(yùn)行以下命令完成容器構(gòu)建了
- docker build -t {image name}.
其中 {image name} 你可以隨便起,我這里用的是ups216/aspnet-mysql,這是我后面要上傳到docker hub上所用的名字
這里在dotnet restore這一步會(huì)比較慢,因?yàn)樾枰螺d所有的依賴包。
注:在日常開發(fā)中,你可以將先用常用包創(chuàng)建一個(gè)自己的base image,替換Dockerfile中的microsoft/dotnet,這樣就不用每次都重新下載包了。
現(xiàn)在鍵入docker images命令就可以看到我們新創(chuàng)建的image了
運(yùn)行
- docker run --name aspnet-msyql-dev -p 5000:5000 ups216/aspnet-msyql
你可以看到我們的容器現(xiàn)在也正確連接到了mysql容器的對(duì)外端口上了。在瀏覽器中輸入docker主機(jī)的ip地址:5000端口,我們的應(yīng)用就完全在容器中跑起來了。
在docker ps中看到的2個(gè)容器是這樣的
3. 生產(chǎn)部署打包
以上過程中我們已經(jīng)將應(yīng)用部署到了容器中,并且連接到另外一個(gè)容器中運(yùn)行的mysql服務(wù)。但是,我們這個(gè)連接是通過mysql容器暴露給主機(jī)的端口來連接的,這樣做在開發(fā)過程中會(huì)比較方便,因?yàn)槟憧梢匀萜鞯膶?shí)用工具連接到mysql進(jìn)行操作,但是如果要進(jìn)行對(duì)外發(fā)布就不是個(gè)好主意了。
同時(shí),我希望能夠?qū)eb應(yīng)用和mysql容器一同部署,形成一個(gè)完整的應(yīng)用部署包。這時(shí),就需要借助docker-compose來完成了。
首先,我們創(chuàng)建一個(gè)用于生產(chǎn)環(huán)境的配置文件,appsettings.Production.json,內(nèi)容如下:
- {
- "ConnectionStrings": {
- "DefaultConnection": "Data Source=aspnetweb01.db",
- "Mysql": "Server=db;database=ef;uid=ef;pwd=P2ssw0rd;"
- },
- "Logging": {
- "IncludeScopes": false,
- "LogLevel": {
- "Default": "Debug",
- "System": "Information",
- "Microsoft": "Information"
- }
- }
- }
代碼鏈接:https://github.com/ups216/aspnet-mysql/blob/master/src/aspnet-mysql/appsettings.Production.json
這里我們主要修改了Mysql的連接字符串,使用db作為數(shù)據(jù)庫,并使用ef作為連接用戶。
然后,我們創(chuàng)建一個(gè) docker-compose.yml 文件,內(nèi)容如下:
- version: '2'
- services:
- db:
- image: mysql
- restart: always
- environment:
- MYSQL_ROOT_PASSWORD: P2ssw0rd
- MYSQL_DATABASE: ef
- MYSQL_USER: ef
- MYSQL_PASSWORD: P2ssw0rd
- web:
- build: .
- depends_on:
- - db
- links:
- - db
- ports:
- - "5000:5000"
- restart: always
- environment:
- ASPNETCORE_ENVIRONMENT: Production
里面的內(nèi)容基本上可以從字面意思讀懂,這里主要?jiǎng)?chuàng)建了db和web兩個(gè)容器,web容器依賴db容器,并通過db這個(gè)名字鏈接過去,同時(shí)設(shè)置asp.net core的environment環(huán)境變量為Production。
對(duì)應(yīng)以上我們?cè)赼ppsettings.Production.json文件中做的修改,你就可以理解我們通過Production這個(gè)環(huán)境變量配置我們的應(yīng)用去到一個(gè)叫做db的mysql服務(wù)器上鏈接名為ef的數(shù)據(jù)庫,并且使用ef作為用戶名。
現(xiàn)在,你只需要運(yùn)行以下這一個(gè)命令就可以完成這兩個(gè)容器的啟動(dòng)了
- docker-compose up
以上代碼都已經(jīng)發(fā)布到我的github上,地址如下:
https://github.com/ups216/aspnet-mysql/
【本文為51CTO專欄作者“徐磊”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過作者微信公眾號(hào)devopshub獲取授權(quán)】