一、docker简介
1、为什么会出现docker?
一般情况下是开发人员开发好代码,本地测试通过后,打成jar包或者war包,交给运维人员,部署到服务器上。就是这么个过程,经常会出现如下场景:
- 运维:哥们,你这代码不行啊,跑不起来;
- 开发:怎么可能,你看我本地是可以的;
- 运维:你过来看啊,服务器上就是不行;
- 开发:我的代码怎么可能有问题,你会不会玩?
……
一样的代码,本地可以跑,服务器上就不行,那这就是环境的问题和配置的问题了。而且,一个产品从开发到上线,往往有开发环境,测试环境,仿真环境和生产环境,每个环境我们都需要安装一遍mysql、redis、nginx,activemq等,运维的工作量也挺大,而且都是重复的工作。为了解决这些痛点,docker就出现了。
欢迎大家关注我的公众号 javawebkf,目前正在慢慢地将文章搬到公众号,以后和公众号文章将同步更新,且上的付费文章在公众号上将免费。
2、是什么?
- 官网:https://www.docker.com
docker就是一个容器,一次构建,处处运行。也就是说,我开发环境安装了mysql、redis,我可以直接将这两个镜像搬到测试环境,开箱即用,而不用重新去配置。
3、虚拟机技术和容器技术:
- 虚拟机:相当于一台电脑,模拟了一整套完整的操作系统。缺点是启动慢,冗余步骤多,占用资源多。
- 容器技术(docker):不是模拟完整的操作系统,而是对进程进行隔离,对可以公用的不进行模拟。因此系统变得轻量,启动也是秒级的。
4、docker的核心概念:
容器:就是docker的logo鲸鱼背上那一个个地集装箱。一个集装箱就是一个容器,比如你在docker上要安装redis、mysql、jdk,那么就需要三个集装箱,也就是三个容器。
镜像:用来生成容器实例的东西
仓库:存放镜像的地方。有个叫docker hub的网站,它就是仓库。不过国内访问docker hub特别慢,国内一般用阿里云和网易云的仓库。
二、docker的安装
1、安装前提:
- centos 6.5或者更高版本
- 如果是centos 6.5,要求系统64位,内核版本2.6.32-431或更高
- 如果是centos 7,要求系统64,内核版本3.10或者更高
- 查看系统版本命令:
cat /etc/redhat-release
- 查看系统内核版本的命令:
uname -r
2、docker的安装:
centos 6安装docker:
yum install -y epel-release
yum install -y docker-io
- 安装后的配置文件:
/etc/sysconfig/docker
- 启动docker服务:
service docker start
- 验证版本信息:
docker version
,出现版本信息说明安装成功。
centos 7安装docker:
- 官网文档:https://docs.docker.com/engine/install/centos/
- 安装gcc相关:
yum install -y gcc
,yum install -y gcc-c++
,安装完执行gcc -v
有版本信息就安装成功。 - 卸载旧版本docker:
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
- 安装需要的软件包:
yum install -y yum-utils
- 设置stable镜像仓库(推荐用阿里云的库):
yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
- 更新yum软件包索引:
yum makecache fast
- 安装docker ce:
yum install -y docker-ce
- 启动docker:
systemctl start docker
- 测试:
docker -v
,有版本信息则安装成功,再执行docker run hello-world
,会有 hello from docker的信息。 - 镜像加速配置:
mkdir -p /etc/docker
vim /etc/docker/daemon.json
systemctl daemon-reload
systemctl restart docker
daemon.json的内容如下:
# 网易云
{"registry-mirrors":["http://hub-mirror.c.163.com"]}
# 阿里云
{"registry-mirrors":["你阿里云的镜像加速链接"]}
获取阿里云加速镜像链接的方法:
登陆https://cr.console.aliyun.com,然后点击左下角的“镜像加速器”即可。
- 卸载:
systemctl stop docker
yum -y remove docker-ce
rm -rf /var/lib/docker
3、hello world:
上面说过执行docker run hello-world
会打印出相关信息,执行这条命令过程如下:
- 首先会查找本机是否有该镜像
- 如果有,直接以本机的那个镜像为模板生成容器实例运行
- 如果没有,去docker hub上查找
- 找到了,下载到本地,然后生成实例运行,找不到就返回错误信息
三、docker常用命令
1、帮助命令:
- 查看版本信息:
docker -v
- 查看docker信息:
docker info
- 查看帮助命令:
docker --help
,类似linux的man
命令
2、镜像命令:
回顾一下docker的logo,海上有一头鲸鱼,鲸鱼背上有一个个的集装箱。对应关系如下:
- 海 ------ 电脑主机
- 鲸鱼 ------ docker
- 集装箱 ------ 容器实例,来自镜像模板
常用镜像命令如下:
- 列出主机上的镜像:
docker images
- 列出全部镜像(包括中间镜像):
docker images -a
- 只列出镜像的id:
docker images -q
- 列出全部镜像的id:
docker images -qa
- 显示镜像的摘要信息:
docker images --digests
- 显示完整的镜像信息:
docker images --no-trunc
- 从docker hub上查找xxx镜像:
docker search xxx
- 从docker hub上查找点赞数超过30的xxx镜像:
docker search -s 30 xxx
- 从docker hub上查找xxx镜像,并显示摘要信息:
docker search -s 30 --no-trunc xxx
- 从docker hub上查找能自动构建的xxx镜像:
docker search --automated xxx
- 从docker hub上拉取(下载)最新版的xxx镜像:
docker pull xxx
- 删除xxx(可以是镜像名,也可以是id)镜像:
docker rmi xxx
- 强制删除xxx镜像:
docker rmi -f xxx
- 删除多个镜像:
docker rmi -f xxx yyy
- 删除全部镜像:
docker rmi -f $(docker images -qa)
3、容器命令:
首先我们执行docker pull centos
拉取一个centos的镜像,下面所说的容器都是指centos(都可以是名字或者id)。
- 新建并启动容器:
docker run -it centos
- 启动容器可选的参数有:
--name
:为容器指定名字;-d
:后台运行容器;-P
:随机端口映射;-p
:指定端口映射;-i
:以交互模式运行容器;-t
:为容器重新分配一个伪输入终端,常与-i
一起使用 - 列出当前正在运行的容器:
docker ps
- 列出运行的容器可选参数有:
-a
:列出当前运行和历史上运行过的容器;-l
:显示最近创建的容器;-n
:显示最近创建的n个容器;-q
:静默模式,只显示容器编号;--no-trunc
:显示完整摘要信息 - 退出容器:
exit
:容器停止退出;ctrl + p + q
:容器不停止退出 - 启动容器:
docker start centos
- 重启容器:
docker restart centos
- 停止容器:
docker stop centos
- 强制停止容器:
docker kill centos
- 删除容器:
docker rm -f centos
- 一次性删除多个容器:
docker rm -f $(docker ps -a -q
或者docker ps -a -q | xargs docker rm
- 后台运行容器:
docker run -d centos
,启动后,再docker ps
,发现根本就没有正在运行的容器,但是刚刚确实启动成功了,因为启动后返回了一个id。这是docker的机制造成的,后台启动docker容器,前台没有交互,docker会认为它没事可做,就杀死了。 - 查看容器倒数n行日志:
docker logs -f -t --tail n 容器id
- 查看容器内运行的进程:
docker top 容器id
- 查看容器内部细节(返回一个json串):
docker inspect 容器id
- 进入正在运行的容器(ctrl + p + q退出后重新进入):
docker attach 容器id
- 不进入容器但对容器执行相关命令:
docker exec -t 容器id 命令
;比如不进入docker上运行的centos直接执行ls /
命令:docker exec -t centos的id ls /
- 将容器内的数据拷贝到主机:
docker cp 容器id:容器内路径 主机路径
四、docker镜像
1、什么是镜像?
镜像是一种轻量级的可执行的独立软件包。用来打包软件运行环境和基于运行环境开发的软件,包括代码、运行时、库、环境变量和配置文件。docker底层是一个unionFS(联合文件系统),即是一层一层的文件系统组成。
2、镜像为什么那么大?
执行docker pull tomcat
命令,下载一个tomcat镜像,然后执行docker images
,发现一个tomcat就600多兆,为什么那么大?因为刚才说的,镜像是一个联合文件系统,tomcat镜像不仅仅包含tomcat,还包含tomcat运行的各种环境,一个镜像包含了很多层,分层镜像如下:
3、docker为什么采用分层镜像?
最大的好处就是共享资源。比如多个镜像都需要jdk,那么宿主机上其实只要保存一份jdk就可以了,内存中也只需要加载一份。镜像的每一层都是可以共享的。
4、docker commit命令:
首先我们运行docker run -it -p 8888:8080 tomcat
,这里表示docker内部将tomcat运行在8080端口,对外暴露8888端口,即执行完这条命令,我们要用8888端口才能访问到tomcat。如果你访问到的是404,不要方,这是因为你下载的这个版本的tomcat,webapps目录是空的,资源都在webapps-dist目录下,可以执行如下操作:
- 进入tomcat目录:
docker exec -it tomcat容器的id /bin/bash
- 列出tomcat目录下的文件:
ls -l
- 给webapps重命名:
mv webapps webapps2
- 将webapps-dist改名为webapps:
mv webapps.dist webapps
刷新页面,就可以看到熟悉的tomcat首页了。
上面是指定对外暴露8888端口,还可以执行docker run -it -P tomcat
,大写的P表示随机分配端口,不自己指定。用这个命令启动后,执行docker ps
,就可以看到随机分配的端口是什么。
上面我们对tomcat做了一些修改,把访问会报404的tomcat改成了一个正常的tomcat,我们就可以使用commit命令以我们改好的tomcat为模板,生成一个启动就能直接访问的新的tomcat镜像。执行如下命令:
docker commit -a="zhusl" -m="normal tomcat" 容器id newtomcat:1.0
- -a是作者,-m是备注信息,newtomcat是新镜像的名字,1.0是版本号
五、容器数据卷
1、是什么?
我们在docker上运行容器实例,运行时产生的数据,当docker关闭了就没了。但是我们希望有些数据可以持久化保存下来,这个保存的地方的就是容器数据卷,并且保存下来的数据可以共享。
2、容器数据卷的特点:
- 数据卷可在容器之间共享或重用数据
- 卷中的更改可直接生效
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为止
3、添加数据卷的方法:
添加数据卷有两种方法,一种是命令添加,一种是用dockerfile。
命令添加:
添加数据卷命令:
docker run -it -v /宿主机绝对路径目录:/容器内目录 镜像名
比如执行docker run -it -v /mydatadir:/dockerdatadir centos
,就表示让centos这个镜像和主机之间建立数据卷,主机根目录下的mydatadir
和centos镜像根目录下的dockerdatadir
目录建立连接,进行数据共享。目录不存会自动新建目录。执行了以上命令后,先查看主机根目录下是否有mydatadir目录,然后再执行docker run -it centos /bin/bash
,ls查看一下centos镜像的根目录下是否有dockerdatadir目录。-
查看数据卷是否挂载成功:
docker inspect 容器id
。如果你看到两个目录都成功新建了还是不放心,可以用这条命令查看,如果在返回的内容中看到了如下信息则挂载成功。
验证通过数据卷可实现数据共享:首先在主机的mydatadir目录下新建一个test.txt文件,然后发现centos的镜像的dockerdatadir目录也有test.txt文件。然后再centos镜像中往test.txt文件些内容,回到主机再次查看test.txt文件,发现也是有内容的。并且容器推出后,在主机上的mydatadir目录下做的任何操作,在容器重启后,都会被同步到dockerdatadir目录下。
以只读方式添加数据卷:
docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
,加上ro,表示read only,容器只能读数据,不能进行写操作。
dockerFile添加:
- 主机根目录下新建
mydocker
文件夹; - 进入
mydocker
目录,新建dockerfile文件:vim dockerfile
,文件内容如下:
FROM centos
VOLUME ["/dockerdatadir1","/dockerdatadir2"]
CMD echo "finished,---------success"
CMD /bin/bash
- 然后执行build命令生成新的镜像,镜像名叫zhusl/centos:
docker build -f /mydocker/dockerfile -t zhusl/centos .
- 查看镜像:
docker images
,发现已经有zhusl/centos这个镜像了。这个镜像就是,我们以centos镜像为来源,添加了数据卷,新生成的一个centos。 - 运行新生成的这个镜像,就可以发现在zhusl/centos的根目录下有两个数据卷,dockerdatadir1和dockerdatadir2。那么这两个数据卷对应宿主机的哪个目录呢?还是执行
docker inspect 容器id
,就可以看到了,如下图:
4、数据卷容器:
上面说了数据卷,数据卷容器其实就是数据卷之间的传递性。比如还是以zhusl/centos镜像为例,先执行docker run -it --name dc01 zhusl/centos
,运行一个名为dc01的实例,然后再执行docker run -it --name doc02 --volumes-from dc02 zhusl/centos
,以dc01为父容器,运行一个dc02。因为zhusl/centos是添加了数据卷的,所以运行的dc01是挂载了数据卷的,然后dc02又是from dc01,所以dc02也挂载了数据卷。如果还有一个dc03也是继承自dc01,当dc01挂了,dc02和dc03之间也是可以进行数据共享的。
六、dockerFile
1、是什么?
就是一个写命令的文件,然后通过这个文件,就可以构建镜像。
2、构建的三个步骤:
- 编写dockerfile文件
- docker build
- docker run
登陆docker hub,然后随便搜索一个镜像,就以centos为例,选择版本然后进入,就可以看到它的dockerfile文件内容。如下就是centos7的dockerfile文件内容:
FROM scratch
ADD centos-7-x86_64-docker.tar.xz /
LABEL \
org.label-schema.schema-version="1.0" \
org.label-schema.name="CentOS Base Image" \
org.label-schema.vendor="CentOS" \
org.label-schema.license="GPLv2" \
org.label-schema.build-date="20200504" \
org.opencontainers.image.title="CentOS Base Image" \
org.opencontainers.image.vendor="CentOS" \
org.opencontainers.image.licenses="GPL-2.0-only" \
org.opencontainers.image.created="2020-05-04 00:00:00+01:00"
CMD ["/bin/bash"]
- FROM scratch:相当于java的Object类。所以镜像的基础镜像,即源镜像。
- ADD:后面的是要添加的东西
- LABEL:一些标签信息
- CMD:要执行的命令
3、dockerfile内容基础知识:
- 保留字(就是上面那些FROM、ADD等关键字)都必须为大写字母并且后面要跟随至少一个参数
- 指令从上到下按顺序执行
- #表示注释
- 每条指令都会创建一个新的镜像层,并对镜像进行提交
4、dockerfile的执行流程:
- docker从基础镜像中运行一个容器
- 执行一条指令并对容器做出修改
- 执行类似docker commit的操作提交一个新的镜像层
- docker再基于刚提交的镜像运行一个新的容器
- 执行dockerfile中的下一条指令直到所有指令都执行完成
5、dockerfile的保留字:
- FROM:表示当前要构建的新镜像是基于哪个镜像的
- MAINTAINER:作者 + 邮箱
- RUN:容器构建时需要运行的命令
- EXPOSE:服务的端口号
- WORKDIR:指定在创建容器后,终端默认登陆进来的工作目录
- ENV:设置环境变量用的
- ADD:要添加进镜像并解压缩的东西
- COPY:要拷贝进镜像的东西
- VOLUME:数据容器卷,用于持久化
- CMD:指定一个容器启动时要运行的命令,一个dockerfile中可以有多个CMD,但最终只有最后一个生效,CMD会被docker run后面的参数替换
- ENTRYPOINT:指定一个容器启动时要运行的命令,docker run后面的参数不会替换这个,而是在后面追加
- ONBUILD:触发器。就是另一个镜像基于自己构建时,当另一个镜像启动时自己要做的事
6、dockerfile构建镜像实操:
- 案例1:基础命令的使用:就以构建centos为例,我们从docker hub上拉下来的centos,默认路径是/,没有vim编辑器,也没有ifconfig命令。这个centos要实现登陆后默认路径的~,要有vim编辑器,要有ifconfig命令。下面开始编写dockerfile文件:
# 基于docker hub上拉下来的centos进行构建
FROM centos
# 容器启动后工作目录
WORKDIR ~
# 安装vim
RUN yum -y install vim
# 安装网络工具,使其能用ifconfig命令
RUN yum -y install net-tools
# 端口号
EXPOSE 80
CMD /bin/bash
新建一个dockerfile2文件,内容就是上面那段,然后执行docker build -f /mydocker/dockerfile2 -t mycentos:1.0 .
进行构建。-f后面的是dockerfile2文件的路径,mycentos是镜像名字,1.0是版本号,.代表当前路径。
执行完后,docker images
就会发现有一个新镜像,名字叫mycentos。然后运行该镜像,就会发现这个mycentos可以使用vim和ifconfig的。再回溯一个问题,镜像那么大,是因为它包含了它运行所需的所有环境,那么是不是很浪费空间?就比如这个,我原本有一个centos镜像,只不过没有vim编辑器,现在我构建一个新的有vim的mycentos,docker images 显示centos 600M,mycentos 620M,那这两个是不是就要占用1个G?其实并不是,因为镜像是可以共享的,mycentos 是form centos的,也就是说这600其实是共用的,最后这两个镜像其实占空间就是620M。
执行docker history 镜像id
,就可以列出镜像的历史,即这个镜像有多少层。
案例2:CMD指令的使用:执行
docker run -it -p 7777:8080 tomcat ls -l
,就是在启动命令后追加ls -l
参数,列出登陆后的目录。然后发现tomcat根本就没有启动,只是列出了tomcat的目录。因为dockerfile的CMD命令只有最后一行生效,ls -l
这个命令把启动tomcat的CMD覆盖了,所以没启动。案例3:ENTRYPOINT的使用:新建一个dockerfile3,内容如下:
FROM centos
RUN yum install -y curl
CMD ["curl","-s","http://ip.cn"]
意思就是制作一个镜像,一启动,就可以查出本机的IP。执行docker build -f /mydocker/dockerfile3 -t myip .
进行构建。然后运行该容器,就可以打印出ip信息。如果过执行的时候想加参数,比如docker run -it myip -i
,实际上就是想执行curl的时候加上-i
参数,打印请求头信息,那么抱歉,-i
会覆盖之前的命令,即覆盖CMD ["curl","-s","http://ip.cn"]
这一样,然后-i
根本就不是一个可执行命令,所以执行报错。要实现上面的需求,即加个-i
,让它真正执行的是curl -s -i https://ip.cn
,只需要把CMD
换成ENTRYPOINT
,然后启动容器时用docker run -it myip -i
即可。
- 案例4:
ONBUILD
的使用:修改dockerfile3,在后面加上如下的命令:
ONBUILD RUN echo "我被触发了"
然后,新建dockerfile4,FROM myip,build的时候会打印出 "我被触发了" 这一句话。
- 案例5:
COPY
和ADD
的使用。首先在opt目录下先搞两个tar.gz包和一个copy.txt文件,一个jdk8,一个tomcat9。编写dockerfile,内容如下:
FROM centos
# 复制文件
COPY copy.txt /usr/local/cincontainer.txt
# 添加并解压jdk
ADD jdk-8u171-linux-x64.tar.gz /usr/local
# 添加并解压tomcat9
ADD apache-tomcat-9.0.8.tar.gz /usr/local
# 安装vim
RUN yum install -y install vim
# 设置登陆落脚点
ENV MYPATH /usr/local
WORKDIR $MYPATH
# 配置jdk和tomcat环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_171
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.8
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.8
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:CATALINA_HOME/bin
# 指定容器运行端口
EXPOSE 8080
# 启动命令
CMD /usr/lcoal/apache-tomcat-9.0.8/bin/startup.sh && tail -F /usr/local/apache-tomat-9.0.8/bin/logs/catalina.out
七、常用镜像安装
1、MySQL:
- 拉取镜像:
docker pull mysql:5.7
- 启动并添加数据卷:
docker run -p 3306:3306 --name mysql -v /zhusl/mysql/conf:/etc/mysql/conf.d -v /zhusl/mysql/logs:/logs -v /zhusl/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
- 进入容器:
docker exec -it 容器id /bin/bash
- 使用mysql:
mysql -u root -p
2、redis:
- 搜索镜像:
docker search redis
- 拉取镜像:
docker pull redis
- 启动redis镜像:
docker run -p 6379:6379 -v /zhusl/redis/data:/data -v /zhusl/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf -d redis redis-server /usr/local/etc/redis/redis.conf --appendonly yes
appendonly yes表示开启aof。 - 连接redis:
docker exec -it redis容器id redis-cli
八、本地镜像推送到阿里云
- 登陆阿里云开发者平台:https://dev.aliyun.com/search.html
- 创建镜像仓库
- 将本地镜像推送到阿里云:
docker login --username= registry.cn-hangzhou.aliyuncs.com
# 执行完上一条命令会要你输入用户名和密码
docker tag 镜像id registry.cn-hangzhou.aliyuncs.com/zhushulin/redis:镜像版本号
docker push registry.cn-hangzhou.aliyuncs.com/zhushulin/redis:镜像版本号