ハードリンクとシンボリックリンク
リンクとはファイルの別名を構成し、
同じファイルにアクセスできるようにする仕組みです。
Linuxでは2種類のリンクファイルが用意されています。
ハードリンク
- 元のファイルとリンクファイルでiノードとファイルの実態を共有する仕組み
- 元のファイルが削除されてもアクセスできる
シンボリックリンク
実際に使ってみる
リンクをつくるにはln
コマンドを使います
ln
コマンド書式
ln [option] リンク元 リンク
Option
-f
リンク先があった場合上書き-s
シンボリックリンクを作成
ln
コマンドでリンクを作る↓
> touch original.txt > ls original.txt > ln original.txt hard.ln > ln -s original.txt symbol.ln > ls -li *.ln 63007659 -rw-r--r-- 2 [user] staff 0 3 21 14:12 hard.ln 63007696 lrwxr-xr-x 1 [user] staff 12 3 21 14:13 symbol.ln@ -> original.txt
シンボリックリンクはls -l
で見たときファイルの種類がlになってる
リンクの中身を参照してみると、オリジナルと同じになっている
> echo "test" > original.txt > cat hard.ln test > cat symbol.ln test
リンク元であるオリジナルを消してみる
> rm -f original.txt > ls -l *.ln -rw-r--r-- 1 [user] staff 5 3 21 14:28 hard.ln lrwxr-xr-x 1 [user] staff 12 3 21 14:13 symbol.ln@ -> original.txt > cat hard.ln test > cat symbol.ln cat: symbol.ln: No such file or directory
ハードリンクは既存のファイルとiノードを共有する仕組みなので,
オリジナルファイルが削除されてもリンクファイルはiノードを引き続き参照できる。
リンクファイルも削除されてリンク数が0になったらiノードが解放される。
シンボリックリンクはオリジナルのパスが記録されているだけなので
オリジナルが削除されたらファイルの内容に参照できなくなる。
go build のエラー `go build main.go- build output "main.go" already exists and is not an object file`
goでpackageをbuildしようとしたらタイトルのようなエラーが出た↓
# go build go build main.go: build output "main.go" already exists and is not an object file
環境
ディレクトリ構造
. ├── Dockerfile ├── README.md ├── docker-compose.yml ├── go.mod ├── go.sum ├── keys │ ├── go.mod │ └── keys.go ├── main.go └── tweet.go
解決法
main 以外の名前でbuildすればよかった
# go build -o app
go run main.goしても分割したファイルの関数が読まれない
Go言語初心者です
# go run main.go # command-line-arguments ./main.go:5:2: undefined: tweet
main.go のmain関数から
同じpackageのtweet.goに記述した
tweet関数を呼び出そうとしたらエラーが出ました
main.goもtweet.goも同じpackageなので呼び出せるはずなのですが
よくわからなかったので調べました。
ディレクトリ構成はこんな感じ↓
. ├── Dockerfile ├── README.md ├── docker-compose.yml ├── go.mod ├── go.sum ├── keys │ ├── go.mod │ └── keys.go ├── main.go └── tweet.go
原因
go run
はカレントディレクトリ以下の全ファイルを読んでくれるわけではないため
必要なファイルを全てオプションで渡す必要がある
go run *.go
or go run main.go tweet.go
もしくは
# go build -o app
TwitterAPI使って自動でTweetするGoのプログラムを作る with Docker
今回はTwitterAPIを使ってGoで書かれたプログラムからツイートしてみます
環境
- Docker Version 20.10.2
- docker-compose version 1.27.4, build 40524192
前準備
Twitterのアカウントを取得して以下の4つを発行します
ソースコード
TwitterAPIを有効にするためOAuth認証を通します
keys.go
package keys import ( //Twitter APIに必要なimportになります。なければインストールしましょう。 "github.com/ChimeraCoder/anaconda" "os" ) func GetTwitterApi() *anaconda.TwitterApi { anaconda.SetConsumerKey(os.Getenv("TFC_API_KEY")) anaconda.SetConsumerSecret(os.Getenv("TFC_API_SECRET_KEY")) api := anaconda.NewTwitterApi(os.Getenv("TFC_ACCESS_TOKEN"), os.Getenv("TFC_ACCESS_SECRET")) return api }
ツイートするための
main.go
package main import ( "fmt" . "fmt" "local.packages/keys" // go modulus環境でローカルパッケージをインポート ) func main() { api := keys.GetTwitterApi() tweet_text := fmt.Sprintf("テストツイート") tweet, err := api.PostTweet(tweet_text, nil) if err != nil { panic(err) } Print(tweet.Text) fmt.Println("posting!") }
import
でローカルにあるkeysパッケージをもってくるのですがこの書き方がベストプラクティスかどうかは微妙です。
mainパッケージとkeysパッケージ両方にgo.modを配置してmainのほうのgo.modファイルをこうします↓
module main.go go 1.14 replace local.packages/keys => ./keys // modules名を相対パスに置き換えている require ( // ry )
なんでこれでうまくいってるのかよくわかってないです
ディレクトリ構成はこうなります↓ keysパッケージのほうのmodファイルには何も書いてません
> tree . ├── Dockerfile ├── README.md ├── docker-compose.yml ├── go.mod ├── go.sum ├── keys │ ├── go.mod │ └── keys.go ├── main.go
Dockerfileを修正
Dockerfileにアナコンダをとってくる文を追加します
FROM golang:1.14.0-alpine3.11 # ビルド時にashを使う SHELL ["/bin/ash", "-c"] WORKDIR /go/src/app COPY ./ ./ # Go Modulesを使うと明言する ENV GO111MODULE=on # apkはalpine linux(超軽量なLinux)の独自のパッケージ管理システム RUN apk add --no-cache alpine-sdk # Golang 環境構築 # (追加)TwitterAPIを捌くのにアナコンダを使用します RUN go get github.com/ChimeraCoder/anaconda
環境変数受け渡しのためにdocker-compose.ymlを修正
ホスト(コンテナの外)のシェルに環境変数をセット
私はFishシェラーをなのでset -x
で環境変数をセットします
TFCは今回のプロジェクトの名前なので気にしないでください
set -x TFC_ACCESS_TOKEN xxxxxxxxxxxxxxxxxxxxxxxxxxxxx set -x TFC_ACCESS_SECRET xxxxxxxxxxxxxxxxxxxxxxxxxxxx set -x TFC_API_KEY xxxxxxxxxxxxxxxxxxxxx set -x TFC_API_SECRET_KEY xxxxxxxxxxxxxxxxxxxxx
docker-composeでコンテナ起動時に環境変数を設定するようにする
こちらの記事を参考にして .ymlファイルを書きました。↓
version: '3' # composeファイルのバーション指定 services: app: # service名 build: . # ビルドに使用するDockerfileがあるディレクトリ指定 tty: true # コンテナの起動永続化 volumes: - .:/go/src/app # マウントディレクトリ指定 environment: TFC_API_KEY: ${TFC_API_KEY?err} # composeが起動しているシェルの環境変数を利用 TFC_API_SECRET_KEY: ${TFC_API_SECRET_KEY?err} # 未設定またはempty値の場合にエラ〜メッセージを表示して終了する TFC_ACCESS_TOKEN: ${TFC_ACCESS_TOKEN?err} TFC_ACCESS_SECRET: ${TFC_ACCESS_SECRET?err}
コンテナの中に入って環境変数を確認
$ docker-compose up -d $ docker-compose exec app /bin/ash $ set 省略
set
コマンドで先ほどセットしたKEYやTOKENが表示されたら成功です
ツイートしてみる
コンテナの中に入ってgo run
します
> docker-compose exec app /bin/sh /go/src/app # go run main.go テストツイートposting!
自分のツイッターアカウントを見にいってみたら成功してました
ちなみに2連続で同じ内容を呟くと以下のようなエラーが返ってきました
panic: Get https://api.twitter.com/1.1/statuses/update.json returned status 403, {"errors":[{"code":187,"message":"Status is a duplicate."}]}
おつかれさまです。次回はツイートの取得をやります。
参考サイト
案外知られてないdocker-composeの環境変数定義の記法
Docker + GitでGoの開発を始める
環境
- Docker Version 20.10.2
- docker-compose version 1.27.4, build 40524192
Githubでリポジトリを作る
URLをとってきて任意のディレクトリで
Git clone [URL]
Docker環境作成
Dockerfileを記述
FROM golang:1.14.0-alpine3.11 # ビルド時にashを使う SHELL ["/bin/ash", "-c"] WORKDIR /go/src/app COPY ./ ./ # EXPOSE 8080 # Go Modulesを使うと明言する ENV GO111MODULE=on # apkはalpine linux(超軽量なLinux)の独自のパッケージ管理システム RUN apk add --no-cache alpine-sdk
docker-compose.ymlを記述
version: '3' # composeファイルのバーション指定 services: app: # service名 build: . # ビルドに使用するDockerfileがあるディレクトリ指定 tty: true # コンテナの起動永続化 volumes: - .:/go/src/app # マウントディレクトリ指定
dockerコンテナの中で作業していく
docker-compose up -d docker-compose exec app /bin/ash
bashではなくashというSHELLを利用していることに注意。alpineベースだとBashはないらしくて怒られた。
touch main.go go mod init main
main.goを作り
Go modulesで初期化する
ローカルディレクトリにもファイルが作られていたら成功
main.goにはとりあえずHelloWorldを書く
package main import "fmt" func main() { fmt.Println("Hello, World!") }
/go/src/app # go run main.go Hello, World!
ちゃんと出力されました。
ここまででディレクトリ構成はこんな感じになってます。
. ├── Dockerfile ├── README.md ├── docker-compose.yml ├── go.mod └── main.go
とりあえずGoのコードを実行するところまで環境構築できました。
次はDocker+Goでアプリ開発していきます。
Go Modules周りとかで詰まりそうで怖い〜
Dockerチュートリアルやってみたメモ
参考にしたQiita記事
環境
MacOS Catalina 10.15.6
Docker for Mac
Docker version 20.10.2
docker-compose version 1.27.4
メモ
Docker run -it の-itとはなにか
docker run -it の「-it」とはなにか - Qiita
-it と入力した場合,
-i or --interactive と -t or --tty 2つのオプションを指定したという意味.
interactive オプションは, 「標準入力」のこと.
データの永続化
Named Volumes
- docker volume createコマンドでボリュームを作成。
2.コンテナを起動するとき、ボリューム接続を指定するのに-vフラグを付け加える
docker volume inspectコマンドで実際の保存場所を確認
$ docker volume inspect todo-db [ { "CreatedAt": "2021-02-28T11:44:06Z", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/todo-db/_data", "Name": "todo-db", "Options": {}, "Scope": "local" } ]
Mountpointがディスク上にデータが保存されている実際の場所
バインドマウント
バインドマウントを使えば、ホスト上の正確なMountpointをコントロールできます。
データの永続化にも使用できますが、追加のデータをコンテナに提供するのによく使われます。
アプリを開発する場合、バインドマウントでソースコードをコンテナに接続して、
コードを変更したり、応答したり、変更をすぐに確認したりできます。
fish: $(...) is not supported.
fish shellはコマンド置換の書き方が異なるので$ははずす
filterを使ってオブジェクトの配列から任意の要素を削除する方法
let array = [ {name:'tarou',id:2}, {name:'jirou',id:4}, {name:'hanako',id:5} ];
たとえばこのようなオブジェクトの配列から
IDが5の要素を削除したい場合
let array = [ {name:'tarou',id:2}, {name:'jirou',id:4}, {name:'hanako',id:5} ]; console.log(`before: array = ${JSON.stringify(array)}`); // before: array = [{"name":"tarou","id":2},{"name":"jirou","id":4},{"name":"hanako","id":5}] array = array.filter( obj => obj.id!==5) console.log(`after: array = ${JSON.stringify(array)}`) // after: array = [{"name":"tarou","id":2},{"name":"jirou","id":4}]
filterを使って特定の要素だけ抽出することができる