from 30

30歳からwebエンジニアになったけど、思ったよりも苦しいので、その苦闘の記録をば

2021年の目標

[1] 2020年まとめ

(1) 仕事関連

  • 1 ~ 3月でAngel Dojo Project参加して、ベストアーキテクト賞を受賞
  • 1月: 不動産系ツール開発プロジェクト(SAM, Lambda(node.js), StepFunctions, CloudWatchEvents, S3)
  • 2 ~ 3月: コーポレートサイトのサーバレス化とIPv6対応(S3, Lambda(Python), ALB, CloudFront, Route53)
  • 4月 ~ 現在: 不動産系企業のフルリプレイス(CDK, Lambda(Python), Cognito, DynamoDB, S3, CloudFront, Nuxt(TypeScript), Elasticsearch, EventBridge)

(2) 資格関連

  • 8月: AWS Certified Solution Architect Professional
  • 9月: AWS Certified DevOps Engineer Professional

(3) 登壇等

  • 4月: AWS Partner SummitでAngel Dojoの発表
  • 11月: AWS Connectの顧客事例で登壇

(4) 転職活動

  • 内定 ... 5社もらえたが、年収は現職横ばい。現職年収低めと思ってたからガッカリ。
  • 落ちた ... ある程度の社格のメガベンチャーからはもらえない
  • 結果 ... 現職残留

[2] 2021年目標

(1) 仕事

  • 客や仕事を選べない以上目標にする意味ない
  • 残業はしない
  • 客とちゃんと話す

(2) 学習

(3) 所感

さて、このブログを書き始めた理由は、

  • どんどん高まっていく要求
  • 要求に応えているのに改善しない待遇

というところで、転職を決意したためでした。

転職活動して一応は数社内定を受けたものの、現職横ばいの待遇で世間の厳しさを実感。
「1年で仕事辞める根性なし。実力も素人に毛が生えた程度」って言うのが実質の評価でした。

とはいえ、今よりはよくなるので退職を申し出ました。
一応現職は年収上げる、残業抑制ということを口約束してくれた。
約束を反故にされたとわかった瞬間に再度転職することを心に誓い残留決定。

残留・転職問わず、当面の目標は年収800万円にもっていくことなので、
現職にせよ転職先にせよ給与アップの交渉材料が欲しいので、
まずはAWS Top Engineerに選出してもらえるように努力をします。

を3月までは全力で取り組みます。

そこがある程度形になったら、今度はエンジニアとして割と硬派な知識を身に付けたいと思っています。
正直、CSやアルゴリズムは実は実務にはあんまり影響が大きくないと思っている。
なのでまずは設計やら開発手法やらの本を読んでいこうと思う。
一年かけて上記の学習リソースは最低限身に付けたいなと思っています。

以上。

今更ながらDockerについて整理してみる[docker-compose / ネットワーク]

概要

前回・前々回でmac上での操作、Dockerfileの作り方についてまとめた。

Dockerfileもとにdocker buildでコンテナイメージを作成して、docker runコマンドでコンテナを立ち上げているが、docker runコマンドでは、ボリュームやポート番号やユーザーなどなど色々とオプションで渡す必要がある。

逐一書いていては面倒だし、同一ホストで複数コンテナを立ち上げる場合などはとても面倒である。

ということで、そう言う時に使用するのが、docker-composeコマンドである。

なお、実環境においては、docker-composeは単一ホストでしか機能しないため、 冗長性やスケーリングの面で不十分である。 (その辺はゆくゆくECSやEKSのまとめでも書いてみよう...)

あくまで手元のオペレーションを効率化するというような視点で捉えるべきである。

1. 基本例

$  docker run --name docker_nginx --env env_key=env_value -p 9090:80 -v ${pwd}:/usr/share/nginx/html nginx

上のdocker runコマンドをdocker-composeで書くと以下。

  • version
  • services ... ここの下にコンテナの種類ごとに書いていく
  • nginxの箇所 ... サービス名を宣言
  • image ... コンテナイメージを指定。docker runコマンドの引数
  • container_name ... --nameオプションに対応
  • environment ... --envオプションに対応
  • ports ... -pオプションに対応
  • volumes ... -vオプションに対応
  • command ... docker runコマンドの第二引数に相当
version: "3.8"
services:
  nginx:
    image: nginx:latest
    container_name: docker_compose_nginx
    environment:
      - env_key=env_value
    ports:
      - "80:80"
    volumes:
      - ${PWD}:/usr/share/nginx/html:ro

docker-composeファイルを元にコンテナ起動するコマンドは以下

  • -f ... docker-compose.ymkのパスを指定
  • up ... 起動
  • down ... 停止
  • logs ... ログを表示
    • --follow ... tailできる
$ docker-compose -f docker-compose.yaml up
$ docker-compose -f docker-compose.yaml down
$ docker-compose -f docker-compose.yaml logs --follow # --followがあるとtailできる

2. 複数コンテナを書く

docker-composeではservices以下に複数のコンテナを記載することも可能である。

version: "3.7"
services:
  nginx:
    image: nginx:latest
    container_name: docker_compose_nginx
    environment:
      - env_key=env_value
    ports:
      - "80:80"
    volumes:
      - ${PWD}:/usr/share/nginx/html:ro
  ubuntu:
    image: ubuntu
    container_name: docker_compose_ubuntu
    ports:
      - "8080:8080"

3. コンテナをレプリケーションする

docker-compose upする際に--scaleオプションでコンテナのレプリケーションが可能である。

$ docker-compose -f docker-compose.yaml up --scale nginx=3

以下の点に注意

  • port番号を範囲にする
  • container_nameをつけることはできない
version: "3.7"
services:
  nginx:
    image: nginx:latest
    environment:
      - env_key=env_value
    ports:
      - "80-85:80"
    volumes:
      - ${PWD}:/usr/share/nginx/html:ro

今更ながらDockerについて整理してみる[Dockerfile]

経緯

手元のmacで動かすコマンド例については前回まとめました。

で、そこの最後でコンテナの中で実行したlibraryやファイルの操作をdocker commitコマンドでイメージにする方法を記載した。

でも、それだとDockerイメージの構成要素がわからなくなってしまうため、基本的にはその方法ではイメージを作成はしないようだ。

というか、実務ではそうやってイメージを作って環境設定ができたらDockerfileに清書するような感覚のようである。

ということでDockerfileについて書く

1. Dockerfileを作成する

概要

docker commitで作成したイメージは構成レイヤーがわかりづらい。 Dockerfileを使用すれば明確化できる。

まずはdocker hubのpublicイメージを元にdockerファイルをみにいく f:id:kohski:20201205162515p:plain

最低限のDockerImageを作成

まずは最低限のdockerfileを書く

FROM ubuntu:latest
# テストファイルを作成
RUN touch test

次にbuildのコマンドを打つ

$ docker build -t <name> <directory>
  • -t を除くと、dangling imageができる
  • -f ... buildcontextにDockerfileがない場合や、環境に応じてDockerfileを使い分けている場合

dirctoryはDockerfileがある場所を指す(大抵はカレントディレクトリの.でOK)

2. インストラクション

Dockerfileにおける、FROM, RUN, COMMANDなどのステートメントのこと

(1) FROM

  • ベースイメージを登録
  • alpine
  • ubuntuなどが多い

(2) RUN

  • linuxコマンドを実行
  • RUN(COPY / ADD)ごとにレイヤーが作成される
  • && コマンドの連結
  • \で改行が可能
  • レイヤーに変更がなければホスト側に残っているキャッシュが使用される
  • libraryのinstall と application codeのinstallは別のlayerにした方がいい。(更新頻度が違うので)
  • aptには-yオプションを入れる
FROM ubuntu:latest
RUN apt-get update && apt-get install -y curl nginx

(3) CMD

CMD ["executable", "param1", "param2" ... ]
  • デフォルトコマンドの定義
  • ubuntu:latestの場合は"/bin/bash"
  • 原則最後に記載
  • 原則一つ
  • RUNと異なりCMDはレイヤーを作らない

(4) COPY

  • buildコンテキスト内のファイルをImageにコピーする
  • 使用頻度が高い
FROM ubuntu:latest
RUN mkdir /new_dir
COPY something /new_dir/

(5) ADD

  • ADDは高機能
  • ADDの用途はtarファイルの圧縮と解凍の際に使用することが多い
  • 基本的にはCOPYを使用した方がいい
FROM ubuntu:latest
ADD compressed.tar /
  • 参考)tar圧縮
$ tar -cvf compressed.tar sample_package/

(6) ENTRYPOINT

  • docker runのときに使用するコマンドを書く
  • docker run コマンドの第二引数で上書きできない
  • コマンドはENTRYPOINTでオプションはCMDで書く
  • CMDに書いたオプションは上書き可能
FROM ubuntu:latest
RUN touch test
ENTRYPOINT [ "ls" ]
CMD ["--help"]

(7) ENV

環境変数をDockerImageに設定する

FROM ubuntu:latest
ENV key1 value
ENV key2=value
ENV key3="v a l u e" key4=v\ a\ l\ u\ e
ENV key5 v a l u e

(7) WORKDIR

以下のDockerfileだと3つめのRUNはrootディレクトリで実行される Image上でのworkingディレクトリを移動する。

FROM ubuntu:latest
RUN mkdir sample_folder
RUN cd sample_folder
RUN touch sample

cdを有効にしたいなら以下

FROM ubuntu:latest
WORKDIR sample_folder
RUN touch sample

ディレクトリがない場合は、作成もしてくれる。

3. Dockerfileの作り方のイメージ

1.curlとnginxをinstall

FROM ubuntu:latest
RUN apt-get update && apt-get install -y curl nginx

2.作成した後でcvsが必要だと気づいた場合は以下のようにRUNをわけることで、curlとnginxのinstallのレイヤーはキャッシュが使われる

FROM ubuntu:latest
RUN apt-get update && apt-get install -y curl nginx
RUN cvs

3.全て完了したら一つのRUNにまとめてレイヤーの圧縮を図る

FROM ubuntu:latest
RUN apt-get update && apt-get install -y \
curl \
nginx \
cvs

4. docker buildコマンドについて

構成要素

  • docker client
  • docker daemon
    • network, volume等のdocker objectとの連携を行っている
  • docker host
  • docker resistory

build context

  • buildするときの環境
  • Dockerfile以下の階層にビルドに使用しないファイルはおかない。

参考) ファイル名の取得

$ du -sh <file名>

今更ながらDockerについて整理してみる[コマンド・基礎]

経緯

今年のre:inventもまあ盛り沢山な内容ですが、例に漏れずサーバレス関連も非常にたくさんのアップデートがあります。

中でも比較的注目されているのが、Lambdaでコンテナイメージがサポートされたことでしょうか。

実行時間やメモリなどはそのままなので、あくまで関数実行モデルはそのままに、コード実行よりも一つ下の層のコントロールが可能になったと言う感じでしょうか。

twitterで聞きかじった感じでは、GCPのCloudFunctionsでは既にコンテナイメージが使えるとのこと。

いずれにせよ、今までなんとなく避けてきたコンテナ関連について一度がっつり向き合ってみようと思い本記事を書くにいたりました。

今までのコンテナ経験

ANGEL Dojo等の催しを通して、一通りのハンズオンでrailsやexpressのアプリケーションをECSへデプロイしてみたことはやったことあります。

あとは、Lambdaのruntimeでpython3.8からAmazonLinux2ベースになっているので、ちょっとした検証をしたことはあるかなって程度です。

$ docker run -it amazon/aws-sam-cli-build-image-python3.8:latest /bin/bash

ぼんやりコマンドのことはわかるけど、細かくはわからないというのが現状です。

まあ、逆に言えば、この程度のことも知らずにそれらしいものを作れるのだから、AWSにおけるサーバレス関連のエコシステムが如何に完成度が高いのかってのの証左でもありますよね(自分を棚にあげた言い訳)

全体像

まずは手元のPCでどうオペレーションするのかというところを中心に以下のような範囲でまとめてみました。

f:id:kohski:20201206180904p:plain

以降はユースケース別にコマンドやオプションについてわかったことを触れてみます。

1. Docker Imageを取得する

docker imageをpullしてくる

$ docker pull <イメージ名>
  • <イメージ名/>のところは<hostname>:<port>/<username>/<repository>:<tag>が省略しない形
  • hostname = regitory-1.docker.io / username = library / tag = latestがデフォルトになっている。
  • registryをdockerhubから変更したい時(ECRとか)はhostnameのを省略せずに記載する。

2. Docker Imageを削除する

docekr rmi <イメージ名 | イメージID>

3. Dockerコンテナを作成して起動する

$ docker run hello-world
$ docker run -it ubuntu bash
  • imageを元にcontainerを起動
  • 第二引数はデフォルトコマンド(DockerfileのCMDインストラクション)を上書きしている
  • Hostにイメージがない時は、dockerhubからpullしてくる。
  • run = create + start
$ dcoker create <コンテナ名 | コンテナID>
$ docker start <コンテナ名 | コンテナID>
  • オプション
    • -i ... インタラクティブ。STDINを開いたままにする
    • -t ... TTYの略。擬似TTYの割当。表示がきれいになる
    • --name
      • コンテナに名前をつける
      • 共有サーバーでコンテナを立てる時などに使用
      • 名前が重複する場合はエラーになるので注意 run = create + start
    • -d ... ディタッチドモード
    • --rm ... Exitになったときにコンテナを削除する(ちょっと手元で試すときに多用)
    • -v
    • -u
      • ユーザーとグループの指定
      • <ユーザ名>:<所有グループ>
    • -p
      • publishの略
      • <ホストのポート>:<コンテナのポート>
    • --cpus ... コンテナがアクセスできる上限のCPUを設定
    • --memory <bytes> ... メモリの上限
docker run -v $(pwd)/mounted_folder:/new_dir -u $(id -u):$(id -g) -it <コンテナID> bash
docker run -it a146787c385e bash

4. コンテナの一覧を確認する

起動中のdockerのコンテナ一覧

$ docker ps
$ docker ps -a
  • -a ... 停止中のコンテナも含めて表示

5. コンテナのプロセスに入る

$ docker exec -it <コンテナID / コンテナ名> bash

6. コンテナから出る

detach

docker ps -a するとstatusがUpのままになる vscodeのterminalを使っているとキーバインドの関係でうまくいかないので、iTermとかで試すといいかも。

ctrl + p + q 

なお、入り直す時は、

$ docker attach <コンテナID / コンテナ名> bash

exit

プロセスを殺して出る。

# exit

docker ps -a するとstatusがExitedになる

7. コンテナを削除する

$ docker rm <コンテナID>

StatusがCreate, Existedなものは削除可能

$ docker stop <コンテナID> <コンテナID> ...

Up状態のコンテナをExitedにする

一括してstopにしたいときは以下のようにコマンドのネスト実行が有効。以下2つは同じ意味。

$ docker stop $(docker ps -a -q)
$ docker stop `docker ps -a -q`
$ docker system prune

stopしているコンテナや

8. コンテナから直接イメージを作成する

まずはdocker execしてコンテナ内で作業 その後、docker commitする

$ docker commit <対象のコンテナID> コンテナ名称
$ docker tag <イメージ名:タグ> <dockerhubのユーザー名/リポジトリ名>
$ docker push <dockerhubのユーザー名/リポジトリ名>

変更されたレイヤーだけpushされる 元にしていたubuntuイメージはdockerhub内で共有されている

f:id:kohski:20201205125927p:plain

rasberry piでdocker を使えるようにする

経緯

最近dockerの学習をしている。

--network hostなdocker runを試そうとしているが、 どうやらdocker on maclinuxだと挙動が違う模様

linuxで試そうと思ったので、兼ねてからlinuxサーバーとして使っているラズパイにdockerを落とそうというわけである。

前提

dstributionのversionは以下

$ cat /etc/debian_version
10.4

方法

dockerのinstall

こちらのブログを参考にしている

$ curl -sSL https://get.docker.com | sh

installできたかを確認

$ docker -v

docker-composeのinstall

docker-composeはpip3経由が無難

pip3 install docker-compose

installできたかを確認

$ docker-compose -v

dockerコマンドのpermittionについて

もろもろのinstallが済んだので喜び勇んでdocker psコマンドを打ってみたが、 以下のエラーが出る。

Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/containers/json: dial unix /var/run/docker.sock: connect: permission denied

こちらの記事を参考にさせてもらって、以下のように対処

$ id {現在使っているユーザー}    #属しているgroupを確認
$ sudo gpasswd -a {現在使っているユーザー} docker

まとめ

これでdockerをrasbery pi上で使えるようになった。 とはいえ、rasbery piはARMアーキテクチャDellのx_86とは違いが大きいようである。 この辺の記事拝見するところ、 あんまりラズパイ上でdockerって使えないっぽい。 まあ、今後調べてみます。

PytestでStepInやbreakpointを使う

経緯

普段の開発ではpytestを使用して、TDDを行っている。

pythonのテストといえば、python標準のunittestもあるが、テスト結果の表示や制御のしやすさからpytestを選択する人が周りでは多いように思う。

で、pytestを使用しているが、今のプロジェクトではconftestでtime freezeやmotoを読み込んでいて、

テストのコンテキストでいろいろとモジュールが用意されていないとテストができない。

vscodeの機能でpytestを実行する機能もあるが、テストディスカバリーがうまく動かずという状態だった。

ということで、自分の悪い癖だが、我慢すればprintデバッグでも問題ないのでずっと我慢していた。

とはいえ、テストの中にデバッガーを仕掛けておかないと、値の検証やなんかがしづらい。

ということで、breakpointやstep実行の仕方について触れたい。

方法

方法はいたって簡単。

pytestを実行している時にpdb(python標準のデバッガー)が使えるのでそれを使うだけである

breakpointを使用したいファイルで

import pdb

その後、breakpointを使用したい箇所で以下のように記載

pdb.set_trace()

pytestを実行すると以下のようなpromptが開くので、

(pdb) s # ステップイン
(pdb) n # ステップオーバー
(pdb) c # 次のブレークポイントへ
(pdb) {変数名} # 変数の表示

と言った感じで使えます。

まとめ

我慢は禁物でちゃんとやり方を調べるべき。 そして、テスティングツールとして当然breakpointの当て方はあるので、ちゃんと公式ドキュメントを読むべきだった。

Cloud Watch Logs Insightsの簡単な使い方

経緯

f:id:kohski:20201118001708p:plain
構成図

顧客のSQL Server on RDSのデータを変換して DynamoDBに入れ直すということをしている。

大体上記のようなアーキテクチャでやっており。

  1. cronジョブでLambdaを5分おきに起動。
  2. RDSの最新の変更をクエリ
  3. DynamoDBに入るように変換
  4. DynamoDBからはDynamoDB Triggerで別のLambdaを起動(構成図上は省略)
  5. ElasticsearchにHTTP POSTしてdocmentを投入する

という作業をしている。

ある日朝起きてみると DynamoDB = 47万件 Elasticsearch = 42万件

「うわぁ、5万件落ちてる。」 「多分ESのmappingsとDDB Triggerから投入したデータがあってないんだわ。」

そして、cloudwatchをみると。。。 47万件の成否が入り混じったログが大量放出されている。 f:id:kohski:20201118003614p:plain

ログストリームを一つ一つ開いて、 [ERROR]接頭辞がついたログをみるのは面倒だ。

ということで調べてみるとログストリーム跨ぎで独自のクエリ構文でログ検索をできるCloudwatch Logs Insightsなるサービスがあるようではないか。

使ってみた

いつものcloudwachの左ペイン Cloudwach Logsの「ログ」メニューのすぐ下の「インサイト」があるではないか。

f:id:kohski:20201118003651p:plain

で、大体添付のようなイメージで処理が可能 ドキュメントはこちら

  • fields ... 読んで時の如くフィールドを指すようだ。(SELECT のあとのフィールド指定に近い?)
  • filter ... 正規表現を使用してフィルターができる。詳細はこちら
  • stats ... 計数処理ができる
  • sort ... 並び替えが可能。右ペインをみると使用可能なフィールドが列挙されている模様
  • limit ... デフォルトで1,000件。1 ~ 10,000件まで選択可能なようだ。

という感じで少しSQLを触ったことがあればすぐになれて使えそうです。

まとめ

Lambdaなんかでは何もせずともCloudwatchにログがでます。

私は従来、単発のエラーのログを探すようなことが多かったので、ロググループから目グレップしていました。

もう少し広範囲で曖昧な検索をしたい場合は、Cloudwatch Logs Insightsは大いに役立ちそうです。