zoukankan      html  css  js  c++  java
  • 基于compose单机部署 etcd + coredns

    相关配置和脚本

    目录结构

    # 位于/home/dns目录下
    coredns_stand-alone
    .
    ├── compose-coredns.yaml
    ├── coredns
    │   └── conf
    │       └── Corefile
    ├── etcd
    │   ├── certs
    │   │   ├── ca-config.json
    │   │   ├── ca.csr
    │   │   ├── ca-csr.json
    │   │   ├── ca-key.pem
    │   │   ├── ca.pem
    │   │   ├── server.csr
    │   │   ├── server-csr.json
    │   │   ├── server-key.pem
    │   │   └── server.pem
    │   └── data
    └── etcd-cert-gen.sh
    

    执行流程

    # 请执行按照前文提到的目录结构来存放这些文件
    # 服务部署文件在当前项目的demo文件夹下
    # Docker和Docker-compose的安装就略过
    # 格式化脚本使用dos2unix
    # 由于脚本文件在Windows下编辑过,其换行符与Unix不同
    # 安装dos2unix
    yum install -y dos2unix
    # 脚本格式化
    dos2unix etcd-cert-gen.sh
    # 进入项目目录
    cd coredns_stand-alone
    # 脚本赋权
    chmod +x etcd-cert-gen.sh
    # 执行脚本,生成etcd的tls证书
    ./etcd-cert-gen.sh
    # 启动coredns容器
    docker-compose -f ./compose-coredns.yaml up -d
    

    .env

    ############################################################
    ######                 Global Setting                 ######
    ############################################################
    COMPOSE_PROJECT_NAME=coredns
    
    # etcd
    # etcd uses gcr.io/etcd-development/etcd as a primary container registry, and quay.io/coreos/etcd as secondary.
    # ETCD_IMAGE_NAME=registry.cn-hangzhou.aliyuncs.com/coreos_etcd/etcd:v3
    ETCD_IMAGE_NAME=quay.io/coreos/etcd:v3.3.20
    ETCD_API_VERSION=3
    ETCD_DATA_DIR=./etcd/data
    ETCD_CERT_DIR=./etcd/certs
    
    # coredns
    COREDNS_IMAGE_NAME=coredns/coredns:1.6.9
    COREDNS_CONF_DIR=./coredns/conf
    

    compose-coredns.yaml

    version: '3'
    
    services:
      # etcd service
      etcd0:
        image: ${ETCD_IMAGE_NAME}
        container_name: etcd0
        restart: always
        environment:
        - ETCDCTL_API=${ETCD_API_VERSION}
        - TZ=CST-8
        - LANG=zh_CN.UTF-8
        command:
        - "/usr/local/bin/etcd"
        # 成员
        - "--name=etcd0"
        - "--data-dir=/etcd-data"
        - "--advertise-client-urls=https://0.0.0.0:2379"
        - "--listen-client-urls=https://0.0.0.0:2379"
        #- "--listen-peer-urls=https://0.0.0.0:2380"
        # 集群
        #- "--initial-advertise-peer-urls=https://0.0.0.0:2380"
        #- "--initial-cluster-token=etcd-cluster"
        #- "--initial-cluster"
        #- "etcd0=https://0.0.0.0:2380"
        #- "--initial-cluster-state=new"
        # 安全
        - "--trusted-ca-file=/etcd-certs/ca.pem"
        - "--cert-file=/etcd-certs/etcd.pem"
        - "--key-file=/etcd-certs/etcd-key.pem"
        #- "--peer-trusted-ca-file=/etcd-certs/etcd-root-ca.pem"
        #- "--peer-cert-file=/etcd-certs/etcd.pem"
        #- "--peer-key-file=/etcd-certs/etcd-key.pem"
        # 日志
        - "--debug=true"
        volumes:
        - ${ETCD_DATA_DIR}:/etcd-data:rw
        - ${ETCD_CERT_DIR}:/etcd-certs:ro
        ports:
        - 2379:2379
        - 2380:2380
      
      # coredns service
      coredns:
        image: ${COREDNS_IMAGE_NAME}
        container_name: coredns
        restart: always
        network_mode: host
        depends_on:
        - etcd0
        command: -conf /etc/coredns/Corefile
        volumes:
        - ${COREDNS_CONF_DIR}:/etc/coredns:ro
        - ${ETCD_CERT_DIR}:/etcd-certs:ro
    
    

    Corefile

    .:53 {
        # 监听tcp和udp的53端口
        # 配置启用etcd插件,后面可以指定域名,例如 etcd test.com {}
        etcd {
            # 启用存根区域功能。 stubzone仅在位于指定的第一个区域下方的etcd树中完成
            stubzones
            # etcd里面的路径。默认为/coredns,以后所有的dns记录就是存储在该存根路径底下
            path /coredns
            # etcd访问地址,多个空格分开
            endpoint https://127.0.0.1:2379
            # upstream设置要使用的上游解析程序解决指向外部域名的在etcd(认为CNAME)中找到的外部域名。
            upstream 114.114.114.114:53 8.8.8.8:53 8.8.4.4:53 /etc/resolv.conf
            # 如果区域匹配但不能生成记录,则将请求传递给下一个插件
            fallthrough
            # 可选参数,etcd认证证书设置
            # 格式: tls CERT KEY CACERT
            tls /etcd-certs/etcd.pem /etcd-certs/etcd-key.pem /etcd-certs/ca.pem
            # 指定访问etcd用户名和密码(根据实际情况使用)
            # credentials USERNAME PASSWORD
        }
        # 健康
        health
        # 监控插件
        prometheus
        # 缓存时间
        cache 160
        # 自动加载时间间隔
        reload 6s
        # 负载均衡,开启DNS记录轮询策略
        loadbalance
        # 上面etcd未查询到的请求转发给设置的DNS服务器解析
        forward . 8.8.8.8:53 8.8.4.4:53 /etc/resolv.conf
        # 打印日志
        log
        # 输出错误
        errors
    }
    
    

    etcd-cert-gen.sh

    #!/usr/bin/env bash
    
    # 文档在Window下编辑过,需要转换为Unix格式。
    # 安装工具: yum install -y dos2unix
    # 然后执行命令: dos2unix ./etcd-cert-gen.sh
    
    CFSSL_FILE="/usr/local/bin/cfssl"
    CFSSL_JSON_FILE="/usr/local/bin/cfssljson"
    CFSSL_CERTINFO_FILE="/usr/local/bin/cfssl-certinfo"
    
    # 下载curl工具
    # -----------------------
    yum install -y curl
    
    # 下载cfssl工具
    # -----------------------
    if [[ ! -f "$CFSSL_FILE" ]]; then
      curl -L https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 -o ${CFSSL_FILE}
      chmod +x ${CFSSL_FILE}
      echo "------> cfssl has been installed successfully !! <------"
    else
      echo "------> cfssl has already installed !! <------"
    fi
    
    if [[ ! -f "$CFSSL_JSON_FILE" ]]; then
      curl -L https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -o ${CFSSL_JSON_FILE}
      chmod +x ${CFSSL_JSON_FILE}
      echo "------> cfssljson has been installed successfully !! <------"
    else
      echo "------> cfssljson has already installed !! <------"
    fi
    
    if [[ ! -f "$CFSSL_CERTINFO_FILE" ]]; then
      curl -L https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 -o ${CFSSL_CERTINFO_FILE}
      chmod +x ${CFSSL_CERTINFO_FILE}
      echo "------> cfssl-certinfo has been installed successfully !! <------"
    else
      echo "------> cfssl-certinfo has already installed !! <------"
    fi
    
    # 创建证书目录
    # -----------------------
    mkdir -p ./etcd/certs
    cd  ./etcd/certs
    
    # CA机构配置:有效期10年
    # -----------------------
    cat > ca-config.json <<EOF
    {
      "signing": {
        "default": {
          "expiry": "87600h"
        },
        "profiles": {
          "server": {
             "expiry": "87600h",
             "usages": [
                "signing",
                "key encipherment",
                "server auth",
                "client auth"
            ]
          },
          "client": {
             "expiry": "87600h",
             "usages": [
                "signing",
                "key encipherment",
                "server auth",
                "client auth"
            ]
          }
        }
      }
    }
    EOF
    
    # CA机构配置: 机构名称Comman Name,所在地Country国家, State省, Locality市
    # -----------------------
    cat > ca-csr.json <<EOF
    {
        "CN": "etcd CA",
        "key": {
            "algo": "rsa",
            "size": 4096
        },
        "names": [
            {
                "C": "CN",
                "L": "HuNan",
                "O": "thyc",
                "ST": "ChangSha"
            }
        ]
    }
    EOF
    
    # 如果是ETCD集群的话就直接在下面的hosts中添加IP或者域名。
    # 向CA机构申请:证书注册 (中国,湖南省,长沙市), 提供服务的IP
    # Organization Name, Common Name
    # -----------------------
    cat > server-csr.json <<EOF
    {
        "CN": "etcd",
        "hosts": [
        "127.0.0.1",
        "192.168.1.111",
        "etcd0"
        ],
        "key": {
            "algo": "rsa",
            "size": 4096
        },
        "names": [
            {
                "C": "CN",
                "L": "HuNan",
                "O": "thyc",
                "ST": "ChangSha"
            }
        ]
    }
    EOF
    
    # 使用定义好的配置初始化CA
    cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
    
    # 生成服务器证书
    cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server server-csr.json | cfssljson -bare etcd
    
    

    etcd client(java)

    pom依赖

       <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>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
            <!--日志-->
            <!-- https://mvnrepository.com/artifact/log4j/log4j -->
            <!--<dependency>-->
                <!--<groupId>log4j</groupId>-->
                <!--<artifactId>log4j</artifactId>-->
                <!--<version>1.2.17</version>-->
            <!--</dependency>-->
    
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-nop</artifactId>
                <version>1.7.25</version>
                <scope>test</scope>
            </dependency>
    
            <!--<dependency>-->
                <!--<groupId>dnsjava</groupId>-->
                <!--<artifactId>dnsjava</artifactId>-->
                <!--<version>2.1.8</version>-->
            <!--</dependency>-->
    
            <dependency>
                <groupId>io.etcd</groupId>
                <artifactId>jetcd-core</artifactId>
                <version>0.5.0</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/io.netty/netty-tcnative-boringssl-static -->
            <dependency>
                <groupId>io.netty</groupId>
                <artifactId>netty-tcnative-boringssl-static</artifactId>
                <version>2.0.29.Final</version>
                <scope>runtime</scope>
            </dependency>
    
        </dependencies>
    
        <build>
            <!-- finalName指定打包生成的文件名,默认为工程名-版本号 -->
            <finalName>mini-dns</finalName>
            <plugins>
                <!-- 指定jdk版本 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <!-- 源码的编译器版本 -->
                        <source>${java.version}</source>
                        <!-- class的编译器版本 -->
                        <target>${java.version}</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <configuration>
                        <!-- 跳过测试 -->
                        <skip>true</skip>
                    </configuration>
                    <version>2.18.1</version>
                </plugin>
            </plugins>
        </build>
    

    EtcdCluster.java

    /**
     * etcd node
     *
     * @author wzm
     * @version 1.0.0
     * @date 2020/5/8 17:21
     **/
    public class EtcdCluster {
        private String ip;
        private Integer port;
        private Boolean tls;
        private String endpoint;
    
        public EtcdCluster(String ip, Integer port) {
            this.ip = ip;
            this.port = port;
        }
    
        protected void setTls(Boolean tls) {
            this.tls = tls;
        }
    
        public String getEndpoint() {
            return endpoint;
        }
    
        protected void generateFormattedEndpoint() {
            String http = "http://";
            String https = "https://";
            if (tls) {
                this.endpoint = https + ip + ":" + port.toString();
            }else {
                this.endpoint = http + ip + ":" + port.toString();
            }
        }
    }
    

    EtcdClient.java

    import com.google.gson.JsonObject;
    import io.etcd.jetcd.ByteSequence;
    import io.etcd.jetcd.Client;
    import io.etcd.jetcd.KV;
    import io.etcd.jetcd.KeyValue;
    import io.etcd.jetcd.kv.DeleteResponse;
    import io.etcd.jetcd.kv.GetResponse;
    import io.grpc.netty.GrpcSslContexts;
    
    import java.io.InputStream;
    import java.nio.charset.Charset;
    import java.nio.charset.StandardCharsets;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.CompletableFuture;
    
    /**
     * etcd Client
     *
     * @author wzm
     * @version 1.0.0
     * @date 2020/5/8 16:53
     **/
    public class EtcdClient {
        private List<EtcdCluster> etcdClusters;
        private String certPath;
        /**
         * etcd Client
         */
        private static Client client;
        /**
         * Charset
         */
        private static Charset charset = StandardCharsets.UTF_8;
        /**
         * Cache time
         */
        private static final Integer TTL = 600;
        /**
         * DNS name
         */
        private static final String DNS_NAME = "coredns";
    
        public EtcdClient(List<EtcdCluster> etcdClusters) {
            for (EtcdCluster etcd : etcdClusters) {
                etcd.setTls(false);
                etcd.generateFormattedEndpoint();
            }
            this.etcdClusters = etcdClusters;
            initClient();
        }
    
        public EtcdClient(List<EtcdCluster> etcdClusters, String certPath) {
            for (EtcdCluster etcd : etcdClusters) {
                etcd.setTls(true);
                etcd.generateFormattedEndpoint();
            }
            this.etcdClusters = etcdClusters;
            this.certPath = certPath;
            initClientWithTls();
        }
    
        public void close() {
            client.close();
        }
    
        /**
         * Initialize the etcd client to support clustering
         *
         * @author wzm
         * @date 2019/11/1 10:19
         */
        private void initClient() {
            String[] endpoints = new String[etcdClusters.size()];
            for (int i = 0; i < etcdClusters.size(); i++) {
                endpoints[i] = etcdClusters.get(i).getEndpoint();
            }
            client = Client.builder().endpoints(endpoints).build();
        }
    
        /**
         * Initialize the etcd client (TLS) to support clustering
         *
         * @author wzm
         * @date 2019/11/1 10:20
         */
        private void initClientWithTls() {
            String[] endpoints = new String[etcdClusters.size()];
            for (int i = 0; i < etcdClusters.size(); i++) {
                endpoints[i] = etcdClusters.get(i).getEndpoint();
            }
            try (InputStream is = getClass().getResourceAsStream(certPath)) {
                //还需要设置证书路径
                client = Client.builder()
                        .endpoints(endpoints)
                        .sslContext(
                                GrpcSslContexts
                                        .forClient()
                                        .trustManager(is)
                                        .build()
                        ).build();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        /**
         * New/modified
         *
         * @param key        域名
         * @param value      ip
         * @param difference 区别
         * @author wzm
         * @date 2019/11/1 9:45
         */
        public boolean putRecord(String key, String value, String difference) {
            try {
                KV kv = client.getKVClient();
                ByteSequence k = ByteSequence.from(formatKey(key, difference), charset);
                ByteSequence v = ByteSequence.from(formatValue(value, TTL), charset);
                // put the key-value
                kv.put(k, v).join();
                return kv.get(k).join().getCount() == 1;
            } catch (Exception e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    
        /**
         * query
         *
         * @param key        key
         * @param difference differ(The same domain name may correspond to more than one IP)
         * @return java.util.Map
         * @author wzm
         * @date 2019/11/1 10:02
         */
        public Map<String, String> getRecord(String key, String difference) {
            try {
                Map<String, String> map = new HashMap<>(1);
                KV kvClient = client.getKVClient();
                ByteSequence k = ByteSequence.from(formatKey(key, difference), charset);
                // get the CompletableFuture
                CompletableFuture<GetResponse> getFuture = kvClient.get(k);
                // get the value from CompletableFuture
                GetResponse response = getFuture.get();
                List<KeyValue> keyValues = response.getKvs();
                for (KeyValue kv : keyValues) {
                    map.put(getKeyFromKv(kv), getValueFromKv(kv));
                }
                return map;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        /**
         * delete
         *
         * @param key        dns name
         * @param difference differ
         * @author wzm
         * @date 2019/11/1 10:17
         */
        public boolean deleteRecord(String key, String difference) {
            try {
                KV kvClient = client.getKVClient();
                CompletableFuture<GetResponse> getFeature = kvClient.get(ByteSequence.from(formatKey(key, difference), charset));
                GetResponse resp = getFeature.get();
                ByteSequence k = ByteSequence.from(formatKey(key, difference), charset);
                DeleteResponse deleteResponse = kvClient.delete(k).get();
                long f = deleteResponse.getDeleted();
                return f == resp.getKvs().size();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        /**
         * Get the key from kv
         *
         * @param kv kv
         * @return java.lang.String
         * @author wzm
         * @date 2019/11/1 9:59
         */
        private static String getKeyFromKv(KeyValue kv) {
            return kv.getKey().toString(charset);
        }
    
        /**
         * Get the value from kv
         *
         * @param kv kv
         * @return java.lang.String
         * @author wzm
         * @date 2019/11/1 9:59
         */
        private static String getValueFromKv(KeyValue kv) {
            return kv.getValue().toString(charset);
        }
    
    
        private static String formatKey(String domainName, String difference) {
            String[] strings = domainName.split("\.");
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("/" + DNS_NAME);
            for (int i = 0; i < strings.length; i++) {
                String tmp = strings[strings.length - 1 - i];
                stringBuilder.append("/").append(tmp);
            }
            String str = stringBuilder.toString();
            if (difference == null || "".equals(difference.trim())) {
                return str;
            }
            return str + "/" + difference;
        }
    
        private static String formatValue(String host, int ttl) {
            JsonObject jsonObject = new JsonObject();
            jsonObject.addProperty("host", host);
            jsonObject.addProperty("ttl", ttl);
            return jsonObject.toString();
        }
        //A记录
        //etcdctl put /coredns/com/leffss/www '{"host":"1.1.1.1","ttl":10}'
        //AAAA记录
        //etcdctl put /coredns/com/leffss/www '{"host":"1002::4:2","ttl":10}'
        //CNAME记录
        //etcdctl put /coredns/com/leffss/www '{"host":"www.baidu.com","ttl":10}'
        //SRV记录
        //etcdctl put /coredns/com/leffss/www '{"host":"www.baidu.com","port":80,"ttl":10}'
        //TXT记录
        //etcdctl put /coredns/com/leffss/www '{"text":"This is text!","ttl":10}'
    }
    

    源码地址:https://github.com/wenming5112/minidns

  • 相关阅读:
    Django REST framework+Vue 打造生鲜超市(十二)
    pymsql
    (四)Jmeter系列之---http接口请求脚本优化
    (三)Jmeter系列之---实现一个简单的http接口请求
    2021每天一个知识点(二月)
    (二)Jmeter系列之---Jmeter工具介绍
    (一)Jmeter系列之---性能测试+工具介绍
    Linux磁盘的分区操作
    Python格式化处理json数据的方式
    (17)-Python3之--文件操作
  • 原文地址:https://www.cnblogs.com/jockming/p/12970343.html
Copyright © 2011-2022 走看看