【Docker】学习笔记(超万字图文整理)

前言

再此感谢黑马程序员提供的Docker课程

什么是Docker?看这一篇干货文章就够了!

UPD: 补充更新微服务集群、Docker镜像仓库部分内容
所有笔记、生活分享首发于个人博客
想要获得最佳的阅读体验(无广告且清爽),请访问本篇笔记

初识Docker

什么是Docker

  • 微服务虽然具备各种各样的优势,但是服务的拆分通常给部署带来了很大的麻烦
    • 分布式系统中,依赖的组件非常多,不同组件之间部署时,往往会产生一些冲突
    • 在数百,数千台服务中重复部署,环境不一定一致,会遇到各种问题
应用部署的环境问题
  • 大型项目组件比较多,运行环境也较为复杂,部署时会碰到一些问题
    • 依赖关系复杂,容易出现兼容性问题
    • 开发、测试、生产环境有差异
  • 例如一个项目中,部署时需要依赖于node.js、Redis、RabbitMQ、MySQL等,这些服务部署时所需要的函数库、依赖项各不相同,审核会有冲突,给部署带来了极大的困难
Docker解决依赖兼容问题
  • 而Docker巧妙的解决了这些问题,那么Docker是如何实现的呢?
  • Docker为了解决依赖的兼容问题,采用了两个手段
    1. 将应用的函数库(libs)、依赖(Deps)、配置与应用一起打包
    2. 将每个应用放到一个隔离容器去运行,避免相互干扰
  • 这样打包好的应用中,既包含了应用本身,也包含了应用所需要用到的函数库和依赖,无需在操作系统上安装这些,自然也就不存在不同应用之间的兼容问题了
  • 虽然解决了不同应用的兼容问题,但是开发、测试等环节会存在差异,操作系统版本也会有差异,这些问题又该如何解决呢?
Docker解决操作系统环境差异
  • 要解决不同操作系统环境差异问题,必须先了解操作系统结构,以一个Ubuntu操作系统为例,结构如下
    • 系统应用:操作系统本身提供的应用、函数库。这些函数库是对内核指令的封装,使用更加方便
    • 系统内核:所有Linux发行版的内核都是Linux,例如CentOS、Ubuntu、Fedora等。内核可以与计算机硬件交互,对外提供内核指令,用于操作计算机硬件。
    • 计算机硬件:例如CPU、内存、磁盘等
  • 应用于计算机交互的流程如下
    1. 应用调用操作系统应用(函数库),实现各种功能
    2. 系统函数库是对内核指令集的封装,会调用内核指令
    3. 内核指令操作计算机硬件
  • Ubuntu和CentOS都是基于Linux内核,无非是系统应用不同,提供的函数库有差异
  • 此时,如果将一个Ubuntu版本的MySQL应用安装到CentOS系统,MySQL在调用Ubuntu函数库时,会发现找不到或不匹配,就会报错
  • Docker如何解决不同系统环境的问题?
    • Docker将用户程序所需要的系统(比如Ubuntu)函数库一起打包
    • Docker运行到不同操作系统时,直接基于打包的函数库,借助于操作系统的Linux内核来运行
小结
  • Docker如何解决大型项目依赖关系复杂,不同组件依赖的兼容性问题?
    • Docker允许开发中将应用、依赖、函数库、配置一起打包,形成可移植镜像
    • Docker应用运行在容器中,使用沙箱机制,相互隔离
  • Docker如何解决开发、测试、生产环境有差异的问题?
    • Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上运行
  • Docker是一个快速交付应用、运行应用的技术,具备以下优势
    1. 可将程序及其依赖、运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统
    2. 运行时利用沙箱机制形成隔离容器,各个应用互不干扰
    3. 启动、移除都可以通过一行命令完成,方便快捷

Docker与虚拟机的区别

  • Docker可以让一个应用在任何操作系统中都十分方便的运行,而我们以前接触的虚拟机,也能在一个操作系统中,运行另外一个操作系统,保护系统中的任何应用

  • 二者有什么差异呢?

    • 虚拟机(virtual machine)是在操作系统中模拟硬件设备,然后运行另一个操作系统。例如在Windows系统中运行CentOS系统,就可以运行任意的CentOS应用了

    • Docker仅仅是封装函数库,并没有模拟完整的操作系统

    • 对比来看

      | 特性 | Docker | 虚拟机 |

      | --- | --- | --- |

      | 性能 | 接近原生 | 性能较差 |

      | 硬盘占用 | 一般为MB | 一般为GB |

      | 启动 | 秒级 | 分钟级 |

  • 小结:Docker和虚拟机的差异

    • Docker是一个系统进程;虚拟机是在操作系统中操作系统
    • Docker体积小、启动速度快、性能好;虚拟机体积大、启动速度慢、性能一般

Docker架构

镜像和容器
  • Docker中有几个重要的概念
    • 镜像(Image):Docker将应用程序及其所需要的依赖、函数库、环境、配置等文件打包在一起,称为镜像
    • 容器(Container):镜像中的应用程序形成后的进程就是容器,只是Docker会给容器进程做隔离,对外不可见
  • 一切应用最终都是代码组成,都是硬盘中的一个个字节形成的文件,只有运行时,才会加载到内存,形成进程
  • 而镜像,就是吧一个应用在硬盘上的文件、机器运行环境、部分系统函数库文件一起打包成的文件包。这个文件包是只读的(防止你对镜像文件进行修改/污染,从而导致镜像不可用,容器从镜像中拷贝一份文件到自己的空间里来写数据)
  • 而容器呢,就是把这些文件中编写的程序、函数加载到内存中允许形成进程,只不过要隔离起来。因此一个镜像可以启动多次,形成多个容器进程。
DockerHub
  • 开源应用程序非常多,打包这些应用往往都是重复性劳动,为了避免这些重复劳动,人们就会将自己打包的应用镜像,例如Redis、MySQL镜像放到网络上来共享使用,就像GitHub的代码共享一样
    • DockerHub:DockerHub是一个官方的Docker镜像托管平台,这样的平台称为Docker Registry。
    • 国内也有类似于DockerHub的公开服务,例如网易云镜像服务阿里云镜像库
  • 我们一方面可以将自己的镜像共享到DockerHub,另一方面也可以从DockerHub拉取镜像
Docker架构
  • 我们要使用Docker来操作镜像、容器,那就必须安装Docker
  • Docker是一个CS架构的程序,由两部分组成
    • 服务端(server):Docker守护进程,负责处理Docker指令,管理镜像、容器等
    • 客户端(client):通过命令或RestAPI向Docker服务端发送指令,可以在本地或远程向服务端发送指令
小结
  • 镜像:
    • 将应用程序及其依赖、环境、配置打包在一起
  • 容器:
    • 镜像运行起来就是容器,一个镜像可以运行多个容器
  • Docker结构:
    • 服务端:接受命令或远程请求,操作镜像或容器
    • 客户端:发送命令或者请求到Docker服务端
  • DockerHub:
    • 一个镜像托管的服务器,类似的还有阿里云镜像服务,统称为DockerRegistry

安装Docker

  • Docker 分为 CE 和 EE 两大版本。CE 即社区版,免费,支持周期 7 个月;EE 即企业版,强调安全,付费使用,支持周期 24 个月。
  • Docker CE 分为 stable test 和 nightly 三个更新频道。
  • 官方网站上有各种环境下的 安装指南,这里主要介绍 Docker CE 在 CentOS上的安装。
  • Docker CE 支持 64 位版本 CentOS 7,并且要求内核版本不低于 3.10

CentOS 7满足最低内核要求,本文也是在CentOS 7安装Docker

卸载(可选)

  • 如果之前安装过旧版本的Docker,可以使用下面命令卸载
bash 复制代码
yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine \
                  docker-ce

安装Docker

  • 首先先安装yum工具
bash 复制代码
yum install -y yum-utils \
           device-mapper-persistent-data \
           lvm2 --skip-broken
  • 然后更新本地镜像源
bash 复制代码
## 设置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
  • 然后安装社区版Docker
bash 复制代码
yum install -y docker-ce

启动Docker

  • Docker应用需要用到各种端口,挨个修改防火墙设置很麻烦,所以这里建议直接关闭防火墙
bash 复制代码
## 关闭
systemctl stop firewalld
## 禁止开机启动防火墙
systemctl disable firewalld
  • 通过命令启动Docker
bash 复制代码
## 启动docker服务
systemctl start docker 

## 停止docker服务
systemctl stop docker

## 重启docker服务
systemctl restart docker
  • 然后输入命令,查看docker版本
bash 复制代码
docker -v
  • 输出
bash 复制代码
[root@localhost ~]# docker -v
Docker version 26.0.0, build 2ae903e

配置镜像加速

Docker的基本操作

镜像制作

镜像名称
  • 首先来看下镜像的名称组成:
    • 镜像名称一般分为两部分:[repository]:[tag]
  • 例如mysql:5.7,这里的mysql就是repository,5.7就是tag,合在一起就是镜像名称,代表5.7版本的MySQL镜像
    • 在没有指定tag时,默认是latest,代表最新版本的镜像,例如mysql:latest
镜像命令
  • 常见的镜像命令如下图
案例一
  • 在这个案例,我们来联系拉取、查看镜像

需求:从DockerHub中拉取一个Nginx镜像并查看

  1. 首先我们取镜像仓库(例如DockerHub)中搜索Nginx镜像
  2. 根据查看到的镜像名称,拉取自己需要的镜像,通过命令:docker pull nginx拉取最新的nginx镜像
bash 复制代码
[root@localhost ~]# docker pull nginx
Using default tag: latest

latest: Pulling from library/nginx
a2abf6c4d29d: Pull complete 
a9edb18cadd1: Pull complete 
589b7251471a: Pull complete 
186b1aaa4aa6: Pull complete 
b4df32aa5a72: Pull complete 
a0bcbecc962e: Pull complete 
Digest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
[root@localhost ~]# 
[root@localhost ~]# 

从日志中我们也可以看出,如果不加tag,用的就是默认的latest,也就是拉取最新的docker镜像

  1. 通过命令docker images查看拉取到的镜像
bash 复制代码
[root@localhost ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
nginx        latest    605c77e624dd   2 years ago   141MB
[root@localhost ~]# 
案例二
  • 在这个案例中,我们练习保存、导入镜像

需求:利用docker save将nginx镜像导出磁盘,然后在通过docker load加载回来

  1. 利用docker xx --help命令查看docker save和docker load的语法
  • 输入docker save --help,结果如下
bash 复制代码
[root@localhost ~]# docker save --help

Usage:  docker save [OPTIONS] IMAGE [IMAGE...]

Save one or more images to a tar archive (streamed to STDOUT by default)

Aliases:
  docker image save, docker save

Options:
  -o, --output string   Write to a file, instead of STDOUT
  • 输入docker load --help,结果如下
bash 复制代码
[root@localhost ~]## docker load --help

Usage:  docker load [OPTIONS]

Load an image from a tar archive or STDIN

Options:
  -i, --input string   Read from tar archive file, instead of STDIN
  -q, --quiet          Suppress the load output
  1. 使用docker save导出镜像到磁盘,随后使用ls命令可以查看到nginx.tar文件
bash 复制代码
[root@localhost ~]# docker save -o nginx.tar nginx:latest
  1. 使用docker save导出镜像到磁盘,随后使用ls命令可以查看到nginx.tar文件
bash 复制代码
[root@localhost ~]# ls
anaconda-ks.cfg  nginx.tar
  1. 使用docker load加载镜像,在此之前,我们使用命令删除本地nginx镜像
bash 复制代码
docker rmi nginx:latest ## rmi是remove image的缩写

随后运行命令,加载本地文件

bash 复制代码
docker load -i nginx.tar
练习

需求:去DockerHub中搜索并拉取一个Redis镜像

  1. 去DockerHub中搜索Redis镜像
  2. 查看Redis镜像的名称和版本
  3. 利用docker pull命令拉取镜像
  4. 使用docker save命令,将redis:latest打包成一个redis.tar包
  5. 使用docker rmi删除本地的redis:latest
  6. 利用docker load重新加载redis.tar文件

容器操作

容器相关命令
  • 容器操作命令如图
  • 容器保护三个状态
    • 运行:进程正常运行
    • 暂停:进程暂停,CPU不再运行,不释放内存
    • 停止:进程终止,回收进程占用的内存、CPU等资源
  • 暂停和停止的操作系统的处理方式不同,暂停是操作系统将容器内的进程挂起,容器关联的内存暂存起来,然后CPU不再执行这个进程,但是使用unpause命令恢复,内存空间被恢复,程序继续运行。
  • 停止是直接将进程杀死,容器所占的内存回收,保存的仅剩容器的文件系统,也就是那些静态的资源
  • docker rm 是将文件系统也彻底删除,也就是将容器彻底删除掉了
  • docker run:创建并运行一个容器,处于运行状态
  • docker pause:让一个运行的容器暂停
  • docker unpause:让一个容器从暂停状态恢复运行
  • docker stop:停止一个运行的容器
  • docker start:让一个停止的容器再次运行
  • docker rm:删除一个容器
案例一
  • 创建并运行nginx容器的命令
bash 复制代码
docker run --name containerName -p 80:80 -d nginx
  • 命令解读
    • docker run:创建并运行一个容器
    • --name:给容器起一个名字,例如叫做myNginx
    • -p:将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口
    • -d:后台运行容器
    • nginx:镜像名称,例如nginx
  • 这里的-p参数,是将容器端口映射到宿主机端口
  • 默认情况下,容器是隔离环境,我们直接访问宿主机的80端口,肯定访问不到容器中的nginx
  • 现在,容器的80端口和宿主机的80端口关联了起来,当我们访问宿主机的80端口时,就会被映射到容器的80端口,这样就能访问nginx了
  • 那我们再浏览器输入虚拟机ip:80就能看到nginx默认页面了
  • 查看日志记录
bash 复制代码
[root@localhost ~]# docker logs myNginx
案例二

需求:进入Nginx容器,修改HTML文件内容,添加Welcome To My Blog!

提示:进入容器要用到docker exec命令

bash 复制代码
[root@localhost ~]## docker exec --help

Usage:  docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

Run a command in a running container

Options:
  -d, --detach               Detached mode: run command in the background
      --detach-keys string   Override the key sequence for detaching a container
  -e, --env list             Set environment variables
      --env-file list        Read in a file of environment variables
  -i, --interactive          Keep STDIN open even if not attached
      --privileged           Give extended privileges to the command
  -t, --tty                  Allocate a pseudo-TTY
  -u, --user string          Username or UID (format: <name|uid>[:<group|gid>])
  -w, --workdir string       Working directory inside the container
  1. 进入容器。进入刚刚我们创建好的nginx容器
bash 复制代码
docker exec -it myNginx bash
  • 命令解读
    • docker exec:进入容器内部,执行一个命令
    • -it:给当前进入的容器创建一个标准输入、输出终端,允许我们与容器交互
    • myNginx:要进入的容器名称
    • bash:进入容器后执行的命令,bash是一个linux终端交互命令
  1. 进入nginx的HTML所在目录
  • 容器内部会模拟一个独立的Linux文件系统,看起来就如同一个linux服务器一样,nginx的环境、配置、运行文件全部都在这个文件系统中,包括我们要修改的html文件
  • 查看DockerHub网站中的nginx页面,可以知道nginx的html目录位置在/usr/share/nginx/html
  • 我们执行命令进入到该目录
bash 复制代码
cd /usr/share/nginx/html

查看目录下的文件

bash 复制代码
root@10c40cc80352:/usr/share/nginx/html# ls
50x.html  index.html
  1. 修改index.html的内容
  • 容器内没有vi命令,无法直接修改,我们使用下面的命令来修改
bash 复制代码
sed -i -e 's#Welcome to nginx#Welcome To My Blog#g' index.html
  1. 在浏览器访问自己的虚拟机ip:80,即可看到结果(80端口可以不写)

5.退出

bash 复制代码
root@10c40cc80352:/usr/share/nginx/html# exit
小结
  • docker run命令常见的参数有哪些?
    • --name:指定容器名称
    • -p:指定端口映射
    • -d:让容器后台运行
  • 查看容器日志的命令
    • docker logs
    • 添加-f参数可以持续查看日志
  • 查看容器状态:
    • docker ps
    • docker ps -a 查看所有容器,包括已停止的

现在是不是感觉修改文件好麻烦,因为没给提供vi命令,不能直接编辑,所以这就要用到我们下面说的数据卷了

数据卷

  • 在之前的nginx案例中,修改nginx的html页面时,需要进入nginx内部。并且因为没有编译器,修改文件也很麻烦,这就是容器与数据(容器内文件)耦合带来的后果,如果我们另外运行一台新的nginx容器,那么这台新的nginx容器也不能直接使用我们修改好的html文件,具有很多缺点
    1. 不便于修改:当我们要修改nginx的html内容时,需要进入容器内部修改,很不方便
    2. 数据不可复用:由于容器内的修改对外是不可见的,所有的修改对新创建的容器也是不可复用的
    3. 升级维护困难:数据在容器内,如果要升级容器必然删除旧容器,那么旧容器中的所有数据也跟着被删除了(包括改好的html页面)
  • 要解决这个问题,必须将数据和容器解耦,这就要用到数据卷了
什么是数据卷
  • 数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录

  • 一旦完成数据卷挂载,对容器的一切操作都会作用在对应的宿主机目录了。这样我们操作宿主机的/var/lib/docker/volumes/html目录,就等同于操作容器内的/usr/share/nginx/html目录了

数据集操作命令
  • 数据卷操作的基本语法如下
bash 复制代码
docker volume [COMMAND]
  • docker volume命令是数据卷操作,根据命令后跟随的command来确定下一步的操作
    • create:创建一个volume
    • inspect:显示一个或多个volume的信息
    • ls:列出所有的volume
    • prune:删除未使用的volume
    • rm:删除一个或多个指定的volume
创建和查看数据卷

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

  1. 创建数据卷
bash 复制代码
docker volume create html
  1. 查看所有数据
bash 复制代码
docker volume ls

结果

bash 复制代码
[root@localhost ~]## docker volume ls
DRIVER    VOLUME NAME
local     html
  1. 查看数据卷详细信息卷
bash 复制代码
docker volume inspect html
bash 复制代码
[root@localhost ~]# docker volume inspect html
[
    {
        "CreatedAt": "2024-04-02T23:29:49+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/html/_data",
        "Name": "html",
        "Options": null,
        "Scope": "local"
    }
]

可以看到我们创建的html这个数据卷关联的宿主机目录为/var/lib/docker/volumes/html/_data

  • 小结:
    • 数据卷的作用
      • 将容器与数据分离,解耦合,方便操作容器内数据,保证数据安全
    • 数据卷操作:
      • docker volume create:创建数据卷
      • docker volume ls:查看所有数据卷
      • docker volume inspect:查看数据卷详细信息,包括关联的宿主机目录位置
      • docker volume rm:删除指定数据卷
      • rocker volume prune:删除所有未使用的数据卷
挂载数据卷
  • 我们在创建容器时,可以通过-v参数来挂载一个数据卷到某个容器内目录,命令格式如下
bash 复制代码
docker run \
    -- name myNginx \
    -v html:/root/html \
    -p 8080:80 \
    nginx \
  • 这里的-v就是挂载数据卷的命令
    • -v html:/root/html:把html数据卷挂载到容器内的/root/html这个目录中
案例一

需求:创建一个nginx容器,修改容器内的html目录的index.html内容

分析:上个案例中,我们进入nginx容器内部,已经知道了nginx的html目录所在位置/usr/share/nginx/html,我们需要把这个目录挂载到html这个数据卷上,方便操作其中的内容

提示:运行容器时,使用-v参数挂载数据卷

  1. 创建容器并挂载数据卷到容器内的HTML目录
bash 复制代码
docker run --name myNginx -v html:/usr/share/nginx/html -p 80:80 -d nginx
  1. 进入html数据卷所在位置,并修改HTML内容
bash 复制代码
## 查看数据卷位置
docker volume inspect html
## 进入该目录
cd /var/lib/docker/volumes/html/_data
## 修改文件
vi index.html
## 也可以在FinalShell中使用外部编译器(例如VSCode)来修改文件
案例二
  • 容器不仅仅可以挂载数据卷,也可以直接挂载到宿主机目录上,关系如下
    • 带数据卷模式:宿主机目录 --> 数据卷 --> 容器内目录
    • 直接挂载模式:宿主机目录 --> 容器内目录
  • 目录挂载和数据卷挂载的语法是类似的
    • -v [宿主机目录]:[容器内目录]
    • -v [宿主机文件]:[容器内文件]

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

  1. 从DockerHub中拉取一个MySQL的镜像
bash 复制代码
docker pull mysql
  1. 创建目录/tmp/mysql/data
bash 复制代码
mkdir -p /tmp/mysql/data
  1. 创建目录/tmp/mysql/conf,将myCnf.cnf文件上传到/tmp/mysql/conf
bash 复制代码
mkdir -p /tmp/mysql/conf
dockerfile 复制代码
[mysqld]
skip-name-resolve
character_set_server=utf8
datadir=/var/lib/mysql
server-id=1000
  1. 去DockerHub中查阅资料,找到mysql容器内的conf目录和data目录的位置
    容器中conf目录的位置是:/etc/mysql/conf.d
    容器中存储数据的目录为:/var/lib/mysql
  2. 创建并运行MySQL容器,要求
  • 挂载/tmp/mysql/data到mysql容器内数据存储目录
  • 挂载/tmp/mysql/conf/myCnf.cnf到mysql容器的配置文件
  • 设置MySQL密码
bash 复制代码
docker run \
--name mysql \ 
-e MYSQL_ROOT_PASSWORD=123456 \
-v /tmp/mysql/conf:/etc/mysql/conf.d \
-v /tmp/mysql/data:/var/lib/mysql \
-p 3306:3306 \
-d mysql
  1. 尝试使用Navicat连接数据库,注意自己设置的密码
小结
  • docker run的命令中通过-v参数挂载文件或目录到容器中
    • -v [volume名称]:[容器内目录]
    • -v [宿主机文件]:[容器内文件]
    • -v [宿主机目录]:[容器内目录]
  • 数据卷挂载与目录直接挂载的区别
    • 数据卷挂载耦合度低,由docker来管理目录,但是目录较深,不好找
    • 目录挂载耦合度高,需要我们自己管理目录,不过目录容易寻找查看

Dockerfile自定义镜像

  • 常见的镜像在DockerHub就能找到,但是我们自己写的项目就必须构建镜像了。而要自定义镜像,则必须先连接镜像的结构才行。

镜像结构

  • 镜像是将应用程序 及其需要的系统函数库,环境、配置、依赖打包而成
  • 以MySQL为例,来看看它的镜像组成结构
  • 简单来说,镜像就是在系统函数库、运行环境的基础上,添加应用程序文件、配置文件、依赖文件等组合,然后编写好启动脚本打包在一起形成的文件
  • 我们要构建镜像,其实就是实现上述打包的过程

Dockerfile语法

  • 构建自定义镜像时,并不需要一个个文件去拷贝,打包。
  • 我们只需要告诉Docker我们的镜像组成,需要哪些BaseImage、需要拷贝什么文件、需要安装什么依赖、启动脚本是什么,将来Docker会帮助我们构建镜像
  • 而描述上述信息的就是Dockerfile文件。
  • Dockerfile就是一个文本文件,其中包含一个个指令(Instruction),用指令说明要执行什么操作来构建镜像,每一个指令都会形成一层Layer。
    | 指令 | 说明 | 示例 |
    | --- | --- | --- |
    | FROM | 指定基础镜像 | FROM centos:6 |
    | ENV | 设置环境变量,可在后面指令使用 | ENV key value |
    | COPY | 拷贝本地文件到镜像的指定目录 | COPY ./mysql-5.7.rpm /tmp |
    | RUN | 执行Linux的shell命令,一般是安装过程的命令 | RUN yum install gcc |
    | EXPOSE | 指定容器运行时监听的端口,是给镜像使用者看的 | EXPOSE 8080 |
    | ENTRYPOINT | 镜像中应用的启动命令,容器运行时调用 | ENTRYPOINTjava -jar xxjar |

构建Java项目

基于Ubuntu构建Java项目

需求:基于Ubuntu镜像构建一个新镜像,运行一个Java项目

  1. 创建一个空文件夹docker-demo
bash 复制代码
mkdir /tmp/docker-demo
  1. 将docker-demo.jar文件拷贝到docker-demo这个目录
  2. 拷贝jdk8.tar.gz文件到docker-demo这个目录
  3. 在docker-demo目录下新建Dockerfile,并写入以下内容
dockerfile 复制代码
## 指定基础镜像
FROM ubuntu:16.04

## 配置环境变量,JDK的安装目录
ENV JAVA_DIR=/usr/local

## 拷贝jdk的到JAVA_DIR目录下
COPY ./jdk8.tar.gz $JAVA_DIR/

## 安装JDK
RUN cd $JAVA_DIR && tar -xf ./jdk8.tar.gz && mv ./jdk1.8.0_44 ./java8

## 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin

## 拷贝java项目的包到指定目录下,我这里是/tmp/app.jar
COPY ./docker-demo.jar /tmp/app.jar

## 暴露端口,注意这里是8090端口,如果你之前没有关闭防火墙,请关闭防火墙或打开对应端口,云服务器同理
EXPOSE 8090

## 入口,java项目的启动命令
ENTERPOINT java -jar /tmp/app.jar
  1. 在docker-demo目录下使用docker build命令构建镜像
bash 复制代码
docker build -t javaweb:1.0 .##注意1.0和 .之间有个空格
  • 最后有个 . 别忘记
  1. 使用docker images命令,查看镜像
bash 复制代码
[root@localhost ~]# docker images
REPOSITORY   TAG            IMAGE ID       CREATED        SIZE
javaweb      1.0            b41e763854e5   16 hours ago   722MB
nginx        latest         605c77e624dd   2 years ago    141MB
redis        latest         7614ae9453d1   2 years ago    113MB
mysql        latest         3218b38490ce   2 years ago    516MB
rabbitmq     3-management   6c3c2a225947   2 years ago    253MB
  1. 创建并运行一个docker_demo容器
dockerfile 复制代码
docker run --name web -p 8090:8090 -d javaweb:1.0
  1. 浏览器访问192.168.200.128:8090/hello/count, 即可看到页面效果(注意修改虚拟机ip )->"今天被访问了1次"
基于Java8构建Java项目
  • 虽然我们可以基于Ubuntu基础镜像,添加任意自己需要的安装包来构建镜像,但是却比较麻烦。所以大多数情况下,我们都可以在一些安装了部分软件的基础镜像上做改造。
  • 我们刚刚构建的Java项目有一个固定的死步骤,那就是安装JDK并配置环境变量,我们每次构建Java项目的镜像的时候,都需要完成这个步骤,所以我们可以找一个已经安装好了JDK的基础镜像,然后在其基础上来构建我们的Java项目的镜像

需求:基于java:8-alpine镜像,将一个Java项目构建为镜像

  1. 新建一个空目录(或者继续使用/tmp/docker-demo目录)
  2. 将docker-demo.jar复制到该目录下(继续使用刚刚的目录就不用管)
  3. 在目录中新建一个文件,命名为Dockerfile,并编写该文件
dockerfile 复制代码
## 将openjdk:8作为基础镜像
FROM openjdk:8
## 拷贝java项目的包到指定目录下,我这里是/tmp/app.jar
COPY ./docker-demo.jar /tmp/app.jar
## 暴露端口
EXPOSE 8090
## 入口
ENTRYPOINT java -jar /tmp/app.jar
  1. 构建镜像
bash 复制代码
docker build -t javaweb:2.0 .
  1. 创建并运行一个docker_demo容器(在此之前停止之前的docker_demo容器)
bash 复制代码
docker run --name web2 -p 8090:8090 -d javaweb:2.0
  1. 浏览器访问192.168.200.128:8090/hello/count,即可看到页面效果->"今天被访问了1次"

小结

  1. Dockerfile本质就是一个文件,通过指令描述镜像的构建过程
  2. Dockerfile的第一行必须是FROM,从一个基础镜像来构建
  3. 基础镜像可以使基本操作系统,如Ubunut,也可以是其他人制作好的镜像,例如openjdk:8

Docker-Compose

  • Docker Compose可以基于Compose文件帮我们快速地部署分布式应用,而无需手动一个个创建和运行容器
  • 真实企业项目开发中,可能有几十个,上百个微服务。

初识DockerCompose

  • Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行,格式如下
bash 复制代码
version: "3.8"
  services:
    ## docker run --name mysql -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 -v /tmp/mysql/data:/var/lib/mysql -v /tmp/mysql/conf/myCnf.cf:/etc/mysql/conf.d/myCnf.cnf -d mysql:5.7.25
    mysql:  ## 对应docker run中的 --name
      image: mysql:5.7.25 ## 对应docker run中最后声明的镜像
      enviroment:   ## 对应docker run中的 -e MYSQL_ROOT_PASSWIRD=123456
        MYSQL_ROOT_PASSWORD: 123456
      volumes: ## 对应docker run中的 -v /tmp/mysql/data:/var/lib/mysql
        - "/tmp/mysql/data:/var/lib/mysql"
        - "/tmp/mysql/conf/myCnf.cf:/etc/mysql/conf.d/myCnf.cnf"
    ## 这里并不需要-d参数来后台运行,因为此种方法默认就是后台运行
    ## 同时也不需要暴露端口,在微服务集群部署中,MySQL仅仅是供给给集群内的服务使用的,所以不需要对外暴露端口

    ## 临时构建镜像并运行,下面的配置文件包含了docker build和docker run两个步骤
    ## docker build -t web:1.0 .
    ## docker run --name web -p 8090:8090 -d web:1.0
    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命令写到一个文件,只是语法稍有差异

安装DockerCompose

  • 在Linux下使用命令下载
bash 复制代码
## 安装curl -L https://github.com/docker/compose/releases/download/1.23.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
  • 修改文件权限
bash 复制代码
    chmod +x /usr/local/bin/docker-compose
  • Base自动补全命令
bash 复制代码
curl -L https://raw.githubusercontent.com/docker/compose/1.29.1/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose

如出现错误Failed connect to raw.githubusercontent.com:443; Connection refused,需要修改自己的hosts文件

bash 复制代码
echo "199.232.68.133 raw.githubusercontent.com" >> /etc/hosts

部署微服务集群

需求:将之前学习的cloud-demo微服务集群利用DockerCompose部署

  • 实现思路
    1. 编写docker-compose文件
    2. 修改自己的cloud-demo项目,将其中的数据库、nacos地址,都重命名为docker-compose中的服务名
    3. 使用maven打包工具,将项目中的每个微服务都打包为app.jar(打包名与Dockerfile中一致即可)
    4. 将打包好的app.jar拷贝到cloud-demo中的每一个对应的子目录中,编写Dockerfile文件
    5. 将cloud-demo上传至虚拟机,利用docker-compose up -d来部署
compose文件
  • 针对我们之前写的cloud-demo,来编写对应的docker-compose文件
yml 复制代码
version: "3.2"

services:
  nacos:
    image: nacos/nacos-server
    environment:
      MDOE: standalone
    ports:
      - "8848:8848"
    mysql:
      image: mysql:5.7.25
      environment:
        MYSQL_ROOT_PASSWORD: root
      volumes:
        - "$PWD/mysql/data:/var/lib/mysql"  ## 这里的$PWD是执行linux命令,获取当前目录
        - "$PWD/mysql/conf:/etc/mysql/conf.d"
    userservice:
      build: ./user-service
    orderservice:
      build: ./order-service
    gateway:
      build: ./gateway
      poets:
        - "10010:10010"
  • 其中包含了5个服务:
    1. nacos:作为注册中心和配置中心
      • image: nacos/nacos-server:基于nacos/nacos-server镜像构建
      • environment: 环境变量
        • MODE: standalone:单点模式启动
      • ports:端口映射,这里暴露了8848端口
    2. mysql:数据库
      • image: mysql5.7.25:基于5.7.25版本的MySQL镜像构建
      • environment:环境变量
        • MYSQL_ROOT_PASSWORD: root:设置数据库root账户密码为root
      • volumes:数据卷挂载,这里挂载了mysql的data和conf目录
    3. userservice:基于Dockerfile临时构建,userservice不需要暴露端口,网关才是微服务的入口,如果暴露了userservice的端口,那么网关的身份认证,权限校验就形同虚设了
    4. orderservice:基于Dockerfile临时构建,不需要暴露端口,理由同上
    5. gateway:基于Dockerfile临时构建,网关需要暴露端口,它是其他微服务的入口
修改微服务配置
  • 使用Docker Compose部署时,所有的服务之间都可以用服务名互相访问,那我们现在就需要修改我们cloud-demo中的yml配置文件,如下
yml 复制代码
## bootstrap.yml
spring:
  cloud:
    nacos:
      ## server-addr: localhost:80 #Nacos地址
      server-addr: nacos:8848 ## 使用compose中的服务名来互相访问,用nacos替换localhost
      config:
        file-extension: yaml ## 文件后缀名
yml 复制代码
## application.yml
server:
  port: 8081
spring:
  datasource:
    ## url: jdbc:mysql://mysql:3306/cloud_user?useSSL=false
    url: jdbc:mysql://mysql:3306/cloud_user?useSSL=false ## 这里同理,使用mysql替换localhost
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
打包
  • 将我们修改好的代码打包,注意修改pom文件指定打包名为app
xml 复制代码
<build>
    <!-- 服务打包的最终名称 -->
    <finalName>app</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
  • 之后使用maven工具打包
拷贝jar包到部署目录,并编写Dockerfile文件

{% tabs test4 %}

yml 复制代码
FROM openjdk:8
COPY ./app.jar /tmp/app.jar
ENTERPOINT java -jar /tmp/app.jar
yml 复制代码
FROM openjdk:8
COPY ./app.jar /tmp/app.jar
ENTERPOINT java -jar /tmp/app.jar
yml 复制代码
FROM openjdk:8
COPY ./app.jar /tmp/app.jar
ENTERPOINT java -jar /tmp/app.jar

{% endtabs %}

部署
  • 将cloud-demo上传到虚拟机,进入目录,执行以下命令
bash 复制代码
docker-compose up -d
  • 启动之后查看日志,会发现日志中报错 com.alibaba.nacos.api.exception.NacosException: failed to req API:/nacos/v1/ns/instance/list after all servers([nacos:8848]) tried: java.net.ConnectException: Connection refused (Connection refused)
bash 复制代码
docker-compose logs -f

阿里巴巴nacos连接失败,其原因是userservice在nacos之前启动了,而nacos启动太慢了,userservice注册失败,而且也没有重试机制(等nacos启动完成后,重试注册,就可以避免这个问题)

  • 所以建议nacos单独先启动,其他服务后启动,我这里的解决方案是重启另外三个服务
  • 重启gateway userservice orderservice服务
bash 复制代码
docker-compose restart gateway userservice orderserivce 
  • 查看userservice启动日志,这次就不报错了
bash 复制代码
docker-compose logs -f userservice
  • 在浏览器中访问192.168.129.128:10010/user/1?authorization=admin就能看到如下界面了!(换成自己的虚拟机ip)
  • (我这里的ip和上文用到的ip不一样,是因为我把虚拟机的网络配置恢复默认了,系统又分配了个新的地址给我)

如果还是启动失败,建议将自己的虚拟机内存调大一些,实测只开这些服务最多只占用3G内存!

Docker镜像仓库

搭建私有镜像仓库

  • 我们自己编写的项目显然是不适合放到Docker的共有仓库的,所以需要我们搭建一个私服
配置Docker信任地址
  • 我们的私服采用的是http协议,默认不被Docker信任,所以需要做一个配置:
bash 复制代码
## 打开要修改的文件
vi /etc/docker/daemon.json
## 添加内容:
"insecure-registries":["http://192.168.129.128:8080"]
## 重加载
systemctl daemon-reload
## 重启docker
systemctl restart docker
带图形化界面版本
  • 使用DockerCompose部署带有图象界面的DockerRegistry,命令如下:
yml 复制代码
version: '3.0'
services:
  registry:
    image: registry
    volumes:
      - ./registry-data:/var/lib/registry
  ui:
    image: joxit/docker-registry-ui:static
    ports:
      - 8080:80
    environment:
      - REGISTRY_TITLE=XINGYE'S私有仓库
      - REGISTRY_URL=http://registry:5000
    depends_on:
      - registry

推送、拉取镜像

  • 推送镜像到私有镜像服务必须先tag,步骤如下

    1. 重新tag本地镜像,名称前缀为私有仓库的地址:192.168.129.128:8080/
    bash 复制代码
    docker tag nginx:latest 192.168.129.128:8080/nginx:1.0
    1. 推送镜像
    bash 复制代码
    docker push 192.168.129.128:8080/nginx:1.0


    3. 拉取镜像

    bash 复制代码
    docker pull 192.168.129.128:8080/nginx:1.0
相关推荐
Mitch3113 分钟前
【漏洞复现】CVE-2015-5531 Arbitrary File Reading
web安全·elasticsearch·网络安全·docker·漏洞复现
虾球xz20 分钟前
游戏引擎学习第55天
学习·游戏引擎
oneouto37 分钟前
selenium学习笔记(二)
笔记·学习·selenium
sealaugh3242 分钟前
aws(学习笔记第十九课) 使用ECS和Fargate进行容器开发
笔记·学习·aws
炭烤玛卡巴卡1 小时前
学习postman工具使用
学习·测试工具·postman
我自飞扬临天下2 小时前
Docker常用命令
docker
thesky1234562 小时前
活着就好20241224
学习·算法
蜗牛hb2 小时前
VMware Workstation虚拟机网络模式
开发语言·学习·php
汤姆和杰瑞在瑞士吃糯米粑粑2 小时前
【C++学习篇】AVL树
开发语言·c++·学习
虾球xz2 小时前
游戏引擎学习第58天
学习·游戏引擎