【Springcloud微服务】Docker下篇

发布于:2024-06-22 ⋅ 阅读:(233) ⋅ 点赞:(0)

🔥 本文由 程序喵正在路上 原创,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:商城管理端的前端代码

部署的容器及端口说明:

项目 容器名 端口 备注
hmall hmall 8080 黑马商城后端API入口
hmall-portal nginx 18080 黑马商城用户端入口
hmall-admin nginx 18081 黑马商城管理端入口
mysql mysql 3306 数据库

在正式部署前,我们先删除之前的 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 指令 说明
–name container_name 容器名称
-p ports 端口映射
-e environment 环境变量
-v volumes 数据卷配置
–network networks 网络

在资料中已经准备了黑马商城的部署文件 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 后台运行
Commands up 创建并启动所有 service 容器
Commands down 停止并移除所有容器、网络
Commands ps 列出所有启动的容器
Commands logs 查看指定容器的日志
Commands stop 停止容器
Commands start 启动容器
Commands restart 重启容器
Commands top 查看运行的进程
Commands exec 在指定的运行中容器中执行命令

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

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

在这里插入图片描述

在这里插入图片描述

然后用 docker compose 部署:

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到