Docker

107 阅读8分钟

认识Docker

解决操作系统环境差异

操作系统结构,以Ubuntu操作系统为例,结构如下:

image.png
  • 计算机硬件:例如CPU、内存、磁盘等。
  • 系统内核:所有Linux发行版的内核都是Linux,例如CentOS、Ubuntu、Fedora等。内核可以与计算机硬件交互,对外提供内核指令,用于操作计算机硬件
  • 系统应用:封装内核的指令,形成函数库。

不同的系统应用函数库不同,如果将一个Ubuntu版本的MySQL应用安装到CentOS系统,MySQL在调用Ubuntu函数库时,会发现找不到或者不匹配,就会报错。


Docker解决不同系统环境的问题:

  • Docker将用户程序与所需要调用的系统(比如Ubuntu)函数库一起打包。
  • Docker运行到不同操作系统时,基于打包的函数库,直接操作Linux内核来运行。
image.png

与虚拟机的区别

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

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

虚拟机和Docker的差异:

  • docker是一个系统进程;虚拟机是在操作系统中的操作系统。

  • docker体积小、启动速度快、性能好;虚拟机体积大、启动速度慢、性能一般。

Docker架构

镜像和容器

镜像(Image):Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,称为镜像,这个文件包是只读的。

容器(Container):镜像中的应用程序运行后形成的进程就是容器,Docker会给容器进程做隔离,对外不可见。

image.png

例如下了个QQ,如果将QQ在磁盘上的运行文件及其运行的操作系统依赖打包,形成QQ镜像。然后就可以启动多次,双开、甚至三开QQ,跟多个妹妹聊天,只不过每个QQ是看不到其他QQ的聊天内容的。

DockerHub

可以将自己的镜像共享到DockerHub,另一方面也可以从DockerHub拉取镜像:

image.png

CS架构

Docker是一个CS架构的程序,由两部分组成:

  • 服务端(server):Docker守护进程,负责处理Docker指令,管理镜像、容器等

  • 客户端(client):通过命令RestAPI向Docker服务端发送指令。可以在本地或远程向服务端发送指令。

image.png

Docker基本操作

镜像

镜像名称

  • 镜像名称一般分两部分组成:[repository]:[tag]
  • 在没有指定tag时,默认是latest,代表最新版本的镜像。
image.png

镜像命令

image.png

容器

状态

image.png

容器的三个状态:

  • 运行:进程正常运行。
  • 暂停:进程挂起,CPU不再运行,并不释放内存。
  • 停止:进程终止,回收进程占用的内存、CPU等资源。

命令

创建并运行nginx容器的命令:

docker run --name containerName -p 80:80 -d nginx

具体含义:

  • docker run :创建并运行一个容器。
  • --name : 给容器起一个名字,比如叫做mn。
  • -p :将宿主机端口与容器端口映射,冒号左侧是宿主机端口,右侧是容器端口。
  • -d:后台运行容器。
  • nginx:镜像名称,例如nginx,省略tag即是latest(最新的)。

每个应用的运行命令不一样,具体到dockerHub官网查看。

宿主机端口不能有冲突。


进入nginx容器:

docker exec -it mn bash

具体含义:

  • docker exec :进入容器内部,执行一个命令

  • -it : 给当前进入的容器创建一个标准输入、输出终端,允许我们与容器交互

  • mn :要进入的容器的名称

  • bash:进入容器后执行的命令,bash是一个linux终端交互命令


查看容器状态:

  • docker ps
  • docker ps -a 查看所有容器,包括已经停止

修改容器的文件:

不推荐在容器中修改文件,因为容器中没有vi命令,是阉割版的,也没有文件修改记录。

数据卷

作用

上面提到不推荐在容器中修改文件,原因如下:

image.png

要解决这些问题,必须将容器与数据解耦,这就要用到数据卷。

数据卷(volume)是一个虚拟目录,指向宿主机文件系统中的某个目录。

image.png

完成数据卷挂载后,对容器的一切操作都会作用在数据卷对应的宿主机目录了。反过来,操作宿主机目录就等同于操作容器内目录

数据卷相当于搭了一座桥。


常用命令

docker volume [COMMAND]

docker volume命令是数据卷操作,根据命令后跟随的command来确定下一步的操作:

  • create 创建一个volume
  • inspect 显示一个或多个volume的信息
  • ls 列出所有的volume
  • prune 删除未使用的volume
  • rm 删除一个或多个指定的volume

挂载数据卷

创建容器时,可以通过 -v 参数来挂载一个数据卷到某个容器内目录,命令格式如下:

docker run \
  --name mn \
  -v html:/root/html \
  -p 8080:80
  nginx \

-v html:/root/htm :把html数据卷挂载到容器内的/root/html这个目录中。

数据卷不存在会自动创建。

容器挂载宿主目录

容器不仅可以挂载数据卷,也可以直接挂载到宿主机目录上。关联关系如下:

  • 带数据卷模式:宿主机目录 <--> 数据卷 <---> 容器内目录
  • 直接挂载模式:宿主机目录 <---> 容器内目录

语法:

目录挂载与数据卷挂载的语法类似:

  • -v [宿主机目录]:[容器内目录]:容器目录会写到宿主目录中
  • -v [宿主机文件]:[容器内文件] :宿主文件会覆盖容器文件

挂载数据卷和直接挂载宿主的区别:

  • 挂载数据卷耦合度低,由docker管理目录,但是目录较深,不方便查看。
  • 挂载宿主耦合度高,需要自己管理目录,不过目录容易寻找查看,删除容器宿主目录和文件还会存在

Dockerfile自定义镜像

镜像结构

MySQL镜像的组成结构:

image.png
  • 镜像是分层结构,每一层称为一个Layer。

  • 简单来说,镜像就是在系统函数库(阉割版)和运行环境基础上,添加应用程序文件、配置文件、依赖文件等组合,然后编写好启动脚本打包在一起形成的文件。

  • 要构建镜像,其实就是实现上述打包的过程。

Dockerfile语法

Dockerfile一个文本文件,其中包含一个个的指令(Instruction),用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。

image.png

以构建Java项目为例

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

  1. 新建一个空文件夹docker-demo

  2. 将jar项目包、jdk8.tar.gz、Dockerfile文件放到文件夹中。

    Dockerfile文件如下:

    # 指定基础镜像
    FROM ubuntu:16.04
    # 配置环境变量,JDK的安装目录
    ENV JAVA_DIR=/usr/local
    
    # 拷贝jdk和java项目的包
    COPY ./jdk8.tar.gz $JAVA_DIR/
    
    # 安装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
    # -----------------------------------手动分割线--------------------------------------------
    COPY ./docker-demo.jar /tmp/app.jar
    
    # 暴露端口
    EXPOSE 8090
    # 入口,java项目的启动命令
    ENTRYPOINT java -jar /tmp/app.jar
    
  3. 运行命令,构建镜像:docker build -t javaweb:1.0 .

    • . 代表的是在Dockerfile所在的目录构建镜像。

在Dockerfile文件中,是基于Ubuntu基础镜像,添加任意自己需要的安装包,构建镜像,但是却比较麻烦,在大多数情况下,可以在一些安装了部分软件的基础镜像上做改造,避免重复造轮子。

例如,构建java项目的镜像,可以用java:8-alpine基础镜像,将一个Java项目构建为镜像,相当于java:8-alpine封装了手动分割线以上的代码,而Dockerfile文件内容只需:

FROM java:8-alpine
COPY ./app.jar /tmp/app.jar
EXPOSE 8090
ENTRYPOINT java -jar /tmp/app.jar

DockerCompose

介绍

  • Docker Compose可以基于Compose文件快速的部署分布式应用,无需一个个手动创建和运行容器。
  • Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行,**相当于是多个docker run命令的集合,**格式如下:
version: "3.8"  #Compose版本
 services:
  mysql: #容器名称
    image: mysql:5.7.25
    environment:
     MYSQL_ROOT_PASSWORD: 123 
    volumes:
     - "/tmp/mysql/data:/var/lib/mysql"
     - "/tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf"
     #---------------------------------上面是基于镜像部署,下面是创建镜像并运行容器。
  web:  #容器名称
    build: .
    ports:
     - "8090:8090"

**MySQL没有指定端口,是因为在微服务中MySQL只是给微服务中的集群使用的,**端口无需暴露。

部署微服务集群

  1. 把每一个微服务放到不同的文件夹,里面有打好的jar包和Dockerfile文件,内容如下:

    FROM java:8-alpine
    COPY ./app.jar /tmp/app.jar
    ENTRYPOINT java -jar /tmp/app.jar
    
  2. 在文件夹外面有一个docker-compose文件,内容如下:

    version: "3.2"
    
    services:
      nacos:
        image: nacos/nacos-server
        environment:
          MODE: standalone
        ports:
          - "8848:8848"
      mysql:
        image: mysql:5.7.25
        environment:
          MYSQL_ROOT_PASSWORD: 123
        volumes:
          - "$PWD/mysql/data:/var/lib/mysql"
          - "$PWD/mysql/conf:/etc/mysql/conf.d/"
      userservice:
        build: ./user-service
      orderservice:
        build: ./order-service
      gateway:
        build: ./gateway
        ports:
          - "10010:10010"
    

    image.png

userserviceorderservicegateway:都是基于Dockerfile临时构建的

  1. 修改微服务配置,因为微服务将来要部署为docker容器,而容器之间互联不是通过IP地址,而是通过容器名

    spring:
      datasource:
        url: jdbc:mysql://mysql:3306/cloud_order?useSSL=false
        username: root
        password: 123
        driver-class-name: com.mysql.jdbc.Driver
      application:
        name: orderservice
      cloud:
        nacos:
          server-addr: nacos:8848 # nacos服务地址
    
  2. 把整个文件夹上传到虚拟机中,进入目录运行下面的命令:

    docker-compose up -d
    
    • up:创建并运行容器
    • -d:后台运行

Docker镜像仓库

搭建私有镜像仓库

简化版镜像仓库

Docker官方的Docker Registry是一个基础版本的Docker镜像仓库,具备仓库管理的完整功能,但是没有图形化界面。

docker run -d \
    --restart=always \
    --name registry	\
    -p 5000:5000 \
    -v registry-data:/var/lib/registry \
    registry

带有图形化界面版本

使用DockerCompose部署带有图象界面的DockerRegistry,命令如下:

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=我的私有仓库
      - REGISTRY_URL=http://registry:5000
    depends_on:
      - registry

配置Docker信任地址

私服采用的是http协议,默认不被Docker信任,所以需要做一个配置:

# 打开要修改的文件
vi /etc/docker/daemon.json
# 添加内容:
"insecure-registries":["http://192.168.150.101:8080"]
# 重新加载
systemctl daemon-reload
# 重启docker
systemctl restart docker

推送,拉取镜像

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

  1. 重新命名tag本地镜像,名称前缀为私有仓库的地址:192.168.150.101:8080/

    docker tag nginx:latest 192.168.150.101:8080/nginx:1.0 
    
  2. 推送镜像

    docker push 192.168.150.101:8080/nginx:1.0 
    
  3. 拉取镜像

    docker pull 192.168.150.101:8080/nginx:1.0