Fejlesszünk konténerben II.
A második részben egy kis alkalmazást fogunk összerakni külön kliens és szerver oldali konténerrel.
El is érkeztünk a második részhez, amiben egy kis alkalmazást fogunk összerakni külön kliens és szerver oldali konténerrel. Aki esetleg lemaradt volna az előző részről, itt tudja pótolni.
A szerver oldalt a változatosság kedvéért most a Python fogja képviselni a Flask keretrendszerrel és az SQLAlchemy ORM-mel. A kliens oldalon a Mithril.js keretrendszer lesz segítségünkre a JavaScript-ben, a CSS-t pedig a Milligram szolgáltatja. A függőségek beszerzéséről az npm gondoskodik és végül az egészet a Webpack segítségével fogjuk egybegyúrni. A kliens oldal szereti a sok szereplőt. No de vágjunk is bele.
Az adatbázis
Egy hasonló példával fejeztük be az előző részt, úgyhogy ebben nem lesz sok újdonság:
docker-compose.yml
version: "3"
volumes:
database-data:
services:
database:
image: mysql:5.7
volumes:
- database-data:/data
environment:
MYSQL_ROOT_PASSWORD: test
MYSQL_DATABASE: test
MYSQL_USER: test
MYSQL_PASSWORD: test
Ha valamilyen oknál fogva közelebbről is meg szeretnénk vizsgálni ezt az adatbázist és a tartalmát, azt a
docker-compose run --rm database mysql -hdatabase -utest -ptest test
paranccsal tehetjük meg. Amennyiben ez galád módon olyasmire panaszkodik, hogy nem ismeri a "database" host-ot, akkor futtassunk egy ilyen parancsot is és próbáljuk újra:
docker-compose start database
A szerver oldal
A Python csomagkezelés egy kicsit eltér a PHP-ban vagy Node.js-ben megszokottól. Docker nélkül valószínűleg Virtualenv-et használnánk és azon belül telepítenénk a függőségeket, de így a legegyszerűbb az lesz, ha a Dockerfile
-ban adjuk meg és a konténer build-elése során települnek majd.
server/Dockerfile
FROM python:3.6-alpine
RUN pip install Flask SQLAlchemy Flask-SQLAlchemy MySQL-Connector
WORKDIR /app/src
EXPOSE 5000
CMD ["flask", "run", "--host=0.0.0.0"]
A Flask alkalmazás az 5000-es porton fog alapból figyelni, úgyhogy ezt jelezzük a Docker felé is. A compose fájl services
része pedig a következőkkel egészül ki:
docker-compose.yml
server:
build: ./server
depends_on:
- database
volumes:
- ./server:/app
environment:
PYTHONDONTWRITEBYTECODE: 1
FLASK_APP: /app/src/app.py
DATABASE_URI: mysql+mysqlconnector://test:test@database/test
Környezeti változók segítségével megmondjuk a Python-nak, hogy ne generáljon .pyc
és .pyo
fájlokat, a flask run
-nak, hogy hol találja az alkalmazásunkat, az alkalmazásunknak pedig azt, hogy hogyan tud csatlakozni az adatbázishoz.
A teljes alkalmazás kódjától megkímélem az olvasókat, az a kapcsolódó GitHub repo-ban megtekinthető az összes korábbi példával együtt. Egy apró részletet emelnék csak ki:
server/src/app.py
# az alkalmazás többi részének a helye
if __name__ == '__main__':
db.create_all()
Ez egy kis egyszerűsítés, hogy könnyebben létre tudjuk hozni az adatbázis szerkezetet a frissen felhúzott MySQL-ben, amit így az alábbi módon tudunk megtenni:
docker-compose run --rm server python -m app
A kliens oldal
Hirtelen nem is tudom hol kezdjem, lesz dolgunk bőven. Talán essünk túl gyorsan a függőségeken:
client/package.json
{
"devDependencies": {
"babel-core": "^6.23.1",
"babel-loader": "^6.3.2",
"babel-preset-es2015": "^6.22.0",
"css-loader": "^0.26.1",
"milligram": "^1.3.0",
"mithril": "^1.0.1",
"style-loader": "^0.13.1",
"webpack": "^2.2.1",
"webpack-dev-server": "^2.3.0"
}
}
Az első változatban még React-ot használtam, a hozzá tartozó babel csomaggal, egy külön HTTP klienssel és még Less-t is fordítottam.
Aztán megpróbáltam egyszerűsíteni egy kicsit a dolgokon, aminek ez lett az eredménye. Így a client
könyvtárban kiadott npm install
parancs már csak a fél világot fogja letölteni nekünk, nem az egészet. Amíg ez megtörténik, térjünk át a webpack konfigurálására.
client/webpack.config.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist/'
}
};
Nem kell megijedni, ez nem a teljes fájl. Ennyivel nem ússzuk meg. Első körben csak megmondjuk a webpack-nak, hogy hol kezdődik az alkalmazásunk és hova szeretnénk tenni a végterméket. A következő lépésben beállítjuk, hogy mit kezdjen a CSS és JS fájlokkal:
client/webpack.config.js
module.exports = {
// a korábbi beállítások helye
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.js$/,
use: [
{ loader: 'babel-loader', options: { presets: [ 'es2015' ] } }
]
}
]
}
};
Ebben a formában a CSS is a JS kódba fog belekerülni, ami azt eredményezi, hogy az oldal CSS nélkül jelenik meg egy pillanatra a betöltődés során. Egy példaalkalmazásnál azt hiszem ezzel együtt lehet élni, de még több függőség behúzásával természetesen ez a probléma is megoldható.
Végül, de nem utolsó sorában hátra van még a dev szerver konfigurálása. Itt van egy olyan huncutság, hogy a /api/
kezdetű kéréseket átdobjuk a Python szervernek, hogy ő kezdjen velük valamit.
client/webpack.config.js
module.exports = {
// a korábbi beállítások helye
devServer: {
host: '0.0.0.0',
proxy: {
'/api/*': {
target: 'http://server:5000',
pathRewrite: { '^/api' : '' }
}
}
}
};
Ha szerencsénk van, mostanra végzett az npm install
, úgyhogy át is térhetünk a dolgok konténeres részére. Végül is ez lenne a fő téma.
client/Dockerfile
FROM node:6.9-alpine
WORKDIR /app
EXPOSE 8080
CMD ["/app/node_modules/.bin/webpack-dev-server"]
Semmi extra, a webpack dev szerverét fogjuk futtatni a konténerben, amit a 8080-as portra konfiguráltunk be az előző lépésekben. Természetesen a compose fájl services
részét is ki kell még egészíteni.
docker-compose.yml
client:
build: ./client
depends_on:
- server
ports:
- 8080:8080
volumes:
- ./client:/app
A kliens konténer függ a szerver konténertől, hogy a webpack dev szerverben beállított proxy-zás tudjon működni. A host 8080-as portját pedig bekötjük a konténer 8080-as portjára. Ezt a szerver konténer esetén nem volt szükséges, mivel csak a kliens konténeren keresztül fogunk vele kommunikálni.
Az alkalmazás kódtól itt is eltekintenék, a repóban megtalálható. Ha minden jól ment, egy docker-compose up
kiadása után a http://127.0.0.1:8080/-t meglátogatva valami ilyesmit kellene látnunk:
Ezzel a végére is értünk a második résznek. A következő részben a Docker Compose néhány kevésbé szokványos felhasználását fogjuk megvizsgálni.