不同语言开发的应用在制作 Docker 镜像的方法是不同的。对 Go 和 Java 来说,容器化改造是比较有友好的;但是对于 PHP、Python 这类脚本语言,就稍微有点复杂了。这里举的两个例子是 PHP 和 Python 的 HTTP 服务。
一、Nginx+PHP 提供 WEB 服务
LNMP 架构是非常常用的一种 webservice。这里的场景是使用 PHP 的 yaf 框架开发的应用进行容器化的改造。
按照以往的部署方式,我们是需要在一台或者多台服务器上部署 Nginx 和 PHP 两个服务。PHP 服务上,我们要根据需要安装 yaf 框架和一些 PHP 的拓展如 mysql、mongodb、redis 等;Nginx 服务则配置一些静态资源的信息和将动态请求转到 PHP 的默认 9000 端口处理。如果是这样的话,我们就相当于提交了两个镜像,使用 docker-compose 可以快速部署,但是在 Kubernetes 上就显得有点不合适。
1-1 基础镜像编译
经过一段时间的尝试,我们使用了 supervisor 这个进程管理工具,将 nginx 和 php-fpm 编译到一个镜像中。
Dockerfile
FROM php:7.2-fpm
# nginx
RUN apt-get -o Acquire::Check-Valid-Until=false update && apt install -y build-essential zlib1g-dev libpcre3 libpcre3-dev libssl-dev libxslt1-dev libxml2-dev libgeoip-dev openssl
RUN curl -L -o /tmp/nginx-1.18.0.tar.gz http://nginx.org/download/nginx-1.18.0.tar.gz \
&& tar xfz /tmp/nginx-1.18.0.tar.gz -C /tmp \
&& cd /tmp/nginx-1.18.0 \
&& ./configure --prefix=/usr/local/nginx --with-http_ssl_module \
&& make && make install \
&& rm -rf /tmp/nginx-1.18.0
COPY nginx.conf /usr/local/nginx/conf/nginx.conf
# PHP extensions
# phpredis 3.1.6
RUN curl -L -o /tmp/redis.tar.gz https://github.com/phpredis/phpredis/archive/3.1.6.tar.gz \
&& tar xfz /tmp/redis.tar.gz \
&& rm -r /tmp/redis.tar.gz \
&& mkdir -p /usr/src/php/ext \
&& mv phpredis-3.1.6 /usr/src/php/ext/redis \
&& docker-php-ext-install redis \
&& rm -rf /usr/src/php
# pdo_mysql
RUN cd /usr/local/bin \
&& ./docker-php-ext-install pdo_mysql
# mongodb
RUN curl -L -o /tmp/mongodb.tgz http://pecl.php.net/get/mongodb-1.5.2.tgz \
&& tar xfz /tmp/mongodb.tgz \
&& mkdir -p /usr/src/php/ext \
&& mv mongodb-1.5.2 /usr/src/php/ext/mongodb \
&& docker-php-ext-configure mongodb --with-mongodb-ssl \
&& docker-php-ext-install mongodb \
&& rm -rf /usr/src/php
# yaf
RUN curl -L -o /tmp/yaf-3.0.6.tgz http://pecl.php.net/get/yaf-3.0.6.tgz \
&& tar xfz /tmp/yaf-3.0.6.tgz \
&& mkdir -p /usr/src/php/ext \
&& mv yaf-3.0.6 /usr/src/php/ext/yaf \
&& docker-php-ext-install yaf \
&& rm -rf /usr/src/php
RUN apt-get -o Acquire::Check-Valid-Until=false update && apt-get install -y libmemcached-dev zlib1g-dev
# memcache
RUN curl -L -o /tmp/pecl-memcache-php7.tar.gz https://github.com/websupport-sk/pecl-memcache/archive/php7.tar.gz \
&& tar xfz /tmp/pecl-memcache-php7.tar.gz \
&& mkdir -p /usr/src/php/ext \
&& mv pecl-memcache-php7 /usr/src/php/ext/memcache \
&& docker-php-ext-install memcache \
&& rm -rf /usr/src/php
# yaf config
COPY docker-php-ext-yaf.ini /usr/local/etc/php/conf.d/docker-php-ext-yaf.ini
# supervisor
RUN rm -rf /var/lib/apt/lists/* && apt-get -o Acquire::Check-Valid-Until=false update && apt-get install supervisor -y
COPY supervisord.conf /etc/supervisord.conf
# start service
ENTRYPOINT ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"]
复制代码
supervisord.conf
; Sample supervisor config file.
; ..........
[program:php-fpm]
command=php-fpm
autostart=true
autorestart=true
priority=5
stdout_events_enabled=true
stderr_events_enabled=true
[program:nginx]
command = /usr/local/nginx/sbin/nginx -g 'daemon off;'
startsecs=0
autostart=true
autorestart=true
[include]
files = /etc/supervisor/conf.d/*.conf
复制代码
这里面有一些细节是需要关注的,比如 php-fpm 和 nginx 需要调成前台运行;nginx 和 php-fpm 的参数设置应该参考 Kubernetes POD 申请的 cpu、内存资源进行调整。
1-2 编写 Kubernetes 部署 yaml
业务是无状态工作负载。根据业务需求编写的 yaml 至少包括 deployment 和 service。程序使用到的配置可以本地取(configmap),也可以使用公用的配置中心。需要落地的文件或者日志则使用 pvc 即可(NAS 或者 NFS)。参考 yaml 如下:
---
# ----- Deployment --- #
apiVersion: apps/v1
kind: Deployment
metadata:
name: fxpd
spec:
replicas: 1
selector:
matchLabels:
k8s-app: fxpd
template:
metadata:
labels:
k8s-app: fxpd
spec:
containers:
- name: fxpd-nginx
image: hubstore.com.cn/webrelease/fxpd:latest
ports:
- containerPort: 80
volumeMounts:
- name: fxpd-configfile
mountPath: /opt/config/fxpd
volumes:
- name : fxpd-configfile
configMap:
name: fxpd-configfile
---
# --- Service --- #
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: fxpd
name: fxpd
namespace: default
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30005
selector:
k8s-app: fxpd
复制代码
kubectl create -f xxx.yaml # 默认在default命名空间下
复制代码
二、Python 提供 WEB 服务
Python 提供 Web 服务使用的是 Flask、FastAPI 之类的框架,使用 gunicorn 启动。
2-1 基础镜像编译
Python 的基础镜像没有 PHP 那么麻烦,基本上基于一个官方的版本即可。基础镜像要保证精简性和较长时间不变动。这里贴出一个 Python 写的服务 Dockerfile 文件。
FROM hubstore.com.cn/base/python:v3
RUN mkdir -p /opt/search
WORKDIR /opt/search
COPY . /opt/search
RUN yum install mysql-devel -y
RUN pip3 install -r requirements.txt -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
RUN pip3 install gunicorn
ENV LANG=zh_CN.UTF-8
ENV LC_ALL=zh_CN.UTF-8
CMD gunicorn -b 0.0.0.0:11000 -w 25 -t 600 startApi:app
复制代码
2-2 编写 Kubernetes 部署 yaml
deployment.yaml
---
# ----- Deployment --- #
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: api
namespace: webapp
labels:
app: api
version: v1
spec:
replicas: 2
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
version: v1
spec:
imagePullSecrets:
- name: hubstore
containers:
- name: api
image: hubstore.mycloud.com/webapp/api:1.0.190188fl
ports:
- containerPort: 80
resources:
limits:
cpu: '3'
memory: 10000Mi
requests:
cpu: 1500m
memory: 4000Mi
volumeMounts:
- name: api
mountPath: /opt/config/api
- name: cache-volume
mountPath: /dev/shm
volumes:
- name: cache-volume
emptyDir:
medium: Memory
sizeLimit: 2000Mi
- name : apilog
persistentVolumeClaim:
claimName: sfs-api-pvc
- name : api
configMap:
name: api
复制代码
这里面有个特殊的点,是配置了内存 mount 到/dev/shm。这是因为 gunicorn 运行过程中,master 和 worker 保持心跳的目录是/dev/shm。如果不使用内存类型的话,默认是磁盘目录。由于磁盘的效率远低于内存,运行一段时间就会出现 gunicorn 僵住,日志不断报 worker 进程退出。所以想正常在 Kubernetes 中使用 gunicorn,比如做这样的配置。
三、如何编写适合自己业务的 yaml
可以说,一开始使用 Kubernetes 的时候都在编写适合自己的 yaml。目前编写 Kubernetes 相关的 yaml 还没有带提示的编辑器,大部分时候都是自己根据官方的例子和自己积累的经验逐步添加的。
当然除此之外,AWS CDK for Kubernetes 是一个很不错的工具。Python SDK 以编码的形式操作 Kubernetes 资源,然后生成相关的 yaml 文件。虽然目前官方文档对于 Python SDK 这块说明不多,但是翻翻源码是可以找到常用的 k8s 资源的使用方法。我一般是根据这个生成半成品的 yaml 文件,然后再编辑成完整的 yaml 文件。还有,Helm 也是一个常用的工具。不过这块和 CI/CD 关联性比较大,我这里一般第一次项目上线会使用到。
评论