一、Docker简介
1.1 什么是docker
docker的英文意思是 码头工人,意思就是搬运东西的意思,其实这和docker的特点是一样的,docker提供的就是一种容器化搬运东西(我们的软件、程序)的过程。docker自己本来是运行在操作系统上一个程序软件,它会提供一个容器环境,使我们的程序独立地运行在容器中,所以说,官方给docker起的这个名字也真是应景。
就连图标也是这么生动形象,富有诗意,让人浮想联翩。。。。(这是去幼儿园的车,还没有拐进大学城)
试想下边这样一个场景:当我们把我们的web网站做成分布式的时候,我们就要加服务器,然后在各个服务器配置web所需要的配置,比如:数据库、web服务器、运行时啥,这样的我们的网站才能跑起来,但是每当我们加服务器的时候,我们都要再重新配置一下,很繁琐,有了docker,我们就可以把我们的网站和所需要的环境配置好,打成一个包(docker镜像),然后在服务器上安装docker,用docker拉取打包好的镜像,直接run(容器)起来就行了,什么都不用管了,很方便,更加的便于管理,镜像中修改配置,重新更新,所有的容器就也能修改了,我们的网站也就修改更新了,特别的方便。
1.2 docker的特点
docker的特点包含在它的口号中
第一句,是“Build, Ship and Run”。(搭建、运输、运行)
第二句,“Build?once,Run?anywhere(一次搭建,到处运行)”。
看了上边的场景,我们可以理解这两句话了,现在哪个软件不是一次搭建、到处运行,真的都不好意思说,从最开始的java,到现在的各种跨平台前端框架(Flutter、ReactNative、Ionic啥的)、小程序框架(Taro),这是解放开发人员减少开发成本提高开发效率的三连击造福码农的事情啊,各种应用层出不穷,叫我们情何以堪、不堪回首、首当其冲、冲锋陷阵啊,一不小心就暴露了我的文化。
1.3 docker和虚拟机
一说到docker,相信大家之前也了解过,那就必须要和虚拟机做一下比较,其实docker是和虚拟机是类似的东西,我们应该知道虚拟机就是在我们的操作系统上虚拟出来一个电脑,然后里边可以安装、运行各种各样的软件,和我们真的电脑是差不多的,我们可以拿着这个虚拟好的电脑(其实是一个文件)在按了虚拟机的其他电脑上可以直接运行,里边的东西就不用我们来回安装和配置了,也是很方便的。
docker其实提供的也是这么一种的技术,只不过它比虚拟机效率更加的高,启动快,占用资源小等一系列的优点,而且虚拟机比较笨重,这是因为虚拟机和docker来实现思想上有本质的区别,我们可以通过下边的两张图可以对比一下:
虚拟机的运作原理:是虚拟电脑的硬件资源,把硬件资源分配出来,然后虚拟出来多个操作系统,虚拟出来的是一个完整的电脑。
docker的运作原理:虚拟的软件资源,把电脑中的网络、存储啥的分成几份虚拟成容器,我们的软件运行在容器中,每个容易只占用电脑的部分所需要的资源,并不是一个完整的电脑。
大概就像上边那么理解吧,反正也不知道是对不对,但是看起来应该不错。
所以,从运作原理上来看,docker更加的轻量级,虚拟机更加的笨重,docker启动也十分的快,部署起来也方便,所以越来越多的人开始使用docker起来。
下边是一张虚拟机和docker的对比,这里要注意一下:但是docker本身并不是容器,而是创建容器的工具;而虚拟机它就是虚拟机了。
二、docker中的核心概念
下面我们来介绍使用docker的过程中必须要掌握的概念,理解这些概念对docker的使用和学习是非常必要的。
2.1 镜像(Image)
镜像到底是个什么东西呢,很多人在学习docker的时候都是一头雾水的,可是是歪果仁对镜像情有独钟吧,好多东西都有镜像的概念。比如我们安装系统的.iso文件,其实就是镜像,这里你就可以把镜像认为是一种模板。我们可以使用docker根据这个模板创建容器来运行,其实更可以理解为镜像是好比github上的仓库一样,我们可以克隆下来源代码然后运行,运行起来的代码可以是一个网站、一个应用程序啥的,这就可以叫做容器。说白了,镜像就是一堆静态的模板,运行起来的镜像就是容器。镜像一般需要我们拉取下来,是只读的,这个我们克隆github上的仓库是一样一样的。
docker镜像中有分层的概念,就是一个镜像可能基于好几个镜像,比如一个web运行环境可能需要操作系统ubuntu、数据库mysql、.net core runtime运行时,那我们拉取的这个镜像就会包好这好几个镜像,这就好像我们前边说的打包好的运行环境一样,直接就拉下来一个小电脑一样。
2.2 容器(Container)
当我们拉取了一个镜像,然后run一下,就会根据这个镜像运行出来一个容器,运行的容器就好像我们的应用程序一样,可以访问可以停止,我们运用多次run命令,就运行了很多很多容器,也可以说是镜像的实例。从这个角度来看,我们可以把镜像看作是类,容器看作new出来的实例,也是很合适的。
2.3 仓库(Repository)
存放镜像的地方就是仓库,就好比存放代码的地方是github一样,我们就把github称为代码的仓库,github算是最大的仓库。那么存放docker镜像的地方我们叫做dockerhub,是docker的*仓库。其实已经有dockerhub这个网站了(https://hub.docker.com/),这就是 存放docker镜像的官方仓库,好多官方的也保存在这里,保证了镜像的安全性和可靠性,我们可以从上边拉取一下镜像来运行我们的软件。当然我们也可以制作好我们自己镜像推送上去,不过这些肯定是要官方审核的,防止有些人写入一些恶意代码。不过我们可以推到我们自己的dockerhub上去,供我们自己使用,这个就好我们的github账号一样了,属于私有镜像了。
2.4 数据卷(Volumn)
实际上我们的容器就好像是一个简易版的操作系统,只不过系统中只安装了我们的程序运行所需要的环境,前边说到我们的容器是new出来的实例,既然是new出来的实例那就会销毁,那如果销毁了我们的程序产生出的需要持久化的数据怎么办呢,容器运行的时候我们可以进容器去查看,容器一旦销毁就什么都没有了。所以数据卷就是来解决这个问题的,是用来做数据持久化到我们的宿主机上容器间的数据共享,简单的说就是将宿主机的目录映射到容器中的目录,应用程序在容器中的目录读写数据会同步到宿主机上,这样容器产生的数据就可以持久化了,比如我们的数据库容器,就可以把数据存到我们宿主机上的真实磁盘上了。
docker中基本的概念已经说了,下面我们就可以来使用docker了。
三、docker的安装与使用
由于docker的服务端只能运行在linux操作系统上,当然我们学习也可以用windows来安装docker,但是比较麻烦,需要开启一系列的配置,我们还是暂时在一台装有虚拟机的linux系统上来学习docker。这里我们以linux ubuntu 16.04 版本来学习。
3.1 docker的安装
我们先执行命令安装docker
1. 执行 sudo apt-get update 更新软件包
2. 执行 sudo apt-get -y install docker.io 安装docker
3. 输入 docker version 检查docker是否安装成功
如果有下面的输出,说明docker安装成功
3.2 docker的使用
docker --help 查看帮助信息,一般的软件都会有这个命令,不再多说。
docker version 查看docker的版本。
docker info 查看docker的基本信息,有多少个容器、镜像什么的。
docker images 查看本机上的所有镜像。
REPOSITORY:仓库,也是镜像名称。
TAG:标签,也是版本号,镜像会有不同的版本号。
IMAGE ID:镜像id,根据这个id我们可以区分不同的镜像,也可以对某个镜像进行操作。
CREATED:创建时间。
SIZE:镜像的大小。
当前我的机器上没有一个镜像,显示如下:
docker pull <镜像名称>:[标签名称]:拉取镜像,默认不写标签名称拉取最新的镜像。
我们输入docker pull hello-world 拉取最新的hello-world镜像
docker run <镜像名称> :运行一个镜像,这时候就变成一个容器了,相当于new 一个Image了。下边我们run hello-world 运行这个镜像,输出以下信息:
至此,我们的docker的hello-world已经运行成功了,接下来我们来启动一个nginx服务器,来从外边访问我们容器里的nginx看看。
同样我们先拉取nginx镜像 docker pull nginx,然后等待拉取完成,运行nginx容器,docker run -p 8080:80 nginx
然后我们用自带的浏览器来访问8080端口,出现welcom to nginx 表示启动成功。
docker的使用方法基本就是上边这样了,接下来我们就可以看一下详细的参数。
3.3 docker的命令
3.3.1 镜像命令:
docker images 查看本机的镜像。
REPOSITORY:仓库,也是镜像名称。
TAG:标签,也是版本号,镜像会有不同的版本号。
IMAGE ID:镜像id,根据这个id我们可以区分不同的镜像,也可以对某个镜像进行操作。
CREATED:创建时间。
SIZE:镜像的大小。
docker rmi 删除本地的镜像,加上一个参数-f表示强制删除,因为有时候若有运行的相关容器的时候是不能删除的,如:docker rmi -f nginx 强行停止容器并删除镜像,不管是否有占用情况。
docker search 根据镜像名称搜索远程仓库中的镜像,可以看一下查到所有相关名称的镜像,可以选择我们要拉取哪个镜像,下边是搜索nignx相关的镜像,红色部分ok 说明是官方镜像。
docker pull <镜像名称>:[标签名称]:拉取镜像,默认不写标签名称拉取最新的镜像。
docker push 推送镜像,当我们制作了我们自己的镜像时,我们就可以推送到我们自己的docker hub上去。
3.3.2 容器命令
有了镜像我们就可以new一个镜像实例了,也就是我们所说的容器。
docker run :基于某个镜像运行一个容器,如果本地有这个镜像就根据本地的镜像创建,如果没有,就去远程拉取一个镜像再创建,参数如下:
-d:启动一个容器,后台运行,不会占用我们当前的控制台,一般都要加上,之前我们启动nginx没有指定这个参数,就会占用当前控制台,会一直挂起,有了这个命令就不会占用了。
-i:以交互模式运行容器,通常会和-t一起来使用(-it)。
-t:为容器也创建一个命令行窗口,是容器内容的命令行窗口,比如我们拉取一个ubuntu的镜像,我们想要在这个操作系统镜像里边执行一些命令,那就需要这个参数了。
-P:这个是大写的P,指定宿主机的随机端口映射到容器内部的端口。
-p:这个是小写的p,指定某个具体端口映射到容器内部端口,比如前边我们用-p 8080:80,就是让宿主机的8080端口映射到容器内的80端口,这样我们就可以在外部用8080端口访问我们容器内部的nginx了(默认容器必须有一个外部的映射端口,不然访问不了)。
-v:指定宿主机与容器内部的目录映射,就是之前的数据卷所需要的参数,好实现数据的持久化和同步。
--name="mynginx":为容器指定一个名称,如果没有指定,那就分配一个随机名称。
下面用参数重启启动我们的nginx镜像:docker run -itd -p 8848:80 --name="mynginx" nginx
docker ps 显示正在运行的容器,加一个参数-a 可以看到停止中的容器
docker stop 停止容器 。
docker kill 强制停止容器。
docker restart 重启容器。
docker rm 删除容器,删除后容器就不在了,就不能重启和停止了。
docker inspect 查看容器的详细信息。
以上就是差不多我们常用的命令,具体的其他更多的功能我们可以查看官方文档 https://docs.docker.com/get-started/。
四、Dockerfile
上边都是我们拉取别人的镜像,我们实际上也可以制作自己的专属镜像,根据我们的需要,配置好我们自己的镜像来使用。Dockerfile就是用来构建我们的镜像的文件,在里边可以写一些命令来构建我们的镜像,构建好后发布到docker hub就可以供别人拉取使用了。
Dockerfile是一个没有后缀名的文本文件,我们通过写入一些命令来实现镜像的构建。下面我们先来看看Dockerfile是怎样来编写的。下边的命令标识形式<> 代表需要的参数,[ ] 是可选参数。
FROM:指定基础镜像,所有构建的镜像都必须有一个基础镜像,且FROM命令必须是Dockerfile的第一个命令。
FROM <image> [AS <name>] 指定从一个镜像构建 AS 起一个新的镜像名字。
FROM <image>[:<tag>] [AS <name>] 指定镜像的版本tag。
例如:FROM mysql:5.0 AS database
MAINTAINER:镜像维护人的信息。
MAINTAINER <name>
例如:MAINTAINER haha lsdjfl@163.com
RUN:构建镜像时要执行的命令。
RUN <command>
例如:RUN ["executable", "param1", "param2"] RUN ["dotnet restore","*.csproj"]
ADD:将本地的文件添加复制到容器中去,压缩包会解压,可以访问网络上的文件,会自动下载。
ADD <src> <dest>
例如:ADD *.csproj /app 添加csproj文件到容器中的app目录下。
COPY:功能和ADD一样,但是只是复制,不会解压或者下载文件。
CMD:启动容器后执行的命令,和RUN不一样,RUN是在构建镜像是要运行的命令。当使用docker run运行容器的时候,这个可以在命令行被覆盖。
CMD ["executable", "param1", "param2"]
ENTRYPOINT:也是执行命令,和CMD一样,只是这个命令不会被命令行覆盖。
ENTRYPOINT ["executable", "param1", "param2"]
例如:ENTRYPOINT ["donnet", "myapp.dll"]
LABEL:为镜像添加元数据,key-value形式的。
LABEL <key>=<value> <key>=<value> <key>=<value> ....
例如:LABEL version="1.0" description="这是一个web应用"
ENV:设置环境变量,有些容器运行时会需要某些环境变量 比如:JAVA_HOME。
ENV <key> <value> 一次设置一个环境变量。
ENV <key>=<value> <key>=<value> <key>=<value> .... 设置多个环境变量。
例如:ENV JAVA_HOME /usr/java1.8/
EXPOSE:暴露对外的端口。
EXPOSE <port>
例如:EXPOSE 80
这里指的是容器内部程序的端口,虽然会和宿主机的一样,但是其实是两个端口,容器运行时,需要用-p 映射外部端口才能访问到容器内的端口。
VOLUME:指定数据持久化的目录,官方语言叫做挂载。
VOLUME /var/log 指定容器中需要被挂载的目录,会把这个目录映射到宿主机的一个随机目录上,实现数据的持久化和同步。
VOLUME ["/var/log","/var/test".....] 指定容器中多个需要被挂载的目录,会把这些目录映射到宿主机的多个随机目录上,实现数据的持久化和同步。
VOLUME /var/data var/log 指定容器中的var/log目录挂载到宿主机上的/var/data目录,这种形式可以手动指定宿主机上的目录。
WORKDIR:设置工作目录,设置完工作目录之后 ,上边的RUN、CMD、COPY、ADD等的工作目录都变成这个了。
WORKDIR <path>
例如:WORKDIR /app/test
USER:指定运行命令时所使用的用户,为了安全和权限起见,有的用户可能权限高,有的用户可能权限低,根据要执行的命令选择不同的用户。
USER <user>:[<group>]
例如:USER test
ARG:设置构建镜像是要传递的参数。
ARG <name>[=<value>]
例如:ARG name=sss
以上是差不多构建Dockerfile时所使用的命令的,构建时命令是从上到下顺序执行的,可能后边的命令需要前边命令的结果,使用 docker build 编译Dockerfile 就可以构建我们的镜像了。
docker build 可以使用参数 -f 指定Dockerfile的目录,默认是在当前目录下找;-t 指定构建的镜像的名称和标签,例如:docker build -f ./aa/bb -t myimage:1.0
五、构建我们自己的镜像
有了上面的基础,我们就可以构建我们自己的镜像了,然后还可以上传到我们自己的docker hub上供别人拉取使用。接下里我们就来实现以下。这里参考了官方的构建步骤:https://docs.docker.com/engine/examples/dotnetcore/
5.1 创建一个mvc项目
首先找一个目录,创建一个文件夹DockerDemo,然后在该目录下执行命令dotnet new mvc,创建我们的mvc程序,如下:
5.2 添加Dockerfile文件
我们直接添加一个名称为Dockerfile的文件,然后输入一下命令:
#构建sdk镜像,是为了进行编译、发布我们的web应用
FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env
# 在容器中设置一个/app目录
WORKDIR /app
# 复制csproj文件 到当前目录下(app目录下) 并执行dotnet restore 还原包
COPY *.csproj ./
RUN dotnet restore
# 再复制其他剩余的文件到当前目录,执行发布命令,用Release模式 并且输出到out文件夹
COPY . ./
RUN dotnet publish -c Release -o out
#构建运行时镜像
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2
# 设置运行时镜像一个/app目录
WORKDIR /app
# 复制之前的镜像的产物中的 /app/out目录下的文件 到当前新镜像的 /app录下(当前目录)
COPY --from=build-env /app/out .
# 执行dotnet 命令,运行我们的web应用
ENTRYPOINT ["dotnet", "DockerDemo.dll"]
我用vscode添加Dockerfile直接变成了一个鲸鱼的图标,说明是一个Docker文件,还有高亮:
5.3 构建Docker镜像
我们直接把DockerDemo这个文件夹直接复制到虚拟机中,我复制到了Desktop下:
然后进入到DockerDemo目录下,直接执行命令 docker build -t dockerdemo . ,这句命令的意思是 使用当前目录下的Dockerfile构建镜像dockerdemo,注意构建的镜像名称必须为小写,否则会报错。
然后就是等待构建了,可能会比较慢, 如果想快的话,可以换成国内的镜像源,这里就不说了,因为我也懒得换。
这次构建过程会分为两步,因为有两个镜像,构建完成后,我们使用docker images 查看,会发现有我们新构建的镜像dockerdemo:
5.4 运行容器
接下来我们输入命令 docker run -d -p 8848:80 --name myapp dockerdemo,运行一个容器,名称叫做myapp
使用docker ps 查看运行中的容器:
5.5 访问我们的mvc程序
在虚拟机的浏览器中输入 http://localhost:8848,或者在你的电脑上输入虚拟机的地址来访问 http://192.168.226.130:8848/ (这个是我的地址),显示如下,表名成功:
六、发布镜像
我们已经构建好了我们的镜像,接下来我们就把镜像推送到我们自己的docker仓库中去,这一步和github是完全一样的,就像把我们的代码推送到github上一样。
6.1 创建账号
首先,我们需要创建一个dockerhub账号,登录https://hub.docker.com/,根据提示注册账号,这一步就不细说了。
6.2 登录
命令行输入docker login,输入我们的账号密码,进行登录。
6.3 推送镜像
推送镜像之前,首先要标记我们的镜像,也就是给我们的镜像起一个名字,使用命令 docker tag <image> <username>/<repository>:<tag>,下边我们就标记我们刚才的dockerdemo的镜像:
使用命令 docker tag dockerdemo emmaccc/dockerdemo:1.0.0
查看我们刚才标记过的镜像:
使用命令 docker push emmaccc/dockerdemo:1.0.0 推送镜像,等待一段时间后推送成功,就可以在自己dockerhub账号上看见了
6.4 使用镜像
推送成功之后,我们就可以在任意一台安装了docker的计算机上来拉取我们镜像,直接run启动我们的web应用了。
docker run -p 4396:80 emmaccc/dockerdemo:1.0.0,运行之后,无需担心各种依赖、配置,也不用在你的新主机上安装任何东西,直接run运行就行了,这也docker独特的魅力所在。
六、总结
学习docker,前前后后看了好几遍,究竟是个什么玩意啊,好半天才看懂,docker的基本使用就差不多了,接下来估计又是k8s了,唉,东西真多,感觉全it界都在造*吧,非要搞这个搞那个,没办法,还是得跟着大佬们走,如果你真的还不明白,那就送你一句话。
web放tomcat里,tomcat放docker里,docker放k8s里,k8s放操作系统上,绕了一大圈,扯淡呢,咋不直接web放操作系统上直接运行呢