Docker-02-镜像&项目部署
文章目录
- Docker-02-镜像&项目部署
- 一、镜像
- ①:镜像结构
- ②:Dockerfile
- ③:构建镜像
- 01:构建
- 02:查看镜像列表
- 03:运行镜像
- 二、网络
- ①:容器的网络IP地址
- ②:网络常见命令
- ③:自定义网络
- 三、项目部署
- ①:准备工作
- ②:准备MySQL、nginx、redis容器
- 01:创建网络
- 02:创建nginx容器
- 03:创建redis容器
- 04:创建MySQL容器
- 05:准备数据
- ③:部署Java项目
- 01:准备Dokerfile文件
- 02:部署后端
- 03:部署前端
一、镜像
前面我们一直在使用别人准备好的镜像,那如果我要部署一个Java项目,把它打包为一个镜像该怎么做呢?
①:镜像结构
要想自己构建镜像,必须先了解镜像的结构。
之前我们说过,镜像之所以能让我们快速跨操作系统部署应用而忽略其运行环境、配置,就是因为镜像中包含了程序运行需要的系统函数库、环境、配置、依赖。
因此,自定义镜像本质就是依次准备好程序运行的基础环境、依赖、应用本身、运行配置等文件,并且打包而成。
举个例子,我们要从0部署一个Java应用,大概流程是这样:
- 准备一个linux服务(CentOS或者Ubuntu均可)
- 安装并配置JDK
- 上传Jar包
- 运行jar包
那因此,我们打包镜像也是分成这么几步:
- 准备Linux运行环境(java项目并不需要完整的操作系统,仅仅是基础运行环境即可)
- 安装并配置JDK
- 拷贝jar包
- 配置启动脚本
上述步骤中的每一次操作其实都是在生产一些文件(系统运行环境、函数库、配置最终都是磁盘文件),所以镜像就是一堆文件的集合。
但需要注意的是,镜像文件不是随意堆放的,而是按照操作的步骤分层叠加而成,每一层形成的文件都会单独打包并标记一个唯一id,称为Layer(层)。这样,如果我们构建时用到的某些层其他人已经制作过,就可以直接拷贝使用这些层,而不用重复制作。
例如,第一步中需要的Linux运行环境,通用性就很强,所以Docker官方就制作了这样的只包含Linux运行环境的镜像。我们在制作java镜像时,就无需重复制作,直接使用Docker官方提供的CentOS或Ubuntu镜像作为基础镜像。然后再搭建其它层即可,这样逐层搭建,最终整个Java项目的镜像结构如图所示:
②:Dockerfile
由于制作镜像的过程中,需要逐层处理和打包,比较复杂,所以Docker就提供了自动打包镜像的功能。我们只需要将打包的过程,每一层要做的事情用固定的语法写下来,交给Docker去执行即可。
而这种记录镜像结构的文件就称为Dockerfile,其对应的语法可以参考官方文档:
https://docs.docker.com/engine/reference/builder/
其中的语法比较多,比较常用的有:
指令 | 说明 | 示例 |
---|---|---|
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镜像,就可以省去JDK的配置了:
# 基础镜像
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"]
是不是简单多了。
③:构建镜像
01:构建
当Dockerfile文件写好以后,就可以利用命令来构建镜像了。
在课前资料中,我们准备好了一个demo项目及对应的Dockerfile:
1.首先,我们将课前资料提供的docker-demo.jar包以及Dockerfile拷贝到虚拟机的/root/demo目录:
2.然后,执行命令,构建镜像:
# 进入镜像目录
cd /root/demo
# 开始构建
docker build -t docker-demo:1.0 .
命令说明:
- docker build : 就是构建一个docker镜像
- -t docker-demo:1.0 :-t参数是指定镜像的名称(repository和tag)
. :
最后的点是指构建时Dockerfile所在路径,由于我们进入了demo目录,所以指定的是.代表当前目录,也可以直接指定Dockerfile目录:
# 直接指定Dockerfile目录
docker build -t docker-demo:1.0 /root/demo
结果:
02:查看镜像列表
1.查看镜像列表:
# 查看镜像列表:
docker images
03:运行镜像
1.运行该镜像:
# 1.创建并运行容器
docker run -d --name dd -p 8080:8080 docker-demo:1.0
# 2.查看容器
docker ps
2.访问
# 3.访问
curl localhost:8080/hello/count
3.查看日志
docker logs -f dd
二、网络
①:容器的网络IP地址
上面我们创建了一个Java项目的容器,而Java项目往往需要访问其它各种中间件,例如MySQL、Redis等。现在,我们的容器之间能否互相访问呢?我们来测试一下
首先,我们查看下MySQL容器的详细信息,重点关注其中的网络IP地址:
1.用基本命令,寻找Networks.bridge.IPAddress属性
# 1.用基本命令,寻找Networks.bridge.IPAddress属性
docker inspect mysql
# 也可以使用format过滤结果
docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' mysql
# 得到IP地址如下:
172.17.0.2
2.进入dd容器,在容器内,通过ping命令测试网络
# 2.然后通过命令进入dd容器
docker exec -it dd bash# 3.在容器内,通过ping命令测试网络
ping 172.17.0.2
发现可以互联,没有问题。
但是,容器的网络IP其实是一个虚拟的IP,其值并不固定与某一个容器绑定,如果我们在开发时写死某个IP,而在部署时很可能MySQL容器的IP会发生变化,连接会失败。
②:网络常见命令
我们必须借助于docker的网络功能来解决这个问题
官方文档:https://docs.docker.com/engine/reference/commandline/network/
常见命令有:
命令 | 说明 | 文档地址 |
---|---|---|
docker network create | 创建一个网络 | docker network create |
docker network ls | 查看所有网络 | docs.docker.com |
docker network rm | 删除指定网络 | docs.docker.com |
docker network prune | 清除未使用的网络 | docs.docker.com |
docker network connect | 使指定容器连接加入某网络 | docs.docker.com |
docker network disconnect | 使指定容器连接离开某网络 | docker network disconnect |
docker network inspect | 查看网络详细信息 | docker network inspect |
③:自定义网络
1.首先通过命令创建一个网络
# 1.首先通过命令创建一个网络
docker network create coke
2…然后查看网络
# 2.然后查看网络 ( 其中,除了coke以外,其它都是默认的网络)
docker network ls
3.让dd和mysql都加入该网络,注意,在加入网络时可以通过–alias给容器起别名
# 3.让dd和mysql都加入该网络,注意,在加入网络时可以通过--alias给容器起别名
# 这样该网络内的其它容器可以用别名互相访问!
# 3.1.mysql容器,指定别名为db,另外每一个容器都有一个别名是容器名
docker network connect coke mysql --alias db
# 3.2.db容器,也就是我们的java项目
docker network connect coke dd
4.进入dd容器,尝试利用别名访问db
# 4.进入dd容器,尝试利用别名访问db
# 4.1.进入容器
docker exec -it dd bash
# 4.2.用db别名访问
ping db
5.进入dd容器,用容器名访问
# 4.3.用容器名访问
ping mysql
OK,现在无需记住IP地址也可以实现容器互联了。
总结:
- 在自定义网络中,可以给容器起多个别名,默认的别名是容器名本身
- 在同一个自定义网络中的容器,可以通过别名互相访问
三、项目部署
①:准备工作
好了,我们已经熟悉了Docker的基本用法,接下来可以尝试部署项目了。
项目说明:
- invoice:发票系统管理的后端代码
- invoice-web:发票系统管理的前端代码
部署的容器及端口说明:
项目 | 容器名 | 端口 | 备注 |
---|---|---|---|
invoice | invoice | 19009 | 发票系统管理的后端API入口 |
invoice-web | nginx | 8899 | 发票系统管理的前端入口 |
mysql | mysql | 3306 | 发票系统管理的前端入口 |
DockerFile
# 基础镜像
FROM openjdk:8-jre-buster# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone# 拷贝jar包
COPY invoice-0.0.1-SNAPSHOT.jar /app.jar# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]
②:准备MySQL、nginx、redis容器
-
因为项目中分别用到了MySQL、nginx和redis,所以提前准备好这些容器(并准备好数据库中的数据)
-
MySQL、nginx、redis容器 将容器放到同一个网络中,之后直接使用容器名字访问即可
01:创建网络
# 创建网络(名为invoice)
docker network create invoice# 查看所有网络
docker network ls
02:创建nginx容器
1.创建容器
创建nginx容器笔记:https://blog.csdn.net/cygqtt/article/details/135665012
由于需要让nginx同时代理invoice-web前端资源,因此我们需要暴露两个端口:
- 80:默认nginx首页端口(也可以不代理)
- 8899:对应invoice-web
docker run \
-p 80:80 \
-p 8899:8899 \
--name nginx \
--restart=always \
-v /usr/local/nginx/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /usr/local/nginx/html:/usr/share/nginx/html \
-v /usr/local/nginx/logs:/var/log/nginx \
-d nginx:1.25.3
2.nginx容器创建之后我们需要将
nginx容器
加入到invoice
网络中
# 查看所有网络
docker network ls
# 将容器加入到网络中
docker network connect invoice nginx
3.查看网络详细信息
docker network inspect invoice
03:创建redis容器
1.创建容器
创建redis容器笔记:https://blog.csdn.net/cygqtt/article/details/135665012
2.redis容器创建之后我们需要将
redis容器
加入到invoice
网络中
# 查看所有网络
docker network ls
# 将容器加入到网络中
docker network connect invoice redis
3.查看网络详细信息
docker network inspect invoice
04:创建MySQL容器
1.创建容器
创建MySQL容器笔记:https://blog.csdn.net/cygqtt/article/details/135665012
2.MySQL容器创建之后我们需要将
MySQL容器
加入到invoice
网络中
# 查看所有网络
docker network ls
# 将容器加入到网络中
docker network connect invoice mysql
3.查看网络详细信息
docker network inspect invoice
05:准备数据
1.运行sql文件
③:部署Java项目
01:准备Dokerfile文件
1.可以使用以下两种方式来命名 Dockerfile 文件:
- Dockerfile:这是最常见和推荐的方式,使用没有后缀的文件名。
- Dockerfile.dockerfile:这种方式在某些情况下可能更具描述性,特别是当你有多个类型的 Dockerfile 文件时。
2.Dokerfile内容
# 基础镜像
FROM openjdk:8-jre-buster# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone# 拷贝jar包
COPY invoice-0.1.2-SNAPSHOT.jar /app.jar# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]
02:部署后端
1.将Dockerfile和jar包一起上传到虚拟机
2.构建镜像
# 1.构建项目镜像,不指定tag,则默认为latest
docker build -t invoice .
2,查看镜像
# 2.查看镜像
docker images
3.创建并运行容器
# 创建并运行一个tomcat容器
docker run -d --name tomcat --restart=always --network invoice -p 8080:8080 tomcat
# 命令分析
-d: #这是一个简写形式,全称为 --detach。它表示在后台运行容器。
--name tomcat: #指定容器的名称为 tomcat。
--restart=always: #设置容器始终自动重启,即使容器异常退出也会自动重新启动。
--network invoice: #将容器连接到名为 invoice 的网络中。这要求在运行此命令之前已经创建了该网络。
-p 8080:8080: #将主机的端口 8080 映射到容器的端口 8080。这使得可以通过主机的 8080 端口访问容器中运行的 Tomcat 服务。
tomcat: #指定要使用的镜像名称为 tomcat。
# 创建并运行容器,并通过--network将其加入hmall网络,这样才能通过容器名访问mysql
docker run -d --name invoice --restart=always --network invoice -p 19009:19009 invoice
4.查看启动日志
docker logs invoice
5.请求api测试 http://192.168.200.128:19009/web/workFile/getAll
- 后端部署成功!
03:部署前端
1.创建目录用于存放前端代码
mkdir -p /usr/local/nginx/html/invoice-web
2.将打包好的前端代表拷贝到目录
/usr/local/nginx/html/invoice-web
下
3.配置nginx
vim /usr/local/nginx/nginx/nginx.conf
- 添加以下信息
worker_processes 1;events {worker_connections 1024;
}http {include mime.types;default_type application/json;sendfile on;keepalive_timeout 65;client_max_body_size 1000M; #(设置客户端请求体最大值) client_body_buffer_size 1000M; #(配置请求体缓存区大小) fastcgi_intercept_errors on;server { listen 8899;server_name _;location /api/ {# 这里配置代理到后端服务的地址proxy_pass http://invoice:19009/;}location / {# 这里配置前端资源的路径(容器内部路径)root /usr/share/nginx/html/invoice-web;index index.html index.htm;try_files $uri $uri/ /index.html;}}
}
4.重启nginx容器
# 重启nginx容器(使配置文件生效)
docker restart nginx
5.访问测试 http://192.168.200.128:8899/web