分類:我的安裝食譜

在docker安裝pyodbc以連線到MSSQL的步驟

在docker安裝pyodbc以連線到MSSQL的步驟

在windows上,python要連線到mssql,只需要透過pyodbc,幾乎不用什麼設定,就可以輕鬆連線上mssql

但是在linux上,遇到的坑與血淚,相信前人遇到的已經太多了!

以下記錄一下步驟與眉角:

首先我們先假設已經有一個存在的docker container在運作了,裡面有基本python 3.6的環境(或其他版本,這邊以3.x為主,自行上docker hub找吧…)

連進去container後,有3大工程要施作…

1.安裝freetds

wget  http://ibiblio.org/pub/Linux/ALPHA/freetds/stable/freetds-stable.tgz

tar zxvf freetds-stable.tgz

cd freetds-0.91/

./configure --with-tdsver=7.1 --prefix=/usr/local/freetds0.91 --build=x86_64-pc-linux-gnu --host=x86_64-pc-linux-gnu --mandir=/usr/share/man --infodir=/usr/share/info --datadir=/usr/share --sysconfdir=/etc --localstatedir=/var/lib --libdir=/usr/lib64 --without-ldap --without-tcl --enable-pkinit --enable-thread-support --without-hesiod --enable-shared --with-system-et --with-system-ss --enable-dns-for-realm --enable-kdc-lookaside-cache --with-system-verto --disable-rpath --with-pkinit-crypto-impl=openssl --with-openssl

make

make install

 cat >> /usr/local/freetds0.91/etc/freetds.conf
加入
[TestDB]
host = mesotest.database.windows.net
port = 1433
tds version = 7.0

註:freetds.conf 的dump file = /tmp/freetds.log反註解,global的tds版本也要改成7.0一致的版本,有dump log的話,後續連線失敗的話,可以看的到錯誤原因,事半功倍

例: severity:9, dberr:20002[Adaptive Server connection failed], oserr:0[Success] –>tds版本問題,要調整,若8.0不行,就7.2->7.1->7.0往回裝

2.測試freetds連線

/usr/local/freetds0.91/bin/tsql -S TestDB -U app_user@mesotest.database.windows.net -P {password} -D test1

若freetds可以連線,也可以查詢的話,應該會像這樣:

可以下sql指令,也回傳的了資料集

2.設定ODBCInit

apt-get install unixodbc-dev
apt-get install python-pip

pip install pyodbc
#yum install gcc-c++

#關鍵中的關鍵
find /usr -name "*\.so" |egrep "libtdsodbc|libtdsS"
 #/usr/lib/libtdsS.so 
 #/usr/local/freetds0.91/lib/libtdsodbc.so

# cp /etc/odbcinst.ini /etc/odbcinst.ini.20160102

# cat >> /etc/odbcinst.ini

[SQL Server]
Description = FreeTDS ODBC driver for MSSQL
Driver = /usr/local/freetds0.91/lib/libtdsodbc.so
Setup = /usr/lib/libtdsS.so
FileUsage = 1

# 檢查一下驅動
# odbcinst -d -q
[SQL Server]

cat >> /etc/odbc.ini
[TESTDSN]
Driver          = SQL Server
Server          = xxx.xxx.xxx.xxx
User            = xxxx
TDS_Version     = 7.0
Port            = 1433

3.執行簡單的python連mssql程式

import pyodbc

conn =  pyodbc.connect("driver={SQL Server};server=mesotest.database.windows.net;PORT=1433 database=test1;UID=app_user@mesotest.database.windows.net;PWD=%s;TDS_Version=7.0;" % "{yourpassword}" )
cursor = conn.cursor()

query = "select getdate()"

print(query)
cursor.execute(query)
row = cursor.fetchone()
while row:
    print(str(row[0]))
    row = cursor.fetchone()

執行成功,我要哭了…凌晨3點了!!

根據網友們的分享,這裡還有一個很大的坑就是連線字串要包含TDS_Version的資訊,版本要跟freetds內配置的版本一樣…

否則就會陷入無限的…08001輪迴,而不知其所以然…

Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
pyodbc.Error: (‘08001’, ‘[08001] [unixODBC][FreeTDS][SQL Server]Unable to connect to data source (0) (SQLDriverConnect)’)

 

關鍵2篇REF

https://blog.csdn.net/samed/article/details/50449808

http://www.voidcn.com/article/p-vaxmczdi-dc.html

locust + nginx + tornado web server 壓測客戶端與服務端 自動化腳本與實測結果

locust + nginx + tornado web server 壓測客戶端與服務端 自動化腳本與實測結果

壓測是考驗整體架構吞吐量與穩定性的最直接方式

繼上一篇 找到了一個壓測新朋友Locust 傳送門

這一篇進一步透過自動化shell,可以加速建置你的壓測Client端集群與Server端集群

首先我們要租2台主機,讓其物理cpu、ram網卡獨立,然後可以預設在相同的DataCenter,以降低跨DataCenter的網路影響因素

整體壓測概念如下:

 

Initail #number of web server Shell Script

#!/bin/bash

read -p "Enter your webserver number to remove: " p_clear_count

for (( i=1 ; ((i < ($p_clear_count+1))) ; i=(($i+1)) ))
do
  sudo docker rm -f webtest$(printf 0%02d $i)
done;
sudo docker rm -f web-test-nginx

read -p "Enter your webserver number: " p_count

for (( i=1 ; ((i < ($p_count+1))) ; i=(($i+1)) ))
do
  sudo docker run -d --name webtest$(printf 0%02d $i) --network="webtest" -p $(printf 100%02d $i):6969 tornado-web-test
done;

sudo docker run --name web-test-nginx --network="webtest" -p 10000:10000 -v /mnt/nginx/conf.d/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx nginx-debug -g 'daemon off;'

Initail #number of locust client Shell Script

#!/bin/bash

Test_Url="http://{{hostname}}:10000"

read -p "Enter your locust client number to remove: " p_clear_count

for (( i=1 ; ((i < ($p_clear_count+1))) ; i=(($i+1)) ))
do
  sudo docker rm -f locust-slave$i
done;
sudo docker rm -f locust-master

read -p "Enter your locust client number: " p_count

sudo docker run -d --name locust-master --hostname locust-master \
 --network="webtest" \
 -p 8089:8089 -p 5557:5557 -p 5558:5558 \
 -v /mnt/webtest/locust-master:/locust \
 -e LOCUST_MODE=master \
 -e ATTACKED_HOST="$Test_Url" \
 grubykarol/locust

for (( i=1 ; ((i < ($p_count+1))) ; i=(($i+1)) ))
do
  sudo docker run -d --name locust-slave$i \
	 --network="webtest" \
	 --env NO_PROXY=locust-master \
	 -e ATTACKED_HOST="$Test_Url" \
	 -v /mnt/webtest/locust-slave:/locust \
	 -e LOCUST_MODE=slave \
	 -e LOCUST_MASTER=locust-master \
	 --rm grubykarol/locust
done;

 

實測下來,8g開10個container(共享)的情況下,估計10000 concurrent user應該已經是很極限了,其實我們會發現極限應該是會在client發出請求端

web server多少會因為os、網卡、容器網路的限制,導致同時connection數無法無限上崗,而web server的運作還不包含更複雜的運算情境(只考慮in ram處理與非同步mongodb log)

因此以此為基準來當作未來擴充的計算基礎參考,應該還ok,若要模擬更複雜的商業邏輯運作,那麼可以仿照此作法去刻

10k

20k (RPS反而降了,看來還是有其極限存在)

美中不足的就是怎麼動態改Nginx的Nginx.Conf,把ReversProxy動態換掉,這個以後再研究吧…

 

100k on AWS
Reference:
1.https://aws.amazon.com/tw/blogs/devops/using-locust-on-aws-elastic-beanstalk-for-distributed-load-generation-and-testing/
2.https://www.slideshare.net/AmazonWebServices/aws-reinvent-2016-how-to-launch-a-100kuser-corporate-back-office-with-microsoft-servers-and-aws-win303?from_action=save

Nginx Proxy建立Load Balance分流機制

Nginx Proxy建立Load Balance分流機制

傳統在雲端平台上,通常都會有現成的Load Balance服務,提供彈性負載分流到自己的應用程式集群

假如我們希望在私有雲下,或是在自己家裡,希望也可以建置Load Balancer的話,透過硬體的F5機制成本高昂

這個時候,就可以依賴Nginx的套件了

其特點是可以大量處理併發連線

Nginx在官方測試的結果中,能夠支援五萬個並列連接,而在實際的運作中,可以支援二萬至四萬個並列連結。

 

假設以下情境,我們希望建立一個WebTest的測試環境,統一1個domain的port為進入點,但背後可能有很多台Api或子web站台來支持不同的服務與運算

因此這個時候,我們需要nginx來做為API.Domain的代理服務器,將實際的請求轉導到對應的內部伺服器,再把結果回傳回去

我們以Docker為測試環境,方便模擬多台伺服器的情況,而Docker在容器間的網路連線上,提供許多Api可以方便我們建置集群

我透過Python寫一隻輕量運算的api server(這也可以是其他案例,例如取得天氣、股市、時間…等)作為範例

當api層級深度太高的話,瓶頸識別會愈來愈不單純,因此我先在這邊假定問題點就是單一台吞吐量有上限,因此我們透過多台+load balance來支持同時併發的連線請求

小型的python get uuid tornado web server

import datetime
import socket
import json
import os
import sys
import uuid
from collections import OrderedDict
from multiprocessing.pool import Pool

import asyncio
import tornado
from tornado import web, gen
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from mongodb_helper import MongoDBHelper

sys.path.append( os.path.abspath( os.path.join( os.path.abspath( __file__ ), os.pardir, os.pardir ) ) )
sys.path.append( "/usr/src/app" )

def get_server_ip():
    return  (([ip for ip in socket.gethostbyname_ex( socket.gethostname() )[2] if not ip.startswith( "127." )] or [[(s.connect( ("8.8.8.8", 53) ), s.getsockname()[0], s.close()) for s in [socket.socket( socket.AF_INET, socket.SOCK_DGRAM )]][0][1]]) + ["no IP found"])[0]

def log(from_ip, action, data):
    service = MongoDBHelper( host="mongodb-dev", port=27017 )
    service.change_db( "tornado_web_test" )
    service.insert_data( collection_name="requestLog_"+get_server_ip() , data=dict( from_ip=from_ip, action=action, data=data ) )

class WebTestEntryHandler( tornado.web.RequestHandler ):
    def initialize(self, pool=None):
        self.local_pool = pool

    def set_default_headers(self):
        self.set_header( "Access-Control-Allow-Origin", "*" )
        self.set_header( "Access-Control-Allow-Headers", "x-requested-with" )
        self.set_header( 'Access-Control-Allow-Methods', 'POST, GET, OPTIONS' )
        self.set_header( "Access-Control-Allow-Headers", "Content-Type" )
        self.set_header( 'Content-Type', 'application/json' )

    def options(self, *args, **kwargs):
        # no body
        self.set_status( 200 )
        self.finish()

    def get_uuid(self):
        requestTime = datetime.datetime.today().strftime( '%Y-%m-%d %H:%M:%S.%f' )[:-3]

        result = str(uuid.uuid4())

        responseTime = datetime.datetime.today().strftime( '%Y-%m-%d %H:%M:%S.%f' )[:-3]

        resultObj = OrderedDict([("IsSuccess", True), ("Data", result), ("RequestTime",requestTime), ("ResponseTime", responseTime)])

        return resultObj

    def common(self, action):
        try:

            if action == "get_uuid":
                resultObj = self.get_uuid()
            else:
                resultObj=dict(IsSuccess=False, Message="action not found")

            self.local_pool.apply_async(log, (self.request.remote_ip, action, resultObj,) )

        except Exception as err:
            resultObj = dict( IsSuccess=False, Message=str(err) )

        if resultObj != None:
            self.write( json.dumps( resultObj ) )

    @gen.coroutine
    def get(self):
        action = None
        if self.get_argument('action', default=None) != None:
            action = self.get_argument('action')
        self.common(action=action)


    @gen.coroutine
    def post(self):
        action = None
        if self.get_argument( 'action', default=None ) != None:
            action = self.get_argument( 'action' )
        self.common( action=action )

def serve(host, port, pool):
    import socket
    if host in ["", None]:
        ip_address = socket.gethostbyname( socket.gethostname() )
    else:
        ip_address = host

    # tornado.options.parse_command_line() not work for websocket
    app = tornado.web.Application( default_host=ip_address, handlers=[
        (r"/webtest", WebTestEntryHandler, dict( pool=pool )),
    ] )
    http_server = HTTPServer( app, max_body_size=1500 * 1024 * 1024 * 1024 )
    http_server.listen( port )  # 1.5M
    io_loop = tornado.ioloop.IOLoop.current()
    print("rest server ready to start!")
    io_loop.start()


def app(pool):
    rest_host_str = "0.0.0.0"
    rest_port_str = "6969"
    rest_port = int( rest_port_str )
    # define( "port", default=rest_port, help="run on the given port", type=int )
    serve( rest_host_str, rest_port, pool )

if __name__ == "__main__":
    pool = Pool( processes=4 )  # start 4 worker processes
    app(pool)

註:上面python的實作,為了統計server的請求處理數據,因此加入了寫入mongodb的異步流程,可以參考使用,呼叫mongodb連線時,記得也要使用container name哦,不然會連不到

將其server打包成容器

FROM python:3

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [ "python", "./rest_server.py" ]

#建立測試api 服務容器映像檔
sudo docker build -t tornado-web-test .

#建立容器群網路,網路內的容器可直接互連
sudo docker network create webtest

#建立容器實例(共3台),分別佔用10001, 10002, 10003
sudo docker run -d –name webtest001 –network=”webtest” -p 10001:6969 tornado-web-test
sudo docker run -d –name webtest002 –network=”webtest” -p 10002:6969 tornado-web-test
sudo docker run -d –name webtest003 –network=”webtest” -p 10003:6969 tornado-web-test

分別請求10001,10002,10003的http://xxx.xxx.xxx.xxx:10001/webtest?action=get_uuid後,可以正常的回應即可

接著主要工作就是配置nginx,其設定檔,很明確的說明了我希望它扮演的角色

nginx.conf

http{
 upstream webtest.localhost {
    server webtest001:6969;
    server webtest002:6969;
    server webtest003:6969;
 }
 
 server {

   listen 10000;

   #ssl_certificate /etc/nginx/certs/demo.pem;
   #ssl_certificate_key /etc/nginx/certs/demo.key;

   gzip_types text/plain text/css application/json application/x-javascript
              text/xml application/xml application/xml+rss text/javascript;

   server_name localhost;

   location / {
       proxy_pass http://webtest.localhost;
   }
 }
}



 events {
   worker_connections  1024;  ## Default: 1024
 }

注意:這邊的webtest1~3的port號都是6969,雖然在之前docker run的時候,有expose綁到其他port號,但是在docker的容器網路內部仍是採用原本容器的設定,因此這邊一定要用容器配置,而不是容器expose的配置,這邊找了一陣子

關於upstream的標籤,官方文件如下:

範例:
upstream backend {
    server backend1.example.com weight=5;
    server 127.0.0.1:8080       max_fails=3 fail_timeout=30s;
    server unix:/tmp/backend3;

    server backup1.example.com  backup;
}

標籤下,我們可以定義伺服器群組,各別伺服器可以監聽不同的port,而且可以TCP/Socket混用

預設,請求會被透過round-robin balancing方法的權重來分配到不同的伺服器,以此為例,每7個請求,會被分配5個請求到backend1.example.com,還有2個分別被轉送到2、3伺服器

假如轉送過程中,有發生error,該請求會自動pass給下一個伺服器,直到所有的伺服器都試過為止。假如沒有任何伺服器可以回傳正確的結果,那用戶端的通訊結果將會是最後一台伺服器的訊息。

 

我們啟動Nginx容器,並試著去連線對外的10000 port

#nginx docker command
sudo docker run –name web-test-nginx –network=”webtest” -p 10000:10000 -v /home/paul/webtest/nginx/conf.d/nginx.conf:/etc/nginx/nginx.conf:ro -d nginx nginx-debug -g ‘daemon off;’

實測:http://xxx.xxx.xxx.xxx:10000/webtest?action=get_uuid

打網址,若可以出現這個畫面,那就代表可以work了,多打幾次後,我追蹤mongodb裡的log,可以看到不同的server都有接到請求

大工告成!!

透過這樣的架構,我們可以讓原本單一一台的請求量提升到n台,假如nginx的配置沒有爆的話,那只要擔心後端的每個端點是否服務正常(這關系到監控機制)

當然,docker、vm,個人電腦都有其物理極限,包含網卡、頻寬,伺服器的連線上限…etc,因此負載測試這個issue,有時因為成本過高,我們會測出單位的負載量後再加倍估算。

這個就另開討論吧…

[centos] 為Tornado開啟SSL-生成SSL證書

[centos] 為Tornado開啟SSL-生成SSL證書

首先要生成伺服器端的私密金鑰(key檔):

sudo openssl genrsa -des3 -out server.key 1024
  1. 運行時會提示輸入密碼,此密碼用於加密key檔(參數des3便是指加密演算法,當然也可以選用其他你認為安全的演算法.),以後每當需讀取此檔(通過openssl提供的命令或API)都需輸入口令.如果覺得不方便,也可以去除這個口令,但一定要採取其他的保護措施!

去除key文件口令的命令:

生成CSR檔:

sudo openssl req -new -key server.key -out server.csr -config /etc/pki/tls/openssl.cnf
  1. 生成Certificate Signing Request(CSR),生成的csr檔交給CA簽名後形成服務端自己的證書.螢幕上將有提示,依照其指示一步一步輸入要求的個人資訊即可.

對用戶端也作同樣的命令生成key及csr文件:

CSR檔必須有CA的簽名才可形成證書.可將此檔發送到verisign等地方由它驗證,要交一大筆錢,何不自己做CA呢.

  1. 在/etc/pki/CA/目錄下新建目錄certs、newcerts
  2. 建立一個空檔 index.txt
  3. 建立一個文字檔 serial, 沒有副檔名,內容是一個合法的16進制數字,例如 0000
  4. sudo openssl req -new -x509 -keyout ca.key -out ca.crt -config /etc/pki/tls/openssl.cnf
    
    sudo openssl genrsa -des3 -out client.key 1024
    
    sudo openssl req -new -key client.key -out client.csr -config openssl.cnf

用生成的CA的證書為剛才生成的server.csr,client.csr文件簽名:

sudo openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key -config /etc/pki/tls/openssl.cnf

sudo openssl ca -in client.csr -out client.crt -cert ca.crt -keyfile ca.key -config /etc/pki/tls/openssl.cnf

到了這裡應該已經創建了可以使用的證書了,如果在為檔簽名的時候有錯誤,那多半是資訊不正確,這時可以去清空一下 index.txt 裡的資訊,然後重新執行第5步裡失敗的操作。

在Tornado啟用https server

http_server = HTTPServer( app, max_body_size=1500 * 1024 * 1024 * 1024,
ssl_options={"certfile": os.path.join( os.path.abspath( "." ), "server.crt" ),
			 "keyfile": os.path.join( os.path.abspath( "." ), "server.key" )} )
ElasticSearch Auto Build With Docker

ElasticSearch Auto Build With Docker

近日因為大數據寫入與分析應用需求的關系

需要大量的使用到ElasticSearch

然而,每次在不同的環境要佈署ELK並配置不同的node來達到Sharding的效果的工作覺得繁瑣

因此手刻了一些半自動化部署Elasticsearch的Cluster的機制(同一台主機,不同的Docker),不同台需要參考使用Docker Swarn

 

以下是相關Shell Script操作,相關步驟如下:

0.設定相關的環境變數,例如java memory, elasticsearch version

1.詢問要建立的容器名稱

2.詢問export的port名稱(有2組,通常為9200、9300)

3.動態建立容器網路(若容器放在同一個網路下無綁定方向問題問題)

4.動態建立cluster的目錄(容器名稱會建立在此目錄下再建立對應容器的目錄)與相關config/esdatadir、log、data目錄,並設立權限

5.動態從版控取得對應的ElasticSearch的config,也可以自行改成放在某個統一位置取得 (網址:http://xxx.xxx.xxx.xxx:yyyy是git的位置)

6. sudo sysctl -w vm.max_map_count=262144(這句不知為什麼一定要執行..java環境才起的來)

7.啟動Docker(並結合相關參數)

#!/bin/bash


sudo docker ps -a
read -p "Enter your elasticsearch container_name(eg. es1, es2): " p_container_name

container_name=$p_container_name
java_memory="2g"
es_version="5.6.6"

read -p "Enter your elasticsearch container_name default export port1(eg. 9200, 9201, 9202): " port1

export_port1=$port1

read -p "Enter your elasticsearch container_name default export port1(eg. 9300, 9301, 9302): " port2
export_port2=$port2

network_name="es_net"

if [ ! "$(sudo docker network ls |grep $network_name)" ]; then
  sudo docker network create "$network_name"
  echo "network created!"
else
  sudo docker network ls | grep "$network_name"
  echo "network $network_name already exist!"
fi

echo "continue..."

DIRECTORY="es_cluster"

sudo cd /mnt

if [ ! -d "$DIRECTORY" ]; then
  sudo mkdir "$DIRECTORY"
fi

cd es_cluster
sudo mkdir "$container_name"
cd "$container_name"

echo "$container_name folder created!"

sudo git init
sudo git remote add origin "http://xxx.xxx.xxx.xxx:yyyy/scm/mes/elasticsearch.git"
sudo git pull origin master
sudo mkdir config/scripts
sudo mkdir esdatadir
cd esdatadir
sudo mkdir log
sudo mkdir data
sudo chmod 777 /mnt/es_cluster/$container_name/esdatadir/*
sudo chmod 777 /mnt/es_cluster/$container_name/config/scripts

echo "$container_name configuration setting done!"

cd /mnt/es_cluster/$container_name

sudo sysctl -w vm.max_map_count=262144

sudo docker run -d -v "/mnt/es_cluster/$container_name/esdatadir/data":/var/lib/elasticsearch \
 -v "/mnt/es_cluster/$container_name/esdatadir/log":/var/log/elasticsearch \
 -v "/mnt/es_cluster/$container_name/config":/usr/share/elasticsearch/config \
 -p "$export_port1:9200" -p "$export_port2:9300" --name $container_name -e "bootstrap.memory_lock=true" \
 -e ES_JAVA_OPTS="-Xms$java_memory -Xmx$java_memory" --ulimit memlock=-1:-1 \
 --restart always \
 --net "$network_name" \
 elasticsearch:$es_version

echo "$container_name docker container up done!"
結合docker swarm 建置ElasticSearch Cluster

結合docker swarm 建置ElasticSearch Cluster

最近因為有要整合ELK方案的需求,因此又再度接觸了這個曾經相當熟悉的工具,還記得那個時候(也才1年多前),還是在2.x版左右,kibana的介面還是長這個樣子的時候。

在先前的公司,有平台團隊專門架設這個服務提供RD團隊整合使用,對於使用是絲毫不陌生,但是建置卻完全沒有經驗。沒想到現在已經轉眼間來到了6.1版本,介面跟架構看起來調整的更鮮艷與簡潔(?),我知道ELK目前在windows上面仍然是有一鍵安裝的版本。對於開發要測試的需求上已經相當足夠(連結),但因為我們目前對外主機主要都是CentOS,因此我這次預計實作如下:

假設我有2台CentOS主機的情境下(一台稱為A Host, 一台稱作B Host),如何透過各別安裝elasticsearch的docker 容器,來串連成elasticsearch 的集群。

同一台的多個容器理論上是相對簡單的,而這邊會遇到的問題,主要的關鍵就是如何解決跨主機間docker容器的通信問題,網路架構也通常都是應用程式間管控較為複雜的一個部分

 

這邊我打算採用Docker Swarm,Swarm為Docker自行開發的容器調度工具,其中的跨主機建立overlay網路功能看起來是非常符合我的情境需求,由於是Docker平台內建工具,看起來並不需要額外的安裝與學習,因此我先從這套工具開始著手,至於關於其他容器調度工具還包含了,Kubernetes、Mesos等。甚至現在的顯學看起來是Kubernetes(k8s),有許多的討論也直指要如何決定該採用哪種方案。

目前看到這一篇(連結)是相對從各面向都有討論到,有興趣的可以看看

Docker swarm的架構圖(來自Docker官網)

從docker 1.9版開始,DockerSwarm已經是內建工具,因此透過以下指令,就可以將主機宣告成DockerSwarm的Manager主機(第一台總是master嘛)

sudo docker swarm init

若有成功啟動DockerSwarm的話,會提示以下訊息

docker swarm join –token SWMTKN-1-5wsc3yya3e87w2e84jkzbpieubpxr9v6qwnbj87m6t2ynv7kxm-8q5amih216368atw8adklx24f %A Host IP%:2377
這個hint很明顯就是我們可以在其他的Host(稱作Worker),透過以上指令來加入到Manager下的叢集中。同時他是透過A Host的2377 Port來進行串連
但假如希望建立起來的Swarm使用其他Port的話,我們可以使用以下指令來做
docker swarm init –advertise-addr=%A Host IP% –listen-addr %A Host IP%:2377
所以我們也可以使用這句語法檢查,是否有啟動了這兩個Port

sudo netstat -nap | grep ^tcp.*dockerd

正常應該會列出對應的2377與7946

tcp6 0 0 :::7946 :::* LISTEN 28184/dockerd
tcp6 0 0 :::2377 :::* LISTEN 28184/dockerd

接著我們在B Host上,輸入剛剛建立完swarm manager 所提示的加入語法

   docker swarm join –token SWMTKN-1-5wsc3yya3e87w2e84jkzbpieubpxr9v6qwnbj87m6t2ynv7kxm-8q5amih216368atw8adklx24f %A Host IP%:2377

在這邊若有無法加入的情況的話,可以往防火牆先檢查,若是防火牆確定有通(telnet看看),那我這邊有遇到原本怎樣都加入不了,但是重開機、重啟docker後,就可以加入的情況。

若Worker成功加入Swarm的話,我們可以在A Host輸入以下指令確認是否節點都完整

docker node ls

既然A-B Host已經加入了同一個網路架構,接著就是建立覆疊網路

sudo docker network create   –driver overlay  –attachable es_net
註:這邊–attachable 若後續要透過run 語法加入指定網路的話,參數一定要加,是不是Docker Compose就不用加,我不確定
成功建立overlay網路後,我們就可以透過以下指令看到我們的網路已經被建立起來,SCOPE是顯示swarm , Driver是顯示overlay
sudo docker network ls
現在網路架構已經看似完成,接著我們來準備Elasticsearh的服務配置吧
這邊我寫了一個腳本來自動建立elasticsearch的相關目錄,目前只要elasticsearch會異動到的檔案目錄權限要開啟來,否則之後容器啟動後,會有權限例外產生:
腳本中改777的部分都是因為遇到所以加上去的,可以自行改成合適的權限配置。
sudo cd /mnt
sudo mkdir elasticsearch
cd elasticsearch
sudo git init
sudo git remote add origin "http://git server ip/scm/mes/elasticsearch.git"
sudo git pull origin master
sudo mkdir config/scripts
sudo mkdir esdatadir
cd esdatadir
sudo mkdir log
sudo mkdir data
sudo chmod 777 /mnt/elasticsearch/esdatadir/*
sudo chmod 777 /mnt/elasticsearch/config/scripts

cd /mnt/elasticsearch

config下的檔案,因為A, B Host各自擁有自己的獨立硬碟,因此我假設他們的環境配置應該都一致,jvm.options、log4j2.properties、檔案都一樣,只有Elasticsearch的yml檔會有些許差異:

path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
cluster.name: meso_es_cluster1
node.master: true #if worker then mark it
node.name: ${HOSTNAME}
network.host: 0.0.0.0
discovery.zen.ping.unicast.hosts: ["A HOST IP", "B HOST IP"]
以上都完成以後,就是來啟動Docker 容器的時候了,以A Host為例,啟動語法如下,B Host只是改–name成es2
sudo docker run -d -v /mnt/elasticsearch/esdatadir/data:/var/lib/elasticsearch -v /mnt/elasticsearch/esdatadir/log:/var/log/elasticsearch -v /mnt/elasticsearch/config:/usr/share/elasticsearch/config -p 9200:9200 -p 9300:9300 --net=es_net --name es1 --security-opt seccomp=unconfined -e "bootstrap.memory_lock=true" -e ES_JAVA_OPTS="-Xms1g -Xmx1g" --ulimit memlock=-1:-1 elasticsearch:5.6.6

分別執行後,我們可以看到docker ps 下是否狀態是正常在up的。

補充說明,啟動es容器時,我曾經發現會有以下的錯誤訊息:max virtual memory areas vm.max_map_count [65530] is too low(請透過docker logs查看)
這個問題若資源分配問題的話,可以透過以下指令解決:
sudo sysctl -w vm.max_map_count=262144
 但我也有一個疑問,logs是記錄容器內部的jvm的錯誤,但是我是在容器外部下語法可解決,這個原因我確實不清楚就是了..

若都是正常,我們可以透過elasticsearch的Api來檢查我們的es cluster是否有正常運作起來

從瀏覽器輸入A Host IP:9200,會返回相關json資訊,若是如下,我們可以認定cluster有啟動,而且服務版本正確,

可以看到name的部分,就正是各別主機上Docker 容器的name,而cluster_name因為配置所以會一致、而若有正確加入的話,cluster_uuid必須要一致
註:我在實作的時候,就一直遇到B Host啟動的起來,但是他無法加入cluster,所以cluster_uuid一直顯示為_na_,後來竟然是整個docker服務重啟就好了,這個羅生門,讓我相信3R的救命招(Recycle, Reset, Reboot),測試期間,也可以透過docker exec -it B HOST Container IP ping -c 3 A_Host_IP來看看是否可以透過容器內解析出跨主機的另一個同網路下的容器名稱

 

如何觀察Elasticsearch的Cluster狀態呢?具瞭解在es 2.x版本的時候,有一套es的plugin,叫作kopf,是拿來可以直接透過es api來監控es服務的工具

後來5.x版以後,發現plugin功能被拔掉了,取而代之是獨立的”cerebro“套件可以達成完全一樣的功能,看來是同個作者寫的。

介面如下

只要在Node Address輸入A Host es容器名稱:9200(注意容器互連必須透過容器名稱,而不能只是ip),就可以看到以下的dashboard,相當的方便,除了可以即時監控Node的資源使用狀況,也可以看到目前的index數量與使用狀況、空間

cerebro 這邊,只需要透過以下指令,就可以啟動:

sudo docker run -d -p 9000:9000 –name cerebro –net es_net yannart/cerebro:latest

同場加映Kibana 5.6.6的啟動指令

sudo docker run –name meso-kibana –link es1:elasticsearch –net es_net -p 5601:5601 -d kibana:5.6.6
透過A Host IP:5601啟用Kibana後,就可加入已經自定好的Index pattern來使用囉
將你的Nodejs React Web App Docker化

將你的Nodejs React Web App Docker化

假設目前已選用React的框架,我試驗的這套名叫https://github.com/zuiidea/antd-admin

方案目錄結構如下:

主要react程式都放在src的目錄下

node_modules是在npm install的時候,才會把相依套件安裝在這個目錄裡面

以下步驟的目的主要是希望啟動一個node.js 的web server,將我們的react web啟動後,可以透過外部編修程式來除錯與開發

production環境,不需要另外開放目錄了,而且依正常nodejs 的production build,就也編譯好了,所以不會因為src的code異動而影響!

 

 

1.在webapp的根目錄,建立DockerFile (touch Dockerfile)

# You should always specify a full version here to ensure all of your developers
# are running the same version of Node.
FROM node:7.8.0

# Override the base log level (info).
ENV NPM_CONFIG_LOGLEVEL warn

# Install and configure `serve`.
RUN npm install -g serve
CMD serve -s build
EXPOSE 8000

# Install all dependencies of the current project.
COPY package.json /node-app/package.json
#COPY npm-shrinkwrap.json npm-shrinkwrap.json
# The -g switch installs the Express Generator globally on your machine so you can run it from anywhere.
RUN cd /node-app;npm install

# Copy all local files into the image.
COPY . /node-app

# Build for production. 正式機時,要解除註解重建images,不然會大大影響效能
# RUN cd /node-app; npm run build

2. 建立Docker Images

docker build -t react-test-docker .

3. 啟動Docker Container(註,因為沒有標記production build,所以要指定start的路徑,start後,要加上prefix參數,指定你的web app目錄)
sudo docker run -d \
            -v /mnt/nodejs-app/mesodashboard/antd-admin-dashboard/src:/node-app/src \
            -p 8000:8000 \
            –name react-test-server \
            react-test-docker \
            npm start --prefix /node-app
[記錄] python 3.x版安裝在CentOS 7(與python2.7併存)與安裝python虛擬環境

[記錄] python 3.x版安裝在CentOS 7(與python2.7併存)與安裝python虛擬環境

最近常常在多個環境中要建置python多版本相容的環境,主因是我們的專案是3.5版,但linux原生已經有2.7版本

$ wget https://www.python.org/ftp/python/3.5.x/Python-3.5.x.tgz

$ tar xzf Python-3.5.1.tgz
cd Python-3.5.1
./configure
make

$ sudo make altinstall
這個時候,我們可能都要安裝一些必要的套件,遇到套件相依的問題,我建議要安裝virtualenv

通常透過pip就可以進行安裝,只是若我們沒有root的權限的話,而sudo又認不得pip指令時(command not found)怎麼辦呢?

常見錯誤:PermissionError: [Errno 13] Permission denied: ‘/usr/local/lib/python3.5/site-packages/virtualenv.py’

 

以下一個解法提供記錄:

1.切換到user的home目錄~/

cd ~

2.編輯.bashrc檔

sudo nano ~/.bashrc

3.在最下面alias區塊貼上,python版本路徑依個人安裝狀況調整:

alias python3=’/usr/local/bin/python3.5′
alias pip3=’/usr/local/bin/pip3.5′
alias sudo=’sudo ‘

4.使alias變更生效

source .bashrc

 

透過virtualenv建立虛擬環境

PS..有些人可能會問,我們統一指定3.5版本了,為什麼還是要安裝虛擬環境呢?

我的想法主要是至少要將開發環境、測試環境、正式環境盡可能獨立。

開發的時候,像是可能會要嘗試新的套件安裝了新的函式庫或是導入ai模組套件等等

或是當我們必須handle不同版本的產品程式碼時,環境的一致性對於重現問題是有助於我們專注在產品程式碼本身的焦點上!

因此無論如何,都建議安裝!!這是個好習慣!

  1. sudo pip3 install virtualenv
  2. 在專案所存存的目錄(e.g. /home/user/projects/) 執行 $ virtualenv -p python3.5 xxxxxx
  3. 啟用 source xxxxxx/bin/activate

這樣,就可以用虛擬環境安裝好獨立的套件囉…

 

其中我有遇到在virtualenv安裝requirement.txt時,出現

Command “python setup.py egg_info” failed with error code 1 in /tmp/pip-build-vozt3r98/mysqlclient/

若你在centos的話,可以透過安裝mysql dev套件到linux上解決這個問題

yum install mariadb-devel

若是在Ubuntu的話,請參考之

apt-get install libmariadbclient-dev
HDFS的其他選擇-GlusterFS in Docker的安裝記錄

HDFS的其他選擇-GlusterFS in Docker的安裝記錄

有鑑於最早時,對於hadoop的hdfs建置後,雖然對於其提供hdfs的api方便性感到相當的便利,但是苦於結合docker後,始終於無法找到”跨實體主機”的cluster建置與hadoop的備份與還原機制。(若有好心人知道也麻煩指點指點),這一點對於維運議題是不可容忍的。因此現階段我必須去找一個hdfs的替代方案…必須考慮到hdfs的特性下,我們有找到幾套方案,大如ceph,小如hdfs的落地方案都有,像是orangefs, glusterfs。而本篇,針對其資源較多的glusterfs,進行試用與評估看看。

 

GlusterFS架構如下:

Read More Read More

[Docker] docker-compose定義檔範例

[Docker] docker-compose定義檔範例

當平台漸漸成形,對於外部套件、方案相依性定調以後,每次都要手動建置測試運行環境,即使docker已經把建置動作簡化到一行指令了,但是還是令人覺得瑣碎。

這個時候,docker-compose這個解決方案,大幅的簡化了我們的部署工作:傳送門

它僅需要配置服務定義檔(yml檔名),就可以跟現有的docker images整合,立即建置出所需的架構環境!

version: '2.1'

services:
  memcache:
    image: memcached
    ports:
      - "11211:11211"
    command: memcached -m 1024m

  hadoop:
    image: "sequenceiq/hadoop-docker:2.7.0"
    ports:
     - "8030:8030"
     - "8040:8040"
     - "8042:8042"
     - "8088:8042"
     - "19888:19888"
     - "49707:49707"
     - "50010:50010"
     - "50020:50020"
     - "50070:50070"
     - "5007:5007"
    command: /etc/bootstrap.sh -d

  cassandra:
    image: "cassandra"
    ports:
     - "9042:9042"
    volumes:
     - /mnt/cassandra/data:/var/lib/cassandra
 
  mariadb:
    image: mariadb
    ports:
     - "3306:3306"
    volumes:
     - /mnt/mariadb/data:/var/lib/mysql
     - /mnt/mariadb/config:/etc/mysql/conf.d
    restart: always
    environment:
       MYSQL_ROOT_PASSWORD: 525402040966518776
       MYSQL_USER: ap_user
       MYSQL_PASSWORD: jUqJ75aFbJEU
  
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    links:
      - mariadb
    environment:
      PMA_HOST: mariadb
      PMA_PORT: 3306
    ports:
     - "8088:80"

  my-python-app:
    depends_on:
     - mariadb
     - cassandra
     - hadoop
     - memcache
    image: python35-app
    volumes:
     - /mnt/python-app/src:/usr/src/app
    ports:
     - "12345:12345"
    command: /usr/src/app/run.sh

註:

1.首先是version,據我所知,目前大部分幾個月前安裝的docker,至少可以支持到2.1,但不見得能支持到3.0的版本。相關upgrade的議題,必須持續survey

2.不需要export的port就不要開,可以透過link或是建立network來進行容器互連

3.可以善用command、environment、volumn等docker支持的掛載與注入的方式來配置容器

4.可以透過現成已build好的image或是動態透過build dockerfile來定義服務。這一段上述例子沒有,有需要時再查使用方式

 

最後,啟動服務群指令(記得加-d,否則會被lock在shell,被script轟炸):

sudo docker-compose up -d

若要下架也很簡單(其他還有run、stop等指令可用)

sudo docker-compose down

 

WP Facebook Auto Publish Powered By : XYZScripts.com