Internal Service in Kubernetes Cluster

In Kubernetes(K8s) cluster, 1 or more containers form a pod and every container in the pod can access other container’s port just like apps in the same local host. For example:

- pod1
  - nginx1
  - gunicorn1, port:8000

- pod2
  - nginx2
  - gunicorn2, port:8000

So nginx1 can access gunicorn1’s port using localhost:8000 and nginx2 can access gunicorn2, etc… However nginx1 can’t see gunicorn2 using localhost.

When it comes to cache, like redis, I would like a shared redis container(or cluster later) so less memory is wasted and the cache doesn’t need to be warmed for each pod. The structure will look like:

- pod1
  - nginx1
  - gunicorn1
- pod2
  - nginx2
  - gunicorn2
- pod3
  - redis

To grant both gunicorns to access redis, redis needs to be a service:

---
apiVersion: v1
kind: Service
metadata:
  name: redis-svc
  labels:
    app: redis
    role: cache
spec:
  type: NodePort
  ports:
    - port: 6379
  selector:
    app: redis
    role: cache

And the deployment to support the service looks like:

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-deploy
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: redis
        role: cache
    spec:
      containers:
        - name: redis
          image: redis:4
          resources:
            requests:
              memory: 200Mi
          ports:
            - containerPort: 6379

Then in the settings.py of the django app running in gunicorn container, redis can be accessed via ENVs setup by K8s:

CACHES = {
  "default": {
    "BACKEND": "django_redis.cache.RedisCache",
    "LOCATION": "redis://{0}:{1}/1".format(os.environ['REDIS_SVC_SERVICE_HOST'], os.environ['REDIS_SVC_SERVICE_PORT']),
    "OPTIONS": {
      "CLIENT_CLASS": "django_redis.client.DefaultClient"
    },
  }
}

🙂

Python+Django笔记之三

关于urls.py

假设test项目下有一个应用模块notes,那么:

test/urls.py:

from django.conf.urls.defaults import *

urlpatterns = patterns(‘ ‘,

(r’^notes/’, include(‘notes.urls’)),

)

这样所有匹配/notes/*的访问将转交给notes/urls.py处理。

test/notes/urls.py:

urlpatterns = patterns(‘ ‘,

url(r’^list/$’, views.list_notes, name=’note_list’),

url(r’^(?P<note_id>\d+)/$’, views.show_note, name=’note_show’),

)

首先,由于需要反向解析(页面名称->URL),所以要使用url(),而不是原来的tuple。对应url(r’^list/$’, views.list_notes, name=’note_list’), 将匹配/notes/list/的访问指向给views.py中的list_notes()方法,并将’note_list’名称反向解析到/notes/list/的URL,以便在webapp中生成链接。

第二行中的(?P<note_id>\d+)是一个参数定义,其中note_id是参数名,\d+说明此参数是一个数字。其实起到的作用就是将/note/123/这样的url解释成一个调用show_note(request, note_id=123)。如果参数不是数字而是字词,那么\d+应该改为\w+,具体定义可以参考regex规则。

Python+Django笔记之二

一个简单的select * 操作的Django方式:

首先配置urls.py

from django.conf.urls.defaults import *
from misc import views

urlpatterns = patterns(‘ ‘,
(r’^notes/$’, views.list_notes),
)

其中r’^notes/$’是regex,用于匹配url;views.list_notes是views.py中的list_notes 方法。

然后写list_notes:

def list_notes(request):
notes = Note.objects.order_by(‘-created_date’)
conduit = {
‘notes’: notes
}
return render_to_response(
‘misc/list.html’,
conduit,
context_instance = RequestContext(request),
)

其中conduit这个词来自Mass Effect 🙂

最后就是把上面的misc/list.html写出来:

{% extends “base.html” %}

{% block title %}List Notes – {{ block.super }}{% endblock %}

{% block body %}
{% if notes %}
<ul>
{% for note in notes %}
<li>{{ note.created_date }} : {{ note.message }}</li>
{% endfor %}
</ul>
{% else %}
<p>No notes yet.</p>
{% endif %}
{% endblock %}

这样用浏览器访问类似http://localhost:8000/app/notes的url时,全部notes应该就列出来了,并按时间倒序排列。

Python+Django笔记之一

副标题:PGB: Project Galactica Board 启动

从无到有启动一个Django project(Django发音类似Jungle),以pgb为例:

>django-admin.py startproject pgb

设置数据库连接:

>vim pgb/settings.py

DATABASE_ENGINE = ‘mysql’           # ‘postgresql_psycopg2’, ‘postgresql’, ‘mysql’, ‘sqlite3’ or ‘oracle’.
DATABASE_NAME = ‘pgb’             # Or path to database file if using sqlite3.
DATABASE_USER = ‘root’             # Not used with sqlite3.
DATABASE_PASSWORD = ‘xxxxxx’         # Not used with sqlite3.

建立一个app(app可以看作项目的一个模块,一个app是一系列相关功能的集合,而且可以被多个project共用):

>cd pgb
>python manage.py startapp appname

添加数据模型:编辑app/models.py,以Profile为例(缩进显示有问题,不过简单的class,凑合看吧),

class Profile(models.Model):
user = models.ForeignKey(User, unique=True)
name = models.CharField(max_length=20)
hp = models.IntegerField()
ep = models.IntegerField()
gp = models.IntegerField()
level = models.IntegerField()
title = models.CharField(max_length=100)
guild = models.ForeignKey(‘Guild’, related_name=’members’, null=True, blank=True)

def  __unicode__(self):
return self.user.username + ‘.’ + self.name

注:上面的’Guild’之所以要用引号,是为了解决环状引用的问题(circular model reference),因为Python是只有runtime check的,前面class引用后面定义的class就会出错。
注2:null=True 是针对DB SQL的NULL,blank=True是针对admin app的post validation。

开启Django著名的admin:

  1. 在urls.py中uncomment(这词有办法翻译么?)相关admin的几行
  2. 在app文件夹添加admin.py,内容类似如下:
    from django.contrib import admin
    from pgb.arena.models import Profileadmin.site.register(Profile)

为DB生成Tables:

>python manage.py syncdb

重新生成DB Tables,数据被销毁:

>python manage.py reset appname

启动测试环境:

>python manage.py runserver [port]

第一篇完。