【Springcloud微服务】Docker下篇

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Springcloud微服务
🌠 首发时间:2024年6月22日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾

Docker基础之镜像

镜像就是包含了应用程序、程序运行的系统函数库、运行配置等文件的文件包。构建镜像的过程其实就是把上述文件打包的过程。

镜像结构

要想自己构建镜像,必须先了解镜像的结构。

之前我们说过,镜像之所以能让我们快速跨操作系统部署应用而忽略其运行环境、配置,就是因为镜像中包含了程序运行需要的系统函数库、环境、配置、依赖。

因此,自定义镜像本质就是依次准备好程序运行的基础环境、依赖、应用本身、运行配置等文件,并且打包而成。

举个例子,如果我们要从 0 部署一个 Java 应用,大概流程是这样的:

  • 准备一个 linux 服务(CentOS 或者 Ubuntu 均可)
  • 安装并配置 JDK
  • 上传 Jar
  • 运行 jar

因此,我们打包镜像也是分成这么几步:

  • 准备 Linux 运行环境(java 项目并不需要完整的操作系统,仅仅是基础运行环境即可)
  • 安装并配置 JDK
  • 拷贝 jar
  • 配置启动脚本

上述步骤中的每一次操作其实都是在生产一些文件(系统运行环境、函数库、配置最终都是磁盘文件),所以镜像就是一堆文件的集合

但需要注意的是,镜像文件不是随意堆放的,而是按照操作的步骤分层叠加而成,每一层形成的文件都会单独打包并标记一个唯一 id,称为Layer(层)。这样,如果我们构建时用到的某些层其他人已经制作过,就可以直接拷贝使用这些层,而不用重复制作。

例如,第一步中需要的 Linux 运行环境,通用性就很强,所以 Docker 官方就制作了这样的只包含 Linux 运行环境的镜像。我们在制作 java 镜像时,就无需重复制作,直接使用 Docker 官方提供的 CentOSUbuntu 镜像作为基础镜像。在此基础上再搭建其它层即可,这样逐层搭建,最终整个 Java 项目的镜像结构如图所示:

在这里插入图片描述

Dockerfile

由于制作镜像的过程中,需要逐层处理和打包,比较复杂,所以 Docker 就提供了自动打包镜像的功能。我们只需要将打包的过程,每一层要做的事情用固定的语法写下来,交给 Docker 去执行即可。

而这种记录镜像结构的文件就称为 Dockerfile,其对应的语法可以参考官方文档:https://docs.docker.com/reference/dockerfile/

其中的语法比较多,比较常用的有:

指令说明示例
FROM指定基础镜像FROM centos:6
ENV设置环境变量,可在后面指令使用ENV key value
COPY拷贝本地文件到镜像的指定目录下COPY ./xx.jar /tmp/app.jar
RUN执行 Linux 的 shell 命令,一般是安装过程的命令RUN yum install gcc
EXPOSE指定容器运行时监听的端口,是给镜像使用者看的EXPOSE 8080
ENTRYPOINT镜像中应用的启动命令,容器运行时调用ENTRYPOINT java -jar xx.jar

例如,如果要基于 Ubuntu 镜像来构建一个 Java 应用,其 Dockerfile 内容如下:

# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录、容器内时区
ENV JAVA_DIR=/usr/local
ENV TZ=Asia/Shanghai
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 设定时区
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 安装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 8080
# 入口,java项目的启动命令
ENTRYPOINT ["java", "-jar", "/app.jar"]

其实,我们仔细思考后会发现,将一个 Java 项目打包为镜像,都需要 Linux 系统环境、JDK 环境这两层,只有上面的 3 层不同(因为 jar 包不同)。如果每次制作 Java 镜像都重复制作前两层镜像,是有点麻烦的。

因此,就有人提供了基础的系统加 JDK 环境,我们在此基础上制作 Java 镜像,就可以省去一些配置了:

# 基础镜像
FROM openjdk:11.0-jre-buster
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 拷贝jar包
COPY docker-demo.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]

构建镜像

Dockerfile 文件写好以后,我们就可以利用命令来构建镜像了。

在资料中,我们已经准备好了一个 demo 项目及对应的 Dockerfile用于演示如何构建镜像:

在这里插入图片描述

在虚拟机中回到根目录,然后我们可以将整个 demo 文件夹直接拖到窗口中,将其上传,里面的 logs 现在没什么用,可以删去:

在这里插入图片描述

我们查看一下这个 Dockerfile,可以发现它是在 JDK 的基础镜像上进行构架的,在资料中也准备了这个镜像,我们将其上传到宿主机中,等一下就不用再浪费时间去下载了:

在这里插入图片描述

images 目录下有一个 jdk.tar,我们将其拖到根目录下即可:

在这里插入图片描述

然后,加载一下:

在这里插入图片描述

一切准备好后,我们就可以来构建镜像了:

# 进入镜像目录
cd /root/demo
# 开始构建
docker build -t docker-demo:1.0 .

命令说明:

  • docker build : 就是构建一个 docker 镜像

  • -t docker-demo:1.0-t 参数是指定镜像的名称(repositorytag

  • . : 最后的点是指构建时 Dockerfile 所在的路径,由于我们进入了 demo 目录,所以指定的是 . 代表当前目录,也可以直接指定 Dockerfile 目录:

    # 直接指定Dockerfile目录
    docker build -t docker-demo:1.0 /root/demo
    

在这里插入图片描述

可以看到,构建很快,1.5 秒就完成了。

# 1.创建并运行容器
docker run -d --name dd -p 8080:8080 docker-demo:1.0
# 2.查看容器
dps
# 3.查看容器运行日志
docker logs -f dd

可以看到一个很熟悉的标志:

在这里插入图片描述

我们可以来到浏览器访问一下虚拟机的 8080 端口:

192.168.150.128:8080/hello/count

记得换成你虚拟机的 IP,回车后我们可以看到这个页面,并且它会统计我们的访问次数:

在这里插入图片描述

我们也可以直接在虚拟机中通过指令来访问:

curl localhost:8080/hello/count

在这里插入图片描述

Docker基础之网络

前面我们创建了一个 Java 项目的容器,而 Java 项目往往需要访问其它各种中间件,例如 MySQLRedis 等。现在,我们的容器之间能否互相访问呢?

默认情况下,所有容器都是以 bridge 方式连接到 Docker 的一个虚拟网桥上:

在这里插入图片描述

首先,我们查看下 MySQL 容器的详细信息,重点关注其中的网络 IP 地址,然后进入 dd 容器中 ping 一下:

# 1.用基本命令,寻找Networks.bridge.IPAddress属性
docker inspect mysql
# 也可以使用format过滤结果
docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' mysql
# 得到IP地址如下:
172.17.0.2

# 2. mysql容器没启动的需要启动
docker start mysql

# 3.然后通过命令进入dd容器
docker exec -it dd bash

# 4.在容器内,通过ping命令测试网络
ping 172.17.0.2

在这里插入图片描述

发现可以互联,没有问题。

但是,容器的网络 IP 其实是一个虚拟的 IP,其值并不固定与某一个容器绑定,如果我们在开发时写死某个 IP,而在部署时很可能 MySQL 容器的 IP 会发生变化,从而导致连接失败。

所以,我们必须借助于 docker 的网络功能来解决这个问题,参考官方文档:https://docs.docker.com/reference/cli/docker/network/

命令说明
docker network create创建一个网络
docker network ls查看所有网络
docker network rm删除指定网络
docker network prune清除未使用的网络
docker network connect使指定容器连接加入某网络
docker network disconnect使指定容器连接离开某网络
docker network inspect查看网络详细信息

下面,我们来自定义一个网络:

# 1.首先通过命令创建一个网络
docker network create hmall

# 2.然后查看网络
docker network ls
# 结果:
NETWORK ID     NAME      DRIVER    SCOPE
8964d77f268d   bridge    bridge    local
7c04bfc41671   hmall     bridge    local
99e9b07b2b5c   host      host      local
a4deb7bd2a05   none      null      local
# 其中,除了hmall以外,其它都是默认的网络

# 3.让dd和mysql都加入该网络,注意,在加入网络时可以通过--alias给容器起别名
# 这样该网络内的其它容器可以用别名互相访问!
# 3.1.mysql容器,指定别名为db,另外每一个容器都有一个别名是容器名
docker network connect hmall mysql --alias db
# 3.2.db容器,也就是我们的java项目
docker network connect hmall dd

# 4.进入dd容器,尝试利用别名访问db
# 4.1.进入容器
docker exec -it dd bash
# 4.2.用db别名访问
ping db
# 结果
PING db (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=1 ttl=64 time=0.070 ms
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=2 ttl=64 time=0.056 ms
# 4.3.用容器名访问
ping mysql
# 结果:
PING mysql (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=1 ttl=64 time=0.044 ms
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=2 ttl=64 time=0.054 ms

我们也可以在创建容器时直接加入网络,但是通过这种方式就不会再连接默认网桥:

docker run -d --name dd -p 8080:8080 --network hmall docker-demo

现在,我们无需记住 IP 地址也可以实现容器互联了,通过别名即可,它会自己去找。

项目部署

前面,我们已经熟悉了 Docker 的基本用法,接下来就可以尝试自己来部署一个项目了。

项目的相关代码已经存在资料中,如图:

在这里插入图片描述

项目说明:

  • hmall:商城的后端代码
  • hmall-portal:商城用户端的前端代码
  • hmall-admin:商城管理端的前端代码

部署的容器及端口说明:

项目容器名端口备注
hmallhmall8080黑马商城后端API入口
hmall-portalnginx18080黑马商城用户端入口
hmall-adminnginx18081黑马商城管理端入口
mysqlmysql3306数据库

在正式部署前,我们先删除之前的 nginxdd 两个容器:

docker rm -f nginx dd

mysql 容器中已经准备好了商城的数据,所以就不再删除了。

部署Java项目

hmall 项目是一个 maven 聚合项目,我们使用 IDEA 打开 hmall 项目,查看项目结构如图:

在这里插入图片描述

我们要部署的就是其中的 hm-service,其中的配置文件采用了多环境的方式:

在这里插入图片描述

其中的 application-dev.yaml 是部署到开发环境的配置,application-local.yaml 是本地运行时的配置。

查看 application.yaml,你会发现其中的 JDBC 地址并未写死,而是读取变量:

在这里插入图片描述

这两个变量在 application-dev.yamlapplication-local.yaml 中并不相同。在 application-local.yaml 中,我们需要修改 host 为自己虚拟机的 ip 地址。

dev 开发环境(也就是 Docker 部署时)采用了 mysql 作为地址,刚好是我们的 mysql 容器名,只要两者在一个网络,就一定能互相访问。

在这里插入图片描述

打包完,我们就能在 target 目录下看到 jar 包了:

在这里插入图片描述

接下来,我们将 hm-service 目录下的 Dockerfilehm-service/target 目录下的 hm-service.jar 一起上传到虚拟机的 root 目录,直接拖文件即可:

在这里插入图片描述

# 1. 启动mysql容器
docker start mysql

# 2. 在当前目录构建项目镜像,不指定tag,则默认为latest
docker build -t hmall .

# 3. 查看镜像
dis

# 4. 创建并运行容器,并通过--network将其加入hmall网络,这样才能通过容器名访问mysql
docker run -d --name hmall --network hmall -p 8080:8080 hmall

# 5. 查看容器运行日志
docker logs -f hmall

通过日志可以看到 SpringMabatisplus 的标志:

在这里插入图片描述

然后,我们可以通过浏览器访问 http://你的虚拟机地址:8080/search/list 这个地址,来测试 Java 项目是否部署成功。

在这里插入图片描述

部署前端

hmall-portalhmall-admin 是前端代码,需要基于 nginx 部署。

其中:

  • html 是静态资源目录
  • nginx.confnginx 的配置文件,主要是完成对 html 下的两个静态资源目录做代理

我们现在要做的就是把整个 nginx 目录上传到虚拟机的 /root 目录下:

在这里插入图片描述

然后创建 nginx 容器并完成两个挂载:

  • /root/nginx/html 挂载到 /usr/share/nginx/html
  • /root/nginx/nginx.conf 挂载到 /etc/nginx/nginx.conf

由于需要让 nginx 同时代理 hmall-portalhmall-admin 两套前端资源,因此我们需要暴露两个端口:

  • 18080:对应 hmall-portal
  • 18081:对应 hmall-admin

如果你在部署之后访问不了前端页面,就可以执行下列指令来开放端口。

# 开放端口18080
sudo firewall-cmd --zone=public --add-port=18080/tcp --permanent

# 开放端口18081
sudo firewall-cmd --zone=public --add-port=18081/tcp --permanent

# 重新加载防火墙规则
sudo firewall-cmd --reload

如果你的虚拟机中已经存在 nginx 容器,需要先将其删除:docker rm -f nginx

新建 nginx 容器:

docker run -d \
  --name nginx \
  -p 18080:18080 \
  -p 18081:18081 \
  -v /root/nginx/html:/usr/share/nginx/html \
  -v /root/nginx/nginx.conf:/etc/nginx/nginx.conf \
  --network hmall \
  nginx

测试,通过浏览器访问:http://你的虚拟机ip:18080

在这里插入图片描述

DockerCompose

大家可以看到,我们部署一个简单的 Java 项目,其中就包含 3 个容器:

  • MySQL
  • Nginx
  • Java项目

而稍微复杂的项目,其中还会有各种各样的其它中间件,需要部署的东西远不止 3 个。如果还像这样手动地逐一部署,就太麻烦了。

Docker Compose 就可以帮助我们实现多个相互关联的 Docker 容器的快速部署。它允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器。

基本语法

docker-compose.yml 文件的基本语法可以参考官方文档:https://docs.docker.com/compose/compose-file/legacy-versions/

docker-compose 文件中可以定义多个相互关联的应用容器,每一个应用容器被称为一个服务(service)。由于 service 就是在定义某个应用的运行时参数,因此与 docker run 参数非常相似,很容易可以上手。

举例来说,用 docker run 部署 MySQL 的命令如下:

docker run -d \
  --name mysql \
  -p 3306:3306 \
  -e TZ=Asia/Shanghai \
  -e MYSQL_ROOT_PASSWORD=123 \
  -v ./mysql/data:/var/lib/mysql \
  -v ./mysql/conf:/etc/mysql/conf.d \
  -v ./mysql/init:/docker-entrypoint-initdb.d \
  --network hmall
  mysql

如果用 docker-compose.yml 文件来定义,就是这样:

version: "3.8" # docker compose的版本

services:
  mysql:
    image: mysql		# 镜像名称
    container_name: mysql	# 容器名称
    ports:
      - "3306:3306"
    environment:	# 环境
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: 123
    volumes: 	# 数据卷
      - "./mysql/conf:/etc/mysql/conf.d"
      - "./mysql/data:/var/lib/mysql"
      - "./mysql/init:/docker-entrypoint-initdb.d"
    networks:	# 网络
      - hm-net
networks:	# 网络
  hm-net:
    name: hmall

对比如下:

docker run 参数docker compose 指令说明
–namecontainer_name容器名称
-pports端口映射
-eenvironment环境变量
-vvolumes数据卷配置
–networknetworks网络

在资料中已经准备了黑马商城的部署文件 docker-compose.yml

在这里插入图片描述

version: "3.8"

services:
  mysql:
    image: mysql
    container_name: mysql
    ports:
      - "3306:3306"
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: 123
    volumes:
      - "./mysql/conf:/etc/mysql/conf.d"
      - "./mysql/data:/var/lib/mysql"
      - "./mysql/init:/docker-entrypoint-initdb.d"
    networks:
      - hm-net
  hmall:
    build: # 构建镜像
      context: .	# 构建路径
      dockerfile: Dockerfile # 根据dockerfile构建
    container_name: hmall
    ports:
      - "8080:8080"
    networks:
      - hm-net
    depends_on:	# 依赖
      - mysql
  nginx:
    image: nginx
    container_name: nginx
    ports:
      - "18080:18080"
      - "18081:18081"
    volumes:
      - "./nginx/nginx.conf:/etc/nginx/nginx.conf"
      - "./nginx/html:/usr/share/nginx/html"
    depends_on:	# 依赖
      - hmall
    networks:
      - hm-net
networks:
  hm-net:
    name: hmall

然后,我们将这个部署文件上传到和其他构建项目所需资料的同一目录下:

在这里插入图片描述

基础命令

编写好 docker-compose.yml 文件,就可以部署项目了。常见的命令可以参考官方文档:https://docs.docker.com/compose/reference/

在这里插入图片描述

基本语法如下:

docker compose [OPTIONS] [COMMAND]

其中,OPTIONSCOMMAND 都是可选参数,比较常见的有:

类型参数或指令说明
Options-f指定 compose 文件的路径和名称,如果在当前目录下可以不用指定
Options-p指定 project 名称。project 就是当前 compose 文件中设置的多个 service 的集合,是逻辑概念,默认为 root
Options-d后台运行
Commandsup创建并启动所有 service 容器
Commandsdown停止并移除所有容器、网络
Commandsps列出所有启动的容器
Commandslogs查看指定容器的日志
Commandsstop停止容器
Commandsstart启动容器
Commandsrestart重启容器
Commandstop查看运行的进程
Commandsexec在指定的运行中容器中执行命令

下面我们就用 docker compose 来部署项目。

首先,先删除前面创建的容器、镜像和网络:

在这里插入图片描述

在这里插入图片描述

然后用 docker compose 部署:

在这里插入图片描述

来到浏览器访问一下前端页面,点击登录,输入 Jack123,登录成功:

在这里插入图片描述

如果我们执行 docker compose down ,就可以删除所有容器和网络:

在这里插入图片描述

  • 26
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序喵正在路上

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

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

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

打赏作者

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

抵扣说明:

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

余额充值