【Docker新手篇】Docker入门

Docker

Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动 开放容器联盟(OCI)。

Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于 LXC,从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runC 和 containerd。

Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。

下面的图片比较了 Docker 和传统虚拟化方式的不同之处。传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。

Docker 镜像

获取镜像

Docker 镜像仓库获取镜像的命令是 docker pull

docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]

docker pull tomcat 
  • Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。
  • 仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。

运行

$ docker run -it --rm tomcat bash

docker run就是运行容器的命令,我们这里简要的说明一下上面用到的参数。

  • -it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 bash 执行一些命令并查看返回结果,因此我们需要交互式终端。
  • --rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。
  • tomcat: 这是指用 tomcat 镜像为基础来启动容器。
  • bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 bash。

最后我们通过 exit(Ctrl+D) 退出了这个容器。

列出镜像

要想列出已经下载下来的镜像,可以使用 docker image ls 命令。
虚悬镜像:上面的镜像列表中,还可以看到一个特殊的镜像,这个镜像既没有仓库名,也没有标签,均为 。

<none>               <none>              00285df0df87        5 days ago          342 MB

这个镜像原本是有镜像名和标签的,原来为 mongo:3.2,随着官方镜像维护,发布了新版本后,重新 docker pull mongo:3.2 时,mongo:3.2 这个镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消,从而成为了 <none>。除了 docker pull 可能导致这种情况,docker build 也同样可以导致这种现象。由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 <none> 的镜像。这类无标签镜像也被称为 虚悬镜像(dangling image)

一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的,可以用下面的命令删除。

docker image prune

删除本地镜像

如果要删除本地的镜像,可以使用 docker image rm 命令,其格式为:

docker image rm [选项] <镜像1> [<镜像2> ...]

其中,<镜像> 可以是镜像短 ID、镜像长 ID、镜像名 或者 镜像摘要。

使用 Dockerfile 定制镜像

镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。

Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

定制 tomcat 镜像为例,这次我们使用 Dockerfile 来定制。

mkdir mytomcat
cd mytomcat
touch Dockerfile

vim Dockerfile 

Dockerfile文件内容

FROM tomcat 
RUN echo "hello docker" > index.html

FROM指定基础镜像:
所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 nginx 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 FROM 就是指定基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。

RUN执行命令:
RUN 指令是用来执行命令行命令的。由于命令行的强大能力,RUN 指令在定制镜像时是最常用的指令之一。

构建镜像

Dockerfile 文件所在目录执行:

docker build -t tomcat_zyshep .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM tomcat
 ---> e43d811ce2f4
Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > index.html
 ---> Running in 9cdc27646c7b
 ---> 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c

从命令的输出结果中,我们可以清晰的看到镜像的构建过程。在 Step 2 中,如同我们之前所说的那样,RUN 指令启动了一个容器 9cdc27646c7b,执行了所要求的命令,并最后提交了这一层 44aa4490ce2c,随后删除了所用到的这个容器 9cdc27646c7b

这里我们使用了 docker build 命令进行镜像构建。其格式为:

docker build [选项] <上下文路径/URL/->

在这里我们指定了最终镜像的名称 -t tomcat_zyshep,构建成功后,我们可以像之前运行 tomcat 那样来运行这个镜像,其结果会和 tomcat 一样。

保存镜像

备份本地仓库的镜像
1、⽤ save ⼦命令将本地仓库的镜像保存当前⽬录下

docker save -o tomcat.zysheep.tar 镜像名称

2、将本地目录下的镜像备份文件导⼊到本地 Docker 仓库

# ⽅式⼀(不输出详细信息):
[root@localhost ~]# docker load -i tomcat.zysheep.tar
# ⽅式⼆(输出详细信息):
[root@localhost ~]# docker load < tomcat.zysheep.tar

在这里插入图片描述

镜像构建上下文(Context)

如果注意,会看到 docker build 命令最后有一个 .. 表示当前目录,而 Dockerfile 就在当前目录,因此不少初学者以为这个路径是在指定 Dockerfile 所在路径,这么理解其实是不准确的。如果对应上面的命令格式,你可能会发现,这是在指定上下文路径。那么什么是上下文呢?

首先我们要理解 docker build 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 ocker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 APIDocker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。

当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?

这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

如果在 Dockerfile 中这么写:

COPY ./package.json /app/

这并不是要复制执行 docker build 命令所在的目录下的 package.json,也不是复制 Dockerfile 所在目录下的 package.json,而是复制 上下文(context) 目录下的 package.json

因此,COPY 这类指令中的源文件的路径都是相对路径。这也是初学者经常会问的为什么 COPY ../package.json /app 或者 COPY /opt/xxxx /app 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。

现在就可以理解刚才的命令 docker build -t tomcat_zysheep . 中的这个.,实际上是在指定上下文的目录,docker build 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。

如果观察 docker build 输出,我们其实已经看到了这个发送上下文的过程:

$ docker build -t tomcat .
Sending build context to Docker daemon 2.048 kB

理解构建上下文对于镜像构建是很重要的,避免犯一些不应该的错误。比如有些初学者在发现 COPY /opt/xxxx /app 不工作后,于是干脆将 Dockerfile 放到了硬盘根目录去构建,结果发现 docker build 执行后,在发送一个几十 GB 的东西,极为缓慢而且很容易构建失败。那是因为这种做法是在让 docker build 打包整个硬盘,这显然是使用错误。

一般来说,应该会将 Dockerfile 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 .gitignore 一样的语法写一个 .dockerignore,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。

那么为什么会有人误以为 . 是指定 Dockerfile 所在目录呢?这是因为在默认情况下,如果不额外指定 Dockerfile 的话,会将上下文目录下的名为 Dockerfile 的文件作为 Dockerfile

这只是默认行为,实际上 Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,比如可以用 -f …/Dockerfile.php 参数指定某个文件作为 Dockerfile。

当然,一般大家习惯性的会使用默认的文件名 Dockerfile,以及会将其置于镜像构建上下文目录中。

Docker 容器

容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。

启动容器

启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动。

新建并启动

所需要的命令主要为 docker run

启动一个 bash 终端,允许用户进行交互。

docker run -t -i tomcat bash

其中,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。

守护态运行

更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 -d 参数来实现。

docker run -d ubuntu:17.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a

此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 docker logs 查看)。

注: 容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关。

终止容器

可以使用 docker container stop 来终止一个运行中的容器。

此外,当 Docker 容器中指定的应用终结时,容器也自动终止

终止状态的容器可以用 docker container ls -a 或者 docker ps -a 命令看到

root@zysheep:~# docker container ls -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
5a68ca3aa075        mysql:5.7.22        "docker-entrypoint.s…"   2 hours ago         Up 8 minutes        0.0.0.0:3306->3306/tcp   mysql
9f676a420eee        zysheep             "catalina.sh run"        2 hours ago         Up 8 minutes        0.0.0.0:8080->8080/tcp   tomcat_zysheep

处于终止状态的容器,可以通过 docker container start 命令来重新启动。

此外,docker container restart 命令会将一个运行态的容器终止,然后再重新启动它。

进入容器

在使用 -d 参数时,容器启动后会进入后台。使用 docker attach 命令(如果从这个 stdin 中 exit,会导致容器的停止)。或 docker exec 命令,推荐大家使用 docker exec 命令

exec 命令

docker exec 后边可以跟多个参数,这里主要说明 -i -t 参数。
只用 -i 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。

-i -t 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。

数据卷(容器数据管理)

在之前的nginx案例中,修改nginx的html页面时,需要进入nginx内部。并且因为没有编辑器,修改文件也很麻烦。

这就是因为容器与数据(容器内文件)耦合带来的后果。
在这里插入图片描述
要解决这个问题,必须将数据与容器解耦,这就要用到数据卷了。

什么是数据卷

数据卷(volume) 是一个虚拟目录,指向宿主机文件系统中的某个目录。

在这里插入图片描述一旦完成数据卷挂载,对容器的一切操作都会作用在数据卷对应的宿主机目录了。

这样,我们操作宿主机的/var/lib/docker/volumes/html目录,就等于操作容器内的/usr/share/nginx/html目录了

数据集操作命令

数据卷操作的基本语法如下:

docker volume [COMMAND]

docker volume命令是数据卷操作,根据命令后跟随的command来确定下一步的操作:

  • create 创建一个volume
  • inspect 显示一个或多个volume的信息
  • ls 列出所有的volume
  • prune 删除未使用的volume
  • rm 删除一个或多个指定的volume

创建和查看数据卷

需求:创建一个数据卷,并查看数据卷在宿主机的目录位置

① 创建数据卷

docker volume create html

② 查看所有数据

docker volume ls

结果:

在这里插入图片描述
③ 查看数据卷详细信息卷

docker volume inspect html

结果:

在这里插入图片描述
可以看到,我们创建的html这个数据卷关联的宿主机目录为/var/lib/docker/volumes/html/_data目录。

小结
数据卷的作用:

  • 将容器与数据分离,解耦合,方便操作容器内数据,保证数据安全

数据卷操作:

  • docker volume create:创建数据卷
  • docker volume ls:查看所有数据卷
  • docker volume inspect:查看数据卷详细信息,包括关联的宿主机目录位置
  • docker volume rm:删除指定数据卷
  • docker volume prune:删除所有未使用的数据卷

挂载数据卷

我们在创建容器时,可以通过 -v 参数来挂载一个数据卷或者宿主机目录到某个容器内目录,命令格式如下:

docker run \
	  --name myshop  -d \
	  -v /usr/local/docker/tomcat/ROOT:/usr/local/tomcat/webapps/ROOT \
	   -p 8081:8080 \
	  myshop \

docker run 启动一个容器
-p(端口映射)  宿主机端口:容器默认端口
--name  容器名字
-d 以守护状态运行在后台运行容器
-v 数据卷路径:挂载的路径

这里的-v就是挂载数据卷的命令:

  • -v html:/root/htm :把html数据卷挂载到容器内的/root/html这个目录中

案例-给MySQL挂载本地目录

容器不仅仅可以挂载数据卷,也可以直接挂载到宿主机目录上。 关联关系如下:

  • 带数据卷模式:宿主机目录 --> 数据卷 —> 容器内目录
  • 直接挂载模式:宿主机目录 —> 容器内目录

如图:

在这里插入图片描述
语法
目录挂载与数据卷挂载的语法是类似的:

  • -v [宿主机目录]:[容器内目录]
  • -v [宿主机文件]:[容器内文件]

需求:创建并运行一个MySQL容器,将宿主机目录直接挂载到容器

实现思路如下:
1)在将课前资料中的mysql.tar文件上传到虚拟机,通过load命令加载为镜像
2)创建目录/tmp/mysql/data
3)创建目录/tmp/mysql/conf,将课前资料提供的hmy.cnf文件上传到/tmp/mysql/conf
4)去DockerHub查阅资料,创建并运行MySQL容器,要求:
① 挂载/tmp/mysql/data到mysql容器内数据存储目录
② 挂载/tmp/mysql/conf/hmy.cnf到mysql容器的配置文件
③ 设置MySQL密码

小结

docker run的命令中通过 -v 参数挂载文件或目录到容器中:

  • -v volume名称:容器内目录
  • -v 宿主机文件:容器内文
  • -v 宿主机目录:容器内目录

数据卷挂载与目录直接挂载的

  • 数据卷挂载耦合度低,由docker来管理目录,但是目录较深,不好找
  • 目录挂载耦合度高,需要我们自己管理目录,不过目录容易寻找查看

Docker 构建 Tomcat

查找 Docker Hub 上的 Tom

docker search tomcat

这里我们拉取官方的镜像

docker pull tomcat

运行容器

docker run -p 8081:8080 --name tomcat2 -v /usr/local/docker/tomcat/ROOT/:/usr/local/tomcat/webapps/ROOT/ tomcat
命令说明:
-p 8080:8080:将容器的8080端口映射到主机的8080端口
-v /usr/local/docker/tomcat/ROOT/:/usr/local/tomcat/webapps/ROOT/:将主机中当前目录下的ROOT挂载到容器的/ROOT 
容器没有则自动创建

查看容器启动情况

docker ps

通过浏览器访问

主机地址:端口号

docker logs 容器id 查看日志

docker logs -f 容器id 监听日志

Docker 构建 MySQL

查找 Docker Hub 上的 MySQL 镜像

docker search mysql

这里我们拉取官方的镜像

docker pull mysql:5.7.22

运行容器:

docker run -p 3306:3306 --name mysql \
-v /usr/local/docker/mysql/conf:/etc/mysql \
-v /usr/local/docker/mysql/logs:/var/log/mysql \
-v /usr/local/docker/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7.22


命令参数:
-p 3306:3306:将容器的3306端口映射到主机的3306端口

-v /usr/local/docker/mysql/conf:/etc/mysql:将主机当前目录下的 conf 挂载到容器的 /etc/mysql(直接使用这个命令因为数据卷里面没有东西,所以挂载mysql容器conf目录下也为空)

-v /usr/local/docker/mysql/logs:/var/log/mysql:将主机当前目录下的 logs 目录挂载到容器的 /var/log/mysql

-v /usr/local/docker/mysql/data:/var/lib/mysql:将主机当前目录下的 data 目录挂载到容器的 /var/lib/mysql

-e MYSQL\_ROOT\_PASSWORD=123456:初始化root用户的密码

查看容器启动情况

docker ps

使用客户端工具连接 MySQL

注意!!!:实现配置挂载时,本地配置目录如果是空目录,那么挂载的时候,mysql容器内的配置目录也会为空(因为挂载了,会同步,同步会将容器内的配置目录也为空),这个时候,容器就无法成功运行。能启动成功,也会出现一系列问题,如连接数问题,无法区分表大小写问题,等
解决这个问题,我们只要保证本地配置目录不为空就行了。解决思路有2个 (我用的是第一个)
1、启动一个容器,将容器内的配置文件 /etc/mysql/my.cnf,copy到我们本地。利用 docker cp命令完成;
2、手动,自己本地新建一个my.cnf配置文件,手动将容器内配置文件 /etc/mysql/my.cnf内容 复制到本地的配置文件中;

复制容器文件到宿主机实现数据卷共用配置文件

不带数据卷的方式启动,查看容器内/etc/mysql的配置,复制到宿主机,删除服务,重新以数据卷的方式启动

docker run -p 3307:3306  --name mysql5.7.22  \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7.22 \

在这里插入图片描述

mysql容器内的/etc/mysql是存在文件的

在这里插入图片描述

exit退出容器,复制容器/etc/mysql目录下的文件到宿主机数据卷目录下

1、cd到宿主机数据卷目录/usr/local/docker/mysql/conf/

cd /usr/local/docker/mysql/conf/

2、复制容器配置到宿主机目录

docker cp 73df52bd37b1:/etc/mysql  .

3、查看目录文件,cd到mysql目录

在这里插入图片描述

4、移动mysql目录下所有文件到上一级目录(配置文件数据卷挂载容器的目录)

mv *.* ..

5、删除mysql目录

rm -rf mysql 

6、删除容器,换带数据卷的方式启动

在这里插入图片描述

7、重新启动容器,带数据卷的方式

docker run -p 3306:3306 --name mysql5.7 \
-v /usr/local/docker/mysql/conf:/etc/mysql \
-v /usr/local/docker/mysql/logs:/var/log/mysql \
-v /usr/local/docker/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7.22;

导入文件过大的错误

原因:mysql容器中 /etc/mysql/conf.d路径下的文件mysqldump.cnf限制了初始的大小

[mysqldump]
quick
quote-names
max_allowed_packet	= 16M

解决:在/etc/mysql/mysql.conf.d路径文件mysqld.cnf中添加内容max_allowed_packet = 128M(这里我该大一点)

each max_allowed_packet= 128M>> mysqld.cnf

就可以导入成功了,重启mysql容器

docker restart mysql 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李熠漾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值