なんかかきたい

プログラミングなどの個人的なメモやサークル「ゆきいろパラソル」の情報を載せてます

AWS CodePipelineからCodeBuildを呼んでlambdaで古いAMIを消すといい感じになる

前回の続き

t-cyrill.hatenablog.jp

AWSでAMIの作成をCodeBuildを使って自動化したけど、CodeBuildが動く部分も自動化したいので、Githubに置いてあるリポジトリのmasterにマージされたタイミングで自動的にAMIが作られるといい感じなんじゃないかと思って、「これくらい簡単にできるっしょ」ってやろうとしたらCodeBuildのトリガーなんてものはないと気づいてAWSめんどいってなったので、めんどい部分を書いておきます。

これ作ってから言うのもアレなんですが、CodePipelineはAWSコンソールから操作する分にはある程度自動化されていて便利な反面、 自前でAMI周りの権限をうまくつけるのは結構面倒なので、terraformとかでCodePipeline/CodeBuild/Lambda/CloudWatchLogs/IAM/S3まわりを一気に設定するのは骨が折れるなあという感想です。

まあ、やろうと思えばできます。ただただ面倒です。なんとかならんのか。

で、本題なんですが、CodePipelineっていうのはCIツール的なやつで、CodeBuildがビルド周りしか担当しないのに対して、CodePipelineはソースの取得、ビルドタスクの実行、ビルド後のタスクなどなど、パイプラインの名の通り色々なタスクをルールに従って処理してくれるやつです。

ビルドの並列化などもできるので、CodeBuildだけで足りないところをいい感じに補ってくれるやつと捉えておくといいかもしれません。

CodePipelineのソースにgithubリポジトリを指定して、masterブランチを監視しておけば、Pull Request単位でレビューをしてmasterにマージされたタイミングで自動的にCodeBuildを走らせるなんてことが簡単にできます。

便利ですね。

流れとしては

  • githubのブランチを監視、変更があればソースで取り込む
  • CodeBuildでAMIを生成
  • Lambdaで古いAMIを削除

という感じになります。

CodePipeline

CodePipeline自体はUIをみたらなんとなく使い方はわかるくらい簡単なので、特に説明はしないのですが、いくつか気になったところだけ補足します。

ソースはアーティファクトの出力が必要

ソースフェーズで取り込んだgithubリポジトリをCodeBuildに渡す際にアーティファクトという形でs3を経由する必要があります。

まあ、ソースなんでそのソースはs3に置くでしょという感じな気もするのですが、一時的にs3に置かれるのはちょっと面倒だなあという感想もあります。

なんか、噂によるとパーミッション周りが消えるとかいう話もあるし、ソースじゃなくて単なるトリガーみたいなのをつくれないのかなあという気持ちはあります。

ここはあんまり気にせずいきましょう。必要な権限があればCodeBuildのprebuildとかでつければいいかなという感想です。

タイムアウト時間には余裕を持っておく

CodeBuildもLambdaもですが、意外にも時間がかかることがあるのでタイムアウト時間が短すぎるとエラー扱いになります。

CodeBuildは初回実行時におおよその時間を割り出しておくと良いでしょう。LambdaはAMI削除程度なら15秒もあれば十分かなという程度です。(実際には2秒程度で終わる)

LambdaからCodePipelineのタスクが成功したかどうかは自分で報告する必要がある

地味にハマった問題で、タスク呼び出しで呼んだLambdaが成功したかどうかはLambdaでCodePipelineに通知する必要があります。

そうしないと、たとえLambdaが正常終了したとしてもタイムアウトまで待った上で失敗扱いになるので、見栄え上よくないです。

これはテンプレート的に成功の場合は通知処理を書いておけばまあ問題にはならないでしょう。

Lambdaで古いAMIを削除する

まあ、消しましょう。Jenkinsのように古いビルドとアーティファクトを消すものはないので自前で作る必要があります。面倒ですね。

Lambdaなので、Pythonでサクッと実行します。itemgetter あたりが知らなかったので勉強になりましたね。

AWS AMI AutoCleanImages

images_limit とかは適当にいじってやるといいと思います。ec2のAPIを呼ぶのでこのLambdaにはEC2の操作権限をつけないといけません。

あと、上で書いたCodepipelineへの通知は code_pipeline.put_job_success_result(jobId=event['CodePipeline.job']['id']) のところになります。

もうなんかとりあえず最後にこれを置いておくかという感じですね。

終わりに

とりあえず、AMI作成はこれで自動化できるようになりました。

あとは本投入周りのことは考える必要がありますが、まあその辺がインフラエンジニアの腕の見せどころでしょう。

正直この辺はハンコなのでインフラエンジニアは隠居していたいところなのですが、まあサービスしておきましょう。

はー、こんなことより早くアプリケーション側から呼ばれているMySQLのめっちゃ遅いクエリ消さないとなー!!!

あ、書くの忘れてたんですが、terraformだと今のところCodeBuildの環境変数設定でParameter Storeを指定することができないので、CodeBuildのタスク内で秘密情報はSSMから取ってくる必要があります。

まあそのうち改善しそうですが、具体的にはansible-vaultのパスワードみたいなのは自分で取ってくる必要があるという感じです。頑張って下さい。

最近暇すぎてGolangを勉強がてら書いてます。

本当はGoroutineを使った並列処理とかそういうのを書きたいんですが、ベンチマーカーくらいしかそんな用途なさそう。

だいたい速度面で困ってないし。

次回はこの辺書くかなという感じです。世界を笑顔に。

AWS CodeBuild と packer と ansible でプロビジョニング済みのAMIを自動生成する

AMI生成部分を自動化できたので、ちょっとだけまとめておく。

CodeBuild

  • AWSのDockerで動くCIツール。
  • CodeBuildの名前の通り、ソースコードをビルドする環境を簡単に作れることをウリにしている
  • CircleCIなどのサービスでビルドしたり、Jenkinsを自前で用意するのと比較してそれほど違いはない
    • 利点があるとすれば、AWS上で動くためサービスロールを使って権限を付与できるところか
    • packerを使う都合上、EC2へのアクセスが必要になるため、外部サービスの場合アクセストークンの発行と管理が必要になる
  • 料金はビルド時間ごとの課金
    • 一応無料枠もある

Packer

  • HashiCorp製のマシンイメージ自動生成ツール
  • Goで書かれていて機能が豊富
    • AWS以外にもいくつかのクラウドサービスに対応している

Ansible

  • Python製のプロビジョニングツール
  • シェルスクリプトでもいいが、モジュールが整備されているので使えるようになると便利
  • Packerと連携するとSSH周りを自動で設定してくれる

参考ソース

AWSのブログにCodeBuildとPackerを使ったAMIビルダーの構築方法がある。

他にはクラスメソッドさんのブログにAnsibleとPackerの連携周りがある。

この辺りを読めばなんとなく作り方のイメージはできるようになるが、いくつかハマった点があるので補足としてまとめておく。

このリンクだけで十分な人はここから先は読まなくてもOKです。

大まかな流れ

  • Ansibleのplaybookはgithubで管理しておく
    • ここは変えてもよいが、CodeBuildはgithubと連携できるのでgithubを使うと簡単。
    • CodeBuildでコードを取ってくる方法は、他にS3やGithub Enterprise、BitbuckerとCodeCommitがある
    • 最悪の場合でもS3が使えるのでなんとかなるとは思う。
  • CodeBuildでビルド用のDockerを起動、事前のセットアップを行う
    • buildspec.ymlprebuild に書く
    • DockerでAnsibleとPackerを実行するのでインストールする
      • インストール済みのDockerイメージを用意できればわざわざビルドのたびにセットアップする必要は無くなる
    • Dockerイメージは任意のものを使って構わないが、AWSが提供しているのは現状 Ubuntu 14.04 LTS (Trusty) ちょっと古いか。
  • ansible-vaultで使うパスワードをKMSから取ってくる
    • 平文を嫌う場合、KMSに登録しておき、環境変数にセットする方法が使える
  • packer build でAMIを生成する

AWSのブログと違う点

  • Packer 1.2.1
  • ansible provisionerを使うため、Docker環境へAnsibleのインストールする
  • packerがIAM Roleを扱えるようになったため、クレデンシャルの生成は不要になった
  • 環境変数 USER を設定する
    • 設定しないと packer user: Current not implemented on linux/amd64 が出る
    • これはDocker環境では環境変数 USER が設定されていないため。Golanguser を取るところでエラーになる
    • CodeBuildの環境変数設定でやっておけば問題はない

クラスメソッドさんのPacker+AnsibleによるAMIの作成と違う点

  • アクセスキーはIAM Roleを使うので変数を使わない
  • ansible-localではなくansibleを使う
  • ansibleのインストールは buildspec.yml で行う

クラスメソッドさんのPacker 0.9の新機能 リモートからのAnsible Provisionerが追加されましたと違う点

  • vaultファイルの管理をする

本題

CodeBuildとPackerを使うため、それぞれのための設定ファイルを用意する。

CodeBuildが buildspec.yml で Packer は任意の名前のJSON

buildspec.yml

---
version: 0.2

phases:
  pre_build:
    commands:
      - echo 'Install Packer 1.2.1'
      - curl -qL -o packer.zip https://releases.hashicorp.com/packer/1.2.1/packer_1.2.1_linux_amd64.zip && unzip packer.zip
      - echo 'Install Ansible'
      - apt-get update -y
      - apt-get install -y software-properties-common
      - apt-add-repository ppa:ansible/ansible
      - apt-get update -y
      - apt-get install -y ansible
      - echo 'Validate packer json'
      - ./packer validate packer_ec2.json
  build:
    commands:
      - aws configure set region $AWS_REGION
      - echo "$ansible_vault_pass" > vault_password_file
      - echo 'Build AMI'
      - ./packer build packer_ec2.json
  post_build:
    commands:
      - echo "Build finished `date`"

packer_ec2.json

{
    "variables":{
        "vault_path": "vault_password_file"
    },
    "builders": [{
        "type": "amazon-ebs",
        "region": "ap-northeast-1",
        "source_ami": "ami-XXXXXX",
        "instance_type": "t2.micro",
        "ssh_username": "root",
        "ssh_timeout": "5m",
        "ami_name": "packer_{{ timestamp }}"
    }],
    "provisioners": [{
        "type" : "ansible",
        "extra_arguments": "--vault-password-file={{user `vault_path`}}",
        "playbook_file" : "playbook.yml",
        "groups" : ["aws", "webserver"]
    }]
}

groups を配列で渡すと inventory file で複数のグループに属した場合と同じ扱いになる。

これは group_vars などで設定を切り替える必要がある場合に便利。

vaultファイルは buildspec.yml で出力したパスを extra_arguments を使ってそのまま渡している。

IAM Role

  • XXXXXXXX にはアカウントIDを入れる

  • 以下のポリシーを作成し、CodeBuildで使うIAM Roleにアタッチする。(インラインでも構わない)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
                "ssm:GetParameters"
            ],
            "Resource": [
                "arn:aws:ssm:ap-northeast-1:XXXXXXXX:parameter/ansible-vault-pass"
            ]
        },
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": [
                "arn:aws:kms:ap-northeast-1:XXXXXXXX:key/alias/aws/ss"
            ]
        }
    ]
}

KMSからデータを取ってくるために必要。名前は適宜設定すること。

終わりに

いくつかハマりポイントはあったが、これでAMIをCodeBuildで自動的に作れるようになった。

あとはgithubのMergeにHookするとかすれば自動でAMIがどんどん作られていくので便利。

ただ、古いのを消さないと溜まり続けて課金がすごいことになりそう。大変ですね。

現場からは以上です

AWSの管理をterraformとroadworkerにした

Ansibleのcloud_moduleで大量に書かれたplaybookをterraformに少しずつ移して、 Route53の管理だけをroadworkerに移してみたのですが、 今のところうまくいっているようなので紹介しておきます。

terraform

HashiCorpのGoで書かれたオーケストレーションツール。

各OS、ディストリビューションに合わせた実行可能ファイルが配布されているのでインストールはとても簡単。 インストール方法で悩むチームでも導入の敷居は低いだろうと思う。というかよろしく入れた。

同種のツールとしてはcloudformationがあるが、terraformはGCPなどにも対応している。

設定ファイルはクラウド側の事情に引っ張られやすいため、EC2の設定をGCPに使うということはもちろんできないが、 管理方法などは共通化できる面もある。

ドキュメントはかなりまとまっていてやりたいことに対して設定の例が乗っていることが多い。 多少学習コストはあるが比較的低いと言える。

設定ファイル

terraformの設定ファイルは *.tfディレクトリに任意の名前でおいて良い。

terraformの特徴の一つがこの設定ファイルの読み込み方法で、ディレクトリ内のすべての *.tf が読み込まれて評価される。

具体的に言えば、 s3.tfrds.tf のようにファイルを分割しても 「s3.tf のみ適用する」ということはできない。

触ったことがないと戸惑うポイントになるが、このルールは「ちゃんと設定を書けば、結果的に個別の適用をする必要は無くなる」ためむしろ楽になる。

terraformの設定はHCLで書く。HCLとは「HashiCorp Configration language」である。

まあ名前の通りなので、特に気にせずこういう感じで書くとだけ思えばいいと思う。

YAMLとかJSONとかそういうものではないが nginx の設定などにインスパイアされた、 人にもそこそこ読みやすく機械的にも扱える設定フォーマットを目指していることもあって、 慣れればそれなりに書きやすい、と思う。

data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"] # Canonical
}

resource "aws_instance" "web" {
  ami           = "${data.aws_ami.ubuntu.id}"
  instance_type = "t2.micro"

  tags {
    Name = "HelloWorld"
  }
}

上記の設定はEC2のインスタンスを立ち上げる設定の例でterraformのマニュアルのもの。

amiinstance_type など AWS 固有の設定があることがわかると思う。

特徴的なのは属性値の参照、 ${data.aws_ami.ubuntu.id} の部分で、 data "aws_ami" "ubuntu" でフィルターして得られたAMIのidを使うという意味になっている。

terraformは設定ファイルに他の設定によって決定する値を埋め込むのが得意なため、 ひとまとまりの構成を比較的簡単に記述することができる。

これは各設定が依存するということでもあるが、記述量が大幅に減らせ変更にも強くなる。

裏返すと、変更が困難な箇所を変更する際に多くの箇所で変更を強いられる場合もあるが、 terraformには実行計画を事前に出力する機能があるため、一応意図しない大幅な構成変更は避けることができるようになっている。

stateファイル

terraformのもう一つの特徴が状態管理で、terraform.tfstate という名前のファイルで実行時に状態を保存するようになっている。

このためterraform以外で(例えばマネジメントコンソール)リモート状態を変更すると手元の状態とリモートの状態が一致しなくなり問題が起こる場合がある。

基本的にterraformは1箇所で実行し terraform.tfstate はたまにバックアップを取っておくのがよさそうだが、 terraform.tfstate にはパスワードなどのセンシティブな情報が含まれることがあるので、gitで管理するのは避けたい。

中身はただのテキストなので、なんとなく手で直すこともできるし、壊れた場合は、importを使ってリモートの状態を tfstate に取り込む方法もある。

terraform import はドキュメントに細かい使い方が乗っているが、あくまでのリモート状態を手元に同期するだけで、 *.tf ファイルを作ることはできないので注意。ただし、適当に作って terraform import した後に差分がないように設定を自分で書くことはできる。

roadworker

わざわざterraformを使っているのにRoute53の管理はterraformを使わないのかという話になるんですが、 前述の通り、terraformには構成ファイル *.tf を生成する機能はない。

これは既存の設定を取り込む必要がある場合に非常に面倒で、DNSのレコードが大量に存在する現場では厳しい。というかまさにこれ。

roadworkerのいいところは、設定ファイルを自動生成する機能がある点で、これだけでも使う価値がある。

生成時にzoneごとにファイルを分けることもできる。まあ、ゾーン内にrrsetが多すぎるとどのみち長くはなるが。

欠点はRubyで書かれているため、Rubyを導入できるだけの現場じゃないと導入が難しいところ。まあ、ここはゴリ押した。

あと、Ruby 2.4対応が完了していないため、Ruby 2.3で動かすのをオススメ。テスト機能で大量に警告が出る。 Rubyユーザとしては最新版で動かないのが少しモニョるが、rbenvとbundlerを使うなら環境は固定できるので、普段使いする分には困らないだろう。

終わりに

まあ当然といえば当然なんですが、用途に合わせて適切なツールは導入した方がいいですね。

学習コストの問題はありますが、ansibleにオーケストレーションを頑張らせるのは却って大変な気はします。

まあ、aws系モジュールのタスクが、boto/boto3を使ってAWS APIをそのまま呼び出していそうなところがplaybookを書くだけで見えてくる時点でお察しな感じはします。

あと、changedとかの判定も今ひとつだし、何より事前に実行計画を見ることができないのは痛いです。terraform plan 相当のことが、ansible-playbook -C では達成できません。

もちろんプロビジョニングについては ansible は強力なツールです。うまく使い分けましょうというお話でした。(これを理解してもらうのがなかなか難しい

現場からは以上です。

CentOS5にCentOS6の環境を作ってchrootする方法を書いておく

諸事情でCentOS5の環境を渡されたので、同じ不幸に見舞われた人向けに書いておく。

一般的な話でいえば、すぐにCentOS7の環境に移行すべきだが、世の中にはいろいろな不幸があるのでめげずに頑張ってほしい。

rinse でCentOS6をインストールする

CentOSchroot環境を作るにはrinseというものがある。Debianでいうdebootstrapみたいなツールで、 rpmパッケージを展開し、chrootできるディレクトリツリーを構築する。

古くからあるパッケージなので、CentOS以外のディストリビューションでも利用できる。

rinse のセットアップ

Debianの場合以下のようにaptでインストールできる。

apt-get install rinse rpm

CentOS6のchroot環境を用意する

mkdir -p ./centos6
rinse --directory=./centos6 --distribution=centos-6 --arch=amd64

この状態で ./centos6 を持っていけばchroot環境を持ち出せる。簡単。

CentOS6にchrootする

sudo mount --rbind /dev ./centos6/dev
sudo mount -t proc none ./centos6/proc
sudo mount --rbind /sys ./centos6/sys
sudo chroot ./centos6 /bin/bash

END

Pythonのパッケージ管理とsystemdでプロセスをずっと上げておく方法を書いておく

タイトルは半分当たっていて、半分は嘘。

Pythonをちゃんと環境作ってやったことが今までなかったので、今わかってる範囲の情報をまとめていく。

pip

Pythonのパッケージ管理システム。Pythonで作られたプロジェクトのインストールにもよく使われている。

依存パッケージの管理

pipにはfreezeサブコマンドがあり、現在の環境で使っているパッケージとバージョンを出力できる。

このリストをrequirements.txtで出力しておき、pipでこれをインストールするということがよく行われているらしい。

これらは後述するvirtualenvvenvと組み合わせて使うとクリーンな環境を用意できる。

環境のパッケージを出力

pip freeze > requirements.txt

環境にパッケージをインストール

pip install -r requirements.txt

virtualenv

実行環境管理ツール。rvm がツールとしては近い。 シェルと組み合わせて仮想環境を構築する。

仮想環境にpipを使って必要なパッケージをインストールして、動作環境を作ることでpython環境を隔離できる。

と思ったけど、Python3.3からvenvが公式に組み込まれたと聞きつけたので、virtualenvはもっぱらPython2系の環境を隔離したいときに使うことになる。

venv

Python3.3からバンドルされている環境管理ツール。 virtualenvと同様の機能を提供するが個別にインストールする必要はなく、Python3.3以降の環境であればデフォルトで使えると考えて良い。

python3 -m venv (環境名)

venvの実行例

python3 -m venv foo
cd foo
source bin/activate

venvが行うこと

他にもあるかもしれない。あんまりちゃんと読んではいない。

注意点

  • source を使って追加のスクリプトをシェルに食わせているので、環境はディレクトリ以下でなくとも有効。
  • 解除するには deactivate を使うが、同じ名前の関数を用意しているとハマるので注意する
    • まあ大した問題ではない気もする

venv環境の切り方

この辺りから我流になってくるが、動作させたいプロジェクトのディレクトリを作ったらその名前でvenvを用意してもいいと思う。

共通で使えるvenvを用意するとすぐ書き始められる利点はあるが、依存パッケージの管理と言う面ではややとっ散らかりそうな雰囲気があるので慣れてくるまでは控える。

環境を揃える

インストール

Macだとbrewで簡単にインストールできる

brew install python3

PATHを通す

pipでインストールされるスクリプトを簡単に呼び出せるように PATH に追加しておくと便利。

export PATH="$HOME/Library/Python/3.6/bin:$PATH"

PDB

Pythonデバッグツール。とりあえず適当に試したいときに使えば便利っぽい。

インタラクティブコマンド

whatis

オブジェクトの型を調べるときに使う。

> whatis res[0]
<class 'dict'>

venv環境でwebサーバのように永続的に動くものをsystemdで起動する

venv環境は環境変数を切り替えた上でシンボリックリンクやコピーを使って仮想環境を構築しているので 、仮想環境にpipで依存ライブラリをインストールした後は、仮想環境内の /bin 以下に置かれた実行可能スクリプトを直接呼び出すだけで同様の動作にできる。

例えば以下のように systemd を使って起動できる。

[Unit]
Description=Gunicorn instance to serve the falcon application
After=network.target

[Service]
User=centos
Group=centos
PIDFile=/tmp/gunicorn.pid
Environment="PATH=/home/centos/venv/project-name-env/bin"
WorkingDirectory=/home/centos/project-name
ExecStart=/home/centos/venv/project-name-env/bin/gunicorn index:app --bind=0.0.0.0:8000 --reload
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

この点はRubyのbundlerと比較して簡単に書けるので好き。結婚したい。

その他

コーディングスタイルガイド

終わり

こんなところか。あとは適当にコード書いていけばいい気はする。

とりあえずPythonはマニュアル読めばだいたい書けそうだし、 ライブラリもそれなりに充実しているので、Webアプリケーションを作るくらいなら苦労はしなさそうだ。

git-browse-remoteが恋しくなったのでgolangで書いてみた

現職場で何か足りないなーと思っていたことがあって、そうか git-browse-remote かと思ったので、お得意の gem install しようと思いましたが、 手元のRubyはバージョンアップのたびに入れなおすのが面倒と思ったので、golangの勉強がてらgoで書き直すことにしました。

github.com

urfave/cli が割と簡単でこれくらいのコマンドなら簡単に作れるのがいいなと思いました。

あまり時間もかかっていませんが、golangに慣れたという感じもしないのが不思議ですね。

Visual Studio Codeを使って書いてみましたが、特にそれほど設定しなくても便利という感想です。

if とか for くらいしか使ってないので、スライスとかそういう概念がほぼなく練習にするにしてももうちょっと機能があるものにすればよかったかもと思いましたが、まあいいか...。

Pythonほどマニュアル見ただけで使えるようになってないけど、追々慣れていく...

勉強がてらでいうと docker とかもキメておきたいな、時間の有り余る今こそ修行

HOMEを手に入れる話

世の中にはいろいろな不幸がある。今回の不幸はユーザが作れないLinuxサーバの運用を任されたことだ。

まあ、こんな不幸は滅多に遭遇することでもないとは思うけど、今後また同じ不幸に遭遇した時のために書いておく。

こんな情報いるのか?という疑問もあるけど、不幸にもぶち当たった人のヒントになるかもしれない。ホント、みんな useradd に感謝したほうがいい。

ホームディレクト

useradd ができない環境では自分のユーザを作成できず、共通のユーザで操作することになるため、.toprc のようないわゆるドットファイルを用意することができない。

今まで触ってきた環境があまりにもぬるま湯過ぎたのか、この事態に対処する方法がすぐには思いつかず、構築されたサーバと運用ルールを呪ったものだが、幸い他の作業をする時間があったため Linuxのホームディレクトリについて調べることにした。

ホームディレクトリ - Wikipedia

まあ、いわゆるホームディレクトリについて書かれている。マルチユーザで使う時にユーザが自由にファイルを作成できるディレクトリというなんともオアシスのような世界について記述されている。

しかし現実の世界に自由はない。そもそもマルチユーザなんて世界じゃないし。

そうそう、ホームディレクトリはログインした時のワーキングディレクトリで /etc/passwd に記述されている。

そしてホームディレクトリは環境変数 $HOME に格納され、多くのプログラムがこれを参照する仕組みになっている。

$HOME を設定するのはログインシェルで具体的には sshd から呼び出される bash みたいなものになるが、sshで接続した直後に環境変数$HOMEを設定できれば実質的なホームディレクトリが手に入る。

sshdのAcceptEnv

sshd の設定にローカルから送られた環境変数を引き継ぐ AcceptEnv という設定がある。

ただ、この設定は sshd の設定で、反映には sshd を再起動する必要があるし、 クライアント側でも SendEnv を書く必要があるとはいえ、AcceptEnv HOME ってのはかなりヤバい気がする。

sshでシェル起動時に環境変数を渡す

まあこっちのほうが現実的か。ログインシェルを変更する用途にも使えて便利だし。

こっちの方法は、 sshbash のようなシェルをログインモードで起動するようコマンドを渡し、その際に環境変数を上書きするという方法。

ssh -t remote_addr HOME=/tmp /usr/bin/bash -i -l

ssh のオプション -t は tty の割り当て。コマンドを渡す場合にはデフォルトでは tty が生成されないため。

bash のオプション -l はログインシェル、-iインタラクティブモード。これらを指定するとよく見る入力待ちの端末が出来上がる。

ただ、ログイン直後のカレントディレクトリは /tmp になっていない。 ログイン後 cd するのもいいが、/tmp/.bashrc の最後に cd と書いておけばいいだけではある。

あと、ssh-keygen のように一部のプログラムは $HOME を参照しない。まあ、こいつらは数が少ないので大抵は無視できる。

終わりに

長々と書いたけど、こういう不幸にはあまり遭遇したくない。

シェル周りのオプションを調べたり、sshのことをこんなに調べるなんてことは今までなかったから面白くはあったけど普通こんなことしないし。

単に top 打ったときの動作をいい感じにしたりするのにこんなことしないといけない環境を若い子には触らせたくないね。

あ、そうそう。実はもう一つの不幸に遭遇していて、そっちは timestamp_timeout=0 を設定された環境を任されたことだけど、これは解消したからよしとする(よくはない)。