jenkins+ansible+gitlab+docker+compose+swarm 自动发布

docker安装:https://blog.csdn.net/jy02268879/article/details/111185992

jenkins安装:https://blog.csdn.net/jy02268879/article/details/89819598

ansible安装:https://blog.csdn.net/jy02268879/article/details/111478340

gitlab安装:https://blog.csdn.net/jy02268879/article/details/111266982

本篇讲docker compose和docker swarm部分,还有jenkins+ansible+gitlab+docker+compose+swarm 自动发布的例子

一、服务器操作

1.初始化swarm节点

不包含在任何 Swarm 中的 Docker 节点,称为运行于单引擎(Single-Engine)模式。一旦被加入 Swarm 集群,则切换为 Swarm 模式,如下图所示。 

在单引擎模式下的 Docker 主机上运行 docker swarm init会将其切换到 Swarm 模式,并创建一个新的 Swarm,将自身设置为 Swarm 的第一个管理节点。 

在选定docker swarm的leader机器既这里是( node1)172.20.10.8上初始化

$ docker swarm init \
--advertise-addr 172.20.10.8:2377 \
--listen-addr 172.20.10.8:2377

将这条命令拆开分析如下。

docker swarm init会通知 Docker 来初始化一个新的 Swarm,并将自身设置为第一个管理节点。同时也会使该节点开启 Swarm 模式。

--advertise-addr 指定其他节点用来连接到当前管理节点的 IP 和端口。这一属性是可选的,当节点上有多个 IP 时,可以用于指定使用哪个IP。此外,还可以用于指定一个节点上没有的 IP,比如一个负载均衡的 IP。

--listen-addr 指定用于承载 Swarm 流量的 IP 和端口。其设置通常与 --advertise-addr 相匹配,但是当节点上有多个 IP 的时候,可用于指定具体某个 IP。并且,如果 --advertise-addr 设置了一个远程 IP 地址(如负载均衡的IP地址),该属性也是需要设置的。建议执行命令时总是使用这两个属性来指定具体 IP 和端口。

Swarm 模式下的操作默认运行于 2337 端口。虽然它是可配置的,但 2377/tcp 是用于客户端与 Swarm 进行安全(HTTPS)通信的约定俗成的端口配置。

执行结果

注意:这里把这些内容记录下来

这个执行结果的内容是告诉你,如果你后面想加入工作节点到这个swarm,请使用这句话

docker swarm join --token SWMTKN-1-4oxx6w39qtnq7o3tk3w71rxusnc5xx9lwlokjk8lw0yd0k6zvb-8uhtcgjhwba91wfmc09x13p0a 172.20.10.8:2377

这个执行结果的内容是告诉你,如果你后面想加入管理节点到这个swarm,请使用这句话查看加入管理节点的命令

docker swarm join-token manager

注意:工作节点和管理节点的接入命令中使用的接入 Token(SWMTKN...)是不同的。因此,一个节点是作为工作节点还是管理节点接入,完全依赖于使用了哪个 Token。

接入 Token 应该被妥善保管,因为这是将一个节点加入 Swarm 的唯一所需! 

列出 Swarm 中的节点。

docker node ls

这能看到该节点的状态,hostname,是否激活,是否为管理机,docker版本,node ID 

 接下来将 node2、node3作为工作节点接入,自动将它们切换为Swarm模式 

在node2、node3中分别执行

docker swarm join --token SWMTKN-1-0iltgvpbvmf8kvfnzckyrmspyo569g9royk05jpd8hvlzh9nme-a338edtsq3vthu8l6us79kmuw 172.20.10.8:2377

如果管理机docker swarm init的时候指定了advertise-addr喝listen-addr,记得在node2和node3加入的时候确保使用 --advertise-addr 与 --listen-addr 属性来指定各自的 IP 地址。既是node3用node3的IP,node2用node2的IP

结果

在这个过程中,每个节点的 Docker 引擎都被切换到 Swarm 模式下。并且,Swarm 已经自动启用了 TLS 以策安全。

每次将节点加入 Swarm 都指定 --advertise-addr 与 --listen-addr 属性是痛苦的。然而,一旦 Swarm 中的网络配置出现问题将会更加痛苦。况且,手动将节点加入 Swarm 也不是一种日常操作,所以在执行该命令时额外指定这两个属性是值得的。 

 此时在管理机node1上执行

docker node ls

docker swarm的集群中的管理机器还能做成多个用以实现高可用HA,共识算法是raft,同一时间只有一个管理机是leader,其他的管理机器是follower,如果docker客户端命令发送到了follower上,follower会转发到leader上,只会有Leader对swarm执行命令。部署奇数个管理节点有利于减少脑裂(Split-Brain)情况的出现机会。

详情看:http://c.biancheng.net/view/3178.html 

2.Node Label 管理

比如service1这个应用,需要启动3个副本,那直接用docker service create设置副本数--replicas=3 模式为Replication Mode,这种模式会部署期望数量的服务副本,并尽可能均匀地将各个副本分布在整个集群中。

而此处,我们是需要每个服务在指定的机器上只启动一个,比如在node1 上启动一个service1,在node2上启动一个service1.并不仅仅只是期望副本数量而已。

全局模式(global),在这种模式下,每个节点上仅运行一个副本。也可以通过给 docker service create 命令传递 --mode global 参数来部署一个全局服务。

我采取的方式是加标签:

docker node update --label-add 

注意这里的label要跟ansible的/etc/anisble/hosts里面一样

先看一下我的ansible是怎么加的

然后得到每个node的ID 

docker node ls

 然后添加标签

在swarm控制机172.20.10.8上执行,给172.20.10.8(node1)打上service1的标签,给172.20.10.9(node2)打上service2的标签,给172.20.10.10(node3)打上service3的标签

docker node update --label-add service1=true kjkv0vkbsygyfwxji46m14izj

docker node update --label-add service2=true lk8j9ob4y345u34msh32faufz

docker node update --label-add service3=true jgvug4jn676k2fpfvgy4rgeui

查看标签

docker node inspect node1

3.docker创建network网络

Docker有以下网络类型:

bridge:多由独立container之间的通信
host: 直接使用宿主机的网络,端口也使用宿主机的
overlay:当有多个docker主机时,跨主机的container通信
macvlan:每个container都有一个虚拟的MAC地址
none: 禁用网络

默认网络

Docker在默认情况下,分别会建立一个bridge、一个host和一个none的网络:

可以看到,driver类型为bridge的网络的名字也为bridge。在默认情况下,container都是使用的这个bridge的网络,此时container是可以访问外网和其他container的(需要通过IP地址)。

默认的名为bridge的网络是有很多限制的,为此,我们可以自行创建bridge类型的网络。默认的bridge网络与自建bridge网络有以下区别:

端口不会自行发布,必须使用-p参数才能为外界访问,而使用自建的bridge网络时,container的端口可直接被相同网络下的其他container访问。

container之间的如果需要通过名字访问,需要--link参数,而如果使用自建的bridge网络,container之间可以通过名字互访。

 我们这里要在docker swarm控制机上执行

docker network create -d overlay --attachable sid

  

4.服务部署条件约束

Service 方式

docker service create \
  --name service1 \
  --constraint 'node.labels.service1 == true' \
  service1

Stack 方式

这里是使用的这种方式

 deploy_build.yml

这个文件主要是jenkins上面执行,对service1 service2 service3制作镜像并且推送到docker registry自己部署私有镜像中心里面去

---   #固定格式
- hosts: swarmserver  #定义需要执行主机
  gather_facts: no  #不收集facts
  tasks:   #定义一个任务的开始
    - name: build components docker images    #定义任务的名称
      shell: "mvn docker:build docker:push -Ddocker.registry.address={{registry_address}} -Dgit.branch={{build_version}}"
      args:
        chdir: "/app/jenkins/workspace/test-docker-ansible-docker-swarm/{{item}}"
      with_items:
        - service1
        - service2
        - service3

 deploy_components.yml

这个文件是ansible控制机上面会执行,主要是通过ansible控制/etc/ansible/hosts中配置的service1的机器从docker registry私有的镜像中心里面拉取service1应用的镜像。service2 service3类似。。

控制每台机器创建相应的日志路径

控制swarmdeployserver机器(就是swarm的leader那台机器)使用stack方式根据compose.yml文件在docker swarm集群中发布应用

---   #固定格式
- hosts: service1,service2,service3  #定义需要执行主机
  gather_facts: no  #不收集facts
  tasks:   #定义一个任务的开始
    # 拉镜像
    - name: pull docker images
      shell: "docker pull {{registry_address}}/sid/{{item}}:{{build_version}}"
      with_items:
        - service1
        - service2
        - service3
    - name: Create DockerHost EnvFile
      shell: |
        echo "" > /etc/dockerhost.env
        echo "export dockerhost=\"`ifconfig ens33 | grep 'inet' | head -n 1 | awk '{print $2}'`\"" >> /etc/dockerhost.env
        echo "export hostOriginName=\"`cat /etc/hostname`\"" >> /etc/dockerhost.env

- hosts: service1
  gather_facts: no
  tasks:
    - name: create log directory
      file:
        dest: "{{item}}"
        mode: 666
        state: directory
      with_items:
        - /log/service1/log

- hosts: service2
  gather_facts: no
  tasks:
    - name: create log directory
      file:
        dest: "{{item}}"
        mode: 666
        state: directory
      with_items:
        - /log/service2/log

- hosts: service3
  gather_facts: no
  tasks:
    - name: create log directory
      file:
        dest: "{{item}}"
        mode: 666
        state: directory
      with_items:
        - /log/service3/log

# 删除sid_components栈
- hosts: swarmserver
  gather_facts: no
  tasks:
    - name: Debug
      debug:
        msg: components deploy work directory {{work_dir}}
    - name: Clean Components Deloy Workspace
      file:
        dest: "{{work_dir}}/components"
        state: absent
    - name: Create Deploy Workspace
      file:
        dest: "{{work_dir}}/components"
        mode: 0755
        state: directory
    - name: Swarm Remove Components Services
      shell: docker stack rm sid_components

# 在leader节点上使用swarm发布java组件
- hosts: swarmserver
  gather_facts: no
  roles:
    - role: swarm

# 删除无用的镜像
- hosts: service1,service2,service3
  gather_facts: no
  tasks:
    - name: Delete Useless Docker Images
      shell: "docker ps -a |grep Exited |grep sid_components |awk '{print $1}'|xargs -r docker rm \
      && \
      docker images |grep none |grep {{registry_address}} |awk '{print $3}'|xargs -r docker rmi "
      ignore_errors: yes

main.yml

---
##swarm
- name: Copy Swarm Compose.yml
  template:
    src: compose.yml
    dest: "{{work_dir}}/components/compose.yml"
- name: Swarm Deploy
  shell: docker stack deploy -c compose.yml sid_components
  args:
    chdir: "{{work_dir}}/components"

 compose.yml

version: "3.3"

services:
  service1:
    image: "{{registry_address}}/sid/service1:{{build_version}}"
    networks:
      - sid
    ports:
      - 8095:8095
    volumes:
      - /etc/localtime:/etc/localtime
      - /log/service1/log:/app/log
      - /etc/dockerhost.env:/etc/dockerhost.env
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
    environment:
      - "TZ=Asia/Shanghai"
      - "jasypt.encryptor.password={{encryptor_password}}"
    deploy:
      mode: global
      placement:
        constraints: [node.labels.service1 == true]
      restart_policy:
        condition: on-failure
  service2:
    image: "{{registry_address}}/sid/service2:{{build_version}}"
    networks:
      - sid
    ports:
      - 8096:8096
    volumes:
      - /etc/localtime:/etc/localtime
      - /log/service2/log:/app/log
      - /etc/dockerhost.env:/etc/dockerhost.env
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
    environment:
      - "TZ=Asia/Shanghai"
      - "jasypt.encryptor.password={{encryptor_password}}"
    deploy:
      mode: global
      placement:
        constraints: [node.labels.service2 == true]
      restart_policy:
        condition: on-failure
  service3:
    image: "{{registry_address}}/sid/service3:{{build_version}}"
    networks:
      - sid
    ports:
      - 8097:8097
    volumes:
      - /etc/localtime:/etc/localtime
      - /log/service3/log:/app/log
      - /etc/dockerhost.env:/etc/dockerhost.env
    logging:
      driver: "json-file"
      options:
        max-size: "50m"
    environment:
      - "TZ=Asia/Shanghai"
      - "jasypt.encryptor.password={{encryptor_password}}"
    deploy:
      mode: global
      placement:
        constraints: [node.labels.service3 == true]
      restart_policy:
        condition: on-failure

networks:
  sid:
    external:
      name: sid

all.yml

---
deploy_dir: /app/sid_deploy/deploy
work_dir: /app/sid_deploy/workspace
confs_dir: /app/sid_deploy/confs
#config_username:
#config_password:

 deploy.initialize.yml

---
- hosts: swarmserver
  gather_facts: no
  vars:
    deploy_dir: /app/sid_deploy/deploy
    confs_dir: /app/sid_deploy/confs
    do_deploy_dir: "{{deploy_dir}}/{{deploy_name}}/{{build_version}}"
  tasks:
    # ready for deploying
    # 创建发布目录
    - name: remove deploy directory
      file:
        dest: "{{do_deploy_dir}}"
        state: absent
    - name: create deloy file directory
      file:
        dest: "{{do_deploy_dir}}/files"
        mode: 0755
        state: directory
    - name: create deloy vars directory
      file:
        dest: "{{do_deploy_dir}}/vars"
        mode: 0755
        state: directory
    # 推送发布脚本
    - name: copy ansile scripts
      copy:
        src: ops/ansible
        dest: "{{do_deploy_dir}}"
        force: yes
    # 推送执行shell脚本
    - name: copy shell scripts
      copy:
        src: ops/bin
        dest: "{{do_deploy_dir}}"
        force: yes
    # 推送执行confs文件,不强制覆盖
    - name: copy conf files
      copy:
        src: ops/confs/
        dest: "{{confs_dir}}"
        force: no
    # 获取配置文件加密秘钥
    - name: Read Conf Encrypt Password
      shell: |
        PASSWORD=`cat {{confs_dir}}/encrypt/password`
        echo $PASSWORD
      register: encryptorPassword
    # 生成变量信息
    - name: save the env variables
      copy:
        content: |
          ---
          registry_address: {{registry_address}}
          build_version: {{build_version}}
          encryptor_password: {{encryptorPassword.stdout or 'sid'}}
        dest: "{{do_deploy_dir}}/vars/vars_deploy.yml"
    # do deploy
    - name: deploy with ansible-playbook
      shell: 'ansible-playbook -e "@{{do_deploy_dir}}/vars/vars_deploy.yml" deploy_components.yml'
      args:
        chdir: "{{do_deploy_dir}}/ansible"
#      when: do_deploy == "yes"

deploy_components.sh

#!/bin/bash

#进入脚本所在目录
workdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $workdir

bin_dir=$(pwd)
vars_dir=$bin_dir/../vars
ansible_dir=$bin_dir/../ansible

ansible-playbook -e "@$vars_dir/vars_deploy.yml" $ansible_dir/deploy_components.yml

 build_deploy_components.sh

#!/bin/bash

JOB_NAME=$0
INVENTORY_FILE=$1
DOCKER_SWARM_LEADER_HOST=$2
GIT_BRANCH=$3
DOCKER_REGISTRY_ADDRESS=$4

##-- 初始化工作空间
WORKSPACE="/app/jenkins/workspace/test-docker-ansible-docker-swarm"
INVENTORY_FILE=inventory_${JOB_NAME##*/}

echo "" > $INVENTORY_FILE
echo "[swarmserver]" > $INVENTORY_FILE
echo "$DOCKER_SWARM_LEADER_HOST" >> $INVENTORY_FILE

echo "WORKSPACE: $WORKSPACE"

##-- 从分支名获取版本
BUILD_BRANCH=`echo ${GIT_BRANCH} | cut -d'/' -f 2-`
echo "BUILD_BRANCH: $BUILD_BRANCH"

##-- Change to workspace
cd $WORKSPACE

#else
##全量构建
echo "全量构建"
cp ops/ansible/deploy_build.yml deploy_build.yml
#ansible-playbook -i $INVENTORY_FILE -e "registry_address=$DOCKER_REGISTRY_ADDRESS \
#                                        build_version=$BUILD_BRANCH \
#                                        build_workspace=$WORKSPACE" deploy_build.yml
ansible-playbook -e "registry_address=$DOCKER_REGISTRY_ADDRESS \
                                        build_version=$BUILD_BRANCH \
                                        build_workspace=$WORKSPACE" deploy_build.yml
#fi

##-- 发布
cp ops/ansible/deploy_initialize.yml deploy_initialize.yml
#ansible-playbook -i $INVENTORY_FILE -e "registry_address=$DOCKER_REGISTRY_ADDRESS \
#                                        build_version=$BUILD_BRANCH \
#                                        deploy_name=sid_components" deploy_components.yml
ansible-playbook -e "registry_address=$DOCKER_REGISTRY_ADDRESS \
                                        build_version=$BUILD_BRANCH \
                                        deploy_name=sid_components" deploy_initialize.yml

#-- Clean
rm -rf deploy_components.yml
rm -rf deploy_build.yml
rm -rf $INVENTORY_FILE

build_pre.sh

#!/bin/bash
#打印maven版本
mvn -v
#打印java版本
java -version

二、jenkins配置及发布

 

 

 

 

 

三、代码

父pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.sid</groupId>
    <artifactId>test-docker</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>service1</module>
        <module>service2</module>
        <module>service3</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!-- spring-boot的web启动的jar包 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.codehaus.janino</groupId>
            <artifactId>janino</artifactId>
            <version>3.0.6</version>
        </dependency>
    </dependencies>


</project>

service1的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>test-docker</artifactId>
        <groupId>com.sid</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>service1</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.codehaus.janino</groupId>
            <artifactId>janino</artifactId>
            <version>3.0.6</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>1.0.0</version>
                <configuration>
                    <!-- 镜像名称  -->
                    <imageName>${docker.registry.address}/sid/service1</imageName>
                    <imageTags>
                        <imagetag>${git.branch}</imagetag>
                    </imageTags>
                    <!-- docker远程服务器地址
                    <dockerHost>http://服务器IP:2375</dockerHost>
                    -->
                    <!-- Dockerfile文件存放目录 -->
                    <dockerDirectory>${basedir}/docker</dockerDirectory>
                    <resources>
                        <resource>
                            <targetPath>/</targetPath>
                            <directory>${project.build.directory}</directory>
                            <include>${project.build.finalName}.jar</include>
                        </resource>
                    </resources>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Dockerfile

FROM centos:7

RUN yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel vorbis-tools

RUN mkdir -p /app/log

COPY service1-1.0-SNAPSHOT.jar /service1-1.0-SNAPSHOT.jar
EXPOSE 8005/tcp

CMD ["/bin/bash", "-c", "cat /etc/dockerhost.env >> /etc/profile && source /etc/profile && java -jar /service1-1.0-SNAPSHOT.jar"]

TestController.java

package com.sid.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;



@Controller
@RequestMapping(value = "/test")
public class TestController {

    private final static  Logger logger = LoggerFactory.getLogger(TestController.class);

    @Value("${hostOriginName}")
    String hostname;

    @RequestMapping(value = "/select")
    @ResponseBody
    public String select(){
        logger.info("hostname : {}",hostname);
        return "service1 select";
    }


}

App1.java

package com.sid;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App1 {
    public static void main(String[] args) {
        SpringApplication.run(App1.class, args);
    }

}

application.yml

server:
  port: 8095
  servlet:
    context-path: /service1

spring:
  mvc:
    view:
      prefix: /
      suffix: .html

# log config
logging:
  config: classpath:logback-spring.xml

四、相关的常用命令 

删除标签

docker node update --label-rm env node1  

运行compose.yml文件

$ docker stack deploy -c docker-compose.yml sid_components

删栈

$ docker stack rm sid_components

某台机器彻底退出swarm集群

docker swarm leave --force

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值