Linux系ディスクトップパソコンで起動時にDockerコンテナを起動する
普段使っている開発用のパソコンには複数のDockerコンテナが有りますが、すべてのDockerコンテナが常時稼働しているわけではなく、必要になった Docker コンテナのみをその都度起動するようにしています。
以下は開発用パソコンにセットアップされている すべての docker コンテナ
docker
├── influxdb
│ ├── trial
│ │ ├── Dockerfile
│ │ ├── docker-compose.yml
│ │ ├── schema
│ │ │ └── weather-sensor.json
│ │ └── scripts
│ │ └── create_bucket_schema.sh
│ └── v2
│ ├── docker-compose.yml
│ ├── influxv2.env
│ └── telegraf
│ └── mytelegraf.conf
├── mysql
│ ├── Dockerfile
│ ├── config
│ │ ├── conf.d
│ │ │ └── docker.cnf
│ │ └── my.cnf
│ ├── docker-compose.yml
│ └── initdb
│ └── create_mylearn.sql
└── postgresql
├── 12
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── initdb
│ ├── 01_create_weather.sh
│ ├── 10_createdb_sensors.sql
│ └── 11_weather_db.sql
├── 16
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── initdb
│ ├── 10_createdb.sql
│ └── 11_createtable.sql
└── qiita
├── Dockerfile
├── docker-compose.yml
└── initdb
└── 10_createdb.sql
例えば Qiita 投稿用のデータベースコンテナは投稿記事の執筆で必要になったきだけ起動しています。
$ cd ~/docker/postgresql/qiita
$ docker compose up -d
[+] Running 2/2
✔ Network qiita_default Created 0.5s
✔ Container postgres-qiita Started
ただ開発用のパソコンでも常時稼働していてほしい Docker コンテナは有ります。
自分は自宅サーバーを稼働させているので不正アクセスしてきたホストの情報を毎日 PostgreSQL サーバー (Dockerコンテナ) に記録しているのですが、このコンテナはパソコン起動時に自動で稼働してほしいコンテナになります。
この記事では最新のUbuntu 24.04 (デスクトップ) に 最新(安定版)の Docker Engine (Community edition) をインストールし、 端末起動時に PostgreSQL サーバー (docker コンテナ) を稼働させるスクリプトを紹介します。スクリプト化することで、新しく導入する端末にも同一環境を構築することが可能になります。
さすがに開発用PCで検証するのは危険な作業なので今回はインストール用端末としてラズパイ4を使った例を示します。ラズパイ4を使うと OS は SDカードにインストールできるため手軽に検証することができます。
参考サイト
Docker エンジンインストール 公式ドキュメント (Ubuntu)
Install Docker Engine on Ubuntu
上記ドキュメントの手順にしたがって Docker Engine をインストールします。
参考書
CentOS 7 システム管理ガイド systemd / NetworkManager Firewalld徹底攻略
【発行日】2015年11月 1日 第1版第1刷
【ISBN】978-4-7980-4491-0 C3055
【発行所】株式会社 秀和システム
【参照にした箇所】
[Chapter 1] systemd [1.3.4] systemd-notify を使う
1 環境
1-1 検証用端末
- ラズパイ4
Raspberry Pi 4 ModelB 4GB
- OS: Ubuntu 24.04 (デスクトップ)
- OSのインストール作業はモニターとキーボード・マウスを接続
- 漢字ディレクトリ名をアルファベットに変更
$ LANG=C;xdg-user-dirs-gtk-update
- OSインストール後に Upgrade 実行
- SSHサーバーのインストール (openssh-server)
※OSインストール完了後は 開発PCからSSHでログインして作業 - Ipv4 ネットワーク: Static IP アドレスに変更
※以降下記の
ホスト名: raspi-4-dev, ユーザ: raspi, IPアドレス: 192.168.0.20
OSバージョン
raspi@raspi-4-dev:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 24.04.1 LTS
Release: 24.04
Codename: noble
アーキテクチャ (arm64)
raspi@raspi-4-dev:~$ hostnamectl
Static hostname: raspi-4-dev
Icon name: computer
Machine ID: 888eca6205914adaa91dff4876a07a63
Boot ID: 2375063679c94ecebb900d19ccce5a55
Operating System: Ubuntu 24.04.1 LTS
Kernel: Linux 6.8.0-1013-raspi
Architecture: arm64
開発PCからSSH経由でスクリプトを実行するため起動モードを multi-user.target に変更
※起動モードが GUI のままだとモニタ、キーボード・マウスが必要になるので面倒です
# OSインストール直後
raspi@raspi-4-dev:~$ systemctl get-default
graphical.target
# 起動モード変更
raspi@raspi-4-dev:~$ sudo systemctl set-default multi-user.target
Removed "/etc/systemd/system/default.target".
Created symlink /etc/systemd/system/default.target → /usr/lib/systemd/system/multi-user.target.
# 変更の確認
raspi@raspi-4-dev:~$ systemctl get-default
multi-user.target
1-2 開発用PCの環境
- OS: Ubuntu 22.04 (デスクトップ)
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.5 LTS
Release: 22.04
Codename: jammy
1-2-1 SDカードのバックアップ
Upgrade実行後に SD カードの内容を開発用 PC にバックアップし、dockerコンテナ生成の失敗時にOS状態を戻せるようにします。
SDカードを開発用 PC を挿したときのデバイスを確認
$ LANG=C;lsblk -e7 | grep sdc
sdc 8:32 1 59.5G 0 disk
├─sdc1 8:33 1 512M 0 part /media/xxxxx/system-boot
└─sdc2 8:34 1 59G 0 part /media/xxxxx/writable
SDカードの内容を開発用PCにバックアップ
$ sudo dd bs=8M if=/dev/sdc \
| gzip > UbuntuDesktop24_04_raspi-4-dev_upgraded.img.gz
[sudo] password for xxxxx:
3730+1 records in
3730+1 records out
31293702144 bytes (31 GB, 29 GiB) copied, 728.261 s, 43.0 MB/s
1-2-2 バックアップからのリストア
(1) SDカードのパーティションを確認する
$ LANG=C;sudo fdisk -l /dev/sdc
[sudo] password for xxxxx:
Disk /dev/sdc: 29.14 GiB, 31293702144 bytes, 61120512 sectors
Disk model: MassStorageClass
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xa0e2a9f8
Device Boot Start End Sectors Size Id Type
/dev/sdc1 * 2048 1050623 1048576 512M c W95 FAT32 (LBA)
/dev/sdc2 1050624 61120478 60069855 28.6G 83 Linux
(2) SDカードのアンマウント
$ sudo umount /dev/sdc1
$ sudo umount /dev/sdc2
(3) パーティションの削除
$ sudo fdisk /dev/sdc
Welcome to fdisk (util-linux 2.37.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): d
Partition number (1,2, default 2):
Partition 2 has been deleted.
Command (m for help): d
Selected partition 1
Partition 1 has been deleted.
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
(4) バックアップのリストア
$ gunzip --stdout UbuntuDesktop24_04_raspi-4-dev_upgraded.img.gz \
| sudo dd bs=8M of=/dev/sdc
0+850944 records in
0+850944 records out
31293702144 bytes (31 GB, 29 GiB) copied, 1757.42 s, 17.8 MB/s
2 Docker コンテナ作成
PostgreSQL-16 データベースコンテナを作成します。
2-1 Docker Engine インストール
SSHでラズパイ4にログインし、コマンドラインから実行します。
$ ssh raspi@192.168.0.20
raspi@192.168.0.20's password:
Welcome to Ubuntu 24.04.1 LTS (GNU/Linux 6.8.0-1013-raspi aarch64)
# ...一部省略...
Last login: Thu Nov 7 17:34:27 2024 from 192.168.0.101
参考サイトの公式ドキュメントページの手順にしたがって実行します。
(1) 非公式パッケージの削除
Raspberry Pi の Ubuntu 24.04 には docker パッケージはインストールされていませんが手順にしたがって実行します。
- 非公式パッケージ
- docker.io
- docker-doc
- docker-compose
- docker-compose-v2
- podman-docker
- containerd
- runc
※1 出力される日本語メッセージは煩雑なので一時的に C ロケールに変更します。
※2 未インストールの場合はその旨のメッセージが出力されるだけです。
raspi@raspi-4-dev:~$ LANG=C
raspi@raspi-4-dev:~$ for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get -y remove $pkg; done
[sudo] password for raspi:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Package 'docker.io' is not installed, so not removed
0 upgraded, 0 newly installed, 0 to remove and 6 not upgraded.
# ...以下省略...
(2) リポジトリ情報更新
raspi@raspi-4-dev:~$ sudo apt-get update
(3) dockerパッケージ取得用ライブラリイントール
raspi@raspi-4-dev:~$ sudo apt-get install -y ca-certificates curl
(4) 公式 GPG キーを keyringsディレクトリに保存
raspi@raspi-4-dev:~$ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
raspi@raspi-4-dev:~$ sudo chmod a+r /etc/apt/keyrings/docker.asc
※保存されたファイルの内容の抜粋は以下のとおりです。
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFit2ioBEADhWpZ8/wvZ6hUTiXOwQHXMAlaFHcPH9hAtr4F1y2+OYdbtMuth
lqqwp028AqyY+PRfVMtSYMbjuQuu5byyKR01BbqYhuS3jtqQmljZ/bJvXqnmiVXh
...一部省略...
jCxcpDzNmXpWQHEtHU7649OXHP7UeNST1mCUCH5qdank0V1iejF6/CfTFU4MfcrG
YT90qFF93M3v01BbxP+EIY2/9tiIPbrd
=0YYh
-----END PGP PUBLIC KEY BLOCK-----
(5) Dockerリポジトリ情報を Apt sources.list に追加してリポジトリ情報を更新
raspi@raspi-4-dev:~$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
raspi@raspi-4-dev:~$ sudo apt-get update
※保存されたファイルの内容は以下のとおりです。
deb [arch=arm64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable
(6) 公式 Docker パッケージインストール
- docker-ce
Docker Community Edition ※無償版 - docker-ce-cli
docker-ce のコマンドラインツール - containerd.io
Container runtime ※コンテナを実行するために構築されたランタイム - docker-buildx-plugin
Docker コマンドを拡張する CLI プラグイン - docker-compose-plugin
Docker compose プラグイン
raspi@raspi-4-dev:~$ sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
docker関連サービス (containerd.service, docker.service, docker.socket) が構成されます。
# ...途中の出力省略...
Created symlink /etc/systemd/system/multi-user.target.wants/containerd.service -> /usr/lib/systemd/system/containerd.service.
# ...途中の出力省略...
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service -> /usr/lib/systemd/system/docker.service.
Created symlink /etc/systemd/system/sockets.target.wants/docker.socket -> /usr/lib/systemd/system/docker.socket.
(7) バージョン確認
raspi@raspi-4-dev:~$ docker --version
Docker version 27.3.1, build ce12230
raspi@raspi-4-dev:~$ docker compose version
Docker Compose version v2.29.7
(8) ログインユーザーを docker グループに追加して実行権限を付与
※実行権限の付与を有効にするには一旦ログアウトする必要が有ります。
raspi@raspi-4-dev:~$ sudo gpasswd -a $USER docker
Adding user raspi to group docker
raspi@raspi-4-dev:~$ exit
logout
Connection to 192.168.0.20 closed.
2-2 PostgreSQL データベースコンテナの作成
PostgreSQL-16 データベース用のコンテナ生成・起動に docker compose を利用します。
※コンテナ生成と起動をファイルで作成可能のためメンテナンスが容易になります。
2-2-1 データベースコンテナ生成に必要なリソースファイル一覧
├── data
│ └── sql # DDL SQLファイル等の格納ディレクトリ (空)
├── docker # docker compose 用リソースディレクトリ
│ └── postgres
│ ├── .env # 機種ごとの差を埋める環境設定ファイル
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── initdb
│ ├── 10_createdb.sql # データベース作成DDL
│ └── 11_createtables.sql # テーブル生成DDL
└── postgresql # PostgreSQL データベース格納ディレクトリ (空)
docker compose 用リソースの詳細については下記投稿記事をご覧ください。
(1) Dockerfile
PostgreSQLの Dokcer 公式イメージ
(dockerHub) Official Images / postgres
※軽量の alpine のイメージを選択
FROM postgres:16-alpine
COPY initdb/*.sql /docker-entrypoint-initdb.d/
(2) docker compose 設定ファイル
ホスト側のボリュームを環境変数として環境変数ファイルに定義します。
※インストール端末に関わらずこの設定ファイルは変更せずにすみます。
services:
postgres:
build: .
env_file: ./.env
container_name: $CONTAINER_NAME
ports:
- "5432:5432"
volumes:
- "${HOST_PG_VOLUME}/data:/var/lib/postgresql/data"
- "${HOST_HOME}/data/sql:${CONTAINER_HOME}/data/sql"
environment:
- POSTGRES_USER=${PG_USER}
- POSTGRES_PASSWORD=${PG_PASSWD}
(3) 端末依存の環境変数ファイル
※以下はラズパイにインストールすることを前提とした値を設定しています。
# Qiita投稿用データベース
CONTAINER_NAME=postgres-raspi
# ラズパイ端末のユーザディレクトリ
USER=raspi
HOST_HOME="/home/$USER"
HOST_PG_VOLUME="$HOST_HOME/postgresql"
PG_USER=postgres
PG_PASSWD=xxxxxxxxx
# display locale
LANG=C
# locale
LANGUAGE=ja_JP:ja
LC_ALL=ja_JP.UTF-8
# Time zone
TZ=Asia/Tokyo
# volume mount
CONTAINER_HOME=/home/qiita
参考までに開発用PC の環境値を下記に示します。
※データベース格納ディレクトリは NFSマウントした NAS 上のボリュームを指定
#CONTAINER_NAME=postgres-16
# SynologyNAS: /volume1/Databases/postgres-16
# Host Nfs: /mnt/nas_databases <- SynologyNAS:/volume1/Databases
HOST_PG_VOLUME=/mnt/nas_databases/postgres-16
DB_NAME=mydmain_pgdb
...一部省略...
# volume mount
HOST_HOME=/home/loginuser
CONTAINER_HOME=/home/loginuser
(4) データベース生成DDLファイル
コンテナビルド時にデータベースとテーブルを生成します。
CREATE ROLE developer WITH LOGIN PASSWORD 'xxxxxxx';
ALTER ROLE developer WITH SUPERUSER;
CREATE DATABASE qiita_exampledb WITH OWNER=developer ENCODING='UTF-8' LC_COLLATE='ja_JP.UTF-8' LC_CTYPE='ja_JP.UTF-8' TEMPLATE=template0;
GRANT ALL PRIVILEGES ON DATABASE qiita_exampledb TO developer;
先頭行の \connect qiita_exampledb
は必須です。
※これを忘れると postgres データベースにテーブルが作成されてしまいます。
\connect qiita_exampledb
-- Qiita投稿用テスト用スキーマ
CREATE SCHEMA mainte2;
-- 不正アクセスされたIPアドレス管理テーブル
CREATE TABLE mainte2.unauth_ip_addr(
id INTEGER NOT NULL,
ip_addr VARCHAR(15) NOT NULL,
reg_date DATE NOT NULL,
country_code CHAR(2)
);
CREATE SEQUENCE mainte2.ip_addr_id OWNED BY mainte2.unauth_ip_addr.id;
ALTER TABLE mainte2.unauth_ip_addr ALTER id SET DEFAULT nextval('mainte2.ip_addr_id');
ALTER TABLE mainte2.unauth_ip_addr ADD CONSTRAINT pk_unauth_ip_addr PRIMARY KEY (id);
-- IPアドレスは重複なし
CREATE UNIQUE INDEX idx_ip_addr ON mainte2.unauth_ip_addr(ip_addr);
-- 不正アクセスカウンターテープル
CREATE TABLE mainte2.ssh_auth_error(
log_date date NOT NULL,
ip_id INTEGER,
appear_count INTEGER NOT NULL
);
ALTER TABLE mainte2.ssh_auth_error ADD CONSTRAINT pk_ssh_auth_error
PRIMARY KEY (log_date, ip_id);
ALTER TABLE mainte2.ssh_auth_error ADD CONSTRAINT fk_ssh_auth_error
FOREIGN KEY (ip_id) REFERENCES mainte2.unauth_ip_addr (id);
ALTER SCHEMA mainte2 OWNER TO developer;
ALTER TABLE mainte2.unauth_ip_addr OWNER TO developer;
ALTER TABLE mainte2.ssh_auth_error OWNER TO developer;
2-2-2 データベースコンテナ生成のビルド
開発用PCからラズパイ4にリソースファイルをコピーします。
$ scp -r data docker postgresql raspi@192.168.0.20:~
raspi@192.168.0.20's password:
ラズパイ4にSSHログインしてリソースを確認します。
raspi@raspi-4-dev:~$ ls -lrt --time-style long-iso | grep -v total
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Downloads
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Desktop
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Videos
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Templates
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Public
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Music
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Documents
drwx------ 3 raspi raspi 4096 2024-11-06 10:16 snap
drwxr-xr-x 4 raspi raspi 4096 2024-11-06 11:15 Pictures
drwxrwxr-x 3 raspi raspi 4096 2024-11-12 18:09 data
drwxrwxr-x 3 raspi raspi 4096 2024-11-12 18:09 docker
drwxrwxr-x 2 raspi raspi 4096 2024-11-12 18:09 postgresql
drwxrwxr-x 9 raspi raspi 4096 2024-11-12 18:09 work
(1) ログインユーザにdocker実行権限が付与されているか確認
末尾に xxx(docker)
が追加されていればOK
raspi@raspi-4-dev:~$ id
uid=1002(raspi) gid=1002(raspi) groups=1002(raspi),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),113(lpadmin),123(sambashare),984(docker)
(2) リソースファイルの確認
raspi@raspi-4-dev:~$ cd docker/postgres/
raspi@raspi-4-dev:~/docker/postgres$ ls -l --time-style long-iso | grep -v total
-rw-rw-r-- 1 raspi raspi 332 2024-11-12 18:09 .env
-rw-rw-r-- 1 raspi raspi 72 2024-11-12 18:09 Dockerfile
-rw-rw-r-- 1 raspi raspi 341 2024-11-12 18:09 docker-compose.yml
drwxrwxr-x 2 raspi raspi 4096 2024-11-12 18:09 initdb
(3) コンテナのビルド
raspi@raspi-4-dev:~/docker/postgres$ docker compose up --build -d
[+] Building 52.2s (8/8) FINISHED docker:default
=> [postgres internal] load build definition from Dockerfile 0.3s
=> => transferring dockerfile: 109B 0.0s
=> [postgres internal] load metadata for docker.io/library/postgres:16-alpine 3.2s
=> [postgres internal] load .dockerignore 0.3s
=> => transferring context: 2B 0.0s
=> [postgres internal] load build context 0.6s
=> => transferring context: 1.63kB 0.0s
=> [postgres 1/2] FROM docker.io/library/postgres:16-alpine@sha256:2aa615bd7f5c50fb2373411963caa48a66a938b29eebd0721 29.4s
=> => resolve docker.io/library/postgres:16-alpine@sha256:2aa615bd7f5c50fb2373411963caa48a66a938b29eebd0721eef29b1aee 0.5s
=> => sha256:2aa615bd7f5c50fb2373411963caa48a66a938b29eebd0721eef29b1aee4bb28 10.29kB / 10.29kB 0.0s
# ...途中のログ割愛...
=> [postgres 2/2] COPY initdb/*.sql /docker-entrypoint-initdb.d/ 17.7s
=> [postgres] exporting to image 0.6s
=> => exporting layers 0.3s
=> => writing image sha256:033aaf39f99521f55dd09ca5178ee41daca2c4ce05d8c7477e7eaf1844dca91c 0.0s
=> => naming to docker.io/library/postgres-postgres 0.1s
=> [postgres] resolving provenance for metadata file 0.0s
[+] Running 2/2
✔ Network postgres_default Created 0.3s
✔ Container postgres-raspi Started 1.4s
(4) コンテナの起動確認
raspi@raspi-4-dev:~/docker/postgres$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0252fbabfd92 postgres-postgres "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp postgres-raspi
(5) データベースとテーブルの確認
コンテナのビルド実行でデータベースが作成されるとともにデータベース上にテーブルが作成されました。
raspi@raspi-4-dev:~$ docker exec -it postgres-raspi bash
fbc6aa8c0427:/# psql -Udeveloper qiita_exampledb
psql (16.4)
Type "help" for help.
qiita_exampledb=# SET search_path TO public,mainte2;
SET
qiita_exampledb=# \dn
List of schemas
Name | Owner
---------+-------------------
mainte2 | developer
public | pg_database_owner
(2 rows)
qiita_exampledb=# \dt
List of relations
Schema | Name | Type | Owner
---------+----------------+-------+-----------
mainte2 | ssh_auth_error | table | developer
mainte2 | unauth_ip_addr | table | developer
(2 rows)
qiita_exampledb=# \dS unauth_ip_addr
Table "mainte2.unauth_ip_addr"
Column | Type | Collation | Nullable | Default
--------------+-----------------------+-----------+----------+---------------------------------
id | integer | | not null | nextval('ip_addr_id'::regclass)
ip_addr | character varying(15) | | not null |
reg_date | date | | not null |
country_code | character(2) | | |
Indexes:
"pk_unauth_ip_addr" PRIMARY KEY, btree (id)
"idx_ip_addr" UNIQUE, btree (ip_addr)
Referenced by:
TABLE "ssh_auth_error" CONSTRAINT "fk_ssh_auth_error" FOREIGN KEY (ip_id) REFERENCES unauth_ip_addr(id)
qiita_exampledb=# \q
fbc6aa8c0427:/# exit
exit
2-3 データベースコンテナ起動のシステムサービス化
端末起動時にデータベースコンテナを稼働させるためにはシステムサービスに登録する必要か有ります。
workディレクトリのユニットファイルをシステムディレクトリにコピーし有効化します。
├── bin
│ └── postgres-docker-service.sh # コンテナの起動・削除 実行スクリプト
└── work
└── etc
├── default
│ └── postgres-docker # ユニットファイルの環境変数設定ファイル
└── systemd
└── system # カスタムサービスのユニットファイル
└── postgres-docker.service # コンテナ起動・削除
(1) docker コンテナを起動、または削除するシェルスクリプト
コンテナの起動完了後 systemd-notify --ready
で、 Type=notify が指定されているサービスに処理完了を通知します。
#!/bin/bash
docker_cmd=$(which docker)
case "$1" in
start)
echo "docker-directory: $2"
cd $2 # Directory in docker-compose.yml
$docker_cmd compose up -d
sleep 1
# コンテナの起動が完了したら Type=nofity が指定されているサービスに通知する
systemd-notify --ready
echo "PostgreSQL($2) container ready!"
sleep 1
container_ls=`docker container ls`
echo "$container_ls"
cd ~
;;
stop)
# At shutdown
exists_container=$(${docker_cmd} container ls | grep postgres-raspi)
if [ -n "$exists_container" ]; then
cd $2
$docker_cmd compose down
echo "PostgreSQL($2) container down!"
cd ~
else
echo "docker.service already shutdownded."
fi
;;
*)
exit 1
;;
esac
(2) 環境変数設定ファイル
docker compose 設定ファイルの格納ディレクトリを指定
BUILD_PATH=/home/raspi/docker/postgres
(3) データベースコンテナ起動サービス
サービスタイプに notify を設定することで、このサービスの稼働完了まで他のサービスの起動を待たせることが可能になります。
[Unit]
Description=PostgreSQL-16 Start service with docker
After=docker.service
[Service]
Type=notify
TimeoutStartSec=0
NotifyAccess=all
EnvironmentFile=/etc/default/postgres-16-docker
ExecStart=/bin/sh -c "/home/raspi/bin/postgres-docker-service.sh start $BUILD_PATH"
User=raspi
[Install]
WantedBy=multi-user.target
Type=notify 指定と シェルスクリプト systemd-notify
を使う理由は?
docker コンテナを起動する処理は時間がかかる処理のため、データベースを必要とするアプリサービスがこのサービス開始のあとに実行されるように構成されていてもデータベースが立ち上がらないうちにアプリサービスが先に起動してしまうことがあります。
このような事象が発生するシステムの例を以下に示します。
上記のような複数のサービスを設定して端末(ラズパイ4)を起動すると以下のようなエラーログが出力され2つのアプリサービスともに起動に失敗します。
データベース接続エラー ※ログの先頭 (タイムスタンプ) 列は省略しています
could not connect to server: Network is unreachable
udp_monitor_from_weather_sensor.sh[470]: self.conn = psycopg2.connect(**dbconf)
udp_monitor_from_weather_sensor.sh[470]: File "/home/pi/py_venv/raspi4_apps/lib/python3.9/site-packages/psycopg2/__init__.py", >
udp_monitor_from_weather_sensor.sh[470]: conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
udp_monitor_from_weather_sensor.sh[470]: psycopg2.OperationalError: could not connect to server: Network is unreachable
udp_monitor_from_weather_sensor.sh[470]: Is the server running on host "raspi-4.local" (192.168.0.16) and accepting
udp_monitor_from_weather_sensor.sh[470]: TCP/IP connections on port 5432?
udp_monitor_from_weather_sensor.sh[470]: could not connect to server: Network is unreachable
udp_monitor_from_weather_sensor.sh[470]: Is the server running on host "raspi-4.local" (192.168.0.16) and accepting
udp_monitor_from_weather_sensor.sh[470]: TCP/IP connections on port 5432?
2つのアプリケーションサービスがデータベースを参照しているため、2つのアプリケーションサービスは PostgreSQLコンテナ起動完了を待って起動されなければなりません。
しかし下記のようにそれぞれのサービスユニットで前後関係を指定しても、順番通りに起動されますがデータベースコンテナの稼働が完了する前に後続のアプリケーションサービスが起動されてしまいます。
そこで PostgreSQLコンテナ起動サービスユニットの Type=notify
とシェルスクリプトの system-notify --ready
を連携させることにより、PostgreSQLコンテナの起動完了後にアプリケーションサービスを起動することができます。
実際のシステムについては下記 GitHubで公開しています。
UDP Weather Sensor packet monitor for Raspberry pi 4
3つのサービスユニットのソースは下記でご覧になれます。
raspi4_apps /src/installer
2-2-2 ユニットファイルをシステムサービスに登録
開発用PCからラズパイ4にリソースファイルをコピーします。
$ scp -r bin work raspi@192.168.0.20:~
raspi@192.168.0.20's password:
postgres-docker-service.sh 100% 695 74.6KB/s 00:00
postgresql-docker.service 100% 395 31.7KB/s 00:00
postgresql-docker 100% 39 2.9KB/s 00:00
ラズパイ4にSSHログインしてリソースを確認します。
raspi@raspi-4-dev:~$ LANG=C;ls -lrt --time-style long-iso | grep -v total
#...2-2-2 と同じなので割愛...
drwxrwxr-x 2 raspi raspi 4096 2024-11-13 18:06 bin
drwxrwxr-x 5 raspi raspi 4096 2024-11-13 18:06 work
(1) リソースをシステム領域にコピー
raspi@raspi-4-dev:~$ sudo cp ~/work/etc/default/postgresql-docker /etc/default
raspi@raspi-4-dev:~$ sudo cp ~/work/etc/systemd/system/postgresql-docker.service /etc/systemd/system
(2) ユニットファイルを有効化
raspi@raspi-4-dev:~$ sudo systemctl enable postgresql-docker.service
Created symlink /etc/systemd/system/multi-user.target.wants/postgresql-docker.service -> /etc/systemd/system/postgresql-docker.service.
(3) 端末をリブート
raspi@raspi-4-dev:~$ sudo reboot
(4) ラズパイ4にSSHログインしてサービスの起動状態を確認します。
raspi@raspi-4-dev:~$ systemctl status postgresql-docker.service
○ postgresql-docker.service - PostgreSQL container start service
Loaded: loaded (/etc/systemd/system/postgresql-docker.service; enabled; preset: enabled)
Active: inactive (dead) since Tue 2024-11-13 18:22:35 JST; 45s ago
Duration: 1.095s
Process: 1595 ExecStart=/bin/sh -c /home/raspi/bin/postgres-docker-service.sh start $BUILD_PATH (code=exited, status=0/S>
Main PID: 1595 (code=exited, status=0/SUCCESS)
CPU: 818ms
11月 13 18:22:31 raspi-4-dev sh[1616]: Network postgres_default Created
11月 13 18:22:31 raspi-4-dev sh[1616]: Container postgres-raspi Creating
11月 13 18:22:32 raspi-4-dev sh[1616]: Container postgres-raspi Created
11月 13 18:22:32 raspi-4-dev sh[1616]: Container postgres-raspi Starting
11月 13 18:22:33 raspi-4-dev sh[1616]: Container postgres-raspi Started
11月 13 18:22:34 raspi-4-dev systemd[1]: Started postgresql-docker.service - PostgreSQL container start service.
11月 13 18:22:34 raspi-4-dev sh[1596]: PostgreSQL(/home/raspi/docker/postgres) container ready!
11月 13 18:22:35 raspi-4-dev sh[1596]: CONTAINER ID IMAGE COMMAND CREATED STATUS >
11月 13 18:22:35 raspi-4-dev sh[1596]: fbc6aa8c0427 postgres-postgres "docker-entrypoint.s…" 4 seconds ago Up 3 seco>
11月 13 18:22:35 raspi-4-dev systemd[1]: postgresql-docker.service: Deactivated successfully.
PostgreSQLコンテナが正常起動されました。
3 インストールとコンテナビルド作業の自動化
いちいちコマンドラインで入力していては途中で失敗したときにもう一度繰り返さなければならないので大変です。
ここでは 「2 Docker コンテナ作成」で示した手順をスクリプト化します。
- インストール手順
- (1) Dockerパッケージのインストールとコンテナのサービス化
1_inst_libraries.sh
スクリプト実行後にログアウトする ※リブートはしない - (2) PostgreSQLデータベースコンテナのビルド
2_create_postgresql_container.sh
スクリプト実行後にリブートする
- (1) Dockerパッケージのインストールとコンテナのサービス化
3-1 インストール用シェルスクリプト作成
(1) Dockerパッケージのインストールとコンテナのサービス化
スクリプト実行前に環境変数 my_passwd を設定する ※ログインユーザのパスワード
#!/bin/bash
# execute before export my_passwd=xxxxxx
# https://docs.docker.com/engine/install/ubuntu/
# Install Docker Engine on Ubuntu
# Uninstall old versions
# 最小構成のUbuntu24.04 又はラズパイのUbuntu24.04ではインストールされていない
echo $my_passwd | {
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc
do
sudo --stdin apt-get -y remove $pkg
done
}
echo $my_passwd | { sudo --stdin apt-get update
sudo apt-get -y install ca-certificates curl
}
# Dockerの公式GPG鍵を追加する
echo $my_passwd | {
sudo --stdin curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
}
# 安定版のリポジトリのセットアップ
echo $my_passwd | { echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo --stdin tee /etc/apt/sources.list.d/docker.list > /dev/null
}
# Docker Engine のインストール: + docker-buildx-plugin
echo $my_passwd | { sudo --stdin apt-get update
sudo apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
}
# docker execute to raspi
echo $my_passwd | sudo --stdin gpasswd -a $USER docker
# Enable user apps system services
echo $my_passwd | { sudo --stdin cp ~/work/etc/default/postgresql-docker /etc/default
sudo cp ~/work/etc/systemd/system/postgresql-docker.service /etc/systemd/system
sudo systemctl enable postgresql-docker.service
}
echo "Done, logout this terminqal."
(2) PostgreSQLデータベースコンテナのビルド
開発用PCからSSHで再ログイン後に実行する。
※ログインユーザに docker実行権限が付与されているので環境変数のパスワード設定は不要
#!/bin/bash
docker_cmd=$(which docker)
# Create PostgreSQL container with qiita_exampledb database.
cd ~/docker/postgres
$docker_cmd compose up --build -d
exit1=$?
echo "Docker create PostgreSQL container >> status=$exit1"
$docker_cmd compose down
if [ $exit1 -ne 0 ]; then
exit $exit1
fi
cd ~/
echo "Done, sudo reboot now."
3-2 インストールアーカイブ作成
開発用PCでリソースの tar アーカイブを作成しインストール端末にコピーします。
docker_on_ubuntu_desktop
├── 1_inst_libraries.sh
├── 2_create_postgresql_container.sh
├── bin
│ └── postgres-docker-service.sh
├── data
│ └── sql
├── docker
│ └── postgres
│ ├── .env
│ ├── Dockerfile
│ ├── docker-compose.yml
│ └── initdb
│ ├── 10_createdb.sql
│ └── 11_createtables.sql
├── postgresql
└── work
└── etc
├── default
│ └── postgresql-docker
└── systemd
└── system
└── postgresql-docker.service
(1) tar アーカイブの作成
$ tar czf ../docker_on_ubuntu_desktop.tar.gz \
bin data docker postgresql work 1_inst_libraries.sh 2_create_postgresql_container.sh
(2) インストール端末にコピー
$ scp docker_on_ubuntu_desktop.tar.gz raspi@192.168.0.20:~
raspi@192.168.0.20's password:
docker_on_ubuntu_desktop.tar.gz 100% 3296 397.1KB/s 00:00
3-3 インストール用シェルスクリプトの実行
開発用PCからSSHでラズパイ4にログインします。
$ ssh raspi@192.168.0.20
raspi@192.168.0.20's password:
Welcome to Ubuntu 24.04.1 LTS (GNU/Linux 6.8.0-1013-raspi aarch64)
# ...以下出力省略...
ユーザディレクトリ確認
raspi@raspi-4-dev:~$ ls -lrt --time-style long-iso | grep -v total
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Downloads
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Desktop
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Videos
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Templates
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Public
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Music
drwxr-xr-x 2 raspi raspi 4096 2024-11-06 10:16 Documents
drwx------ 3 raspi raspi 4096 2024-11-06 10:16 snap
drwxr-xr-x 4 raspi raspi 4096 2024-11-06 11:15 Pictures
drwxrwxr-x 5 raspi raspi 4096 2024-11-14 10:06 work
-rw-rw-r-- 1 raspi raspi 3288 2024-11-15 09:58 docker_on_ubuntu_desktop.tar.gz
(1) tarアーカイブを解凍
raspi@raspi-4-dev:~$ tar zxf docker_on_ubuntu_desktop.tar.gz
raspi@raspi-4-dev:~$ ls -l --time-style long-iso | grep -v 2024-11-06 | grep -v total
-rwxrwxr-x 1 raspi raspi 1829 2024-11-12 11:24 1_inst_libraries.sh
-rwxrwxr-x 1 raspi raspi 332 2024-10-29 09:51 2_create_postgresql_container.sh
drwxrwxr-x 2 raspi raspi 4096 2024-11-12 17:10 bin
drwxrwxr-x 3 raspi raspi 4096 2024-10-21 14:12 data
drwxrwxr-x 3 raspi raspi 4096 2024-10-24 14:00 docker
-rw-rw-r-- 1 raspi raspi 3288 2024-11-15 09:58 docker_on_ubuntu_desktop.tar.gz
drwxrwxr-x 3 raspi raspi 4096 2024-11-15 10:08 postgresql
drwxrwxr-x 5 raspi raspi 4096 2024-11-14 10:06 work
3-3-1 Dockerパッケージのインストールとコンテナサービス化
(1) 1_inst_libraries.sh
実行
環境変数にログインパスワードを設定してスクリプト実行
export my_passwd=xxxxxxx
最後に下記のようにログが出力されたらインストール成功です。
raspi@raspi-4-dev:~$ export my_passwd=xxxxxxxx
raspi@raspi-4-dev:~$ ./1_inst_libraries.sh 2>&1 | tee ~/work/logs/1_inst_libraries.log
# ...途中のログ割愛...
Adding user raspi to group docker
Created symlink /etc/systemd/system/multi-user.target.wants/postgresql-docker.service -> /etc/systemd/system/postgresql-docker.service.
Done, logout this terminqal.
(2) 一旦端末からログアウト
raspi@raspi-4-dev:~$ exit
logout
Connection to 192.168.0.20 closed.
3-3-2 PostgreSQLデータベースコンテナのビルド
開発用PCから再度ログイン。
ログインユーザにdocker実行権限が付与されているのを確認
raspi@raspi-4-dev:~$ id
uid=1002(raspi) gid=1002(raspi) groups=1002(raspi),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),113(lpadmin),123(sambashare),984(docker)
(1) 2_create_postgresql_container.sh
実行
※下記のように出力されたらビルド完了です。
raspi@raspi-4-dev:~$ LANG=C
raspi@raspi-4-dev:~$ ./2_create_postgresql_container.sh 2>&1 \
tee ~/work/logs/2_create_postgresql_container.log
#...途中割愛...
Network postgres_default Creating
Network postgres_default Created
Container postgres-raspi Creating
Container postgres-raspi Created
Container postgres-raspi Starting
Container postgres-raspi Started
Docker create PostgreSQL container >> status=0
Container postgres-raspi Stopping
Container postgres-raspi Stopped
Container postgres-raspi Removing
Container postgres-raspi Removed
Network postgres_default Removing
Network postgres_default Removed
Done, sudo reboot now.
(2) 手動でリブート
raspi@raspi-4-dev:~$ sudo reboot
(3) 開発用PCから再度ログインし、PostgreSQLコンテナの起動ステータスを確認する
raspi@raspi-4-dev:~$ systemctl status postgresql-docker.service
○ postgresql-docker.service - PostgreSQL container start service
Loaded: loaded (/etc/systemd/system/postgresql-docker.service; enabled; preset: enabled)
Active: inactive (dead) since Fri 2024-11-15 10:11:52 JST; 1min 56s ago
Duration: 1.104s
Process: 1659 ExecStart=/bin/sh -c /home/raspi/bin/postgres-docker-service.sh start $BUILD_PATH (code=exited, status=0/SUCCESS)
Main PID: 1659 (code=exited, status=0/SUCCESS)
CPU: 865ms
11月 15 10:11:47 raspi-4-dev sh[1685]: Network postgres_default Created
11月 15 10:11:47 raspi-4-dev sh[1685]: Container postgres-raspi Creating
11月 15 10:11:48 raspi-4-dev sh[1685]: Container postgres-raspi Created
11月 15 10:11:48 raspi-4-dev sh[1685]: Container postgres-raspi Starting
11月 15 10:11:50 raspi-4-dev sh[1685]: Container postgres-raspi Started
11月 15 10:11:51 raspi-4-dev systemd[1]: Started postgresql-docker.service - PostgreSQL container start service.
11月 15 10:11:51 raspi-4-dev sh[1660]: PostgreSQL(/home/raspi/docker/postgres) container ready!
11月 15 10:11:52 raspi-4-dev sh[1660]: CONTAINER ID IMAGE COMMAND CREATED STATUS P>
11月 15 10:11:52 raspi-4-dev sh[1660]: e731e25b489a postgres-postgres "docker-entrypoint.s…" 5 seconds ago Up 3 seconds 0>
11月 15 10:11:52 raspi-4-dev systemd[1]: postgresql-docker.service: Deactivated successfully.
(4) 実行中の PostgreSQLコンテナに入りテーブルが作成されたか確認する
raspi@raspi-4-dev:~$ docker exec -it postgres-raspi bash
e731e25b489a:/# psql -Udeveloper qiita_exampledb
psql (16.5)
Type "help" for help.
qiita_exampledb=# SET search_path TO public,mainte2;
SET
qiita_exampledb=# \dn
List of schemas
Name | Owner
---------+-------------------
mainte2 | developer
public | pg_database_owner
(2 rows)
qiita_exampledb=# \dt
List of relations
Schema | Name | Type | Owner
---------+----------------+-------+-----------
mainte2 | ssh_auth_error | table | developer
mainte2 | unauth_ip_addr | table | developer
(2 rows)
qiita_exampledb=# \dS unauth_ip_addr
Table "mainte2.unauth_ip_addr"
Column | Type | Collation | Nullable | Default
--------------+-----------------------+-----------+----------+---------------------------------
id | integer | | not null | nextval('ip_addr_id'::regclass)
ip_addr | character varying(15) | | not null |
reg_date | date | | not null |
country_code | character(2) | | |
Indexes:
"pk_unauth_ip_addr" PRIMARY KEY, btree (id)
"idx_ip_addr" UNIQUE, btree (ip_addr)
Referenced by:
TABLE "ssh_auth_error" CONSTRAINT "fk_ssh_auth_error" FOREIGN KEY (ip_id) REFERENCES unauth_ip_addr(id)
qiita_exampledb=# exit
e731e25b489a:/# exit
exit
最後に
今回の検証にラズパイ4を使った理由は、現在開発用PCでデータベースに登録している処理をラズパイ端末に移行することを検討しているからになります。
自宅サーバーは年中休まず稼働しているので不正アクセスしてくるホストのデータも毎日データベースに登録なければなりません。
ラズパイをこの用途のサーバーにすればデータベースへの登録作業が自動化できると考えています。不正アクセスしてきたホスト情報、そのホストが属する国情報などを可視化する計画があるので OSはデスクトップを選択しました。
この記事で紹介したソースは下記 GitHub で公開しています
「3-2 インストールアーカイブ作成」で示したリソースになります。
pipito-yuko / qiita-posts / RaspberryPi / docker_on_ubuntu_desktop