Kuzunoha-NEのブログ

プログラミングなどの勉強をしてます

【Linux】Bashのバックグラウンドで実行する

こんばんは、葛の葉です。

さて、今回はLinuxでバックグラウンドモードで起動する方法をお伝えします。

flaskアプリを作る

以下のコードをapp.pyとして作成する。

from flask import Flask
app = Flask(__name__)


@app.route("/")
def index():
    return "Hello World!"

&を使う

例えばflaskアプリがあるとして、flask runで通常通り実行すると以下のようになります。

[kuzunoha@:13:44:38:~]$ flask run
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

こうなるとキーを受け付けません。

flask run &として実行をすればバックグラウンドで起動します。

[kuzunoha@:13:40:16:~]$ flask run &
[1] 11422
[kuzunoha@:13:41:06:~]$ * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
■ <- EnterKeyを押す
[kuzunoha@:13:42:21:~]$

コマンドが返ってきているようですがよくわからないのでエンターキーを押してみましょう。PSが表示されどういうことかがわかりやすくなります。

バックグラウンドで動いているのでCurlで確認できます。

[kuzunoha@:13:45:45:~]$ curl http://127.0.0.1:5000/
127.0.0.1 - - [19/Jul/2019 13:47:28] "GET / HTTP/1.1" 200 -
Hello World![kuzunoha@:13:47:28:~]$

バックグラウンドで動いていたコマンドを元に戻す

fgと打てばコマンドが戻ってきます。

[kuzunoha@:13:47:28:~]$fg
flask run

flask runのコマンドに戻ってきたのでCtrl + cで停止します。

^C[kuzunoha@:13:49:29:~]$

起動中のバックグラウンドコマンドを見るには

jobsコマンドを打ちます。

[kuzunoha@:13:49:29:~]$flask run &
[1] 12423
[kuzunoha@:13:50:16:~]$jobs
[1]+  実行中               flask run &

また、バックグラウンドで起動しているコマンドを削除をするにはkillを使います。kill %nとしてjobsで表示された[n]を指定します(nはjob番号といいます)。

[kuzunoha@:13:50:18:~]$kill %1
[1]+  Terminated              flask run
[kuzunoha@:13:53:30:~]$jobs
[kuzunoha@:13:53:39:~]$ <- jobに表示できるものがないため何も返されない

【TypeScript】mochaとchaiでユニットテストする

こんばんは、葛の葉です。

前回からTypeScriptを使っています。

kuzunoha-ne.hateblo.jp

今回はTypeScriptでUnittestを行いたいと思います。

フォルダ構成

├── src
│   └── fizzbuzz.ts
└── test
    └── fizzbuzz.test.ts

FizzBuzzをつくる

srcディレクトリ内でfizzbuzz.tsという名前のプログラムを作成します。

export class Numbers {
    fizz: number = 3;
    buzz: number = 5;
    fizzbuzz: number = this.fizz * this.buzz;
}

export class FizzBuzz {
    public function: string; check(params: number) {
        if (params % 15 == 0) {
            return "FizzBuzz!";
        } else if (params % 3 == 0) {
            return "Fizz!";
        } else if (params % 5 == 0) {
            return "Buzz!";
        } else {
            return params.toString()
        };
    };
}

export class とはモジュールとして他のJSから取得出来るようにするための記述?のようですね。まだこの辺りも含めて、JSとTSがよくわかっていないというのが現状です。モウシワケナイ。

tsc fizzbuzz.tsとして、コンパイルしましょう。

fizzbuzz.jsが出来上がります。

"use strict";
exports.__esModule = true;
var Numbers = /** @class */ (function () {
    function Numbers() {
        this.fizz = 3;
        this.buzz = 5;
        this.fizzbuzz = this.fizz * this.buzz;
    }
    return Numbers;
}());
exports.Numbers = Numbers;
var FizzBuzz = /** @class */ (function () {
    function FizzBuzz() {
    }
    FizzBuzz.prototype.check = function (params) {
        if (params % 15 == 0) {
            return "FizzBuzz!";
        }
        else if (params % 3 == 0) {
            return "Fizz!";
        }
        else if (params % 5 == 0) {
            return "Buzz!";
        }
        else {
            return params.toString();
        }
        ;
    };
    ;
    return FizzBuzz;
}());
exports.FizzBuzz = FizzBuzz;

テストプログラムをつくる

npmでmochaというライブラリとchaiというライブラリを入手します。この辺りもちょっとよくわからないので、間違ったインストール方法だったら申し訳ないです。

npm install mocha chai --save-dev

npm install @types/{mocha,chai} --save-dev

testディレクトリ内でfizzbuzz.test.tsという名前のプログラムを作ります。

import * as chai from "chai"

import { FizzBuzz } from "../src/fizzbuzz"
import { Numbers } from "../src/fizzbuzz"

describe("FizzBuzzのテスト", () => {
    it("check関数のテスト", () => {
        let fizzbuzz = new FizzBuzz();
        let nums = new Numbers();
        chai.assert.equal(fizzbuzz.check(nums.fizz), "Fizz!");
        chai.assert.equal(fizzbuzz.check(nums.buzz), "Buzz!");
        chai.assert.equal(fizzbuzz.check(nums.fizzbuzz), "FizzBuzz!");
        chai.assert.equal(fizzbuzz.check(4), "4");
    })
})

describeitとはmochaというテストフレームワークの記述になります。describeがテスト内容の大項目で、itが小項目に当たるといった感じでしょうか。

mochajs.org

chaiは多種のアサーション機能を備えたライブラリのようです。

www.chaijs.com

tsc fizzbuzz.test.tsとしてコンパイルします。

fizzbuzz.test.jsが出来上がります。

"use strict";
exports.__esModule = true;
var chai = require("chai");
var fizzbuzz_1 = require("../src/fizzbuzz");
var fizzbuzz_2 = require("../src/fizzbuzz");
describe("FizzBuzzのテスト", function () {
    it("check関数のテスト", function () {
        var fizzbuzz = new fizzbuzz_1.FizzBuzz();
        var nums = new fizzbuzz_2.Numbers();
        chai.assert.equal(fizzbuzz.check(nums.fizz), "Fizz!");
        chai.assert.equal(fizzbuzz.check(nums.buzz), "Buzz!");
        chai.assert.equal(fizzbuzz.check(nums.fizzbuzz), "FizzBuzz!");
        chai.assert.equal(fizzbuzz.check(4), "4");
    });
});

ユニットテストを実施する

testディレクトリがカレントディレクトリなら以下のコマンドを打ちます。

mocha fizzbuzz.test.js

以下のようにログが出力されます。

[kuzunoha@:23:44:18:~]$mocha fizzbuzz.test.js 


  FizzBuzzのテスト
    ✓ check関数のテスト


  1 passing (7ms)

【TypeScript】列挙型をTypeScriptで使う

こんばんは、葛の葉です。

現在、TypeScriptを勉強しています。

Enumesをちょっとだけ勉強しました。

ディレクトリ構成

├── app
│   └── enum.ts
└── enum.html

enum.html

/*./app/enum.tsじゃないよ*/
<script src="./app/enum.js"></script>

TypeScriptを書く

app/enum.tsをいじる。

enum Hoge {
    foo,
    bar,
    hoge,
    piyo,
    fuga
}

let tmp = Hoge.foo

console.log(tmp)

tsc app/enum.tsとしてjsにビルドします。そうするとapp/enum.jsが出来上がります。

app/enum.js

var Hoge;
(function (Hoge) {
    Hoge[Hoge["foo"] = 0] = "foo";
    Hoge[Hoge["bar"] = 1] = "bar";
    Hoge[Hoge["hoge"] = 2] = "hoge";
    Hoge[Hoge["piyo"] = 3] = "piyo";
    Hoge[Hoge["fuga"] = 4] = "fuga";
})(Hoge || (Hoge = {}));
var tmp = Hoge.foo;
console.log(tmp);

Chromeで表示してみる。

console.logの出力結果を見たいのでenum.htmlChromeで開き、F12キーを押します。

Consoleタブを開くと画像のような感じに出力されていると思います。

f:id:Kuzunoha-NE:20190704153856j:plain
コンソールログで0と表示されている。

let tmp = Hoge.foofooとしたところが、数字0に相当するからです。

let tmp = Hoge.barにすると1と表示されます。

enum.tsをもう少しいじる。

TypeScriptといえば型宣言なのかなぁと思います。

tmpという変数に代入できる値を数字だけにしてみましょう。

enum Hoge {
    foo,
    bar,
    hoge,
    piyo,
    fuga
}

/* :numberは数値のみ代入を受け入れるということ */
let tmp: number = Hoge.foo

console.log(tmp)

これでもtsc enum.tsによってbuildは通るのです。

enumってただの数字ですから。

また、TypeScriptにはオブジェクト型宣言というのが使えますのでこちらを使ってみます。

enum Hoge {
    foo,
    bar,
    hoge,
    piyo,
    fuga
}

/* :HogeはHogeというオブジェクトのみ受け付けるということ */
let tmp: Hoge = Hoge.foo

console.log(tmp)

これでも通ります。

オブジェクト型宣言の方が用途がわかりやすい気がしますので、私はこっちを選びたいなぁと思いました。

ちなみに

let tmp: Hoge = Hoge.foo
tmp = 5

としても

let tmp: number = Hoge.foo
tmp = 5

としても

いずれもビルドは通りました。

ちょっとこんがらがってきたので、勉強を続けたいと思います。

【Flask】Configについて

とても参考になったサイト

https://qiita.com/nanakenashi/items/e272ff1aafb3889230bc

https://www.subarunari.com/entry/2018/03/17/いまさらながらFlaskについてまとめる_〜Configuration〜

http://flask.pocoo.org/docs/1.0/config/

https://damyanon.net/post/flask-series-configuration/

Configを使おうというお話。

あなたは何らかのデータベースサーバーやあるいはGoogleSpreadSheetsと連携してプログラムを書いていることでしょう。

書いている内容としてはこんな感じでしょうか。

gss_url = "http://google.****.com"

さて、本番と開発をわけた開発をするとした場合、このgss_urlの値をツドツド変更しているのかもしれません。あるいは、コメントアウトして使っているのかもしれません。

# 本番
# gss_url = 'http://google.****.com'
# 開発
gss_url = 'http://test.google.****.com'

ただ、どちらかといえば本番時はhttp://google.****.com、そうでないときはhttp://test.google.****.comと条件分岐で指定してあげたいところです。

if HONBAN == True:
    gss_url = 'http://google.****.com'
else:
    gss_url = 'http://test.google.****.com'

また、Flaskなら開発環境でDEBUGモードで開発していると思います。本番でDEBUGモードが起きるのはセキュリティ的によくないでしょう。ただ、その複数の切り替えを手動で行っていると絶対忘れが発生して事故るでしょう。

app.run(debug=True)
# app.run()

開発環境、本番環境によって変えたい項目は沢山あるはずです。もう一つテスト環境というものがあると思います。

テスト環境はテスト用のデータが挿入された環境であることが望ましいかも知れませんし、あるいはデータそのものが全くない環境が望ましいものかもしれません。

テスト環境1
テスト環境2
開発環境
本番環境

それぞれの状況でアクセスするDBのアドレスやGSSのアドレスが切り替わるような仕組みがあったら便利ですね。今回は環境変数を使って切り替わりを実施できるような仕組みを考えます。

Flaskのデバッグモードとconfigについて

Flaskクラスのインスタンスにはconfigというものがあります。configはdictionaryとなっています。

app.pyとして以下を作成します。

from flask import Flask
app = Flask(__name__)
print(app.config)

python app.pyとして起動すると以下のようになります。

[kuzunoha@:11:09:05:~/sand_box/flaskconfig]$python app.py 
<Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(0, 43200), 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}>

また、app.pyの3行目を更新してprint(app.config['DEBUG'])として実行します。

app.py

from flask import Flask
app = Flask(__name__)
print(app.config['DEBUG'])

実行結果

[kuzunoha@:11:09:10:~/sand_box/flaskconfig]$python app.py 
False

次にapp.debug = Trueを追記してprintしてみます。

from flask import Flask
app = Flask(__name__)
app.debug = True
print(app.config['DEBUG'])

これを実行すると

[kuzunoha@:11:14:58:~/sand_box/flaskconfig]$python app.py 
True

それではapp.config['Debug'] = Falseと追記してからprintしてみます。

from flask import Flask
app = Flask(__name__)
app.debug = True
app.config['DEBUG'] = False
print(app.config['DEBUG'])

これを実行すると

[kuzunoha@:11:17:35:~/sand_box/flaskconfig]$python app.py 
False

すなわち以下の2つは同義であるということです。

app.debug = Boolean
app.config['DEBUG'] = Boolean

また、app.run(debug=True)で設定するDebugの真偽も同様です。

その他にも環境変数を使用するなどflaskをデバッグモードで実施する方法はあるようです。

参考:http://flask.pocoo.org/docs/1.0/quickstart/#debug-mode

このようにconfig[PARAMETAR]を変更することで、Flaskアプリケーションのコンフィグを変更できます。

その他のデフォルトのパラメータは公式に載っています。

参考:http://flask.pocoo.org/docs/0.12/config/#builtin-configuration-values

オリジナルのパラメータを作る

flaskのコンフィグパラメータは好きに作ることが出来ます。app.pyを以下のように記載します。

from flask import Flask
app = Flask(__name__)
app.config['hogehoge'] = 'piyopiyo'
print(app.config['hogehoge'])

そして実行すると

[kuzunoha@:11:19:08:~/sand_box/flaskconfig]$python app.py 
piyopiyo

となります。そして、これはすなわち以下のようにも使えます。

from flask import Flask
app = Flask(__name__)
app.config['MYSQL_ADDRESS'] = '127.0.0.1'
print(app.config['MYSQL_ADDRESS'])

実行すると

[kuzunoha@:11:34:35:~/sand_box/flaskconfig]$python app.py 
127.0.0.1

となります。ただ、以下のようにインスタンスとして持ち合わせることは出来ません。

from flask import Flask
app = Flask(__name__)
app.config['MYSQL_ADDRESS'] = '127.0.0.1'
print(app.config.mysql_address)

Traceback (most recent call last):
  File "app.py", line 4, in <module>
    print(app.config.mysql_address)
AttributeError: 'Config' object has no attribute 'mysql_address'

さて、それではその他にもパラメータを追加してMySQLに繋げてみましょう。以下が一例になります。(ほんとにただの一例ですのでこれで実行しても何も起きないです。)

from flask import Flask
app = Flask(__name__)
app.config['MYSQL_ADDRESS'] = '127.0.0.1'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = 'password'
app.config['MYSQL_PORT'] = 3306
app.config['MYSQL_DATABASE'] = 'hogepiyodb'

import MySQLdb
connection = MySQLdb.connect(
    host=app.config['MYSQL_ADDRESS'],
    user=app.config['MYSQL_USER'],
    passwd=app.config['MYSQL_PASSWORD'],
    port=app.config['MYSQL_PORT'],
    db=app.config['MYSQL_DATABASE'])
connection.commit()
connection.close()

MySQLdbというモジュールについては以下の参考サイトを見てください。

参考:https://uxmilk.jp/23509

app.config['MYSQL_ADDRESS'] = '127.0.0.1'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = 'password'
app.config['MYSQL_PORT'] = 3306
app.config['MYSQL_DATABASE'] = 'hogepiyodb'

と、このようにパラメータに値を代入していくことが出来るのです。

パラメータの代入方法の選択

Configのパラメータには直接値を代入する方法以外にも方法があります。

参考:https://www.subarunari.com/entry/2018/03/17/いまさらながらFlaskについてまとめる_〜Configuration〜

以下がそれぞれ同じ設定方法になります。ここに記載してる方法以外のものもあります。

ハードコーディング(参考)

上記に書いたものと同じですね。

app.py

from flask import Flask
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['MYSQL_ADDRESS'] = '127.0.0.1'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = 'password'
app.config['MYSQL_PORT'] = 3306
app.config['MYSQL_DATABASE'] = 'hogepiyodb'
print(app.config['DEBUG'])
print(app.config['MYSQL_ADDRESS'])
print(app.config['MYSQL_USER'])
print(app.config['MYSQL_PASSWORD'])
print(app.config['MYSQL_PORT'])
print(app.config['MYSQL_DATABASE'])

結果は以下の感じ

[kuzunoha@:12:14:56:~/sand_box/flaskconfig]$python app.py 
True
127.0.0.1
root
password
3306
hogepiyodb

from_pyfile

cfgファイルを読み込むタイプです。app.pyと同じディレクトリにdevelopment.cfgというcfgファイルを作って、それを読み込むようにします。

development.cfg

DEBUG=True
MYSQL_ADDRESS='127.0.0.1'
MYSQL_USER='root'
MYSQL_PASSWORD='password'
MYSQL_PORT=3306
MYSQL_DATABASE='hogepiyodb'

app.py

from flask import Flask
app = Flask(__name__)
app.config.from_pyfile('development.cfg')
print(app.config['DEBUG'])
print(app.config['MYSQL_ADDRESS'])
print(app.config['MYSQL_USER'])
print(app.config['MYSQL_PASSWORD'])
print(app.config['MYSQL_PORT'])
print(app.config['MYSQL_DATABASE'])

app.pyの3行目app.config.from_pyfile('development.cfg')に注目です。

app.config.from_pyfile(CFGファイル名)となっております。

実行結果は以下の通り

[kuzunoha@:12:29:10:~/sand_box/flaskconfig]$python app.py
True
127.0.0.1
root
password
3306
hogepiyodb

from_object

設定のクラスが書かれたPythonファイルを読み込むようにします。

setting.py

class Development(object):
    DEBUG=True
    MYSQL_ADDRESS='127.0.0.1'
    MYSQL_USER='root'
    MYSQL_PASSWORD='password'
    MYSQL_PORT=3306
    MYSQL_DATABASE='hogepiyodb'

app.py

from flask import Flask
app = Flask(__name__)
app.config.from_object('setting.Development')
print(app.config['DEBUG'])
print(app.config['MYSQL_ADDRESS'])
print(app.config['MYSQL_USER'])
print(app.config['MYSQL_PASSWORD'])
print(app.config['MYSQL_PORT'])
print(app.config['MYSQL_DATABASE'])

実行結果は以下の通り

[kuzunoha@:12:37:18:~/sand_box/flaskconfig]$python app.py 
True
127.0.0.1
root
password
3306
hogepiyodb

app.pyの3行目app.config.from_object('setting.Development')に注目です。

app.config.from_object('Pythonファイル名.オブジェクト名')となっていることがわかると思います。

オブジェクトを取得するようになっているので、setting.pyに新しいクラスを作って読み込ませることも出来ます。クラスなので継承を使って新しいクラスを作っても良いです。

setting.py

class Development(object):
    DEBUG=True
    MYSQL_ADDRESS='127.0.0.1'
    MYSQL_USER='root'
    MYSQL_PASSWORD='password'
    MYSQL_PORT=3306
    MYSQL_DATABASE='hogepiyodb'

class Production(Development):
    DEBUG=False
    MYSQL_ADDRESS='63.31.125.212'

app.py

from flask import Flask
app = Flask(__name__)
app.config.from_object('setting.Production')
print(app.config['DEBUG'])
print(app.config['MYSQL_ADDRESS'])
print(app.config['MYSQL_USER'])
print(app.config['MYSQL_PASSWORD'])
print(app.config['MYSQL_PORT'])
print(app.config['MYSQL_DATABASE'])

実行結果はこうなりました

[kuzunoha@:12:54:12:~/sand_box/flaskconfig]$python app.py 
False
63.31.125.212
root
password
3306
hogepiyodb

DEBUGMYSQL_ADDRESSの値が変更されていることと、それ以外は継承元のそれと同じであるというところが注目点です。

from_pyfile + from_object

ではこの2つを合体して使ってみるとどうでしょうか

password.cfg

MYSQL_PASSWORD='heavensdoor'

setting.py

class Development(object):
    DEBUG=True
    MYSQL_ADDRESS='127.0.0.1'
    MYSQL_USER='root'
    MYSQL_PASSWORD='password'
    MYSQL_PORT=3306
    MYSQL_DATABASE='hogepiyodb'

class Production(Development):
    DEBUG=False
    MYSQL_ADDRESS='63.31.125.212'

app.py

from flask import Flask
app = Flask(__name__)
app.config.from_object('setting.Production')
app.config.from_pyfile('password.cfg')
print(app.config['DEBUG'])
print(app.config['MYSQL_ADDRESS'])
print(app.config['MYSQL_USER'])
print(app.config['MYSQL_PASSWORD'])
print(app.config['MYSQL_PORT'])
print(app.config['MYSQL_DATABASE'])

実行結果は以下の通りです。

[kuzunoha@:13:10:48:~/sand_box/flaskconfig]$python app.py 
False
63.31.125.212
root
heavensdoor
3306
hogepiyodb

最終的なMYSQL_PASSWORDheavensdoorになりました。app.pyの3-4行目を見てみてください。

app.config.from_object('setting.Production')
app.config.from_pyfile('password.cfg')

変数のそれと同じく、最後に宣言された値に上書きされていくからです。

環境変数を使ってそれぞれの環境にConfを変える

from_objectで読み込むクラスを変更すれば各環境ごとに使うconfigを変更できます。

# 開発なら
app.config.from_object('setting.Development')
# 本番なら
app.config.from_object('setting.Production')

辞書型を使うともう少しスマートになります。

dict_confmode = {
    'dev' : 'setting.Development',
    'pro' : 'setting.Production'
}

# 開発なら
confmode = dict_confmode['dev']
# 本番なら
confmode = dict_confmode['pro']

app.config.from_object(confmode)

さらに環境変数を使います。例えばFLASK_CONFIGURATIONという環境変数名に開発ならdev、本番ならproと入れておきます。

---開発環境なら---

環境変数

FLASK_CONFIGURATION=dev

app.pyの一部

import os

dict_confmode = {
    'dev' : 'setting.Development',
    'pro' : 'setting.Production'
}

env_confmode = os.getenv('FLASK_CONFIGURATION')

confmode = dict_confmode[env_confmode]

app.config.from_object(confmode)

---本番環境なら---

環境変数

FLASK_CONFIGURATION=pro

app.pyの一部

import os

dict_confmode = {
    'dev' : 'setting.Development',
    'pro' : 'setting.Production'
}

env_confmode = os.getenv('FLASK_CONFIGURATION')

confmode = dict_confmode[env_confmode]

app.config.from_object(confmode)

それぞれのapp.pyには全く違いはありません。この通り、PCに宣言した環境変数だけで環境を変更できるようになりました。

参考:https://wa3.i-3-i.info/word11027.html

共有することがあまりよろしくない値はfrom_pyfileへ

DBのpasswordや暗号方法、暗号キーやアクセストークンなどはcfgファイルへ記載するようにします。

そうしてinstanceディレクトリを作ってそのcfgファイルを置いておくようにします。

アプリケーションを初期化する際にinstance_relative_config=Trueという引数を当ててあげます。そうすることでinstanceというディレクトリからcfgファイルを読みに行くようになります。

app = Flask(__name__, instance_relative_config=True)

そうして、.gitignoreinstanceを入れてあげます。

最終的な構成はこのようになりました。

app.py
setting.py
instance/
 ┗ password.cfg

password.cfg

MYSQL_PASSWORD='heavensdoor'

setting.py

class Development(object):
    DEBUG=True 
    MYSQL_ADDRESS='127.0.0.1'
    MYSQL_USER='root'
    MYSQL_PASSWORD='password'
    MYSQL_PORT=3306
    MYSQL_DATABASE='hogepiyodb'

class Production(Development):
    DEBUG = False
    MYSQL_ADDRESS='63.31.125.212'

app.py

import os

from flask import Flask

app = Flask(__name__, instance_relative_config=True)
dict_confmode = {
    'dev' : 'setting.Development',
    'pro' : 'setting.Production'
}
env_confmode = os.getenv('FLASK_CONFIGURATION')
confmode = dict_confmode[env_confmode]
app.config.from_object(confmode)
app.config.from_pyfile('password.cfg')
print(app.config['DEBUG'])
print(app.config['MYSQL_ADDRESS'])
print(app.config['MYSQL_USER'])
print(app.config['MYSQL_PASSWORD'])
print(app.config['MYSQL_PORT'])
print(app.config['MYSQL_DATABASE'])

実行結果

[kuzunoha@:14:38:21:~/sand_box/flaskconfig]$export FLASK_CONFIGURATION=dev
[kuzunoha@:14:38:30:~/sand_box/flaskconfig]$python app.py 
True
127.0.0.1
root
heavensdoor
3306
hogepiyodb
[kuzunoha@:14:38:32:~/sand_box/flaskconfig]$export FLASK_CONFIGURATION=pro
[kuzunoha@:14:38:43:~/sand_box/flaskconfig]$python app.py 
False
63.31.125.212
root
heavensdoor
3306
hogepiyodb

参考 Windows環境変数設定:https://www.k-cube.co.jp/wakaba/server/environ.html

参考 Unix,Linux,Mac環境変数設定:https://qiita.com/iam1at/items/91cb8478160c9fbee134

残りはベストプラクティスを見てみてください。

細かいところは違いますが、以下のURLの内容がよくわかるようになったと思います。後はここを読んでください。

https://qiita.com/nanakenashi/items/e272ff1aafb3889230bc

【Docker】コマンド以降をEchoするDockerfileの作成 - ENTRYPOINT

こんにちは、葛の葉です。

Dockerに出てくるENTRYPOINTについて調べていました。

今回はdocker run [IMAGE_ID] ******をエコーするDockerImageを作成したいと思います。

大変参考になったサイト

qiita.com

Dockerfile

FROM alpine:3.9.4
ENTRYPOINT ["echo"]
CMD ["Hello World!"]

今回はechoという名前でDockerImageにbuildしておきます。

実行してみる。

docker run --rm -it echo

とすると

Hello World!

と返ってきます。

[kuzunoha@:18:17:53:~]$docker run -it --rm echo
Hello World!

docker run --rm -it echo FOX

とすると

FOX

と返ってきます。

[kuzunoha@:18:18:39:~]$docker run -it --rm echo FOX
FOX

ENTRYPOINT(echo)がコマンドでCMD(Hello World)がデフォルト引数といったような感じになりました。

シェルを起動したいなぁ

しかしこのままでは以下のようなコマンドではシェルを起動できません。docker run -it --rm echo /bin/shでは/bin/shという値が返ってきます。

[kuzunoha@:18:22:53:~]$docker run -it --rm echo /bin/sh
/bin/sh

そのため、entrypointを上書きしてあげる必要があります。

docker run -it --rm --entrypoint=/bin/sh echoとすればシェルが起動します。

[kuzunoha@:18:27:45:~]$docker run -it --rm --entrypoint=/bin/sh echo 
/ # 

【DockerCompose】DockerfileのFROMに使える変数の設定

こんばんは、葛の葉です。

DockerfileでFROMに対する変数を入れることで、色々使いまわせます。

docs.docker.com

DockerComposeのargsを使えばこんなこともできます。

ファイル構成

.
├── Dockerfile
└── docker-compose.yml

Dockerfile

ARG Ver
FROM python:$Ver

docker-compose.yml

version: '3'
services:
  test_app:
    build: 
      context: ./
      args:
        Ver: "3.7.3"
    command: python --version

upしてみる。

Creating network "teste_default" with the default driver
Creating teste_test_app_1 ... done
Attaching to teste_test_app_1
test_app_1  | Python 3.7.3
teste_test_app_1 exited with code 0

もう一つServiceを追加して実行する。

docker-compose.yml

version: '3'
services:
  test_app:
    build: 
      context: ./
      args:
        Ver: "3.7.3"
    command: python --version

  test_app2:
    build: 
      context: ./
      args:
        Ver: "3.6.5"
    command: python --version

docker-compose upとしてみる。

Starting teste_test_app_1  ... done
Creating teste_test_app2_1 ... done
Attaching to teste_test_app_1, teste_test_app2_1
test_app_1   | Python 3.7.3
test_app2_1  | Python 3.6.5
teste_test_app_1 exited with code 0
teste_test_app2_1 exited with code 0

結論

ひとつのDockerfileを使いまわして複数のServiceを起動できる。

【Docker】Dockerっていうものを使ってみようぜ その1

こんにちは、葛の葉です。

そろそろブログを本格的に初めて1年ほどになりました。そういえば、どんな記事を書いたかな、と思い、振り返ってみたところ、こんな記事を作っていました。

kuzunoha-ne.hateblo.jp

初心に戻り、同じ事柄について記事に書いてみましょうか。

キャラ変わったなぁ…

Dockerとは

コンテナ型仮想環境を構築するソフトウェアのことです。

コンテナ型仮想化とは、環境のみを仮想化しその環境内でソフトウェアを実行します。環境のみを構築するため、その中にOSはありません。その他にも無駄な常駐ソフトウェアが入っておりません。カーネルOSはホストOSのものを使用します。

そのため、使用するメモリやCPUといったリソースの使用量が、今までのOS仮想化環境よりもかなり少なく済みます。

また、コンテナは破棄してもホストOSには何ら影響がありません。そのため、環境を作っては消し、作っては消しを簡単に行なえます。

その他にも、Dockerfile、DockerComposeを使うことでコンテナをコード化出来ます。もちろんこれらはGitHub上に載せることも出来ます。そのため、環境構築込みのコードを共有することが出来ます。

ちょっと試してみようか

alpineというDockerコンテナを作成しましょう。

aplineとはLinuxディストリビューションのことです。非常にコンパクトなものなので、コンテナと相性が良いです。

ja.wikipedia.org

Dockerコンテナを作成するにはDockerイメージをダウンロードする必要があります。

docker pull alpine

プログレスバーが沢山動いて、問題なくコマンドが返ってきたら次のコマンドでコンテナを作成します。

docker run -it alpine

そうするとシェルが返ってきたと思います。

[kuzunoha@:10:14:39:~]$docker run -it alpine
/ # 

これでコンテナを作成することに成功しました。

DockerコンテナとDockerイメージについて

さて、DockerコンテナとDockerイメージという二人の登場人物が出てきたと思います。

Dockerコンテナは先に説明した仮想的な領域そのものをいいます。

では、Dockerイメージはなんであるかというと、そのDockerコンテナを作成する設計図になります。

設計図を元に複数同じものを作ることが可能なように、Dockerイメージを元にDockerコンテナを複数作ることも可能です。

例えばMySQLの入ったDockerイメージを元にMySQLのコンテナを複数作成することも出来ます。

コンテナをカスタマイズする。

さて、いまはコンテナのシェルが立ち上がったところで止まっているかと思います。

[kuzunoha@:10:14:39:~]$docker run -it alpine
/ # 

このシェルはコンテナ内のシェルになります。UbuntuCentOSのそれとほぼ一緒になります。

では、curlを使ってみましょうか。

/ # curl https://www.google.com
/bin/sh: curl: not found

curlコマンドが無いと言われてしまいました。aplineコンテナはほとんどの機能をもっていませんが、色々と機能を追加してあげることができます。

apk add PACKAGE_NAMEで追加出来ます。

apk add curl

curlコマンド追加します。

/ # apk add curl
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz
(1/5) Installing ca-certificates (20190108-r0)
(2/5) Installing nghttp2-libs (1.35.1-r0)
(3/5) Installing libssh2 (1.8.2-r0)
(4/5) Installing libcurl (7.64.0-r1)
(5/5) Installing curl (7.64.0-r1)
Executing busybox-1.29.3-r10.trigger
Executing ca-certificates-20190108-r0.trigger
OK: 7 MiB in 19 packages

curlを組み込むと以下のコマンドが実行できます。

/ # curl https://www.google.com
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ja"><head><meta content="&#19990;&#30028;&#20013;&#12398;&#12.....

curlが実行できるようになりました。

では、exitしてシェルから帰ります。

/ # exit
[kuzunoha@:10:16:28:~]$

お掃除

docker ps -aと打つとコンテナの一覧が出てきます。

[kuzunoha@:10:29:11:~]$docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                        PORTS               NAMES
5c97080be58b        alpine              "/bin/sh"           12 minutes ago      Exited (127) 12 minutes ago                       elated_sammet

これは先程まで動かしていたコンテナの死骸です。この子を使ってDockerイメージを作ることも出来ます。

が、今回はちょっと邪魔なのでクリーンしましょう。

docker rm CONTAINER_IDで削除できます。

[kuzunoha@:10:31:41:~]$docker rm 5c97080be58b
5c97080be58b
[kuzunoha@:10:31:49:~]$

もう一度docker ps -aで消えたか確認します。

[kuzunoha@:10:31:54:~]$docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

消えました。

To Be Continued...