深入浅出,白话文Docker入门,万字笔记
深入浅出 白话文Docker入门笔记
-
- 1.什么是Docker:
-
- 1.1 传统项目上线模式:
- 1.2 虚拟机:
- 1.3 Linux 容器:
- 1.4 Docker:
- 2.Docker初体验:
-
- 2.1 Docker三要素:
-
- 2.1.1 镜像(Image):
- 2.1.2 容器(Container):
- 2.1.3 仓库(repository):
- 2.2 Docker的安装:
- 3.Docker常用指令:
- 4.Docker简单入门案例:
-
- 4.1 使用Docker:
- 4.2 docker commit更新镜像:
- 4.3 本地镜像发布到阿里云(了解即可):
- 5.Docker容器数据卷:
-
- 5.1 容器数据卷的定义:
- 5.2 容器数据卷的继承:
- 6.DockerFile:
-
- 6.1 DockerFile的定义:
- 6.2 DockerFile的编写:
-
- 6.2.1 DockerFile编写规范:
- 6.2.2 保留字指令介绍:
- 6.2.3 DockerFile案例演示:
- 7.实战:本地微服务构建Docker镜像(最重要):
-
- 7.1 准备工作:
- 7.2 创建镜像并启动测试:
- 7.3 查看docker内应用的日志:
-
- 7.3.1 docker logs(推荐):
- 7.3.2 docker attach:
现在互联网行业大批人员涌入,鱼龙混杂。其中也不乏很多自身硬件条件不好(学历,年龄等)但确非常努力的朋友。
也许你实力非凡,但可能就因为一纸文凭,一句年纪太大将你拒之门外。怎么办呢?
随着人员的大量涌入,程序员不仅仅只是程序员了,往往还要担任“前端”,“运维”等等多重身份。对我们而言,也许我们在其他方面比不上我们的竞争对手,但我们可以沉下心来钻研,在工作中慢慢独当一面。机会是留给有准备的人的。
我希望我的分享能够帮助到更多和我一样在内卷中迷茫的但努力的小伙伴。
其实很多伙伴在工作中接触到Docker的机会并不多,甚至接触linux的机会都很少。但为什么我们要花时间来学习Docker呢。
拿起你的Boss、智联。看看现在的招聘要求,对于中高级程序员来说十个有九个会要求你掌握Docker,尽管你用不到! 所以,其实有时候不是我们愿意来学习这些知识,实在是被时代的洪流推着走。 身后有家庭,孩子,我们没有理由不踏出舒适圈。 哪怕就只是为了面试,为了投机。 我们还要潜下心来,认认真真地把Docker收拾了。
相关链接:
Docker常用指令,包含解释.
1.什么是Docker:
1.1 传统项目上线模式:
在了解Docker之前,我们先回忆一下我们辛辛苦苦写的代码最后是如何上线使用的。
如上图所示,在正规的开发流程中至少会存在:
- 开发环境:开发人员提交本地代码后的环境。
- 测试环境:测试人员测试使用的环境。
- 生产环境:项目上线所使用的环境。
如此众多的环境对于运维和开发人员来说都是极为头疼的! 为什么呢? 举个例子:
代码在开发环境跑的好好的,一部署到测试环境上就跑不起来了。 这往往就是因为不同环境的准备工作有差异。
例如: 在开发环境使用的是redis 3.0版本,但运维人员在搭建测试环境服务器的时候不注意,使用了redis1.0版本。版本差异导致代码报错。
或者: 两个环境的Maven版本或配置不同,导致不能获取到jar包。
看的出来,传统的项目部署方案是非常恼火的。不能保证每个服务器的配置、环境等等因素完全一样。排查起来也是困难重重。 所以人们就会想:
既然代码在开发环境跑得起来,那我能不能把开发环境的整体(例如配置、MySQL、Redis、MQ等等)一起打包起来部署到其他的环境呢?
1.2 虚拟机:
玩过虚拟机的人很容易就会想到虚拟机这个东西。 比如我的程序在服务器A上面运行的很好。为了避免程序在其他的服务器运行中存在配置、环境、版本等等的兼容问题。 好! 我就把服务器A整个打包,形成一个虚拟机镜像(iso文件)。
拿到这个镜像文件,通过虚拟机技术,我去完完全全赋值一个服务器B。那么这样就保证两个服务器的两个环境肯定是一摸一样的。
但这样也会存在缺点。
1. 虚拟机开销大。每生成一个虚拟机就等于生成了一个电脑,占用了大量的硬盘空间和运行内存。
2. 启动慢,不利于快速扩展。
3.体积大。
1.3 Linux 容器:
Linux 容器和虚拟机不同,Linux 容器不是一个完整的操作系统(电脑),而是对进程进行隔离。
或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。
Linux 容器完美地解决了虚拟机带来的所有缺点。
1.4 Docker:
我们的Docker就是 Linux 容器的一种最流行的实现方式。
Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
2.Docker初体验:
要使用Docker,首先要知道,Docker是一个Linux容器,必须依赖Linux内核才能运行。
Docker实质上是在已经运行的Linux下制造一个隔离的运行环境。因此我们需要准备一个
Linux服务器(CentOS7以上)
查看服务器版本:
cat /etc/redhat-release
我们这里使用的是7.6.18的CentOs。
2.1 Docker三要素:
2.1.1 镜像(Image):
Docker镜像就类似于一个安装包。比如我们去游民星空上面下载一个孤岛惊魂的游戏,我们本地会得到一个游戏的安装包。运行安装包,游戏就可以直接运行起来了。
一个镜像可以多次运行,例如我们现在有一个Redis的镜像。运行一个就会生成一个容器,Redis就在这个容器中运行。 那如果我们现在要搭建Redis集群。我们也可以再次运行镜像,启动多个容器!
一句话:Docker镜像就是我们Java中的类模板,一个类模板可以new出多个对象出来。
2.1.2 容器(Container):
Docker容器就是我们的应用实际的运行环境。通过镜像产生,是镜像的一个运行实例。 容器可以被启动、暂停、停止、删除等等。 每个容器都是相互隔离的。
一句话:Docker容器就是(一个简易版的Linux系统+应用程序)的运行环境。
2.1.3 仓库(repository):
Docker仓库就是存放镜像的地方。
仓库分为:
- 公开库:如阿里云
- 私有库:如我们自己公司的镜像仓库
一句话:Docker仓库就类似Maven仓库,存放各种镜像资源的地方,如(MYSQL、Redis等等),供我们随时
拉取镜像资源
2.2 Docker的安装:
- 安装gcc,gcc是什么不赘述了
yum -y install gcc
- 安装所需要的软件包:
yum install -y yum-utils
- 设置远程仓库地址,我们这里设置阿里云的仓库(国外的仓库太慢)
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
- 可选,优化yum安装索引
yum makecache fast
- 安装docker
yum install docker-ce docker-ce-cli containerd.io
- 启动docker
systemctl start docker
- 查看docker版本
docker -v
3.Docker常用指令:
全靠孰能生巧。这里没有理论性的东西。常用指令:Docker常用指令,包含解释.
后面的案例中不会有指令的解释。请熟悉一下基本的docker指令。
4.Docker简单入门案例:
4.1 使用Docker:
好。如果对docker的基础指令稍微熟悉了之后。接下来就安排一个入门的案例。
假如部门经理今天对你说,小易啊,你去服务器上部署一个redis服务,5分钟后我来看。
5分钟。如果按照以前的方式:先去官网下载适合我们版本的redis压缩包,然后把压缩包传到我们的服务器,然后解压,启动。。。。
来得及吗?如果你非要说:”我动作快!来得及!“。 那如果是让你5分钟内在三台服务器上启动同一个版本的Redis呢,你还来得及吗?
好。docker的优势就体现出来了。
- 拉取适合我们的Redis镜像文件:
docker pull redis:6.0.8
- 以后台启动的方式启动Redis容器:
docker run -d redis:6.0.8
- 查看redis运行情况:
ps -ef|grep redis
Redis搭建完成。 20秒最多。
4.2 docker commit更新镜像:
例如,我们现在有一个Ubuntu系统的容器,是从公有库拉下来的一个基础镜像运行的容器。
我们发现这个Ubuntu系统没有vim编辑功能。但我们现在需要一个有vim编辑功能的Ubuntu镜像。
怎么办呢? 难道要每个容器启动之后去手动的安装吗?
这个时候commit就出来了。 什么是commit,做过java开发的大家应该都知道git吧,我们利用git提交代码的时候是不是就是commit命令啊。
做法:
1. 进入原始的Ubuntu系统容器
2. 安装Vim组件
3. 安装完成后,当前容器就是一个包好Vim命令的容器了,容器id为”123456“(举例而已)
4. 通过这个容器,生成镜像:
docker commit -m =描述信息 -a=作者 容器ID 要创建的目标镜像名:版本号如: docker commit -m ="易柏州专属镜像" -a="易柏州" 123456 myImage:1.0
好。 这个时候我们就会发现多了一个名为”myImage“的新镜像。 这个新镜像就是我们的自定义镜像,包含了vim命令的Ubuntu镜像!
一句话:类似Java的类继承,扩展父类,形成了全新的、更实用的对象
4.3 本地镜像发布到阿里云(了解即可):
好!我们刚才生成了一个我们自己的镜像。但是我们的同事也想用这个镜像,怎么办呢?
回想一下我们是怎么通过git提交代码的。 首先commit 到本地,然后Push到远程库。 这样别人就可以从远程库拉到我们的代码了。
所以我们现在要做同样的事情,将我们本地的镜像,推送到远程库,供别人拉取使用。
我们现在想把刚刚创建的镜像:myImage (镜像id:888888) 推送到阿里云
- 先登录账号(找你们公司运维要)
docker login --username=ybz registry.cn-xxxxx.aliyuncs.com
输入密码
- 预推送
docker tag 镜像ID registry.cn-xxxxx.aliyuncs.com/包名/镜像名:版本号例如: docker tag 888888 registry.cn-xxxxx.aliyuncs.com/ybz/myImage:1.0
- 推送
docker push registry.cn-xxxxx.aliyuncs.com/ybz/myImage:1.0
以上的 registry.cn-xxxxx.aliyuncs.com 路径也由你们公司的运维人员提供。
5.Docker容器数据卷:
5.1 容器数据卷的定义:
我们来思考一个问题。假如现在我们把自己的一个springboot项目已docker的方式打包成了镜像。
那么里面肯定包含了很多重要的配置文件信息,比如:服务端口,数据库地址,验证密钥。。。。
这些重要数据和文件我们称之为重要文件,我们一般会将这种文件持久化到我们的本机服务器本地作为备份。 这个备份就叫做容器数据卷。
目的:
- 保护容器内重要数据,以免丢失。
- 可动态修改数据卷,由docker挂在数据卷到容器内运行。例如端口号,我们可以在本地数据卷进行修改,对应的docker容器挂载启动后的服务的端口也会随之改变。
具体怎么用呢?
docker run -it --privileged=true -v /本机数据卷绝对路径:/容器内对应目录 镜像名解释:--privileged=true:开启权限 -v:挂载
是,是 看球不懂。
接下来直接上案例,案例还是使用Ubuntu的镜像。
- 建立本机和目标容器的数据卷关联:
docker run -it --privileged=true -v /tmp/host_data:/tmp/docker_data ubuntu
- 我们进入到容器内,去到/tmp/docker_data目录下,发现什么都没有。
- 但我们如果在这里新建了文件,那么在我们本机的/tmp/host_data目录下就会对应生成文件。
- 反之亦然。本机的文件也会同步到容器中
- 那么如果我们想要控制读写权限:容器只能读不能写,本机才能写:
docker run -it --privileged=true -v /tmp/host_data:/tmp/docker_data:ro ubuntu解释:ro(readOnly)容器内只读权限
5.2 容器数据卷的继承:
比如,我现在有这样的需求。 多个容器都使用一个挂载路径和其中的数据内容。 我们不会每个容器都去创建这个挂载关系。而是:
- 容器1挂载了本机。
- 容器2去挂载容器1。
docker run -it --privileged=true --volumes-from 容器1名称 镜像名
6.DockerFile:
想要知道怎么样把我们的java程序已docker方式运行。DockerFile是必学的。
6.1 DockerFile的定义:
DockerFIle是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本文件。
DockerFile的创建和使用遵循以下的步骤:
- 编写DockerFile文件。
- docker build 构建镜像。
- docker run 运行镜像。
6.2 DockerFile的编写:
6.2.1 DockerFile编写规范:
这里你可以理解为写代码。 写代码要遵循一定的规范。
- 每条保留字指令都必须为大写字母且后面要跟至少一个参数。
- 指令从上到下依次执行。
- #符号表示注释。
- 每条指令都会创建一个新的镜像层并对镜像进行更新。
6.2.2 保留字指令介绍:
保留字 | 解释 |
---|---|
FROM | 通常出现在DockerFile第一行,表示我当前的镜像是来自与哪里,指定一个已经存在的基础镜像。 |
MAINTAINER | 镜像维护者的姓名和邮箱地址 。 |
RUN | 镜像构建时(docker build)需要执行的命令。例如RUN yum -y install vim,在构建时会执行这条指令,安装vim。 |
EXPOSE | 当前容器对外暴露的端口。 |
WORKDIR | 指定在容器创建后,主机进入容器的默认工作目录,如/opt/。 |
USER | 指定改镜像以哪个用户去执行,如果不指定默认为root。通常不指定 |
ENV | 用于在构建镜像的时候设置环境变量。 如 ENV mydata /usr/local ,把/usr/local这个路径赋值给mydata作为环境变量,可以在其他地方通过$mydata 引用此变量。 |
ADD | 将本机的文件拷贝到镜像中并自动处理路径以及解压.tar压缩包。 |
COPY | 类似ADD,拷贝文件和目录到镜像中,但不会解压压缩包。 |
VOLUME | 容器数据卷,用于数据保存和持久化。 |
CMD | 指定容器启动后要做的事情。DockerFile可以用多个CMD,但只有最后一个生效,CMD会被docker run 后面的参数替换。 和RUN的区别:RUN是在docker build的时候运行;CMD是在docker run的时候运行。 |
ENTRYPOINT | 类似CMD,但ENTRYPOINT不会被docker run 后面的参数覆盖,而且这些命令行参数会被当成参数传递给ENTRYPOINT指令指定的程序。 如果ENTRYPOINT和CMD一起使用,CMD的含义就发生了变化,不会直接运行而是将CMD的内容传递给ENTRYPOINT。![]() |
看不懂没关系,下面开始案例
6.2.3 DockerFile案例演示:
现在我们利用一个最原始的centos镜像,通过DockerFile,创建我们自己的一个镜像:
centos+ vim命令 + ifconfig命令(原始的centos镜像不带这两个命令)+ jdk1.8。
- 在本机服务器下选择一个路径,创建Dockerfile文件(D字母要大写)
vim Dockerfile
- 编写Dockerfile文件,我这里直接把编写好的展示出来,逐行解释:
#FROM哪个基础镜像,这里我们选我们本机已经存在的centos镜像FROM centos#用户,邮箱MAINTAINER ybz<xxxxxxx@qq.com>#定义环境变量ENV MYPATH /usr/local#使用环境变量,WORKDIR 设置进入容器后的默认目录WORKDIR $MYPATH#修改yum远程库为阿里云RUN cd /etc/yum.repos.dRUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*#安装vimRUN yum -y install vim#安装ifconigRUN yum -y install net-tools#安装jdk1.8及lib库RUN yum -y install glibc.i686RUN mkdir /usr/local/java#把本机的jdk压缩包添加到容器中,压缩包和Dockerfile必须在同一位置。 ADD jdk-linux-x64.tar.gz /usr/local/java#配置java环境变量:ENV JAVA_HOME /usr/local/java/jdkENV JRE_HOME $JAVA_HOME.jreENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATHENV PATH $JAVA_HOME/bin:$PATH#对外暴露的端口EXPOSE 80#echo:控制台打印CMD echo $MYPATHCMD echo "----------------success-------------"#伪终端参数,非必须CMD /bin/bash
- 构建(在Dockerfile目录下)
命令公式:
docker build -t 新镜像名字:TAG .注意:TAG后面有一个空格和一个.如:docker build -t mycentos:1.0 .
好! 如果你跟着做到这一步,我们来看看成功后,控制台的效果:
恭喜,看到这样的效果就是构建成功了。
然后我们docker images 看看本地镜像库有没有我们刚刚自己构建的镜像?
我们启动这个镜像然后进去看看jdk,vim,ifconfig有没有安装成功!
答案肯定是成功的,这里我就不截图了,到这里为止,Dockerfile的基本使用大功告成。
但我们还没有演示最关键的一步,怎么把我们自己的java/SpringBoot/SpringCloud项目打成Docker镜像运行呢?
好人做到底,送佛送到西。上案例!
7.实战:本地微服务构建Docker镜像(最重要):
7.1 准备工作:
上图是我本地的一个微服务架构的项目。 我用其中的docker_demo模块做为演示,简单看一下docker_demo模块的样子:
就是一个简单的Springboot项目。 一个Controller,一个启动类,一个appllcation配置文件。
- 端口:
server: port: 8888
- 接口:
@Controllerpublic class TestController { @RequestMapping("testDocker") @ResponseBody public String item(){ System.out.println("testDocker"); return "testDocker"; }}
- 效果:
我们启动项目后,浏览器访问localhost:8888/testDocker。
- 确认项目功能无误之后,打jar包。
- 使用xftp将此jar包传到服务器上
- 拉取一个基础镜像:java:8 (作为我们自定义镜像的模板)
docker pull java:8
7.2 创建镜像并启动测试:
以上的工作做好之后,我们就要利用这个jar包,创建我们自己的镜像,启动并访问。
- 编写Dockerfile
在jar包所在的目录下创建Dockefile文件:
# 基础镜像java:8FROM java:8# 作者MAINTAINER ybz# 挂载目录,指定临时文件目录为/tmp,在本机的/var/lib/docker路径下创建了一个临时文件并关联到容器的/tmpVOLUME /tmp# 将jar包添加到容器,并更名为my.jarADD docker_demo-0.0.1-SNAPSHOT.jar my.jar# 运行jar包RUN bash -c 'touch /my.jar'ENTRYPOINT ["java","-jar","my.jar"]# 对外暴露的服务端口号EXPOSE 8888
- 构建镜像:
docker build -t my_application:1.0 .
- 查看现在本地镜像,有没有我们自定义的myApplication:
- 启动镜像:
docker run -p 8888:8888 my_application:1.0
如果出现这个场景,恭喜,启动成功。
- 测试:
方法1: 使用curl模拟接口请求
curl 127.0.0.1:8888/testDocker
返回testDocker字符串,测试成功。
方法2:用浏览器访问,这里不截图了。
7.3 查看docker内应用的日志:
7.3.1 docker logs(推荐):
docker logs [options] 容器ID
options | 默认值 | 描述 |
---|---|---|
- -details | 显示更多信息 | |
-f, - -follow | 终端挂起,实时更新日志 | |
- -since string | 显示自某个timestamp之后的日志,或相对时间 如- -since=“2022-02-18” | |
- -tail string | all | 从日志末尾显示多少行日志 |
-t | 显示时间戳 | |
- -until string | 显示自某个timestamp之前的日志,或相对时间 如- -until =“2022-02-18” |
7.3.2 docker attach:
docker attach [options] 容器ID
会连接到正在运行的容器,然后将容器的标准输入、输出和错误流信息附在本地打印出来。命令中options的取值有三种:
- -detach-keys
- -no-stdin
- -sig-proxy。
该命令只是进入容器终端,不会启动新的进程。所以当你同时使用多个窗口进入该容器,所有的窗口都会同步显示。如果一个窗口阻塞,那么其他窗口也就无法再进行操作。
使用ctrl+c可以直接断开连接,但是这样会导致容器退出,而且还stop了。如果想在脱离容器终端时,容器依然运行。就需要使用–sig-proxy这个参数。(不推荐使用)
好了,个人认为对于一个开发工程师,了解这些内容就够了。其他的更深更底层的原理和其他的用法,这里就不多赘述了。 此篇文章仅供入门参考,如果有纰漏或错误请留言,我会及时改正,谢谢。