ひゃまだのblog

ひゃまだ(id:hymd3a)の趣味のブログ

Alpine Linuxを育みながらDockerのことを知る

(2023-12-03 初稿 - 2023-12-19 修正・追記)

はじめに

Alpine Linuxのことは、Dockerのことを調べていたら、人気のあるデストリビューションであること初めて知った。

最初に驚いたのは、そのサイズの小ささ。

$ docker pull alpine
$ docker images
REPOSITORY              TAG       IMAGE ID       CREATED          SIZE
alpine                  latest    b541f2080109   2 days ago       7.34MB
tensorflow/tensorflow   latest    fc4fde60d99e   2 months ago     1.76GB
busybox                 latest    a416a98b71e2   4 months ago     4.26MB
hello-world             latest    9c7a54a9a43c   7 months ago     13.3kB

なんと、7.34MBでhello-worldよりも小さく、busyboxよりもちょっと大きいだけ。

Alpine Linuxのことは、ほとんど知らないけど、このサイズならいろいろといじって、dockerのことを勉強しようと思った。

Dockerを使っていて、不便だと感じていた点

Dockerを使っていて不便だと感じていた点は、主に以下の2つ。

  1. 新しいContainerを作成するたびに、ユーザの追加やインストール、設定を実行する必要がある
  2. 特に docker run コマンドでオプションを間違えると後で変更が大変

最初の不便は Dockerfile を作成することで、ほとんどの部分は解消する。

2番目の不便なことも、commitとすることでrunのオプションを変更できるけど、Dockerfileがうまくできていれば、再度runし直しても大して手間がかからないことがわかった。

このページでは、Docker初心者の筆者が、Alpine Linuxを育みながらDockerの設定についてのメモを記す。

Alpine Linuxをインストールしたらやること

Alpine Linuxをインストールして、一時的に利用する場合で無ければ、筆者はたぶん以下のことをする。

  1. alpine Linux のアップデートアップ、グレード(apk update && apk upgrade)
  2. ユーザ(例:hoge)の追加 (adduser)
  3. プロンプトの変更 user@hostname:directory 形式     #2023-12-06 追記
  4. /home/hoge/bin の作成                                                #2023-12-06 追記
  5. /home/hoge/bin へのPATH 追加                                  #2023-12-06 追記
  6. sudoのインストール( apk add sudo)
  7. opensshのインストール(apk add openssh)
  8. /etc/hostsの設定                                                          #2023-12-19 追記
  9. 各種ソフトウェアの設定ファイルのコピー等

上記のうち、1〜7までは、Dockerfileに記述すれば、最初のログインから使えるようになる。

8については、Docker compose を利用すればできるようだけど、このページではホストと橋渡しディレクトリで渡すことにする。

また、9については、Dockerfileの中にCOPYでContainer内にコピーできるが、8と同様に橋渡しディレクトリで引き渡すことにする。

8、9ともに、ホストへsshができれば、実際にはそれほど困らないと思う。

以上の方針の下に、Dockerfileを作成する。

作成したDockerfile

本記事では、doc-al(docker-alpineの略のつもり)ディレクトリを新たに作成し、Dockerfileを置くことにする。

作成したDockerfileは以下のとおり。

FROM alpine:latest

RUN apk update && apk upgrade
RUN apk update && apk add   sudo   openssh

ARG USERNAME=hoge
ARG GROUPNAME=hoge
ARG UID=1000
ARG GID=1000

RUN addgroup -S -g "${GID}" "${USERNAME}" &&   adduser -u "${UID}" -G "${USERNAME}" -D "${USERNAME}" &&   echo "$USERNAME       ALL=(ALL) NOPASSWD:ALL" >gt;>gt; /etc/sudoers

USER $USERNAME
WORKDIR /home/$USERNAME/

# add 2023-12-06
COPY --chown=$USERNAME:$GROUPNAME .profile /home/$USERNAME/

ADD --chown=$USERNAME:$GROUPNAME ./bin /opt/bin
RUN ln -s /opt/bin /home/$USERNAME/bin

ちょっと解説。

  • FROM alpine:latest    # 最新版のAlpine Linux を利用
  • RUN アップデート、アップグレードで最新にしてから、opensshとsudoをインストール
  • ARG hogeさんのUIDやGIDの設定
  • RUN グループhogeの作成、ユーザhogeの追加、sudoersにhogeを追加。これで、初回起動時からsudoが使える。
  • COPY コメント化してあるけど、doc-alディレクトリに.vimrc等を保存しておけば、Conteiner内の/home/hogeディレクトリにコピーすることができる。
  • USERは、hoge ワークディレクトリは /home/hogeにする。
  • /ect/profile を ホストの doc-al ディレクトリにコピーし、PATHとプロンプトを変更しておく。変更した .profile をコンテナのホームディレクトリにコピー
  • ホストのdoc-al/binにコンテナ内で必要な実行ファイルを入れておき、コンテナのホームディレクトリにシンボリックリンクを貼る。

参考までに上記のDockerfileは、以下のようなディレクトリにおいてある。

binディレクトリには、テスト用にhello-alpineというbashスクリプトをおいてあるけど、コンテナ内で必要なスクリプトを保存しておくと便利。

doc-al
├── .profile
├── Dockerfile
└── bin
    └── hello-alpine

2 directories, 3 files

また、/rtc/profileからコピーして作成した .profile は、PATHの追加とプロンプトの変更してあり、中身は以下のとおり。

# mod 2023-12-06 add /home/hoge/bin:
export PATH="/home/hoge/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

export PAGER=less
umask 022

# use nicer PS1 for bash and busybox ash
if [ -n "$BASH_VERSION" -o "$BB_ASH_VERSION" ]; then
	#PS1='h:w$ '
	PS1='u@h:w$ '                  # mod 2023-12-06
# use nicer PS1 for zsh
elif [ -n "$ZSH_VERSION" ]; then
	PS1='%m:%~%# '
# set up fallback default PS1
else
	: "${HOSTNAME:=$(hostname)}"
	PS1='${HOSTNAME%%.*}:$PWD'
	[ "$(id -u)" -eq 0 ] && PS1="${PS1}# " || PS1="${PS1}$ "
fi

if [ -n "$BASH_VERSION" ] && [ "$BASH" != "/bin/sh" ]; then
	# if we're bash (and not /bin/sh bash), also source the bashrc
	# by default, bash sources the bashrc for non-login,
	# and only /etc/profile on login (-l). so, make it do both on login.
	# this ensures that login-shell bash (e.g. -bash or bash -l) still sources the
	# system bashrc, which e.g. loads bash-completion
	. /etc/bash/bashrc
fi

for script in /etc/profile.d/*.sh ; do
	if [ -r "$script" ] ; then
		. "$script"
	fi
done
unset script

Dockerfileからimageの作成

上記でDockerfileを作成したので、imageを作成する。

ここでは、imageの名前を myalpine にする。

$ cd doc-al
$ docker build -t myalpine .

エラーが無ければ、無事にimageが作成できる。

ちなみに、docker imagesで確認すると、まだわずか17.5MBでimageが作成できた。

$ docker images
REPOSITORY              TAG       IMAGE ID       CREATED          SIZE
myalpine                latest    f7c1a7752c5d   20 minutes ago   17.5MB

Alpine Linux すごいね。

作成したImageを使って、runコマンドでContainerを作成

実は、Dockerを使っていて、筆者が一番気を使うコマンドがrun。オプションをつけ忘れてContainerを使い込んで、 後で後悔するのがこのrunコマンド。

後ほど、救済策は紹介するけど、やはり最初から間違えないのに越したことはない。

ここではContainer名を myal 、コンテナのホスト名を test-al として、runコマンドでContainerを作成する。

docker run --name myal -v $PWD:/work -h test-al -itd myalpine

ここで、重要なのは -v(volume)オプションで、現在のディレクトリをContainerの /work ディレクトリにリンクする。これで、ホストとContainerのデータコピー等がスムーズにできるようになる。ちなみに、筆者は、doc-alを/workディレクトリに設定している。

ちょっと不便だけど、インストール後に実行することの5と6番目のことは、-vオプションで概ね解決できる。

なお、以下のように --add-host オプションをつけるとsshでのhostとの連携が良くなる。ここでは、hostをhost-pc アドレスは、192.168.1.xxxに設定。

docker run --name myal -v $PWD:/work --add-host=host-pc:192.168.1.xxx -h test-al -itd myalpine

上記のようにContainerを作成すれば、初回ログインからssh host-pcでssh接続できる。

なお、オプションの-itdは以下の意味。

  • -it インターラクティブな操作
  • -d  Containerを作成したら、デタッチ

(2023-12-19追記)

上記では、--add-host オプションでホストのIPアドレスを追加したけど、カレントディレクトリにhostsファイルを作成しておき、-vオプションを使えば /etc/hosts に渡せることに気がついた。

docker run --name myal -v $PWD:/work -v ./hosts:/etc/hosts:ro -h test-al -itd myalpine

ちなみに、ホストのファイルなので、念のためにROオプションを付けてRead Onlyにしてある。

(追記終了)

runでContainerを作成して、本来なら Ctrl+p Ctrl+q でデタッチすべきところを、exitで終了するとContainerが終了した状態になってしまう。

これを避けるため、筆者は次節で説明するexecコマンドで、ログインするようにしている。

なお、runコマンドでexitしても、以下のとおり start コマンドで再び開始することができる。

docker start myal

execコマンドでログイン、作業の実行

前節で述べたとおり、原則 exec コマンドでログイン、通常の作業をするようにしている。

docker exec -it myal ash
docker exec -it --user hoge myal ash --login

ちなみに、Alpine Linuxのデフォルトのshellはashとのこと。下の行のように、--loginオプションをつけるとホームディレクトリの.profileを実行してくれるよ。

ログイン後、現在のディレクトリを確認。

$ pwd
/home/hoge

sudoが使えるか確認。

$ sudo apk update && apk upgrade

sshの確認。

$ ssh host-pc
The authenticity of host '192.168.1.xxx (192.168.1.xxx)' can't be established.
ED25519 key fingerprint is SHA256:2JQIaz3cqnqqMTKorWnEKpUvzxb7Ac9xApgyK9Wx2yI.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? 

Enterを押して、パスワードを入力すれば、無事にhost-pcにつながる。

$ ls /work
Dockerfile 

ホストの doc-al に保存したDockfileが表示される。

/workディレクトリは、ホストとのファイルの橋渡しに利用できる大切なディレクトリ。

以上までで、素のAlpine Linuxを利用する場合に比べて、だいぶ楽に使い始めることができると思う。

Bashスクリプト

物覚えの悪い筆者は、Dockerのコマンドやオプションをすぐに忘れてしまう。(^_^;)

そこで、Dockerのイメージbuildからexecコマンドでログインするまでを、以下のBashスクリプトを書いて実行している。

#!/usr/bin/env bash
# docker exec file
# ver0.01 start 2023-12-03
# ver0.02 hostname user login 2023-12-06
# need Dockerfile

WORKDIR="/home/hoge/doc-al"
DOCNAME="myal"
DOCIMAGENAME="myalpine"
HNAME=$HOSTNAME"-al"

cd $WORKDIR

if [[ ! $(docker images | grep $DOCIMAGENAME) ]]; then
  docker build -t $DOCIMAGENAME .
fi
    
result=$(docker ps -a | grep $DOCNAME)
#echo $result

if [[ $(echo $result | grep "Exited") ]]; then
  docker start $DOCNAME >gt;/dev/null
elif [[ ! $result ]]; then
  docker run --name $DOCNAME -v $PWD:/work -h $HNAME -itd $DOCIMAGENAME
  #docker run --name $DOCNAME -v $PWD:/work --add-host=fam:192.168.1.XXX -h $HNAME -itd $DOCIMAGENAME
fi

docker exec -it --user $USER $DOCNAME ash --login

スクリプトの実行は、chmod +x doc-alで実行権を与えて、以下のとおり実行する。

$ doc-al

特に難しいことはしていないが、簡単に解説する。

  • 初回実行時は、myalpine imageができていないので、buildしてimageを作る。
  • また、runコマンドを実行して、myalと命名し、/workを橋渡しディレクトリにする。
  • ホストの再起動などで、もしmyalがstop(Exit)しているようであれば、startコマンドで起動する。
  • myalが起動していれば、execでログインする。

(2023-12-06追記)

上記のBashスクリプトを実行すると、プロンプトも以下のとおり変更され、PATHの追加やユーザのホームディレクトリにログインできるようになる。

hoge@fuga:~$ doc-al
hoge@test-al:~$ pwd
/home/hoge
hoge@test-al:~$ echo $PATH
/home/hoge/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

これまでの設定を行った後、Alpine Linuxのサイズを調べてみると以下のとおり。

$ docker ps -a -s
CONTAINER ID   IMAGE      COMMAND     CREATED       STATUS       PORTS     NAMES     SIZE
9e004b908ef7   myalpine   "/bin/sh"   2 hours ago   Up 2 hours             myal      1.88MB (virtual 19.4MB)

なんと、まだ20MBにも達していない。なんと優秀なディストリビューションだこと。

これまでのContainerやImageの削除

上記スクリプトを実行するにあたって、一度、ContainerやImageを削除して実行しないと、スクリプトの評価ができない。

ContainerやImageの削除は、以下のとおり。

$ docker stop myal
$ docker rm myal
$ docker rmi myalpine

実行後は、docker ps -a や docker imagesで確認を。

runのオプションを変更したい場合

長くContainerを使う場合は、runのオプションに気を使う。

万が一、オプションを変更したい場合は、以下のとおりimageをcommitした後、commitしたimageを用いてrunすることにより、オプションを変更することができる。

ただし、Dockerfile等を変更してimageを作り直すのではないので、あくまでも一時的な対策とのこと。

docker stop myal
docker commit myal myalpine:v1  (Versionは自分で、つけない場合はlatest)
docker run  オプション --name myal-v2 -v $PWD:/work -itd myalpine

以上、参考までに。

おわりに

取り急ぎ、Dockerfieを作成して、Containerを新規に作成しても、ある程度使いやすいようにBashスクリプトを作成した。

今後は、Docker Composeを利用するとさらに便利になるとのことなので、Composeを勉強して、何かわかったら追記するつもり。