关于Docker的官方介绍网上太多了我就不贴了,就实际体验来说Docker可以极大的简化环境搭建及服务部署的操作流程,大大降低部署的时间成本,解放你的双手 。
本文不会深入讲解Docker底层架构及运行原理,也不会有一堆架构图贴在这里。该篇旨在让你以最快的速度学会使用Docker,关于Docker的架构及其底层的一些知识,你可以在学会Docker的基本使用之后再去了解。开门见山讲架构聊底层有点容易让人犯迷糊,但在使用Docker之前你至少应该了解他的三大核心组件:仓库、镜像和容器,以及他们之前的关系。本文将通过一个MySQL示例带你了解并使用Docker,待你对Docker有一个基本了解后你再回头去看他的体系架构会容易理解。
三大核心组件 仓库 :仓库是集中存储镜像的地方,我们本地安装Docker之后,需要从仓库中拉取镜像。可以类比于Maven,有公有仓库和私有仓库之分。
镜像 :是一个Linux的文件系统,它里面存放着可以再Linux内核中运行的程序及其数据。
容器 :是镜像创建的运行实例,可以把它理解为一个精简版的Linux系统,里面运行着镜像里的程序。
为了更好的让你理解这三者的关系,我打一个不恰当但很形象的比方,镜像就相当于你weixin.exe文件,容器相当于你安装好的微信程序,微信程序(容器)需要你的weixin.exe文件(镜像)来安装(创建),那么仓库就相当于应用商店了,你可以从商店下载你要的.exe文件(镜像) 。
微信程序安装完成后你可以选择运行或者关闭,Docker容器一样可以运行和停止,微信程序你可以从系统卸载,Docker容器你同样可以选择删除。
但有一点不同的地方是,weixin.exe文件安装完成你就可以删除了,它和你的微信程序并没有关系,删掉安装文件不影响你微信程序的运行。但是镜像不同,如果有容器正在使用这个镜像,那么这个镜像是不能删除的(删除时会报Error不让你删)。
安装Docker CentOS安装Docker要求:
必须是64位操作系统 内核版本在3.8以上 你可以通过uname -r查看你的系统内核:
1 2 3 [root@localhost ~]# uname -r 3.10.0-1062.18.1.el7.x86_64 [root@localhost ~]#
yum方式安装:
安装完成你可以通过docker version查看你的docker版本信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@localhost ~]# docker version Client: Version: 1.13.1 API version: 1.26 Package version: docker-1.13.1-109.gitcccb291.el7.centos.x86_64 Go version: go1.10.3 Git commit: cccb291/1.13.1 Built: Tue Mar 3 17:21:24 2020 OS/Arch: linux/amd64 Server: Version: 1.13.1 API version: 1.26 (minimum version 1.12) Package version: docker-1.13.1-109.gitcccb291.el7.centos.x86_64 Go version: go1.10.3 Git commit: cccb291/1.13.1 Built: Tue Mar 3 17:21:24 2020 OS/Arch: linux/amd64 Experimental: false [root@localhost ~]#
看到如上的信息说明你的Docker安装成功,你可以用 docker info 命令查看更详细的信息。
一些常用命令:
1 2 3 4 5 6 7 8 9 10 11 # 启动docker service docker start # 停止docker service docker stop # 重启docker service docker restart # 开启Docker开机自启 systemctl enable docker # 关闭Docker开机自启 systemctl disable docker
配置镜像加速 为了更愉快的使用Docker你可能还需要配置镜像加速 ,可以类比于Maven的私服,使用国内的镜像仓库能让你更快的拉取镜像。
执行vim /etc/docker/daemon.json,修改为如下配置:
1 2 3 4 5 6 { "registry-mirrors" :[ "https://reg-mirror.qiniu.com/" , "https://hub-mirror.c.163.com/" ] }
重新加载配置及重启Docker服务:
1 2 systemctl daemon-reload systemctl restart docker
执行docker info你可以看到镜像仓库配置已经生效了。
拉取镜像 Docker安装和配置都搞定了,现在你要从仓库下载镜像了,这里以 MySQL 5.7 为例:
1 2 # 5.7为版本号,你也可以安装其他版本 docker pull mysql:5.7
拉取成功后通过docker images命令查看本地镜像:
1 2 3 4 [root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/mysql 5.7 f965319e89de 3 hours ago 448 MB [root@localhost ~]#
创建容器 有了镜像,你需要用它创建一个容器才能运行,创建并运行MySQL容器:
1 docker run -d -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
命令参数说明(后面会有更加详细的说明): -d:后台运行 -p:端口映射,前面的为宿主机端口,后面的为容器端口,这里我将宿主机的3306端口指向了容器的3306端口 –name:为启动的容器命名 -e:指定容器内的环境变量,这里指配置MySQL的Root用户密码为:123456
执行成功后会返回容器ID,查看已创建的容器:docker ps -a
1 2 3 4 5 6 [root@localhost ~]# docker run -d -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7 8e1cd060075db23c61cb31cecb3a3321df92cf56ea7086476cc21e8709382d19 [root@localhost ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8e1cd060075d mysql:5.7 "docker-entrypoint..." 3 seconds ago Up 1 second 0.0.0.0:3306->3306/tcp, 33060/tcp mysql [root@localhost ~]#
可以看到刚才创建的MySQL容器已经在运行了,现在你可以通过${IP}:3306连接MySQL数据库了(记得放行端口或者关闭防火墙)。
挂载目录 通过上面的步骤,你已经通过Docker运行起你的第一个容器MySQL了,而且你也能通过宿主机的端口连接到容器中的MySQL。但这样做还不是很安全,关于这个我们要先简单了解一下容器和宿主机之间的关系。
容器内和宿主机的文件系统是独立的(虽然整个容器也是以文件的形式存放在宿主机的某个目录上的),包括他们之间的网络,也是独立的。刚才运行的MySQL容器有一个 -p 3306:3306 的参数,这个参数就是映射宿主机和容器之间的端口的,你也可以配置成比如 -p 1234:3306 ,这样你通过访问宿主机的1234端口就能访问到容器的3306端口。
那么再回到文件系统,容器本身也是一个精简版的Linux系统,只不过他运行在宿主机上依赖于宿主机的硬件。容器内部也是有着一套独立的文件系统的,且随着容器的删除,所有存在于容器内的所有文件都将被清除 。刚才我们创建的那个MySQL容器,只要我们删除容器,数据库里的所有数据都将清除,这显然不是我们想看到的。
Docker的run命令提供了一个-v的参数,允许我们容器内部目录挂载为宿主机的本地目录,这样容器内的程序在操作这个目录时,其实操作的是宿主机的目录。那么我们可以把程序运行的关键数据挂载到宿主机上的目录,比如MySQL的数据库文件,程序运行的日志文件等等 。这样一来当我们在删除容器时,因为这些目录是存在于宿主机的,所以不会随着容器一起删除,从而实现了容器数据的持久化。
还是以刚才的MySQL容器为例,我们先删掉刚才的容器:
1 2 3 # mysql为容器名称,也可以是容器ID,通过 docker ps -a 查看容器信息 docker stop mysql docker rm mysql
接着用下面的命令创建并运行MySQL容器,增加了一个 -v 参数:
1 2 3 4 5 6 7 8 9 10 11 # 在宿主机创建挂载目录 mkdir -p /usr/local/mysql/conf mkdir -p /usr/local/mysql/logs mkdir -p /usr/local/mysql/data # 创建并运行容器 docker run -p 3306:3306 --name mysql \ -v /usr/local/mysql/conf:/etc/mysql \ -v /usr/local/mysql/logs:/var/log/mysql \ -v /usr/local/mysql/data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=123456 \ -d mysql:5.7
-v /usr/local/mysql/data:/var/lib/mysql
表示将宿主机的 /usr/local/mysql/data 目录挂载到容器内的 /var/lib/mysql 目录,那么容器内的MySQL程序操作的数据实际上是写入了宿主机的 /usr/local/mysql/data 目录了,其他两项同理。
这里挂载的三个目录分别为数据库运行的配置、日志和数据,也是MySQL程序最重要的数据,即便这个容器删除了,只要这些数据还在,我们就能通过重新创建容器恢复全部数据。
而且这样挂载以后,我们无需进入容器,直接在宿主机操作这几个目录里的文件就能同步体现到容器内部,比如修改一些配置,导出数据之类的,不用进入容器直接在宿主机操作即可。
用以上命令运行容器之后,在你的宿主机的/usr/local/mysql/data 目录就能看到MySQL运行生成的数据库文件了:
Docker的数据卷挂载(我习惯称之为挂载目录)功能非常重要,我们运行的任何程序都有需要持久化的数据,如果这些数据直接放在容器内部是非常不安全的。而且挂载目录还可以实现直接在宿主机操作容器内的数据,也能做到容器间的数据共享 ,用Docker一定要养成挂载重要数据到宿主机的习惯。
错误排查 上面这个命令是直接后台运行容器的,如果需要调试可以把 -d 参数修改为 -it 参数以在前台运行,在某些情况下你很可能会遇到类似于下面这些错误(可以通过前台运行查看到):
1 2 3 chown: changing ownership of '/var/lib/mysql/': Permission denied # 或者 mysqld: Can't create/write to file '/var/lib/mysql/is_writable' (Errcode: 13 - Permission denied)
如果出现上述问题,那么你需要关闭SELINUX,方法如下:
临时关闭:setenforce 0
永久关闭:vim /etc/selinux/config,修改 SELINUX 的值为 disabled 然后重启机器即可,看图:
再次运行容器就能看到成功提示了。
常用命令 通过上面的示例基本已经知道了Docker是怎样工作的,下面是一些基本命令,包括最常用的目录挂载功能等命令说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 docker version|info # 显示Docker信息,常用于查看Docker版本或检测Docker是否正确安装。 docker images # 列出机器上的镜像(images)及信息:REPOSITORY、TAG、IMAGE ID、CREATED、SIZE。 # IMAGE ID列其实是缩写,要显示完整则带上--no-trunc选项。 # -a 列出本地所有的镜像(含中间映像层,默认情况下,过滤掉中间映像层) # -no-trunc 显示完整的镜像信息 # -q 静默模式,只显示镜像ID docker search tomcat # 在docker index中搜索image,搜索的范围是官方镜像和所有个人公共镜像。NAME列的 / 后面是仓库的名字。 docker pull tomcat # 从docker registry server 中下拉image或repository。 # 语法:docker pull [OPTIONS] NAME[:TAG|@DIGEST] # -a 拉取所有 tagged 镜像 # --disable -content-trust 忽略镜像的校验,默认开启 # 上面的命令没有指定参数,在docker v1.2版本及以前,会下载官方镜像的tomcat仓库里的所有镜像 # 而从v.13开始只会下载tag为latest的镜像,也可以明确指定具体的镜像 # 如:docker pull tomcat:8,后面的8为tomcat版本tag。 docker run -d --name tomcat -p 8081:8080 tomcat:8 # 启动容器,语法为:docker run [OPTIONS] IMAGE [COMMAND] [ARG...] # -d 后台运行容器,并返回容器ID,不使用-d我们会看到tomcat启动日志,此时Ctrl+C容器会停止运行。 # -e 指定容器内的环境变量 # -name 指定容器的名称,如果不指定docker会帮我们取一个名字。 # -p 端口映射,宿主机端口:容器端口,上面我们就将宿主机的8081映射到容器的8080端口 # 此时我们访问宿主机的ip:8081就能访问容器内的tomcat了。 # -i 以交互模式运行容器,通常与 -t 同时使用。 # -t 为容器重新分配一个伪输入终端,通常与 -i 同时使用 # -v 目录挂载,本地目录:容器目录 docker ps # 查看容器的信息,默认显示当前正在运行中的容器 # -a 查看所有容器 # -l 显示最新启动的一个容器(包括已停止的) docker start|stop|restart CONTAINER_ID # 启动/停止/重启容器,需要用到容器ID,可以使用docker ps -a 查看所有容器的ID。 docker attach CONTAINER_ID|NAME # 进入容器,后面跟容器ID或者NANE,可以使用docker ps -a 查看所有容器的ID。 # 可以认为这是一个过时的命令,更多的docker用户会考虑使用docker exec 来实现相同的功能 # 但是出于docker官方并没有删除这个命令,我们还是有必要学习一下的。 # 进入容器后输入exit 命令可以退出,同时容器停止运行,如若想退出但不停止容器,可以用快捷键Ctrl+P+Q退出。 docker exec -i -t CONTAINER_ID|NAME /bin/bash # 进入容器,推荐使用这种方式。 # 语法:docker exec [OPTIONS] CONTAINER COMMAND [ARG...] # -i 以交互模式运行容器,通常与 -t 同时使用。 # -t 为容器重新分配一个伪输入终端,通常与 -i 同时使用 # 示例:docker exec -it mycentos /bin/sh /root/start.sh # 上面这条命令表示在容器 mycentos 中以交互模式执行容器内 /root/start.sh 脚本 docker rm CONTAINER_ID|NAME # 删除一个或多少容器,如:docker rm $(docker ps -a -q),为删除所有停止的容器 # -l 移除容器间的网络连接,而非容器本身 # -v 删除与容器关联的卷 # -f 通过SIGKILL信号强制删除一个运行中的容器 docker rmi CONTAINER_ID|NAME # 删除本地一个或多少镜像 # -f 强制删除; # --no-prune 不移除该镜像的过程镜像,默认移除; docker build # 使用 Dockerfile 创建镜像,语法:docker build [OPTIONS] PATH | URL | - # -f 指定要使用的Dockerfile路径 # -t,--tag 指定镜像的名字及标签:name:tag或者name,可以在一次构建中为一个镜像设置多个标签
Dockerfile Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。
初学者一开始可不必关注Dockerfile,待你熟悉Docker的整个体系结构及其运行方式后,再回头看这个就会一目了然了。
1 2 3 4 5 6 7 FROM FROM <image> FROM <image>:<tag> FROM <image>@<digest> FROM docker.io/centos:latest
1 2 3 4 5 MAINTAINER MAINTAINER <name> MAINTAINER guitu "xianjian-mail@qq.com"
1 2 3 4 5 6 7 8 9 10 11 12 RUN RUN <command > RUN ["executable" , "param1" , "param2" ] RUN apk update RUN ["executable" , "param1" , "param2" ] RUN ["/etc/execfile" , "arg1" , "arg1" ]
1 2 3 4 5 6 7 8 9 10 11 12 ADD ADD <src>... <dest> ADD ["<src>" ,... "<dest>" ] ADD hom* /mydir/ ADD hom?.txt /mydir/ ADD test relativeDir/ ADD test /absoluteDir/
1 2 3 4 5 6 7 8 9 CMD CMD ["executable" ,"param1" ,"param2" ] (执行可执行文件,优先) CMD ["param1" ,"param2" ] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数) CMD command param1 param2 (执行shell内部命令) CMD echo "This is a test." | wc - CMD ["/usr/bin/wc" ,"--help" ]
1 2 3 4 5 6 7 8 9 10 ENTRYPOINT ENTRYPOINT ["executable" , "param1" , "param2" ] (可执行文件, 优先) ENTRYPOINT command param1 param2 (shell内部命令) FROM ubuntu ENTRYPOINT ["top" , "-b" ] CMD ["-c" ]
1 2 3 4 5 6 7 LABEL LABEL <key>=<value> <key>=<value> <key>=<value> ... LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"
1 2 3 4 5 6 7 8 9 ENV ENV <key> <value> ENV <key>=<value> ... ENV JAVA_HOME /docker/jdk
1 2 3 4 5 6 7 8 9 10 11 12 VOLUME VOLUME ["/path/to/dir" ] VOLUME ["/data" ] VOLUME ["/var/www" , "/var/log/apache2" , "/etc/apache2" ]
1 2 3 4 5 6 7 8 9 WORKDIR WORKDIR /path/to/workdir WORKDIR /a (这时工作目录为/a) WORKDIR b (这时工作目录为/a/b) WORKDIR c (这时工作目录为/a/b/c)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 USER USER user USER user :group USER uid USER uid:gid USER user :gid USER uid:group USER www
1 2 3 4 5 6 ARG ARG <name>=[<default value>] ARG site ARG build_user=www
1 2 3 4 5 6 7 ONBUILD ONBUILD [INSTRUCTION] ONBUILD ADD . /app/src ONBUILD RUN /usr/local /bin/python-build --dir /app/src
构建镜像 了解了上面的命令之后,我们可以尝试着创建我的第一个自制镜像了,以下是我的Dockerfile示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 FROM docker.io/centos:latestMAINTAINER guitu "xianjian-mail@qq.com" RUN yum install -y subversion ADD jdk-8u231-linux-x64.tar.gz /docker/ ADD apache-tomcat-8.0.53.tar.gz /docker/ ENV JAVA_HOME /docker/jdk1.8.0 _231ENV TOMCAT_HOME /docker/apache-tomcat-8.0 .53 ENV PATH $PATH:$JAVA_HOME/bin:$TOMCAT_HOME/binWORKDIR /docker/apache-tomcat-8.0.53 EXPOSE 8080 CMD ["bin/startup.sh && tail -f logs/catalina.out" ]
通过Dockerfile创建镜像:
1 docker build -t mytomcat8:v0.1 .
通过Dockerfile文件构建镜像完成。