docker容器部署+蓝绿部署+mysql双主同步+Jenkins自动化部署

768

docker笔记

服务环境 : centos7

1. docker部署步骤

如果不是root用户或者没有root权限,请用sudo提权操作

  • 确保 yum 包更新到最新。

      yum update
    
  • 查看本机是否存在旧版本docker

      docker version
    
  • (如果包含旧版本docker)执行如下语句

      yum remove docker  docker-common docker-selinux docker-engine
    
  • 安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的

      yum install -y yum-utils device-mapper-persistent-data lvm2
    
  • 设置yum源

      yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
  • 查看仓库中的docker版本

      yum list docker-ce --showduplicates | sort -r
    
  • 安装docker

      yum install docker-ce  //默认最新稳定版本
      //或者安装特定版本
      yum install docker-ce-18.09.6.ce
    
  • 启动docker,并且设置开机启动

      systemctl start docker
      systemctl enable docker
    
  • 验证是否安装成功

      docker version
    
    
  • 查看日志

      docker logs -f tomcat7
    
    
2. tomcat基于docker容器中部署

docker容器上部署tomcat不需要jdk环境,所以对jdk没有要求 tomcat7默认环境为jre7 8为jre8

概念: 宿主机也就是服务器 docker为容器 tomcat为docker镜像

  • 查看关于tomcat的镜像

      docker search tomcat
    
    
  • 拉取tomcat的镜像(ps: tomcat:7就是tomcat7 tomcat:8就是tomcat8以此类推)

      docker image pull tomcat:7
    
    
  • 拉取完成后查看本地已经拉取的镜像列表

      docker image ls
    
    

    docker容器部署只需要两样,一是docker容器,二是镜像,镜像不需要再安装,直接启动即可

  • 通过docker 启动tomcat

    • 第一次启动tomcat(主要任务将容器内的文件复制出来)
      docker run -d -p 8085:8080 --name tomcat7 tomcat:7
    
      8085:8080的意思是把容器的8080端口映射到服务器(宿主机)的8085的端口
      --restart=always 指定该容器(也就是tomcat开机启动,前提是你docker开机也启动)
      --name 指定容器的名称,后面可以根据名称停止,启动,删除容器,以及查看日志用的 
      -v 把本机的目录与容器内的目录关联起来,我这里把bin目录(方便修改启动文件),webapps目录(方便部署应用),logs目录(方便查看日志),conf目录(方便修改配置文件)都映射出来了 
      -d让容器在后台启动,如果不加这个参数,容器启动就跟你平时执行catalina.sh run的效果一样,ctrl+c的话tomcat也就停止了 
      -p 把容器里面监听的端口映射到本机的端口,我这里把8080映射到了本机的8081
    
    
    • 随后将容器中的conf,log,webapps文件copy出来(前提创建好home/docker-tomcat这个文件夹)

       #b7b231a3476a为拿到的短id(通过docker ps来查看id
       docker cp b7b231a3476a:/usr/local/tomcat/conf /home/docker-tomcat
      
       docker cp b7b231a3476a:/usr/local/tomcat/logs /home/docker-tomcat
      
       docker cp b7b231a3476a:/usr/local/tomcat/webapps /home/docker-tomcat
      
    • 随后停止并且删除该容器

       docker stop tomcat7(容器名)
      
      
       docker rm tomcat7(容器名)
      
      
    • 重新启动容器,进行了映射文件到宿主机操作(此操作是方便以后发布war包以及tomcat配置和日志查询)

      docker run -d -p 8085:8080 --name tomcat7 --restart=always -v /home/docker-tomcat/conf:/usr/local/tomcat/conf -v /home/docker-tomcat/logs:/usr/local/tomcat/logs -v /home/docker-tomcat/webapps:/usr/local/tomcat/webapps tomcat:7
      
      

      映射对应,可以自己自定义其他目录 我用的目录为/home/docker-tomcat

      -v /home/docker-tomcat/conf:/usr/local/tomcat/conf

      -v /home/docker-tomcat/logs:/usr/local/tomcat/logs

      -v /home/docker-tomcat/webapps:/usr/local/tomcat/webapps

  • 查看正在运行的tomcat

      docker ps
    
    
  • 查看容器中正在运行的tomcat日志

      docker logs <容器名称或者容器短id(CONTAINER ID)>
    
    
  • 重启正在运行的tomcat

      docker restart <容器名称或者容器短id(CONTAINER ID)>
    
    
  • 停止正在运行的tomcat

    • 第一种没有加-d参数启动的

      • ctrl+c或者关闭当前连接工具就可以停止运行tomcat
    • 第二种加入-d启动参数的(后台运行 推荐使用)

        docker stop <容器名称或者容器短id(CONTAINER ID)>
      
      
  • 其他设置(时区)

      echo "Asia/Shanghai" > /etc/timezone
    
    
3. nginx基于docker容器中部署
  • 拉取nginx镜像

      docker pull nginx
    
    
  • 第一次运行(copy配置文件到宿主机,方便映射)

      docker run -d -p 80:80 --name mynginx nginx
    
    
  • 复制文件至/home/docker-nginx/下

      docker cp <容器名称或者容器短id>:/etc/nginx/conf.d /home/docker-nginx/
      docker cp <容器名称或者容器短id>:/etc/nginx/nginx.conf /home/docker-nginx/
    
    
  • 关闭并且移除当前nginx容器

      docker stop mynginx(容器名称或者容器短id)
      docker rm -f mynginx
    
    
  • 第二次运行(映射配置文件到宿主机)

      docker run -d -p 80:80 --name mynginx -v /home/docker-nginx/conf.d:/etc/nginx/conf.d -v /home/docker-nginx/nginx.conf:/etc/nginx/nginx.conf nginx 
    
    
    • /etc/nginx/conf.d 映射到 /home/docker-nginx/conf.d

      /etc/nginx/nginx.conf nginx 映射到 /home/docker-nginx/nginx.conf

  • 相关配置(此处nginx只作负载均衡,nginx也可以当做其他用处,比如静态资源处理)

    • 均衡策略分为六种

      • 轮询默认方式
        weight权重方式
        ip_hash根据ip地址的hash分配
        least_conn最少连接方式
        fair(第三方)响应时间方式
        url_hash(第三方)依据URL分配方式
    • ####/etc/nginx/nginx.conf下的http里面
      upstream myweb {
      	  #均衡策略
            #none 轮询(权重由weight决定)
            #ip_hash   一个ip只会访问一个服务器
            #fair
            #url_hash
            server 192.168.31.158:8085;
            server 192.168.31.158:8086;
      }
      ####/etc/nginx/conf.d/default.conf
       server {
        listen       80;
        server_name  localhost;
        #省略其他配置
        location / {
           #root   /usr/share/nginx/html;
           #index  index.html index.htm;
           proxy_pass http://myweb;    //与上面upstream后面的名称对应
           # 设置主机真实host,以及真实ip地址
           proxy_set_header 	Host 			 $host;
           proxy_set_header   X-Real-IP        $remote_addr; 
           proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        }
      }
      
      
4. mysql基于docker容器中部署
  • 拉取mysql 5.7的镜像 (mysql版本不要用最新的,新版本的mysql驱动有变化)

      docker pull mysql:5.7
    
    
  • 创建文件

      mkdir -p /home/docker-mysql/data /home/docker-mysql/logs /home/docker-mysql/conf
    
    
  • 在conf下创建自定义配置文件my.conf

      [client]
      port = 3306
      default-character-set = utf8mb4
    
      [mysql]
      port = 3306
      default-character-set = utf8mb4
    
      [mysqld]
      ##########################
      # summary
      ##########################
      #bind-address = 0.0.0.0
      #port = 3306
      #datadir=/datavol/mysql/data #数据存储目录
    
      ##########################
      # log bin
      ##########################
      server-id = 300			#必须唯一
      log_bin = mysql-bin 	#开启及设置二进制日志文件名称
      binlog_format = MIXED
      sync_binlog = 1
      expire_logs_days =7		#二进制日志自动删除/过期的天数。默认值为0,表示不自动删除。
    
      #binlog_cache_size = 128m
      #max_binlog_cache_size = 512m
      #max_binlog_size = 256M
    
      binlog-do-db = test	#要同步的数据库
    
      binlog-ignore-db = mysql 	#不需要同步的数据库 
      binlog_ignore_db = information_schema
      binlog_ignore_db = performation_schema
      binlog_ignore_db = sys
    
      #########################
      #防止id重复
      #########################
      auto_increment_increment= 2		#设定为主服务器的数量,防止auto_increment字段重复
      auto_increment_offset	= 2		#自增长字段的初始值,在多台master环境下,不会出现自增长ID重复
      ##########################
      # character set
      ##########################
      character-set-server = utf8mb4
      collation-server = utf8mb4_unicode_ci
    
    
  • 启动(映射路径)

      docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=1234 --name mysql -v /home/docker-mysql/conf:/etc/mysql/conf.d -v /home/docker-mysql/data:/var/lib/mysql -v /home/docker-mysql/logs:/logs  mysql:5.7
      ##-e MYSQL_ROOT_PASSWORD=1234为设置密码参数(必须得有此参数,否则启动失败)
    
    
  • 其他命令

    • 进入mysql容器登陆

      • docker exec -it <容器名或者id> /bin/bash
        mysql -u root -p
        
        
5. redis基于docker容器部署
  • 拉取redis 3.2的镜像

      docker pull redis:3.2
    
    
  • 创建目录方便映射

      mkdir -p /home/docker-redis/data
    
    
  • 启动redis

      docker run -d -p 6379:6379 --name redis -v /home/docker-redis/data:/data  redis:3.2 redis-server --appendonly yes
      ##redis-server --appendonly yes开启持久化配置
    
    
6. 蓝绿部署架构
graph TD
	A[docker nginx服务器 80]
	A-->B[docker tomcat1 8085]
	A-->C[docker tomcat2 8086]
	B-->D[docker redis 6379] 
	C-->D
	B-->E[docker mysql 3306]
	C-->E
  • nginx负责负载均衡以及反向代理

  • tomcat为集群可以横向拓展

  • redis为tomcat集群做session共享

      session共享相关配置
      ##pom.xml
      <dependency>
      	<groupId>org.springframework.session</groupId>
      	<artifactId>spring-session</artifactId>
      	<version>1.3.0.RELEASE</version>
      </dependency>
      ##web.xml
      <!-- 过滤器配置 -->
      <filter>
        <filter-name>springSessionRepositoryFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
      </filter>
      <filter-mapping>
        <filter-name>springSessionRepositoryFilter</filter-name>
        <url-pattern>*.do</url-pattern>
      </filter-mapping>
      ###spring bean xml配置 也可以改为javaconfig(无xml)方法进行注入
      <bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
      	<property name="maxInactiveIntervalInSeconds" value="120" />
      </bean>
    
    
  • 蓝绿部署优点

    • 当系统升级的时候,一个tomcat停止运行,nginx自动会将流量导向其他服务器,集群依次进行升级,是一种可以保证系统在不间断提供服务的情况下上线的部署方式。
  • 部署步骤

    • 先更新一个tomcat容器,确认无误后,再依次更新其他tomcat容器
7. 数据库双主同步
  • 需要准备两个配置文件my.cnf 路径为映射的容器中的/etc/mysql/conf.d文件夹下

      ##主库一配置文件id为300 
      [client]
      port = 3306
      default-character-set = utf8mb4
    
      [mysql]
      port = 3306
      default-character-set = utf8mb4
    
      [mysqld]
      ##########################
      # summary
      ##########################
      #bind-address = 0.0.0.0
      #port = 3306
      #datadir=/datavol/mysql/data #数据存储目录
    
      ##########################
      # log bin
      ##########################
      server-id = 300			#必须唯一
      log_bin = mysql-bin 	#开启及设置二进制日志文件名称
      binlog_format = MIXED
      sync_binlog = 1
      log_slave_updates       #把从库的写操作记录到binlog中 (缺少之后,双主创建失败)
      expire_logs_days =7		#二进制日志自动删除/过期的天数。默认值为0,表示不自动删除。
    
      #binlog_cache_size = 128m
      #max_binlog_cache_size = 512m
      #max_binlog_size = 256M
    
      binlog-do-db = test	#要同步的数据库
    
      binlog-ignore-db = mysql 	#不需要同步的数据库 
      binlog_ignore_db = information_schema
      binlog_ignore_db = performation_schema
      binlog_ignore_db = sys
    
      #########################
      #防止id重复
      #########################
      auto_increment_increment= 2		#设定为主服务器的数量,防止auto_increment字段重复
      auto_increment_offset	= 1		#自增长字段的初始值,在多台master环境下,不会出现自增长ID重复
      ##########################
      # character set
      ##########################
      character-set-server = utf8mb4
      collation-server = utf8mb4_unicode_ci
    
    
      ##主库二配置文件id为400 
      [client]
      port = 3306
      default-character-set = utf8mb4
    
      [mysql]
        port = 3306
        default-character-set = utf8mb4
    
      [mysqld]
        ##########################
    
      # summary
    
        ##########################
        #bind-address = 0.0.0.0
        #port = 3306
        #datadir=/datavol/mysql/data #数据存储目录
    
      ##########################
    
      # log bin
    
        ##########################
        server-id = 400		#必须唯一
        log_bin = mysql-bin 	#开启及设置二进制日志文件名称
        binlog_format = MIXED
        sync_binlog = 1
        log_slave_updates #把从库的写操作记录到binlog中 (缺少之后,双主创建失败)
        expire_logs_days =7 #二进制日志自动删除/过期的天数。默认值为0,表示不自动删除。
    
      #binlog_cache_size = 128m
        #max_binlog_cache_size = 512m
        #max_binlog_size = 256M
    
      binlog-do-db = test	#要同步的数据库
    
      binlog-ignore-db = mysql 	#不需要同步的数据库 
        binlog_ignore_db = information_schema
        binlog_ignore_db = performation_schema
        binlog_ignore_db = sys
    
      #########################
        #防止id重复
        #########################
        auto_increment_increment= 2		#设定为主服务器的数量,防止auto_increment字段重复
        auto_increment_offset	= 2		#自增长字段的初始值,在多台master环境下,不会出现自增长ID重复
        ##########################
    
      # character set
    
        ##########################
        character-set-server = utf8mb4
        collation-server = utf8mb4_unicode_ci
    
    
    
  • 创建同步账号

进入容器内,并且登陆mysql
// 主库服务器一
GRANT REPLICATION SLAVE ON *.* TO 'admin'@'192.168.31.158' IDENTIFIED BY '1234';
FLUSH PRIVILEGES;
// 主库服务器二
GRANT REPLICATION SLAVE ON *.* TO 'admin'@'192.168.31.186' IDENTIFIED BY '1234';
  FLUSH PRIVILEGES;

  • 查看服务器master的状态

      show master status;
      ##数据库主库一
      mysql> show master status;
      +------------------+----------+--------------+--------------------------------------------------+-------------------+
      | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB                                 | Executed_Gtid_Set |
      +------------------+----------+--------------+--------------------------------------------------+-------------------+
      | mysql-bin.000002 |     1066 | test         | mysql,information_schema,performation_schema,sys |                   |
      +------------------+----------+--------------+--------------------------------------------------+-------------------+
      1 row in set (0.00 sec)
      ##数据库主库二
      mysql> show master status;
      +------------------+----------+--------------+--------------------------------------------------+-------------------+
      | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB                                 | Executed_Gtid_Set |
      +------------------+----------+--------------+--------------------------------------------------+-------------------+
      | mysql-bin.000006 |      154 | test         | mysql,information_schema,performation_schema,sys |                   |
      +------------------+----------+--------------+--------------------------------------------------+-------------------+
      1 row in set (0.00 sec)
    
    
    
  • 设置同步

      ##主库一的设置
      mysql > CHANGE MASTER TO 
      MASTER_HOST='192.168.31.158',MASTER_PORT=3306,MASTER_USER='admin',MASTER_PASSWORD='1234',MASTER_LOG_FILE='mysql-bin.000007',MASTER_LOG_POS=476;
    
      START SLAVE;
    
      ##主库二的设置
      mysql > CHANGE MASTER TO MASTER_HOST='192.168.31.186',MASTER_PORT=3306,MASTER_USER='admin',MASTER_PASSWORD='1234',MASTER_LOG_FILE='mysql-bin.000003',MASTER_LOG_POS=474;
    
    
      START SLAVE;
    
    
    
    • 检查是否同步

    • mysql >  SHOW SLAVE STATUS\G
      ##查看着两个属性后面是否都是yes
       Slave_IO_Running: Yes
       Slave_SQL_Running: Yes
      
      
    • 分布式主键生成策略

      • snowflake (推特开源算法)

      • /**
         * Twitter_Snowflake<br>
         * SnowFlake的结构如下(每部分用-分开):<br>
         * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
         * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
         * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
         * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。
         * 41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
         * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
         * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
         * 加起来刚好64位,为一个Long型。<br>
         * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,
         * 经测试,SnowFlake每秒能够产生26万ID左右。
         */
        public class SnowflakeIdUtil
        {
        	// ==============================Fields===========================================
        	/** 开始时间截 (2015-01-01) */
        	private final long twepoch = 1420041600000L;
        
        	/** 机器id所占的位数 */
        	private final long workerIdBits = 5L;
        
        	/** 数据标识id所占的位数 */
        	private final long datacenterIdBits = 5L;
        
        	/** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
        	private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
        
        	/** 支持的最大数据标识id,结果是31 */
        	private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
        
        	/** 序列在id中占的位数 */
        	private final long sequenceBits = 12L;
        
        	/** 机器ID向左移12位 */
        	private final long workerIdShift = sequenceBits;
        
        	/** 数据标识id向左移17位(12+5) */
        	private final long datacenterIdShift = sequenceBits + workerIdBits;
        
        	/** 时间截向左移22位(5+5+12) */
        	private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
        
        	/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
        	private final long sequenceMask = -1L ^ (-1L << sequenceBits);
        
        	/** 工作机器ID(0~31) */
        	private long workerId;
        
        	/** 数据中心ID(0~31) */
        	private long datacenterId;
        
        	/** 毫秒内序列(0~4095) */
        	private long sequence = 0L;
        
        	/** 上次生成ID的时间截 */
        	private long lastTimestamp = -1L;
        	//单例对象
        	private static volatile SnowflakeIdUtil instance = null;
        
        	//==============================Constructors=====================================
        	/**
        	 * 构造函数
        	 * @param workerId 工作ID (0~31)
        	 * @param datacenterId 数据中心ID (0~31)
        	 */
        	public SnowflakeIdUtil(long workerId, long datacenterId) {
        		if (workerId > maxWorkerId || workerId < 0) {
        			throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        		}
        		if (datacenterId > maxDatacenterId || datacenterId < 0) {
        			throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        		}
        		this.workerId = workerId;
        		this.datacenterId = datacenterId;
        	}
        
        	/*
        	* 单例模式获取对象
        	* */
        
        	public static SnowflakeIdUtil getInstance() {
        		if (instance == null) {
        			synchronized(SnowflakeIdUtil.class) {
        				if (instance == null) {
        					instance = new SnowflakeIdUtil(0,0);
        				}
        			}
        		}
        		return instance;
        	}
        
        	// ==============================Methods==========================================
        	/**
        	 * 获得下一个ID (该方法是线程安全的)
        	 * @return SnowflakeId
        	 */
        	public synchronized long nextId() {
        		long timestamp = timeGen();
        
        		//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        		if (timestamp < lastTimestamp) {
        			long offset = lastTimestamp - timestamp;
        			if (offset <= 5) {
        				try {
        					//时间偏差大小小于5ms,则等待两倍时间
        					wait(offset << 1);//wait
        					timestamp = timeGen();
        					if (timestamp < lastTimestamp) {
        						//还是小于,抛异常并上报
        						throw new RuntimeException("时钟回拨");
        					}
        				} catch (InterruptedException e) {
        					throw new RuntimeException("时钟回拨");
        				}
        			} else {
        				throw new RuntimeException("时钟回拨");
        			}
        		}
        
        		//如果是同一时间生成的,则进行毫秒内序列
        		if (lastTimestamp == timestamp) {
        			sequence = (sequence + 1) & sequenceMask;
        			//毫秒内序列溢出
        			if (sequence == 0) {
        				//阻塞到下一个毫秒,获得新的时间戳
        				timestamp = tilNextMillis(lastTimestamp);
        			}
        		}
        		//时间戳改变,毫秒内序列重置
        		else {
        			sequence = 0L;
        		}
        
        		//上次生成ID的时间截
        		lastTimestamp = timestamp;
        
        		//移位并通过或运算拼到一起组成64位的ID
        		return ((timestamp - twepoch) << timestampLeftShift) //
        				| (datacenterId << datacenterIdShift) //
        				| (workerId << workerIdShift) //
        				| sequence;
        	}
        
        	/**
        	 * 阻塞到下一个毫秒,直到获得新的时间戳
        	 * @param lastTimestamp 上次生成ID的时间截
        	 * @return 当前时间戳
        	 */
        	protected long tilNextMillis(long lastTimestamp) {
        		long timestamp = timeGen();
        		while (timestamp <= lastTimestamp) {
        			timestamp = timeGen();
        		}
        		return timestamp;
        	}
        
        	/**
        	 * 返回以毫秒为单位的当前时间
        	 * @return 当前时间(毫秒)
        	 */
        	protected long timeGen() {
        		return System.currentTimeMillis();
        	}
        
        	//==============================Test=============================================
        	/** 测试 */
        	public static void main(String[] args) {
        		//通过单例获取对象
        		SnowflakeIdUtil snowflakeId = SnowflakeIdUtil.getInstance();
        		for (int i = 0; i < 1000; i++) {
        			long id = snowflakeId.nextId();
        //			System.out.println(Long.toBinaryString(id));
        			System.out.println(id);
        		}
        	}
        }
        
        
        
8. 自动化部署-jenkins在docker下部署
  • 拉取镜像

      docker pull jenkins/jenkins:lts
    
    
  • 创建映射文件夹

     mkdir -p /home/docker-jenkins
    
    
  • 运行

      docker run -d -v /home/docker-jenkins:/var/jenkins_home -p 8080:8080 -p 50000:50000 --name jenkins -u root jenkins/jenkins
    
    
  • 时区问题修改

      ##进入web控制台输入 
      ##进入方式 系统管理>脚本命令行
      System.setProperty('org.apache.commons.jelly.tags.fmt.timeZone', 'Asia/Shanghai')