Odooを読む②
公式ドキュメントを読む。 開発者向けチュートリアルがあったので、とりあえずそれをやっていく。
事前準備
Docker内でCustom Moduleを使ってOdooを立ち上げる
実行時に--addons-path
を付与することで、アドオンをディレクトリから追加できる。
Dockerイメージにhostのディレクトリをマウントすれば同じことができるはず
./odoo-bin --addons-path="addons/,../enterprise/,../technical-training-sandbox" -d rd-demo
参考: Chapter2
チュートリアル
Docker環境で、Chapter3を進める。
Docker Hubの記載によると、/mnt/extra-addon
にマウントすればカスタムアドオンを使えるらしい。
この場合、compose.yaml
は以下のようになる。
services: web: image: odoo:16.0 container_name: odoo_server volumes: - ./technical-training-sandbox:/mnt/extra-addons depends_on: - db ports: - "8069:8069" db: image: postgres:15 container_name: odoo_postgres command: postgres -c log_destination=stderr -c log_statement=all -c log_connections=on -c log_disconnections=on env_file: - .env ports: - "5433:5432" volumes: - odoo_data:/var/lib/postgresql/data volumes: odoo_data:
あとはドキュメントの記載に倣って進め、./technical-training-sandbox
以下にaddonのファイルを配置することで、estateのアドオンを追加できた。
Odooを読む
業務システムのバックエンドの作り方を知りたいので、Odooから勉強する。
- 基本的な構成を理解
- 個々の機能を理解
- 機能追加時のDBの変更を確認する
基本的な構成
OwlによるSPAだが、JSONRPCを使っていて一般的なJSON色付けではなさそう。
docker composeで立ち上げる
- 5433でローカルから接続する
- クエリのログを確認できるようにする
services: web: image: odoo:16.0 container_name: odoo_server depends_on: - db ports: - "8069:8069" db: image: postgres:15 container_name: odoo_postgres command: postgres -c log_destination=stderr -c log_statement=all -c log_connections=on -c log_disconnections=on env_file: - .env ports: - "5433:5432" volumes: - odoo_data:/var/lib/postgresql/data volumes: odoo_data:
デフォルトのDBのテーブル定義書をA5:SQL Mk-2で出力
アドオンを追加して、テーブル定義書の差分をとることで、追加されたテーブルを確認する
在庫アドオンを追加すると、以下のテーブルが追加される
diff default_entities/index.html stock_entities/index.html | grep -oE "(\">).*?(</a>)" | sed -e 's/\">//g' | sed -e 's/<\/a>//g'
追加されたテーブル
account_analytic_account account_analytic_applicability account_analytic_distribution_model account_analytic_line account_analytic_plan barcode_nomenclature barcode_rule base_module_install_request base_module_install_review confirm_stock_sms crm_tag crm_team crm_team_member digest_digest digest_digest_res_users_rel digest_tip digest_tip_res_users_rel email_template_attachment_rel fetchmail_server iap_account iap_account_res_company_rel ir_act_server_res_partner_rel lot_label_layout lot_label_layout_stock_picking_rel mail_activity mail_activity_rel mail_activity_type mail_activity_type_mail_template_rel mail_alias mail_blacklist mail_blacklist_remove mail_channel mail_channel_member mail_channel_res_groups_rel mail_channel_rtc_session mail_compose_message mail_compose_message_ir_attachments_rel mail_compose_message_res_partner_rel mail_followers mail_followers_mail_message_subtype_rel mail_gateway_allowed mail_guest mail_ice_server mail_link_preview mail_mail mail_mail_res_partner_rel mail_message mail_message_reaction mail_message_res_partner_rel mail_message_res_partner_starred_rel mail_message_schedule mail_message_subtype mail_notification mail_notification_mail_resend_message_rel mail_resend_message mail_resend_partner mail_shortcode mail_template mail_template_mail_template_reset_rel mail_template_preview mail_template_reset mail_tracking_value mail_wizard_invite mail_wizard_invite_res_partner_rel message_attachment_rel payment_country_rel payment_icon payment_icon_payment_provider_rel payment_link_wizard payment_provider payment_provider_onboarding_wizard payment_token payment_transaction phone_blacklist phone_blacklist_remove picking_label_type picking_label_type_stock_picking_rel portal_share portal_share_res_partner_rel portal_wizard portal_wizard_res_partner_rel portal_wizard_user privacy_log privacy_lookup_wizard privacy_lookup_wizard_line procurement_group product_attribute product_attribute_custom_value product_attribute_product_template_rel product_attribute_value product_attribute_value_product_template_attribute_line_rel product_attr_exclusion_value_ids_rel product_category product_label_layout product_label_layout_product_product_rel product_label_layout_product_template_rel product_label_layout_stock_move_line_rel product_packaging product_pricelist product_pricelist_item product_product product_product_stock_track_confirmation_rel product_removal product_replenish product_replenish_stock_route_rel product_supplierinfo product_tag product_tag_product_product_rel product_tag_product_template_rel product_template product_template_attribute_exclusion product_template_attribute_line product_template_attribute_value product_variant_combination report_stock_quantity resource_calendar resource_calendar_attendance resource_calendar_leaves resource_resource res_country_group_pricelist_rel res_groups_spreadsheet_dashboard_rel res_partner_autocomplete_sync res_users_settings res_users_settings_volumes sms_composer sms_resend sms_resend_recipient sms_sms sms_template sms_template_preview sms_template_reset sms_template_sms_template_reset_rel snailmail_letter snailmail_letter_format_error snailmail_letter_missing_required_fields spreadsheet_dashboard spreadsheet_dashboard_group stock_assign_serial stock_backorder_confirmation stock_backorder_confirmation_line stock_change_product_qty stock_conflict_quant_rel stock_immediate_transfer stock_immediate_transfer_line stock_inventory_adjustment_name stock_inventory_adjustment_name_stock_quant_rel stock_inventory_conflict stock_inventory_conflict_stock_quant_rel stock_inventory_warning stock_inventory_warning_stock_quant_rel stock_location stock_lot stock_move stock_move_line stock_move_line_consume_rel stock_move_move_rel stock_orderpoint_snooze stock_orderpoint_snooze_stock_warehouse_orderpoint_rel stock_package_destination stock_package_level stock_package_type stock_package_type_stock_putaway_rule_rel stock_picking stock_picking_backorder_rel stock_picking_sms_rel stock_picking_transfer_rel stock_picking_type stock_putaway_rule stock_quant stock_quantity_history stock_quant_package stock_quant_stock_request_count_rel stock_quant_stock_track_confirmation_rel stock_replenishment_info stock_replenishment_option stock_request_count stock_return_picking stock_return_picking_line stock_route stock_route_categ stock_route_move stock_route_packaging stock_route_product stock_route_warehouse stock_rule stock_rules_report stock_rules_report_stock_warehouse_rel stock_scheduler_compute stock_scrap stock_storage_category stock_storage_category_capacity stock_traceability_report stock_track_confirmation stock_track_line stock_warehouse stock_warehouse_orderpoint stock_warn_insufficient_qty_scrap stock_wh_resupply_table team_favorite_user_rel uom_category uom_uom utm_campaign utm_medium utm_source utm_stage utm_tag utm_tag_rel
2023-07-02 06:36:28.977 UTC [36] LOG: statement: SELECT "stock_picking_type".id FROM "stock_picking_type" WHERE ("stock_picking_type"."active" = true) AND ("stock_picking_type"."company_id" in (1)) ORDER BY "stock_picking_type"."sequence" ,"stock_picking_type"."id" LIMIT 80 2023-07-02 06:36:28.978 UTC [36] LOG: statement: SELECT "stock_picking_type"."id" AS "id", "stock_picking_type"."color" AS "color", "stock_picking_type"."code" AS "code", COALESCE("stock_picking_type"."name"->>'ja_JP', "stock_picking_type"."name"->>'en_US') AS "name" FROM "stock_picking_type" WHERE ("stock_picking_type"."company_id" in (1)) AND "stock_picking_type".id IN (1, 2, 6) SELECT min("stock_picking".id) AS id, count("stock_picking".id) AS "picking_type_id_count" , "stock_picking"."picking_type_id" as "picking_type_id" FROM "stock_picking" WHERE ((("stock_picking"."state" = 'draft') AND (("stock_picking"."state" not in ('done', 'cancel')) OR "stock_picking"."state" IS NULL)) AND ("stock_picking"."picking_type_id" in (1, 2, 6))) AND ("stock_picking"."company_id" in (1)) GROUP BY "stock_picking"."picking_type_id" SELECT min("stock_picking".id) AS id, count("stock_picking".id) AS "picking_type_id_count" , "stock_picking"."picking_type_id" as "picking_type_id" FROM "stock_picking" WHERE ((("stock_picking"."state" in ('confirmed', 'waiting')) AND (("stock_picking"."state" not in ('done', 'cancel')) OR "stock_picking"."state" IS NULL)) AND ("stock_picking"."picking_type_id" in (1, 2, 6))) AND ("stock_picking"."company_id" in (1)) GROUP BY "stock_picking"."picking_type_id" SELECT min("stock_picking".id) AS id, count("stock_picking".id) AS "picking_type_id_count" , "stock_picking"."picking_type_id" as "picking_type_id" FROM "stock_picking" WHERE ((("stock_picking"."state" = 'assigned') AND (("stock_picking"."state" not in ('done', 'cancel')) OR "stock_picking"."state" IS NULL)) AND ("stock_picking"."picking_type_id" in (1, 2, 6))) AND ("stock_picking"."company_id" in (1)) GROUP BY "stock_picking"."picking_type_id" SELECT min("stock_picking".id) AS id, count("stock_picking".id) AS "picking_type_id_count" , "stock_picking"."picking_type_id" as "picking_type_id" FROM "stock_picking" WHERE ((("stock_picking"."state" in ('assigned', 'waiting', 'confirmed')) AND (("stock_picking"."state" not in ('done', 'cancel')) OR "stock_picking"."state" IS NULL)) AND ("stock_picking"."picking_type_id" in (1, 2, 6))) AND ("stock_picking"."company_id" in (1)) GROUP BY "stock_picking"."picking_type_id" SELECT min("stock_picking".id) AS id, count("stock_picking".id) AS "picking_type_id_count" , "stock_picking"."picking_type_id" as "picking_type_id" FROM "stock_picking" WHERE (((("stock_picking"."scheduled_date" < '2023-07-02 06:36:28') AND ("stock_picking"."state" in ('assigned', 'waiting', 'confirmed'))) AND (("stock_picking"."state" not in ('done', 'cancel')) OR "stock_picking"."state" IS NULL)) AND ("stock_picking"."picking_type_id" in (1, 2, 6))) AND ("stock_picking"."company_id" in (1)) GROUP BY "stock_picking"."picking_type_id" SELECT min("stock_picking".id) AS id, count("stock_picking".id) AS "picking_type_id_count" , "stock_picking"."picking_type_id" as "picking_type_id" FROM "stock_picking" WHERE ((("stock_picking"."backorder_id" IS NOT NULL AND ("stock_picking"."state" in ('confirmed', 'assigned', 'waiting'))) AND (("stock_picking"."state" not in ('done', 'cancel')) OR "stock_picking"."state" IS NULL)) AND ("stock_picking"."picking_type_id" in (1, 2, 6))) AND ("stock_picking"."company_id" in (1)) GROUP BY "stock_picking"."picking_type_id"
クエリした結果をみると、数字が多く何を意味しているかよくわからない。
id | picking_type_id_count | picking_type_id ----+-----------------------+----------------- 9 | 3 | 1 1 | 3 | 2
サーバーへのリクエストを見てみる
2023-07-02 07:00:25,722 1 INFO test werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:25] "GET /web HTTP/1.1" 200 - 10 0.008 0.014 2023-07-02 07:00:26,349 1 INFO test werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:26] "POST /web/action/load HTTP/1.1" 200 - 9 0.007 0.011 2023-07-02 07:00:26,425 1 INFO test werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:26] "POST /mail/init_messaging HTTP/1.1" 200 - 32 0.019 0.023 2023-07-02 07:00:26,708 1 INFO test werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:26] "POST /web/dataset/call_kw/res.users/systray_get_activities HTTP/1.1" 200 - 2 0.001 0.003 2023-07-02 07:00:26,800 1 INFO test werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:26] "POST /web/dataset/call_kw/stock.picking.type/get_views HTTP/1.1" 200 - 2 0.002 0.011 2023-07-02 07:00:26,823 1 INFO test werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:26] "GET /web/image/res.company/1/favicon HTTP/1.1" 304 - 5 0.003 0.008 2023-07-02 07:00:26,846 1 INFO test werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:26] "POST /mail/load_message_failures HTTP/1.1" 200 - 9 0.022 0.012 2023-07-02 07:00:26,849 1 INFO test werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:26] "POST /web/dataset/call_kw/stock.picking.type/web_search_read HTTP/1.1" 200 - 9 0.012 0.012 2023-07-02 07:00:26,852 1 INFO test werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:26] "GET /web/image/res.company/1/favicon HTTP/1.1" 304 - 5 0.007 0.012 2023-07-02 07:00:27,140 1 INFO ? werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:27] "GET /mail/static/src/audio/ting.ogg HTTP/1.1" 206 - 0 0.000 0.001 2023-07-02 07:00:27,146 1 INFO test werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:27] "GET /web/image/res.company/1/favicon HTTP/1.1" 304 - 5 0.002 0.006 2023-07-02 07:00:27,185 1 INFO test werkzeug: 172.27.0.1 - - [02/Jul/2023 07:00:27] "GET /web/image?model=res.users&field=avatar_128&id=2 HTTP/1.1" 304 - 9 0.007 0.010
コードを読む
from odoo.api import call_kw @http.route(['/web/dataset/call_kw', '/web/dataset/call_kw/<path:path>'], type='json', auth="user") def call_kw(self, model, method, args, kwargs, path=None): return self._call_kw(model, method, args, kwargs) def _call_kw(self, model, method, args, kwargs): check_method_name(method) return call_kw(request.env[model], method, args, kwargs)
def call_kw(model, name, args, kwargs): """ Invoke the given method ``name`` on the recordset ``model``. """ method = getattr(type(model), name) api = getattr(method, '_api', None) if api == 'model': result = _call_kw_model(method, model, args, kwargs) elif api == 'model_create': result = _call_kw_model_create(method, model, args, kwargs) else: result = _call_kw_multi(method, model, args, kwargs) model.env.flush_all() return result
{ "id": 3, "jsonrpc": "2.0", "method": "call", "params": { "model": "stock.picking.type", "method": "get_views", "args": [], "kwargs": { "context": { "lang": "ja_JP", "tz": "Asia/Tokyo", "uid": 2, "allowed_company_ids": [ 1 ], "params": { "action": 371, "model": "stock.picking.type", "view_type": "kanban", "cids": 1, "menu_id": 204 } }, "views": [ [ false, "kanban" ], [ false, "form" ], [ false, "search" ] ], "options": { "action_id": 371, "load_filters": true, "toolbar": true, "mobile": true } } } }
rpcでほぼ同じパラメータでリクエストしてるのを発見
this._cache.views[viewsKey] = rpc.query({ args: [], kwargs: { context, options, views: views_descr }, model, method: 'get_views', })
これは、web.rpcで定義されている
var rpc = require('web.rpc');
この実際の定義が見つからない。addons/web/static/src/legacy/js/core/rpc.js
にはあるが、legacyとあるのでこれを使っているのかがわからない。
次回、公式ドキュメントから学んでいく https://www.odoo.com/documentation/16.0/developer/tutorials/getting_started/01_architecture.html
SQSの動作を検証する①(配信遅延・可視性タイムアウト)
TL;DR
- 配信遅延を設定すると、メッセージを送信してから受信できるようになるまで配信遅延分のラグを生じさせる
- 可視性タイムアウトを設定すると、メッセージを受信してから同じメッセージを再度取得するまでのラグを生じさせる
- 受信したメッセージの可視性タイムアウトを変更することもできる
検証
ドキュメントに従って、Go SDKを用いてキューの動作を検証する。
サンプルコードを実行すると、
1. QUEUE_URL
で指定されたキューに対してメッセージを送信
2. QUEUE_URL
で指定されたキューからメッセージを受信
される。
なお、2.で受信した際にまだキューにメッセージがない場合、その旨を標準出力に出力してリトライする。
➜ sqs-example git:(main) go run main.go
出力
{"Attributes":{"SentTimestamp":"1678598881871"},"Body":"Information about current NY Times fiction bestseller for week of 12/11/2016.","MD5OfBody":"xxx","MD5OfMessageAttributes":"xxx","MessageAttributes":{"Author":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"John Grisham"},"Title":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"The Whistler"},"WeeksOn":{"BinaryListValues":null,"BinaryValue":null,"DataType":"Number","StringListValues":null,"StringValue":"6"}},"MessageId":"xxx","ReceiptHandle":"xxx"}
無事に出力された。
配信遅延
配信遅延を5秒に設定してみる。
再実行
➜ sqs-example git:(main) go run main.go
出力
message is empty message is empty message is empty message is empty message is empty {"Attributes":{"SentTimestamp":"1678599920508"},"Body":"Information about current NY Times fiction bestseller for week of 12/11/2016.","MD5OfBody":"xxx","MD5OfMessageAttributes":"xxx","MessageAttributes":{"Author":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"John Grisham"},"Title":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"The Whistler"},"WeeksOn":{"BinaryListValues":null,"BinaryValue":null,"DataType":"Number","StringListValues":null,"StringValue":"6"}},"MessageId":"xxx","ReceiptHandle":"xxx"}
1秒ごとにリトライするので、配信遅延を5秒に設定すると5回リトライし、6回目にメッセージを取得する。
可視性タイムアウト
メッセージを取得後に可視性タイムアウトが始まる。
可視性タイムアウトが時間切れになるまでの間に取得したメッセージを削除しなかった場合、該当のメッセージは再度取得できるようになる。
取得後のメッセージを削除しないように変更し、再実行。
なお、可視性タイムアウトは5秒に設定している
➜ sqs-example git:(feature-visibility-timeout-test) go run main.go
出力
{"Attributes":{"SentTimestamp":"1678662317827"},"Body":"Information about current NY Times fiction bestseller for week of 12/11/2016.","MD5OfBody":"xxx","MD5OfMessageAttributes":"xxx","MessageAttributes":{"Author":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"John Grisham"},"Title":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"The Whistler"},"WeeksOn":{"BinaryListValues":null,"BinaryValue":null,"DataType":"Number","StringListValues":null,"StringValue":"6"}},"MessageId":"xxx","ReceiptHandle":"xxx"} message is empty // 1秒 message is empty // 2秒 message is empty // 3秒 message is empty // 4秒 message is empty // 5秒 message is empty // 6秒 {"Attributes":{"SentTimestamp":"1678662317827"},"Body":"Information about current NY Times fiction bestseller for week of 12/11/2016.","MD5OfBody":"xxx","MD5OfMessageAttributes":"xxx","MessageAttributes":{"Author":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"John Grisham"},"Title":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"The Whistler"},"WeeksOn":{"BinaryListValues":null,"BinaryValue":null,"DataType":"Number","StringListValues":null,"StringValue":"6"}},"MessageId":"xxx","ReceiptHandle":"xxx"} message is empty // 1秒 message is empty // 2秒 message is empty // 3秒 message is empty // 4秒 message is empty // 5秒 message is empty // 6秒 message is empty // 7秒 {"Attributes":{"SentTimestamp":"1678662317827"},"Body":"Information about current NY Times fiction bestseller for week of 12/11/2016.","MD5OfBody":"xxx","MD5OfMessageAttributes":"xxx","MessageAttributes":{"Author":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"John Grisham"},"Title":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"The Whistler"},"WeeksOn":{"BinaryListValues":null,"BinaryValue":null,"DataType":"Number","StringListValues":null,"StringValue":"6"}},"MessageId":"xxx","ReceiptHandle":"xxx"}
少なくとも5秒間は間をあけないと、新しいメッセージを取得できないことがわかる。
さらに、ChangeMessageVisibilityを用いて受信したメッセージの可視性タイムアウトを5秒ずつ延ばしてみる
➜ sqs-example git:(feature-expand-visibility-timeout-by-execution) ✗ go run main.go
出力
{"Attributes":{"SentTimestamp":"1678663932591"},"Body":"Information about current NY Times fiction bestseller for week of 12/11/2016.","MD5OfBody":"xxx","MD5OfMessageAttributes":"xxx","MessageAttributes":{"Author":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"John Grisham"},"Title":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"The Whistler"},"WeeksOn":{"BinaryListValues":null,"BinaryValue":null,"DataType":"Number","StringListValues":null,"StringValue":"6"}},"MessageId":"xxx","ReceiptHandle":"xxx"} message is empty // 1秒 message is empty // 2秒 message is empty // 3秒 message is empty // 4秒 message is empty // 5秒 message is empty // 6秒 message is empty // 7秒 {"Attributes":{"SentTimestamp":"1678663932591"},"Body":"Information about current NY Times fiction bestseller for week of 12/11/2016.","MD5OfBody":"xxx","MD5OfMessageAttributes":"xxx","MessageAttributes":{"Author":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"John Grisham"},"Title":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"The Whistler"},"WeeksOn":{"BinaryListValues":null,"BinaryValue":null,"DataType":"Number","StringListValues":null,"StringValue":"6"}},"MessageId":"xxx","ReceiptHandle":"xxx"} message is empty // 1秒 message is empty // 2秒 message is empty // 3秒 message is empty // 4秒 message is empty // 5秒 message is empty // 6秒 message is empty // 7秒 message is empty // 8秒 message is empty // 9秒 message is empty // 10秒 message is empty // 11秒 {"Attributes":{"SentTimestamp":"1678663932591"},"Body":"Information about current NY Times fiction bestseller for week of 12/11/2016.","MD5OfBody":"xxx","MD5OfMessageAttributes":"xxx","MessageAttributes":{"Author":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"John Grisham"},"Title":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"The Whistler"},"WeeksOn":{"BinaryListValues":null,"BinaryValue":null,"DataType":"Number","StringListValues":null,"StringValue":"6"}},"MessageId":"xxx","ReceiptHandle":"xxx"}
1回目のメッセージ取得は0秒、2回目は少なくとも7(>5)秒、3回目は11(>10)秒のインターバルがある。
これにより、取得したメッセージの可視性タイムアウトを操作できることがわかる。
また、受信したメッセージの可視性タイムアウトを0に設定することもできる。
コードを変更し、実行
➜ sqs-example git:(feature-terminate-visibility-timeout) go run main.go
出力
{"Attributes":{"SentTimestamp":"1678664647087"},"Body":"Information about current NY Times fiction bestseller for week of 12/11/2016.","MD5OfBody":"xxx","MD5OfMessageAttributes":"xxx","MessageAttributes":{"Author":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"John Grisham"},"Title":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"The Whistler"},"WeeksOn":{"BinaryListValues":null,"BinaryValue":null,"DataType":"Number","StringListValues":null,"StringValue":"6"}},"MessageId":"xxx","ReceiptHandle":"xxx"} {"Attributes":{"SentTimestamp":"1678664647087"},"Body":"Information about current NY Times fiction bestseller for week of 12/11/2016.","MD5OfBody":"xxx","MD5OfMessageAttributes":"xxx","MessageAttributes":{"Author":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"John Grisham"},"Title":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"The Whistler"},"WeeksOn":{"BinaryListValues":null,"BinaryValue":null,"DataType":"Number","StringListValues":null,"StringValue":"6"}},"MessageId":"xxx","ReceiptHandle":"xxx"} message is empty {"Attributes":{"SentTimestamp":"1678664647087"},"Body":"Information about current NY Times fiction bestseller for week of 12/11/2016.","MD5OfBody":"xxx","MD5OfMessageAttributes":"xxx","MessageAttributes":{"Author":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"John Grisham"},"Title":{"BinaryListValues":null,"BinaryValue":null,"DataType":"String","StringListValues":null,"StringValue":"The Whistler"},"WeeksOn":{"BinaryListValues":null,"BinaryValue":null,"DataType":"Number","StringListValues":null,"StringValue":"6"}},"MessageId":"xxx","ReceiptHandle":xxx"}
間髪入れずにメッセージを取得できることが確認できた。
GCPの基本(リソース階層・IAM)
- 組織、フォルダ、プロジェクト、リソースに対しての権限を付与
- 複数の権限をロールにまとめてプリンシパルに設定
ロール: 権限の集合
ポリシー: プリンシパルへのロールの割り当て
ロールと権限を紐づける画面は以下のような感じ
基本ロール
- Browser: フォルダ、組織、IAMポリシーなどのプロジェクトの階層の閲覧権限。リソースの表示権限はない
- Viewer: 閲覧者
- Editor: Viewer権限に加え、リソースの編集権限
- Owner: すべてのEditor権限に加え、リソースの権限管理、課金情報設定が可能
権限の一つ一つは、以下のように表される
- compute.instances.list: VMインスタンスの一覧
- compute.instances.stop: VMインスタンスの停止
- pubsub.topics.publish: トピックへのパブリッシュ
まとめ
microservices-demoをAzureにデプロイする その1
結論
Azureにデプロイできた。k8sのマニフェストは何の修正もいらなかった。
概要
前回の記事ではAWSにデプロイしたが、Azureにもmicroservices-demoをデプロイしたい。
Kubernetesリソースの差分
CSP固有のサービスを使う箇所はtype: LoadBalancer
のみ。
GKEとAWSでは、それぞれ以下のタイプのLBだった。
GKE: 外部TCP/UDPロードバランサ
AWS: Internet-Facing NLB
AzureはおそらくパブリックAzure Load Balancerだろう。
実行
①AKSクラスタの作成
ドキュメントに従って、クラスタを作成する。
以下のように設定する。
その他は、すべてデフォルトの値でデプロイする。
②kubectlの設定
az login az account set --subscription "従量課金" az aks get-credentials --resource-group rg-aks-poc --name poc-cluster k get nodes NAME STATUS ROLES AGE VERSION aks-agentpool-xxxxxxxxx-vmss000000 Ready agent 20m v1.24.9 aks-agentpool-xxxxxxxxx-vmss000001 Ready agent 19m v1.24.9
Nodeを確認できた。
③yamlの修正
特になし!!何もせずにAKSにデプロイできるということになる
④k8sリソース作成
kubectl apply -f ./release/kubernetes-manifests.yaml
Podの作成完了
k apply -fしてからPodがすべて立ち上がるまでが明らかに早い気がする。20秒くらいですべてデプロイし終わった。
デプロイも完了
作られたリソースの確認
NSGは以下の通り
Azure Load Balancer
フロントエンドIP
バックエンドプール
アウトバウンドルール
インバウンドルール
ワーカーノード
microservices-demoをAWSにデプロイする その1
結論
AWSにデプロイすることはできた。
しかしFargateが使えない分、GKEと比べるとやや面倒1。
概要
microservices-demoを用いて、Kubernetesのアプリケーションの検証をしたい。
GKEのクラスタを作成後、以下を実行することで実現できる。
kubectl apply -f ./release/kubernetes-manifests.yaml
これをAWSでもデプロイできるようにしたい。
※EKS Fargateにアプリケーションがデプロイされるだけで、Pod間の通信制御などは行われない
Kubernetesリソースの差分
リポジトリをクローンしてきて、kubernetes-manifests.yaml
を確認する。
Ingressリソースはないため、L7ロードバランサはない。その代わり、以下のLoadBalancerを作成した際にGKEではLoadBalancer Serviceが作成される
apiVersion: v1 kind: Service metadata: name: frontend-external spec: type: LoadBalancer selector: app: frontend ports: - name: http port: 80 targetPort: 8080
インターネットからのアクセスが可能なので、作成されるのは外部 TCP / UDP ロードバランサに該当する。
LoadBalancer サービスを作成すると、GKE により、その特性がサービス マニフェストのパラメータに依存する Google Cloud のパススルー ロードバランサが構成されます。 https://cloud.google.com/kubernetes-engine/docs/concepts/service-load-balancer?hl=ja
外部 TCP / UDP ロードバランサはSSLを終端しないロードバランサ(NLBのようなもの)らしい。
パススルー ロードバランサでは、クライアント接続を終端しません。ロード バランシングされたパケットは、パケットの送信元、宛先、ポート情報(該当する場合)が変更されずにバックエンド VM によって受信されます。
したがって、Internet-facingなNLBを作成して、Podにリクエストするようにすればとりあえずデプロイはできる。
AWSでtypeがLoadBalancer
のServiceを作成するとCLBかNLBが作成される。この方法は推奨されていないが、一旦はこの方法を用いる。
実行
①EKSクラスタの作成
②EKS Nodeの作成
③yamlの修正
kubernetes-manifests.yaml
を以下のように修正(service.beta.kubernetes.io/aws-load-balancer-type: nlb
をアノテーションに追加)
kind: Service metadata: name: frontend-external + annotations: + service.beta.kubernetes.io/aws-load-balancer-type: nlb spec: type: LoadBalancer selector: app: frontend ports: - name: http port: 80 targetPort: 8080
④k8sリソース作成
kubectl apply -f ./release/kubernetes-manifests.yaml
Podの作成完了
デプロイも完了
- Fargateを用いようとするとAWS Load Balancer Controller add-onを使う必要があるなど、差分が大きい↩
runcを読む その1
モチベーション
ある日、Youkiというコンテナランタイムが出てきて衝撃を受けた。
自分もコンテナが何か知りたくなったので、runcを読むことにした。
正攻法と挫折
OCIの仕様を読み込むところから始める。
…と思ったが、抽象的過ぎてイメージがつかめない。
ふんわりと分かったこと
①以下のOperationがある
- state
- create
- start
- kill
- delete
②コンテナランタイムを起動するためにはbundle
とconfig.json
が必要
runcの大体の動作を把握する
実際に動かしてみる。
ubuntu18.04からbundle
を取得する
docker run --rm -d --name ubuntu ubuntu:18.04 tail -f /dev/null docker export ubuntu > rootfs.tar docker kill ubuntu mkdir rootfs tar xf rootfs.tar -C rootfs
runc は、runc specで適当なconfig.json
を生成できる。
runc spec
準備完了。runcを実行する。 確かにUbuntu18.04LTSのイメージが実行されている
sudo runc run ubuntu # cat /etc/os-release NAME="Ubuntu" VERSION="18.04.6 LTS (Bionic Beaver)" ID=ubuntu (中略) # exit cat /etc/os-release NAME="Ubuntu" VERSION="20.04.5 LTS (Focal Fossa)" ID=ubuntu (中略)
runc runを実行できた。 次回から、runc のコードリーディングを本格的に進めていく。