zoukankan      html  css  js  c++  java
  • 容器基础(六): 应用程序容器化

    概述

    传统的一些服务器程序,通常是通过读配置文件的方式来读入参数,  如果要把程序容器化,通过配置文件读参就存在不方便的情况。现在以debian-python27为基础镜像, 以一个脚本程序为例来进行程序容器化改造! 改造前后的目录结构如下所示:

    linux:/app # tree
    .
    ├── original               # 初始代码目录, 模拟老的服务器程序, 日志文件位于log/server.log
    │   ├── server             # 服务端, 通过Dockerfile打包的镜像名称: original/server:v0.1
    │   │   ├── Dockerfile     # server程序打包Dockerfile
    │   │   ├── config.py      # 读取配置文件的辅助模块
    │   │   ├── ini
    │   │   │   └── config.ini # 配置文件
    │   │   └── server.py      # 监听并回送客户端发送的字符串! 需从ini/config.ini读取监听端口
    │   └── worker             # 客户端, 日志文件位于log/worker.log, 对应镜像: original/worker:v0.1
    │       ├── Dockerfile
    │       ├── config.py      # 读取配置文件的辅助模块, 和server/config.py相同
    │       ├── ini
    │       │   └── config.ini # 配置文件, 包含连接ip,port等
    │       └── worker.py      # 客户端程序, 读取ini/config.ini连接到server, 发送"hello, docker!"给server
    └── update                 # 经过改造的程序目录, 删除了读取配置文件, 其它文件和original目录下一致
        ├── server             # 改造后对应镜像: update/server:v0.1
        │   ├── Dockerfile
        │   └── server.py
        └── worker             # 改造后对应镜像: update/worker:v0.1
            ├── Dockerfile
            └── worker.py
    
    8 directories, 12 files
    linux:/app #

     初始程序代码如下:

     1 #!/usr/bin/python
     2 # -*- coding: utf-8 -*-
     3 
     4 import os
     5 import sys
     6 import ConfigParser
     7 
     8 class Config:
     9   def __init__(self, ini_path):
    10     self.config_ = ConfigParser.ConfigParser()
    11     self.config_.read(ini_path)
    12 
    13   def get_string(self, section, key, default_string_value):
    14     try:
    15       return self.config_.get(section, key)
    16     except Exception, e:
    17       return default_string_value
    18 
    19   def get_int(self, section, key, default_int_value):
    20     try:
    21       return int(self.config_.get(section, key))
    22     except Exception, e:
    23       return default_int_value
    /app/original/[server|worker]/config.py

      

     1 #!/usr/bin/python
     2 # -*- coding: utf-8 -*-
     3 
     4 import os
     5 import sys
     6 import time
     7 import socket
     8 import select
     9 import signal
    10 import threading
    11 from config import Config
    12 
    13 log_file = "log/server.log"
    14 
    15 def log(msg):
    16   if not os.path.exists("log"):
    17     os.mkdir("log")
    18   with open(log_file, "a") as wf:
    19     wf.writelines(
    20         time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) + 
    21         " [INFO]  " + msg + "
    ")
    22 
    23 def do_listen():
    24   cfg = Config("ini/config.ini")
    25   server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    26   server_socket.bind(('', cfg.get_int("listen", "port", 3000)))
    27   server_socket.listen(5)
    28   print 'Waiting for connection...'
    29   log('Waiting for connection...')
    30   server_socket.setblocking(0)
    31 
    32   rfd = [server_socket, ]
    33 
    34   while True:
    35     rlist, wlist, xlist = select.select(rfd, [], [])
    36     for sock in rlist:
    37       if sock == server_socket:
    38         newsock, addr = server_socket.accept()
    39         print '[+] %s connected' % str(addr)
    40         log('%s connected' % str(addr))
    41         rfd.append(newsock)
    42       else:
    43         data = sock.recv(1024)
    44         if data:
    45           sock.send(data)
    46         else:
    47           print '[-] %s closed' % str(sock.getpeername())
    48           log('%s closed' % str(sock.getpeername()))
    49           rfd.remove(sock)
    50           sock.close()
    51 
    52 def signal_handler(signum, frame):
    53   print '
    [-] signal(%d) received, exit!' % signum
    54   log('signal(%d) received, exit!' % signum)
    55   sys.exit(-1)
    56 
    57 if __name__ == '__main__':
    58   signal.signal(signal.SIGINT, signal_handler)  
    59 
    60   try:
    61     do_listen()
    62   except Exception, e:
    63     print e
    64     print '
    Exit'
    /app/original/server/server.py
     1 FROM jason/debian-python27:v1.0
     2 
     3 MAINTAINER jason<djsxut@163.com>
     4 
     5 RUN mkdir -p /original/server
     6 
     7 COPY . /original/server
     8 
     9 WORKDIR /original/server
    10 
    11 ENV PATH $PATH:/original/server
    12 
    13 ENTRYPOINT ["python", "server.py"]
    /app/original/server/Dockerfile
    1 [listen]
    2 port = 3000
    3 
    4 [log]
    5 level = LOG_INFO
    /app/original/server/ini/config.ini
     1 #!/usr/bin/python
     2 # -*- coding: utf-8 -*-
     3 
     4 import os
     5 import sys
     6 import time
     7 import socket
     8 import signal
     9 import threading
    10 from config import Config
    11 
    12 log_file = "log/worker.log"
    13 
    14 def log(msg):
    15   if not os.path.exists("log"):
    16     os.mkdir("log")
    17   with open(log_file, "a") as wf:
    18     wf.writelines(
    19         time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) +
    20         " [INFO]  " + msg + "
    ")
    21 
    22 def do_handler():
    23   cfg = Config("ini/config.ini")
    24   sc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    25   while True:
    26     try:
    27       sc.connect((cfg.get_string("server", "ip", "127.0.0.1"), 
    28                   cfg.get_int("server", "port", 3000)))
    29     except Exception, e:
    30       print '[-] connect failed...'
    31       log('connect failed...')
    32       time.sleep(5)
    33     else:
    34       break
    35 
    36   while True:
    37     # do something
    38     sc.send('hello, docker!')
    39     data = sc.recv(1024)
    40     if data:
    41       print '[+][recv] %s' % data
    42       log('[recv] %s' % data)
    43       time.sleep(5)
    44     else:
    45       print '[-] server closed, exit'
    46       log('server closed, exit')
    47       sc.close()
    48       break
    49 
    50 def signal_handler(signum, frame):
    51   print '
    [-] signal(%d) received, exit!' % signum
    52   log('signal(%d) received, exit!' % signum)
    53   sys.exit(-1)
    54 
    55 if __name__ == '__main__':
    56   signal.signal(signal.SIGINT, signal_handler)  
    57   try:
    58     do_handler()
    59   except Exception, e:
    60     print e
    61     print '
    Exit'
    /app/original/worker/worker.py
     1 FROM jason/debian-python27:v1.0
     2 
     3 MAINTAINER jason<djsxut@163.com>
     4 
     5 RUN mkdir -p /original/worker
     6 
     7 COPY . /original/worker
     8 
     9 WORKDIR /original/worker
    10 
    11 ENV PATH $PATH:/original/worker
    12 
    13 ENTRYPOINT ["python", "worker.py"]
    /app/original/worker/Dockerfile
    1 [server]
    2 ip = 127.0.0.1
    3 port = 3000
    4 
    5 [log]
    6 level = LOG_INFO
    /app/original/worker/ini/config.ini

    应用程序容器化方法

    一. 使用共享网络(配置--net=host), 配置文件通过数据卷挂载到容器

    优点是程序不用修改,缺点是隔离性减少。

    通过Dockerfile构建镜像,执行结果如下:

    linux:/app/original/server # docker run -d --rm --net=host -v /app/original/server/ini/:/original/server/ini:ro original/server:v0.1
    4211d8846cf21f62baf9beddb65dd795c47ed1495d3a47d7cb139c3d45db7963
    linux:/app/original/server # netstat -apn | grep 3000
    tcp        0      0 0.0.0.0:3000            0.0.0.0:*               LISTEN      11353/python      
    linux:/app/original/server # docker exec 4211d884 tail -f /original/server/log/server.log
    2018-11-19 08:26:17 [INFO]  Waiting for connection...
    2018-11-19 08:26:57 [INFO]  ('127.0.0.1', 43628) connected
    ^C
    linux:/app/original/server # netstat -apn | grep 3000
    tcp        0      0 0.0.0.0:3000            0.0.0.0:*               LISTEN      11353/python      
    tcp        0      0 127.0.0.1:43628         127.0.0.1:3000          ESTABLISHED 11395/python      
    tcp        0      0 127.0.0.1:3000          127.0.0.1:43628         ESTABLISHED 11353/python      
    linux:/app/original/server #
    
    
    linux:/app/original/worker # docker run -d --rm --net=host -v /app/original/worker/ini/:/original/worker/ini:ro original/worker:v0.1
    f7dbbcfb39b12f4c080c6854777d0f5cf3b173a79c4be5104e9d0f8a4e61471a
    linux:/app/original/worker # docker exec f7dbbcfb tail -f /original/worker/log/worker.log
    tail: unrecognized file system type 0x794c7630 for '/original/worker/log/worker.log'. please report this to bug-coreutils@gnu.org. reverting to polling
    2018-11-19 08:27:07 [INFO]  [recv] hello, docker!
    ^C
    linux:/app/original/worker #

    二. 参数通过传参传入(其它参数或者固化或者依然通过挂载方式传入容器)

    优点是隔离性好, 配置灵活, 对于示例程序,先通过传参传入域名, 然后通过DNS解析获取IP地址,对多机器部署能带来方便;缺点是程序需要改动。

    修改代码如下:

     1 #!/usr/bin/python
     2 # -*- coding: utf-8 -*-
     3 
     4 import os
     5 import sys
     6 import time
     7 import socket
     8 import select
     9 import signal
    10 import threading
    11 
    12 log_file = "log/server.log"
    13 
    14 def log(msg):
    15   if not os.path.exists("log"):
    16     os.mkdir("log")
    17   with open(log_file, "a") as wf:
    18     wf.writelines(
    19         time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) +
    20         " [INFO]  " + msg + "
    ")
    21 
    22 def do_listen(port):
    23   server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    24   server_socket.bind(('', port))
    25   server_socket.listen(5)
    26   print 'Waiting for connection...'
    27   log('Waiting for connection...')
    28   server_socket.setblocking(0)
    29 
    30   rfd = [server_socket, ]
    31 
    32   while True:
    33     rlist, wlist, xlist = select.select(rfd, [], [])
    34     for sock in rlist:
    35       if sock == server_socket:
    36         newsock, addr = server_socket.accept()
    37         print '[+] %s connected' % str(addr)
    38         log('%s connected' % str(addr))
    39         rfd.append(newsock)
    40       else:
    41         data = sock.recv(1024)
    42         if data:
    43           sock.send(data)
    44         else:
    45           print '[-] %s closed' % str(sock.getpeername())
    46           log('%s closed' % str(sock.getpeername()))
    47           rfd.remove(sock)
    48           sock.close()
    49 
    50 def signal_handler(signum, frame):
    51   print '
    [-] signal(%d) received, exit!' % signum
    52   log('signal(%d) received, exit!' % signum)
    53   sys.exit(-1)
    54 
    55 if __name__ == '__main__':
    56   try:
    57     port = int(sys.argv[2]) 
    58   except:
    59     print 'usage: server.py [-p listen_port]'
    60     log('usage: server.py [-p listen_port]')
    61     sys.exit(-1)
    62 
    63   signal.signal(signal.SIGINT, signal_handler)  
    64 
    65   try:
    66     do_listen(port)
    67   except Exception, e:
    68     print e
    69     print '
    Exit'
    /app/update/server/server.py
     1 FROM jason/debian-python27:v1.0
     2 
     3 MAINTAINER jason<djsxut@163.com>
     4 
     5 RUN mkdir -p /update/server
     6 
     7 COPY . /update/server
     8 
     9 WORKDIR /update/server
    10 
    11 ENV PATH $PATH:/update/server
    12 
    13 #EXPOSE 3000
    14 
    15 ENTRYPOINT ["python", "server.py"]
    16 
    17 /app/update/server/Dockerfile
    /app/update/server/Dockerfile
      1 #!/usr/bin/python
      2 # -*- coding: utf-8 -*-
      3 
      4 import os
      5 import sys
      6 import time
      7 import socket
      8 import signal
      9 import getopt
     10 import threading
     11 
     12 log_file = "log/worker.log"
     13 
     14 def log(msg):
     15   if not os.path.exists("log"):
     16     os.mkdir("log")
     17   with open(log_file, "a") as wf:
     18     wf.writelines(
     19         time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) +
     20         " [INFO]  " + msg + "
    ")
     21 
     22 class Handler:
     23   def __init__(self, domain, port):
     24     self.domain = domain
     25     self.port   = port
     26     self.sock   = None
     27 
     28   def get_socket(self):
     29     for res in socket.getaddrinfo(self.domain, self.port,
     30                                   socket.AF_INET, socket.SOCK_STREAM):
     31       family, socktype, proto, canonname, sa = res
     32       try:
     33         self.sock = socket.socket(family, socktype, proto)
     34         #self.sock.setblocking(0)
     35         #sock.settimeout(0.5)
     36         self.sock.connect(sa)
     37       except socket.error, msg:
     38         if self.sock != None:
     39           self.sock.close()
     40         self.sock = None
     41       else:
     42         return True
     43 
     44     return False
     45 
     46   def do_handler(self):
     47     while self.sock == None and self.get_socket() == False:
     48       print '[-] connect failed...'
     49       log('connect failed...')
     50       time.sleep(5)
     51 
     52     print '[+] %s connected' % str(self.sock.getpeername())
     53     log('[+] %s connected' % str(self.sock.getpeername()))
     54     while True:
     55       # do something
     56       self.sock.send('hello, docker!')
     57       data = self.sock.recv(1024)
     58       if data:
     59         print '[+][recv] %s' % data
     60         log('[recv] %s' % data)
     61         time.sleep(5)
     62       else:
     63         print '[-] server closed, exit'
     64         log('server closed, exit')
     65         self.sock.close()
     66         self.sock = None
     67         break
     68 
     69 def signal_handler(signum, frame):
     70   print '
    [-] signal(%d) received, exit!' % signum
     71   log('signal(%d) received, exit!' % signum)
     72   sys.exit(-1)
     73 
     74 def usage():
     75   print 'Usage: worker.py option' + 
     76         ' [-d domain]' + 
     77         ' [-p port]' +   
     78         ' [-h]'
     79 
     80 def get_params(argvs):
     81   domain = None
     82   port = None
     83   try:
     84     opts, args = getopt.getopt(argvs[1:], "hd:p:", [""])
     85     for op, value in opts:
     86       if op == "-d":
     87         domain = value
     88       elif op == "-p":
     89         port = int(value)
     90   except Exception, e:
     91     usage()
     92     sys.exit(-1)
     93     
     94   if domain == None or port == None:
     95     usage()
     96     sys.exit()
     97 
     98   return domain, port
     99 
    100 if __name__ == '__main__':
    101   signal.signal(signal.SIGINT, signal_handler)  
    102   domain, port = get_params(sys.argv)
    103 
    104   try:
    105     handler = Handler(domain, port)
    106     handler.do_handler()
    107   except Exception, e:
    108     print e
    109     print '
    Exit'
    /app/update/worker/worker.py
     1 FROM jason/debian-python27:v1.0
     2 
     3 MAINTAINER jason<djsxut@163.com>
     4 
     5 RUN mkdir -p /update/worker
     6 
     7 COPY . /update/worker
     8 
     9 WORKDIR /update/worker
    10 
    11 ENV PATH $PATH:/update/worker
    12 
    13 ENTRYPOINT ["python", "worker.py"]
    /app/update/worker/Dockerfile

      

    通过传参调用结果如下:

    linux:/app/update/server # docker run -d --rm --name server update/server:v0.1 -p 3000
    5868163fa574810269ca03f60b30aa473c9bed0a0f55e73678b648fd56f8a722
    linux:/app/update/server # docker exec 5868 tail -f /update/server/log/server.log
    tail: unrecognized file system type 0x794c7630 for '/update/server/log/server.log'. please report this to bug-coreutils@gnu.org. reverting to polling
    2018-11-19 08:11:08 [INFO]  Waiting for connection...
    2018-11-19 08:16:52 [INFO]  ('172.17.0.3', 59538) connected
    linux:/app/update/server #
    
    linux:/app/update/worker # docker run -d --rm --name worker --link server:server update/worker:v0.1 -d server -p 3000
    851ba3bc9b74ad022f2df9e4d9639c696485e4687e47197111f442e66de19c7d
    linux:/app/update/worker # docker exec 851ba tail -f /update/worker/log/worker.log
    2018-11-19 08:16:52 [INFO]  [+] ('172.17.0.2', 3000) connected
    2018-11-19 08:16:52 [INFO]  [recv] hello, docker!
    2018-11-19 08:16:57 [INFO]  [recv] hello, docker!
    linux:/app/update/worker # docker ps
    CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS               NAMES
    851ba3bc9b74        update/worker:v0.1   "python worker.py ..."   2 minutes ago       Up 2 minutes                            worker
    5868163fa574        update/server:v0.1   "python server.py ..."   8 minutes ago       Up 8 minutes                            server
    linux:/app/update/worker # docker exec server ss -a | grep 3000
    tcp    LISTEN     0      5                    *:3000                  *:*
    tcp    ESTAB      0      0           172.17.0.2:3000         172.17.0.3:59538
    linux:/app/update/worker # docker exec worker ss -a | grep 3000
    tcp    ESTAB      0      0           172.17.0.3:59538        172.17.0.2:3000
    linux:/app/update/worker #

    三. 参数通过环境变量传入

    具体参考容器基础(七): 使用docker compose部署程序

    Excellence, is not an act, but a habit.
    作者:子厚.
    出处:http://www.cnblogs.com/aios/
    本文版权归作者和博客园共有,欢迎转载、交流、点赞、评论,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

  • 相关阅读:
    理解javascript作用域及hosting机制
    angular分页指令
    python 设计模式之门面模式
    代码Rework中的反思
    python 设计模式之观察者模式
    python 设计模式之命令模式
    Django性能调优
    python 设计模式之MVC模式
    httperf+autobench测试web应用
    对工作的感悟
  • 原文地址:https://www.cnblogs.com/aios/p/9980726.html
Copyright © 2011-2022 走看看