maksim 发布的文章

https://docs.docker.com/compose/

Dockerfile。商城。拆分很多微服务。10微服务

购物车微服务+Redis+MySQL = 一套服务(自动化的全套部署,全套下线)。交给Docker Compose【编排】。一个整套的application会有非常多的service

一次性的将所有相关联的服务自动部署。

前面我们使用 Docker 的时候,定义 Dockerfile 文件,然后使用 docker build、docker run 等命令操作容器。然而微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,那么效率之低,维护量之大可想而知。批量的维护整套应用

使用 Docker Compose 可以轻松、高效的管理容器,它是一个用于定义和运行多容器 Docker 的应用程序工具

Compose的三步最佳实战:

  1. 定义自己应用的 Dockerfile 文件。为了在anywhere进行构建。
  2. 使用 docker-compose.yml定义我们服务。这一整套服务可以一起在一个隔离环境运行.
  3. 运行 docker-compose up 就可以启动整个app.

总结:批量服务编排。docker-compose.yml声明服务的规则。

单机环境。

1、安装

sudo curl -L "https://github.com/docker/compose/releases/download/1.25.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

docker-compose version

2、体验

1、创建一个测试应用

#1、Create a directory for the project:

mkdir composetest
cd composetest

#2、Create a file called app.py in your project directory and paste this in:
import time

import redis
from flask import Flask

app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)


def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)


@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.\n'.format(count)
#3、Create another file called requirements.txt in your project directory and paste this in:
flask
redis

2、创建Dockerfile

#In your project directory, create a file named Dockerfile and paste the following:
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP app.py
ENV FLASK_RUN_HOST 0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["flask", "run"]

3、在Compose file里定义服务

#Create a file called docker-compose.yml in your project directory and paste the following:
version: '3'
services:
  web:
    build: .
    ports:
      - "5000:5000"
  redis:
    image: "redis:alpine"

4、Compose构建和运行应用

docker-compose up -d
#访问 http://localhost:5000/ 测试效果

5、效果

自己打包---》dockerfile--》结合docker-compose

1、创建网络

​ 默认的网络:1998ee0450ee composetest_default bridge local

version: '3'
services:
  web:
    build: .
    ports:
      - "5000:5000"
  redis:
    image: "redis:alpine"
#默认两个服务都用一个网络,是contextName_default;web里面可以使用redis的hostname访问
#cache = redis.Redis(host='redis', port=6379)
#默认每一个启动的容器 contextName_serviceName_1
docker network inspect composetest_default  
#效果:这个网络里面有。两个容器
#docker service ls #查看当前的服务。docker集群下,我们能看到所有的服务 容器的名_num。代表第几个副本。

集群状态下;compose更帅。我们可以直接定义服务在多个机器都有副本。

2022-10-04T12:50:49.png

2、构建每个镜像

3、创建两个容器在同一网络,我们可以使用hostname访问

docker-compose帮我们自定的部署了pyweb,redis,并且pyweb能访问redis。pyweb的5000浏览器也能访问

2022-10-04T12:51:11.png

2022-10-04T12:51:47.png

整个docker:

1、dockerfile

2、docker-compose

3、docker二次开发,如何更换底层存储驱动,如何自定义docker的网络。。。

详情:https://docs.docker.com/compose/compose-file/#reference-and-guidelines

#语法
三层
version: ""
services:  #定义很多服务
    服务1:
      #当前服务配置
    服务2:
      #当前服务配置
#服务要用的网络、卷,等其他全局规则,定义
volumes:
networks:
configs:
secrets:



networks:
  outside:
    external: true #外面创建好了,docker-compose不自动创建
如果自定义用了网络。卷等,都必须声明
version: "3"
services:
  java-app: 
     build: .
     ports:
       - "9000:8080"
     depends_on:
       - mysql
     command: java -jar /app.jar --spring.datasource.url=jdbc:mysql://mysql:3306/wps?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
     networks:
       - app-network
  mysql:
    image: mysql:5.7
    environment:
       MYSQL_ROOT_PASSWORD: 123456
    networks:
       - app-network

networks:
  app-network: {}


docker-compose up -d
docker-compose up -d --build #再触发一下手动构建


随便网上一个项目。dockerfile+docker-compose 一键搞定
mysql dockerfile-->run 网上的项目拉下来,把的sql文件导入数据。


docker-compose替代了dockerfile。完全可以(镜像存在)自定义的镜像 dockerfile+docker-compose。

整个docker-compose up/down/rm 单机情况下的编排

app-redis。用同一个网络。

集群情况。官方默认docker-swarm

docker 5个。 2主(HA) 3从(工作节点)

5、概念

工程、服务、容器

  • Docker Compose 将所管理的容器分为三层,分别是工程(project)、服务(service)、容器(container)
  • Docker Compose 运行目录下的所有文件(docker-compose.yml)组成一个工程,一个工程包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖,一个服务可包括多个容器实例

6、实战-使用Compose构建一个wordpress应用

https://docs.docker.com/compose/wordpress/

Docker仓库(私有仓库)

阿里云

开通阿里云容器镜像服务

l8u4n4s4.png

#阿里云下载镜像,需要填写前缀
docker pull registry.cn-shenzhen.aliyuncs.com/ali/mysql:[镜像版本号]

ali:命名空间
mysql:镜像仓库
  • 创建命名空间
  • 创建镜像仓库

Harbor

网络高级

#安装Es+kibana;kibana得指定es地址,
#sb-book-crud-service   mysql

Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。

l8u4nks6.png

Linux虚拟网络技术。

Docker容器网络就很好的利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);

Docker中的网络接口默认都是虚拟的接口。虚拟接口的优势就是转发效率极高(因为Linux是在内核中进行数据的复制来实现虚拟接口之间的数据转发,无需通过外部的网络设备交换),对于本地系统和容器系统来说,虚拟接口跟一个正常的以太网卡相比并没有区别,只是他的速度快很多。

原理:

1、每一个安装了Docker的linux主机都有一个docker0的虚拟网卡。桥接网卡

2、没启动一个容器linux主机多了一个虚拟网卡。

3、docker run -d -P --name tomcat --net bridge tomcat:8

#1、docker0网络的特点。,
    默认
    域名访问不通
    --link 域名通了,但是删了又不行
    
#2、可以让容器创建的时候使用自定义网络
     1、自定义创建的默认default "bridge"
     2、自定义创建一个网络网络
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
    所有东西实时维护好,直接域名ping同
    3、理解网络。

docker network connect [OPTIONS] NETWORK CONTAINER

跨网络连接别人就用
docker network connect mynet tomcat
效果:
    1、自定义网络,默认都可以用主机名访问通
    2、跨网络连接别人就用 docker network connect mynet tomcat

1、容器启动,指定容器ip。 docker run --ip 192.168.0.3 --net 自定义网络  
2、网络。docker network create --subnet 指定子网范围  --driver bridge 所有东西实时维护好,直接域名ping同
3、docker compose。

image-20200425220210857

1、网络的创建过程

Docker创建一个容器的时候,会具体执行以下操作:

  1. 创建一对虚拟接口,分别放到本地主机和新容器的命名空间中
  2. 本地主机一段的虚拟接口连接到默认的docker0网桥或者指定网桥上,并具有一个以veth开头的唯一名字
  3. 容器一段的虚拟接口将放到新创建的容器中,并修改名字为eth0。这个接口只在容器的命名空间可见
  4. 从网桥的可用地址段中获取一个空闲的地址分配给容器的eth0(如:172.17.0.2/16),并配置默认路由网关为docker0网卡的内部接口docker0的IP地址(如:172.17.42.1/16)

完成以上,容器就可以使用它所能看到的eth0虚拟网卡来连接其他容器和访问外部网络。

网络模式

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

实验测试网络互连

  • 大家重点理解veth pair技术
  • --link是容器在默认网络模式下,可以互相使用容器名ping通的
  • 如果容器创建时使用自定义网络,不使用--link也是可以ping通相互的容器名
  • --link 生产一般不用,我们可以使用自定义网络的方式
  • docker network connect

部署一个集群(redis)

Dockerfile

Dockerfile指南:https://docs.docker.com/engine/reference/builder/

事情:nginx。tomcat。mysql。镜像从哪里来?

我们自己如何做一个镜像。微服务。SpringBoot。上云部署。最方便是Docker。

微服务打包成镜像。任何装了Docker。都可以下载使用。

应用-->Dockerfile--->打包成镜像--->上传到仓库(公有仓库,私有仓库)--->下载镜像--->启动运行。

移植:扩充服务器。

如何得到一个镜像

1、自己做的Dockefile

https://docs.docker.com/engine/reference/builder/

#nginx Dockerfile的示例
#alpine:3.11超迷你的linux 4mb;
#alpine:3.11 linux全目录 基于宿主机linux的一个统一接口。cpu,mem。主机。
FROM alpine:3.11

LABEL maintainer="NGINX Docker Maintainers <[email protected]>"

#环境
ENV NGINX_VERSION 1.18.0
ENV NJS_VERSION   0.4.0
ENV PKG_RELEASE   1

RUN set -x \
# create nginx user/group first, to be consistent throughout docker variants
    && addgroup -g 101 -S nginx \
    && adduser -S -D -H -u 101 -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx \
    && apkArch="$(cat /etc/apk/arch)" \
    && nginxPackages=" \
        nginx=${NGINX_VERSION}-r${PKG_RELEASE} \
        nginx-module-xslt=${NGINX_VERSION}-r${PKG_RELEASE} \
        nginx-module-geoip=${NGINX_VERSION}-r${PKG_RELEASE} \
        nginx-module-image-filter=${NGINX_VERSION}-r${PKG_RELEASE} \
        nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-r${PKG_RELEASE} \
    " \
    && case "$apkArch" in \
        x86_64) \
# arches officially built by upstream
            set -x \
            && KEY_SHA512="e7fa8303923d9b95db37a77ad46c68fd4755ff935d0a534d26eba83de193c76166c68bfe7f65471bf8881004ef4aa6df3e34689c305662750c0172fca5d8552a *stdin" \
            && apk add --no-cache --virtual .cert-deps \
                openssl \
            && wget -O /tmp/nginx_signing.rsa.pub https://nginx.org/keys/nginx_signing.rsa.pub \
            && if [ "$(openssl rsa -pubin -in /tmp/nginx_signing.rsa.pub -text -noout | openssl sha512 -r)" = "$KEY_SHA512" ]; then \
                echo "key verification succeeded!"; \
                mv /tmp/nginx_signing.rsa.pub /etc/apk/keys/; \
            else \
                echo "key verification failed!"; \
                exit 1; \
            fi \
            && apk del .cert-deps \
            && apk add -X "https://nginx.org/packages/alpine/v$(egrep -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" --no-cache $nginxPackages \
            ;; \
        *) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published packaging sources
            set -x \
            && tempDir="$(mktemp -d)" \
            && chown nobody:nobody $tempDir \
            && apk add --no-cache --virtual .build-deps \
                gcc \
                libc-dev \
                make \
                openssl-dev \
                pcre-dev \
                zlib-dev \
                linux-headers \
                libxslt-dev \
                gd-dev \
                geoip-dev \
                perl-dev \
                libedit-dev \
                mercurial \
                bash \
                alpine-sdk \
                findutils \
            && su nobody -s /bin/sh -c " \
                export HOME=${tempDir} \
                && cd ${tempDir} \
                && hg clone https://hg.nginx.org/pkg-oss \
                && cd pkg-oss \
                && hg up -r 474 \
                && cd alpine \
                && make all \
                && apk index -o ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz ${tempDir}/packages/alpine/${apkArch}/*.apk \
                && abuild-sign -k ${tempDir}/.abuild/abuild-key.rsa ${tempDir}/packages/alpine/${apkArch}/APKINDEX.tar.gz \
                " \
            && cp ${tempDir}/.abuild/abuild-key.rsa.pub /etc/apk/keys/ \
            && apk del .build-deps \
            && apk add -X ${tempDir}/packages/alpine/ --no-cache $nginxPackages \
            ;; \
    esac \
# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
    && if [ -n "$tempDir" ]; then rm -rf "$tempDir"; fi \
    && if [ -n "/etc/apk/keys/abuild-key.rsa.pub" ]; then rm -f /etc/apk/keys/abuild-key.rsa.pub; fi \
    && if [ -n "/etc/apk/keys/nginx_signing.rsa.pub" ]; then rm -f /etc/apk/keys/nginx_signing.rsa.pub; fi \
# Bring in gettext so we can get `envsubst`, then throw
# the rest away. To do this, we need to install `gettext`
# then move `envsubst` out of the way so `gettext` can
# be deleted completely, then move `envsubst` back.
    && apk add --no-cache --virtual .gettext gettext \
    && mv /usr/bin/envsubst /tmp/ \
    \
    && runDeps="$( \
        scanelf --needed --nobanner /tmp/envsubst \
            | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
            | sort -u \
            | xargs -r apk info --installed \
            | sort -u \
    )" \
    && apk add --no-cache $runDeps \
    && apk del .gettext \
    && mv /tmp/envsubst /usr/local/bin/ \
# Bring in tzdata so users could set the timezones through the environment
# variables
    && apk add --no-cache tzdata \
# Bring in curl and ca-certificates to make registering on DNS SD easier
    && apk add --no-cache curl ca-certificates \
# forward request and error logs to docker log collector
    && ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log \
# make default server listen on ipv6
    && sed -i -E 's,listen       80;,listen       80;\n    listen  [::]:80;,' \
        /etc/nginx/conf.d/default.conf

EXPOSE 80

STOPSIGNAL SIGTERM

CMD ["nginx", "-g", "daemon off;"]
# Dockerfile复原了我们在linux上安装nginx的过程。

2、别人给我们准备好的文件。xxx.tar。可以通过硬盘传输使用。

1586440635817

1、创建项目dockerfile

2、上传项目到服务器。

3、进入项目,构建镜像到本地仓库;

  • docker build -t nginx:1.0 -f ./Dockerfile . 别忘了最后的小数点。
  • docker images 查看镜像
  • docker exec -it 容器id /bin/bash;进入容器,修改容器
  • docker commit -a “icoding” -m “nginxxx” 容器id mynginx:2.0

    • docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
    • OPTIONS说明:

      • -a :提交的镜像作者;
      • -c :使用Dockerfile指令来创建镜像;
      • -m :提交时的说明文字;
      • -p :在commit时,将容器暂停。
  • docker login : 登陆到一个Docker镜像仓库,如果未指定镜像仓库地址,默认为官方仓库 Docker Hub

    • docker login -u 用户名 -p 密码
  • docker logout : 登出一个Docker镜像仓库,如果未指定镜像仓库地址,默认为官方仓库 Docker Hub

4、推送镜像到docker hub

  • 标记镜像,docker tag local-image:tagname username/new-repo:tagname
  • 上传镜像,docker push username/new-repo:tagname

5、保存镜像,加载镜像

  • 可以保存镜像为tar,使用u盘等设备复制到任意docker主机,再次加载镜像
  • 保存:docker save spring-boot-docker -o /home/spring-boot-docker.tar
  • 加载:docker load -i spring-boot-docker.tar

6、阿里云操作

  • 登录阿里云,密码就是开通镜像仓库时 的密码

    • docker login --username=icoding registry.cn-hangzhou.aliyuncs.com
  • 拉取镜像

    • docker pull registry.cn-hangzhou.aliyuncs.com/icoding/i-nginx:v1.0
  • 推送镜像

    • docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/icoding/icoding-nginx:v2
    • docker push registry.cn-hangzhou.aliyuncs.com/icoding/icoding-nginx:v2

2、Dockerfile详解

Dockerfile由一行行命令语句组成,并且支持以#开头的注释行。

一般而言,Dockerfile可以分为四部分

基础镜像信息 维护者信息 镜像操作指令 启动时执行指令

指令说明
FROM指定基础镜像
MAINTAINER指定维护者信息,已经过时,可以使用LABEL maintainer=xxx 来替代
RUN运行命令 v
CMD指定启动容器时默认的命令 v
LABEL指定生成镜像的元数据标签信息 v
EXPOSE声明镜像内服务监听的端口 v
ENV指定环境变量,可以在docker run的时候使用-e改变 v
ADD复制指定的src路径下的内容到容器中的dest路径下,src可以为url会自动下载,可以为tar文件,会自动解压
COPY复制本地主机的src路径下的内容到镜像中的dest路径下,但不会自动解压等
ENTRYPOINT指定镜像的默认入口.运行命令 v
VOLUME创建数据卷挂载点
USER指定运行容器时的用户名或UID
WORKDIR配置工作目录,为后续的RUN、CMD、ENTRYPOINT指令配置工作目录
ARG指定镜像内使用的参数(如版本号信息等),可以在build的时候,使用--build-args改变 v
OBBUILD配置当创建的镜像作为其他镜像的基础镜像是,所指定的创建操作指令
STOPSIGNAL容器退出的信号值
HEALTHCHECK健康检查
SHELL指定使用shell时的默认shell类型

1、RUN、CMD、ENTRYPOINT区别

  1. RUN 执行命令并创建新的镜像层RUN 经常用于安装软件包(在构建镜像时运行的)。
  2. CMD 设置容器启动后默认执行的命令及其参数但 CMD 能够被 docker run 后面跟的命令行参数替换。
  3. ENTRYPOINT 配置容器启动时运行的命令。
  4. 以上命令都可以使用shell或者exec方式执行
  5. CMD ["executable","param1","param2"] (exec form, this is the preferred form)
  6. CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
  7. CMD command param1 param2 (shell form)

2、shell和exec方式

1.  shell 是  /bin/sh -c <command>的方式,

2.  exec ["/bin/sh","-c",command] 的方式== shell方式
eg:shell方式

ENV name icoding  
ENTRYPOINT echo "Hello, $name" 
#输出 Hello icoding

ENTRYPOINT ["/bin/echo", "Hello, $name"]
#输出 Hello $name

ENTRYPOINT ["/bin/sh", "-c", "echo Hello, $name"]
#输出 Hello icoding

##建议:CMD 和 ENTRYPOINT 推荐使用 Exec 格式因为指令可读性更强更容易理解。RUN 则两种格式都可以。exec的时候要获取环境变量等值,就用/bin/sh -c 即可。
No ENTRYPOINTENTRYPOINT exec_entry p1_entryENTRYPOINT [“exec_entry”, “p1_entry”]
No CMDerror, not allowed/bin/sh -c exec_entry p1_entryexec_entry p1_entry
CMD [“exec_cmd”, “p1_cmd”]exec_cmd p1_cmd/bin/sh -c exec_entry p1_entryexec_entry p1_entry exec_cmd p1_cmd
CMD [“p1_cmd”, “p2_cmd”]p1_cmd p2_cmd/bin/sh -c exec_entry p1_entryexec_entry p1_entry p1_cmd p2_cmd
CMD exec_cmd p1_cmd/bin/sh -c exec_cmd p1_cmd/bin/sh -c exec_entry p1_entryexec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

3、实战Dockerfile

#1、ping命令小工具。
编写Dockerfile-->打包镜像--->根据这个镜像启动容器使用容器的功能
docker build -t hello .
-t:tag标签。镜像名字
.:在当前目录下工作
默认.这个目录下就得有一个 Dockerfile
FROM alpine
CMD ping baidu.com

#2、文件名不是Dockerfile。用-f 指定
docker build -f Dockerfile2 -t hello:v1.0 .
docker run -d --name hello hello;

#3、默认构建出来的镜像,放到了我们的本地镜像仓库。
    1)、登陆到dockerhub
    2)、dockehub国外,就算能Push,非常慢。阿里云加速的是下载。
    
#4、把这个镜像发布到Docker hub
    1)、docker login -u icodingallen;登陆到docker hub
    2)、docker push nginx:v1.0;推送过去。
    太慢了
#5、搭建私有的镜像仓库。使用阿里云的镜像仓库(免费)。
    1)、docker login [email protected] registry.cn-hangzhou.aliyuncs.com

为了团队自治。只能看到自己的仓库的所有镜像。namespace;
创建镜像仓库。保存镜像各种版本,book: 1 2 3 4 5
docker tag hello:v1.0 registry.cn-hangzhou.aliyuncs.com/icodingdocker/hello:v1.3
docker push registry.cn-hangzhou.aliyuncs.com/icodingdocker/hello:v1.3

image-20200427213352196

image-20200427215730365

docker.io/libarary/hello:latest

registry.cn-hangzhou.aliyuncs.com/icodingdocker/hello:v1.0

镜像---》打包--》上传。

5、Dockerfile区分一些易混淆的指令

1、USER 执行cmd等之类命令的使用那个用户

alpine  sudo   gosu
FROM centos
RUN groupadd -r abc && useradd -r -g abc aaa
USER aaa
CMD whoami
# CMD 就是容器启动以后要执行的命令

2、ARG、ENV

  • The ARG instruction defines a variable that users can pass at build-time to the builder with the docker build :arg在构建期间。docker build
  • The environment variables set using ENV will persist when a container is run from the resulting image. You can view the values using docker inspect, and change them using docker run --env =. env在运行是可以用到的
FROM alpine

ARG bbb haha
ENV abc 666

CMD echo $bbb
容器运行的时候arg的东西拿不到

CMD echo $abc
容器运行的时候env的能拿到


FROM alpine

ARG bbb haha
ENV abc=$bbb

CMD echo $abc

ARG指定的值,在镜像构建的后面位置,构建期间都可以使用到。

改变这些值行不行
docker build -f Dockerfile-arg-env -t file-arg --build-arg bbb=888 --build-arg abc=777 .
结果888 


# 构建时 bbb=888 abc=777 ,cmd打印888.原因 构建时不能改变env
# 运行时 
\ARGENV
build时√。--build-arg改变 构建参数(ARG声明)生效,能不能改(不能)
运行时不生效生效。能改 -e abc

最佳实战:

ARG:定义一些版本信息

​ FROM alpine

​ ARG version=1

​ RUN yum install nginx:$version

Env:运行时的环境变量。

ENV env = --spring.profile.active=prod

​ -e修改。sb java -jar xxx.jar $env

3、ADD和COPY

#构建了一个SpringBoot镜像。 xxx.jar 
/opt

docker build -t hello .

ADD:将当前目录下的内容放到镜像里面一起打包。

COPY:将当前目录下的内容放到镜像里面一起打包。

ADD ["<src1>","<src2>","<dest>"]  dest:容器里面的目录
可以指定很多种路径地址。自动下载,解压复制。
FROM alpine

ADD hello.tar /opt/hello

COPY hello.tar /opt/world/

CMD echo "1234"

这个东西构建的镜像为什么docker run -d 不行。因为容器运行的是ech 1234;
没有一个守护进程一直运行。
CMD ping baidu.com

4、VOLUME和WORKDIR

VOLUME:指定容器需要挂载的卷
WORKDIR:工作目录。
    1、以后的其他命令在这个目录里面运行
    2、exec进去都默认来到了 WORKDIR 指定的目录。sh
    docker run -it --rm file-volume sh
    
WORKDIR


WORKDIR /root  == RUN cd /root

挂载麻烦。自动挂载。
FROM alpine

WORKDIR /opt/a

VOLUME /opt/b

COPY hello.txt .

ADD hello.tar /opt/b

CMD whoami

volume声明的挂载目录,即使容器运行的时候,不用-v进行挂载。docker也会自动的进行匿名挂载。
nginx:

5、RUN、CMD、ENTRYPOINT

相同点:运行命令

不同点:

​ RUN:在构建镜像的时候运行的命令

CMD、ENTRYPOINT:在容器启动运行的命令

测试RUN;

#想构建一个具有git功能的镜像。
FROM centos

RUN yum install -y git

WORKDIR /opt/data

RUN git clone https://gitee.com/lanoox/luject.git

VOLUME /opt/data
CMD echo "git clone success"
#CMD 容器运行的时候CMD的命令才执行。

image-20200429201703690

image-20200429201639095

image-20200429202237672

相同的镜像layer层发生变化,只有这层变化。

  • -RUN指令的所有命令都在镜像docker build期间就执行
  • CMD和ENTRYPOINT在容器启动时运行

    • CMD 容器运行的时候CMD的命令才执行。docker run -it --rm file-run bash 能进容器中
    • 替换为ENTRYPOINT。虽然指令运行了。但是 docker run -it --rm file-run bash 。最后的bash没有进去,失效了。

无论是CMD还是ENTRYPOINT还是RUN

  • RUN (shell form, the command is run in a shell, which by default is /bin/sh -c on Linux or cmd /S /C on Windows) RUN yum install -y git;/bin/sh -c可以动态获取一些变量
  • RUN ["executable", "param1", "param2"] (exec form);无法动态获取一些变量
FROM centos

ARG soft=git
RUN ["yum install","-y","$soft"] #这是错误的。因为非`/bin/sh -c`方式,用不到前面声明的ARG,ENV

WORKDIR /opt/data

RUN git clone https://gitee.com/lanoox/luject.git

VOLUME /opt/data
ENTRYPOINT echo "git clone success"

image-20200429203242832

# bash-c 和 数组方式的区别,  修改后的;安装正确。
FROM centos

ARG soft=git
RUN ["/bin/sh","-c","yum install -y $soft"]
#-c command:后免是完整命令
WORKDIR /opt/data

RUN git clone https://gitee.com/lanoox/luject.git

VOLUME /opt/data
ENTRYPOINT echo "git clone success"

RUN、CMD、ENTRYPOINT都支持一下两种方式

The exec form, which is the preferred form:

ENTRYPOINT ["executable", "param1", "param2"]

The shell form:

ENTRYPOINT command param1 param2

总结:

  • 如果运行命令是。[]方式,默认不是bash -c就无法用变化,普通的方式RUN yum -install -y $soft可以使用变量。
  • CMD、ENTRYPOINT的最佳实战

    • Dockerfile文件必须至少有一个 CMD 或者ENTRYPOINT 命令.
    • ENTRYPOINT 用来定义容器如何运行.
    • CMD 应该被用来作为给ENTRYPOINT 传递。默认参数的一种方式
    • CMD 将会被覆盖,容器运行时指定参数的时候. ENTRYPOINT 命令不会被覆盖。
    • CMD多个只会有一个生效。
    • ENTRYPOINT :不被传入的指令覆盖,但是多个也只有一个生效。

    为什么我的指令Dockerfile写CMD的时候,docker run -it --rm file-run bash可以进控制台,而ENTRYPOINT 不行?

    运行效果:

    • Dockerfile是CMD;没打印git clone success。但是bash生效。

    image-20200429205557526

    • Dockerfile 是 ENTRYPOINT;打印了git clone success但是没有进容器(bash没生效)

    image-20200429205729145

  • 混写。CMD + ENTRYPOINT
FROM centos

ARG soft=git
RUN ["/bin/bash","-c","yum install -y $soft"]

WORKDIR /opt/data

RUN git clone https://gitee.com/lanoox/luject.git

VOLUME /opt/data


CMD ["nginx"] #CMD给entrypoint提供参数
ENTRYPOINT ["yum","install","-y"]


运行:
docker run -it --rm file-run maven
FROM centos

ARG soft=git
RUN ["/bin/bash","-c","yum install -y $soft"]

WORKDIR /opt/data

RUN git clone https://gitee.com/lanoox/luject.git

VOLUME /opt/data


CMD ["","","",""] #可以放空
ENTRYPOINT ["/bin/sh","-c","yum install -y"]

#容器启动就是yum install -y "";自己启动命令加上git
CMD 后面有N参数。传了参数替换N个还是最后一个?
docker run -it --rm file-run maven  CMD ["maven"]  对
docker run -it --rm file-run maven  CMD [“”,“”,"maven"] 错

目的:构建镜像。SpringBoot编写的微服务,怎么做镜像?

1、运行的业务的目标环境?SB服务。java环境。FROM

2、怎么启动业务的。得到jar包,java -jar xxx.jar --spring.profile.active=prod --server.port=8080;决定镜像的ENTRYPOINT怎么写。docker exec?docker run(第一次容器启动要执行)?

docker exec haha -it bash【和entrypoint,cmd没啥关系】;

3、业务运行的时候需要啥?java。jar。jar包就想办法放进镜像中。COPY 。ADD

4、业务的那些数据是需要做持久化。VOLUME怎么写。

FROM java:8


#服务器只有dockerfile和jar在一起
COPY *.jar /app.jar

#即使运行没有-v,也会匿名挂载
VOLUME ["/logs"]

CMD ["--server.port=8080"]

EXPOSE ["8080"]

ENTRYPOINT ["java","-jar","/app.jar"]

4、idea快速整合使用

作为一个了解。

1、 Docker开启远程访问

#修改该Docker服务文件
vi /lib/systemd/system/docker.service
#修改ExecStart这行,将原来注释,加上这个命令
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
#重新加载配置文件
systemctl daemon-reload
#重启服务
systemctl restart docker.service
#查看端口是否开启
netstat -nlpt #如果找不到netstat命令,可进行安装。yum install net-tools
#直接curl看是否生效
curl http://127.0.0.1:2375/info

2、IDEA安装Docker插件

1586754551705

3、IDEA配置docker

1586754754747

4、也可以整合自己的私有镜像仓库

1586755152991

==可以提供非常方便的部署功能==

1586755804914

docker build -t hello-java . && docker run -p 88:8080 -v /logs:/logs --name hello-666 hello-java

Dockerfile解耦应用的开发与部署。

maven插件再把部署流程一配置就不解耦。,

应用。代码变了改应用。部署只改Dockerfile。

maven。代码部署放在了一个完整的生命周期。运维人员想要改网络部署等配置,还得改pom。CICD只需要一个Dockerfile。

5、docker-maven-plugin

市面上docker-maven-plugin太多,我们推荐一个 docker-maven-plugin,照着文档使用就行。不管使用哪一种docker插件来构造image,都比不上直接用Dockerfile编写简单,而且可复用,不用学习不同插件不同的构造规则。

最好给自己的Docker服务器配置阿里云镜像加速,否则经常导致镜像下载不来的情况

fabric8io的有两大功能:

  1. 构造并推送Docker镜像
  2. 启动和停止Docker容器
<plugin>
    <groupId>io.fabric8</groupId>
    <artifactId>docker-maven-plugin</artifactId>
    <version>0.33.0</version>

    <!--全局配置-->
    <configuration>
        <!--这一部分是为了实现对远程docker容器的控制-->
        <!--docker主机地址,用于完成docker各项功能,注意是tcp不是http!-->
        <dockerHost>tcp://公网IP:2376</dockerHost>
        <!--docker远程访问所需证书地址,如果docker远程主机没有启用TLS验证则不需要配证书-->
        <certPath>${project.basedir}/docker/ssh</certPath>

        <!--这一部分是为了实现docker镜像的构建和推送-->
        <!--registry地址,用于推送,拉取镜像,我这里用的是阿里的registry-->
        <registry>registry.cn-shenzhen.aliyuncs.com</registry>
        <!--认证配置,用于私有registry认证,如果忘记了可以去阿里的registry查看-->
        <authConfig>
            <push>
                <username>这里填registry的用户名</username>
                <password>这里填registry的密码</password>
            </push>
        </authConfig>

        <!--镜像相关配置,支持多镜像-->
        <images>
            <!-- 单个镜像配置 -->
            <image>
                <!--镜像名(含版本号)-->
                <name>命名空间/仓库名称:镜像版本号</name>
                <!--别名:用于容器命名和在docker-compose.yml文件只能找到对应名字的配置-->
                <alias>${project.name}</alias>
                <!--镜像build相关配置-->
                <build>
                    <!--使用dockerFile文件-->
                    <dockerFile>${project.basedir}/docker/${project.name}</dockerFile>
                </build>
                <!--配置docker-compose文件-->
                <external>
                    <type>compose</type>
                    <basedir>${project.basedir}/docker</basedir>
                    <composeFile>docker-compose.yml</composeFile>
                </external>
                <!--容器run相关配置-->
                <run>
                    <!--配置运行时容器命名策略为:别名,如果不指定则默认为none,即使用随机分配名称-->
                    <namingStrategy>alias</namingStrategy>
                </run>
            </image>
        </images>
    </configuration>
</plugin>
maven指令功能
docker:start创建和启动容器
docker:stop停止并销毁容器
docker:build构建镜像
docker:watch自动进行重建和重启
docker:push将镜像推送到registry
docker:remove从本地docker主机删除镜像
docker:logs显示容器日志
docker:source将docker build archive附加到Maven项目
docker:save将镜像保存到文件
docker:volume-create创建卷以在容器之间共享数据
docker:volume-remove删除创建的卷
#打包到容器运行的完整流程
mvn clean package docker:stop docker:remove docker:build docker:run 
#不是每一次运行都要推送镜像,如果要的话可以单独调用docker:push
mvn docker:push
#示例配置
<plugin>
    <groupId>io.fabric8</groupId>
    <artifactId>docker-maven-plugin</artifactId>
    <version>0.33.0</version>

    <configuration>
        <dockerHost>tcp://111.229.61.54:2375</dockerHost>
        <registry>registry.cn-shenzhen.aliyuncs.com</registry>
        <authConfig>
            <push>
                <username>icodingallen</username>
                <password>icoding123</password>
            </push>
        </authConfig>
        <images>
            <image>
                <name>icoding/icoding-docker:v1.2</name>
                <build>
<!--使用dockerFile文件 <dockerFile>${project.basedir}/docker/${project.name}</dockerFile>-->
                    <dockerFile>${project.basedir}/Dockerfile</dockerFile>
                </build>
                <run>
                    <ports>
                        <port>8080:8080</port>
                    </ports>
                </run>
            </image>
        </images>
    </configuration>
</plugin>
FROM java:8
LABEL maintainer=icoding
ADD target/*.jar /app.jar
EXPOSE 8080
CMD ["java","-jar","app.jar"]
@RestController
public class HelloController {

    @Value("${project.version}")
    String ver;
    @Value("${project.name}")
    String name;

    @GetMapping("/")
    public String hello(){
        return "Hello Docker!"+ver+"=>"+name;
    }
}
#获取maven pom对象的相关属性值。在springboot中获取如下
[email protected]@
[email protected]@
#在pom文件中直接使用${project.xxxx},按照对象属性关系获取即可

安装MySQL

docker run -p 3306:3306 --name mysql \
-v /idata/mysql/log:/var/log/mysql \
-v /idata/mysql/data:/var/lib/mysql \
-v /idata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7

docker exec -it mysql mysql -uroot -proot
#配置MySQL主从复制 
#创建master
docker run -p 3306:3306 --name mysql-master \
-v /idata/mysql/log:/var/log/mysql \
-v /idata/mysql/data:/var/lib/mysql \
-v /idata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
##配置文件
vim /idata/mysql/conf/my.cnf

[client]
default-character-set=utf8
 
[mysql]
default-character-set=utf8
 
[mysqld]
init_connect='SET collation_connection = utf8_unicode_ci'
init_connect='SET NAMES utf8'
character-set-server=utf8
collation-server=utf8_unicode_ci
skip-character-set-client-handshake
skip-name-resolve

server_id=1
log-bin=mysql-bin
read-only=0
binlog-do-db=icoding_test

replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema

#创建Slaver,和master一致,配置文件修改如下即可
server_id=2
log-bin=mysql-bin
read-only=1
binlog-do-db=icoding_test

replicate-ignore-db=mysql
replicate-ignore-db=sys
replicate-ignore-db=information_schema
replicate-ignore-db=performance_schema

#配置matser
1、添加用来同步的用户
       GRANT REPLICATION SLAVE ON *.* to 'backup'@'%' identified by '123456';
2、查看master状态
   show master status\G;
 
#配置slaver
1、change master to master_host='mysql-master',master_user='backup',master_password='123456',master_log_file='mysql-bin.000003',master_log_pos=0,master_port=3306;
2、start slave;
3、show slave status\G;

安装Redis

mkdir -p /idata/redis/conf
touch /idata/redis/conf/redis.conf

docker run -p 6379:6379 --name redis -v /idata/redis/data:/data \
-v /idata/redis/conf:/etc/redis \
-d redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf

redis 自描述文件:
https://raw.githubusercontent.com/antirez/redis/4.0/redis.conf

#安装redis-cluster;3主3从方式,从为了同步备份,主进行slot数据分片
##单机情况下可以这样做。
## cluster-announce-ip 192.168.56.10 为自己服务器的ip
for port in $(seq 7001 7006); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port ${port}
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.26.65.131 
cluster-announce-port ${port}
cluster-announce-bus-port 1${port}
appendonly yes
EOF
docker run -p ${port}:${port} -p 1${port}:1${port} --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \
done
docker stop $(docker ps -a |grep redis-700 | awk '{ print $1}')
docker rm $(docker ps -a |grep redis-700 | awk '{ print $1}')

##建立集群
docker exec -it redis-7001 /bin/sh
redis-cli --cluster create 172.26.65.131:7001 172.26.65.131:7002 172.26.65.131:7003 172.26.65.131:7004 172.26.65.131:7005 172.26.65.131:7006 --cluster-replicas 1

redis-cli -c -h 192.168.56.10 -p 7006  # -c方式连接
cluster info ,cluster node;测试
#模拟网络分离的
docker network create redis --subnet 172.38.0.0/16
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done



docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 192.168.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \


docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /mydata/redis/node-1/data:/data \
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf


docker run -p 6376:6379 -p 16376:16379 --name redis-6 \
-v /mydata/redis/node-6/data:/data \
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf

docker exec -it redis-1 /bin/sh
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1

安装ElasticSearch

mkdir -p /idata/es/config
mkdir -p /idata/es/data
echo "http.host: 0.0.0.0" >> /idata/es/config/elasticsearch.yml
chmod -R 777 /idata/es/ #保证权限
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /idata/es/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /idata/es/data:/usr/share/elasticsearch/data \
-v /idata/es/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.0.10:9200 -p 5601:5601 \
-d kibana:7.4.2
#安装Es集群
所有之前先运行:sysctl -w vm.max_map_count=262144
我们只是测试,所以临时修改。永久修改使用下面
#防止 JVM 报错
echo vm.max_map_count=262144 >> /etc/sysctl.conf
sysctl -p

##############3-master##########################
for port in $(seq 1 3); \
do \
mkdir -p /mydata/elasticsearch/master-${port}/config
mkdir -p /mydata/elasticsearch/master-${port}/data
chmod -R 777 /mydata/elasticsearch/master-${port}
cat << EOF >/mydata/elasticsearch/master-${port}/config/elasticsearch.yml
cluster.name: my-es #集群的名称,同一个集群该值必须设置成相同的
node.name: es-master-${port} #该节点的名字
node.master: true #该节点有机会成为 master 节点
node.data: false #该节点可以存储数据
network.host: 0.0.0.0
http.host: 0.0.0.0 #所有 http 均可访问
http.port: 920${port}
transport.tcp.port: 930${port}
discovery.zen.ping_timeout: 10s #设置集群中自动发现其他节点时 ping 连接的超时时间
discovery.seed_hosts: ["172.18.12.21:9301", "172.18.12.22:9302", "172.18.12.23:9303"] #设置集群中的 Master 节点的初始列表,可以通过这些节点来自动发现其他新加入集群的节点,es7的新增配置;
cluster.initial_master_nodes: ["172.18.12.21"] #新集群初始时的候选主节点,es7 的新增配置
EOF
docker run --name elasticsearch-node-${port} \ -p 920${port}:920${port} -p 930${port}:930${port} \ --network=mynet --ip 172.18.12.2${port} \
-e ES_JAVA_OPTS="-Xms300m -Xmx300m" \ -v
/mydata/elasticsearch/master-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/el
asticsearch.yml \ -v /mydata/elasticsearch/master-${port}/data:/usr/share/elasticsearch/data \ -v /mydata/elasticsearch/master-${port}/plugins:/usr/share/elasticsearch/plugins \ -d elasticsearch:7.4.2
done




#######################3-node#######################
for port in $(seq 4 6); \
do \
mkdir -p /mydata/elasticsearch/node-${port}/config
mkdir -p /mydata/elasticsearch/node-${port}/data
chmod -R 777 /mydata/elasticsearch/node-${port}
cat << EOF >/mydata/elasticsearch/node-${port}/config/elasticsearch.yml
cluster.name: my-es #集群的名称,同一个集群该值必须设置成相同的
node.name: es-node-${port} #该节点的名字
node.master: false #该节点有机会成为 master 节点
node.data: true #该节点可以存储数据
network.host: 0.0.0.0
#network.publish_host: 192.168.56.10 #互相通信 ip,要设置为本机可被外界访问的 ip,否则
无法通信
http.host: 0.0.0.0 #所有 http 均可访问
http.port: 920${port}
transport.tcp.port: 930${port}
#discovery.zen.minimum_master_nodes: 2 #设置这个参数来保证集群中的节点可以知道其
它 N 个有 master 资格的节点。官方推荐(N/2)+1
discovery.zen.ping_timeout: 10s #设置集群中自动发现其他节点时 ping 连接的超时时间
discovery.seed_hosts: ["172.18.12.21:9301", "172.18.12.22:9302", "172.18.12.23:9303"] #设置集群中的 Master 节点的初始列表,可以通过这些节点来自动发现其他新加入集群的节点,es7
的新增配置
cluster.initial_master_nodes: ["172.18.12.21"] #新集群初始时的候选主节点,es7 的新增配置
EOF
docker run --name elasticsearch-node-${port} \ -p 920${port}:920${port} -p 930${port}:930${port} \ --network=mynet --ip 172.18.12.2${port} \ -e ES_JAVA_OPTS="-Xms300m -Xmx300m" \ -v
/mydata/elasticsearch/node-${port}/config/elasticsearch.yml:/usr/share/elasticsearch/config/ela
sticsearch.yml \ -v /mydata/elasticsearch/node-${port}/data:/usr/share/elasticsearch/data \ -v /mydata/elasticsearch/node-${port}/plugins:/usr/share/elasticsearch/plugins \ -d elasticsearch:7.4.2
done

##检查
_nodes/process?pretty 查看节点状况
_cluster/stats?pretty 查看集群状态
_cluster/health?pretty 查看集群健康状况
_cat/nodes 查看各个节点信息
- discovery.seed_hosts=es01,es03
- cluster.initial_master_nodes=es01,es02,es03
#还可以只开放一个实例端口.其他的都用集群内访问就可以啦。
#也可以使用自定义Docker网络的方式
docker network create --driver bridge --subnet 172.21.0.0/16 --gateway 172.21.0.1 mynet
#运行容器的时候,使用 --ip --network指定网络

安装Nginx

  • 随便启动一个 nginx 实例, 只是为了复制出配置

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

  • 将容器内的配置文件拷贝到当前目录:

    • mkdir -p /idata/nginx/conf
    • docker container cp nginx:/etc/nginx/. /idata/nginx/conf
  • 创建新的 nginx; 执行以下命令
    docker run -p 80:80 --name nginx \
    -v /idata/nginx/html:/usr/share/nginx/html \
    -v /idata/nginx/logs:/var/log/nginx \
    -v /idata/nginx/conf:/etc/nginx \
    -d nginx:1.10
  • 给 nginx 的 html 下面放的所有资源可以直接访问;

安装RabbitMQ

#简单安装
docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p
25672:25672 -p 15671:15671 -p 15672:15672 -v /idata/rabbitmq:/var/lib/rabbitmq rabbitmq:management

4369, 25672 (Erlang发现&集群端口)
5672, 5671 (AMQP端口)
15672 (web管理后台端口)
61613, 61614 (STOMP协议端口)
1883, 8883 (MQTT协议端口)
https://www.rabbitmq.com/networking.html
#安装RabbitMQ镜像集群
#三台机器分别运行
mkdir -p /idata/rabbitmq
vi /idata/rabbitmq/hosts
192.168.0.48 rabbit1 rabbit1
192.168.0.47 rabbit2 rabbit2
192.168.0.46 rabbit3 rabbit3

#注意修改每个-h 参数,修改为正确的hostname
docker run -d --name rabbitmq -h rabbit1 -v /idata/rabbitmq:/var/lib/rabbitmq -v /idata/rabbitmq/hosts:/etc/hosts -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 -e RABBITMQ_ERLANG_COOKIE='icodingedu' rabbitmq:management
#上面这种方式匹配主机名适用于完全分布式情况,单机模拟集群,大家觉得还有更好的方式吗?(请使用自定义网络的方式)

#RABBITMQ_ERLANG_COOKIE 节点认证作用,部署集成时 需要同步该值

#节点加入集群
#初始化第一个节点
docker exec -it rabbitmq /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
Exit

#设置集群内的其他节点
docker exec -it rabbitmq /bin/bash
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbit1 #master的ip
rabbitmqctl start_app
exit

#实现镜像集群
#进入主节点进行操作
docker exec -it rabbitmq bash
rabbitmqctl set_policy -p / ha "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'

可以使用 rabbitmqctl list_policies -p /;查看vhost/下面的所有policy
在cluster中任意节点启用策略,策略会自动同步到集群节点
rabbitmqctl set_policy -p / ha-all "^" '{"ha-mode":"all"}'
策略模式 all 即复制到所有节点,包含新增节点,策略正则表达式为 “^” 表示所有匹配所有队列名称。“^hello”表示只匹配名为hello开始的队列

端口映射

docker create -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --name hello-mysql mysql:5.7

-p HostPort:ContainerPort | IP:HostPort:ContainerPort | HostPort:ContainerPort

  • 查看端口映射

    • docker port hello-mysql

容器互联

--link name:alias,name连接容器的名称,alias连接的别名

场景:我们无需暴露mysql的情况下,让web应用使用mysql;

docker run -d -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
docker run -d --link mysql01:mysql --name tomcat tomcat:7

docker exec -it tomcat bash
cat /etc/hosts

使用Docker过程中,往往需要对数据进行持久化,或者多个容器之间进行数据共享,这就需要我们熟悉容器的数据管理操作

容器中管理数据主要的两种方式:

  • 数据卷(Data Volumes):容器内数据直接映射到本地主机环境
  • 数据卷容器(Data Volumes Containers):使用特定容器维护数据

数据卷

将主机操作系统目录直接映射进容器,类似于Linux中的mount。数据卷很好的解耦了容器应用和数据。对数据卷内数据的操作(无论是在本地主机还是容器内),都会立即生效。

docker run -d -p 3307:3306 -v /my/custom:/etc/mysql/conf.d \
-v /my/own/datadir:/var/lib/mysql --restart always --name mysql01 mysql:5.7

-v hostDir:containerDir:hostDir不存在会自动创建,默认权限是rw,也可以改为ro,这样容器内部就无法对数据卷的数据进行修改了。

docker run -d -p 3307:3306 -v /my/custom:/etc/mysql/conf.d:ro \
-v /my/own/datadir:/var/lib/mysql --restart always --name mysql01 mysql:5.7
#数据卷创建
docker volume create [OPTIONS] [VOLUME]
docker volume create hello
docker run -d -p 3307:3306 -v hello:/etc/mysql/conf.d \
-v /my/own/datadir:/var/lib/mysql --restart always --name mysql01 mysql:5.7

#数据卷列表
docker volume ls

#数据卷详情
docker volume inspect hello
[
    {
        "CreatedAt": "2020-04-08T06:43:08Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/hello/_data",
        "Name": "hello",
        "Options": {},
        "Scope": "local"
    }
]
#docker cp 用于容器与主机之间的数据拷贝
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
  • SRC_PATH 指定为一个文件

    • DEST_PATH 不存在:文件名为 DEST_PATH,内容为SRC的内容
    • DEST_PATH 不存在并且以 / 结尾:报错
    • DEST_PATH 存在并且是文件:目标文件内容被替换为SRC_PATH的文件内容。
    • DEST_PATH 存在并且是目录:文件复制到目录内,文件名为SRC_PATH指定的名字
  • SRC_PATH 指定为一个目录

    • DEST_PATH 不存在:DEST_PATH创建文件夹,复制源文件夹内的所有内容
    • DEST_PATH 存在是文件:报错
    • DEST_PATH 存在是目录

      • SRC_PATH 不以 /. 结束:源文件夹复制到目标里面
      • SRC_PATH/. 结束:源文件夹里面的内容复制到目标里面

      自动创建文件夹不会做递归。把父文件夹做好

数据卷容器

如果需要在多个容器之间共享一些持续更新的数据,最简单的方式是使用数据卷容器。数据卷容器也是一个容器,但他专门用来提供数据卷供其他容器挂载。

docker run -it -v /idata --name idata ubuntu

docker run -it --volumes-from idata --name d01 ubuntu
docker run -it --volumes-from idata --name d02 ubuntu
#使用--volumes-from参数所挂载的数据卷容器本身不需要保持在运行状态

数据卷的用法细节

docker run -d -P --name nginx nginx
#方便修改nginx的配置和html页面等
#容器中 /usr/share/nginx/html
#容器中 /etc/nginx

##参照一下各种写法
# hostPath:containerPath
docker run -d -P --name nginx -v /opt/nginx/html:/usr/share/nginx/html nginx;
    #为什么看到的是403而不是index页面内容。原来容器里面变为空了?主机的内容复制到容器中,导致,容器同步到了主机的空文件夹。
    #特别是配置问津,必须提前保证我们主机挂载的目录里面提前有内容。
docker run -d -P --name nginx -v /opt/nginx:/etc/nginx nginx
    #为什么docker ps看不到;出错了。open() "/etc/nginx/nginx.conf" failed (2: No such file or directory) nginx: [emerg] open() "/etc/nginx/nginx.conf" failed (2: No such file or directory)
#我就想自定义挂载路径,并且把他们挂载出来?
    # 1、复制容器的文件
        docker cp
        1、启动一个nginx,先不挂载。
        2、docker cp把运行中的nginx的内容复制出来
            Usage:    docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
                    docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
        docker cp nginx:/usr/share/nginx/html /opt/hahanginx
        nginx:/usr/share/nginx/html/. 把html里面的内容复制到 /opt/hahanginx
        nginx:/usr/share/nginx/html 把html文件夹复制到 /opt/hahanginx
        
        
        完整过程
        mkdir /opt/nginx
        docker cp nginx:/usr/share/nginx/html /opt/nginx 复制html
        docker cp nginx:/etc/nginx/. /opt/nginx/conf
        docker run -d -P --name nginx -v /opt/nginx/conf:/etc/nginx -v /opt/nginx/html:/usr/share/nginx/html nginx
        
# 匿名卷方式volume,挂载任意目录
docker run -d -P --name nginx02 -v 容器路径
eg:
docker run -d -P --name nginx02 -v /usr/share/nginx/html -v /etc/nginx nginx
想要知道到底迎合到主机的哪里了?
docker inspect nginx02.
"Mounts": [
            {
                "Type": "volume",
                "Name": "9f4f8ac4c9cd9cb77df7a5386a5b68520c98ea1bb28e26b6593255c1d0ff9cdf",
                "Source": "/var/lib/docker/volumes/9f4f8ac4c9cd9cb77df7a5386a5b68520c98ea1bb28e26b6593255c1d0ff9cdf/_data",
                "Destination": "/usr/share/nginx/html",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            },
            {
                "Type": "volume",
                "Name": "68afef2bf4979e2c9369f6eb68f8a94adc61e63d3a1fb3dad7469e243442cf32",
                "Source": "/var/lib/docker/volumes/68afef2bf4979e2c9369f6eb68f8a94adc61e63d3a1fb3dad7469e243442cf32/_data",
                "Destination": "/etc/nginx",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],

#匿名挂载的缺点?不好维护。 docker volume可以维护他们
docker volume ls

#具名挂载。有卷。如果有可视化界面。我们就知道卷是干嘛,就不乱删除了。
#创建好的卷,会自动的和容器中需要挂载的这个文件夹做好同步。
docker volume create 卷名字, 创建一个卷。/var/lib/docker/volumes/自定义的卷名/_data
docker run -d -P --name nginx03 -v nginxconf:/etc/nginx -v nginxhtml:/usr/share/nginx/html nginx
最佳实战。

怎么判断挂载的是卷名而不是本机目录名
  不以/开始就是卷名,以/开始就是主机名。 
 
#改变文件的读写权限
#ro:readonly rw:readwrite; 指定容器对我们这个挂载出来的内容的读写权限
docker run -d -P --name nginx04 -v nginx04conf:/etc/nginx:ro -v nginx04html:/usr/share/nginx/html:rw nginx
ro:效果
root@c721b01b2752:/etc/nginx# echo 123 > nginx.conf
bash: nginx.conf: Read-only file system
容器内不能再对ro指定的所有进行修改了。