【AWS】Ansible & KanikoでECRにDockerイメージをpushする

 Kubernetesクラスタ上でDockerイメージをビルド&プッシュするためのツール「Kaniko」について、仕様を調査したのでメモ。

Kanikoとは

 実体はKubernetesクラスタ上でDockerイメージをビルド、プッシュを行うためのOSSのDockerイメージです。
Googleが開発しているようですが、公式サポートはしていないようです。
 READMEを見ると各クラウド環境のレジストリへのプッシュにも対応しており、使いやすそうな印象です。

github.com

前提条件

 前回使用したEC2、ECR、ソースコードをそのまま使用します。
Ansibleで直接ECRにプッシュしていた箇所を、Kanikoポッドでプッシュするように変更してみます。

tm200.hatenablog.com


 KanikoポッドがECRにイメージをプッシュするには、稼働環境(EC2/EKS)にIAMロールを付与するか、IAMロールが付与されたユーザーのクレデンシャルをコンテナに渡す必要があります。
今回は前回と同じユーザーを使用するため、クレデンシャルのSecretを作成しました。

$ k create secret generic aws-secret --from-file=.aws/credentials -n ansible-test


 AWS ECR周りの設定は、この辺りに詳細が記載されています。

ディレクトリ構成

.
├─ deploy-image.yml     実行するAnsible playbook
├─ Dockerfile           プッシュするイメージのDockerfile
├─ Jenkinsfile          イメージに含めるJenkinsfile
├─ inventory
│  └─ hosts
└─ roles
   └─ k8s-kaniko
       └─ tasks
             ├─ kaniko.yml    Kanikoポッドを立ち上げる為のマニフェスト
             └─ main.yml      Ansibleのロール

実装

 記載のないファイルの内容は前回と同様です。

deploy-image.yml

 roleを含めるだけのplaybookです。

---
- name: k8s kaniko Test
  hosts: local
  roles:
    - k8s-kaniko
roles/k8s-kaniko/tasks/main.yml

 DockerfileJenkinsfileをKanikoコンテナに渡す必要があるため、ConfigMapにしてからVolume経由でコンテナに渡す想定です。
 1つのConfigMapに纏めようとしたのですが、yamlのパースエラーが起きるので2つに分けています。
 改行+インデントがうまく設定できるようなコマンドを含めれば解消できそうですが、今回はそこまでの作りこみはしません。

---

- name: "Build docker image in k8s cluster"
  debug:
    msg: "Build docker image in k8s cluster"

- name: Install boto3
  pip:
    name: boto3
    state: present

- name: Slurp Dockerfile
  slurp:
    path: Dockerfile
  register: cat_Dockerfile

- name: Slurp Jenkinsfile
  slurp:
    path: Jenkinsfile
  register: cat_Jenkinsfile

- name: Create config-map in Dockerfile
  k8s:
    kubeconfig: ~/.kube/config
    state: present
    resource_definition:
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: kaniko-test-cm-docker
        namespace: ansible-test
      data:
        Dockerfile: "{{ cat_Dockerfile.content | b64decode }}"

- name: Create config-map in Jenkinsfile
  k8s:
    kubeconfig: ~/.kube/config
    state: present
    resource_definition:
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: kaniko-test-cm-jenkins
        namespace: ansible-test
      data:
        Jenkinsfile: "{{ cat_Jenkinsfile.content | b64decode }}"

- name: Create repository
  ecs_ecr:
    name: "{{ image_name }}"
    aws_access_key: "{{ key_id }}"
    aws_secret_key: "{{ access_key }}"
    region: "{{ region }}"
  register: ecr_repo

- name: Push docker image
  vars:
    imagename: "{{ ecr_repo.repository.repositoryUri }}:{{ image_version }}"
    namespace: ansible-test
  k8s:
    definition: '{{ item }}'
    kubeconfig: ~/.kube/config
    state: present
  loop:
    - "{{ lookup('template', 'kaniko.yml') | from_yaml_all | list }}"
kaniko.yml

 Kanikoポッドを立ち上げる為のマニフェストファイルです。
使い方の詳細はGitHubのREADMEに記載されています。

apiVersion: v1
kind: Pod
metadata:
  name: kaniko
  namespace: {{namespace}}
spec:
  restartPolicy: Never
  containers:
    - name: kaniko
      image: gcr.io/kaniko-project/executor:latest
      args:
        - "--dockerfile=./Dockerfile"
        - "--context=/kaniko-test-cm"
        - "--destination={{ imagename }}"
      volumeMounts:
        - name: aws-secret
          mountPath: /root/.aws
          readOnly: true
        - name: kaniko-test-cm
          mountPath: /kaniko-test-cm
          readOnly: true

  volumes:
    - name: aws-secret
      secret:
        secretName: aws-secret
    - name: kaniko-test-cm
      projected:
        sources:
        - configMap:
            name: kaniko-test-cm-docker
        - configMap:
            name: kaniko-test-cm-jenkins

playbook実行

 正常終了することを確認。

$ ansible-playbook deploy-image.yml -i inventory/hosts

PLAY [k8s kaniko Test] *************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************
ok: [localhost]

TASK [k8s-kaniko : Build docker image in k8s cluster] ******************************************************************
ok: [localhost] => {
    "msg": "Build docker image in k8s cluster"
}

TASK [k8s-kaniko : Install boto3] **************************************************************************************
ok: [localhost]

TASK [k8s-kaniko : Slurp Dockerfile] ***********************************************************************************
ok: [localhost]

TASK [k8s-kaniko : Slurp Jenkinsfile] **********************************************************************************
ok: [localhost]

TASK [k8s-kaniko : Create config-map in Dockerfile] ********************************************************************
ok: [localhost]

TASK [k8s-kaniko : Create config-map in Jenkinsfile] *******************************************************************
ok: [localhost]

TASK [k8s-kaniko : Create repository] **********************************************************************************
ok: [localhost]

TASK [k8s-kaniko : Push docker image] **********************************************************************************
changed: [localhost] => (item=[{u'kind': u'Pod', u'spec': {u'restartPolicy': u'Never', u'volumes': [{u'secret': {u'secretName': u'aws-secret'}, u'name': u'aws-secret'}, {u'name': u'kaniko-test-cm', u'projected': {u'sources': [{u'configMap': {u'name': u'kaniko-test-cm-docker'}}, {u'configMap': {u'name': u'kaniko-test-cm-jenkins'}}]}}], u'containers': [{u'image': u'gcr.io/kaniko-project/executor:latest', u'args': [u'--dockerfile=./Dockerfile', u'--context=/kaniko-test-cm', u'--destination=xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/jenkins-test/jenkins-test:v1.0.0'], u'volumeMounts': [{u'readOnly': True, u'mountPath': u'/root/.aws', u'name': u'aws-secret'}, {u'readOnly': True, u'mountPath': u'/kaniko-test-cm', u'name': u'kaniko-test-cm'}], u'name': u'kaniko'}]}, u'apiVersion': u'v1', u'metadata': {u'namespace': u'ansible-test', u'name': u'kaniko'}}])

PLAY RECAP *************************************************************************************************************
localhost                  : ok=9    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


 コンソールからECRにイメージがプッシュされたことが確認できます。

注意

 issuesプルリクから察するに、メンテナンスはあまり活発に行われていないように思います。
 また、最近のissuesの中にはマルチステージビルドに失敗する等、クリティカルな内容の物も見受けられます。
 もし技術選定に関われる立場にあるなら、できる限りKanikoは使用しない設計に倒した方が無難でしょう。

【AWS】ansibleでECRにDockerイメージをpushして、EKS環境にデプロイする

目的

  • EC2から、AWS EKSにansibleでデプロイしてみる
    • AWS ECRにDockerイメージをプッシュする
  • とりあえず動かしてみて、各ツールの使い方を学習する

前提条件

  • 作業する環境にPythonAnsiblekubectlのインストールが必要
  • aws eks update-kubeconfig ...でconfigが作成済み
# Ansibleのk8sモジュールを使用する際に必要なライブラリ
$  pip install pyyaml kubernetes openshift

参考

 こちらの記事を参考にEKS環境を構築しました。

qiita.com

変更点は以下となります。

  • 社用アカウントのVPC数が上限に達していたので、CloudFormationによるデプロイはせず、既存のVPCを利用
  • addonのコンテナを起動するとセカンダリIPが不足するので、ノードグループのインスタンスタイプをt3.mediumに変更
    • 使用しないときはAutoScalingのノード数を0に設定すると、ノードが停止します

構成図

ディレクトリ構成

 とりあえず、必要最低限のファイルを用意しました。
ディレクトリ構成について、公式がベストプラクティスを定義していますので、そちらが参考になります。

docs.ansible.com

.
├── local.yml            # site.yml から呼び出されて実行されるファイル
├── site.yml             # 本YAMLファイルから、[任意の処理名].ymlを呼び出す。
├── Dockerfile           # pushするDockerイメージ
├── inventory            # ホストとそれに紐づく変数を定義するファイル
│     └── hosts
└── roles                # Playbookのタスクetc...を格納。詳細は公式ドキュメント参照。
     ├── ...
     └── k8-jenkins             # role毎にディレクトリを用意すると良さそう。
         └── tasks
             ├── deployment.yml   # EKSへデプロイするマニフェスト。
             └── main.yml    # Dockerイメージのビルドやpush等の実処理を記載するファイル。

実装

site.yml

 実行ファイルをインポート。

---
- import_playbook: local.yml
local.yml

 実行対象のroleをrolesの配列に含める。
ansible.kubernetes-modulesk8sデプロイモジュールを利用する際に必要になります。
先にインストール済みの環境であれば、不要かも?

---
- name: Ansible Test
  hosts: local
  roles:
    - ansible.kubernetes-modules
    - k8s
inventory/hosts

 ローカル環境の定義のみ記載しています。
ベストプラクティスによると、変数定義はgroup_varshost_varsディレクトリに纏めて配置するようです。
クレデンシャル管理はPJの方針に従ってください。
 また、最初はinventoriesという名前のディレクトリにしていたのですが、--syntax-chek実行時に警告?が出ていたので、とりあえずinventoryとしています。

[local]
localhost ansible_connection=local

[local:vars]
region=ap-northeast-1
key_id=xxxxxxxx
access_key=xxxxxxxx
image_name=jenkins-test/jenkins-test
image_version=v1.0.0
roles/k8s/tasks/main.yml

 AWSコマンドを使用する為、configなどの配置も併せて行っています。
埋め込み文字は Jinja2 構文 で変数が展開されます。

---

- name: "Push Docker image to AWS ECR"
  debug: "Push Docker image to AWS ECR"

- name: Make aws directory
  file:
    dest: ~/.aws
    state: directory
    mode: u=rwx,g=o=

- name: Copy aws config
  template:
    src: aws/config
    dest: ~/.aws/config
    mode: u=rw,g=o=

- name: Copy aws credentitals
  template:
    src: aws/credentitals
    dest: ~/.aws/credentitals
    mode: u=rw,g=o=

- name: Install docker-py for build Docker
  pip:
    name: docker-py
    state: present

- name: Build Docker image
  docker_image:
    build:
      path: ./
    name: "{{ image_name }}"
    tag: "{{ image_version }}"

- name: Docker login
  shell: "aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin <ユーザーID>.dkr.ecr.ap-northeast-1.amazonaws.com"
  args:
    executable: /bin/bash

- name: Install boto3
  pip:
    name: boto3
    state: present

- name: Create repository
  ecs_ecr:
    name: "{{ image_name }}"
    aws_access_key: "{{ key_id }}"
    aws_secret_key: "{{ access_key }}"
    region: "{{ region }}"
  register: ecr_repo

- name: Add tag
  docker_image:
    name: "{{ image_name }}:{{ image_version }}"
    repository: "{{ ecr_repo.repository.repositoryUri }}"
    tag: "{{ image_version }}"

- name: Push image to ECR
  docker_image:
    name: "{{ ecr_repo.repository.repositoryUri }}:{{ image_version }}"
    push: yes

- name: Create deployment to EKS
  vars:
    imagename: "{{ ecr_repo.repository.repositoryUri }}:{{ image_version }}"
    replica: 1
    namespace: ansible-test
  k8s:
    definition: '{{ item }}'
    kubeconfig: ~/.kube/config
    state: present
  loop:
    - "{{ lookup('template', 'deployment.yml') | from_yaml_all | list }}"

roles/k8s/tasks/deployment.yml

 EKSへデプロイするマニフェストファイル。
ジョブで定義したvarsの各値がマニフェストの埋め込み文字に反映されます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: test-jenkins-app
  namespace: {{ namespace }}
spec:
  replicas: {{ replica }}
  selector:
    matchLabels:
      app: test-jenkins-app
  minReadySeconds: 10
  template:
    metadata:
      labels:
        app: test-jenkins-app
    spec:
      volumes:
      - name: git-secret-volume
         secret:
           secretName: git-secret

    containers:
    - image: {{ imagename }}
       name: jenkins-test-container
       restartPolicy: Always
       ports:
       - containerPort: 8080
       resources:
         requests:
           cpu: 200m
           memory: 20Mi
         limits:
           cpu: 512m
           memory: 500Mi
       volumeMounts:
       - name: git-secret-volume
          mountPath: /var/jenkins_home/.ssh
          readOnly: true

---

apiVersion: v1
kind: Service
metadata:
  name: test-jenkins-service
  namespace: {{ namespace }}
  labels:
    app: test-jenkins-service
spec:
  type: LoadBakancer
  ports:
  - port: 80
     protocol: TCP
     targetPort: 8080
  selector:
    app: test-jenkins-app
Dockerfile

とりあえずJenkinsを動かしてみます。

FROM jenkins/jenkins:latest

RUN mkdir -p /var/jenkins_home/.ssh && ¥
         chown -R jenkins:jenkins /var/jenkins_home

COPY --chown=jenkins:jenkins ./Jenkinsfile ./

playbook実行

 各ジョブが正常終了することを確認。

$ ansible-playbook site.yml -i inventory/hosts

PLAY [Ansible Test] ********************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************
[WARNING]: Platform linux on host localhost is using the discovered Python interpreter at /usr/bin/python, but future installation of
another Python interpreter could change this. See https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html
for more information.
ok: [localhost]

TASK [ansible.kubernetes-modules : Install latest openshift client] ********************************************************************
skipping: [localhost]

TASK [k8s : push docker image to ECR & deploy to EKS] ******************************************************************************
ok: [localhost] => {
    "msg": "push docker image to AWS ECR & deploy to EKS"
}

TASK [k8s : make aws directory] ********************************************************************************************************
ok: [localhost]

TASK [k8s : copy aws config] ***********************************************************************************************************
ok: [localhost]

TASK [k8s : copy aws credentials] ******************************************************************************************************
ok: [localhost]

TASK [k8s : install docker-py for build docker] ****************************************************************************************
ok: [localhost]

TASK [k8s : build docker image] ********************************************************************************************************
[WARNING]: The value of the "source" option was determined to be "build". Please set the "source" option explicitly. Autodetection will
be removed in Ansible 2.12.
ok: [localhost]

TASK [k8s : docker login] **************************************************************************************************************
changed: [localhost]

TASK [k8s : install boto3] *************************************************************************************************************
ok: [localhost]

TASK [k8s : create repository] *********************************************************************************************************
ok: [localhost]

TASK [k8s : add tag] *******************************************************************************************************************
[WARNING]: The value of the "source" option was determined to be "pull". Please set the "source" option explicitly. Autodetection will
be removed in Ansible 2.12.
ok: [localhost]

TASK [k8s : push image to ecr] *********************************************************************************************************
ok: [localhost]

TASK [k8s : Create a Deployment to EKS] ************************************************************************************************
ok: [localhost] => (item=[{u'kind': u'Deployment', u'spec': {u'selector': {u'matchLabels': {u'app': u'test-jenkins-app'}}, u'minReadySeconds': 10,
....
u'spec': {u'type': u'LoadBalancer', u'ports': [{u'targetPort': 8080, u'protocol': u'TCP', u'port': 80}], u'selector': {u'app': u'test-jenkins-app'}}, u'apiVersion': u'v1', u'metadata': {u'labels': {u'app': u'test-jenkins-service'}, u'namespace': u'ansible-test', u'name': u'test-jenkins-service'}}])

PLAY RECAP *****************************************************************************************************************************
localhost                  : ok=13   changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

コンテナの起動確認

 参考サイトや公式ドキュメントの手順通りに設定していれば、IAMの設定はできていると思います。

$ alias k=kubectl

$ k get pod --namespace ansible-test
NAME                                READY   STATUS    RESTARTS   AGE
test-jenkins-app-7c7ffcfb46-fq5q6   1/1     Running   0          46m

# コンテナにログインして、jenkinsのinitialAdminPasswordを確認
$ k exec -it test-jenkins-app-7c7ffcfb46-fq5q6 -n ansible-test -- /bin/bash

jenkins@test-jenkins-app-7c7ffcfb46-fq5q6:/$ cat /var/jenkins_home/secrets/initialAdminPassword
xxxxxxxxxxxxxxxxxxxxxxxxxxx

jenkins@test-jenkins-app-7c7ffcfb46-fq5q6:/$ exit
exit

# アクセス先を確認
$ k get service --namespace ansible-test
NAME                   TYPE           CLUSTER-IP       EXTERNAL-IP                                                                    PORT(S)        AGE
test-jenkins-service   LoadBalancer   10.100.138.217   ac7d2f38812bc41d7bf81a39a4a7c467-1945438877.ap-northeast-1.elb.amazonaws.com   80:32108/TCP   3d17h

 http://<EXTERNAL-IP>にアクセスすると、jenkinsの初期画面が表示される。
 HTTPS接続にしたい場合、ACMで証明書を作成すると良いでしょう。

【Docker】PythonのAWS LambdaLayerをDockerで作成する

AWS LambdaのLayerモジュールをzip化するまでの処理をDockerで行えるようにしたので、その方法をメモ。


coin-look.pages.dev


Dockerfile

amazonlinuxのイメージを利用することで、EC2などを用意せずにx86_64アーキテクチャに対応させます。
Pythonのバージョンは適宜変更する。

FROM amazonlinux

ENV PATH $PATH:/usr/local/bin

RUN yum -y install ¥
         gcc openssl-devel bzip2-devel libffi-devel ¥
         wget tar gzip zip make && ¥
         wget https://www.python.org/ftp/python/3.9.11/Python-3.9.11.tgz && ¥
         tar xzf Python-3.9.11.tgz && ¥
         cd Python-3.9.11 && ¥
         ./configure --enable-optimizations && ¥
         make altinstall

RUN mkdir ./python-src && ¥
         python3.9 -m pip install xxx... -t ./python-src 

COPY . ./

RUN zip -r /tmp/aws-layer.zip ./python-src

起動スクリプト

zipファイルをカレントディレクトリに配置するまでを自動化。

#/bin/sh

IMAGE_NAME=aws-layer:0.0.0
CONTAINER=aws-layer-con

docker stop $CONTAINER
docker rm $CONTAINER

docker build -t $IMAGE_NAME . --no-cache

docker run -itd --name $CONTAINER $IMAGE_NAME

docker cp $CONTAINER:/tmp/layer.zip .

【Oracle Cloud】ORA-28001: the password has expiredの際の対処方法【Oracle Autonomous Database】

Oracle Cloudで稼働させているOracleDBがパスワードの有効期限切れで落ちるようになっていたので、対処方法をメモ。


coin-look.pages.dev


sqlplusのインストール

yumsqlplusをインストールします。
バージョンは使用しているDBと同じものを選択してください。

[opc@xxxxx ~]$ yum search sqlplus
/usr/lib/python2.7/site-packages/OpenSSL/crypto.py:12: CryptographyDeprecationWarning: Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in the next release.
  from cryptography import x509
読み込んだプラグイン:langpacks, ulninfo
===================================================================================== N/S matched: sqlplus ======================================================================================
oracle-instantclient12.2-sqlplus.x86_64 : SQL*Plus for Instant Client.
oracle-instantclient18.3-sqlplus.x86_64 : SQL*Plus for Instant Client.
oracle-instantclient18.5-sqlplus.x86_64 : SQL*Plus for Instant Client.
oracle-instantclient19.10-sqlplus.x86_64 : Oracle Instant Client SQL*Plus package
oracle-instantclient19.11-sqlplus.x86_64 : Oracle Instant Client SQL*Plus package
oracle-instantclient19.12-sqlplus.x86_64 : Oracle Instant Client SQL*Plus package
oracle-instantclient19.13-sqlplus.x86_64 : Oracle Instant Client SQL*Plus package
oracle-instantclient19.14-sqlplus.x86_64 : Oracle Instant Client SQL*Plus package
oracle-instantclient19.15-sqlplus.x86_64 : Oracle Instant Client SQL*Plus package
oracle-instantclient19.3-sqlplus.x86_64 : Oracle Instant Client SQL*Plus package
oracle-instantclient19.5-sqlplus.x86_64 : Oracle Instant Client SQL*Plus package
oracle-instantclient19.6-sqlplus.x86_64 : Oracle Instant Client SQL*Plus package
oracle-instantclient19.8-sqlplus.x86_64 : Oracle Instant Client SQL*Plus package
oracle-instantclient19.9-sqlplus.x86_64 : Oracle Instant Client SQL*Plus package

  Name and summary matches only, use "search all" for everything.
[opc@xxxxx ~]$ yum install oracle-instantclient19.X-sqlplus.x86_64

sqlplusからパスワードを更新

以下のコマンドでパスワードを更新します。 更新後のパスワードで問題なく接続できることが確認できたら、作業は完了です。

[opc@xxxxx ~]$ sqlplus ADMIN/{パスワード}@xxxxxxxx0_high

SQL> alter user {ユーザー名} identified by "{新しいパスワード}";

User altered.

SQL> ALTER USER {ユーザー名} ACCOUNT UNLOCK;

User altered.

# 開発の現場では、あまりやらない方が良いかも
SQL> ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;

Profile altered.

SQL> exit;

【AWS】API Gateway マッピングテンプレートの設定方法

f:id:tm200:20220417162425p:plain

AWSAPI Gatewayを介してlambda関数にHTTPヘッダやPOSTパラメータを渡す際の設定方法をメモ。
Lambda プロキシ統合にチェックを入れるだけでもパラメータを参照することはできますが、レスポンスに不要な情報が含まれてしまう為、そちらは利用せずにパラメータを渡したかった次第です。


coin-look.pages.dev


ドキュメント

マッピングテンプレートのパラメータ名については、公式に一覧があります。 docs.aws.amazon.com

HTTPヘッダの設定方法

マッピングテンプレートを以下のように設定すれば、lambda関数からAuthorizationヘッダやカスタムヘッダなどのHTTPヘッダを参照できます。
$util.escapeJavaScriptエスケープしておくのがベターです。 HTTPヘッダは以下の記述のみで参照可能ですが、URLパラメータを参照するには「メソッドリクエスト」と「統合リクエスト」の欄から「URLクエリ文字列パラメータ」を指定する必要があります。

マッピングテンプレートの記載例

#set($allParams = $input.params())
{
  "params": {
    #foreach($type in $allParams.keySet())
    #set($params = $allParams.get($type))
    "$type": {
      #foreach($paramName in $params.keySet())
        "$paramName": "$util.escapeJavaScript($params.get($paramName))"
        #if($foreach.hasNext),#end
        #end
    }
    #if($foreach.hasNext),#end
    #end
  }
}

lambda関数の実装例

def lambda_handler(event, context):
    # `Authorization`ヘッダの取得
    auth_header = event.get('params', {}).get('header', {}).get('Authorization', {})
    # クエリパラメータ(GETのURLパラメータ)の取得
    p = event.get('params', {}).get('querystring', {}).get('<パラメータ名>')

POSTパラメータの設定方法

POST時のリクエストパラメータのJSONは以下のように設定すると、lambda関数から参照可能となります。

マッピングテンプレートの記載例

#set($allParams = $input.params())
{
  "params": {
    "body": $input.json('$'),      **←ココ**
    #foreach($type in $allParams.keySet())
    #set($params = $allParams.get($type))
    "$type": {
      #foreach($paramName in $params.keySet())
        "$paramName": "$util.escapeJavaScript($params.get($paramName))"
        #if($foreach.hasNext),#end
        #end
    }
    #if($foreach.hasNext),#end
    #end
  }
}

lambda関数の実装例

def lambda_handler(event, context):
    # jsonボディの取得
    body = event.get('params', {}).get('body', {})
    # json物理名を指定して、各パラメータを取得
    p = body.get('<パラメータ名>')

【Python】toxで複数のPythonバージョンをテストする

f:id:tm200:20220221223803p:plain

Pythonのテストコードを実行する際に、複数のPythonバージョンで動作確認したい時があり、toxの設定が便利だったので備忘メモ。

インストール

$ pip3 install tox

tox.ini

設定ファイルにlinttestの設定を記載できます。
波括弧で複数バージョンの指定が可能です。
また、lintのルールをflake8の引数で変更可能です。

[tox]
envlist = py3{7,8,9}-{lint,test},coverage-report

[testenv]
setenv =
    COVERAGE_FILE = .coverage.{envname}
changedir = {toxinidir}/tests
install_command = \
    pip install \
    --index-url=https://xxx.xxx.xxx:8000/xxx \
    --trusted-host=xxx.xxx.xxx {opts} {packages}

deps = 
    pytest
    pytest-cov
    coverage
    mock

commands =
    pytest --cov . {posargs}

[lint]
changedir = {toxinidir}
skip_install = True
deps =
    readme_renderer
    flake8
    flake8-import-order
    pylint
    mock
    pytest

commands =
    flake8 --import-order-style=pep8 --max-complexity=11 --max-line-length=120 <モジュールのディレクトリ名> (setup.py, testsなども指定可能)

[testenv:py37-lint]
skip_install = {[lint]skip_install}
changedir = {[lint]changedir}
deps = {[lint]deps}
commands = {[lint]commands}

[testenv:py38-lint]
...py37と同様

[testenv:py39-lint]
...py37と同様

実行方法

PRなどをトリガーに、CICDのパイプラインで各Pythonバージョンのコンテナを起動して、テストを自動実行するようにするとGood😎

#lint
$ tox -e py38-lint

# test
$ tox -e py38-test

【Hive】beeline実行時のオプションについて

f:id:tm200:20211209234112j:plain

beelineでHQLを実行する際のオプション指定方法について記載します。


coin-look.pages.dev


--hiveconf

Hiveの設置をオプションで指定可能です。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' --hiveconf hive.execution.engine=tez --hiveconf tez.queue.name=unfunded

-i <設定ファイル>

指定したいオプションが複数ある場合、--hiveconfではなく-i <設定ファイルのパス>などのように別ファイルにまとめることが可能です。
セッションOPEN時の初期化処理に適用されるようです。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' -i hiverc
hiverc
set hive.execution.engine=tez;
set tez.queue.name=unfunded;
set hive.exec.scratchdir=/user/xxx/tmp
.....

-f <HQLファイル>

HQLファイルを実行したい場合、-fで指定します。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' -f /path/to/test.hql

--hivevar key1=value1

--hivevarで変数展開が可能です。
複数ある場合は--hivevar key1=value1 --hivevar key2=value2 ...のように続けて記載します。

$ beeline -u 'jdbc:hive2://xxx.co.jp:10000/default;...' -f /path/to/test.hql --hivevar yyyymmdd=20211101

バックグラウンド実行

HADOOP_CLIENT_OPTSを設定する必要があります。

$ export HADOOP_CLIENT_OPTS="-Djline.terminal=jline.UnsupportedTerminal"