docker 入門資料,參考:https://yeasy.gitbooks.io/docker_practice/content/html
Dockerfile經常使用命令,圖片來源於網絡前端
Dockerfile 打包控制檯應用程序node
新建一個控制檯程序,控制檯程序添加一個文本文件,去掉.txt 擴展名,改爲Dockerfile 輸入如下代碼mysql
FROM microsoft/dotnet:sdk AS build WORKDIR /code COPY *.csproj /code RUN dotnet restore COPY . /code RUN dotnet publish -c Release -o out FROM microsoft/dotnet:runtime WORKDIR /app COPY --from=build /code/out /app ENTRYPOINT ["dotnet","console.dll"]
Program.cs 中編寫測試代碼linux
一切準備完成。就是build把項目打包成鏡像了git
切換到當前項目路徑下。輸入: docker build -t cn/console:v1 . github
docker build -t :是打包固有的命令web
cn/console:v1 :redis
cn:是組織名稱或者說是用戶名,若是你想把本身的鏡像push到hub.docker 上,cn必須是你本身的用戶名sql
console:是鏡像名稱
v1:是tag。一個標籤,能夠用來區分同一個鏡像,不一樣用途。,若是不指定。默認是latest
. :表明當前目錄爲上下文,dockerfile也一定是在當前目錄下
回車後,會看到一系列的執行步驟,dockerfile中。一條命令就是一個步驟
經過 docker images 能夠查看全部鏡像
經過docker images cn/console 查看相關鏡像
好比我本地有3個 cn/console鏡像,但tag不一樣
既然鏡像有了。那麼就能夠根據鏡像生成容器了。容器是鏡像的一個實例。鏡像運行起來纔會有容器,就跟類和對象同樣,new一個類,是實例化的操做
輸入命令:
docker run --name myfirst cn/console:v1
由於是佔用前端線程運行容器,全部界面沒法繼續輸入命令了。能夠Ctrl+c 結束容器運行
從上面的dockerfile。你會發現,咱們是把源碼打包成鏡像的。也就算執行了restore,到Release操做
其實若是你是已經Release後的文件了。dockerfile能夠更簡單
FROM microsoft/dotnet WORKDIR /app COPY . /app CMD ["dotnet","run"]
以上就是一個基礎的程序打包成鏡像,我以爲這不是重點,經常使用的應該是應用程序,而不是控制檯程序
後面打算把net core api打包成鏡像。在講這個以前,咱們先來搭建好環境。
Docker mysql
由於我有個阿里雲服務器(CentOS7),而後有2檯筆記本,一個是Docker for Windows 環境,一個是CentOS7,因此常常會在這3個環境中來回折騰
兩種系統仍是有區別的,至少我弄的時候,遇到過很多問題
1:for Windows中默認拉起的鏡像都在C盤。會致使C盤愈來愈大,建議遷移
若是遷移的盤。好比我這個E盤。路徑中已經存在MobyLinuxVM.vhdx 。是遷移不過的。要刪除,但以前的鏡像都沒有了
若是你想保存,先重命名MobyLinuxVM.vhdx,遷移後。刪除以後的。以前的重命名回來便可
2:共享盤。爲了數據卷掛載用
3:配置鏡像加速(https://hlef8lmt.mirror.aliyuncs.com)
而後能夠去hub.docker上尋找須要的鏡像,官方的mysql有2個鏡像
固然你經過命令也能夠收索到: docker search mysql
首先來看docker mysql
準備須要掛載的目錄和文件,上面我設置的共享盤是D盤,因此掛載的在D盤
my.cnf配置文件,主要是設置mysql的參數
[mysqld] user=mysql character-set-server=utf8 [client] default-character-set=utf8 [mysql] default-character-set=utf8
data是空的。當run的時候,mysql會寫入文件
sql是須要在運行myslq後執行的初始化文件,好比我這裏是給剛建立的用戶名分配權限
這裏爲了說明sql是執行成功的。我在加條。建立數據庫的sql,建立數據庫 docker和user表,並插入一條數據
GRANT ALL PRIVILEGES ON *.* TO 'test'@'%' WITH GRANT OPTION;
Create DATABASE docker;
USE docker;
CREATE TABLE user (ID int auto_increment primary key,name nvarchar(20),address nvarchar(50));
insert into user(name,address)values('劉德華','香港');
初始化後就執行的好處是。不用在run後,去手動執行,關於run後手動執行,
能夠查看我以前的docker安裝mysql http://www.javashuo.com/article/p-edswdmfv-ey.html
所有配置完成後,開始敲命令,如下命令須要去掉註釋
docker run -d -p 3306:3306 --restart always #老是自動重啓。好比系統重啓,該容器會自動啓動 -e MYSQL_USER=test #建立用戶名test -e MYSQL_PASSWORD=123456 #test密碼 -e MYSQL_PASSWORD_HOST=% #test 開啓外部登錄 -e MYSQL_ROOT_PASSWORD=123456 #root密碼 -e MYSQL_ROOT_HOST=% #root開啓外部登錄 -v /d/docker/mysql/my.cnf:/etc/my.cnf #配置文件 -v /d/docker/mysql/sql:/docker-entrypoint-initdb.d #初始化的sql -v /d/docker/mysql/data:/var/lib/mysql #data文件 --name mysql #鏡像名稱 mysql #基於那個鏡像建立容器
執行成功沒有異常後。經過 docker ps 能夠查看運行的容器,若是沒有, 那就經過 docker ps -a 必定會有的
如今能夠經過Navicat鏈接試試
建立了docker庫。user表也有數據,能看到mysql庫,說明test用戶是有權限的
當我使用mysql-server 鏡像時,建立容器會沒法啓動
能夠看到。啓動失敗後。又繼續重啓,由於參數指定了restart always
輸入命令 docker logs mysql 查看啓動日誌
最後在my.cnf中加這個,經測試,啓動成功,就不一一放圖了
數據庫準備好了,那麼就快速的構建一個net core api 接口
1:引入NugGet包,MySql.Data.EntityFrameworkCore
2:建立DbContext
using Docker.Api.Model; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Docker.Api.Data { public class DbUserInfoContext : DbContext { public DbUserInfoContext(DbContextOptions<DbUserInfoContext> options) : base(options) { } public DbSet<UseInfo> userInfos { get; set; } /// <summary> /// 模型建立時觸發 /// </summary> /// <param name="modelBuilder"></param> protected override void OnModelCreating(ModelBuilder modelBuilder) { /* 修改表名和主鍵,user對應數據庫的表,mysql默認是區分大小寫的 查看:show variables like '%lower%'; lower_case_table_names 爲 0 區分,1 不區分 */ modelBuilder.Entity<UseInfo>(b => b.ToTable("user").HasKey(u => u.id)); //or //modelBuilder.Entity<user>() // .ToTable("user") // .HasKey(u => u.id); base.OnModelCreating(modelBuilder); } } }
3:添加UserInfo控制器
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using Docker.Api.Data; 6 using Microsoft.AspNetCore.Http; 7 using Microsoft.AspNetCore.Mvc; 8 using Microsoft.EntityFrameworkCore; 9 10 namespace Docker.Api.Controllers 11 { 12 [Route("api/[controller]")] 13 [ApiController] 14 public class UserInfoController : ControllerBase 15 { 16 private DbUserInfoContext _DbUserInfoContext; 17 public UserInfoController(DbUserInfoContext context) 18 { 19 _DbUserInfoContext = context; 20 } 21 [HttpGet] 22 public async Task<IActionResult> Get() 23 { 24 return new JsonResult(await _DbUserInfoContext.userInfos.FirstOrDefaultAsync()); 25 } 26 } 27 }
4:配置sql鏈接字符串: server=localhost;port=3306;userid=test;password=123456;database=docker
run項目。訪問能成功獲取信息
容器互連,Docker Network
接下來咱們把這個api也打包成鏡像,而後鏈接mysql鏡像。這稱之爲容器互連
容器互連有3種方式
1:Link方式。已經被docker淘汰,docker官方不推薦使用該方式
2:Bridger,橋接的方式,單臺機器用
3:Overlay 適用於集羣時候用
Overlay 就我目前環境不適合測試,集羣也不懂。就不搞了
說說LInk和Bridger方式,具體理論知識請看docker官方文檔。我這裏只實踐
如今一切來回憶下
剛上面打包控制檯應用程序用的是:microsoft/dotnet 鏡像
而後後面帶上tag
好比:
Micirosoft/dotnet:sdk |
包含了運行時和sdk命令,打包後會很大,由於包含sdk,通常用於測試環境 |
Microsoft/dotnet:<version>-runtime |
包含運行時,不包含sdk,打包後就很小了,通常用於正式環境 |
Microsoft/dotnet:<version>-runtime-deps |
打包的時候,會自包含runtime,也就是部署的機器有沒有runtime是沒有關係 上面2種,必須機器要包含core環境 |
修改程序port運行在80上
編寫api的Dockerfile
我這裏用的sdk,由於要用到sdk命令好比dotnet restore,dotnet publish
若是已經publish的文件,直接用runtime會方便不少。上面也有說起
1 #FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build 2 FROM microsoft/dotnet:2.2-sdk AS build 3 WORKDIR /src 4 WORKDIR /source 5 #這裏的後面的 . 就是/source 路徑 6 #或者 COPY *.csproj /source 7 COPY *.csproj . 8 RUN dotnet restore 9 COPY . . 10 # 發佈到 /source/out 下 11 RUN dotnet publish -c Release -o out 12 13 #FROM mcr.microsoft.com/dotnet/core/runtime:2.2 14 FROM microsoft/dotnet:2.2-aspnetcore-runtime 15 WORKDIR /app 16 COPY --from=build /source/out . 17 EXPOSE 80 18 ENTRYPOINT ["dotnet","Docker.Api.dll"]
開始build項目 docker build -t cn/myapi .
能夠看到。這裏沒有指定tag。因此默認是latest,size也不大
成功後開始run一個容器,不過這以前要先:
準備掛載目錄。由於配置文件 appsettings 會須要動態配置,因此掛載出來
還有,好比一個網站都有log日誌,這些也須要掛載出來。便於管理。
我這裏就只掛載appsettings.json
執行命令:
docker run -d -p 80:80 --restart always --link mysql:mysqldb -v /d/docker/myapi/appsettings.json:/app/appsettings.json --name api cn/myapi
分析:
--restart always :老是重啓
-d:是在後臺執行
-p 80:80 :第一個80是暴露給外部的。第二個80是程序的。
--link mysql:mysqldb : mysql是容器名稱,mysqldb是自定義名稱,能夠理解爲服務器
-v /d/docker/myapi/appsettings.json:/app/appsettings.json:這裏就是掛載外部的數據捲了
也許你會問。我怎麼知道這個路徑的:/app/appsettings.json。從編寫的dockerfile能分析出來,待會也能夠進入容器看看
最有的工做目錄是 根路徑下: /app
而後經過頁面訪問試試
發現依然沒法訪問,由於修改appsettings.json的鏈接方式
記住這裏是修改D:\docker\myapi\appsettings.json ,由於已經掛載出來
把server改爲mysqldb,而後重啓容器: docker restart api
再次刷新頁面
咱們 進入容器看看: docker exec -it api bash 能夠看到根目錄下存在app目錄
進入app目錄
我的認爲link方式是最簡單的。在這3種中,接下來看看Bridge方式
1:首先建立一個網絡 network,名稱叫api2bridge
docker network create -d bridge api2bridge
經過: docker network ls 能夠查看到已經建立成功
2:實例化容器
爲了區別於上面的80端口,這裏新增一個8081
docker run -d -p 8081:80 --restart always -v /d/docker/myapi/appsettings.json:/app/appsettings.json --net api2bridge --name api2 cn/myapi
建立容器的時候,自定network 這裏的--net api2bridge 就是上面的bridge
3:鏈接2個容器,經過: docker network connect api2bridge mysql 把api2和mysql鏈接起來
4:修改appsettings.json server=mysql
5 : restart 容器,若是是在建立容器前修改的配置文件。是不須要重啓的,測試經過
看看這兩個容器是怎麼鏈接的。經過命令: docker inspect api2bridge 能夠查看對象的元數據(容器或者網絡)
分別看看;
docker inspect api2
docker inspect mysql
你會發現mysql有個"IPAddress":地址,
上面咱們在api2中的appsettings.json的server是直接些的容器名稱:mysql。也能夠直接些這個ip地址。好比: server=172.20.0.3 一樣是能夠的。
Overlay方式就不講了。由於我也不知道。哈哈
docker-compose 容器編排
經過這幾個例子你會發現。2個容器要部署2個,若是項目依賴mysql,redis,MQ等等。那得部署屢次,如此重複性的工做會影響效率
因此有了docker-compose,compose
參考:https://yeasy.gitbooks.io/docker_practice/content/compose/install.html
安裝:
sudo curl -L https://github.com/docker/compose/releases/download/1.17.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose
安裝完成後,能夠經過: docker-compose --version 查看版本
經過: docker-compose --help 查看基本的命令
不過我英文很差,就經過百度翻譯了,翻譯得有點生硬。僅供參考
Commands: build 創建或重建服務 bundle 從撰寫文件生成Docker捆綁包 config 驗證並查看撰寫文件 create 建立服務 down 中止並刪除容器、網絡、圖像和卷 events 從容器接收實時事件 exec 在正在運行的容器中執行命令 help 獲取有關命令的幫助 images 列表圖像 kill 殺死容器 logs 查看容器的輸出 pause 暫停服務 port 打印端口綁定的公共端口 ps 列表容器 pull 拉取服務圖像 push 推送服務圖像 restart 從新啓動服務 rm 移除中止的容器 run 運行一次性命令 scale 設置服務的容器數 start 啓動服務 stop 中止服務 top 顯示正在運行的進程 unpause 取消暫停服務 up 建立和啓動容器 version 顯示Docker撰寫版本信息
目前爲止已經有3個容器了,
爲了區別於以前的mysql和api和api2,這裏命名要修改,編寫在程序根目錄下添加docker-compose.yml文件
compose用的是yml語法。能夠參考阮一峯些的文章
http://www.ruanyifeng.com/blog/2016/07/yaml.html
項目準備。依然在上面的api項目中添磚加瓦
還記得上面初始化的建立docker庫,user表嗎。這裏咱們經過在代碼中來實現,
場景:建立myslq的時候,判斷數據庫是否有數據,不然新增一條數據
技術棧:項目依賴mysql,redis,其實我工做中用的都是mssql,因此待會也會介紹
1:init.sql 只保留一條sql語句
2:新增UserInit類。用於初始化數據
using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Docker.Api.Data { public class UserInit { private ILogger<UserInit> _logger; public UserInit(ILogger<UserInit> logger) { _logger = logger; } public static async Task InitData(IApplicationBuilder app, ILoggerFactory loggerFactory) { using (var scope = app.ApplicationServices.CreateScope()) { var context = scope.ServiceProvider.GetService<DbUserInfoContext>(); var logger = scope.ServiceProvider.GetService<ILogger<UserInit>>(); logger.LogDebug("begin mysql init"); context.Database.Migrate(); if (context.userInfos.Count() <= 0) { context.userInfos.Add(new Model.UseInfo { name = "admin", address = "博客園" }); context.SaveChanges(); } } await Task.CompletedTask; } } }
程序啓動調用:
3:實體類
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Docker.Api.Model { public class UseInfo { public int id { get; set; } public string name { get; set; } public string address { get; set; } } }
4:DbContext 上面也列出,這裏就不展現了
5:RedisHelper網絡有。這裏也不提了
只准備2個接口。用於測試redis。一個讀,一個寫
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace Docker.Api.Controllers { [Route("api/[controller]")] [ApiController] public class RedisController : ControllerBase { [HttpPost] public void Post() { RedisCommon.GetRedis().StringSet("docker", "hello", TimeSpan.FromMinutes(1)); } [HttpGet] public string Get() { var docker = RedisCommon.GetRedis().GetStringKey("docker"); if (docker.HasValue) return docker.ToString(); return "empty"; } } }
6:根據Model生成Migration,這裏簡單過一下,具體參考我以前的:http://www.javashuo.com/article/p-scqvowfx-gk.html
調出程序包管理控制檯
輸入: Add-Migration init
若是成功了就會這樣:
編寫docker-compose.yml 文件,我這裏的註釋是便於理解。儘可能不要寫
注:我是直接在項目中建立的文本文件,而後修改後綴名
在網絡上看到說。若是是在外部建立的記事本。要修改編碼爲:ASCII編碼格式,我未測試
version: '3' services: db: image: mysql container_name: 'mysql01' command: --character-set-server=utf8 --collation-server=utf8_general_ci restart: always ports: - '3307:3306' environment: MYSQL_USER: test MYSQL_PASSWORD: 123456 MYSQL_PASSWORD_HOST: '%' MYSQL_ROOT_PASSWORD: 123456 MYSQL_ROOT_HOST: '%' volumes: - /d/docker/mysql02/my.cnf:/etc/my.cnf - /d/docker/mysql02/data:/var/lib/mysql - /d/docker/mysql02/SqlInit:/docker-entrypoint-initdb.d redis: image: redis container_name: 'redis' command: redis-server /usr/local/etc/redis/redis.conf restart: always ports: - '6379:6379' environment: requirepass: 123456 #redis密碼 appendonly: 'yes' #redis是否持久化 volumes: - /d/docker/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf - /d/docker/redis/data:/data #這裏會保存持久化數據 web: build: . #會執行當前目錄下面的dockerfile文件 container_name: 'api3' #容器名稱 restart: always # web依賴於db,若是web比db啓動快。就鏈接不上db致使web異常,web容器啓動失敗,restart能夠不斷重試,直到鏈接爲止 volumes: - /d/docker/myapi/appsettings.json:/app/appsettings.json ports: - '8082:80' depends_on: #依賴db容器,並不表明執行順序 - db - redis
若是想看編寫的yml文件是否正確,能夠去在線的網站,驗證是否正確,好比:http://nodeca.github.io/js-yaml/
切換到當前目錄輸入: docker-compose build 會開始build項目成鏡像
查看鏡像:名字叫dockerapi_web
輸入命令: docker-compose up 會開始建立容器並啓動
輸出的日誌太多。這裏只點幾個有用的看
EFcore插入Migration歷史記錄
建立表:
直到最後,程序阻塞。顯示成功,由於這裏沒用用 -d 會阻塞,調試的時候不建議 -d
而後新打開一個PowerShell,輸入docker ps 查看運行的容器
分別測試是否成功
一樣驗證redis,用RedisDesktopManager鏈接
從容器能夠看出api3端口是8082,嘗試訪問下
測試寫redis,打開Postman寫入Redis
寫人成功
那麼讀取就不是什麼大問題了
問題彙總:
若是你修改了代碼,須要從新build。那麼先刪除容器: docker-compose down 會中止容器並刪除
docker-compose ps 查看容器列表
docker-compose up -d 後端運行,不阻塞前端
docker-compose restart 重啓全部容器。
自此全部容器成功運行,但我感受還不夠,由於一直都是在windos上玩。而沒有上CentOS7,可我又不缺CentOS環境。因此要玩一把
net core Api 跨平臺部署
技術棧:Jexus,mysql,mssql,redis
關於jexus部署net core 能夠參考我前面寫的文章:https://www.cnblogs.com/nsky/p/10386460.html
既然要加入新的成員。jexus 和 mssql,那麼就得修改docker-compose文件
在經過docker-compose統一打包前,咱們先來單獨玩玩mssql
準備數據卷掛載目錄
data:保存數據庫文件
sql:執行的腳本。mssql沒有mysql的docker-entrypoint-initdb.d 掛載,啓動mysql就執行sql。這裏sql文件夾
雖然保存的是.sql文件。但要手動執行,不知道是否是我沒有找到具體的方案
sql裏面放一個init.sql文件。編寫sql腳本以下
這裏要注意一點,一條語句完成必需要帶一個Go語句
參考官方文檔:
鏡像文檔:https://hub.docker.com/_/microsoft-mssql-server
//註釋部分 docker run -d -p 1433:1433 \ -e ACCEPT_EULA=Y \ #確認您接受最終用戶許可協議。 -e SA_PASSWORD=DockerPwd123 \ #強大的系統管理員(SA)密碼:至少8個字符,包括大寫,小寫字母,基數爲10的數字和/或非字母數字符號。 -e MSSQL_PID=Express \ #版本(Developer,Express,Enterprise,EnterpriseCore)默認值:Developer -v /docker/mssql:/var/opt/mssql \ # 映射數據庫 v /d/docker/mssql/sql:/script #把須要執行的腳本放這裏,script路徑隨便改,不是初始化執行,是手到執行 --name mssql #容器名稱 mcr.microsoft.com/mssql/server #鏡像
執行成功後。數據卷掛載目錄。生成了文件
此時data也有默認的數據庫了
經過 MSSMS( Microsoft SQL Server Management Studio )鏈接試試
剛上面說了sql中文件是沒有被執行的。必須手動執行。
手動執行前,先來看看其餘一些相關命令
進入容器後: docker exec -it mssql bash
登錄數據庫:localhost也能夠用指定的ip代替,若是有端口。則帶端口號便可
/opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P DockerPwd123 -S 是服務器,無論端口是多少,都不用寫 -U 是用戶名 -P 是密碼
若是出現 1> 說明的登錄成功了
能夠輸入語句:select getdate() 試試,回車後,須要加Go語句,不過日期怎麼不對?好像是相差8個時區
執行sql中的文件
登錄容器後執行操做: /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P DockerPwd123 -i /script/init.sql
掛載目錄也有,這樣就算容器沒法進入。數據庫也存在
因爲時間問題,docker-compose 就不加入mssql,只加jexus,修改docker-compose以下
阿里雲安裝docker-compose 特別慢,今天就不寫。後期在加上
關鍵時刻掉鏈子,寫了這麼多。其實也就一點皮毛而已,docker強大之處遠遠不止這些
未完待續
Portainer管理鏡像
上傳鏡像到hub.docker