2020年11月20日金曜日

certbot renew で証明書の自動更新メモ

概要

自分用のメモです
standalone プラグインを使って証明書を取得している場合はかなり簡単に自動更新できます
--pre-hook と --post-hook を使って 80 を一瞬停止するのがポイントです

環境

  • Ubuntu18.04
  • certbot 1.9.0

cron 設定

2 ヶ月に 1 回 24:30 に更新する設定です
Lets’s Encrypt は 90 日で期限切れで残り 30 日から更新可能です

80 番はコンテナで動作しているので docker コマンドで停止しています
ここは自身の環境にあったコマンドに変更すれば OK です

30 15 25 */2 * cd /home/hawksnowlog/app; sudo certbot-auto renew --pre-hook "docker-compose stop proxy" --post-hook "docker-compose start proxy"

他にファイアウォールなどで 80 番ポートを制御している場合はそれも一瞬開放する必要があります

2020年11月19日木曜日

docker 上で squid を動かす

概要

前回 Ubuntu 上に squid を構築しました
今回は docker 上に構築してみたいと思います

環境

  • Ubuntu 16.04
  • docker 19.03.12

キャッシュ用のディレクトリ作成

squid がキャッシュした情報を永続化するためにホスト側にマウントします
ディレクトリは何でも OK です

  • mkdir /home/vagrant/cache

コンテナ起動

ではコンテナを起動します
先程作成したキャッシュ用のディレクトリを /var/spool/squid でマウントします

docker run --name squid -d --restart=always \
  -p 3128:3128 \
  -v /home/vagrant/cache:/var/spool/squid \
  sameersbn/squid:3.5.27-2

設定ファイルを使う

デフォルトだと http/https プロキシとして使えないので設定ファイルを変更します

  • mkdir /home/vagrant/config
  • vim /home/vagrant/squid.conf
http_access allow all
docker run --name squid -d --restart=always \
  -p 3128:3128 \
  -v /home/vagrant/config/squid.conf:/etc/squid/squid.conf \
  -v /home/vagrant/cache:/var/spool/squid \
  sameersbn/squid:3.5.27-2

使う

あとは普通に使うだけです
curl の場合は以下のように使えます

  • http_proxy=192.168.100.10:3128 https_proxy=192.168.100.10:3128 curl https://kaka-request-dumper.herokuapp.com/

設定ファイルをリロードするには

SIGHUP を送信します

  • docker kill -s HUP squid

おまけ: docker-compose で起動する

docker-compose で管理したい場合はこれを使いましょう

  • vim docker-compose.yml
version: '3'
services:
  squid:
    image: sameersbn/squid:3.5.27-2
    ports:
      - "3128:3128"
    volumes:
      - /home/vagrant/cache:/var/spool/squid
      - /home/vagrant/config/squid.conf:/etc/squid/squid.conf
    restart: always
  • docker-compose up -d

最後に

docker 上で squid コンテナを立ててみました
バージョンを上げたい場合はイメージを変更すれば動作すると思います

参考サイト

2020年11月18日水曜日

Ansible で環境変数を使って特定の環境ごとにタスクを切り替える方法

概要

例えばデプロイするファイルを動的に切り替えたい場合に毎回 playbook を変更するのは面倒です
また group_vars などで変数で定義してもいいですがそれも毎回変更するのは面倒です
そんなときは環境変数で playbook を実行するたびに別の値を設定しそれを参照するようにできます

環境

  • macOS 10.15.7
  • ansible 2.9.10

サンプルコード

HOGE という環境変数を設定することを想定しています
copy のファイル名や when の条件にも使えます
環境変数の設定を必須にしたい場合は playbook の冒頭で変数のチェックをして空であれば fail にするような処理を入れたほうが良いかなと思います

---
- hosts: localhost
  gather_facts: no
  vars_files:
    - vault.yml
  tasks:
    - name: Check HOGE env
      fail: 
        msg: The HOGE variable is an empty or None.
      when: lookup('env', 'HOGE') == ""

    - name: Print HOGE env
      debug:
        msg: "HOGE = {{ lookup('env', 'HOGE') }}"

    - name: Put a file
      copy:
        src: files/test_{{ lookup('env', 'HOGE') }}.txt
        dest: ~/Downloads/test.txt
        owner: hawksnowlog
        group: staff
        mode: '0644'

実行する場合はコマンドの前に環境変数をセットしてから実行しましょう

  • HOGE=fuga ansible-playbook -i hosts site.yml

特定の値が環境変数に設定されているか確認する場合は

サンプルコードは空の場合にエラーにしました
特定の値が入っていない場合はエラーにしたい場合は when を以下のように変更しましょう

when: lookup('env', 'HOGE') not in ["hoge", "fuga", "foo"]

参考サイト

2020年11月17日火曜日

Gitlab CI で dind 超入門

概要

Gitlab CI で docker in docker (dind) を使ってプロジェクト内のソースを docker build してみました
手順や流れを紹介します

環境

  • macOS 10.15.7
  • docker 19.03.13
  • Gitlab 13.5.1-ee

事前準備: Gitlab の準備

コンテナレジストリを有効にした Gitlab を用意してください
過去の記事を参考にしてローカルに構築しても OK です

事前準備: Gitlab Runner を構築する

過去に Specific Runner を構築する方法を紹介しているのでそれを参考にしても OK です

だたポイントとして以下の条件を満たす Runner をインストールしてください

  • executor は docker を選択
  • Runner 上のマシンで docker が動作する
  • Runner に --docker-privileged の権限を付与

プロジェクトを作成する

プロジェクト名は test として進めます
まずはプロジェクトに push するためのアプリケーションを作成します
何でも OK ですが今回は Web アプリケーションにしてみます

  • bundle init
  • vim Gemfile
gem "sinatra"
  • vim app.rb
require 'sinatra'

get '/' do
  'ok'
end

ローカルで動作確認する場合は

  • bundle exec ruby app.rb -o 0.0.0.0
  • curl localhost:4567

で「ok」が返ってくることを確認します

一応 .gitignore を作成しておきましょう

Dockerfile を作成する

次に Dockerfile を作成します
dind を使った CI ではこの Dockerfile を元に docker build します

  • vim Dockerfile
FROM ruby:2.7

ADD . /app
WORKDIR /app

RUN bundle install

EXPOSE 4567

CMD ["bundle", "exec", "ruby", "app.rb", "-o", "0.0.0.0"]

ローカルで動作確認する場合は

  • docker build -t test .
  • docker run -d --name test -p 4567:4567 test
  • curl localhost:4567

で「ok」が返ってくることを確認します

.gitlab-ci.yml 作成する

では dind を使った docker build を行う CI のルールを作成します
今回の肝になります

使用するベースイメージは docker:19.03.13 ですが実際にビルドするイメージは docker:19.03.13-dind を指定しましょう
CI_REGISTRY_USER, CI_REGISTRY_PASSWORD, CI_REGISTRY は gitlab が用意してくれている変数なのでこれを使って docke login や push を行います

今回は Gitlab の Container Registry に push するのでこのような指定にしていますが dockerhub などの場合には URL や ID/PW を変更してください

  • vim .gitlab-ci.yml
image: docker:19.03.13

stages:
  - build

build_image:
  stage: build
  services:
    - name: docker:19.03.13-dind
  variables:
    DOCKER_HOST: tcp://docker:2375
    DOCKER_TLS_CERTDIR: ""
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $CI_REGISTRY_IMAGE:latest .
    - docker push $CI_REGISTRY_IMAGE:latest

push して CI が成功するか確認する

  • git init
  • git add .
  • git commit -m "First commit"
  • git remote add origin https://gitlab.example.com/root/test.git
  • git push -u origin master


これで Gitlab のプロジェクトの CI/CD を確認してみましょう

ちゃんと Gitlab の Container Registry にも push されているのが確認できると思います

最後に

Gitlab の CI/CD で docker in docker を使ってイメージビルドしてみました
Runner 側で docker executor の設定がいろいろと必要なのがポイントかなと思います

push する際のイメージ名は latest 以外にもコミットハッシュなどが変数として使えるので状況に応じて変更すると良いかなと思います

参考サイト

2020年11月16日月曜日

flask + python の print が docker logs で表示されない場合の対処方法

概要

どうやら python の print はデフォルトだとバッファリングしているようで、そのままだと docker logs の標準出力に出ないようです
簡単な対象方法を紹介します

環境

  • macOS 10.15.7
  • Python 3.8.5
  • docker 19.03.13

アプリ作成

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    print('hello')
    return 'Hello'

ターミナル上で確認

  • FLASK_APP=app.py pipenv run flask run
  • curl localhost:5000

=> hello が表示される

Dockerfile

FROM python:3.8-buster

ADD . /app
WORKDIR /app

RUN pip install pipenv
RUN pipenv install
ENV FLASK_APP app.py

EXPOSE 5000

CMD ["pipenv", "run", "flask", "run", "--host", "0.0.0.0"]

docker 上で確認

  • docker build -t test_app .
  • docker run -p 5000:5000 --name test_app test_app
  • docker logs test_app

=> 表示されない

対処方法1: flush=True

print 時に flush=True を指定します

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    print('hello', flush=True)
    return 'Hello'

対象方法2: PYTHONUNBUFFERED を指定する

すべての print に flush=True を入れるのは大変なので PYTHONUNBUFFERED で制御します

  • docker run -e PYTHONUNBUFFERED=1 --name test_app -p 5000:5000 test_app

もしくは Dockerfile を書き換えても OK です

FROM python:3.8-buster

ADD . /app
WORKDIR /app

RUN pip install pipenv
RUN pipenv install
ENV FLASK_APP app.py
ENV PYTHONUNBUFFERED 1

EXPOSE 5000

CMD ["pipenv", "run", "flask", "run", "--host", "0.0.0.0"]

app.logger の場合は

特に何もせずに docker logs に出力されました
素直に app.logger を使い回すのが docker との相性は良さそうです

import logging
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    app.logger.setLevel(logging.INFO)
    app.logger.info('hello')
    return 'Hello'

最後に

flask で print を使った際にバッファリングさせない方法を紹介しました
python の print はデフォルトだとバッファリングするので flask には関係なく今回の現象は発生すると思います

flask を使っている場合は素直に app.logger を使うことをおすすめします

参考サイト

2020年11月12日木曜日

Sinatra をミドルウェアとして使ってみる

概要

Sinatra をミドルウェアとして使うと before の前に処理を実行することができるようになります

環境

  • macOS 10.15.7
  • Ruby 2.7.1p83
    • sinatra 2.1.0

サンプルコード

事前に /auth にアクセスしていないと /hello にアクセスすることができません
Middleware として使用している Sinatra のモジュラークラス内で定義している session は引き続きアプリケーションクラス側でも参照することができます

require 'sinatra/base'

class MyMiddleware < Sinatra::Base
  enable :sessions

  get '/auth' do
    session['user'] = 'admin'
  end
end

class MyApp < Sinatra::Base
  use MyMiddleware

  before do
    unless session['user'] == 'admin'
      redirect '/auth'
    end
  end

  get '/hello' do
    'ok'
  end
end

動作確認

curl で確認します
curl で cookies を使う場合は -c, -b オプションを使います

  • curl -c cookie.txt localhost:9292/auth

これでセッションを保存し -b を使って cookie を使用します

  • curl -b localhost:9292/hello

これで ok が返ってくることが確認できます
cookie.txt がない状態で /hello にアクセスするとリダイレクトされることが確認できると思います

最後に

基本的には session やデータベースと組み合わせて使うことになりそうです
処理的には認証などの共通処理や事前処理で使えそうです

2020年11月11日水曜日

Sinatra のクラシックスタイルで Inline Template を使う方法

概要

Inline Template とはテンプレートの情報をアプリケーションのファイルに記載することができる機能です
本来テンプレート (ビュー) は views ディレクトリ配下に作成しますが簡単なアプリケーションなら Inline Template が使えます

環境

  • macOS 10.15.7
  • Ruby 2.7.1p83
    • sinatra 2.1.0

ポイント

クラシックスタイルの場合はクラス内で enable :inline_templates を呼び出す必要があります

サンプルコード

  • vim app.rb
require "sinatra/base"

class MyClass < Sinatra::Base
  enable :inline_templates

  get '/' do
    @message = "Hello"
    erb :home
  end
end

__END__

@@home
<html>
<head>
  <title>home</tile>
</head>
<body>
<h2><%= @message %></h2>
</body>
</html>
  • vim config.ru
require "./app"
run MyClass
  • bundle exec rackup config.ru

参考サイト