文章目录
- 1. 认识Docker
- 1.1 容器
- 1.2 Linux容器
- 1.3 Docker
- 2. 配置Docker
- 2.1 安装Docker
- 2.2 启动Docker
- 2.3 配置镜像加速
- 3. Docker镜像操作
- 3.1 拉取镜像
- 3.2 镜像的打包和加载
- 3.3 查看帮助文档
- 4. 容器命令
- 4.1 运行容器
- 4.2 进入容器
- 4.3 数据卷
- 5. 自定义镜像
- 5.1 Dockerfile语法
- 5.2 基于java8构建Java项目
- 6. DockerCompose
- 6.1 安装DockerCompose
- 6.2 配置DockerCompose
1. 认识Docker
1.1 容器
在了解Docker前,我们首先来认识什么是容器。
在典型的虚拟化环境中,一台或多台虚拟机运行在一个物理服务器之上,这些物理服务器又使用着像 Xen、Hyper-V 等管理程序。而容器运行在操作系统内核之上,我们可以将其称为操作系统级虚拟化。
在深入了解底层容器概念之前,我们需要了解两个关键的 Linux 概念:
- 用户空间:运行用户程序(应用程序、进程)所需的所有代码被称为用户空间。当启动程序操作时,例如创建一个文件,用户空间中的进程会向内核空间发出系统调用。
- 内核空间:这里有着与系统硬件、存储等交互的内核代码,是操作系统的核心。
从本质上讲,容器是一个进程。当我们启动一个应用程序,比如说 Nginx Web 服务器,这个时候实际上是在启动一个进程。而进程本身是一个具有有限隔离的自包含指令。
那么如果我们仅使用进程运行和操作所需的文件与配置来隔离进程,又会怎样呢?而这正是容器的工作原理。容器基本上是一个具有足够隔离用户空间组件的进程,因此它给人一种独立操作系统的感觉。
父容器进程可能有一个子进程,所以可以说,一个容器也是一组进程。
1.2 Linux容器
Linux 有两个重要的内核功能,分别是命名空间(namespaces)和控制组(control groups)。在主机中,容器与容器之间的隔离正是由这两个内核功能实现的。
命名空间为容器设置了边界,让容器拥有了自己的挂载点、用户、IP 地址、以及进程管理等。创建容器就是创建一个隔离良好的环境来运行服务(进程)。为了达到这种级别的隔离,容器应该要有自己的文件系统、IP 地址、挂载点、进程 ID 等,这就需要使用 Linux 命名空间来实现这一点。使用这些命名空间,容器可以拥有自己的网络接口、IP 地址等。每个容器都拥有自己的命名空间,并且在该命名空间内运行的进程在其他命名空间中没有任何权限。
Linux 控制组管理着容器使用的资源,我们可以限制容器的 CPU、内存、网络和 IO 资源。由于我们可以在主机内运行多个容器,因此应该有一种机制来限制资源的使用、设备的访问等。而这里正是控制组的用武之地。如果不限制,单个容器可能最终会占用所有的主机资源,无资源可用就会导致其他容器的崩溃。
1.3 Docker
Docker 是一个流行的用 Go 语言开发的开源项目,由 Dotcloud 公司开发。Docker 基本上就是一个容器引擎,它使用 Linux 内核功能(如命名空间和控制组)在操作系统之上创建容器。
除了作为一种容器技术之外,Docker 还具有定义明确的包装器组件,这使打包应用程序变得十分容易。要知道,在 Docker 出现之前,运行容器并不是一件容易的事。所有的这一切意味着,Docker 通过将所有应用程序系统的需求打包到容器中,完成了将应用程序与基础设施分离的所有工作。例如,如果有一个 Java jar 文件,我们可以在任何安装了 java 的服务器上运行它。同样的,一旦使用 Docker 将容器与所需的应用程序打包在一起,我们就可以在任何其他安装了 Docker 的主机上运行它。
2. 配置Docker
2.1 安装Docker
接下来我们就对Docker进行在虚拟机上的安装尝试,首先需要安装yum工具,安装命令如下:
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2 --skip-broken
然后更新本地镜像源:
# 设置docker镜像源
yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo
yum makecache fast
然后输入命令以下命令即可进行安装:
yum install -y docker-ce
docker-ce
为社区免费版本。稍等片刻,docker即可安装成功。
输入下面的命令测试Docker是否安装成功
docker -v
安装成功时会输出Docker 的版本号,如下
2.2 启动Docker
Docker应用需要用到各种端口,逐一去修改防火墙设置。非常麻烦,因此建议大家直接关闭防火墙!
启动docker前,一定要关闭防火墙后!!
# 关闭
systemctl stop firewalld
# 禁止开机启动防火墙
systemctl disable firewalld
通过以下命令对Docker进行操控:
systemctl start docker # 启动docker服务
systemctl stop docker # 停止docker服务
systemctl restart docker # 重启docker服务
2.3 配置镜像加速
docker官方镜像仓库网速较差,我们需要设置国内镜像服务,一般选择的是阿里云的镜像服务,配置时只需要将以下命令复制并执行即可:
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://debe2hlb.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
3. Docker镜像操作
首先来看下镜像的名称组成:
- 镜名称一般分两部分组成:[repository]:[tag]。
- 在没有指定tag时,默认是latest,代表最新版本的镜像
比如需要使用 Nginx
的镜像,那么使用1.9版本的 Nginx
命令为 nginx:1.9
,直接输入 nginx
则默认为最新版本。
Docker镜像操作中常见的命令流程如下:
接下来我们以 Nginx镜像的操作为例。
3.1 拉取镜像
首先我们进入到镜像仓库搜索Nginx的镜像,这里可以使用镜像仓库DockHub ,然后搜索Nginx,复制其中的pull命令,界面如下,
直接复制pull命令,其后是没有tag的,即得到的是最新版的,如果需要指定版本,可以在下面的红框中进行寻找,并补在tag的位置。
我们需要先使用命令 systemctl start docker
将docker进行启动,然后将Nginx镜像使用命令 systemctl start docker
进行拉取,拉取成功后,可以使用 docker images
命令查看本地镜像,查看如下:
3.2 镜像的打包和加载
使用 save
命令可以将镜像打包成一个压缩文件,之后还可以使用 load
命令进行加载,这样更利于镜像存储和传递,比如我需要将Nginx镜像打包为nginx.tar,则使用一下命令,
docker save -o nginx.tar nginx:latest
使用 ll
查看当前路径下的文件后,显示有 nginx.tar 的存在,
然后我们将镜像进行删除,删除使用的命令是 docker rmi image
命令,这里删除Nginx镜像的命令如下:
docker rmi nginx:latest
再次使用 docker images
命令则查看不到任何镜像,
使用 load
命令可以将打包的镜像进行加载,这里我们加载打包的Nginx镜像为,
docker load -i nginx.tar
加载压缩包的镜像以及查看的命令如下,
可以看到,加载了压缩包的镜像后,Nginx镜像又出现在了本地仓库。
3.3 查看帮助文档
docker命令如果有不清楚的部分,一般都会使用帮助文档进行查询,比如使用 docker --help
则可以查看docker的全部命令,但这些命令都没有具体的参数,比如我想查看 save
命令的具体参数,那么直接使用 docker save --help
即可查看所有的参数,在实际中如果有命令不清楚的,一般都会使用 --help
进行查看。
4. 容器命令
docker最重要的就是容器,那么我们自然也就需要操作容器了,容器状态有以下的三种,
常用的跟容器相关的命令有以下几个:
# 进入容器执行命令
docker exec
# 查看容器运行日志
docker logs
# 查看所有的容器及运行状态
docker ps
# 删除指定容器
docker rm
4.1 运行容器
可以去镜像仓库DockHub 查看相应的容器的使用方法,以Nginx为例,其使用方法在页面上有如下内容,
比如,我们可以使用下面的命令来运行一个Nginx容器,
docker run --name containerName -p 80:80 -d nginx
上述命令中 docker run
是创建并运行一个容器, --name
参数是给容器起一个别名,-p
将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口。因为docker中的容器是隔离的,所以外部的访问不能直接访问到容器内部,使用该命令后,该容器会与宿主机的端口进行关联,在外部访问宿主机端口时,会将其命令映射到docker容器中进行访问。-d
参数表示是后台运行。
现在我们运行Nginx,如下,
docker run --name mn -p 80:80 -d nginx
使用 docker ps
查看运行状态如下:
运行后产生的一串编码是容器的唯一ID。
最后,访问虚拟机的IP地址的80端口可以看到Nginx已经启动,
使用 docker logs mn
还可以对指定的容器查看日志。
4.2 进入容器
以上面的Nginx容器为例,因为容器是隔离的,所以我们没有办法在外面操控容器的细节,这时我们就可以进入容器内部,使用如下命令进入到容器的内部,
docker exec -it mn bash
docker exec
进入容器内部,执行一个命令,-it
给当前进入的容器创建一个标准输入、输出终端,允许我们与容器交互,mn
要进入的容器的名称,bash
进入容器后执行的命令,bash是一个linux终端交互命令。
输入的命令如以上图片所示。可见,当我们进入容器内部后,是进入的根目录,输入 ls
,下面有与Linux根目录类似的一个个文件夹,这也证明了容器内部是独立并且隔离的,输入 exit
即可退出容器。
exec
命令也可以进入容器内部对文件进行修改,但是这是不推荐的,一是容器内并没有 vi
命令,修改并不方便,二是因为容器内修改是没有记录的,无法追溯你的修改记录。
4.3 数据卷
当我们需要对容器内的文件进行修改时,需要进入到容器内部进行修改,极其不方便的同时,以后如果要升级容器必然删除旧容器,所有数据都跟着删除了,这个时候,就需要一种方式来解决这个问题,我们采用的方式就是数据卷。
数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录,而之后在宿主机的目录下也会创建一个跟容器内目录相同的结构,修改数据卷关联的目录内文件的内容后,其会立即更改到容器中。
现在我们来以更改Nginx的欢迎界面的文字为例,将 “Welcome to nginx” 改为 “Welcome to ikun” 。
首秀安,我们创建一个数据卷,使用 docker volume create
命令进行创建,我们下面创建一个名为 html
的数据卷,
docker volume create html
使用 docker volume ls
可以查看创建的数据卷。
然后我们需要找到 “Welcome to nginx” 在哪个文件夹中,这需要查看Dockerhub里的说明,
从这里我们可以看出静态页面的地址,于是,在运行docker时我们可以将这个地址与数据卷进行关联,使用命令如下:
docker run --name mn -p 80:80 -v html:/usr/share/nginx/html -d nginx
数据卷的操作主要有如下命令:
# 显示一个或多个volume的信息
docker volume inspect
# 删除未使用的volume
docker volume prune
# 删除一个或多个指定的volume
docker volume rm
使用 docker volume inspect html
可以看到数据卷的一些信息,
上面的 Mountpoint
就是数据卷挂载的地址 。
我们切换到上面的挂载地址所在的目录可以看到,目录下面有两个文件,这两个文件就是Nginx容器内部目录下的两个文件。
我们只需要对这两个文件进行修改,容器内的文件一样会进行修改,比如,我们修改 index.html
中的文件,将 “Welcome to nginx” 改为 “Welcome to ikun” ,保存后,访问网页,可以看到页面的欢迎内容已经被修改了。
这里我们是先创建的数据卷再进行的数据卷的映射,其实可以不用创建数据卷,直接映射,如果数据卷不存在,docker会为你自动创建一个数据卷。
5. 自定义镜像
镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。
我们以MySQL为例,来看看镜像的组成结构:
简单来说,镜像就是在系统函数库、运行环境基础上,添加应用程序文件、配置文件、依赖文件等组合,然后编写好启动脚本打包在一起形成的文件。我们要构建镜像,其实就是实现上述打包的过程。
5.1 Dockerfile语法
构建自定义的镜像时,并不需要一个个文件去拷贝,打包。我们只需要告诉Docker,我们的镜像的组成,需要哪些BaseImage、需要拷贝什么文件、需要安装什么依赖、启动脚本是什么,将来Docker会帮助我们构建镜像。描述上述信息的文件就是Dockerfile文件。Dockerfile就是一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer,该文件需要我们自己去写,基本语法如下:
指令 | 说明 | 示例 |
---|---|---|
FROM | 指定基础镜像 | FROM centos:7 |
ENV | 设置环境变量 | ENV key value |
COPY | 拷贝本地文件到镜像的指定目录 | COPY ./mysql-5.7.rpm /tmp |
RUN | 执行Linux的shell命令,一般是安装过程中的命令 | RUN yun install gcc |
EXPOSE | 指定容器运行时监听的端口,给镜像使用者看 | EXPOSE 8080 |
ENTERPOINT | 镜像中应用的启动命令,容器运行时调用 | ENTERPOINT java -jar /tmp/app.jar |
比如我们传入一个javaweb项目 docker-demo
以及JDK8的包 jdk8.tar.gz
,Dockerfile的构建时内容如下:
# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 安装JDK
RUN cd $JAVA_DIR \
&& tar -xf ./jdk8.tar.gz \
&& mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 暴露端口
EXPOSE 8090
# 入口,java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar
之后再执行 docker build -t javaweb:1.0 .
即可构建镜像,该条命令中的 .
指的是dockerfile所在的目录。
5.2 基于java8构建Java项目
虽然我们可以基于Ubuntu基础镜像,添加任意自己需要的安装包,构建镜像,但是却比较麻烦。所以大多数情况下,我们都可以在一些安装了部分软件的基础镜像上做改造。例如,构建java项目的镜像,可以在已经准备了JDK的基础镜像基础上构建。
java:8-alpine
正好提供了这样的基础镜像,我们构建镜像时就可以基于这个镜像包来构建,就不需要额外安装java8的安装包了,该方式的Dockerfile文件内容如下:
FROM java:8-alpine
COPY ./app.jar /tmp/app.jar
EXPOSE 8090
ENTRYPOINT java -jar /tmp/app.jar
肉眼可见的少了很多的代码。
6. DockerCompose
Docker Compose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。一个例子如下:
version: "3.8"
services:
mysql:
image: mysql:5.7.25
environment:
MYSQL_ROOT_PASSWORD: 123
volumes:
- "/tmp/mysql/data:/var/lib/mysql"
- "/tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf"
web:
build: .
ports:
- "8090:8090"
上面的Compose文件就描述一个项目,其中包含两个容器:
- mysql:一个基于
mysql:5.7.25
镜像构建的容器,并且挂载了两个目录 - web:一个基于
docker build
临时构建的镜像容器,映射端口时8090
DockerCompose的详细语法参考官网:https://docs.docker.com/compose/compose-file/
其实DockerCompose文件可以看做是将多个docker run命令写到一个文件,只是语法稍有差异。
6.1 安装DockerCompose
首先下载DockerCompose文件到 /usr/local/bin/
目录下,然后修改文件的权限,使用如下命令:
chmod +x /usr/local/bin/docker-compose
之后再配置自动补全命令。
配置自动补全的下载源在国外,这里建议修改一下hosts文件再配置,修改如下:
echo "199.232.68.133 raw.githubusercontent.com" >> /etc/hosts
然后执行命令,配置自动补全:
curl -L https://raw.githubusercontent.com/docker/compose/1.29.1/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose
6.2 配置DockerCompose
将服务全部打包,点击IDEA上的 Package
按钮,能够将Module打包为一个 jar
文件,然后,在里面配置一个名为 docker-compose.yml
的文件,其中的语法是按照DockerCompose的语法来进行编写的,内容如下:
version: "3.2"
services:
nacos:
image: nacos/nacos-server
environment:
MODE: standalone
ports:
- "8848:8848"
# 配置MySQL
mysql:
image: mysql:5.7.25
environment:
MYSQL_ROOT_PASSWORD: 123
volumes:
- "$PWD/mysql/data:/var/lib/mysql"
- "$PWD/mysql/conf:/etc/mysql/conf.d/"
# 各微服务不向外暴露接口
userservice:
build: ./user-service
orderservice:
build: ./order-service
gateway:
build: ./gateway
ports:
- "10010:10010"
之后,将文件夹与 docker-compose.yml
文件传送到虚拟机下,启动Docker,使用 docker-compose up -d
启动微服务,并且将其转为后台运行。
使用 docker-compose logs
能够查看打印的日志,如果不出意外的话,日志里面应该有报错的信息。因为Nacos的启动相比其他微服务的启动来说太慢了,这就导致了其他微服务启动后Nacos都还未启动,这种情况下我们需要在Nacos启动后,将其他微服务进行重启,重启如下
docker-compose restart gateway userservice orderservice
之后输入虚拟机的IP地址,并访问统一网关 http://192.168.59.233:10010/user/2?authorization=admin
,能够打印出信息,这就大功告成了。