如何把Angular 2部署到Apache服务器

问题

我想把Angular 2应用部署到Apache服务器。已经按照网上的几篇教程部署,都不成功。服务器上安装了npm和ng。
在nutshell中,做了如下操作:
1. 完整克隆整个Angular项目到服务器上
2. 使用npm install安装依赖
3. 执行ng build –prod命令,生成了dist目录
4. 更改apache根目录到/var/www/html/dist目录
5. 启用mod_rewrite,重启apache并在dist目录添加.htaccess文件,内容如下:

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.html [L]
</IfModule>

不过仅是主页domain.com正常,其它页面像domain.com/login,domain.com/register等抛出了404错误。甚至是domain.com/index.html/login也是一样。
这个应用是我的本地系统使用ng serve时是正常的。这是哪里错了?

最佳答案

在/etc/apache2/sites-enabled/000-default.conf添加如下内容并重启apache

<Directory "/var/www/html/dist">
  AllowOverride All
</Directory>

Zookeeper安装部署(单点/集群)

Zookeeper 是个分布式开源框架,之前在做分布式日志收集的时候,就使用到,Zookeeper搭建比较简单。

下载

目前最新稳定版本:3.4.10, 下载地址https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/

进入 stable 目录下载:

未分类

下载后解压:

[root@1c271ed316ca ~]#  tar zxvf zookeeper-3.4.10.tar.gz 

单点模式启动

使用默认配置,启动监听端口:2181

[root@1c271ed316ca ~]#  cd zookeeper-3.4.10
[root@1c271ed316ca zookeeper-3.4.10 ]#  cp conf/zoo_sample.cfg conf/zoo.cfg
[root@1c271ed316ca zookeeper-3.4.10 ]#  bin/zkServer.sh start

默认情况下,zkServer.sh 加载 ../conf/zoo.cfg 配置文件,配置文件主要有下面内容:

可以修改 dataDir 的目录,Zookeeper暴露给客户端的端口。

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/tmp/zookeeper
# the port at which the clients will connect
clientPort=2181

集群模式

步骤1:配置文件增加节点信息

分别在不同的节点A、B、C机器上下载Zookeeper,并且解压,在默认配置文件基础上,添加下面的节点信息:

server.1=xx.xx.xx.xx:2888:3888 # A节点IP
server.2=xx.xx.xx.xx:2888:3888 # B节点IP
server.2=xx.xx.xx.xx:2888:3888 # C节点IP

说明:

  • 2888 端口: Zookeeper 各个节点之间的通信端口;
  • 3888端口: Zookeeper 选择Leader的端口;
  • 这些端口可以自行修改,需要指定好 当前节点的ID(myid)即可,Zookeeper在启动服务后,会开启当前节点的端口配置;

步骤2:配置当前节点的ID

[root@1c271ed316ca ~]# echo "X"  > /tmp/zookeeper/myid

注意:

  • “X” 改为当前节点的编号,对应于配置文件中 server.X ;
  • /tmp/zookeeper 改为实际 Zookeeper 配置文件中的 dataDir路径;

Zookeeper 服务启动的时候,会在 Zookeeper DataDir 目录查找 myid文件,里面的数字即表示当前的节点是 server.X.

步骤3:各个节点分别启动服务

[root@A节点 zookeeper-3.4.10 ]#  bin/zkServer.sh start
[root@B节点 zookeeper-3.4.10 ]#  bin/zkServer.sh start
[root@C节点 zookeeper-3.4.10 ]#  bin/zkServer.sh start

ActiveMQ基于zookeeper的主从(levelDB Master/Slave)搭建

一、说明

ActiveMQ 5.9.0新推出的主从实现,基于zookeeper来选举出一个master,其他节点自动作为slave实时同步消息。因为有实时同步数据的slave的存在,master不用担心数据丢失,所以leveldb会优先采用内存存储消息,异步同步到磁盘,所以该方式的activeMQ读写性能最好因为选举机制要超过半数,所以最少需要3台节点,才能实现高可用。如果集群是两台则master失效后slave会不起作用,所以集群至少三台。此种方式仅实现主备功能,避免单点故障,没有负载均衡功能。

二、环境准备

IP
192.168.3.10    server1
192.168.3.11    server2
192.168.3.12 server3

安装软件信息:

apache-activemq-5.13.0-bin.tar.gz

zookeeper-3.5.2-alpha.tar.gz

ZooInspector.zip

三、搭建Zookeeper集群

1、将zookeeper-3.5.2-alpha.tar.gz文件解压到/home/wzh/zk目录;

2、将zoo_sample.cfg复制一份为 zoo.cfg,并修改其配置信息

wzh@hd-master:~/zk/zookeeper-3.5.2-alpha/conf$ cp zoo_sample.cfg zoo.cfg

wzh@hd-master:~/zk/zookeeper-3.5.2-alpha/conf$vim zoo.cfg
tickTime=2000

initLimit=10

syncLimit=5

dataDir=/tmp/zookeeper

clientPort=2181



server.1=192.168.3.10:2888:3888

server.2=192.168.3.11:2888:3888

server.3=192.168.3.11:2888:3888

3、创建/tmp/zookeeper目录

在该目录下创建名为myid的文件,内容为1(这个值随server而改变)

4、将server1上的/home/wzh/zk/zookeeper-3.5.2-alpha文件夹复制到server2,server3,然后创建/tmp/zookeeper目录

在该目录下创建名为myid的文件,内容为2

5、启动zookeeper

[192.168.3.10]

wzh@hd-master:~/zk/zookeeper-3.5.2-alpha/bin$ ./zkServer.sh start

ZooKeeper JMX enabled by default

Using config: /home/wzh/zk/zookeeper-3.5.2-alpha/bin/../conf/zoo.cfg

Starting zookeeper ... STARTED

[192.168.3.11]

wzh@hd-slave1:~/zk/zookeeper-3.5.2-alpha/bin$ ./zkServer.sh start

ZooKeeper JMX enabled by default

Using config: /home/wzh/zk/zookeeper-3.5.2-alpha/bin/../conf/zoo.cfg

Starting zookeeper ... STARTED

[192.168.3.12]

wzh@hd-slave2:~/zk/zookeeper-3.5.2-alpha/bin$ ./zkServer.sh start

ZooKeeper JMX enabled by default

Using config: /home/wzh/zk/zookeeper-3.5.2-alpha/bin/../conf/zoo.cfg

Starting zookeeper ... STARTED

四、搭建ActiveMQ集群

1、将apache-activemq-5.13.0-bin.tar.gz解压到/home/wzh/amq

2、修改activemq.xml配置文件

  • 将broker节点的brokerName设置为wzhamq
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="wzhamq" dataDirectory="${activemq.data}">
  • 将persistenceAdapter的持久化方式选用replicatedLevelDB,将kahaDB方式注释掉
 <persistenceAdapter>
         <!--
            <kahaDB directory="${activemq.data}/kahadb"/>
         -->
        <replicatedLevelDB 
                directory="${activemq.data}/leveldb" 
                replicas="3" 
                bind="tcp://0.0.0.0:0"        
                zkAddress="192.168.3.10:2181,192.168.3.11:2181"     
                hostname="192.168.3.10"          
                sync="local_disk"          
                zkPath="/activemq/leveldb-stores"/>
        </persistenceAdapter>
  • 将apache-activemq-5.13.复制到11,12机器
wzh@hd-master:~/amq$ scp -r apache-activemq-5.13.0/ [email protected]:/tmp
  • 修改配置文件中的hostname=”192.168.3.11″

  • 修改配置文件中的hostname=”192.168.3.12″

3、启动ActiveMQ

wzh@hd-master:~/amq$ ./apache-activemq-5.13.0/bin/activemq status
INFO: Loading '/home/wzh/amq/apache-activemq-5.13.0//bin/env'
INFO: Using java '/opt/java/jdk1.8.0_91/bin/java'
ActiveMQ is running (pid '2031')
wzh@hd-master:~/amq$

依次启动192.168.3.11,192.168.3.12机器

五、集群管理

1、通过使用ZooInspector工具查看zookeeper集群情况

2、http://192.168.3.10:8161/admin/ 默认用户名与口令为admin登录ActiveMQ管理端

未分类

六、通过Spring-boot操作ActiveMQ JMS

1、通过gradle构建Spring-boot应用,在 gradle文件中增加

dependencies {
    compile('org.springframework.boot:spring-boot-starter-activemq')
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

2、application中增加以下配置

spring.activemq.broker-url=failover:(tcp://192.168.3.10:61616,tcp://192.168.3.11:61616,tcp://192.168.3.12:61616)
spring.activemq.in-memory=true
spring.activemq.pool.enabled=false
spring.activemq.user=admin
spring.activemq.password=admin

3、JMS消息发送

@Service
public class Producer {

    @Autowired
    private JmsMessagingTemplate jmsTemplate;

    public void sendMessage(Destination destination, final String message){
        jmsTemplate.convertAndSend(destination, message);
    }
}

4、JMS消息接收

@Component
public class Consumer {
    @JmsListener(destination = "test.queue")
    public void receiveQueue(String text){

        System.out.println("Consumer收到的报文为:"+text);
    }
}

5、测试

@RestController
@RequestMapping(
        value = "/test",
        headers = "Accept=application/json",
        produces = "application/json;charset=utf-8"
)
public class TestCtrl {
    @Autowired
    Producer producer;

    Destination destination = new ActiveMQQueue("test.queue");

    @RequestMapping(
            value = "/say/{msg}/to/{name}",
            method = RequestMethod.GET
    )
    public Map<String, Object> say(@PathVariable String msg, @PathVariable String name){
        Map<String, Object> map = new HashMap<>();
        map.put("msg", msg);
        map.put("name", name);

        producer.sendMessage(destination, msg);

        return map;
    }
}

6、进入ActiveMQ管理控制台创建一个消息队列

test.queue

7、通过POSTMAN进行测试

2017-08-03 08:10:44.928 INFO 12820 --- [ActiveMQ Task-3] o.a.a.t.failover.FailoverTransport : Successfully reconnected to tcp://192.168.3.10:61616
2017-08-03 08:11:08.854 INFO 12820 --- [ActiveMQ Task-1] o.a.a.t.failover.FailoverTransport : Successfully connected to tcp://192.168.3.10:61616
Consumer收到的报文为:hello
2017-08-03 08:43:39.464 INFO 12820 --- [ActiveMQ Task-1] o.a.a.t.failover.FailoverTransport : Successfully connected to tcp://192.168.3.10:61616
Consumer收到的报文为:hello

8、目前系统连接的是10,如果此时将10集群Down掉,系统会理解选择一台slave作为master提供服务,从而启动案例主备的效果。

ZooKeeper高可用集群的安装及配置

Zookeeper作为很多服务的注册协调中心(dubbo,jstom等),因此高可用集群方案也是必不可少的,Zookeeper集群时要注意将ZK集群的节点数量要为奇数(2n+1:如 3、5、7 个节点)较为合适。

范例项目: http://wosyingjun.iteye.com/blog/2312553

1、下载并上传zookeeper-3.4.6.tar.gz到各个服务器的/usr/local/目录

$ cd /usr/local/
$ wget http://apache.fayea.com/zookeeper/zookeeper-3.4.6/zookeeper-3.4.6.tar.gz

2、在各个服务器上解压zookeeper安装包,并按节点号对zookeeper目录重命名

$ tar -zxvf zookeeper-3.4.6.tar.gz
服务器 1:
$ mv zookeeper-3.4.6 zookeeper-3.4.6_(1)
服务器 2:
$ mv zookeeper-3.4.6 zookeeper-3.4.6_(2)
服务器 3:
$ mv zookeeper-3.4.6 zookeeper-3.4.6_(3)

3、在各zookeeper节点目录下创建以下目录:

$ cd /usr/local/zookeeper-3.4.6_(x)(x代表节点号)
$ mkdir data
$ mkdir logs

4、将 zookeeper/zookeeper-3.4.6_(x)/conf目录下的zoo_sample.cfg文件拷贝一份,命名为zoo.cfg:

$ cp zoo_sample.cfg zoo.cfg

5、修改 zoo.cfg 配置文件

#zookeeper-3.4.6_(1)的配置(/usr/local/zookeeper-3.4.6_(1)/conf/zoo.cfg)如下:
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zookeeper-3.4.6_(1)/data
dataLogDir=/usr/local/zookeeper-3.4.6_(1)/logs
clientPort=2181
server.1=192.168.11.97:2881:3881
server.2=192.168.11.98:2882:3882
server.3=192.168.11.99:2883:3883

#zookeeper-3.4.6_(2)的配置(/usr/local/zookeeper-3.4.6_(2)/conf/zoo.cfg)如下:
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zookeeper-3.4.6_(2)/data
dataLogDir=/usr/local/zookeeper-3.4.6_(2)/logs
clientPort=2182
server.1=192.168.11.97:2881:3881
server.2=192.168.11.98:2882:3882
server.3=192.168.11.99:2883:3883

#zookeeper-3.4.6_(3)的配置(/usr/local/zookeeper-3.4.6_(3)/conf/zoo.cfg)如下:
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zookeeper-3.4.6_(3)/data
dataLogDir=/usr/local/zookeeper-3.4.6_(3)/logs
clientPort=2183
server.1=192.168.11.97:2881:3881
server.2=192.168.11.98:2882:3882
server.3=192.168.11.99:2883:3883

参数说明

  • tickTime=2000
    tickTime这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个tickTime时间就会发送一个心跳。

  • initLimit=10
    initLimit这个配置项是用来配置Zookeeper接受客户端(这里所说的客户端不是用户连接Zookeeper 服务器的客户端,而是Zookeeper服务器集群中连接到Leader的Follower 服务器)初始化连接时最长 能忍受多少个心跳时间间隔数。当已经超过 10 个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 10*2000=20 秒。

  • syncLimit=5
    syncLimit 这个配置项标识Leader与Follower之间发送消息,请求和应答时间长度,最长不能超过多少个tickTime的时间长度,总的时间长度就是5*2000=10秒。

  • dataDir=/usr/local/zookeeper-3.4.6_(x)/data
    dataDir顾名思义就是Zookeeper保存数据的目录,默认情况下Zookeeper将写数据的日志文件也保存在这个目录里。

  • clientPort=2181
    clientPort这个端口就是客户端(应用程序)连接Zookeeper服务器的端口,Zookeeper 会监听这个端 口接受客户端的访问请求。

server.A=B:C:D
server.1=192.168.11.97:2881:3881
server.2=192.168.11.98:2882:3882
server.3=192.168.11.99:2883:3883
  • A 是一个数字,表示这个是第几号服务器;
  • B 是这个服务器的IP地址(或者是与IP地址做了映射的主机名);
  • C 第一个端口用来集群成员的信息交换,表示这个服务器与集群中的 Leader 服务器交换信息的端口;
  • D 是在leader挂掉时专门用来进行选举 leader 所用的端口。

6、在dataDir=/usr/local/zookeeper-3.4.6_(x)/data下创建 myid 文件

$ vi /usr/local/zookeeper-3.4.6(1) /data/myid 设置值为1
$ vi /usr/local/zookeeper-3.4.6(2) /data/myid 设置值为1
$ vi /usr/local/zookeeper-3.4.6_(3) /data/myid 设置值值为3

7、在防火墙中打开要用到的端口218X、288X、388X

$ vi /etc/sysconfig/iptables
-A INPUT -m state --state NEW -m tcp -p tcp --dport 218X -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 288X -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 388X -j ACCEPT
$ service iptables restart

8、启动并查看zookeeper:

$ /usr/local/zookeeper-3.4.6(x)/bin/zkServer.sh start
$ /usr/local/zookeeper-3.4.6(x)/bin/zkServer.sh status

9、连接zookeeper的客户端配置修改:

zookeeper://192.168.11.97:2181?backup=192.168.11.98:2182,192.168.11.99:2183

Zookeeper系统设计的优点

Zookeeper系统应用越来越广泛,在同一领域内开源软件方面基本处于垄断地位。(最近有个etcd借了docker的东方而异军突起)但是实际用过的人都会觉得这个软件属于可用但又不那么好用的类型。本文是本人结合自己的实际使用经验与思考,同时参考真正大牛对这个系统的分析与评价进行的总结,主要还是想归纳一下关于Zookeeper真正的使用需求,并思考这个系统有哪些设计与实现上的优点,从而能获得如此成功。

一、常见应用场景

先归纳一下工程应用中常见的Zookeeper使用场景(以下简称ZK),这里按照个人感觉应用的频率从高到低排序说明。

1、可靠存储在实际使用中可以表现为配置管理、名字服务,这种应用完全是因为ZK多备份的可靠性强。当然也可以利用回调机制在数据变更时可以进行全体通知。实现起来非常简单而且很有效,所以是应用最广的场景。

2、集群管理利用ZK的通讯与回调机制完成分布式集群的机器状态监视,甚至很多系统中做主从备份时都会在ZK中注册以方便做热备切换。

3、服务注册发现管理由可靠存储加上通知回调机制其实满足了服务注册发现的最基本需求,某些在本人看起来不那么靠谱的应用场景,居然也在采用ZK实现。大有一统天下之势,所有类似的需求都开始采用ZK方案,比较出名的系统比如国内的Dubbo和国外的Kafka(居然还把ZK用在了负载均衡上面)、jStorm、Heron(twitter)等等

4、选主服务选主服务是ZK参考的原始系统Chubby设计出来最初的应用需求,当时是满足BigTable的master选主。ZK最初也是用在HBase里面,而后所有需要选主服务的都在采用,很多KV系统用来方便从多节点中选择一个中心节点(但是本人还真没找到什么)。
需要注意的是有时选主服务在讨论里也被称为分布式锁的一种,很容易混淆概念。的确使用ZK来实现选主服务(实现方法最好跟分布式锁的方法完全一样,这里官方文档都曾经犯过错误)客观上遵循了时间优先原则,但是实际需求并非一定要满足这条,只要保证关键的唯一性就可以了,因此与同步意义上的锁很是有不同的。

5、分布式同步机制即真正的分布式锁,但是实际应用并不常见。本人实现过几次,目前准备运用在表单提交的同步上。

6、负载均衡

二、特性设计与优势

ZK主要使用场景远不是满足最初设计时对一致性调解的需求,这么受欢迎是因为其灵活的特性设计,只要简单组合就能满足很多种需求,同样将特性的受欢迎程度按照个人感觉从高到低进行说明

1、通知回调机制通过创建节点与设置Watcher可以很方便的建立回调通知。ZK的所有应用都基于这个特性,没有这个机制那么机器监控相关的应用都不能处理,也就不会诞生后来在服务注册发现相关的使用方法。实际上为分布式系统提供节点间回调通知方法的系统真的很少,甚至可能只有ZK(大家可以提供一些其他答案?)

2、可靠存储系统设计最初的需求之一,也是ZK特性中实现最好的部分,作为可靠存储ZK基本没出现过问题,仅此一项就可保证其的流行。

3、连接状态维护ZK自动维护了客户端所在的应用与服务器通信连接状态的变化,可以比较简单地维护系统中的成员通信情况。主要是不需要自己再去处理麻烦的通信状态监控问题,比如断线后自动释放节点并产生回调。

4、文件系统模型提供文件接口模型而不是锁接口,更具通用性。文件系统模型中文件与目录的概念可以映射多种含有层级关系的其他模型

5、自增长序列这点包含了锁的本质,但是因为zk的模型设计导致判断与仲裁需在客户端进行

三、实现技术选择与优点

zk本身的系统特性设计很出色,同时选择的实现技术也比较扎实,可谓蕴含相当的分布式系统工程经验在其中。下面结合个人理解讨论下这些实现技术有哪些特别优点与选择时可能的设计思路。

1、通讯机制与状态的实现

基于jute进行编解码处理保证通用性,服务器端通信使用nio或netty都是标准选择。

2、Zab协议与Paxos

zk使用Zab协议保证部署的多台机器间构成的整体系统的一致性与可靠性。这个分布式协议类似Paxos但是更加具体有效,实际上Paxos工程实现会碰到很多协议中没有定义的问题,G家员工为在Chubby中使用Paxos算法甚至专门发了一篇文章来说明Paxos工程化踩了多少坑。

Zab协议中将选主阶段与正常运行之间的阶段用catch up方式进行弥补,而关键的选主阶段使用了一个极其工程化的算法“fast leader election”(这个算法似乎没有经过形式化证明),这个算法足够粗暴有效,实现起来很简单。

最近Paxos的工程简化版算法Raft很火,所有考虑使用Paxos的系统都在实现Raft协议,其过程与Zab协议很类似,但是选主算法更加简单(可能实现结果是比Zab选主更慢)而且无法如Zab一样简单替换这个部分的算法。个人看法是Zab协议比Raft其实更容易理解,而且容易工程实现。(为何没有Raft火爆?可能是因为Zab协议选主部分设计的过于复杂,但是Raft目前还没有工业级的系统进行验证)

3、使用JVM

zk作为一个以稳定性与一致性为主的系统,性能上面肯定有一点损失。相信大家实现这种系统首先都会考虑要利用语言本身的速度优势尽量弥补系统的性能损失,于是我们就能看到很多c++实现的类zk系统(比如Chubby),但是这些新系统却没有zk的普及率。

可以说zk的流行中很重要的一点就是牺牲部分可能的性能使用JVM作为底层。正是因为虚拟机的使用屏蔽了各种异构系统底层,让zk可以很容易的稳定部署在多台配置性能都可能各异的机器上。个人理解这也是为什么现在那么多分布式系统都基于JVM技术栈,分布式系统需求的机器多,不可能所有配置都一样,而且机器都需要很容易进行物理替换或是系统升级,目前还只有JVM可以非常简单的提供这种等级的虚拟化屏蔽。

当然最近docker容器技术大放异彩,轻量级虚拟化方案以极快的速度兴起,让各种异构系统有了更简单可定制的底层虚拟化方式,也许有可能改变分布式系统的底层技术栈。

Zookeeper实现参数的集中式管理

前言

应用项目中都会有一些参数,一般的做法通常可以选择将其存储在本地配置文件或者内存变量中;对于集群机器规模不大、配置变更不是特别频繁的情况下,这两种方式都能很好的解决;但是一旦集群机器规模变大,且配置信息越来越频繁,依靠这两种方式就越来越困难;我们希望能够快速的做到全局参数的变更,因此需要一种参数的集中式管理,下面利用Zookeeper的一些特性来实现简单的参数管理。

准备

jdk:1.7.0_80
zookeeper:3.4.3
curator:2.6.0
spring:3.1.2

Maven引入

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>3.1.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>3.1.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>3.1.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.3</version>
    <exclusions>
        <exclusion>
            <groupId>com.sun.jmx</groupId>
            <artifactId>jmxri</artifactId>
        </exclusion>
        <exclusion>
            <groupId>com.sun.jdmk</groupId>
            <artifactId>jmxtools</artifactId>
        </exclusion>
        <exclusion>
            <groupId>javax.jms</groupId>
            <artifactId>jms</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>2.6.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>2.6.0</version>
</dependency>

目标

1、可以同时配置监听多个节点如/app1,/app2;

2、希望只需要配置如/app1,就能够监听其子节点如/app1/modual1以及子节点的子节点如/app1/modual1/xxx/…;

3、服务器启动能获取当前指定父节点下的所有子节点数据;

4、在添加节点或者在更新节点数据的时候能够动态通知,这样代码中就能够实时获取最新的数据;

5、spring配置中可以从Zookeeper中读取参数进行初始化。

实现

提供ZKWatcher类主要用来和Zookeeper建立连接,监听节点,初始化节点数据,更新节点数据,存储节点数据等

1、同时配置监听多个节点

提供一个字符串数组给用户用来添加需要监听的节点:

private String[] keyPatterns;

2、能够监听其子节点以及子节点的子节点

使用递归的方式用来获取指定监听节点的子节点:

private List<String> listChildren(String path) throws Exception {
    List<String> pathList = new ArrayList<String>();
    pathList.add(path);
    List<String> list = client.getChildren().forPath(path);
    if (list != null && list.size() > 0) {
        for (String cPath : list) {
            String temp = "";
            if ("/".equals(path)) {
                temp = path + cPath;
            } else {
                temp = path + "/" + cPath;
            }
            pathList.addAll(listChildren(temp));
        }
    }
    return pathList;
}

3、服务器启动初始化节点数据

上面已经递归获取了所有的节点,所有可以遍历获取所有节点数据,并且存储在Map中:

private Map<String, String> keyValueMap = new ConcurrentHashMap<String, String>();

if (pathList != null && pathList.size() > 0) {
    for (String path : pathList) {
        keyValueMap.put(path, readPath(path));
        watcherPath(path);
    }
}

private String readPath(String path) throws Exception {
    byte[] buffer = client.getData().forPath(path);
    String value = new String(buffer);
    logger.info("readPath:path = " + path + ",value = " + value);
    return value;
}

4、监听节点数据的变更

使用PathChildrenCache用来监听子节点的CHILD_ADDED,CHILD_UPDATED,CHILD_REMOVED事件:

private void watcherPath(String path) {
    PathChildrenCache cache = null;
    try {
        cache = new PathChildrenCache(client, path, true);
        cache.start(StartMode.POST_INITIALIZED_EVENT);
        cache.getListenable().addListener(new PathChildrenCacheListener() {

            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                switch (event.getType()) {
                case CHILD_ADDED:
                    logger.info("CHILD_ADDED," + event.getData().getPath());
                    watcherPath(event.getData().getPath());
                    keyValueMap.put(event.getData().getPath(), new String(event.getData().getData()));
                    break;
                case CHILD_UPDATED:
                    logger.info("CHILD_UPDATED," + event.getData().getPath());
                    keyValueMap.put(event.getData().getPath(), new String(event.getData().getData()));
                    break;
                case CHILD_REMOVED:
                    logger.info("CHILD_REMOVED," + event.getData().getPath());
                    break;
                default:
                    break;
                }
            }
        });
    } catch (Exception e) {
        if (cache != null) {
            try {
                cache.close();
            } catch (IOException e1) {
            }
        }
        logger.error("watch path error", e);
    }
}

5、spring配置中可以从Zookeeper中读取参数进行初始化

实现自定义的PropertyPlaceholderConfigurer类ZKPropPlaceholderConfigurer:

public class ZKPropPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

    private ZKWatcher zkwatcher;

    @Override
    protected Properties mergeProperties() throws IOException {
        return loadPropFromZK(super.mergeProperties());
    }

    /**
     * 从zk中加载配置的常量
     * 
     * @param result
     * @return
     */
    private Properties loadPropFromZK(Properties result) {
        zkwatcher.watcherKeys();
        zkwatcher.fillProperties(result);
        return result;
    }
        ......
}

通过以上的处理,可以使用如下简单的配置来达到目标:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <bean id="zkwatcher" class="zh.maven.DynamicConf.ZKWatcher">
        <property name="keyPatterns" value="/a2,/a3/m1" />
    </bean>

    <bean id="propertyConfigurer" class="zh.maven.DynamicConf.ZKPropPlaceholderConfigurer">
        <property name="zkwatcher" ref="zkwatcher"></property>
    </bean>

    <bean id="person" class="zh.maven.DynamicConf.Person">
        <property name="name">
            <value>${/a2/m1}</value>
        </property>
        <property name="address">
            <value>${/a3/m1/v2}</value>
        </property>
        <property name="company">
            <value>${/a3/m1/v2/t2}</value>
        </property>
    </bean>
</beans>

详细代码svn地址:http://code.taobao.org/svn/temp-pj/DynamicConf

测试

1、首先启动Zookeeper

zkServer.cmd

2、初始化需要使用的节点

public class Create_Node {

    static String path = "/a3/m1/v2/t2";
    static CuratorFramework client = CuratorFrameworkFactory.builder()
            .connectString("127.0.0.1:2181").sessionTimeoutMs(5000)
            .retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();

    public static void main(String[] args) throws Exception {
        client.start();
        client.create().creatingParentsIfNeeded()
                .withMode(CreateMode.PERSISTENT)
                .forPath(path, "init".getBytes());
    }
}

创建需要的节点方便ZKWatcher来监听,这里根据以上的配置,分别初始化/a3/m1/v2/t2和/a2/m1/v1/t1

3、启动Main,分别验证配置文件中的初始化以及代码动态获取参数

public class Main {

    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "spring-config.xml" });
        Person person = (Person) context.getBean("person");
        System.out.println(person.toString());

        ZKWatcher zkwatcher = (ZKWatcher) context.getBean("zkwatcher");
        while (true) {
            Person p = new Person(zkwatcher.getKeyValue("/a2/m1"), zkwatcher.getKeyValue("/a3/m1/v2"),
                    zkwatcher.getKeyValue("/a3/m1/v2/t2"));
            System.out.println(p.toString());

            Thread.sleep(1000);
        }
    }
}

4.观察日志同时更新参数:

public class Set_Data {

    static String path = "/a3/m1/v2/t2";
    static CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
            .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();

    public static void main(String[] args) throws Exception {
        client.start();
        Stat stat = new Stat();
        System.out.println(stat.getVersion());
        System.out.println("Success set node for :" + path + ",new version:"
                + client.setData().forPath(path, "codingo_v2".getBytes()).getVersion());
    }
}

部分日志如下:

2017-08-05 18:04:57 - watcher path : [/a2, /a2/m1, /a2/m1/v1, /a2/m1/v1/t2, /a3/m1, /a3/m1/v2, /a3/m1/v2/t2]
2017-08-05 18:04:57 - readPath:path = /a2,value = 
2017-08-05 18:04:57 - readPath:path = /a2/m1,value = zhaohui
2017-08-05 18:04:57 - readPath:path = /a2/m1/v1,value = 
2017-08-05 18:04:57 - readPath:path = /a2/m1/v1/t2,value = init
2017-08-05 18:04:57 - readPath:path = /a3/m1,value = 
2017-08-05 18:04:57 - readPath:path = /a3/m1/v2,value = nanjing
2017-08-05 18:04:57 - readPath:path = /a3/m1/v2/t2,value = codingo_v10
2017-08-05 18:04:57 - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@182f4aea: defining beans [zkwatcher,propertyConfigurer,person]; root of factory hierarchy
name = zhaohui,address = nanjing,company = codingo_v10
name = zhaohui,address = nanjing,company = codingo_v10
2017-08-05 18:04:57 - CHILD_ADDED,/a2/m1
2017-08-05 18:04:57 - CHILD_ADDED,/a3/m1/v2
2017-08-05 18:04:57 - CHILD_ADDED,/a2/m1/v1
2017-08-05 18:04:57 - CHILD_ADDED,/a2/m1/v1/t2
2017-08-05 18:04:57 - CHILD_ADDED,/a3/m1/v2/t2
name = zhaohui,address = nanjing,company = codingo_v10
name = zhaohui,address = nanjing,company = codingo_v10
name = zhaohui,address = nanjing,company = codingo_v10
2017-08-05 18:05:04 - CHILD_UPDATED,/a3/m1/v2/t2
name = zhaohui,address = nanjing,company = codingo_v11
name = zhaohui,address = nanjing,company = codingo_v11

总结

通过Zookeeper实现了一个简单的参数化平台,当然想在生产中使用还有很多需要优化的地方,本文在于提供一个思路;当然除了Zookeeper还可以使用MQ,分布式缓存等来实现参数化平台。

配置jenkins实现gitlab自动化构建

构建

需要将jenkins服务器上 jenkins用户的公钥发送给 目标服务器的gs用户,使得在jenkins上能用gs免密登录目标服务器

复制密钥到目标机器上(需要登录到的机器)

ssh-copy-id -i  .ssh/id_rsa.pub [email protected](目标机器)

未分类

自动化构建

需要配置gitlab的钩子 ,并在jenkins上做配置

未分类

未分类

点击Test Hook,测试配置,如果返回Hook successfully executed.表示配置成功,只要下次push代码就可以自动发布,Jenkins效果如下图

未分类

自动构建后,邮件通知。

Jenkins安装Job Configuration History插件实现配置信息变更历史

像 Jenkins 这样的系统,使用的过程就是配置文件变更的过程。如果能够对配置文件的变更进行跟踪管理,将极大的提高系统的可用性。Job Configuration History 插件就是这么一款实用而精巧的组件。很显然,相对于它的功能而言,它的名字实在是太低调了。因为它不仅能处理 Job Configuration 的变更历史,还能够处理系统级别的配置变更历史。

安装 Job Configuration History 插件

在 Jenkins->Plugin Manager 界面中选择 “Available” 标签页,输入 “Job Configuration History” 进行过滤:

未分类

点击安装并重启的按钮就可以啦!

Overview 视图

安装完成后,主页的菜单项中已经添加了 “Job Config History” 菜单:

未分类

点击该菜单进入插件的 Overview 视图:

未分类

在这里我们可以总览系统中的配置变更(其实是系统配置和所有根及项目的配置),并且可以通过左上方的菜单项或者是正上方的链接过滤出 “系统配置”、”Job 配置”、”创建 Job 的配置” 以及 “删除 Job 的配置” 的历史记录。并且可以查看历史记录中配置文件的内容。

Agent Config History 视图

下面我们通过 Agent Config History 视图来介绍该插件对配置文件历史数据的强大处理能力。选择并进入一个 Agent 的信息界面:

未分类

选择 “Agent Config History”:

未分类

我们可以选择不同的配置版本进行比较,或者是用历史版本覆盖当前的版本。
我们选择不同的版本,然后点击 “Show Diffs” 按钮:

未分类

上图主要是对比不同版本配置文件的差异,但是看到这么多的按钮确实让人有点不知所措。它们的操作为:

  • Prev:左右两个文件都更新为前一个版本(时间上比当前版本更早的一个版本)。
  • Next:左右两个文件都更新为下一个版本(时间上比当前版本更晚的一个版本)。

  • 左 Shrink Diff:左边文件更新为时间上比当前版本更晚的一个版本。

  • 左 Expand Diff:左边文件更新为时间上比当前版本更早的一个版本。

  • 右 Shrink Diff:右边文件更新为时间上比当前版本更早的一个版本。

  • 右 Expand Diff:右边文件更新为时间上比当前版本更晚的一个版本。

  • Restore this configuration:用某个历史版本的配置信息覆盖当前的配置信息。

乍一看让人倍感凌乱的按钮,在细看之下发现每个按钮的功能都不可替代。它们组合在一起可以让我们方便的对比文件的不同版本。并且可以轻松的把配置回滚到某个历史时刻。

Job Config History 视图

和 Agent Config History 视图类似,Job Config History 视图提供了 Job 配置的历史版本管理界面。在 Job 的信息界面点击 “Job Config History”即可打开,具体功能和使用方法和 Agent Config History 视图相同,因而不再赘述。

对于 Job Config,Job Configuration History 插件提供的另一个有用功能是在 Build 的历史记录中显示配置文件的变化记录:

未分类

从上图中我们可以清楚的看到具体某次 Build 时配置文件发生了变化,点击小图标还能看到配置文件变化的具体内容:

未分类

这能够极大的提高我们调试配置文件时的生产力,尤其是当错误发生时,我们可以立即定位是哪些配置的变化导致 Build 失败了。

实现原理

Job Configuration History 是一款非常实用的插件,我们不禁会好奇,它是如何实现的呢?

答案可能会让人有点失望,因为它的思路很简单:当配置发生变化时,就把旧的配置文件复制一份存起来!旧配置文件的存放路径默认就在 Jenkins 安装目录下的 config-history 目录中:

未分类

不管是系统级别配置的历史记录,还是 Job、Agent 配置的历史记录,全都被按照一定的规则组织放置在这个目录下。

总结

Job Configuration History 插件兼具低调、实用和设计简单等优点,实为居家、旅行之必备良品!相信每一个 Jenkins 管理员都会对之爱不释手。

Jenkins & Docker持续集成实践

前言

持续集成(CI/CD)是一种软件开发实践。用于帮助团队成员频繁、快速的集成,测试他们的工作成果,以尽快发现集成错误。 更频繁、更早的集成意味着更早的发现问题。通过持续集成,及时发现和解决代码故障,提高代码质量,减少故障处理成本等等。

常见持续集成工具

目前持续集成的生态越来越完善,工具也有很多,开源的或商业的。如:

最最流行的,也是使用最多的 Jenkins

有着持续集成DNA的ThoughtWorks GO。理念:”Deployment as pipeline” (华为容器平台应该是基于GO做的二次开发实现)

  • Atlassian工具链之一的Bamboo (数人云应该是基于Banboo实现的CI/CD)

  • 与Gitlab紧密集成的Gitlab CI

  • 专为开源打造的Travis CI,与Github紧密集成

  • 使用 python 语言实现的Buildbot,相信 pythoner 看到会喜欢

  • 我们的选型是 Jenkins,所以我们来看下 Jenkins

Jenkins

Jenkins 特点

  • Jenkins是开源的应用最广泛的持续集成工具,支持CI, CD;

  • Jenkins有很多插件,而且用户也可以自定义插件,可扩展性非常强;

  • Jenkins对Docker支持非常好,有一套完善的Docker插件;

  • Jenkins2.0开始支持Pipeline,一个非常强大的插件,使用基于Groovy的DSL,支持CI/CD流水线;

  • Jenkins 基于 Java 语言开发;

Jenkins 几个概念

  • master 是jenkins安装和运行的地方,它负责解析job脚本,处理任务,调度计算资源;

  • agent 负责处理从master分发的任务;

  • executor 就是执行任务的计算资源,它可以在master或者agent上运行。多个executor也可以合作执行一些任务;

  • job 任务,用来定义具体的构建过程;

  • Groovy 是一种基于JVM(Java虚拟机)的敏捷开发语言,它结合了Python、Ruby和Smalltalk的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。由于其运行在 JVM 上的特性,Groovy 可以使用其他 Java 语言编写的库。Jenkins 用Groovy作为DSL;

  • pipeline 流水线即代码(Pipeline as Code),通过编码而非配置持续集成/持续交付(CI/CD)运行工具的方式定义部署。流水线使得部署是可重现、可重复的;

流水线包括节点(Node)、阶段(Stage)和步骤(Step)。

流水线执行在节点上。节点是Jenkins安装的一部分。流水线通常包含多个阶段。一个阶段包含多个步骤。流水线上手指南可以查看到更多的内容。

node 在Pipeline中的context中,node是 job 运行的地方。 node会给job创建一个工作空间。工作空间就是一个文件目录,这是为了避免跟资源相关的处理互相产生影响。工作空间是node创建的,在node里的所有step都执行完毕后会自动删除。

stage 阶段,stage是一个任务执行过程的独立的并且唯一的逻辑块,pipeline定义在语法上就是由一系列的stage组成的。 每一个stage逻辑都包含一个或多个step。

step 步骤,一个step是整个流程中的一系列事情中的一个独立的任务,step是用来告诉Jenkins如何做。

  • Jenkinfile Jenkins支持创建流水线。它使用一种基于Groovy的流水线领域特定语言(Pipeline DSL)的简单脚。而这些脚本,通常名字叫Jenkinsfile。它定义了一些根据指定参数执行简单或复杂的任务的步骤。流水线创建好后,可以用来构建代码,或者编排从代码提交到交付过程中所需的工作。Jenkins中的Jenkinsfile有点类似Docker中的Dockfile的感觉。

Jenkins 部署

Jenkins组件其实非常少,安装部署也非常简单。 Jenkins按角色就两类: master节点和slave节点。master安装完成后,在控制台中添加节点即可。

下面以Dcoker的部署方式为例说一下Jenkins的部署:

master 节点

查看 docker hub 中 jenkins 的 image

[root@k3128v /home/huomingming]# docker search jenkinsNAME                               DESCRIPTION                                     STARS     OFFICIAL   AUTOMATEDjenkins Official Jenkins Docker image                   2600      [OK]       stephenreed/jenkins-java8-maven-git   Automated build that provides a continuous...   53 

可以看到第一个是官方提供的,所以我们选择这个即可。

拉取 jenkins image

docker pull jenkins

启动 jenkins 容器

jenkins没有数据库,所有数据都是存放在文件中的,首先在本地创建jenkins数据目录,用于保存jenkins的数据 这个目录需要定期的备份,用于容灾(当前jenkins容器所在节点由于不可抗因素无法使用时,可以在新机器上使用备份的数据启动新的jenkins master节点)。

sudo mkdir /var/jenkins
sudo chown 1000:1000 /var/jenkins
sudo docker run -p 8080:8080 -p 50000:50000 -v /var/jenkins:/var/jenkins_home --name my_jenkins -d jenkins

这样jenkins就成功跑起来了。可以直接通过机器的8080端口访问jenkins. 本地的/var/jenkins就相当于容器里jenkins用户的用户主目录,所以要保证该目录的权限为uid为j1000的用户目录。

配置 jenkins

启动完 jenkins master 后,在浏览器中数据输入 http://jenkins_master_ip:8080 登录 Jenkins 控制台进行接下来的安装和配置。 具体图就不贴出来了~~

查看jenkins的版本

java -jar /usr/share/jenkins/jenkins.war --version

slave 节点

安装 java jdk

yun install java-1.8.0-openjdk

创建 jenkins 用户

$ useradd -m jenkins -d /home/jenkins$ passwd jenkins

创建工作目录

mkdir /data/jenkinschown jenkins.jenkins /data/jenkins

添加 jenkins 用户到 docker 用户组

sudo usermod -a -G docker jenkins

配置 ssh 互信,master免密码登陆slave

master 有多种管理 slave 的方式,我们选择SSH方式 在master节点中,切换到jenkins用户 ssh-keygen -t rsa 创建秘钥对 把公钥拷贝到slave节点

scp ~/.ssh/id_rsa.pub jenkins@slave_ip:~/.ssh/authorized_keys

确保在scp前,slave节点根目录下.ssh目录已存在

chmod 700 authorized_keys

使用 jenkins 来构建 docker 是需要安装插件的。那我们需要安装哪些插件呢?

Jenkins 有哪些 Docker 的 Plugins

未分类

是非常丰富的,但并不是我们都能用的上,所以需要根据你使用的环境和平台来选择适合自己的 plugin 安装就可以了。 每个 plugin 都需要适配 Jenkins 的版本,且每个 plugin 也需要依赖一些其它 plugin,上面都已经做了标注,需要配套来用。

这里介绍几个常用的 Docker 插件

Docker Commons Plugin

其基本功能:

  • API for managing Docker image and container fingerprints

  • Credentials and location of Docker Registry

  • Credentials and location of Docker Daemon (aka Docker Remote API)

  • ToolInstallation for Docker CLI clients

  • DockerImageExtractor extension point to get Docker image relations from jobs

  • Simple UI referring related image fingerprints in Docker builds

Docker Plugins

该插件是将 docker 作为 jenkins 的 slave 来使用,来在 docker 容器种完成项目的build,build 完成后该容器 slave 容器就会被销毁。所有的工作都是在 slave 容器内完成。

docker-build-step

该插件在 build 过程种增加了对 Docker 命令的支持。

Docker Pipeline Plugin

基于 Docker Commons Plugin 实现的一些上层的基于 Docker 的 Pipeline 编排

Docker Hub Notification Trigger Plugin

该插件提供了当registry中的image发生变化时触发build新镜像的功能。

CloudBees Docker Build and Publish

该插件提供了通过 Dockerfile 来构建项目并将生成的镜像上传到镜像仓库的功能。

CloudBees Docker Custom Build Environment

This plugin allows the definition of a build environment for a job using a Docker container.

该插件适用于 “自由风格的软件项目”,如图:

未分类

CloudBees Docker Traceability

用于追踪通过jenkins启停的容器的事件

Kubernetes

This plugin allows Jenkins agents to be dynamically provisioned on a Kubernetes cluster. The aim of the Kubernetes plugin is to be able to use a Kubernetes cluster to dynamically provision a Jenkins agent (using Kubernetes scheduling mechanisms to optimize the loads), run a single build, then tear-down that slave.

与 Kubernetes 结合,通过 kubernetes 提供 jenkins 的 slave 节点。利用 kubernetes 的调度功能提供快速的启停 slave 节点执行 build 等任务。目前是0.11版本,稳定性有待验证。

Jenkins 有没有 API?

因为,我们不是直接在 Jenkins 的 Dashbord 来操作, 而是集成到现有平台,所以我们需要使用它的API。

Jenkins的Remote API以REST的形式进行提供。例如,我们搭建的Jenkins站点为http://myjenkins.com:8080。那么,访问http://myjenkins.com:8080/api 即可查看到该站点所有可用的API;

查询操作

未分类

执行一些动作

未分类

例如,我要创建一个 job,名字为 my_job

my_job的配置文件

<?xml version='1.0' encoding='UTF-8'?>
<project>
 <actions/>
 <description></description>
 <keepDependencies>false</keepDependencies>
 <properties/>
 <scm class="hudson.scm.NullSCM"/>
 <canRoam>true</canRoam>
 <disabled>false</disabled>
 <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
 <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
 <triggers class="vector"/>
 <concurrentBuild>false</concurrentBuild>
 <builders/>
 <publishers/>
 <buildWrappers/>
</project>

调用 api 创建 my_job

curl -X POST http://www.xxx.xxx/jenkins/createItem?name=my_job  --user uname:pass --data-binary "my_job_config.xml" -H "Content-Type: application/xml"

然后,你就可以在 Jenkins dashboard 上看到这个 job 了。它的管理页面为http://myjenkins.com:8080/job/my_job。那么我们访问 /my_job/api/ 即可查看到该job可用的API。

更多的 api 介绍可以参考Jenkins的官方wiki,这里只是个引子,在此就不过多进行介绍。

忘记Jenkins admin密码时重置的方法

一、admin密码未更改情况

1、进入Jenkinssecrets目录,打开initialAdminPassword文件,复制密码;

find / -name initialAdminPassword
[root@jenkins jenkins]# cat /var/lib/jenkins/secrets/initialAdminPassword
796008f906d2453ca0d22e2f8fa46d33

2.、访问Jenkins页面,输入管理员admin,及刚才的密码;

3、进入后可更改其他管理员密码;

二、admin密码更改忘记情况

1、删除Jenkins目录下config.xml文件中下面代码,并保存文件。

<useSecurity>true</useSecurity>  
<authorizationStrategy class="hudson.security.FullControlOnceLoggedInAuthorizationStrategy">  
  <denyAnonymousReadAccess>true</denyAnonymousReadAccess>  
</authorizationStrategy>  
<securityRealm class="hudson.security.HudsonPrivateSecurityRealm">  
  <disableSignup>true</disableSignup>  
  <enableCaptcha>false</enableCaptcha>  
</securityRealm>  

2、重启Jenkins服务;

3、进入首页>“系统管理”>“Configure Global Security”;

4、勾选“启用安全”;

5、点选“Jenkins专有用户数据库”,并点击“保存”;

6、重新点击首页>“系统管理”,发现此时出现“管理用户”;

7、点击进入展示“用户列表”;

8、点击右侧进入修改密码页面,修改后即可重新登录。