kubernetes を使ってローカルで web アプリを開発できる環境を整えてみた
React, Flask, PostgreSQL の構成で作成した
構成
マイクロサービスといわれるらしい
利用イメージとしては
react(計算結果の描画) <==> flask(計算、dbの値操作) <==> postgresql(計算データや計算結果を保存)
こんな感じだろうか 各サービスはレプリカ数など独立して変更できるよう 各々 deployment として作成し、service で相互通信を行う
ディレクトリ構成はこんな感じ
tree -L 1
.
├── micro-database
├── kube-deploy.yml
├── kube-start.sh
├── micro-api
└── micro-app
deployment 作成
起動用スクリプト
任意の名前で
docker build
#/bin/sh
docker build -t api -f ./micro-api/Dockerfile ./micro-api
docker build -t app -f ./micro-app/Dockerfile.dev ./micro-app
kubectl apply -f kube-deploy.yml
kubernetes コンフィグ
Namespace と、各 Deployment、Service を作成
apiVersion: v1
kind: Namespace
metadata:
name: micro
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: micro-app
namespace: micro
spec:
replicas: 1
selector:
matchLabels:
name: micro-app
template:
metadata:
labels:
name: micro-app
spec:
containers:
- name: app
image: app
ports:
- containerPort: 3000
command: ["npm", "run", "start"]
imagePullPolicy: IfNotPresent
env:
- name: REACT_APP_API_URL
value: "http://127.0.0.1:5000"
- name: CHOKIDAR_USEPOLLING
value: "true"
volumeMounts:
- mountPath: /app/src
name: app-src
volumes:
- name: app-src
hostPath:
path: "絶対パス"/micro-app/src
type: DirectoryOrCreate
---
kind: Service
apiVersion: v1
metadata:
labels:
name: micro-app
name: micro-app-svc
namespace: micro
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 3000
targetPort: 3000
selector:
name: micro-app
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: micro-api
namespace: micro
spec:
replicas: 1
selector:
matchLabels:
name: micro-api
template:
metadata:
labels:
name: micro-api
spec:
containers:
- name: api
image: api
ports:
- containerPort: 5000
command: ["sh", "/app/src/server_config/start_uwsgi.sh"]
imagePullPolicy: IfNotPresent
env:
- name: POSTGRES_URL
value: "micro-db-svc:5432"
- name: POSTGRES_USER
value: "postgres"
- name: POSTGRES_PW
value: "postgres"
- name: POSTGRES_DB
value: "postgres"
volumeMounts:
- mountPath: /app/src
name: api-src
volumes:
- name: api-src
hostPath:
path: "絶対パス"/micro-api
type: DirectoryOrCreate
---
kind: Service
apiVersion: v1
metadata:
labels:
name: micro-api
name: micro-api-svc
namespace: micro
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 5000
targetPort: 5000
selector:
name: micro-api
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: micro-db
namespace: micro
spec:
replicas: 1
selector:
matchLabels:
name: micro-db
template:
metadata:
labels:
name: micro-db
spec:
containers:
- name: db
image: postgres:10
ports:
- containerPort: 5432
imagePullPolicy: IfNotPresent
env:
- name: POSTGRES_USER
value: "postgres"
- name: POSTGRES_PASSWORD
value: "postgres"
- name: POSTGRES_DB
value: "postgres"
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: db-data
- mountPath: /docker-entrypoint-initdb.d
name: db-init
volumes:
- name: db-data
hostPath:
path: "絶対パス"/micro-database/data
type: DirectoryOrCreate
- name: db-init
hostPath:
path: "絶対パス"/micro-database/init
type: DirectoryOrCreate
---
kind: Service
apiVersion: v1
metadata:
labels:
name: micro-db
name: micro-db-svc
namespace: micro
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 5432
targetPort: 5432
selector:
name: micro-db
sh ./kube-start.sh
...
Sending build context to Docker daemon 2.189GB
...
service "micro-db-svc" created
pod 間通信
ネットワーク(service)の設定が難しかったので、わかったことを残しておく
-
同じネームスペースであれば、service の名前(micro-db-svc)で名前解決ができる
-
異なるネームスペースであれば、service の名前+namespace(micro)の名前で名前解決できる
また、ホスト PC から localhost で繋げるようにするには type を
LoadBalancer
接続確認
app->api
api
flask api には uwsgi を入れて起動する(UWSGI)
[uwsgi]
master = true
base = /app/src
chdir = /app/src
# filename
module = server
callable = app
http-socket = :5000
processes = 1
threads = 2
uid = uwsgiusr
gid = uwsgiusr
logto = ./log/uwsgi.log
pidfile = ./log/uwsgi.pid
pcre = true
vacuum = true
die-on-term = true
# auto reload
py-autoreload = 1
wsgi-disable-file-wrapper = true
uwsgi --ini /app/src/server_config/uwsgi.ini &
tail -f /app/src/log/uwsgi.log
FROM python:3.6
RUN mkdir -p /app/src
WORKDIR /app/src
COPY requirements.txt /app/src
RUN pip3 install -r requirements.txt
## set uwsgi
RUN apt-get install -y libpcre3 libpcre3-dev
RUN useradd -r -s /bin/fslse uwsgiusr
USER uwsgiusr
COPY . /app/src
EXPOSE 5000
CMD sh ./server_config/start_uwsgi.sh
flask
flask-cors
Flask-SQLAlchemy
uwsgi
from __future__ import print_function
from __future__ import unicode_literals
from flask import Flask, jsonify
from flask_cors import CORS
app = Flask(__name__)
# cors対策
cors = CORS(app)
def main():
data = [
{"name":"山田",
"age":30}
]
return jsonify({
'status': 200,
'data': data
})
@app.route('/test', methods=['GET'])
def index():
return main()
if __name__ == '__main__':
print('url map', app.url_map)
app.run(host='0.0.0.0', port=5000, debug=True)
app
react の構築方法は割愛する
app 側では、api へリクエストを投げて返ってきた値を render する リクエストには axios を用いる
FROM node:8
ENV NODE_ENV=development
RUN mkdir /app
WORKDIR /app
ADD . /app
RUN npm install
EXPOSE 3000
CMD ["npm", "run", "start"]
import React, { Component } from 'react'
import axiosBase from 'axios'
export default class Home extends Component {
state = {
result: null
}
componentDidMount() {
this.getFromApi()
}
getFromApi() {
const getTest = (text) => {
// ブラウザからアクセスするので
// 127.0.0.1:5000
let url = process.env.REACT_APP_API_URL
let ax = axiosBase.create({
baseURL: url,
responseType: 'json'
})
ax.get('/test').then((res) => {
this.setState({
result: res.data.data
})
}).catch((err) => {
return err
})
}
getTest()
}
renderResult() {
let result = this.state.result
if (result === null) {
return
}
return (
<div>
<p>name: {result[0].name}</p>
<p>age: {result[0].age}</p>
</div>
)
}
render() {
return (
<div>
{this.renderResult()}
</div>
)
}
}
api から取得した値が表示される
削除
kubectl delete -f kube-deploy.yml
感想
kubernetes でローカル開発環境を整えるには、docker build を行う手間が加わるためそこが面倒に感じた ローカルで使うには
docker-compose
kubernetes 発環境〜本番環境まで一貫してできるといいかなと思ったが、本番環境向けにするための設定をもう少し考えないといけない。もうちょっと楽な方法があれば調べていきたい
困ったところ
hostpath は絶対パスでないといけない
kubectl describe pods 'POD_NAME'
...
"~/" includes invalid characters for a local volume name, only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed. If you intended to pass a host directory, use absolute path
相対パスや、変数を設定してコンフィグファイル内で使いたいが、方法がわからず。
参考
pod 間の名前解決について詳しい