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