新着記事

Viewing posts for the category Docker

旧Redash(V2) から 新Redash(V8) にアップデートを行いました

Qiita の TORICO Advent Calendar 2019 の記事として書きました

社内での実績閲覧のツールとして Re:dash を使っています。

最初に導入した Re:dash v2 のまましばらくアップデートを行っていなかったので、v8 にアップデートを行いました。

アップデート方針

v8 は dockerコンテナで提供され、 docker-compose で起動させることができます。提供されている docker-compose.yml は、Postgres や redis コンテナの起動も自動でされますし、インストールスクリプトも良くできており、初期状態の ubuntu サーバであればコマンド一発で docker エンジンのインストールも含めアプリの起動まで行うことができます。

従来の EC2イメージの Redash から、スクリプトでアップデートできたら楽なのですが、少し試したところ Postgres の DB のスキーマ名が変わっていたり、Postgresのサーバのバージョンも 9.3 -> 9.6 への大きなアップデートとなることもあり、今回は「Redash v8 を新規EC2で起動し、旧サーバからDBのレコードをコピーする」という方針としました。

Redash V8 サーバの構築

https://github.com/getredash/setup/ こちらのスクリプトを使うことで、非常に簡単に起動させることができます。

1. EC2 で Ubuntu18 サーバを起動

2. ホームディレクトリで、

git clone git@github.com:getredash/setup.git

3.

cd setup
bash setup.sh

あとは、スクリプトの中で docker エンジンのインストール、postgres のデータファイル構築や ランダムなDBパスワードの作成、初期スキーマの構築などすべて行ってくれます。スクリプト終了時に docker-compose up -d まで行うため、ブラウザでアクセスすれば Redash のサインインページが表示されます。

postgres コンテナへの接続

インポートするために、Postgres の Docker コンテナに外部から接続できるようにします。

上記スクリプトで Redash をインストールした場合、/opt/redash/docker-compose.yml が作成されますので、この postgres サービスの箇所に

 postgres:
image: postgres:9.6-alpine
env_file: /opt/redash/env
volumes:
- /opt/redash/postgres-data:/var/lib/postgresql/data
restart: always
# 追加
ports:
- "5432:5432"

ports項目を追加し、ホストマシンの5432ポートをコンテナに接続します。
/opt/redash/env の POSTGRES_PASSWORD に作成されているパスワードと、ユーザー名 postgres でコンテナの postgres に接続できます。

旧サーバの暗号キー等の設定のコピー

旧サーバの、/opt/redash/.env の設定値を新サーバにコピーします。

REDASH_COOKIE_SECRET 

REDASH_COOKIE_SECRET は、DBの data_sources テーブルにある暗号化フィールドの復号化に使われますので、テーブル内容をそのままコピーする場合は一致させる必要があります。

これを一致させない場合、Redash 起動時の data source の選択の際にローディングスピナーがぐるぐる回り続けることになります。APIレスポンス自体はHTTP500が返ってきており、docker-compose のログには Pythonの例外が、暗号化内容を復号できない旨のメッセージと共に出力されます。肝心のトレースバック(スタックトレース)は保存し忘れました。

実際のDBの復号化には settings.DATASOURCE_SECRET_KEY が暗号キーとして使われ、settingsを読んでみると

COOKIE_SECRET = os.environ.get("REDASH_COOKIE_SECRET", "c292a0a3aa32397cdb050e233733900f")
SECRET_KEY = os.environ.get('REDASH_SECRET_KEY', COOKIE_SECRET)
DATASOURCE_SECRET_KEY = os.environ.get('REDASH_SECRET_KEY', SECRET_KEY)

このようになっており、DBの暗号キーとクッキーの暗号キーは個別に設定できるようですが、旧Redashの場合はそれぞれ同じ値を使っていると思いますので、新サーバの /opt/redash/env には 旧サーバの /opt/redash/.env から REDASH_COOKIE_SECRET の内容だけコピーしておきます。REDASH_SECRET_KEY ができていたら項目ごと消しておきます。

その他の設定

Google (Gsuite ) を使っている場合は、

REDASH_GOOGLE_APPS_DOMAIN
REDASH_GOOGLE_CLIENT_ID
REDASH_GOOGLE_CLIENT_SECRET

をコピーすることで新サーバでもGoogle 認証が行えるようになります。そのほか、REDASH_MAIL_* など、旧サーバの設定内容はそのままコピーします。

REDASH_GOOGLE_CLIENT_ID="1xxxxxxxx.apps.googleusercontent.com"

このように、ダブルクォーテーションで囲っているとV8ではうまく動作しませんので、envファイル内でダブルクオーテーションは書かないようにします。

テーブル内容のコピー

おそらく、慣れてる方であれば pg_dump を使うのが良いと思うのですが、私は postgres や pg_dump の経験があまり無いため、datagrip を使ってSELECT 結果から INSERT 文を生成し、それをコピペで流し込むことにしました。

旧Redash からの変更点として、データベーススキーマ名が redash から postgres に変更されたこと、また旧版の queries テーブルや dashboards にあった user_email フィールドが無くなっていることなどあり、pg_dump での対応が難しそうだと思ったためです。

データ量として 700クエリー、30ダッシュボード、60ユーザー程度の規模ですので、コピペで十分対応できました。なお、query_results テーブルに過去のSQL結果が入ってますが、2GB程度のストレージを使っており、不要だと思ったため今回は query_results は移行しませんでした。

旧サーバの postgres に接続し、以下のテーブル内容をコピーしていきます。

organizations
users
data_sources
groups
data_source_groups
queries
visualizations
dashboards
widgets

外部キー制約があるため、流し込む順番が違うと失敗しますが、上記の順番で INSERT できると思います。

queries , dashboards には user_email という不要フィールドがあり、queries にはさらに query_results への参照があるのですが、それらは不要なため下記のようなSELECT 文で datagrip 上で検索し、INSERT 文としてコピーして 新DBに INSERT します。

SELECT id, updated_at, created_at, org_id, data_source_id, name, description, query, query_hash, api_key, user_id, last_modified_by_id, is_archived, options, version, is_draft, schedule_failures, search_vector, tags, schedule
FROM public.queries
ORDER BY id;
SELECT id, updated_at, created_at, org_id, slug, name, user_id, layout, dashboard_filters_enabled, is_archived, version, is_draft, tags
FROM public.dashboards
ORDER BY id;

ついでに、MySQL データソースは RDS用のものが新たに出来ていたため、 data_sources の type フィールドは mysql から rds_mysql 変更しました。

シーケンスの更新

INSERT しただけではオートインクリメントPKに使われるシーケンス番号が1から始まり、新しいクエリを保存する時エラーになってしまうので、シーケンスも更新しておきます。

SELECT * FROM users_id_seq;
SELECT MAX(id) + 1 FROM users;
ALTER SEQUENCE users_id_seq RESTART WITH 61;

SELECT * FROM groups_id_seq;
SELECT MAX(id) + 1 FROM groups;
ALTER SEQUENCE groups_id_seq RESTART WITH 6;

SELECT * FROM data_sources_id_seq;
SELECT MAX(id) + 1 FROM data_sources;
ALTER SEQUENCE data_sources_id_seq RESTART WITH 24;

SELECT * FROM data_source_groups_id_seq;
SELECT MAX(id) + 1 FROM data_source_groups;
ALTER SEQUENCE data_source_groups_id_seq RESTART WITH 22;

SELECT * FROM dashboards_id_seq;
SELECT MAX(id) + 1 FROM dashboards;
ALTER SEQUENCE dashboards_id_seq RESTART WITH 37;

SELECT * FROM queries_id_seq;
SELECT MAX(id) + 1 FROM queries;
ALTER SEQUENCE queries_id_seq RESTART WITH 699;

SELECT * FROM visualizations_id_seq;
SELECT MAX(id) + 1 FROM visualizations;
ALTER SEQUENCE visualizations_id_seq RESTART WITH 1230;

SELECT * FROM widgets_id_seq;
SELECT MAX(id) + 1 FROM widgets;
ALTER SEQUENCE widgets_id_seq RESTART WITH 695;

このように、MAX(id) の結果をコピペしてALTER SEQUENCE していきました。1行で書けそうなのですが、経験が無く書き方がわからなかったため、愚直に書いてます。

これで、新サーバで旧サーバの状態が再現されていると思います。

nginx に SSL証明書を組み込む

自己認証局のSSL証明書がありますので、使います。

参考: dockerでredashのnginxをssl化したら思いの外ハマった

mkdir -p /home/ubuntu/nginx/certs

certs に、証明書 ( my-awesome.crt, my-awesome.key )を入れる 

/home/ubuntu/nginx/default.conf を作成

upstream redash {
server redash:5000;
}

server {
listen 80 default;
return 301 https://$host$request_uri;
}

server {
listen 443;
ssl on;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1.2;
ssl_ciphers "ECDHE+RSAGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!EXPORT:!DES:!3DES:!MD5:!DSS";

gzip on;
gzip_types *;
gzip_proxied any;

ssl_certificate /etc/nginx/certs/my-awesome.crt;
ssl_certificate_key /etc/nginx/certs/my-awesome.key;

error_log /var/log/nginx/nginx_error.log;
access_log /var/log/nginx/nginx_access.log;

location / {
proxy_ssl_server_name on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_pass http://redash;
}
}

/opt/redash/docker-compose.yml を修正

 nginx:
image: redash/nginx:latest
ports:
- "80:80"
- "443:443"
depends_on:
- server
links:
- server:redash
restart: always
volumes:
- /home/ubuntu/nginx/default.conf:/etc/nginx/conf.d/default.conf
- /home/ubuntu/nginx/certs:/etc/nginx/certs

最後に

繰り返しとなりますが、データソース選択ビューで、INSERTしたはずのデータソースが表示されず、ぐるぐるとローディングスピナーが回り続ける場合、旧サーバと settings.DATASOURCE_SECRET_KEY が一致していません。/opt/redash/env の REDASH_COOKIE_SECRET を旧サーバと同じにして、REDASH_SECRET_KEY 設定を消すとデータソースが見れるようになると思います。

PyCharm + Docker で Django 開発環境を構築する

PyCharm 2016 の Docker サポートの使い勝手が良いです。

Docker で開発環境を作るのは初めてだったのですが、

  • ローカル ( mac ) は Docker Toolbox を使って開発環境を作る。PyCharm のデバッガでステップ実行できるようにする。
  • ソースコード、およびログディレクトリは Docker イメージの中に入れず、ホストのディレクトリをマウントして使う
  • 公開サーバを見越して uwsgi, nginx の設定も作っておく


という所までできたので、手順を書きます。

コード置いてあります。 https://github.com/ytyng/docker-django-skeleton

こちらを参考にしました。https://github.com/dockerfiles/django-uwsgi-nginx

必要アプリのインストール

インストーラに従ってインストールしておきます。

プロジェクトディレクトリを作成し、PyCharm で開く

$ mkdir docker-django-skeleton

$ open -a PyCharm docker-django-skeleton

PyCharm用の Bash プラグインのインストール

メニューバーの PyCharm → Preferences → 検索: Plugins → Plugins の検索で「BashSupport」を検索 → 該当したものをインストールしておきます。

PyCharmの再起動を求められるので、案内通り再起動します。

Docker 用のファイルを作る

プロジェクトルートに Dockerfile そのまま置くと煩雑になってしまうので、docker ディレクトリを作って関連ファイルを入れます。

docker/Dockerfile

FROM ubuntu:16.04

MAINTAINER ytyng

RUN apt-get update && apt-get install -y \
    python3 \
    python3-pip \
    python3-dev \
    python3-setuptools \
    libjpeg-dev \
    nginx \
    supervisor \
  && rm -rf /var/lib/apt/lists/*

# ipython, gnureadline が必要なら
# lib32ncurses5-dev

RUN ln -s /usr/lib/x86_64-linux-gnu/libjpeg.so /usr/lib/libjpeg.so

# RUN easy_install3 pip  # aptで入れた方が安定

COPY requirements/base.txt /tmp/requirements/base.txt
RUN pip3 install -r /tmp/requirements/base.txt

# setup all the configfiles
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
COPY nginx-app.conf /etc/nginx/sites-available/default
COPY supervisor-app.conf /etc/supervisor/conf.d/

RUN mkdir -p /etc/nginx/certs
# COPY certs /etc/nginx/certs/

RUN mkdir -p /var/run/django/

# set locale
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

EXPOSE 80
#EXPOSE 443

↑ libjpeg などインストールしてるのは Pillow 用

docker/requirements/base.txt

Django==1.10.1
pytz
uwsgi
# MySQL使う場合
PyMySQL

↑ 今回のサンプルは、DBは SQLite3 なので PyMySQL 不要ですがサンプルとして書いてます。



docker/nginx-app.conf

server {
    listen 80;
    # listen 443 ssl;

    server_name docker-django-skeleton.example.com;

    charset utf-8;

    # ssl_certificate /etc/nginx/certs/xxx.crt;
    # ssl_certificate_key /etc/nginx/certs/xxx.key;

    location /static/ {
        alias /var/django/docker-django-skeleton/staticfiles/;
        break;
    }

    location / {
        include uwsgi_params;
        uwsgi_pass 127.0.0.1:3031;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

docker/supervisor-app.conf

[program:app-uwsgi]
command = /usr/local/bin/uwsgi --ini /var/django/docker-django-skeleton/conf/uwsgi.ini

[program:nginx-app]
command = /usr/sbin/nginx

Docker 用の設定ファイルの作成

環境変数など設定する Bash スクリプトを用意しておきます。
mac で実行した場合と公開サーバで起動した場合に読み込まれる設定を変えるようにしています。

docker/_settings.sh

#!/usr/bin/env bash

APP_NAME=docker-django-skeleton
DJANGO_APP_NAME=docker_django_skeleton

PROJECT_DIR=$(cd $(dirname ${BASH_SOURCE:-$0})/..;pwd)

if [ $(which docker-machine) ]; then
    # mac
    eval "$(docker-machine env default)"
    DOCKER_COMMAND=docker
    DOCKER_MACHINE_COMMAND=docker-machine
    LOG_DIR=/tmp/log/${APP_NAME}
    export UWSGI_PROCESSES=1
    export UWSGI_THREADS=1
    export DJANGO_SETTINGS_MODULE=${DJANGO_APP_NAME}.settings.local
else
    # ubuntu
    DOCKER_COMMAND="sudo docker"
    LOG_DIR=/var/log/${APP_NAME}
    OPTIONAL_PARAMS="--link mysql:mysql"
    RUN_COMMAND="supervisord -n"
    export UWSGI_PROCESSES=2
    export UWSGI_THREADS=2
    export DJANGO_SETTINGS_MODULE=${DJANGO_APP_NAME}.settings.production
fi


Dockerイメージ作成用スクリプト

docker/docker-01-build.sh

#!/usr/bin/env bash
cd $(dirname ${BASH_SOURCE:-$0})
. _settings.sh

${DOCKER_COMMAND} build -t ${APP_NAME} .

docker/docker-02-run.sh

#!/usr/bin/env bash
cd $(dirname ${BASH_SOURCE:-$0})
. _settings.sh

mkdir -p -m 777 ${LOG_DIR}

${DOCKER_COMMAND} run -d -P -p 80:80 -p 443:443 \
  -v ${PROJECT_DIR}:/var/django/${APP_NAME} \
  -v ${LOG_DIR}:/var/log/django \
  -v /etc/localtime:/etc/localtime:ro \
  -e DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} \
  -e UWSGI_PROCESSES=${UWSGI_PROCESSES} \
  -e UWSGI_THREADS=${UWSGI_THREADS} \
  ${OPTIONAL_PARAMS} \
  --name ${APP_NAME} ${APP_NAME} ${RUN_COMMAND}

if [ "${DOCKER_MACHINE_COMMAND}" ]; then
    docker-machine ip default
fi

${DOCKER_COMMAND} ps

docker/docker-03-bash.sh

#!/usr/bin/env bash
cd $(dirname ${BASH_SOURCE:-$0})
. _settings.sh

EXISTS=$(${DOCKER_COMMAND} ps --quiet --filter name=${APP_NAME})

if [ "${EXISTS}" ]; then
    ${DOCKER_COMMAND} exec -it ${APP_NAME} /bin/bash $@
else
    ${DOCKER_COMMAND} run --rm -it \
    -e DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} \
    -v ${PROJECT_DIR}:/var/django/${APP_NAME} \
    --name ${APP_NAME} ${APP_NAME} /bin/bash $@
fi

docker/docker-09-rm.sh

#!/usr/bin/env bash
cd $(dirname ${BASH_SOURCE:-$0})
. _settings.sh

${DOCKER_COMMAND} rm --force ${APP_NAME}

docker/manage.sh

#!/usr/bin/env bash
cd $(dirname ${BASH_SOURCE:-$0})
. _settings.sh

EXISTS=$(${DOCKER_COMMAND} ps --quiet --filter name=${APP_NAME})

if [ "${EXISTS}" ]; then
    ${DOCKER_COMMAND} exec -it ${APP_NAME} \
    /bin/bash -c "cd /var/django/${APP_NAME}/${APP_NAME}/; ./manage.py $*"

else
    ${DOCKER_COMMAND} run --rm -it \
    -e DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} \
    -v ${PROJECT_DIR}:/var/django/${APP_NAME} \
    --name ${APP_NAME} ${APP_NAME} /bin/bash -c \
    "cd /var/django/${APP_NAME}/${APP_NAME}/; ./manage.py $*"
fi


*.sh には、実行パーミッションを付与しておきます。

$ chmod +x docker-*.sh manage.sh

docker-django-skeleton
└── docker
    ├── Dockerfile
    ├── _settings.sh
    ├── docker-01-build.sh
    ├── docker-02-run.sh
    ├── docker-03-bash.sh
    ├── docker-09-rm.sh
    ├── manage.sh
    ├── nginx-app.conf
    ├── requirements
    │   └── base.txt
    └── supervisor-app.conf


Docker イメージの作成

先ほど作成した、docker-01-build.sh を PyCharm 上で右クリック → Run 'docker-01-build.sh'


Dockerイメージができます。

Django プロジェクトディレクトリの作成

既に他の件で Django が mac にインストールされている場合は、それを使って `django-admin.py startproject` すれば良いと思いますが、今回は先ほど Docker イメージにインストールした Django を使って Django プロジェクトを作成します。

プロジェクトディレクトリをまるごと Docker にマウントし、Docker内で startproject します。

$ docker run -it -v `pwd`:/var/django/docker-django-skeleton docker-django-skeleton /bin/bash

※ pwd は、docker-django-skeleton ディレクトリ。docker ディレクトリの親ディレクトリ

$ docker run -it -v `pwd`:/var/django/docker-django-skeleton docker-django-skeleton /bin/bash
root@8d2ba7a54287:/# cd /var/django/docker-django-skeleton/
root@8d2ba7a54287:/var/django/docker-django-skeleton# django-admin.py startproject docker_django_skeleton
root@8d2ba7a54287:/var/django/docker-django-skeleton# mv docker_django_skeleton docker-django-skeleton
root@8d2ba7a54287:/var/django/docker-django-skeleton#


好みの問題ですが、Django プロジェクトのディレクトリ名は アンダーバーではなく ハイフンに変更しています。

PyCharm を見てみると、docker-django-skeleton ディレクトリが作成されているのが確認できます。

settings を環境ごとに分ける

Django でよくやるプラクティスで、settings を ローカル環境と公開環境で分けるというものがあります。

docker_django_skeleton ディレクトリ内で、

1. settings ディレクトリを作成
2. settings.py は、 settings/base.py に改名
3. settings/local.py settings/production.py を作成。


settings/local.py, settings/production.py の内容は以下のようになります。

settings/local.py

from .base import *  # NOQA


内容はありませんが、今回はこれで良しとします。

manage.py のインタプリタ (シェバン shebang ) を python3 にしておく


作成された manage.py を見てみると、1行目が

manage.py

#!/usr/bin/env python

となっていますが、Docker イメージ内には Python2 が残っているため不具合が起こることがあります。

#!/usr/bin/env python3

に変更しておきます。

PyCharm のインタプリタで Docker の Python を指定する

PyCharm → Preferences → 検索: interpreter

Project Interpreter が出てきます。

ここで、右上の「...」ボタンをクリックし、Add Remote を選択。

Configure Remote Python Interpreter になるので、
「Docker」を選択、Image name は docker-django-skeleton:latest を選択してください。

それと、Python interpreter path: は python となっていますが、python3 に変更します。

すると、Docker 内の Python 環境を認識します。すごい!

Djangoサポートを有効にする

Preferences で、検索: django

Language & Frameworks の Django が出て来るので選択し、Enable Django Support にチェック。

Django project root: manage.py のある、docker-django-skeleton
Settings: docker_django_skeleton/settings/local.py

OKをクリック

Project Structure を設定する

Preferences で、検索: structure

manage.py のある、docker-django-skeleton ディレクトリを選択し、Source をクリック → OK

migrate

次は、./manage.py migrate をします。
docker コンテナ内なので、./manage.py を実行するのも少し面倒ですが、docker ディレクトリの中に manage.sh を作ってあるのでこれを使います。

$ cd docker
$ ./manage.sh

Type 'manage.py help <subcommand>' for help on a specific subcommand.

Available subcommands:

[auth]
    changepassword
    createsuperuser

[django]
    check
    compilemessages
...

$ ./manage.sh migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
...

Docker の Django環境を使ってマイグレーションしてくれました。

テストサーバの起動

PyCharm の右上、Run/Debug の選択をクリックし、Edit Configurations...

Run/Debug Configurations ダイアログで、左上の + をクリック

Django server を選択

Host: には、0.0.0.0 を入力(重要)
Name: は適当に (例: runserver )
→ OK

出来た runserver 実行環境が選択されている状態で、虫(デバッグ) ボタンを押下

System check identified no issues (0 silenced).
September 26, 2016 - 09:54:15
Django version 1.10.1, using settings 'docker_django_skeleton.settings.local'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.

となりますので、0.0.0.0:8000 をクリックするとブラウザが起動し、Docker マシーンにリダイレクトされてページが表示されます。
デバッガも有効な状態ですので、ブレイクポイントが使えます。すごい!

公開環境で起動する

uwsgi.ini

[uwsgi]

app = $(APP_NAME)
django_app = $(DJANGO_APP_NAME)

base = /var/django/%(app)/%(app)/
pythonpath = %(base)
chdir = %(base)

socket = 127.0.0.1:3031

module = %(django_app).wsgi:application

processes = $(UWSGI_PROCESSES)
threads = $(UWSGI_THREADS)

master = true

pidfile = /var/run/django/%(app).pid

touch-reload = %(base)/%(django_app)/wsgi.py
lazy-apps = true
thunder-lock = true

buffer-size = 32768

logto = /var/log/django/%(app)/%n.log

uwsgi の設定ファイルはこんな感じですかね。

ソースコードを公開環境のサーバに持っていって、

$ ./docker-01-build.sh

でコンテナを作り (ローカルで作ったのを Docker hub に push -> 本番で Pull でもいいですが)、

$ docker-02-run.sh

で起動すると、公開環境 (というか docker-machine がインストールされてない環境 ) では本番用設定で nginx → uwsgi → Django で起動します。

Docker コンテナを削除する時は

$ docker-09-rm.sh

で消えます。

Search

Recent Tweets

  • 大坂昌彦

    大坂昌彦 @masahiko_osaka

    ytyng

    今日はヤマハへ遊びに行って、新製品を試奏しました!!その模様をiPhoneで撮ったので見て下さい!! https://t.co/EQ9LBtK5a9 https://t.co/OkGiLYC24l
    3 weeks, 4 days ago

  • ytyng

    ytyng @ytyng

    なおってた
    1 month, 1 week ago

  • ytyng

    ytyng @ytyng

    vuetify って2になって、グリッドのタグ名がBootstrapと同じになったのね。v-flex と書かなくてよくなってた。良い。
    1 month, 1 week ago

  • ytyng

    ytyng @ytyng

    YahooショッピングのAPI、応答を返さない状態じゃない? https://t.co/rYKTlr7RcU
    1 month, 1 week ago

  • ytyng

    ytyng @ytyng

    復旧したようですね
    1 month, 3 weeks ago