学习Docker 已经一周了,大致的学会了怎么创建镜像、创建容器、运行容器等,做个总结。
容器
什么是镜像?
在知道什么是容器前,先理解下什么是镜像。
下面这段是使用翻译工具从官方文档翻译过来的内容
映像是一个只读模板,其中包含创建 Docker 容器的说明。通常,一个图像基于另一个图像,并带有一些额外的自定义。例如,您可以基于该映像构建一个映像ubuntu ,但安装 Apache Web 服务器和您的应用程序,以及使您的应用程序运行所需的配置详细信息。
您可以创建自己的图像,也可以只使用其他人创建并在注册表中发布的图像。要构建您自己的镜像,您需要使用简单的语法创建一个Dockerfile ,用于定义创建和运行镜像所需的步骤。Dockerfile 中的每条指令都会在映像中创建一个层。当您更改 Dockerfile 并重建映像时,仅重建那些已更改的层。与其他虚拟化技术相比,这是使映像如此轻量、小巧和快速的部分原因。
这里面有一段话要记住 “一个图像基于另一个图像,并带有一些额外的自定义。”, 在后面创建镜像时,编写Dockerfile 文件的第一句话 FROM xxx
就是这个的解释。
因为创建镜像时是要基于另外一个镜像来创建的,这里的 xxx 可以是 centos:7 或者其他的自己本机/docker hub 上存在的镜像。
如果你本机生没有这个镜像他会去docker hub 上下载的。
除了官网的文档,还查阅了其他博客或者技术论坛的解释,有的解释的比较深入讲到了底层实现,有的解释的比较深奥吧,作为初学者的我,有些词汇不太理解。下面是我目前对镜像的一个大致的理解,如果有误差可以评论提出来,因为这只是我个人的理解。
我理解的镜像按现实当中的比喻,将镜像比作一个“模板”。拿简历模板来做比喻,我们从网上下载一个简历模板(从docker hub 上拉去了一个镜像) ,模板的样式、要填写的选项(姓名、联系方式、学校、项目经验...)是固定的,然后我们基于这个模板可以增加自己的一些选项比如 “个人简介” 或者修改模板的样式 ,然后作为新的模板再发布到网上给别人去下载(这个相当于创建了新的镜像并发布到Docker hub 上)。这个过程就是镜像的制作和发布
容器是什么?
好了,理解镜像的概念后可以开始了解容器时什么了?
下面这段也是使用翻译工具从官方文档翻译过来的内容
容器是图像的可运行实例。您可以使用 Docker API 或 CLI 创建、启动、停止、移动或删除容器。您可以将容器连接到一个或多个网络,将存储附加到它,甚至可以根据其当前状态创建新映像。
默认情况下,一个容器与其他容器及其主机的隔离相对较好。您可以控制容器的网络、存储或其他底层子系统与其他容器或主机的隔离程度。
容器由其映像以及您在创建或启动它时提供给它的任何配置选项定义。当容器被移除时,任何未存储在持久存储中的状态更改都会消失。
在我刚开始学习容器时看着上面的解释感觉头大,我还不理解镜像是什么,就直接试图理解什么是容器,翻阅资料总感觉理解的不到位。大多数的解释是下面几种:
- 容器是一个进程
- 容器和虚拟机类似
- 容器是沙箱子
这对于一个没有接触过容器的小白来说理解起来很痛苦。所以我还是先把这些概念简单化的理解下(因为是个人理解,可能get不到其他人的点):
举两个例子:
- 还是拿简历模板来说,模板比作镜像,那么使用这个镜像的容器 比作是一个人在做某一件事(相当与一个进程)。当然不同的模板针对不同的行业,填写的内容选项也就不同(不同的镜像,执行的内容不同)。当人拿到这个模板后,里面的内容选项(姓名、联系方式、学校、项目经验...)位置是固定的(相当于通过挂载目录来给这些内容留填写框),但是这这些位置上的内容任你填写,填写完成后你再将简历发布到网上(指定好参数,运行容器),当你要修改时重新再发布一次就行了(修改挂载目录下的内容重启容器),然后简历就制作完成了,之后只需要留意电话或者邮件等待消息(这个进程一直再后台运行)。
- 拿平常的后端部署来说,Java 部署先是打包成jar 包,然后上传到服务器上运行命令启动服务。但是运行服务需要JDK,同时运行这个服务还需要mysql 、redis 等一些工具。这时你制作了一个镜像,这里面将mysql、redis、jdk 等都集成了,你只需要把jar 放到指定的挂载目录下,然后运行容器时指定你制作好的镜像,项目就部署成功了。同时因为容器的隔离特性,当你运行第二个项目时,JDK、Mysql、Redis 版本都不同,只需要制作一个符合第二个项目运行的镜像,两个项目也能同时运行互不冲突。
最后总结一下,我理解的容器和镜像的关系一个是模板提供者,一个是模板使用这。
制作第一个镜像
好了,知道容器和镜像的概念后 开始制作第一个镜像。
首相我们要知道,制作镜像的文件叫做 Dockerfile, 这个文件的编写就是执行 Linux 的命令
以下拿一个JDK环境的镜像做例子。
# 指定该镜像基于 centos7制作
FROM centos:7
# 作者
MAINTAINER kafeim "373431485@qq.com"
# RUN Linux命令
RUN mkdir /java
# 同RUN一样作用大致一样,区别在于不会创建 image 新增
CMD echo "创建Java目录"
# 复制命令
COPY openjdk-11_28_linux-x64_bin.tar.gz /java
CMD echo "复制JDK"
RUN cd /java && tar -zxvf openjdk-11_28_linux-x64_bin.tar.gz
CMD echo "解压jdk"
RUN rm -rf openjdk-11_28_linux-x64_bin.tar.gz
CMD echo "删除jdk压缩包"
# ENV 用于设置环境变量
ENV JAVA_HOME=/java/jdk-11
ENV CLASSPATH=$:CLASSPATH:$JAVA_HOME/lib/
ENV PATH=$PATH:$JAVA_HOME/bin
CMD echo "设置jdk环境变量"
CMD /bin/bash
写好这个文件后,运行命令制作镜像
docker build -f dockerfile_11 -t java:11 .
-f 指定要运行的Dockerfile文件
-t 镜像的名称 这里面 : 冒号后面跟着的时标签
最后面的 "." 表示工作目录。 比如你当前所在的目录是 /home 这个命令 COPY openjdk-11_28_linux-x64_bin.tar.gz /java
表示将当前目录也就是 /home 这个目录下的openjdk-11_28_linux-x64_bin.tar.gz 复制到容器里面的 /java 目录下。 如果将 “.” 改成 “/” 的话 那么里面的命令也要修改 COPY home/openjdk-11_28_linux-x64_bin.tar.gz /java
好了运行命令成功后镜像也就制作完成了,这个时候运行 docker images
查看当前的镜像
可以看到,centos:7 的镜像 docker 帮我们下载到本地 ,java:11 是我们自己制作的镜像。
Dockerfile命令参考
可以使用Google翻译来协助阅读
运行第一个容器
这里是我运行的一个博客网站做例子。
Dockerfile 文件
FROM java:11
ADD *.jar /root/.halo/
CMD /java/jdk-11/bin/java -server -Xms256m -Xmx256m -jar /root/.halo/application.jar
基于创建的 java:11 的镜像创建一个新的镜像 并且把jar 放到容器里面 /root/.halo/ 目录下,然后再启动服务
创建镜像:
docker build -f dockerfile_halo -t halo:1 .
运行容器:
docker run -it -d --name halo -p 8090:8090 -v $(pwd):/root/.halo --restart=unless-stopped halo:1 /bin/bash
-d
表示以隔离模式启动容器
--name halo
指定容器的名称
-p 8090:8090
这个表示将宿主机的8090端口映射到容器的8090端口
-v $(pwd):/root/.halo
(pwd) 表示的就是 /home 。 这一段的意思是把 当前目录挂在到容器里面的 /root/.halo 目录下
--restart=unless-stopped
重启策略这里指定,在容器非正常退出时(退出状态非0),才会重启容器
halo:1
最后的这一段表示指定启动容器的镜像
/bin/bash
容器启动以后,内部第一个执行的命令。这里是启动 Bash,保证用户可以使用 Shell。有时候容器一启动就变成了重启状态,查资料说是因为容器必须要运行一个进程,避免陷入重启状态这个还是有必要加的。
当容器启动成功后,后面的部署只需要将挂在目录下的 application.jar 替换掉 再运行重启容器命令就行了。
需要注意的时,dockerfile 文件里面的ADD *.jar /root/.halo/
命令 ,如果运行启动命令时挂载了容器里面 “/root/.halo/”目录,那么需要你自己将 jar 上传到挂载的目录里面。如果没有挂载该目录,则会将镜像中添加的jar 放到容器里面 “/root/.halo/”目录下
下面时常用的命令
查看运行的容器:
docker ps
查看所有的容器:
docker ps -a
进入容器:
docker exec -it halo /bin/bash
停止容器:
docker stop halo
重启容器:
docker restart halo
查看日志:
docker logs --tail 200 -f halo
删除容器(删除容器前要先停止容器):
docker rm halo
查看镜像:
docker images
删除镜像(删除镜像前先删除使用该镜像的容器):
docker image rm 镜像的ID(通过查看镜像命令查找)
写文章对于目前的我来说只是知识梳理,留下进步的脚印,让自己能记得更加深刻一点。
同时也希望对其他人有一些帮助,如果里面有什么不对的欢迎指出来大家可以一起讨论。
博客地址:http://kafeim.cn/