Docker是一个开源平台,主要基于Go语言构建,它使开发者能够将应用程序及其依赖项打包到一个轻量级、可移植的容器中。容器技术的核心概念是将操作系统和应用程序隔离在一个容器内,这极大地提高了效率和灵活性。
与传统的虚拟化技术相比,Docker提供了一种更轻量级的方案。它是一个进程级别的虚拟化,可以隔离文件、进程和资源等,而无需模拟整个操作系统。此外,Docker容器可以在几乎任何主流的Linux机器上运行,无论是物理机、虚拟机还是云服务器。
如果linux中没有安装过yum工具,首先要安装一个yum工具:
yum install -y yum-utils
接下来配置一个Docker的安装源:
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
然后执行命令安装Docker:
yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
接下来启动一下Docker:
# 启动Docker
systemctl start docker
# 停止Docker
systemctl stop docker
# 重启
systemctl restart docker
# 设置开机自启
systemctl enable docker
# 执行docker ps命令,如果不报错,说明安装启动成功
docker ps
由于Docker的仓库在国外,下载镜像会非常慢,所以需要配置一个阿里云镜像加速。
当我们利用 Docker 安装应用时,Docker 会自动搜索并下载应用镜像 (image) 。镜像不仅包含应用本身,还包含应用运行所需要的环境、配置、系统函数库。
Docker 会在运行镜像时创建一个隔离环境,称为容器 (container)。容器可以形象理解为独属于docker的阉割版微型操作系统。它有自己的内存空间,有自己的文件系统,甚至有自己的网络空间。
镜像仓库,存储和管理镜像的平台。Docker 官方维护了一个公共仓库, Docker.Hub。开发者可以将自己的应用的镜像上传到这个公共仓库之中,比如MySQL、redis等都在公共仓库上有自己的镜像。
镜像就相当于一个“免安装”的软件,只需要下载下来就可以直接启动。不用关心各种配置、依赖、操作系统的差异。
所有的命令都可以通过--help
参数查看它的参数列表以及用法。
docker pull 镜像名称:版本号
:从docker.pub中拉取对应镜像
docker images
:查看所有的本地镜像
docker rmi 镜像名:版本
:删除对应镜像
docker save 参数 镜像名:版本
:将镜像打包到本地
-o 镜像名.tar
:把镜像保存成tar文件,或者也可以tar.gz打包压缩docker load 参数
:将save的镜像tar包重新加载为镜像文件
-i tar文件名
docker run 参数 镜像名:版本
:创建容器并运行一个镜像,如果没有镜像则会自动pull镜像
-d
:让容器在后台运行--name
:给容器起个名字,必须唯一。-p 宿主机端口号:容器端口号
:将创建的容器的端口号3306映射到宿主机的端口号3307。容器拥有自己的IP地址,但是docker的宿主机之外的主机是无法看到容器的。所以只能通过让外部主机访问宿主机,再让宿主机将请求映射到Docker容器的端口中。-e key=value
:设置环境变量,这个是由镜像的制作者决定的。使用者直接上docker.pub直接查看有哪些环境变量,按需要配置即可。可以配置多个-e参数。要在容器中运行的镜像名字
:镜像名称一般由两部分组成,[repository]:[tag],镜像名:版本。比如mysql:5.7。如果没有指定版本号,默认是latest的镜像。(反斜杠是为了美观进行的换行,不加也可)。
docker stop 容器名
:停止容器中线程的运行。比如如果容器中运行的是MySQL,会停掉MySQL。
docker start 容器名
:启动停止的容器
docker ps
:查看各个容器的状态
docker inspect 容器名
:查看具体容器的状态,比如它的数据卷、网络配置等等。
docker rm 容器名
:删除容器
docker logs 容器名
:查看容器运行日志
docker exec -it 容器名 bash
:进入容器内部,使用命令行进行交互
虽然可以使用命令进入容器中,但是由于容器只是一个最小化的系统环境,它只具备运行镜像所需的环境,是不会具备vim等编辑器的,所以容器中的文件无法进行修改。
如果有一个需求:
应该如何修改容器中的html文件呢?这就需要用到数据卷了。
数据卷(volume)是一个虚拟目录,是容器目录与宿主机目录映射的桥梁。想要更改容器中的文件,只能通过数据卷进行修改。
具体来说,需要创建一个数据卷,然后将数据卷挂载到容器目录和宿主机目录,之后容器目录和宿主机目录就建立起了映射,对宿主机目录做修改,都能映射到容器所在目录。
创建、查看、删除数据卷的命令如图。
应该如何挂载数据卷到容器目录和宿主机目录呢?==当创建容器的时候,就需要同时挂载数据卷到容器目录和宿主机目录了,否则创建完容器之后,是无法进行挂载的。==在创建容器(docker run)时,直接加上参数-v 数据卷名:容器内目录
完成容器目录数据卷的挂载(这里没有宿主机目录是因为docker默认根据容器目录创建了一个对应的宿主机目录并将数据卷挂载到该目录了)。可以使用docker inspect 数据卷名
查看数据卷挂载的宿主机目录,然后进入该目录进行操作。
上边宿主机目录是docker自动指定的,实际上也可以自己手动指定。
如果指定本地目录,数据卷的名称默认为该本地目录的名称。例如,如果要将本地的/path/to/local/dir目录挂载到容器中,则数据卷的名称为/path/to/local/dir。
Java项目可以通过docker进行部署,这就需要会使用docker制造一个镜像。
镜像就是包含了应用程序、程序运行的系统函数库、运行配置等文件的文件包。构建镜像的过程其实就是把上述文件打包的过程。
传统部署一个Java应用,通常需要:
看似,镜像中只需要jre和jar包就足够了,但是Java程序需要jre中的jvm去执行,jvm是需要跟操作系统进行交互的,所以还需要准备好具体的操作系统环境。具体来说,是操作系统的系统函数。问题来了,需要用到哪些系统函数呢?
可以使用一个笨方法,直接把整个操作系统的所有系统函数塞到镜像中,这样就绝对没问题。
所以,制作一个镜像的步骤如下:
这四个部分的内容,事实上并不会混合在一起放到镜像中去(当然也可以这样做,但是混在一起并不明智,难道所有的东西都要制作者自己去找吗?)。而是每个部分都作为一个子镜像(有些子镜像已经有别人做好了,比如linux的环境),四个子镜像组合成为一个镜像。在docker中,将这四部分内容称为有四个层(layer)。
最底下的几层称为**基础镜像。**这些镜像都是别人已经做好的通用镜像,开发者可以基于这些镜像来制作自己的镜像。
最上的层称为入口。一般是程序启动的脚本和参数。
开发者只需要描述自己想要制作的镜像的结构(基础镜像、中间层、入口),docker就会自动完成整个镜像的构建。
需要在Dockfile文件中使用指令来描述镜像结构。常见指令如图:
这并不是所有的命令,如果有更多的需求可以查看官方文档。
可以基于Ubuntu的基础镜像自己搭建一个Java应用的镜像。当然,也有更加方便的镜像,已经把Java的所有的环境都配置好,只需要把jar包塞到镜像之中然后配置入口就可以了。
当编写好了Dockfile,可以利用一个命令来将Dockfile构建成镜像:
docker build -t 镜像名:版本 Dockerfile所在目录(如果在当前目录,指定为.)
之后,就是将这个镜像使用docker save打包成tar,然后发给另一个开发者,然后另一个开发者使用docker load加载镜像,然后使用docker run创建容器并运行镜像即可。
docker把Java应用成功部署了,而Java应用是需要用到redis、MySQL等数据库的。redis、MySQL可以做成镜像运行在容器之中。redis和MySQL的容器都有一个ip地址,它们的ip地址是由docker分配的。启动一个镜像,docker会给该镜像的容器分配一个ip地址,第二次启动同一个镜像时,docker会再次给该镜像的容器分配一个ip地址,前后两次分配的ip地址有可能不同。
现在问题来了,Java应用程序中似乎不能知道redis和MySQL的ip地址。
docker是通过自定义网络来解决这个问题的。各个容器之间通过容器名来访问彼此,由docker通过容器名获取该容器的ip地址然后访问该容器,而不是直接通过ip来访问彼此。
docker的自定义网络的常用命令如下:
值得注意的是,每一个容器在创建的时候都会默认指定给一个默认网络bridge,在这个网络中,是不能够通过容器名访问容器的。只能通过ip地址。只有通过自定义网络才能具备容器名访问的功能。
也可以通过创建一个容器的时候(docker run)通过参数--network 自定义网络名
直接将该容器加入自定义网络。使用这种方式,会将默认网络直接覆盖为自定义网络。
在一个springboot工程中,如果它之后要使用docker进行部署,那么配置文件中的mysql数据源信息也要记得修改,数据源的主机应该写为容器的名字,而不是ip地址。
一般在本地进行开发的时候,此时,还没有进入docker环境,所以会使用本机MySQL的ip地址。
在部署的时候,如果要使用docker进行部署,则必须要使用容器名代替配置文件中的ip地址。
如果现在有Java应用程序的镜像,MySQL的镜像,redis的镜像,包括一些微服务中间件的镜像,那么手动部署起来会非常麻烦。
如果手动进行部署,会非常麻烦,这时候,就出现了一种技术,能够将相互关联的镜像同一部署,提高部署的效率。这就是DockerCompose。
DockerCompose 通过一个单独的 docker-compose.yml 模板文件 (YAML 格式)来定义一组相关联的应用容器,帮助我们实现多个相互关联的 Docker 容器的快速部署。
在DockerCompose之中,各个相互关联的容器的整体称为项目(project),每一个容器称为一个服务(service)。各个服务写在一个yml文件之中,之后通过该文件进行统一部署。
实际上,项目文件之中写的内容跟docker run
差不多。docker run
的各种参数实际上就是在描述容器的启动状态,所以项目文件中的每一个服务也需要有这些信息。将来读取该文件进行部署的时候,实际上就是自动化运行了多个docker run
命令。
yml文件之中还可以自定义镜像(仍然需要手动制作Dockerfile,不过免去了生成镜像的命令)。
yml文件之中还可以创建自定义网络。其他容器需要用到自定义网络,只需要写上在yml文件中创建的自定义网络名即可。
最后,DockerCompose文件制作完毕,执行以下命令即可将所有容器一次性创建:
docker compose [options] [command]
以下是docker compose的常用命令(有一个-d参数也很常用,能够让创建的容器在后台运行):
所以,最后,开发者要使用docker部署一整个项目,只需要写Dockerfile文件,然后再写DockerCompose文件即可。
当然,docker的功能还远不止如此。以上学习的内容都仅限于单个服务器中,如果需要集群部署,docker也是可以做到的。不过作为一个后端开发人员,以上知识就足够了,更加深入的docker知识需要运维人员去了解。