【基础技术与框架】一篇文章搞掂:Docker

时间:2022-06-13 17:15:26

注意!!注意系统内存,一开始我使用阿里云1核1G系统,各种问题,搞了几天,原来是内存不足

一、使用VM虚拟机,安装CentOS7.X系统,并安装和使用Docker

1.1、虚拟机安装CentOS7.X系统

1、登录官网https://www.centos.org/download/下载

【基础技术与框架】一篇文章搞掂:Docker

本次下载的版本是CentOS-7-x86_64-DVD-1804.iso

2、安装虚拟机

【基础技术与框架】一篇文章搞掂:Docker

 3、安装系统

【基础技术与框架】一篇文章搞掂:Docker

1.2、安装CentOS7.X,并能连同外网

  1. 虚拟机中安装CentOS7.X,参考上面步骤或http://blog.51cto.com/13438667/2059926
  2. 安装完后,启动系统,输入帐号密码登录
  3. 登录后进入了命令行模式
  4. 输入命令ping www.baidu.com,看是否能连通
  5. 出现错误提示name or service not know的解决办法,参考https://blog.csdn.net/phs999/article/details/77448639
  • 原因是没有添加DNS或虚拟机网络设备开机时没有启用,
  • 一般是没有开启网络设备,DNS可以先不配置,尝试一下开启后不行再设置。
  • 添加DNS
    • vi /etc/resolv.conf进入设置此文件
    • 输入2个命令
    • nameserver 8.8.8.8
    • nameserver 8.8.4.4
    • 然后按ESC,输入:wq,保存退出
    • 如果按错了,进入了recording状态,则按ESC后再按q即可退出
  • 设置网络设备开机启用
    • 输入命令vi /etc/sysconfig/network-scripts/ifcfg,然后按2此TAB键,可以得到网络适配器的提示
    • 一般是使用ens33
    • 然后输入命令vi /etc/sysconfig/network-scripts/ifcfg-ens33
    • 修改里面一个onboot配置为yes
    • 然后按ESC,输入:wq,保存退出
  • 设置完成后,输入reboot,重启服务器
  • 再次输入ping www.baidu.com,应该可以ping通了,ping的时候会一直获取数据,按Ctrl+C即可停止。

1.3、安装Docker,调用它的测试例子

  • 查看是否系统自带docker,如果有相关信息则代表有自带docker:docker version
  • 删除自带docker:sudo yum -y remove docker
  • 安装yum-utils,使用其yum-config-manager设置Yum源:sudo yum install -y yum-utils
  • 设置Yum源:sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  • 更新Yum缓存:sudo yum makecache fast
  • 安装Docker CE:sudo yum -y install docker-ce
  • 启动Docker后台服务:sudo systemctl start docker
  • 查看docker版本:docker --version

【基础技术与框架】一篇文章搞掂:Docker

  • 启动测试例子:docker run hello-world

由于本次没有这个镜像,所以会从往网上下载

【基础技术与框架】一篇文章搞掂:Docker

1.4、使用阿里云镜像加速器

可以提升获取Docker官方镜像的速度

注册阿里云,进入控制台,点击左上角按钮,展开产品列表,搜索“镜像”,进入容器镜像服务

【基础技术与框架】一篇文章搞掂:Docker

复制下方命令,即可完成配置

然后可以使用vi /etc/docker/daemon.json命令,确认是否保存了配置

1.5、Docker常用命令展示

Docker镜像常用命令:

  • 搜索镜像:搜索存放在Docker Hub中的镜像:docker search java
  • 下载镜像从Docker Registry上下载镜像到本地
    • 下载最新版本Java镜像:docker pull java
    • 从指定的Docker Registry中下载标签为7的Java镜像:docker pull reg.itmuch.com/java:7

列出已下载镜像:docker images

删除本地镜像

    • 删除指定镜像:docker rmi hello-world
    • 删除所有镜像,-f表示强制删除:docker rmi -f $(docker images)

Docker容器常用命令:

  • 新建并启动容器
    • docker run
    • -d选项:后台运行
    • -P选项:随机端口映射
    • -p选项:指定端口映射,4种格式
    • ip:hostPort:containerPort
    • ip::containerPort
    • hostPort:containerPort
    • containerPort
    • --network选项:指定网络模式
    • --network=bridge:默认选项,表示连接到默认的网桥
    • --network=host:容器使用宿主机的网络
    • --network=container:Name_or_ID:告诉Docker让新建的容器使用已有容器的网络配置。
    • --network=none:不配置该容器的网络,用户可以自定义网络配置。
    • 例子:docker run java /bin/echo 'Hello World'
    • 终端会打印Hello World字样,等同本地直接执行/bin/echo 'Hello World'
    • 例子:docker run -d -p 91:80 nginx
    • 启动一个Nginx容器,-d表示后台运行,-p 91:80 表示开放容器端口80到宿主机端口91
    • 然后可以访问http://Docker宿主机IP:91/,看到nginx首页
  • 列出容器:docker ps
  • 停止容器:docker stop 容器ID/容器名称
  • 强制停止容器:docker kill 容器ID/容器名称
  • 启动已停止的容器:docker start 容器ID/容器名称
  • 重启容器:docker restart 容器ID/容器名称
  • 进入容器
    • docker attach 容器ID/容器名称
    • 这种方式不方便,多个窗口同时attach到同一个容器时,所有窗口同步显示。如果一个窗口发生阻塞,其它窗口也无法操作。
    • 使用nsenter进入容器
    • 先找到容器第一个进程的ID
    • docker inspect --format "{{.State.Pid}}" 容器ID
    • 会输出一个进程ID
    • 进入容器
    • nsenter --target 进程ID --mount --uts --ipc --net --pid
  • 删除容器:docker rm 容器ID/容器名称
  • 删除所有容器:docker rm -f $(docker ps -a -q)

二、把Spring Boot项目的jar文件放到Docker部署

2.1、创建镜像生成脚本DockerFile

创建一个名为Dockerfile的文件,输入以下代码

FROM java:8
VOLUME /tmp
ADD lt-eureka-1.1.6.jar app.jar
RUN bash -c 'touch /app.jar'
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
EXPOSE 1000
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

2.2、Spring Boot项目生成Jar文件

使用Maven命令:mvn clean package即可打包

注意一个问题:spring boot项目打包的jar包运行时提示:no main manifest attribute

此时可以在本次测试一下java -jar xx.jar直接运行你的jar包是否报错

这个报错的原因是:maven打包时没有把主清单属性打包进去,参考https://blog.csdn.net/u010429286/article/details/79085212

在pom文件中加入插件即可:

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

2.3、使用XFTP把文件上传到服务器

我们需要上传jar文件和DockerFile到CentOS服务器

使用XFTP工具,百度XFTP到官网即可下载,个人使用免费

在CentOS上输入命令ifconfig,查看系统的IP地址

【基础技术与框架】一篇文章搞掂:Docker

创建文件夹

  • mkdir ltmicro
  • mkdir ltmicro/eureka

然后可以查看是否成功

  • ls
  • ls ltmicro
  • ls -l
  • ls ltmicro -l

在Windows系统上,打开XFTP工具

【基础技术与框架】一篇文章搞掂:Docker

需要上传的文件只要拖动即可,还能新建文件夹等

【基础技术与框架】一篇文章搞掂:Docker

2.4、使用Docker部署jar程序

前面我们通过XFTP创建ltmicro/eureka目录,并上传Dockerfile和生成jar包

然后运行命令创建Docker镜像:docker build -t eureka ltmicro/eureka

成功后,查看系统已生成的镜像:docker images

使用镜像生成并运行一个容器:docker run --name eureka_1 -d -p 1000:1000 eureka

查看容器运行的控制条输出日志:docker logs -f eureka_1

2.5、测试程序

访问虚拟机ip:1000,即可访问到部署在Docker中的项目了

三、使用Docker-compose编排微服务

如果我们有多个程序需要集中运行,如微服务系统,如果都用dockerfile进行部署,会十分麻烦。

可以使用Docker-compose来对这些容器进行编排,按照规定的方式进行启动,免除复杂的操作。

3.1、安装docker-compose

  • 访问docker-compose的github主页的版本页面:https://github.com/docker/compose/releases
  • 会有最新版本的安装语句

【基础技术与框架】一篇文章搞掂:Docker

例如

curl -L https://github.com/docker/compose/releases/download/1.22.0-rc1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

 

  • 复制命令到CentOS系统中进行安装
  • 然后执行命令更改compose工具的执行权限:chmod +x /usr/local/bin/docker-compose
  • 查看安装版本:docker-compose version
  • 查看帮助:docker-compose -h
  • 查看某个命令的帮助:docker-compose help down
  • 如果github的方式下载慢,也可以尝试sudo pip install docker-compose这种安装方式

3.2、编写编排脚本

  • 创建一个docker-compose.yml文件
  • 输入以下脚本
eureka:
  build: ./eureka
  ports:
    - "1000:1000"
  • 使用XFTP上传到ltmicro目录下
  • 然后输入命令来到docker-compose.yml文件所在目录:
    • cd ltmicro
  • 执行命令:
    • docker-compose up -d
    • 这个命令会自动执行下载镜像、创建镜像、创建容器、运行容器、运行程序的工作,一句命令则完成了我们上面的所有代码的工作。
  • 停止脚本相关容器
    • docker-compose down --rmi all
    • 这个命令会自动执行停止程序、停止容器、删除容器、删除镜像的工作。

四、Docker部署Redis、MySQL

4.1、Docker部署Redis

  • 获取Redis镜像:docker pull redis:3.2
  • 创建Docker容器并运行Redis
    • docker run --name redis -p 6379:6379 -v $PWD/data:/data -d redis:3.2 redis-server --appendonly yes
      • -p 6379:6379 : 将容器的6379端口映射到主机的6379端口
      • -v $PWD/data:/data : 将主机中当前目录下的data挂载到容器的/data
      • redis-server --appendonly yes : 在容器执行redis-server启动命令,并打开redis持久化配置
  • 查看运行情况:docker ps
  • 尝试连接装载redis的容器
    • docker exec -it 43f7a65ec7f8 redis-cli
    • 然后输入info,可以看到相关信息,代表成功连接

4.2、Docker部署MySQL

4.2.1、使用Docker命令创建

  • 查找Docker Hub上的mysql镜像:docker search mysql
  • 拉取官方的镜像,标签为5.6:docker pull mysql:5.6
  • 运行MySQl容器(容器内mysql默认3306端口,所以内部端口要用3306,4401为服务器暴露端口。如果要修改容器内MySQL端口则需要修改配置文件;指定容器数据、日志、配置文件地址,会自动创建对应文件夹)
    • docker run -p 4401:3306 --name mysql4401 -v $PWD/conf:/etc/mysql/conf.d -v $PWD/logs:/logs -v $PWD/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6
  • 进入容器:docker exec -it mysql4401 bash
  • 登录MySQL:mysql -uroot -p123456
  • 查看用户信息:select host,user,plugin,authentication_string from mysql.user;
  • 修改用户权限,支持外网访问:grant all privileges on *.* to root@"%" identified by "你的密码" with grant option;
  • 更新用户权限:FLUSH PRIVILEGES;
  • 退出容器:CTRL+P+Q

五、多个微服务部署时需要解决的问题

5.1、同docker-compose.yml文件下的容器之间通讯

使用同一个docker-compose.yml文件部署多个容器时,docker默认为这些容器创建同一个网络。

在同一个网络下,容器之间可以直接通过容器名称来直接访问。

所以,通过以下方式即可实现容器之间通讯:

1、修改程序的配置文件,支持环境变量取值

eureka:
  client:
    # 注册到注册中心的地址
    service-url:
      defaultZone: http://me:1234@${EUREKA_HOST:localhost}:${EUREKA_PORT:1000}/eureka
    # 客户端定时获取服务器注册表的间隔时间,默认30
    registry-fetch-interval-seconds: 10

2、修改docker-compose.yml文件,使用容器名作为环境变量传入

version: '2'
services:
  eureka:
    build: ./eureka
    ports:
      - "1000:1000"
  auth:
    build: ./auth
    ports:
      - "2000:2000"
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - EUREKA_HOST=eureka
      - EUREKA_PORT=1000

5.2、不同docker-compose.yml文件下的容器之间通讯

原理同上,可以将这些容器放置到同一个网络下即可。

参考:https://www.cnblogs.com/jsonhc/p/7823286.html

network配置参考:https://blog.csdn.net/Kiloveyousmile/article/details/79830810

在实际应用过程,我们可以考虑以下方案:

  • 手动创建网络:docker network create my_net
  • 查看当前network列表:docker network ls
  • 单独运行的容器,可以通过network参数设置对应网络:docker run --name redis --network my_net -p 6379:6379 -v $PWD/data:/data -d redis:3.2 redis-server --appendonly yes
  • 使用docker-compose.yml运行的容器,设置network有2种方式:
    • 修改默认值
version: '2'
services:
  eureka:
    build: ./eureka
    ports:
      - "1000:1000"
  auth:
    build: ./auth
    ports:
      - "2000:2000"
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - EUREKA_HOST=eureka
      - EUREKA_PORT=1000
networks:
  default:
    external:
      name: my_net
    • 为每个服务单独配置
version: '2'
services:
  eureka:
    build: ./eureka
    ports:
      - "1000:1000"
    networks:
      - "ex_my_net"
  auth:
    build: ./auth
    ports:
      - "2000:2000"
    networks:
      - "ex_my_net"
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - EUREKA_HOST=eureka
      - EUREKA_PORT=1000
networks:
  ex_my_net:
    external:
      name: my_net

5.3、调用顺序问题

在开发过程中,我们一般是先运行注册中心,运行完成后,再运行认证中心;认证中心运行完成,在运行网关,然后再到通用权限系统。

即使docker-compose中使用了depends_on,只会控制其开始启动的先后顺序,而不会等到某个启动完成才启动下一个。

方案:使用wait-for-it镜像(还有其他工具自行研究)

原理:使用wait-for-it包裹该容器后,容器启动后,会检测其需要等待的服务所在端口是否已开放;端口开放代表该服务已启动完毕,才会运行这个被包裹容器。

用法:

1、到github:https://github.com/vishnubob/wait-for-it下载wait-for-it.sh,然后通过XFTP上传到服务器中

2、需要等待其他容器才启动的容器,对应的dockerfile加上语句

ADD wait-for-it.sh /wait-for-it.sh

代表需要把这个文件复制到容器中

3、 docker-compose文件中,加入相应命令

zuul:
  build: ./zuul
  ports:
    - "3000:3000"
  depends_on:
    - eureka
    - auth
  links:
    - redis
  volumes:
    - ./wait-for-it.sh:/wait-for-it.sh
  entrypoint: /wait-for-it.sh auth:2000 -t 600 --
  command: java -Djava.security.egd=file:/dev/./urandom -jar /app.jar
  environment:
    - REDIS_HOST=redis
    - REDIS_PORT=6379
    - EUREKA_HOST=eureka
    - EUREKA_PORT=1000
    - AUTH_HOST=auth
    - AUTH_PORT=2000

5.4、内存管理问题

六、微服务系统部署到阿里云CentOS上去

6.1、项目介绍

  • 项目一共有5个微服务,分别是:Eureka注册中心,OAuth认证服务器,Zuul网关,Upms通用权限系统,Inventory进销存系统
  • 运行顺序要求:Zuul网关需要在Oauth认证服务器启动完成后才能启动,否则会报错关闭掉;其它服务可以一起启动,没有要求

6.2、操作流程

  • 阿里云使用公用镜像安装CentOS系统
    • 这里使用CentOS7.3版本,之前用过CentOS7.4版本,出现一些问题,但是不肯定这些问题是由于版本产生,还是由于当时操作原因产生的。
    • 使用的是ID为m-wz90dpguuze302fl71ad的【Java运行环境(Centos7 64 | JDK8|Tomcat8)】镜像
    • 使用阿里云公共镜像中的CentOS7.3
    • 测试ping www.baidu.com看是否连通外网,如果不连通,按照1.2的做法处理
  • 安装docker
    • 查看是否安装docker:docker version
    • 删除自带docker:sudo yum -y remove docker
    • 安装yum-utils:sudo yum install -y yum-utils
    • 设置Yum源:sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    • 更新Yum缓存:sudo yum makecache fast
    • 安装Docker CE:

      sudo yum -y install docker-ce

    • 启动Docker后台服务:

      sudo systemctl start docker

    • 查看docker版本:docker --version

  • 设置docker镜像加速器:登录阿里云,参考https://cr.console.aliyun.com/cn-shenzhen/mirrors
  • 使用docker创建一个网络,使容器之间能相互通信:docker network create my_net
  • 使用docker在上面的网络中,运行一个redis容器,并命名容器为redis
    • 获取Redis镜像:docker pull redis:3.2
    • 运行Redis容器:docker run --name redis --network my_net -p 6379:6379 -v $PWD/redis/data:/data -d redis:3.2 redis-server --appendonly yes
    • 尝试连接装载redis的容器
      • docker exec -it 43f7a65ec7f8 redis-cli
      • 然后输入info,可以看到相关信息,代表成功连接
  • 使用docker在上面的网络中,运行一个MySQL容器,并名为容器为mysql
    • 拉取官方的镜像,标签为5.6:docker pull mysql:5.6
    • 运行MySQl容器,指定容器数据、日志、配置文件地址,会自动创建对应文件夹:
      • docker run -p 3306:3306 --name mysql --network my_net -v $PWD/mysql/conf:/etc/mysql/conf.d -v $PWD/mysql/logs:/logs -v $PWD/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6
    • 进入容器:docker exec -it mysql bash
    • 登录MySQL:mysql -uroot -p123456
    • 查看用户信息:select host,user,plugin,authentication_string from mysql.user;
    • 修改用户权限,支持外网访问:grant all privileges on *.* to root@"%" identified by "你的密码" with grant option;
    • 更新用户权限:FLUSH PRIVILEGES;
    • 退出容器:CTRL+P+Q
  • 打包5个微服务的jar包
    • 参考2.2,为程序加入打包插件
    • 参考5.1,对程序的配置进行修改,支持环境变量传入
    • 然后使用Idea中的Maven Package命令生成对应jar包
  • 使用FTP上传的所需文件
    • 【基础技术与框架】一篇文章搞掂:Docker
    • Dockerfile的形式参考2.1
    • docker-compose文件如下
【基础技术与框架】一篇文章搞掂:Docker【基础技术与框架】一篇文章搞掂:Docker
version: '2'
services:
  eureka:
    build: ./eureka
    ports:
      - "1000:1000"
  auth:
    build: ./auth
    ports:
      - "2000:2000"
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - EUREKA_HOST=eureka
      - EUREKA_PORT=1000
      - MYSQL_HOST=mysql
      - MYSQL_PORT=3306
      - MYSQL_USER=root
      - MYSQL_PASSWORD=123456
  zuul:
    build: ./zuul
    ports:
      - "3000:3000"
    volumes:
      - ./wait-for-it.sh:/wait-for-it.sh
    entrypoint: /wait-for-it.sh auth:2000 -t 600 --
    command: java -Djava.security.egd=file:/dev/./urandom -jar /app.jar
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - EUREKA_HOST=eureka
      - EUREKA_PORT=1000
      - AUTH_HOST=auth
      - AUTH_PORT=2000
  upms:
    build: ./upms
    ports:
      - "4000:4000"
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - EUREKA_HOST=eureka
      - EUREKA_PORT=1000
      - MYSQL_HOST=mysql
      - MYSQL_PORT=3306
      - MYSQL_USER=root
      - MYSQL_PASSWORD=123456
  inv:
    build: ./inventory
    ports:
      - "5000:5000"
    environment:
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - EUREKA_HOST=eureka
      - EUREKA_PORT=1000
      - MYSQL_HOST=mysql
      - MYSQL_PORT=3306
      - MYSQL_USER=root
      - MYSQL_PASSWORD=123456
networks:
  default:
    external:
      name: my_net
docker-compose.yml
    • 使用FTP把ltmicro文件夹上传到root目录下
    • 并为wait-for-it.sh文件设置所有权限
  • 安装docker-compose
curl -L https://github.com/docker/compose/releases/download/1.22.0-rc1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
    • 然后执行命令更改compose工具的执行权限:chmod +x /usr/local/bin/docker-compose
    • 查看安装版本:docker-compose version
    • 查看帮助:docker-compose -h
    • 查看某个命令的帮助:docker-compose help down
  • 运行docker-compose编排文件
    • 转到目录:cd ltmicro
    • 执行命令:docker-compose up -d
    • 停止和删除相关容器:docker-compose down --rmi all

问题:

以下问题可能是我购买的云服务器内存不足引发的,这个需要留意下。

1、docker无法启动

提示:Job for docker.service failed because the control process exited with error

解决方法:

以下为一个找到的方案,但是实际中,是不知道是因为我在阿里云使用了CentOS7.4系统的问题还是其中安装软件的问题,后来更换阿里云官方镜像CentOS7.3后则解决问题了。

参考:https://blog.csdn.net/qq_35904833/article/details/74189383

# rm -rf /var/lib/docker/
# vim /etc/docker/daemon.json

添加以下内容
{
    "graph": "/mnt/docker-data",
    "storage-driver": "overlay"
}

重启服务
systemctl restart docker-data

2、各种问题找到的一个可能原因

如上面的,以及No such file or directory,需要升级内核?yum update即可?https://blog.csdn.net/github_39599694/article/details/79563573

3、No such file or directory

使用wait-for-it.sh的时候,提示No such file or directory,是以为该文件被打开过,保存后,编码改变了。

可以参考:https://blog.csdn.net/huoyunshen88/article/details/41575225

4、Docker内服务通过外网访问本机的MySQL

在QQ群看到的一个问题,在这里记录一下,他说执行grant all privileges on *.* to root@'%' identified by '******'就可以了

——基础知识:

1、Dockerfile详解

Dockerfile用于定制镜像,把镜像的每一层修改、安装、构建、操作的命令都写入一个脚本,进行集中管理。

注意,Dockerfile中每一个指令都会增加一层镜像,

1.1、FROM指令

用于选择基础镜像

  • 在 Docker Store 上有非常多的高质量的官方镜像
    • 可以直接拿来使用的服务类的镜像:nginx 、 redis 、 mongo 、mysql 等;
    • 方便开发、构建、运行各种语言应用的镜像:node 、 openjdk 、 python 等;
    • 更为基础的操作系统镜像:ubuntu 、 debian 、 centos 等;
    • 空白镜像:scratch 。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。

1.2、RUN指令

用于执行命令行命令

  • 2种格式:
    • shell 格式: RUN <命令> ,就像直接在命令行中输入的命令一样
    • exec 格式: RUN ["可执行文件", "参数1", "参数2"]
  • Dockerfile中可以写多个RUN命令,但是每一个命令会增加一层镜像,所以不应该这么做
  • 正确写法,应该使用\换行和&&连接:
    • FROM debian:jessie
    • RUN buildDeps='gcc libc6-dev make' \
    •     && apt-get update \
    •     && apt-get install -y $buildDeps \

FROM debian:jessie RUN buildDeps='gcc libc6-dev make'\&& apt-get update \&& apt-get install -y$buildDeps\