Administrator
发布于 2023-05-01 / 11 阅读
0
0

微服务(十)——Docker进阶

镜像结构

镜像是分层结构,每一层称为一个Layer。

  • BaseImage层:包含基本的系统函数库、环境变量、文件系统
  • Entrypoint:入口,是镜像中应用启动的命令
  • 其它层:在BaseImage基础上添加依赖、安装程序等,完成整个应用的安装和配置

以mysql5.7为例

Dockerfile

注意几点

  • Dockerfile的本质是一个文件,通过指令描述镜像的构建过程
  • Dockerfile的第一行必须是FROM,从一个基础镜像来构建
  • 基础镜像可以是基本操作系统,如Ubuntu。也可以是其他人制作好的镜像,如java:8-alpine

测试下,创建一个web项目,写一个测试接口

@RestController
public class TestController {
    @GetMapping("now")
    public String test(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
}

打成jar包,上传服务器,我这里是放在/data/docker/java目录下

同目录下创建Dockerfile

# 指定基础镜像 这个镜像是基于linux系统构建的一个java镜像 帮我们省去了jdk安装配置的步骤
FROM java:8-alpine

# 将jar包拷贝到容器内
COPY ./docker-web.jar /tmp/app.jar

# 暴露端口 这里只是描述作用,为了方便查看暴露的端口 实际不会影响和改变项目里配置的端口
EXPOSE 8080

# 入口 java项目的启动命令
ENTRYPOINT java -jar /tmp/app.jar

现在,构建镜像的必要条件已经完成

生成自定义镜像,docker build是构建命令,-t表示标签,myweb:1.0代表镜像名称,.代表Dockerfile的路径,我们放在了同一目录

docker build -t myweb:1.0 .

来看一下,可以看到,成功生成了一个镜像

接下来运行一下

docker run -d -p 8890:8080 testweb:1.0

容器运行成功

访问测试下

Docker网络

Docker容器是一个具有隔离性的虚拟系统,容器内可以有自己独立的网络空间,容器与容器之间相互隔离,通过Docker网络进行相互通信。

了解docker0

我们先查看下宿主机网卡

ip addr # 查看所有网卡

这里有3个网络

lo: 127.0.0.1 #本地回环接口,主要用于测试本机的网络功能。
eth0: 172.17.0.11 #以太网接口,也就是有线网卡。可以看成宿主机的内网ip
docker0: 172.18.0.1 #docker虚拟网桥  它是Docker容器的虚拟网络接口,用于连接容器和主机之间的网络。

docker0在我们安装docker的时候就会创建。docker0用来和宿主机之间通信。

Docker 是如何处理容器网络访问的?我们启动一个tomcat来看一下

进入容器,查看容器网卡信息

docker exec -it mt bash         #进入容器
apt update
apt install -y iproute2         # 安装 ip addr
apt install -y iputils-ping     # 安装 ping
ip addr                         # 查看容器网卡信息

退出容器,宿主机上可以ping通172.18.0.2

再次查看宿主机网卡信息,发现多出一个217,刚好和 mt 容器网卡216 相对应。

同样方式再启动一个tomcat容器mt2,端口8082,查看mt2容器网卡信息(进入容器提前安装iproute2、iputils-ping)

宿主机上可以ping通172.18.0.3

再次查看宿主机网卡

可以看到,我们每启动一个容器,就会多出一对网卡,同时他们被连接到docker0上,而docker0又和宿主机之间连通。

测试mt和mt2容器是否可以ping通

docker exec -it mt ping 172.18.0.2

可以看到,容器和容器之间是可以相互 ping 通的

在这里,我们可以认为docker0相当于一个路由器的作用,任何一个容器启动默认都是docker0网络。

docker默认会给容器分配一个可用ip,并把它同docke0相连。使用到的就是veth pair技术。

容器互联-link

现在我们考虑一个问题,如果需要部署一个微服务,是直接使用172.18.0.2、172.18.0.3这种ip做容器之间的连接访问吗?这样是不合理的,因为容器使用的是虚拟ip,每次更新启动ip都会变化。

那么是否可以通过容器名进行访问呢,我们测试下容器名之间是否能ping通

docker exec -it mt ping mt2

答案是不行的

我们再启动一个tomcat容器,并加上link参数,使mt3连接到mt2

docker run -it -d -p 8083:8080 --name mt3 --link mt2 tomcat

测试容器mt2和mt3是否能ping通名称

可以看到,mt3可以ping通mt2,反之不行。因为link是单向的。

docker exec -it mt3 cat /etc/hosts

查看容器mt3网卡信息也可以看到,--link 就是在hosts配置中增加ip与容器名的绑定。

不过我们现在不建议使用 --link了,推荐使用自定义网络。

自定义网络

docker0的特点:

  • 它是默认的

  • 域名访问不通

  • –link 域名通了,但是删了又不行

docker为我们提供了三种网络模式,使用如下命令可以查看

这其中默认使用的是 bridge,也就是我们的docker0网卡。

docker网络模式
网络模式 配置 说明
bridge模式 --net=bridge 默认值,在Docker网桥docker0上为容器创建新的网络栈
none模式 --net=none 不配置网络,用户可以稍后进入容器,自行配置
container模式 --net=container:name/id 容器和另一个容器共享network namespace。
host模式 --net=host 容器和宿主机共享network namespace。
用户自定义 --net=自定义网络 用户自己使用network相关命令定义网络,创建容器的时候可以指定为自己定义的网络

上面我们创建容器mt时,其实默认加了--net bridge

docker run -it -d -p 8081:8080 --name mt --net bridge tomcat

现在我们自定义一个docker网络

docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet

命令说明

  • docker network create:docker网络创建命令
  • --driver bridge:指定网络模式为bridge
  • --subnet 192.168.0.0/16:指定子网网段
  • --gateway 192.168.0.1:指定网关ip
  • mynet:网络名称

查看docker网络

查看详情

下面我们使用自定义的网络启动 tomcat

docker run -d  -p 8091:8080 --name tomcat-net-01 --net mynet tomcat
docker run -d  -p 8092:8080 --name tomcat-net-02 --net mynet tomcat

查看容器

再次查看mynet网络,可以看到tomcat-net-01和tomcat-net-02

我们再来ping测试下,通过ip和容器名都可以访问。

网络连通

docker0和自定义网络肯定不通,我们使用自定义网络的好处就是网络隔离。

但是在实际的工作中,比如我们部署了mysql使用了一个网段。部署了tomcat使用了另一个网段,两个网段之间肯定是不能相互连通的,但是tomcat和mysql又需要相互连通,我们就要使用网络连通。原理图如下:

图片

网络连通就是将一个容器和一个网段之间的连通。

比如我前面使用的默认docker0的tm,需要连接到mynet网络。

docker network connect [OPTIONS] NETWORK CONTAINER

 测试下

查看mynet网络,可以看到容器mt也加入到了mynet网络,这样就实现了网络的连通。(现在容器mt在docker0和mynet两个网络中都能访问到)

总结:

  • veth pair是成对出现的一种虚拟网络设备接口,一端连着网络协议栈,一端彼此相连。

  • docker中默认使用docker0网络。

  • docker0相当于一个路由器的作用,任何一个容器启动默认都是docker0网络。

  • docker0是容器和虚拟机之间通信的桥梁。

  • 推荐使用自定义网络,更好实现使用服务名的连通方式,避免ip改变的尴尬。

  • 网络之间不能直接连通,网络连通是将一个容器和一个网络之间的连通,实现跨网络操作。

Docker Compose

什么是DockerCompose

  • Docker Compos可以基础Compose文件帮我们快速的部署分布式应用,而无需一个个创建和运行容器
  • Compose文件是一个文本文件,通过指令定义及集群中的每个容器如何运行

下面是一个简单的Compose文件的结构示例

更多用法可以参考官网学习

Docker Compose的安装很简单,只要在线下载或直接上传本地下载好的的docker-compose文件,放到/usr/local/bin目录下,然后修改文件权限,给docker-compose添加可执行权限。

Docker Compose的作用主要是帮助我们快速部署分布式应用,无需一个个微服务去构建镜像和部署。

实战部署下之前的cloud-demo微服务项目

先看下我们的项目结构,我们需要部署的微服务是user-service、order-service、gateway三个微服务(mysql的话我是直接使用宿主机服务器的mysql,大家也可以使用mysql镜像)

因为我们需要使用docker部署,所以不能再使用localhost了。

修改gateway、order-service、user-service中的nacos的地址,localhost改成容器服务名nacos

修改mysql的配置,改成线上地址、账号密码

pom.xml打包名改成app

  <build>
    <finalName>app</finalName>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

执行maven打包

创建一个cloud-demo-docker文件夹,用于存在需要的配置文件及jar包等,目录结构如下

准备一个Dockerfile文件,内容如下

#基于java8镜像构建
FROM java:8-alpine

#拷贝微服务jar包到容器内
COPY ./app.jar /tmp/app.jar

分别复制到user-service,order-service,gateway文件夹下。并且3个微服务app.jar放到对应文件夹下。

编写docker-compose.yml

version: "3.2"

services:
  nacos:
    image: nacos/nacos-server
    environment:
      MODE: standalone
    ports:
      - "8848:8848"
  userservice:
    build: ./user-service
  orderservice:
    build: ./order-service
  gateway:
    build: ./gateway
    ports:
      - "10010:10010"

上传服务器,我是放在/data目录下,复制本地cloud_user和cloud_order数据到服务器上的mysql

部署

docker-compose up -d

等待打包完成,查看镜像和容器已成功创建,并且docker-compose会默认创建一个网络

查看日志

docker-compose logs -f

访问测试

镜像仓库

镜像仓库(Docker Registry)有公有的和私有的两种形式:

  • 公有仓库:例如Docker官方的Docker Hub,国内也有一些云服务商提供类似于Docker Hub的公开服务,比如网易云镜像服务、DaoCloud镜像服务、阿里云镜像服务等。
  • 除了使用公开仓库外,用户还可以在本地搭建私有Docker Registry。企业自己的镜像最好是采用私有Docker Registry来实现。

部署Docker Registry

version: '3.0'
services:
  registry:
    image: registry
    volumes:
      - ./registry-data:/var/lib/registry
  ui:
    image: joxit/docker-registry-ui:static
    ports:
      - 8888:80
    environment:
      - REGISTRY_TITLE=lsy的私有仓库
      - REGISTRY_URL=http://registry:5000
    depends_on:
      - registry

另外,我们服务器一般采用的是http协议,默认不被Docker信任,所以需要做一个配置

# 打开要修改的文件
vi /etc/docker/daemon.json
# 添加内容 这里是服务器的ip+docker compose里配置的ui界面访问端口
"insecure-registries":["http://111.229.191.140:8888"]
# 重新加载
systemctl daemon-reload
# 重启docker
systemctl restart docker

 将上面的docker-compose配置文件传到服务器上,我是放在/data/registry-ui

执行构建

构建完成,打开浏览器测试下

目前没有镜像。接下来,我们试着推送一个镜像到私有仓库

先查看下本地镜像

首先,我们需要重新tag本地镜像,名称前缀为私有仓库的地址:111.229.191.140:8888,以gateway镜像为例

docker tag cloud-demo-docker_gateway:latest 111.229.191.140:8888/gateway:1.0

可以看到,多了一个本地镜像,并且两个gateway的image id其实是一样的,相当于一个镜像副本

推送镜像

docker push 111.229.191.140:8888/gateway:1.0

可以看到,push成功。(PS:真慢,大家可以推送以个小一点的镜像)

去私有仓库刷新查看,可以看到,多了一个gateway镜像

点击还能查看镜像详情

拉取镜像

docker pull 111.229.191.140:8888/gateway:1.0

为了测试效果,先删除本地的111.229.191.140:8888/gateway:1.0镜像,再拉取

可以看到,拉取成功。(PS:这里因为本地已经有cloud-demo-docker_gateway镜像的文件层,所以拉取很快。)


评论