この記事について
C言語の開発環境には様々な依存ライブラリをインストールする必要があります。そのため開発者それぞれの環境を統一するのが大変です。これまで環境を統一するためには、
- 開発者全員が同じマシンの開発環境を使って開発
- 開発環境構築手順書を作成し、配布する
- 開発環境のVMイメージを配布する
のような面倒なことをする必要がありました。
でも、今はコンテナという便利な物があります。
筆者もこれまでAWSのEC2上に開発&試験環境を作り、開発者へ提供していました。現在はDockerfileやDockerイメージを提供しています。ここではDockerfileとイメージの作成し、その利用方法を説明します。
前提
Docker for Mac/Windowsなど,Dockerコンテナが動作する環境がインストール済みである事。あと、ほんの少しDockerやLinuxの知識が必要です。
尚、この記事ではmac上での作業内容を記載しています。Windowsで作業する場合はパス等、適宜読み替えてください。
ゴール
この記事では、DockerコンテナのCentOS上で次の事ができるようになります。
- viでC言語のソースが書ける
- gcc,makeを使って、C言語のプログラムがコンパイルできる
- コンパイル済みの実行ファイルが実行できる
- コンパイル済みの実行ファイルがgdbでデバッグできる
- (おまけ)java,pythonも使える
Dokerfile作成
dokerファイルはコンテナイメージを作るための設計図です。
コンテナを使う以前は、OSやライブラリ等のインストールを手作業で行っていました。その手順をメモする替わりにDockerfileに書いておけば、簡単に再現できます。
これぞまさしく Infrastructure as Code(IaC) の第一歩ですね。
まずイメージを作る場所を決める
まず、イメージを作る場所を決めます。そこにDockerfileを作成/配置します。
> cd ~/docker/
Dockerfile
完成版はこちら(Dockerfile)から
Dockerfileにいろいろ記載してきます。
ベースイメージ
好きなOSを選んでください。
ここでは開発環境としてCentOS 7を選びます。
From centos:7
ワーキングディレクトリ
インストール作業するディレクトリを指定します。
WORKDIR /root
モジュールのインスール
yum で好きなパッケージをインストールしてください。
C言語で開発するなら、yumのgroupinstallが便利です。イメージが大きくなりますが、開発環境なら問題ないでしょう。
RUN yum -y update && \
yum -y groupinstall "Development Tools"
その他はご自由に!
こちら(Dockerfile)には、普通に使いそうなものを記載しています。
イメージへファイルの取り込み
ホストPCのファイルシステムからイメージ内へファイルを取り込みます。
COPY .bashrc /root/
今回はshell実行時に環境変数が設定された状態にしたいので、.bashrcをイメージに追加しています。
必要なファイルがあったら
COPY [ホストPCのファイル] [Dockerイメージ内の位置]
で追加してください。
Dockerイメージ作成
Dockerファイルのあるディレクトリ(ここでは ~/docker/)に移動してビルドします。
> cd ~/docker/
> docker build -t c_dev_env .
ちょっと時間がかかります。
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
c_dev_env latest aa8cc7f4372f About an hour ago 1.45GB
これでDockerイメージ「c_dev_env」の完成です。
参考
DockerHubに登録(push)するためには、タグ(-tオプション)に[organization]を付加する必要があります。
docker build -t [organization]/c_dev_env .
#Dockerコンテナ実行
この記事で一番重要な内容部分です。gdbコマンドがシステムコールを使用しているため、適切なオプションを付けないと実行できません。
docker run -it --name "my_dev_env" --cap-add=SYS_PTRACE --security-opt="seccomp=unconfined" c_dev_env /bin/bash
オプション、パラメタの説明
オプション/パラメタ | 説明 |
---|---|
-i | (--interactive)stdinを使用する |
-t | ttyを有効にする |
--name | 名前をつける。ここでは"my_dev_env" |
--cap-add=SYS_PTRACE | Linux Capability ptrace()システムコールを許可。 (参考)Man page of Capabilities |
--security-opt="seccomp=unconfined" | seccompという仕組みでもシステムコールが制限されているため、それを回避。(参考)seccompプロファイルを使ってdockerのシステムコールを制御 |
パラメタ1 | イメージ名。ここでは c_dev_env を指定 |
パラメタ2 | 実行コマンド。ここでは/bin/bash を指定 |
-v [ホストPCのディレクトリ]:[コンテナのディレクトリ] で、マウントしておくと便利です。ホストPCの慣れたエディタで開発できます。
> docker run -it --name "my_dev_env" --cap-add=SYS_PTRACE --security-opt="seccomp=unconfined" c_dev /bin/bash
bash-4.2# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
bash-4.2#
これで実行環境が完成しました。
プログラム作成&実行
それでは簡単なプログラムを作って、実行してみましょう。
Dockerイメージ内 /root/sampleの下へ
bash-4.2# cd /root
bash-4.2# mkdir sample
bash-4.2# cd sample
bash-4.2# vi hello.c
プログラム作成 hello.c
#include <stdio.h>
int main(void)
{
int sum = 0;
sum++;
printf("Hello, World!\n");
printf("sum = %d\n", sum );
return 0;
}
そしてコンパイル
gcc -g -o hello hello.c
-g:デバッグオプションを忘れずに!
bash-4.2# gcc -g -o hello hello.c
bash-4.2# ls -la
total 24
drwxr-xr-x 2 root root 4096 Jun 23 14:24 .
dr-xr-x--- 1 root root 4096 Jun 23 14:23 ..
-rwxr-xr-x 1 root root 9528 Jun 23 14:24 hello
-rw-r--r-- 1 root root 136 Jun 23 14:24 hello.c
bash-4.2# ./hello
Hello, World!
sum = 1
ちゃんと実行できています。
デバッグ
今度は、gdbでデバッグしてみましょう
bash-4.2# gdb hello
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-114.el7
〜 省略 〜
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/sample/hello...done.
(gdb)
エラーもなくプロンプトが出てきました。
続いて、ブレークポイント設定&ステップ実行
(gdb) l <---リスト表示
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int sum = 0;
6 sum++;
7 printf("Hello, World!\n");
8 printf("sum = %d\n", sum );
9 return 0;
10 }
(gdb) b 6 <---6行目にブレークポイント設定
Breakpoint 1 at 0x40056c: file hello.c, line 6.
(gdb) r <---実行
Starting program: /root/sample/hello
Breakpoint 1, main () at hello.c:6
6 sum++; <---ブレークポイントで止まった!
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7_6.5.x86_64
(gdb) n <---ステップ実行
7 printf("Hello, World!\n");
(gdb) p sum <---変数内容表示
$2 = 1
ブレークポイント設定、ステップ実行問題なくできています。
Dockerコンテナから抜ける
最後に、DockerコンテナのLinuxから抜けるには Ctrl+p ,Ctrl+qです。
もう一度つなぐ時は、 docker attach [タグ名]
> docker attach my_dev_env
bash-4.2#
まとめ
Infrastructure as Code(IaC) は大規模システムの環境構築、特にパブリッククラウドやコンテナオーケストレーション環境では必須の技術要素です。しかし、こんな身近な環境でも体感できる事に、少し感動しました。(当たり前なのですが)
- インストール手順書をDockerfileで書く
- 環境構築手順書をPlaybookに書く
これも**Infrastructure as Code(IaC)**の重要な要素であり、これだけでもトイル(
SRE サイトリライアビリティエンジニアリング 5章)を確実に減らすとことができる気がします。ただし、Dockerfile職人とPlaybook職人による属人化には気をつけて。